net/fm10k: add basic functions for switch management

Add I2C to control the inside LED and PHY.
All the operations of I2C are using fm10k I2C register.
Add SBUS to communicate with spico(micro code in serdes)
by using fm10k SBUS register. This is like I2C operations.
Add registers defination, which include all the registers
will be used in the driver. Add switch management log API.
Add switch management structures. Modify Makefile to add
new files building. Add CONFIG_RTE_FM10K_MANAGEMENT=n
in config/common_linux.

Signed-off-by: Xiaojun Liu <xiaojun.liu@silicom.co.il>
This commit is contained in:
Xiaojun Liu 2020-04-09 14:26:24 +08:00 committed by WeebDataHoarder
parent cd6f1d7a5f
commit 475c6a430a
8 changed files with 3479 additions and 0 deletions

View file

@ -66,3 +66,8 @@ CONFIG_RTE_LIBRTE_HINIC_PMD=y
# Hisilicon HNS3 PMD driver
#
CONFIG_RTE_LIBRTE_HNS3_PMD=y
#
## FM10K switch management
##
CONFIG_RTE_FM10K_SWITCH_MANAGEMENT=n

View file

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2019 Silicom Ltd. Connectivity Solutions
*/
#ifndef _FM10K_DEBUG_H_
#define _FM10K_DEBUG_H_
#define FM10K_SW_ERR(...) PMD_INIT_LOG(ERR, __VA_ARGS__)
#define FM10K_SW_INFO(...) PMD_INIT_LOG(INFO, __VA_ARGS__)
#define FM10K_SW_TRACE(...) PMD_INIT_LOG(DEBUG, __VA_ARGS__)
#define FM10K_SW_ASSERT(...) do {} while (0)
#define FM10K_SW_STATS_TRACE_ENABLE 1
#define FM10K_SW_FFU_CONF_TRACE_ENABLE 0
#define FM10K_SW_MIRROR_TRACE_ENABLE 0
#endif /* _FM10K_DEBUG_H_ */

View file

@ -0,0 +1,309 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2019 Silicom Ltd. Connectivity Solutions
*/
#include <rte_malloc.h>
#include <rte_time.h>
#include <rte_kvargs.h>
#include <rte_hash.h>
#include <rte_flow.h>
#include <rte_flow_driver.h>
#include <rte_tm_driver.h>
#include "fm10k_debug.h"
#include "fm10k_i2c.h"
#include "fm10k_regs.h"
#include "fm10k_switch.h"
static void fm10k_i2c_init(struct fm10k_i2c *);
struct fm10k_i2c *
fm10k_i2c_attach(struct fm10k_switch *sw)
{
struct fm10k_i2c *i2c;
FM10K_SW_TRACE("i2c: attaching");
i2c = (struct fm10k_i2c *)rte_zmalloc("fm10k_i2c",
sizeof(struct fm10k_i2c), 0);
if (i2c == NULL) {
FM10K_SW_INFO("i2c: failed to allocate context");
goto fail;
}
i2c->sw = sw;
pthread_mutex_init(&i2c->req_lock, NULL);
pthread_mutex_init(&i2c->bus_lock, NULL);
sem_init(&i2c->req_cv, 0, 0);
fm10k_i2c_init(i2c);
FM10K_SW_TRACE("i2c: attach successful");
return i2c;
fail:
if (i2c)
fm10k_i2c_detach(i2c);
return NULL;
}
void
fm10k_i2c_detach(struct fm10k_i2c *i2c)
{
FM10K_SW_TRACE("i2c: detaching");
rte_free(i2c);
}
static void
fm10k_i2c_init(struct fm10k_i2c *i2c)
{
struct fm10k_switch *sw = i2c->sw;
struct fm10k_device_info *cfg = sw->info;
uint32_t freq = FM10K_SW_I2C_CFG_DIVIDER_400_KHZ;
uint32_t data;
if (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice) ==
FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4))
freq = FM10K_SW_I2C_CFG_DIVIDER_100_KHZ;
/* clear any pending interrupt */
fm10k_write_switch_reg(sw, FM10K_SW_I2C_CTRL,
FM10K_SW_I2C_CTRL_INTERRUPT_PENDING);
/* 400 KHz, master mode, unmask interrupt */
data = fm10k_read_switch_reg(sw, FM10K_SW_I2C_CFG);
data &= ~FM10K_SW_I2C_CFG_SLAVE_ENABLE;
if (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice) ==
FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4))
FM10K_SW_REPLACE_REG_FIELD(data, I2C_CFG_DIVIDER, freq, data);
data &= ~FM10K_SW_I2C_CFG_INTERRUPT_MASK;
fm10k_write_switch_reg(sw, FM10K_SW_I2C_CFG, data);
if (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice) ==
FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4))
/* reset I2C */
fm10k_gpio_output_set(sw, 5, 1);
}
unsigned int
fm10k_i2c_intr(struct fm10k_i2c *i2c)
{
struct fm10k_switch *sw = i2c->sw;
struct fm10k_i2c_req *req;
int i;
uint32_t data[3];
uint32_t ctrl;
req = i2c->cur_req;
FM10K_SW_SWITCH_LOCK(sw);
ctrl = fm10k_read_switch_reg(sw, FM10K_SW_I2C_CTRL);
fm10k_write_switch_reg(sw, FM10K_SW_I2C_CTRL,
FM10K_SW_I2C_CTRL_INTERRUPT_PENDING);
req->status = FM10K_SW_REG_FIELD(ctrl, I2C_CTRL_COMMAND_COMPLETED);
if ((req->cmd == FM10K_SW_I2C_COMMAND_RD ||
req->cmd == FM10K_SW_I2C_COMMAND_WR_RD) &&
req->status == FM10K_SW_I2C_COMPLETION_NORMAL) {
for (i = 0; i < FM10K_SW_HOWMANY(req->read_len, 4, 4); i++)
data[i] = fm10k_read_switch_reg
(sw, FM10K_SW_I2C_DATA(i));
for (i = 0; i < req->read_len; i++)
req->msg[i] =
(data[i / 4] >> (24 - (i % 4) * 8)) & 0xff;
}
FM10K_SW_SWITCH_UNLOCK(sw);
sem_post(&i2c->req_cv);
return 1;
}
int
fm10k_i2c_exec(struct fm10k_i2c *i2c, struct fm10k_i2c_req *req)
{
struct fm10k_switch *sw = i2c->sw;
int i;
uint32_t ctrl;
uint32_t data[3];
if (((req->cmd == FM10K_SW_I2C_COMMAND_WR ||
req->cmd == FM10K_SW_I2C_COMMAND_WR_RD) &&
req->write_len > FM10K_SW_I2C_MSG_MAX) ||
((req->cmd == FM10K_SW_I2C_COMMAND_RD ||
req->cmd == FM10K_SW_I2C_COMMAND_WR_RD) &&
((req->read_len == 0 ||
req->read_len > FM10K_SW_I2C_MSG_MAX))))
return (-1);
FM10K_SW_TRACE("i2c: initiating command %u", req->cmd);
ctrl =
FM10K_SW_MAKE_REG_FIELD(I2C_CTRL_ADDR, req->addr << 1) |
FM10K_SW_MAKE_REG_FIELD(I2C_CTRL_COMMAND, req->cmd);
if (req->cmd == FM10K_SW_I2C_COMMAND_WR ||
req->cmd == FM10K_SW_I2C_COMMAND_WR_RD) {
ctrl |= FM10K_SW_MAKE_REG_FIELD
(I2C_CTRL_LENGTH_W, req->write_len);
data[0] = 0;
data[1] = 0;
data[2] = 0;
for (i = 0; i < req->write_len; i++)
data[i / 4] |= req->msg[i] << (24 - (i % 4) * 8);
for (i = 0; i < FM10K_SW_HOWMANY(req->write_len, 4, 4); i++)
fm10k_write_switch_reg(sw,
FM10K_SW_I2C_DATA(i), data[i]);
}
if (req->cmd == FM10K_SW_I2C_COMMAND_RD ||
req->cmd == FM10K_SW_I2C_COMMAND_WR_RD)
ctrl |= FM10K_SW_MAKE_REG_FIELD
(I2C_CTRL_LENGTH_R, req->read_len);
req->status = FM10K_SW_I2C_COMPLETION_RUNNING;
i2c->cur_req = req;
FM10K_SW_SWITCH_LOCK(sw);
/* zero command field */
fm10k_write_switch_reg(sw, FM10K_SW_I2C_CTRL, 0);
/* initiate command */
fm10k_write_switch_reg(sw, FM10K_SW_I2C_CTRL, ctrl);
FM10K_SW_SWITCH_UNLOCK(sw);
while (req->status == FM10K_SW_I2C_COMPLETION_RUNNING)
sem_wait(&i2c->req_cv);
return 0;
}
int
fm10k_i2c_read8(struct fm10k_i2c *i2c, uint8_t addr, uint8_t *result)
{
struct fm10k_i2c_req req;
int error;
req.addr = addr;
req.cmd = FM10K_SW_I2C_COMMAND_RD;
req.read_len = 1;
req.msg[0] = 0;
error = fm10k_i2c_exec(i2c, &req);
if (error)
goto done;
if (req.status != FM10K_SW_I2C_COMPLETION_NORMAL) {
FM10K_SW_INFO("i2c read failed (%u)", req.status);
error = -1;
goto done;
}
*result = req.msg[0];
done:
return (error);
}
int
fm10k_i2c_read8_ext(struct fm10k_i2c *i2c,
uint8_t addr, uint8_t reg, uint8_t *result)
{
struct fm10k_i2c_req req;
int error;
req.addr = addr;
req.cmd = FM10K_SW_I2C_COMMAND_WR_RD;
req.write_len = 1;
req.read_len = 1;
req.msg[0] = reg;
error = fm10k_i2c_exec(i2c, &req);
if (error)
goto done;
if (req.status != FM10K_SW_I2C_COMPLETION_NORMAL) {
FM10K_SW_INFO("i2c read failed (%u)", req.status);
error = -1;
goto done;
}
*result = req.msg[0];
done:
return (error);
}
int
fm10k_i2c_write8(struct fm10k_i2c *i2c, uint8_t addr, uint8_t data)
{
struct fm10k_i2c_req req;
int error;
req.addr = addr;
req.cmd = FM10K_SW_I2C_COMMAND_WR;
req.write_len = 1;
req.msg[0] = data;
error = fm10k_i2c_exec(i2c, &req);
if (error)
goto done;
if (req.status != FM10K_SW_I2C_COMPLETION_NORMAL) {
error = -1;
goto done;
}
done:
return (error);
}
int
fm10k_i2c_write16(struct fm10k_i2c *i2c,
uint8_t addr, uint8_t data0, uint8_t data1)
{
struct fm10k_i2c_req req;
int error;
req.addr = addr;
req.cmd = FM10K_SW_I2C_COMMAND_WR;
req.write_len = 2;
req.msg[0] = data0;
req.msg[1] = data1;
error = fm10k_i2c_exec(i2c, &req);
if (error)
goto done;
if (req.status != FM10K_SW_I2C_COMPLETION_NORMAL) {
error = -1;
goto done;
}
done:
return (error);
}
int
fm10k_i2c_probe(struct fm10k_i2c *i2c, uint8_t addr)
{
struct fm10k_i2c_req req;
int error;
req.addr = addr;
req.cmd = FM10K_SW_I2C_COMMAND_WR;
req.write_len = 0;
error = fm10k_i2c_exec(i2c, &req);
if (error)
goto done;
if (req.status != FM10K_SW_I2C_COMPLETION_NORMAL) {
error = -1;
goto done;
}
done:
return (error);
}

View file

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2019 Silicom Ltd. Connectivity Solutions
*/
#ifndef _FM10K_SW_I2C_H_
#define _FM10K_SW_I2C_H_
#include <semaphore.h>
#include <pthread.h>
#include "rte_spinlock.h"
#include "fm10k_debug.h"
#define FM10K_SW_I2C_MSG_MAX 12
struct fm10k_i2c_req {
uint8_t addr; /* 7-bit address */
uint8_t cmd; /* FM10K_SW_I2C_COMMAND_* */
uint8_t write_len;
uint8_t read_len;
uint8_t status; /* FM10K_SW_I2C_COMPLETION_ */
uint8_t msg[FM10K_SW_I2C_MSG_MAX];
};
struct fm10k_i2c {
struct fm10k_switch *sw;
pthread_mutex_t bus_lock;
pthread_mutex_t req_lock;
sem_t req_cv;
struct fm10k_i2c_req *cur_req;
};
#define FM10K_SW_I2C_LOCK(i2c_) \
pthread_mutex_lock(&(i2c_)->bus_lock)
#define FM10K_SW_I2C_UNLOCK(i2c_) \
pthread_mutex_unlock(&(i2c_)->bus_lock)
#define FM10K_SW_I2C_REQ_LOCK(i2c_) \
pthread_mutex_lock(&((i2c_)->req_lock))
#define FM10K_SW_I2C_REQ_UNLOCK(i2c_) \
pthread_mutex_unlock(&((i2c_)->req_lock))
struct fm10k_i2c *fm10k_i2c_attach(struct fm10k_switch *sw);
void fm10k_i2c_detach(struct fm10k_i2c *i2c);
unsigned int fm10k_i2c_intr(struct fm10k_i2c *i2c);
int fm10k_i2c_exec(struct fm10k_i2c *i2c, struct fm10k_i2c_req *req);
int fm10k_i2c_read8(struct fm10k_i2c *i2c, uint8_t addr, uint8_t *result);
int fm10k_i2c_read8_ext(struct fm10k_i2c *i2c,
uint8_t addr, uint8_t reg, uint8_t *result);
int fm10k_i2c_write8(struct fm10k_i2c *i2c, uint8_t addr, uint8_t data);
int fm10k_i2c_write16(struct fm10k_i2c *i2c,
uint8_t addr, uint8_t data0, uint8_t data1);
int fm10k_i2c_probe(struct fm10k_i2c *i2c, uint8_t addr);
#endif /* _FM10K_SW_I2C_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,291 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2019 Silicom Ltd. Connectivity Solutions
*/
#include <rte_malloc.h>
#include <rte_time.h>
#include <rte_kvargs.h>
#include <rte_hash.h>
#include <rte_flow.h>
#include <rte_flow_driver.h>
#include <rte_tm_driver.h>
#include "fm10k_debug.h"
#include "fm10k_regs.h"
#include "fm10k_sbus.h"
#include "fm10k_switch.h"
#define FM10K_SW_SBUS_COMMAND_WAIT_LOCK_US_MAX 4
#define FM10K_SW_SBUS_COMMAND_TIMEOUT_US 500000
#define FM10K_SW_SBUS_RING_DIVIDER 0x01
static int fm10k_sbus_init(struct fm10k_sbus *);
static int fm10k_sbus_exec(struct fm10k_sbus *, struct fm10k_sbus_req *);
static int fm10k_sbus_reset_all(struct fm10k_sbus *);
static int fm10k_sbus_sbm_reset(struct fm10k_sbus *);
struct fm10k_sbus *
fm10k_sbus_attach(struct fm10k_switch *sw,
const char *name,
unsigned int cfg_reg)
{
struct fm10k_sbus *sb;
int error;
FM10K_SW_TRACE("sbus %s: attaching", name);
sb = (struct fm10k_sbus *)rte_zmalloc
("fm10k_sbus", sizeof(struct fm10k_sbus), 0);
if (sb == NULL) {
FM10K_SW_INFO("sbus %s: failed to allocate context", name);
goto fail;
}
sb->sw = sw;
sb->name = name;
pthread_mutex_init(&sb->lock, NULL);
sb->cfg_reg = cfg_reg;
sb->cmd_reg = cfg_reg + FM10K_SW_REG_OFF(1);
sb->req_reg = cfg_reg + FM10K_SW_REG_OFF(2);
sb->resp_reg = cfg_reg + FM10K_SW_REG_OFF(3);
error = fm10k_sbus_init(sb);
if (error)
goto fail;
FM10K_SW_TRACE("sbus %s: attach successful", name);
return sb;
fail:
if (sb)
fm10k_sbus_detach(sb);
return NULL;
}
void
fm10k_sbus_detach(struct fm10k_sbus *sb)
{
FM10K_SW_TRACE("sbus %s: detaching", sb->name);
rte_free(sb);
}
static int
fm10k_sbus_init(struct fm10k_sbus *sb)
{
uint32_t data;
int error;
error = fm10k_sbus_sbm_reset(sb);
if (error)
goto done;
error = fm10k_sbus_reset_all(sb);
if (error)
goto done;
/* set clock to REFCLK/2 */
error = fm10k_sbus_write(sb, FM10K_SW_SBUS_ADDR_SBUS_CONTROLLER, 0x0a,
FM10K_SW_SBUS_RING_DIVIDER);
if (error) {
FM10K_SW_ERR("sbus %s: failed to set ring divider", sb->name);
goto done;
}
error = fm10k_sbus_read(sb, FM10K_SW_SBUS_ADDR_SBUS_CONTROLLER, 0x0a,
&data);
if (error) {
FM10K_SW_ERR("sbus %s: failed to read back ring divider",
sb->name);
goto done;
}
if (data != FM10K_SW_SBUS_RING_DIVIDER) {
FM10K_SW_ERR("sbus %s: ring divider "
"verify failed (expected %u, got %u)",
sb->name, FM10K_SW_SBUS_RING_DIVIDER, data);
error = -1;
goto done;
}
done:
return error;
}
static int
fm10k_sbus_exec(struct fm10k_sbus *sb, struct fm10k_sbus_req *req)
{
struct fm10k_switch *sw = sb->sw;
unsigned int total_usecs;
unsigned int drop_lock;
unsigned int delay_time;
int error = 0;
uint32_t data;
uint8_t expected_result;
FM10K_SW_SBUS_LOCK(sb);
FM10K_SW_SWITCH_LOCK(sw);
data = fm10k_read_switch_reg(sw, sb->cmd_reg);
if (data & FM10K_SW_SBUS_COMMAND_BUSY) {
FM10K_SW_INFO("sbus %s: bus busy (0x%08x)",
sb->name, data);
error = -1;
goto done;
}
switch (req->op) {
case FM10K_SW_SBUS_OP_RESET:
FM10K_SW_TRACE("sbus %s: RESET dev=0x%02x reg=0x%02x data=0x%08x",
sb->name, req->dev, req->reg, req->data);
expected_result = FM10K_SW_SBUS_RESULT_RESET;
fm10k_write_switch_reg(sw, sb->req_reg, req->data);
break;
case FM10K_SW_SBUS_OP_WRITE:
FM10K_SW_TRACE("sbus %s: WRITE dev=0x%02x reg=0x%02x data=0x%08x",
sb->name, req->dev, req->reg, req->data);
expected_result = FM10K_SW_SBUS_RESULT_WRITE;
fm10k_write_switch_reg(sw, sb->req_reg, req->data);
break;
case FM10K_SW_SBUS_OP_READ:
FM10K_SW_TRACE("sbus %s: READ dev=0x%02x reg=0x%02x",
sb->name, req->dev, req->reg);
expected_result = FM10K_SW_SBUS_RESULT_READ;
req->data = 0;
break;
default:
FM10K_SW_INFO("sbus %s: invalid opcode 0x%02x",
sb->name, req->op);
error = -1;
goto done;
}
/* Clear the execute bit */
fm10k_write_switch_reg(sw, sb->cmd_reg, 0);
data =
FM10K_SW_MAKE_REG_FIELD(SBUS_COMMAND_OP, req->op) |
FM10K_SW_MAKE_REG_FIELD(SBUS_COMMAND_ADDRESS, req->dev) |
FM10K_SW_MAKE_REG_FIELD(SBUS_COMMAND_REGISTER, req->reg) |
FM10K_SW_SBUS_COMMAND_EXECUTE;
fm10k_write_switch_reg(sw, sb->cmd_reg, data);
fm10k_write_flush(sw);
total_usecs = 0;
delay_time = 1;
drop_lock = 0;
do {
if (drop_lock)
FM10K_SW_SWITCH_UNLOCK(sw);
fm10k_udelay(delay_time);
if (drop_lock)
FM10K_SW_SWITCH_LOCK(sw);
total_usecs += delay_time;
if (total_usecs >= FM10K_SW_SBUS_COMMAND_WAIT_LOCK_US_MAX) {
drop_lock = 1;
delay_time <<= 1;
}
data = fm10k_read_switch_reg(sw, sb->cmd_reg);
if (!(data & FM10K_SW_SBUS_COMMAND_BUSY)) {
if (FM10K_SW_REG_FIELD(data,
SBUS_COMMAND_RESULT_CODE)
!= expected_result) {
FM10K_SW_INFO("sbus %s: expected "
"result code %u, got %u", sb->name,
expected_result,
FM10K_SW_REG_FIELD(data,
SBUS_COMMAND_RESULT_CODE));
error = -1;
goto done;
}
if (req->op == FM10K_SW_SBUS_OP_READ) {
req->data =
fm10k_read_switch_reg(sw, sb->resp_reg);
FM10K_SW_TRACE("sbus %s: READ data=0x%02x",
sb->name, req->data);
}
goto done;
}
} while (total_usecs < FM10K_SW_SBUS_COMMAND_TIMEOUT_US);
error = -1;
FM10K_SW_INFO("sbus %s: command timed out after %u us "
"(op=0x%02x dev=0x%02x reg=0x%02x data=0x%08x)", sb->name,
total_usecs, req->op, req->dev, req->reg, req->data);
done:
FM10K_SW_SWITCH_UNLOCK(sw);
FM10K_SW_SBUS_UNLOCK(sb);
return error;
}
static int
fm10k_sbus_reset_all(struct fm10k_sbus *sb)
{
struct fm10k_sbus_req req;
int error;
req.op = FM10K_SW_SBUS_OP_RESET;
req.dev = FM10K_SW_SBUS_ADDR_BROADCAST;
req.reg = 0;
req.data = 0;
error = fm10k_sbus_exec(sb, &req);
FM10K_SW_TRACE("sbus %s: broadcast reset %s (%d)", sb->name,
error ? "failed" : "succeeded", error);
return error;
}
static int
fm10k_sbus_sbm_reset(struct fm10k_sbus *sb)
{
struct fm10k_sbus_req req;
int error;
req.op = FM10K_SW_SBUS_OP_RESET;
req.dev = FM10K_SW_SBUS_ADDR_SPICO;
req.reg = 0;
req.data = 0;
error = fm10k_sbus_exec(sb, &req);
FM10K_SW_TRACE("sbus %s: SBM reset %s (%d)", sb->name,
error ? "failed" : "succeeded", error);
return error;
}
int
fm10k_sbus_read(struct fm10k_sbus *sb, uint8_t dev, uint8_t reg, uint32_t *data)
{
struct fm10k_sbus_req req;
int error;
req.op = FM10K_SW_SBUS_OP_READ;
req.dev = dev;
req.reg = reg;
error = fm10k_sbus_exec(sb, &req);
*data = error ? 0 : req.data;
return error;
}
int
fm10k_sbus_write(struct fm10k_sbus *sb, uint8_t dev, uint8_t reg, uint32_t data)
{
struct fm10k_sbus_req req;
int error;
req.op = FM10K_SW_SBUS_OP_WRITE;
req.dev = dev;
req.reg = reg;
req.data = data;
error = fm10k_sbus_exec(sb, &req);
return error;
}

View file

@ -0,0 +1,38 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2019 Silicom Ltd. Connectivity Solutions
*/
#ifndef _FM10K_SW_SBUS_H_
#define _FM10K_SW_SBUS_H_
#include <pthread.h>
struct fm10k_sbus {
struct fm10k_switch *sw;
const char *name;
pthread_mutex_t lock;
unsigned int cfg_reg;
unsigned int cmd_reg;
unsigned int req_reg;
unsigned int resp_reg;
};
#define FM10K_SW_SBUS_LOCK(sb_) pthread_mutex_lock(&((sb_)->lock))
#define FM10K_SW_SBUS_UNLOCK(sb_) pthread_mutex_unlock(&((sb_)->lock))
struct fm10k_sbus_req {
uint8_t op;
uint8_t dev;
uint8_t reg;
uint32_t data;
};
struct fm10k_sbus *fm10k_sbus_attach(struct fm10k_switch *sw,
const char *name, unsigned int cfg_reg);
void fm10k_sbus_detach(struct fm10k_sbus *sb);
int fm10k_sbus_read(struct fm10k_sbus *sb,
uint8_t dev, uint8_t reg, uint32_t *data);
int fm10k_sbus_write(struct fm10k_sbus *sb,
uint8_t dev, uint8_t reg, uint32_t data);
#endif /* _FM10K_SW_SBUS_H_ */

View file

@ -0,0 +1,465 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2019 Silicom Ltd. Connectivity Solutions
*/
#ifndef _FM10K_SW_SWITCH_H_
#define _FM10K_SW_SWITCH_H_
#include <rte_spinlock.h>
#include <pthread.h>
#include <sys/time.h>
#include <semaphore.h>
#include "../fm10k.h"
#include "fm10k_debug.h"
#include "fm10k_regs.h"
/*
* EPL
*/
#define FM10K_SW_EXT_PORTS_MAX 4
#define FM10K_SW_EPLS_MAX 9
#define FM10K_SW_EPLS_SUPPORTED 2
#define FM10K_SW_EPL_LANES 4
#define FM10K_SW_EPLS_PEP_MAX 2
/*
* PEP
*/
#define FM10K_SW_PEP_GROUP_MAX 10
#define FM10K_SW_LOGICAL_PORTS_MAX 48
#define FM10K_SW_PEPS_MAX 9
#define FM10K_SW_PEPS_SUPPORTED 4
#define FM10K_SW_PEP_PORTS_MAX 4
#define FM10K_SW_PEP_MAP_NUM_MAX 2
/*
* GLORT
*/
#define FM10K_SW_GROUP_MAX 10
#define FM10K_SW_VFS_MAX 64
#define FM10K_SW_PFS_GLORT_START 0x1000
#define FM10K_SW_VF_GLORT_START 0x2000
#define FM10K_SW_MULTI_GLORT_START 0x3000
#define FM10K_SW_DPORT_MASK(lport) (1ULL << (lport))
/*
* CONFIG
*/
#define FM10K_SW_CONFIG_MAX 1000
#define FM10K_SW_FFU_RULE_MAX 256
#define FM10K_SW_FFU_RULE_ITEM_MAX 10
/*
* FFU COUNT
*/
#define FM10K_SW_FFU_CNT_BANK 3
#define FM10K_SW_POLICER_LAST 19
#define FM10K_SW_FFU_CNT_START (FM10K_SW_POLICER_LAST + 1)
#define FM10K_SW_FFU_CNT_MAX 100
#define FM10K_SW_PEP_QUEUES_MAX 256
#define FM10K_SW_VF_QUEUES_MAX (FM10K_SW_PEP_QUEUES_MAX - 1)
#define FM10K_SW_FTAG_SIZE 8
#define FM10K_SW_PACKET_SIZE_MIN 17
#define FM10K_SW_PACKET_SIZE_MAX (15 * 1024)
#define FM10K_SW_MTU_MAX \
(FM10K_SW_PACKET_SIZE_MAX - ETHER_HDR_LEN - ETHER_VLAN_ENCAP_LEN)
#define FM10K_SW_SEG_SIZE_MAX (16 * 1024)
#define FM10K_SW_TSO_SIZE_MAX (256 * 1024 - 1)
#define FM10K_SW_MEM_POOL_SEG_SIZE 192
#define FM10K_SW_MEM_POOL_SEGS_MAX 24576
#define FM10K_SW_MEM_POOL_SEGS_RSVD 256
/*
* GLORT MAP
*/
#define FM10K_SW_EPLA_GLORT 0x10
#define FM10K_SW_EPLB_GLORT 0x20
#define FM10K_SW_PEP01_GLORT 0x30
#define FM10K_SW_PEP23_GLORT 0x40
#define FM10K_SW_PEP45_GLORT 0x50
#define FM10K_SW_PEP67_GLORT 0x60
/*
* logical port number
*/
#define FM10K_SW_EPLA_LOGICAL_PORT 1
#define FM10K_SW_EPLB_LOGICAL_PORT 2
#define FM10K_SW_PEP01_LOGICAL_PORT 3
#define FM10K_SW_PEP23_LOGICAL_PORT 4
#define FM10K_SW_PEP45_LOGICAL_PORT 5
#define FM10K_SW_PEP67_LOGICAL_PORT 6
/*
* physical port number
*/
#define FM10K_SW_EPLA_PHYSICAL_PORT 0
#define FM10K_SW_EPLB_PHYSICAL_PORT 4
#define FM10K_SW_PEP01_PHYSICAL_PORT 36
#define FM10K_SW_PEP23_PHYSICAL_PORT 40
#define FM10K_SW_PEP45_PHYSICAL_PORT 44
#define FM10K_SW_PEP67_PHYSICAL_PORT 48
#define FM10K_SW_CARD_ID(v_, d_) (((v_) << 16) | (d_))
#define FM10K_SW_CARD(vname_, dname_) \
FM10K_SW_CARD_ID(FM10K_SW_VENDOR_ID_##vname_, \
FM10K_SW_DEV_ID_##dname_)
/*
* All IDs that may appear in the vendor ID or subsystem vendor ID.
*/
#define FM10K_SW_VENDOR_ID_INTEL 0x8086
#define FM10K_SW_VENDOR_ID_SILICOM 0x1374
#define FM10K_SW_VENDOR_ID_SILICOM_RB 0x1B2E
/*
* All IDs that may appear in the device ID or subsystem device ID.
*/
#define FM10K_SW_DEV_ID_FM10K 0x15a4
/* Silicom cards */
#define FM10K_SW_DEV_ID_PE310G4DBIR_T 0x01B0
#define FM10K_SW_DEV_ID_PE310G4DBIR_SRD 0x01B1
#define FM10K_SW_DEV_ID_PE310G4DBIR_LRD 0x01B2
#define FM10K_SW_DEV_ID_PE310G4DBIR_ER 0x01B3
#define FM10K_SW_DEV_ID_PE310G4DBIR_DA 0x01B4
#define FM10K_SW_DEV_ID_PE340G2DBIR_QS41 0x01B8
#define FM10K_SW_DEV_ID_PE340G2DBIR_QS43 0x01B9
#define FM10K_SW_DEV_ID_PE340G2DBIR_QL4 0x01BA
#define FM10K_SW_DEV_ID_PE3100G2DQIR_QXSL4 0x01C0
#define FM10K_SW_DEV_ID_PE3100G2DQIR_QXSL4_REV_2 0x01C4
#define FM10K_SW_DEV_ID_PE3100G2DQIRL_QXSL4 0x01C1
#define FM10K_SW_DEV_ID_PE3100G2DQIRM_QXSL4 0x01C2
#define FM10K_SW_DEV_ID_PE325G2DSIR 0x01C8
/*
* SWITCH
*/
struct fm10k_device_info {
uint16_t subvendor;
uint16_t subdevice;
const char *desc;
uint8_t num_ext_ports;
uint8_t ext_port_speed;
uint8_t num_epls;
uint8_t num_peps;
};
static struct fm10k_device_info fm10k_device_table[] = {
{ FM10K_SW_VENDOR_ID_SILICOM, FM10K_SW_DEV_ID_PE3100G2DQIR_QXSL4,
"Silicom PE3100G2DQiR-QX4/QS4/QL4", 2, 100, 2, 2 },
{ FM10K_SW_VENDOR_ID_SILICOM, FM10K_SW_DEV_ID_PE3100G2DQIRL_QXSL4,
"Silicom PE3100G2DQiRL-QX4/QS4/QL4", 2, 100, 2, 2 },
{ FM10K_SW_VENDOR_ID_SILICOM, FM10K_SW_DEV_ID_PE3100G2DQIRM_QXSL4,
"Silicom PE3100G2DQiRM-QX4/QS4/QL4", 2, 100, 2, 4 },
};
struct fm10k_i2c;
struct fm10k_sbus;
struct fm10k_hw;
struct fm10k_ext_ports;
struct fm10k_dpdk_cfg;
struct fm10k_sw_port_map {
uint32_t glort;
uint16_t logical_port; /* logical port number */
uint16_t physical_port; /* */
};
struct fm10k_switch {
uint32_t *hw_addr;
uint32_t *sw_addr;
struct fm10k_hw *master_hw;
struct fm10k_device_info *info;
pthread_mutex_t lock;
struct fm10k_i2c *i2c;
struct fm10k_sbus *epl_sbus;
struct fm10k_ext_ports *ext_ports;
sem_t intr_tq;
pthread_t intr_task;
pthread_t led_task;
pthread_t stats_task;
uint32_t detaching;
uint32_t glort_cam_ram_idx;
uint32_t glort_dest_table_idx;
uint32_t mcast_dest_table_idx;
uint32_t mcast_len_table_idx;
uint32_t mcast_vlan_table_idx;
uint32_t epl_serdes_code_version_build_id;
uint16_t pep_mask; /* mask of non-master peps */
uint8_t pepno;
uint8_t serdes_loopback;
uint8_t epla_no;
uint8_t eplb_no;
uint8_t mac_addr[6];
struct fm10k_dpdk_cfg *dpdk_cfg;
struct fm10k_sw_port_map *pep_map;
struct fm10k_sw_port_map *epl_map;
int inited;
};
#define FM10K_SW_SWITCH_LOCK(sw_) pthread_mutex_lock(&((sw_)->lock))
#define FM10K_SW_SWITCH_UNLOCK(sw_) pthread_mutex_unlock(&((sw_)->lock))
#define FM10K_SW_HOWMANY(x, y, y1) (((x) + (y) - 1) / (y1))
static inline uint64_t
fm10k_uptime_us(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((uint64_t)tv.tv_sec * 1000000 + tv.tv_usec);
}
static inline uint32_t
fm10k_read_switch_reg(struct fm10k_switch *sw, uint32_t reg)
{
return ((volatile uint32_t *)sw->master_hw->sw_addr)[reg];
}
static inline uint64_t
fm10k_read_switch_reg64(struct fm10k_switch *sw, uint32_t reg)
{
uint64_t temp, result;
result = ((volatile uint32_t *)sw->master_hw->sw_addr)[reg];
temp = ((volatile uint32_t *)sw->master_hw->sw_addr)[reg + 1];
result |= temp << 32;
return result;
}
static inline void
fm10k_read_switch_array(struct fm10k_switch *sw,
uint32_t reg, uint32_t *results, uint32_t count)
{
unsigned int i;
for (i = 0; i < count; i++)
results[i] =
((volatile uint32_t *)sw->master_hw->sw_addr)[reg + i];
}
static inline void
fm10k_write_switch_reg(struct fm10k_switch *sw, uint32_t reg, uint32_t val)
{
((volatile uint32_t *)sw->master_hw->sw_addr)[reg] = val;
}
static inline void
fm10k_write_switch_reg64(struct fm10k_switch *sw, uint32_t reg, uint64_t val)
{
((volatile uint32_t *)sw->master_hw->sw_addr)[reg] = val & 0xffffffff;
((volatile uint32_t *)sw->master_hw->sw_addr)[reg + 1] = val >> 32;
}
static inline void
fm10k_write_switch_reg128(struct fm10k_switch *sw,
uint32_t reg, uint64_t val_hi, uint64_t val_lo)
{
((volatile uint32_t *)sw->master_hw->sw_addr)[reg] =
val_lo & 0xffffffff;
((volatile uint32_t *)sw->master_hw->sw_addr)[reg + 1] =
val_lo >> 32;
((volatile uint32_t *)sw->master_hw->sw_addr)[reg + 2] =
val_hi & 0xffffffff;
((volatile uint32_t *)sw->master_hw->sw_addr)[reg + 3] =
val_hi >> 32;
}
static inline void
fm10k_write_switch_array(struct fm10k_switch *sw,
uint32_t reg, uint32_t *data, uint32_t count)
{
unsigned int i;
for (i = 0; i < count; i++)
((volatile uint32_t *)sw->master_hw->sw_addr)[reg + i] =
data[i];
}
static inline uint32_t
fm10k_write_flush(struct fm10k_switch *sw)
{
return ((volatile uint32_t *)sw->master_hw->hw_addr)[FM10K_SW_CTRL];
}
static inline void
fm10k_gpio_output_set(struct fm10k_switch *sw, int gpio_pin, int value)
{
uint32_t data;
data = fm10k_read_switch_reg(sw, FM10K_SW_GPIO_CFG);
data |= 1 << gpio_pin;
data &= ~(1 << (gpio_pin + 16));
/* set gpio output */
fm10k_write_switch_reg(sw, FM10K_SW_GPIO_CFG, data);
/*
* Wait 1 msec (PCA spec specifies reset pulse width = 4 ns and
* and reset time = 100 ns)
*/
usec_delay(1000);
/* set reset */
data = fm10k_read_switch_reg(sw, FM10K_SW_GPIO_DATA);
if (value == 0)
data &= ~(1 << gpio_pin);
else
data |= 1 << gpio_pin;
fm10k_write_switch_reg(sw, FM10K_SW_GPIO_DATA, data);
}
static struct fm10k_sw_port_map fm10k_pep_port_map[FM10K_SW_PEPS_SUPPORTED] = {
{
FM10K_SW_PEP01_GLORT,
FM10K_SW_PEP01_LOGICAL_PORT,
FM10K_SW_PEP01_PHYSICAL_PORT
},
{
FM10K_SW_PEP23_GLORT,
FM10K_SW_PEP23_LOGICAL_PORT,
FM10K_SW_PEP23_PHYSICAL_PORT
},
{
FM10K_SW_PEP45_GLORT,
FM10K_SW_PEP45_LOGICAL_PORT,
FM10K_SW_PEP45_PHYSICAL_PORT
},
{
FM10K_SW_PEP67_GLORT,
FM10K_SW_PEP67_LOGICAL_PORT,
FM10K_SW_PEP67_PHYSICAL_PORT
},
};
static struct fm10k_sw_port_map fm10k_epl_port_map[FM10K_SW_EPLS_SUPPORTED] = {
{
FM10K_SW_EPLA_GLORT,
FM10K_SW_EPLA_LOGICAL_PORT,
FM10K_SW_EPLA_PHYSICAL_PORT
},
{
FM10K_SW_EPLB_GLORT,
FM10K_SW_EPLB_LOGICAL_PORT,
FM10K_SW_EPLB_PHYSICAL_PORT
},
};
static inline uint32_t
fm10k_switch_pf_logical_get(uint8_t pf_no)
{
return fm10k_pep_port_map[pf_no].logical_port;
}
static inline uint32_t
fm10k_switch_epl_logical_get(uint8_t epl_no)
{
return fm10k_epl_port_map[epl_no].logical_port;
}
static inline uint32_t
fm10k_switch_vf_glort_get(uint8_t vf_no)
{
return FM10K_SW_VF_GLORT_START + vf_no;
}
static inline uint32_t
fm10k_switch_pf_glort_get(uint8_t pf_no)
{
return fm10k_pep_port_map[pf_no].glort;
}
static inline uint32_t
fm10k_switch_epl_glort_get(uint8_t epl_no)
{
return fm10k_epl_port_map[epl_no].glort;
}
static inline uint32_t
fm10k_switch_pfs_glort_get(uint8_t pf1, uint8_t pf2)
{
uint8_t idx;
if (pf1 > pf2)
idx = (pf2 & 0xf) | (pf1 << 4 & 0xf0);
else
idx = (pf1 & 0xf) | (pf2 << 4 & 0xf0);
return FM10K_SW_PFS_GLORT_START + idx;
}
static inline struct fm10k_device_info*
fm10k_get_device_info(struct fm10k_hw *hw)
{
unsigned int i;
struct fm10k_device_info *info;
uint16_t pci_vendor = hw->vendor_id;
uint16_t pci_device = hw->device_id;
uint16_t pci_subvendor = hw->subsystem_vendor_id;
uint16_t pci_subdevice = hw->subsystem_device_id;
if (pci_vendor != FM10K_SW_VENDOR_ID_INTEL ||
pci_device != FM10K_SW_DEV_ID_FM10K)
return (NULL);
for (i = 0;
i < sizeof(fm10k_device_table) /
sizeof(fm10k_device_table[0]);
i++) {
info = &fm10k_device_table[i];
if (pci_subvendor == info->subvendor &&
pci_subdevice == info->subdevice) {
return info;
}
}
return NULL;
}
#define fm10k_udelay usec_delay
typedef int eth_fm10k_dev_init_half_func(struct fm10k_hw *hw);
unsigned int fm10k_switch_eplidx_to_eplno
(struct fm10k_switch *sw, unsigned int eplidx);
void fm10k_switch_intr(struct fm10k_hw *hw);
struct fm10k_switch *fm10k_switch_get(void);
struct fm10k_device_info *fm10k_get_device_info(struct fm10k_hw *hw);
int fm10k_switch_dpdk_port_start(struct fm10k_hw *hw, void *rte_dev,
uint8_t is_pf, bool master, eth_fm10k_dev_init_half_func *func);
void fm10k_switch_dpdk_port_stop(struct fm10k_hw *hw);
int fm10k_switch_dpdk_hw_queue_map(struct fm10k_hw *hw,
uint16_t queue, uint16_t max_queue,
struct fm10k_hw **map_hw, uint16_t *map_queue);
int fm10k_switch_dpdk_mapped_hw_get(struct fm10k_hw *hw,
struct fm10k_hw *hw_list[]);
int fm10k_switch_dpdk_pf_no_get(struct fm10k_hw *hw);
int fm10k_switch_dpdk_port_no_get(struct fm10k_hw *hw);
void *fm10k_switch_dpdk_port_rte_dev_get(struct fm10k_hw *hw);
struct fm10k_flow_list *
fm10k_switch_dpdk_port_flow_list_get(struct fm10k_hw *hw);
void fm10k_switch_dpdk_tx_queue_num_set(struct fm10k_hw *hw, uint8_t num);
void fm10k_switch_dpdk_rx_queue_num_set(struct fm10k_hw *hw, uint8_t num);
int fm10k_switch_mirror_set(struct fm10k_hw *hw, u16 dest_port, u16 vlan);
int fm10k_switch_mirror_reset(struct fm10k_hw *hw);
void fm10k_switch_flowset_switchto(const char *name);
void fm10k_switch_show_port(void);
void fm10k_switch_show_ffu(void);
void fm10k_switch_show_bank(void);
void fm10k_flow_list_init(void *flow_list);
const struct rte_flow_ops *fm10k_flow_ops_get(void);
#endif