162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is part of the Chelsio T4/T5/T6 Ethernet driver for Linux.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2017 Chelsio Communications, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software is available to you under a choice of one of two
762306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1062306a36Sopenharmony_ci * OpenIB.org BSD license below:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1362306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1462306a36Sopenharmony_ci *     conditions are met:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1762306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1862306a36Sopenharmony_ci *        disclaimer.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2162306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2262306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2362306a36Sopenharmony_ci *        provided with the distribution.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3262306a36Sopenharmony_ci * SOFTWARE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "cxgb4.h"
3662306a36Sopenharmony_ci#include "smt.h"
3762306a36Sopenharmony_ci#include "t4_msg.h"
3862306a36Sopenharmony_ci#include "t4fw_api.h"
3962306a36Sopenharmony_ci#include "t4_regs.h"
4062306a36Sopenharmony_ci#include "t4_values.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct smt_data *t4_init_smt(void)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	unsigned int smt_size;
4562306a36Sopenharmony_ci	struct smt_data *s;
4662306a36Sopenharmony_ci	int i;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	smt_size = SMT_SIZE;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	s = kvzalloc(struct_size(s, smtab, smt_size), GFP_KERNEL);
5162306a36Sopenharmony_ci	if (!s)
5262306a36Sopenharmony_ci		return NULL;
5362306a36Sopenharmony_ci	s->smt_size = smt_size;
5462306a36Sopenharmony_ci	rwlock_init(&s->lock);
5562306a36Sopenharmony_ci	for (i = 0; i < s->smt_size; ++i) {
5662306a36Sopenharmony_ci		s->smtab[i].idx = i;
5762306a36Sopenharmony_ci		s->smtab[i].state = SMT_STATE_UNUSED;
5862306a36Sopenharmony_ci		eth_zero_addr(s->smtab[i].src_mac);
5962306a36Sopenharmony_ci		spin_lock_init(&s->smtab[i].lock);
6062306a36Sopenharmony_ci		s->smtab[i].refcnt = 0;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	return s;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic struct smt_entry *find_or_alloc_smte(struct smt_data *s, u8 *smac)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct smt_entry *first_free = NULL;
6862306a36Sopenharmony_ci	struct smt_entry *e, *end;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (e = &s->smtab[0], end = &s->smtab[s->smt_size]; e != end; ++e) {
7162306a36Sopenharmony_ci		if (e->refcnt == 0) {
7262306a36Sopenharmony_ci			if (!first_free)
7362306a36Sopenharmony_ci				first_free = e;
7462306a36Sopenharmony_ci		} else {
7562306a36Sopenharmony_ci			if (e->state == SMT_STATE_SWITCHING) {
7662306a36Sopenharmony_ci				/* This entry is actually in use. See if we can
7762306a36Sopenharmony_ci				 * re-use it ?
7862306a36Sopenharmony_ci				 */
7962306a36Sopenharmony_ci				if (memcmp(e->src_mac, smac, ETH_ALEN) == 0)
8062306a36Sopenharmony_ci					goto found_reuse;
8162306a36Sopenharmony_ci			}
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (first_free) {
8662306a36Sopenharmony_ci		e = first_free;
8762306a36Sopenharmony_ci		goto found;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	return NULL;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cifound:
9262306a36Sopenharmony_ci	e->state = SMT_STATE_UNUSED;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cifound_reuse:
9562306a36Sopenharmony_ci	return e;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void t4_smte_free(struct smt_entry *e)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	if (e->refcnt == 0) {  /* hasn't been recycled */
10162306a36Sopenharmony_ci		e->state = SMT_STATE_UNUSED;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * cxgb4_smt_release - Release SMT entry
10762306a36Sopenharmony_ci * @e: smt entry to release
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Releases ref count and frees up an smt entry from SMT table
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_civoid cxgb4_smt_release(struct smt_entry *e)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	spin_lock_bh(&e->lock);
11462306a36Sopenharmony_ci	if ((--e->refcnt) == 0)
11562306a36Sopenharmony_ci		t4_smte_free(e);
11662306a36Sopenharmony_ci	spin_unlock_bh(&e->lock);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_smt_release);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_civoid do_smt_write_rpl(struct adapter *adap, const struct cpl_smt_write_rpl *rpl)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unsigned int smtidx = TID_TID_G(GET_TID(rpl));
12362306a36Sopenharmony_ci	struct smt_data *s = adap->smt;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (unlikely(rpl->status != CPL_ERR_NONE)) {
12662306a36Sopenharmony_ci		struct smt_entry *e = &s->smtab[smtidx];
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		dev_err(adap->pdev_dev,
12962306a36Sopenharmony_ci			"Unexpected SMT_WRITE_RPL status %u for entry %u\n",
13062306a36Sopenharmony_ci			rpl->status, smtidx);
13162306a36Sopenharmony_ci		spin_lock(&e->lock);
13262306a36Sopenharmony_ci		e->state = SMT_STATE_ERROR;
13362306a36Sopenharmony_ci		spin_unlock(&e->lock);
13462306a36Sopenharmony_ci		return;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int write_smt_entry(struct adapter *adapter, struct smt_entry *e)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct cpl_t6_smt_write_req *t6req;
14162306a36Sopenharmony_ci	struct smt_data *s = adapter->smt;
14262306a36Sopenharmony_ci	struct cpl_smt_write_req *req;
14362306a36Sopenharmony_ci	struct sk_buff *skb;
14462306a36Sopenharmony_ci	int size;
14562306a36Sopenharmony_ci	u8 row;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) {
14862306a36Sopenharmony_ci		size = sizeof(*req);
14962306a36Sopenharmony_ci		skb = alloc_skb(size, GFP_ATOMIC);
15062306a36Sopenharmony_ci		if (!skb)
15162306a36Sopenharmony_ci			return -ENOMEM;
15262306a36Sopenharmony_ci		/* Source MAC Table (SMT) contains 256 SMAC entries
15362306a36Sopenharmony_ci		 * organized in 128 rows of 2 entries each.
15462306a36Sopenharmony_ci		 */
15562306a36Sopenharmony_ci		req = (struct cpl_smt_write_req *)__skb_put(skb, size);
15662306a36Sopenharmony_ci		INIT_TP_WR(req, 0);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		/* Each row contains an SMAC pair.
15962306a36Sopenharmony_ci		 * LSB selects the SMAC entry within a row
16062306a36Sopenharmony_ci		 */
16162306a36Sopenharmony_ci		row = (e->idx >> 1);
16262306a36Sopenharmony_ci		if (e->idx & 1) {
16362306a36Sopenharmony_ci			req->pfvf1 = 0x0;
16462306a36Sopenharmony_ci			memcpy(req->src_mac1, e->src_mac, ETH_ALEN);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			/* fill pfvf0/src_mac0 with entry
16762306a36Sopenharmony_ci			 * at prev index from smt-tab.
16862306a36Sopenharmony_ci			 */
16962306a36Sopenharmony_ci			req->pfvf0 = 0x0;
17062306a36Sopenharmony_ci			memcpy(req->src_mac0, s->smtab[e->idx - 1].src_mac,
17162306a36Sopenharmony_ci			       ETH_ALEN);
17262306a36Sopenharmony_ci		} else {
17362306a36Sopenharmony_ci			req->pfvf0 = 0x0;
17462306a36Sopenharmony_ci			memcpy(req->src_mac0, e->src_mac, ETH_ALEN);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci			/* fill pfvf1/src_mac1 with entry
17762306a36Sopenharmony_ci			 * at next index from smt-tab
17862306a36Sopenharmony_ci			 */
17962306a36Sopenharmony_ci			req->pfvf1 = 0x0;
18062306a36Sopenharmony_ci			memcpy(req->src_mac1, s->smtab[e->idx + 1].src_mac,
18162306a36Sopenharmony_ci			       ETH_ALEN);
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	} else {
18462306a36Sopenharmony_ci		size = sizeof(*t6req);
18562306a36Sopenharmony_ci		skb = alloc_skb(size, GFP_ATOMIC);
18662306a36Sopenharmony_ci		if (!skb)
18762306a36Sopenharmony_ci			return -ENOMEM;
18862306a36Sopenharmony_ci		/* Source MAC Table (SMT) contains 256 SMAC entries */
18962306a36Sopenharmony_ci		t6req = (struct cpl_t6_smt_write_req *)__skb_put(skb, size);
19062306a36Sopenharmony_ci		INIT_TP_WR(t6req, 0);
19162306a36Sopenharmony_ci		req = (struct cpl_smt_write_req *)t6req;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		/* fill pfvf0/src_mac0 from smt-tab */
19462306a36Sopenharmony_ci		req->pfvf0 = 0x0;
19562306a36Sopenharmony_ci		memcpy(req->src_mac0, s->smtab[e->idx].src_mac, ETH_ALEN);
19662306a36Sopenharmony_ci		row = e->idx;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	OPCODE_TID(req) =
20062306a36Sopenharmony_ci		htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, e->idx |
20162306a36Sopenharmony_ci				    TID_QID_V(adapter->sge.fw_evtq.abs_id)));
20262306a36Sopenharmony_ci	req->params = htonl(SMTW_NORPL_V(0) |
20362306a36Sopenharmony_ci			    SMTW_IDX_V(row) |
20462306a36Sopenharmony_ci			    SMTW_OVLAN_IDX_V(0));
20562306a36Sopenharmony_ci	t4_mgmt_tx(adapter, skb);
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic struct smt_entry *t4_smt_alloc_switching(struct adapter *adap, u16 pfvf,
21062306a36Sopenharmony_ci						u8 *smac)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct smt_data *s = adap->smt;
21362306a36Sopenharmony_ci	struct smt_entry *e;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	write_lock_bh(&s->lock);
21662306a36Sopenharmony_ci	e = find_or_alloc_smte(s, smac);
21762306a36Sopenharmony_ci	if (e) {
21862306a36Sopenharmony_ci		spin_lock(&e->lock);
21962306a36Sopenharmony_ci		if (!e->refcnt) {
22062306a36Sopenharmony_ci			e->refcnt = 1;
22162306a36Sopenharmony_ci			e->state = SMT_STATE_SWITCHING;
22262306a36Sopenharmony_ci			e->pfvf = pfvf;
22362306a36Sopenharmony_ci			memcpy(e->src_mac, smac, ETH_ALEN);
22462306a36Sopenharmony_ci			write_smt_entry(adap, e);
22562306a36Sopenharmony_ci		} else {
22662306a36Sopenharmony_ci			++e->refcnt;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci		spin_unlock(&e->lock);
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	write_unlock_bh(&s->lock);
23162306a36Sopenharmony_ci	return e;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * cxgb4_smt_alloc_switching - Allocates an SMT entry for switch filters.
23662306a36Sopenharmony_ci * @dev: net_device pointer
23762306a36Sopenharmony_ci * @smac: MAC address to add to SMT
23862306a36Sopenharmony_ci * Returns pointer to the SMT entry created
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci * Allocates an SMT entry to be used by switching rule of a filter.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistruct smt_entry *cxgb4_smt_alloc_switching(struct net_device *dev, u8 *smac)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct adapter *adap = netdev2adap(dev);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return t4_smt_alloc_switching(adap, 0x0, smac);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_smt_alloc_switching);
249