Commit 8c4238df authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-minimal-Add-ethtool-and-resource-query-support'

Ido Schimmel says:

====================
mlxsw: minimal: Add ethtool and resource query support

Vadim says:

The minimal driver is chip independent and uses I2C bus for chip access.
Its purpose is to support chassis management on systems equipped with
Mellanox switch ASICs. For example, from a BMC (Board Management
Controller) device.

Patches #1-#3 add ethtool support to the minimal driver so that QSFP/SFP
module info could be retrieved by the driver. This is done by exposing a
dummy netdev for each front panel port and implementing the required
ethtool operations.

Patches #4-#8 add resource query support. This allows the driver to
query the firmware about values of certain resources (e.g., maximum
number of ports). It is required on systems where the maximum number of
ports is larger than the hard coded default (64).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a9836336 6a986993
......@@ -1917,6 +1917,43 @@ void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core)
}
EXPORT_SYMBOL(mlxsw_core_fw_flash_end);
int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox,
struct mlxsw_res *res)
{
int index, i;
u64 data;
u16 id;
int err;
if (!res)
return 0;
mlxsw_cmd_mbox_zero(mbox);
for (index = 0; index < MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES;
index++) {
err = mlxsw_cmd_query_resources(mlxsw_core, mbox, index);
if (err)
return err;
for (i = 0; i < MLXSW_CMD_QUERY_RESOURCES_PER_QUERY; i++) {
id = mlxsw_cmd_mbox_query_resource_id_get(mbox, i);
data = mlxsw_cmd_mbox_query_resource_data_get(mbox, i);
if (id == MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID)
return 0;
mlxsw_res_parse(res, id, data);
}
}
/* If after MLXSW_RESOURCES_QUERY_MAX_QUERIES we still didn't get
* MLXSW_RESOURCES_TABLE_END_ID, something went bad in the FW.
*/
return -EIO;
}
EXPORT_SYMBOL(mlxsw_core_resources_query);
static int __init mlxsw_core_module_init(void)
{
int err;
......
......@@ -182,6 +182,8 @@ int mlxsw_core_port_get_phys_port_name(struct mlxsw_core *mlxsw_core,
int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay);
bool mlxsw_core_schedule_work(struct work_struct *work);
void mlxsw_core_flush_owq(void);
int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox,
struct mlxsw_res *res);
#define MLXSW_CONFIG_PROFILE_SWID_COUNT 8
......
......@@ -41,6 +41,47 @@ static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
return 0;
}
static int
mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
u16 offset, u16 size, void *data,
unsigned int *p_read_size)
{
char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
char mcia_pl[MLXSW_REG_MCIA_LEN];
u16 i2c_addr;
int status;
int err;
size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
/* Cross pages read, read until offset 256 in low page */
size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
}
mlxsw_reg_mcia_pack(mcia_pl, module, 0, 0, offset, size, i2c_addr);
err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
if (err)
return err;
status = mlxsw_reg_mcia_status_get(mcia_pl);
if (status)
return -EIO;
mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
memcpy(data, eeprom_tmp, size);
*p_read_size = size;
return 0;
}
int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
int off, int *temp)
{
......@@ -115,3 +156,83 @@ int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
return 0;
}
int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
struct ethtool_modinfo *modinfo)
{
u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
u8 module_rev_id, module_id;
unsigned int read_size;
int err;
err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
module_info, &read_size);
if (err)
return err;
if (read_size < offset)
return -EIO;
module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
switch (module_id) {
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
module_rev_id >=
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
modinfo->type = ETH_MODULE_SFF_8636;
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
} else {
modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
}
break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
modinfo->type = ETH_MODULE_SFF_8472;
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
break;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(mlxsw_env_get_module_info);
int mlxsw_env_get_module_eeprom(struct net_device *netdev,
struct mlxsw_core *mlxsw_core, int module,
struct ethtool_eeprom *ee, u8 *data)
{
int offset = ee->offset;
unsigned int read_size;
int i = 0;
int err;
if (!ee->len)
return -EINVAL;
memset(data, 0, ee->len);
while (i < ee->len) {
err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
ee->len - i, data + i,
&read_size);
if (err) {
netdev_err(netdev, "Eeprom query failed\n");
return err;
}
i += read_size;
offset += read_size;
}
return 0;
}
EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
......@@ -7,4 +7,11 @@
int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
int off, int *temp);
int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
struct ethtool_modinfo *modinfo);
int mlxsw_env_get_module_eeprom(struct net_device *netdev,
struct mlxsw_core *mlxsw_core, int module,
struct ethtool_eeprom *ee, u8 *data);
#endif
......@@ -14,14 +14,17 @@
#include "cmd.h"
#include "core.h"
#include "i2c.h"
#include "resources.h"
#define MLXSW_I2C_CIR2_BASE 0x72000
#define MLXSW_I2C_CIR_STATUS_OFF 0x18
#define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \
MLXSW_I2C_CIR_STATUS_OFF)
#define MLXSW_I2C_OPMOD_SHIFT 12
#define MLXSW_I2C_EVENT_BIT_SHIFT 22
#define MLXSW_I2C_GO_BIT_SHIFT 23
#define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24
#define MLXSW_I2C_EVENT_BIT BIT(MLXSW_I2C_EVENT_BIT_SHIFT)
#define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT)
#define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT)
#define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \
......@@ -33,6 +36,9 @@
#define MLXSW_I2C_TLV_HDR_SIZE 0x10
#define MLXSW_I2C_ADDR_WIDTH 4
#define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4)
#define MLXSW_I2C_SET_EVENT_CMD (MLXSW_I2C_EVENT_BIT)
#define MLXSW_I2C_PUSH_EVENT_CMD (MLXSW_I2C_GO_BIT | \
MLXSW_I2C_SET_EVENT_CMD)
#define MLXSW_I2C_READ_SEMA_SIZE 4
#define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28)
#define MLXSW_I2C_MBOX_SIZE 20
......@@ -44,6 +50,7 @@
#define MLXSW_I2C_BLK_MAX 32
#define MLXSW_I2C_RETRY 5
#define MLXSW_I2C_TIMEOUT_MSECS 5000
#define MLXSW_I2C_MAX_DATA_SIZE 256
/**
* struct mlxsw_i2c - device private data:
......@@ -167,7 +174,7 @@ static int mlxsw_i2c_wait_go_bit(struct i2c_client *client,
return err > 0 ? 0 : err;
}
/* Routine posts a command to ASIC though mail box. */
/* Routine posts a command to ASIC through mail box. */
static int mlxsw_i2c_write_cmd(struct i2c_client *client,
struct mlxsw_i2c *mlxsw_i2c,
int immediate)
......@@ -213,6 +220,66 @@ static int mlxsw_i2c_write_cmd(struct i2c_client *client,
return 0;
}
/* Routine posts initialization command to ASIC through mail box. */
static int
mlxsw_i2c_write_init_cmd(struct i2c_client *client,
struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod)
{
__be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = {
0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD)
};
__be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = {
0, 0, 0, 0, 0, 0,
cpu_to_be32(client->adapter->nr & 0xffff),
cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD)
};
struct i2c_msg push_cmd =
MLXSW_I2C_WRITE_MSG(client, push_cmd_buf,
MLXSW_I2C_PUSH_CMD_SIZE);
struct i2c_msg prep_cmd =
MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE);
u8 status;
int err;
push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode);
prep_cmd_buf[3] = cpu_to_be32(in_mod);
prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode);
mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf,
MLXSW_I2C_CIR2_BASE);
mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf,
MLXSW_I2C_CIR2_OFF_STATUS);
/* Prepare Command Interface Register for transaction */
err = i2c_transfer(client->adapter, &prep_cmd, 1);
if (err < 0)
return err;
else if (err != 1)
return -EIO;
/* Write out Command Interface Register GO bit to push transaction */
err = i2c_transfer(client->adapter, &push_cmd, 1);
if (err < 0)
return err;
else if (err != 1)
return -EIO;
/* Wait until go bit is cleared. */
err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status);
if (err) {
dev_err(&client->dev, "HW semaphore is not released");
return err;
}
/* Validate transaction completion status. */
if (status) {
dev_err(&client->dev, "Bad transaction completion status %x\n",
status);
return -EIO;
}
return 0;
}
/* Routine obtains mail box offsets from ASIC register space. */
static int mlxsw_i2c_get_mbox(struct i2c_client *client,
struct mlxsw_i2c *mlxsw_i2c)
......@@ -310,8 +377,8 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
/* Routine executes I2C command. */
static int
mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox,
size_t out_mbox_size, u8 *out_mbox, u8 *status)
mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status)
{
struct i2c_client *client = to_i2c_client(dev);
struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
......@@ -326,24 +393,40 @@ mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox,
WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32));
reg_size = mlxsw_i2c_get_reg_size(in_mbox);
num = reg_size / MLXSW_I2C_BLK_MAX;
if (reg_size % MLXSW_I2C_BLK_MAX)
num++;
if (in_mbox) {
reg_size = mlxsw_i2c_get_reg_size(in_mbox);
num = reg_size / MLXSW_I2C_BLK_MAX;
if (reg_size % MLXSW_I2C_BLK_MAX)
num++;
if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
dev_err(&client->dev, "Could not acquire lock");
return -EINVAL;
}
if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
dev_err(&client->dev, "Could not acquire lock");
return -EINVAL;
}
err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status);
if (err)
goto cmd_fail;
/* No out mailbox is case of write transaction. */
if (!out_mbox) {
mutex_unlock(&mlxsw_i2c->cmd.lock);
return 0;
}
} else {
/* No input mailbox is case of initialization query command. */
reg_size = MLXSW_I2C_MAX_DATA_SIZE;
num = reg_size / MLXSW_I2C_BLK_MAX;
err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status);
if (err)
goto cmd_fail;
if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
dev_err(&client->dev, "Could not acquire lock");
return -EINVAL;
}
/* No out mailbox is case of write transaction. */
if (!out_mbox) {
mutex_unlock(&mlxsw_i2c->cmd.lock);
return 0;
err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode,
in_mod);
if (err)
goto cmd_fail;
}
/* Send read transaction to get output mailbox content. */
......@@ -395,8 +478,8 @@ static int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
{
struct mlxsw_i2c *mlxsw_i2c = bus_priv;
return mlxsw_i2c_cmd(mlxsw_i2c->dev, in_mbox_size, in_mbox,
out_mbox_size, out_mbox, status);
return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size,
in_mbox, out_mbox_size, out_mbox, status);
}
static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv,
......@@ -414,13 +497,22 @@ static int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb,
static int
mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
const struct mlxsw_config_profile *profile,
struct mlxsw_res *resources)
struct mlxsw_res *res)
{
struct mlxsw_i2c *mlxsw_i2c = bus_priv;
char *mbox;
int err;
mlxsw_i2c->core = mlxsw_core;
return 0;
mbox = mlxsw_cmd_mbox_alloc();
if (!mbox)
return -ENOMEM;
err = mlxsw_core_resources_query(mlxsw_core, mbox, res);
mlxsw_cmd_mbox_free(mbox);
return err;
}
static void mlxsw_i2c_fini(void *bus_priv)
......
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */
/* Copyright (c) 2016-2019 Mellanox Technologies. All rights reserved */
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
......@@ -8,59 +11,377 @@
#include <linux/types.h>
#include "core.h"
#include "core_env.h"
#include "i2c.h"
static const char mlxsw_minimal_driver_name[] = "mlxsw_minimal";
static const char mlxsw_m_driver_name[] = "mlxsw_minimal";
static const struct mlxsw_config_profile mlxsw_minimal_config_profile;
struct mlxsw_m_port;
static struct mlxsw_driver mlxsw_minimal_driver = {
.kind = mlxsw_minimal_driver_name,
.priv_size = 1,
.profile = &mlxsw_minimal_config_profile,
struct mlxsw_m {
struct mlxsw_m_port **ports;
int *module_to_port;
struct mlxsw_core *core;
const struct mlxsw_bus_info *bus_info;
u8 base_mac[ETH_ALEN];
u8 max_ports;
};
static const struct i2c_device_id mlxsw_minimal_i2c_id[] = {
struct mlxsw_m_port {
struct net_device *dev;
struct mlxsw_m *mlxsw_m;
u8 local_port;
u8 module;
};
static int mlxsw_m_port_dummy_open_stop(struct net_device *dev)
{
return 0;
}
static int
mlxsw_m_port_get_phys_port_name(struct net_device *dev, char *name, size_t len)
{
struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
u8 local_port = mlxsw_m_port->local_port;
return mlxsw_core_port_get_phys_port_name(core, local_port, name, len);
}
static int mlxsw_m_port_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
ppid->id_len = sizeof(mlxsw_m->base_mac);
memcpy(&ppid->id, &mlxsw_m->base_mac, ppid->id_len);
return 0;
}
static const struct net_device_ops mlxsw_m_port_netdev_ops = {
.ndo_open = mlxsw_m_port_dummy_open_stop,
.ndo_stop = mlxsw_m_port_dummy_open_stop,
.ndo_get_phys_port_name = mlxsw_m_port_get_phys_port_name,
.ndo_get_port_parent_id = mlxsw_m_port_get_port_parent_id,
};
static int mlxsw_m_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
{
struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
return mlxsw_env_get_module_info(core, mlxsw_m_port->module, modinfo);
}
static int
mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee,
u8 *data)
{
struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
return mlxsw_env_get_module_eeprom(netdev, core, mlxsw_m_port->module,
ee, data);
}
static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {
.get_module_info = mlxsw_m_get_module_info,
.get_module_eeprom = mlxsw_m_get_module_eeprom,
};
static int
mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u8 local_port,
u8 *p_module, u8 *p_width)
{
char pmlp_pl[MLXSW_REG_PMLP_LEN];
int err;
mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl);
if (err)
return err;
*p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
*p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
return 0;
}
static int
mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port)
{
struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
struct net_device *dev = mlxsw_m_port->dev;
char ppad_pl[MLXSW_REG_PPAD_LEN];
int err;
mlxsw_reg_ppad_pack(ppad_pl, false, 0);
err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl);
if (err)
return err;
mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
/* The last byte value in base mac address is guaranteed
* to be such it does not overflow when adding local_port
* value.
*/
dev->dev_addr[ETH_ALEN - 1] += mlxsw_m_port->module + 1;
return 0;
}
static int
mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module)
{
struct mlxsw_m_port *mlxsw_m_port;
struct net_device *dev;
int err;
err = mlxsw_core_port_init(mlxsw_m->core, local_port);
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
local_port);
return err;
}
dev = alloc_etherdev(sizeof(struct mlxsw_m_port));
if (!dev) {
err = -ENOMEM;
goto err_alloc_etherdev;
}
SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev);
mlxsw_m_port = netdev_priv(dev);
mlxsw_m_port->dev = dev;
mlxsw_m_port->mlxsw_m = mlxsw_m;
mlxsw_m_port->local_port = local_port;
mlxsw_m_port->module = module;
dev->netdev_ops = &mlxsw_m_port_netdev_ops;
dev->ethtool_ops = &mlxsw_m_port_ethtool_ops;
err = mlxsw_m_port_dev_addr_get(mlxsw_m_port);
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n",
mlxsw_m_port->local_port);
goto err_dev_addr_get;
}
netif_carrier_off(dev);
mlxsw_m->ports[local_port] = mlxsw_m_port;
err = register_netdev(dev);
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n",
mlxsw_m_port->local_port);
goto err_register_netdev;
}
mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port,
mlxsw_m_port, dev, module + 1, false, 0);
return 0;
err_register_netdev:
mlxsw_m->ports[local_port] = NULL;
free_netdev(dev);
err_dev_addr_get:
err_alloc_etherdev:
mlxsw_core_port_fini(mlxsw_m->core, local_port);
return err;
}
static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u8 local_port)
{
struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port];
mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m);
unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */
mlxsw_m->ports[local_port] = NULL;
free_netdev(mlxsw_m_port->dev);
mlxsw_core_port_fini(mlxsw_m->core, local_port);
}
static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port,
u8 *last_module)
{
u8 module, width;
int err;
/* Fill out to local port mapping array */
err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module,
&width);
if (err)
return err;
if (!width)
return 0;
/* Skip, if port belongs to the cluster */
if (module == *last_module)
return 0;
*last_module = module;
mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports;
return 0;
}
static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module)
{
mlxsw_m->module_to_port[module] = -1;
}
static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)
{
unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
u8 last_module = max_ports;
int i;
int err;
mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports),
GFP_KERNEL);
if (!mlxsw_m->ports)
return -ENOMEM;
mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int),
GFP_KERNEL);
if (!mlxsw_m->module_to_port) {
err = -ENOMEM;
goto err_module_to_port_alloc;
}
/* Invalidate the entries of module to local port mapping array */
for (i = 0; i < max_ports; i++)
mlxsw_m->module_to_port[i] = -1;
/* Fill out module to local port mapping array */
for (i = 1; i < max_ports; i++) {
err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module);
if (err)
goto err_module_to_port_map;
}
/* Create port objects for each valid entry */
for (i = 0; i < mlxsw_m->max_ports; i++) {
if (mlxsw_m->module_to_port[i] > 0) {
err = mlxsw_m_port_create(mlxsw_m,
mlxsw_m->module_to_port[i],
i);
if (err)
goto err_module_to_port_create;
}
}
return 0;
err_module_to_port_create:
for (i--; i >= 0; i--) {
if (mlxsw_m->module_to_port[i] > 0)
mlxsw_m_port_remove(mlxsw_m,
mlxsw_m->module_to_port[i]);
}
i = max_ports;
err_module_to_port_map:
for (i--; i > 0; i--)
mlxsw_m_port_module_unmap(mlxsw_m, i);
kfree(mlxsw_m->module_to_port);
err_module_to_port_alloc:
kfree(mlxsw_m->ports);
return err;
}
static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m)
{
int i;
for (i = 0; i < mlxsw_m->max_ports; i++) {
if (mlxsw_m->module_to_port[i] > 0) {
mlxsw_m_port_remove(mlxsw_m,
mlxsw_m->module_to_port[i]);
mlxsw_m_port_module_unmap(mlxsw_m, i);
}
}
kfree(mlxsw_m->module_to_port);
kfree(mlxsw_m->ports);
}
static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info)
{
struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
int err;
mlxsw_m->core = mlxsw_core;
mlxsw_m->bus_info = mlxsw_bus_info;
err = mlxsw_m_ports_create(mlxsw_m);
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");
return err;
}
return 0;
}
static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core)
{
struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
mlxsw_m_ports_remove(mlxsw_m);
}
static const struct mlxsw_config_profile mlxsw_m_config_profile;
static struct mlxsw_driver mlxsw_m_driver = {
.kind = mlxsw_m_driver_name,
.priv_size = sizeof(struct mlxsw_m),
.init = mlxsw_m_init,
.fini = mlxsw_m_fini,
.profile = &mlxsw_m_config_profile,
.res_query_enabled = true,
};
static const struct i2c_device_id mlxsw_m_i2c_id[] = {
{ "mlxsw_minimal", 0},
{ },
};
static struct i2c_driver mlxsw_minimal_i2c_driver = {
static struct i2c_driver mlxsw_m_i2c_driver = {
.driver.name = "mlxsw_minimal",
.class = I2C_CLASS_HWMON,
.id_table = mlxsw_minimal_i2c_id,
.id_table = mlxsw_m_i2c_id,
};
static int __init mlxsw_minimal_module_init(void)
static int __init mlxsw_m_module_init(void)
{
int err;
err = mlxsw_core_driver_register(&mlxsw_minimal_driver);
err = mlxsw_core_driver_register(&mlxsw_m_driver);
if (err)
return err;
err = mlxsw_i2c_driver_register(&mlxsw_minimal_i2c_driver);
err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver);
if (err)
goto err_i2c_driver_register;
return 0;
err_i2c_driver_register:
mlxsw_core_driver_unregister(&mlxsw_minimal_driver);
mlxsw_core_driver_unregister(&mlxsw_m_driver);
return err;
}
static void __exit mlxsw_minimal_module_exit(void)
static void __exit mlxsw_m_module_exit(void)
{
mlxsw_i2c_driver_unregister(&mlxsw_minimal_i2c_driver);
mlxsw_core_driver_unregister(&mlxsw_minimal_driver);
mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver);
mlxsw_core_driver_unregister(&mlxsw_m_driver);
}
module_init(mlxsw_minimal_module_init);
module_exit(mlxsw_minimal_module_exit);
module_init(mlxsw_m_module_init);
module_exit(mlxsw_m_module_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("Mellanox minimal driver");
MODULE_DEVICE_TABLE(i2c, mlxsw_minimal_i2c_id);
MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id);
......@@ -1039,42 +1039,6 @@ mlxsw_pci_config_profile_swid_config(struct mlxsw_pci *mlxsw_pci,
mlxsw_cmd_mbox_config_profile_swid_config_mask_set(mbox, index, mask);
}
static int mlxsw_pci_resources_query(struct mlxsw_pci *mlxsw_pci, char *mbox,
struct mlxsw_res *res)
{
int index, i;
u64 data;
u16 id;
int err;
if (!res)
return 0;
mlxsw_cmd_mbox_zero(mbox);
for (index = 0; index < MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES;
index++) {
err = mlxsw_cmd_query_resources(mlxsw_pci->core, mbox, index);
if (err)
return err;
for (i = 0; i < MLXSW_CMD_QUERY_RESOURCES_PER_QUERY; i++) {
id = mlxsw_cmd_mbox_query_resource_id_get(mbox, i);
data = mlxsw_cmd_mbox_query_resource_data_get(mbox, i);
if (id == MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID)
return 0;
mlxsw_res_parse(res, id, data);
}
}
/* If after MLXSW_RESOURCES_QUERY_MAX_QUERIES we still didn't get
* MLXSW_RESOURCES_TABLE_END_ID, something went bad in the FW.
*/
return -EIO;
}
static int
mlxsw_pci_profile_get_kvd_sizes(const struct mlxsw_pci *mlxsw_pci,
const struct mlxsw_config_profile *profile,
......@@ -1459,7 +1423,7 @@ static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
if (err)
goto err_boardinfo;
err = mlxsw_pci_resources_query(mlxsw_pci, mbox, res);
err = mlxsw_core_resources_query(mlxsw_core, mbox, res);
if (err)
goto err_query_resources;
......
......@@ -32,6 +32,7 @@
#include "spectrum.h"
#include "pci.h"
#include "core.h"
#include "core_env.h"
#include "reg.h"
#include "port.h"
#include "trap.h"
......@@ -3161,99 +3162,18 @@ static int mlxsw_sp_flash_device(struct net_device *dev,
return err;
}
#define MLXSW_SP_I2C_ADDR_LOW 0x50
#define MLXSW_SP_I2C_ADDR_HIGH 0x51
#define MLXSW_SP_EEPROM_PAGE_LENGTH 256
static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
u16 offset, u16 size, void *data,
unsigned int *p_read_size)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
char mcia_pl[MLXSW_REG_MCIA_LEN];
u16 i2c_addr;
int status;
int err;
size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
/* Cross pages read, read until offset 256 in low page */
size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
}
mlxsw_reg_mcia_pack(mcia_pl, mlxsw_sp_port->mapping.module,
0, 0, offset, size, i2c_addr);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mcia), mcia_pl);
if (err)
return err;
status = mlxsw_reg_mcia_status_get(mcia_pl);
if (status)
return -EIO;
mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
memcpy(data, eeprom_tmp, size);
*p_read_size = size;
return 0;
}
static int mlxsw_sp_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
u8 module_rev_id, module_id;
unsigned int read_size;
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
int err;
err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0, offset,
module_info, &read_size);
if (err)
return err;
if (read_size < offset)
return -EIO;
err = mlxsw_env_get_module_info(mlxsw_sp->core,
mlxsw_sp_port->mapping.module,
modinfo);
module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
switch (module_id) {
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
module_rev_id >=
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
modinfo->type = ETH_MODULE_SFF_8636;
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
} else {
modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
}
break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
modinfo->type = ETH_MODULE_SFF_8472;
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
break;
default:
return -EINVAL;
}
return 0;
return err;
}
static int mlxsw_sp_get_module_eeprom(struct net_device *netdev,
......@@ -3261,30 +3181,14 @@ static int mlxsw_sp_get_module_eeprom(struct net_device *netdev,
u8 *data)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
int offset = ee->offset;
unsigned int read_size;
int i = 0;
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
int err;
if (!ee->len)
return -EINVAL;
memset(data, 0, ee->len);
err = mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core,
mlxsw_sp_port->mapping.module, ee,
data);
while (i < ee->len) {
err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, offset,
ee->len - i, data + i,
&read_size);
if (err) {
netdev_err(mlxsw_sp_port->dev, "Eeprom query failed\n");
return err;
}
i += read_size;
offset += read_size;
}
return 0;
return err;
}
static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment