Commit 79e08823 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-hwmon-and-thermal-extensions'

Ido Schimmel says:

====================
mlxsw: hwmon and thermal extensions

Vadim says:

This patchset contains various improvements to hwmon and thermal code in
mlxsw. The most significant improvement is the ability to read modules'
temperature attributes (input, fault, critical and emergency thresholds)
as well as fans' fault indication. These new attributes will improve the
ability to monitor the system.

Patches #1-#4 add the necessary device registers and APIs to read
modules' temperature attributes and fans' fault indication.

Patches #5-#8 perform small improvements in hwmon and thermal code such
as using a more indicative name for cooling devices.

Patch #9 exposes fans' fault indication via hwmon.

Patch #10 exposes modules' temperature attributes via hwmon.

Patch #11 adds an hwmon label to modules' temperature sensor. This helps
to parse the output of utilities such as "sensors".

Patch #12 allows to bind an external cooling device ("mlxreg-fan") to
mlxsw thermal zone. This will allow the mlxsw thermal zone to change the
cooling level of cooling devices not programmed via switch registers.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 03b96742 97cd342a
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o
mlxsw_core-objs := core.o core_acl_flex_keys.o \ mlxsw_core-objs := core.o core_acl_flex_keys.o \
core_acl_flex_actions.o core_acl_flex_actions.o core_env.o
mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o
mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o
obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o
......
...@@ -344,6 +344,7 @@ struct mlxsw_bus_info { ...@@ -344,6 +344,7 @@ struct mlxsw_bus_info {
struct mlxsw_fw_rev fw_rev; struct mlxsw_fw_rev fw_rev;
u8 vsd[MLXSW_CMD_BOARDINFO_VSD_LEN]; u8 vsd[MLXSW_CMD_BOARDINFO_VSD_LEN];
u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN]; u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN];
u8 low_frequency;
}; };
struct mlxsw_hwmon; struct mlxsw_hwmon;
......
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
#include <linux/kernel.h>
#include <linux/err.h>
#include "core.h"
#include "core_env.h"
#include "item.h"
#include "reg.h"
static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
bool *qsfp)
{
char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
char mcia_pl[MLXSW_REG_MCIA_LEN];
u8 ident;
int err;
mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
MLXSW_REG_MCIA_I2C_ADDR_LOW);
err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
if (err)
return err;
mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
ident = eeprom_tmp[0];
switch (ident) {
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
*qsfp = false;
break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
*qsfp = true;
break;
default:
return -EINVAL;
}
return 0;
}
int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
int off, int *temp)
{
char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
union {
u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
u16 temp;
} temp_thresh;
char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
u16 module_temp;
bool qsfp;
int err;
mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
1);
err = mlxsw_reg_query(core, MLXSW_REG(mtbr), mtbr_pl);
if (err)
return err;
/* Don't read temperature thresholds for module with no valid info. */
mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &module_temp, NULL);
switch (module_temp) {
case MLXSW_REG_MTBR_BAD_SENS_INFO: /* fall-through */
case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
case MLXSW_REG_MTBR_INDEX_NA:
*temp = 0;
return 0;
default:
/* Do not consider thresholds for zero temperature. */
if (!MLXSW_REG_MTMP_TEMP_TO_MC(module_temp)) {
*temp = 0;
return 0;
}
break;
}
/* Read Free Side Device Temperature Thresholds from page 03h
* (MSB at lower byte address).
* Bytes:
* 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
* 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
* 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
* 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
*/
/* Validate module identifier value. */
err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
if (err)
return err;
if (qsfp)
mlxsw_reg_mcia_pack(mcia_pl, module, 0,
MLXSW_REG_MCIA_TH_PAGE_NUM,
MLXSW_REG_MCIA_TH_PAGE_OFF + off,
MLXSW_REG_MCIA_TH_ITEM_SIZE,
MLXSW_REG_MCIA_I2C_ADDR_LOW);
else
mlxsw_reg_mcia_pack(mcia_pl, module, 0,
MLXSW_REG_MCIA_PAGE0_LO,
off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
MLXSW_REG_MCIA_I2C_ADDR_HIGH);
err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
if (err)
return err;
mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
*temp = temp_thresh.temp * 1000;
return 0;
}
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
#ifndef _MLXSW_CORE_ENV_H
#define _MLXSW_CORE_ENV_H
int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
int off, int *temp);
#endif
...@@ -13,7 +13,12 @@ ...@@ -13,7 +13,12 @@
#include "core.h" #include "core.h"
#define MLXSW_THERMAL_POLL_INT 1000 /* ms */ #define MLXSW_THERMAL_POLL_INT 1000 /* ms */
#define MLXSW_THERMAL_MAX_TEMP 110000 /* 110C */ #define MLXSW_THERMAL_SLOW_POLL_INT 20000 /* ms */
#define MLXSW_THERMAL_ASIC_TEMP_NORM 75000 /* 75C */
#define MLXSW_THERMAL_ASIC_TEMP_HIGH 85000 /* 85C */
#define MLXSW_THERMAL_ASIC_TEMP_HOT 105000 /* 105C */
#define MLXSW_THERMAL_ASIC_TEMP_CRIT 110000 /* 110C */
#define MLXSW_THERMAL_HYSTERESIS_TEMP 5000 /* 5C */
#define MLXSW_THERMAL_MAX_STATE 10 #define MLXSW_THERMAL_MAX_STATE 10
#define MLXSW_THERMAL_MAX_DUTY 255 #define MLXSW_THERMAL_MAX_DUTY 255
/* Minimum and maximum fan allowed speed in percent: from 20% to 100%. Values /* Minimum and maximum fan allowed speed in percent: from 20% to 100%. Values
...@@ -26,9 +31,15 @@ ...@@ -26,9 +31,15 @@
#define MLXSW_THERMAL_SPEED_MAX (MLXSW_THERMAL_MAX_STATE * 2) #define MLXSW_THERMAL_SPEED_MAX (MLXSW_THERMAL_MAX_STATE * 2)
#define MLXSW_THERMAL_SPEED_MIN_LEVEL 2 /* 20% */ #define MLXSW_THERMAL_SPEED_MIN_LEVEL 2 /* 20% */
/* External cooling devices, allowed for binding to mlxsw thermal zones. */
static char * const mlxsw_thermal_external_allowed_cdev[] = {
"mlxreg_fan",
};
struct mlxsw_thermal_trip { struct mlxsw_thermal_trip {
int type; int type;
int temp; int temp;
int hyst;
int min_state; int min_state;
int max_state; int max_state;
}; };
...@@ -36,32 +47,29 @@ struct mlxsw_thermal_trip { ...@@ -36,32 +47,29 @@ struct mlxsw_thermal_trip {
static const struct mlxsw_thermal_trip default_thermal_trips[] = { static const struct mlxsw_thermal_trip default_thermal_trips[] = {
{ /* In range - 0-40% PWM */ { /* In range - 0-40% PWM */
.type = THERMAL_TRIP_ACTIVE, .type = THERMAL_TRIP_ACTIVE,
.temp = 75000, .temp = MLXSW_THERMAL_ASIC_TEMP_NORM,
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
.min_state = 0, .min_state = 0,
.max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10, .max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
}, },
{ /* High - 40-100% PWM */
.type = THERMAL_TRIP_ACTIVE,
.temp = 80000,
.min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
.max_state = MLXSW_THERMAL_MAX_STATE,
},
{ {
/* Very high - 100% PWM */ /* In range - 40-100% PWM */
.type = THERMAL_TRIP_ACTIVE, .type = THERMAL_TRIP_ACTIVE,
.temp = 85000, .temp = MLXSW_THERMAL_ASIC_TEMP_HIGH,
.min_state = MLXSW_THERMAL_MAX_STATE, .hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
.min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
.max_state = MLXSW_THERMAL_MAX_STATE, .max_state = MLXSW_THERMAL_MAX_STATE,
}, },
{ /* Warning */ { /* Warning */
.type = THERMAL_TRIP_HOT, .type = THERMAL_TRIP_HOT,
.temp = 105000, .temp = MLXSW_THERMAL_ASIC_TEMP_HOT,
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
.min_state = MLXSW_THERMAL_MAX_STATE, .min_state = MLXSW_THERMAL_MAX_STATE,
.max_state = MLXSW_THERMAL_MAX_STATE, .max_state = MLXSW_THERMAL_MAX_STATE,
}, },
{ /* Critical - soft poweroff */ { /* Critical - soft poweroff */
.type = THERMAL_TRIP_CRITICAL, .type = THERMAL_TRIP_CRITICAL,
.temp = MLXSW_THERMAL_MAX_TEMP, .temp = MLXSW_THERMAL_ASIC_TEMP_CRIT,
.min_state = MLXSW_THERMAL_MAX_STATE, .min_state = MLXSW_THERMAL_MAX_STATE,
.max_state = MLXSW_THERMAL_MAX_STATE, .max_state = MLXSW_THERMAL_MAX_STATE,
} }
...@@ -76,6 +84,7 @@ struct mlxsw_thermal { ...@@ -76,6 +84,7 @@ struct mlxsw_thermal {
struct mlxsw_core *core; struct mlxsw_core *core;
const struct mlxsw_bus_info *bus_info; const struct mlxsw_bus_info *bus_info;
struct thermal_zone_device *tzdev; struct thermal_zone_device *tzdev;
int polling_delay;
struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX]; struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1]; u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
...@@ -103,6 +112,13 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal, ...@@ -103,6 +112,13 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
if (thermal->cdevs[i] == cdev) if (thermal->cdevs[i] == cdev)
return i; return i;
/* Allow mlxsw thermal zone binding to an external cooling device */
for (i = 0; i < ARRAY_SIZE(mlxsw_thermal_external_allowed_cdev); i++) {
if (strnstr(cdev->type, mlxsw_thermal_external_allowed_cdev[i],
sizeof(cdev->type)))
return 0;
}
return -ENODEV; return -ENODEV;
} }
...@@ -172,7 +188,7 @@ static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev, ...@@ -172,7 +188,7 @@ static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev,
mutex_lock(&tzdev->lock); mutex_lock(&tzdev->lock);
if (mode == THERMAL_DEVICE_ENABLED) if (mode == THERMAL_DEVICE_ENABLED)
tzdev->polling_delay = MLXSW_THERMAL_POLL_INT; tzdev->polling_delay = thermal->polling_delay;
else else
tzdev->polling_delay = 0; tzdev->polling_delay = 0;
...@@ -237,13 +253,31 @@ static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev, ...@@ -237,13 +253,31 @@ static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev,
struct mlxsw_thermal *thermal = tzdev->devdata; struct mlxsw_thermal *thermal = tzdev->devdata;
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS || if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS ||
temp > MLXSW_THERMAL_MAX_TEMP) temp > MLXSW_THERMAL_ASIC_TEMP_CRIT)
return -EINVAL; return -EINVAL;
thermal->trips[trip].temp = temp; thermal->trips[trip].temp = temp;
return 0; return 0;
} }
static int mlxsw_thermal_get_trip_hyst(struct thermal_zone_device *tzdev,
int trip, int *p_hyst)
{
struct mlxsw_thermal *thermal = tzdev->devdata;
*p_hyst = thermal->trips[trip].hyst;
return 0;
}
static int mlxsw_thermal_set_trip_hyst(struct thermal_zone_device *tzdev,
int trip, int hyst)
{
struct mlxsw_thermal *thermal = tzdev->devdata;
thermal->trips[trip].hyst = hyst;
return 0;
}
static struct thermal_zone_device_ops mlxsw_thermal_ops = { static struct thermal_zone_device_ops mlxsw_thermal_ops = {
.bind = mlxsw_thermal_bind, .bind = mlxsw_thermal_bind,
.unbind = mlxsw_thermal_unbind, .unbind = mlxsw_thermal_unbind,
...@@ -253,6 +287,8 @@ static struct thermal_zone_device_ops mlxsw_thermal_ops = { ...@@ -253,6 +287,8 @@ static struct thermal_zone_device_ops mlxsw_thermal_ops = {
.get_trip_type = mlxsw_thermal_get_trip_type, .get_trip_type = mlxsw_thermal_get_trip_type,
.get_trip_temp = mlxsw_thermal_get_trip_temp, .get_trip_temp = mlxsw_thermal_get_trip_temp,
.set_trip_temp = mlxsw_thermal_set_trip_temp, .set_trip_temp = mlxsw_thermal_set_trip_temp,
.get_trip_hyst = mlxsw_thermal_get_trip_hyst,
.set_trip_hyst = mlxsw_thermal_set_trip_hyst,
}; };
static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev, static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev,
...@@ -407,7 +443,8 @@ int mlxsw_thermal_init(struct mlxsw_core *core, ...@@ -407,7 +443,8 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
if (pwm_active & BIT(i)) { if (pwm_active & BIT(i)) {
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
cdev = thermal_cooling_device_register("Fan", thermal, cdev = thermal_cooling_device_register("mlxsw_fan",
thermal,
&mlxsw_cooling_ops); &mlxsw_cooling_ops);
if (IS_ERR(cdev)) { if (IS_ERR(cdev)) {
err = PTR_ERR(cdev); err = PTR_ERR(cdev);
...@@ -423,13 +460,17 @@ int mlxsw_thermal_init(struct mlxsw_core *core, ...@@ -423,13 +460,17 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
thermal->cooling_levels[i] = max(MLXSW_THERMAL_SPEED_MIN_LEVEL, thermal->cooling_levels[i] = max(MLXSW_THERMAL_SPEED_MIN_LEVEL,
i); i);
thermal->polling_delay = bus_info->low_frequency ?
MLXSW_THERMAL_SLOW_POLL_INT :
MLXSW_THERMAL_POLL_INT;
thermal->tzdev = thermal_zone_device_register("mlxsw", thermal->tzdev = thermal_zone_device_register("mlxsw",
MLXSW_THERMAL_NUM_TRIPS, MLXSW_THERMAL_NUM_TRIPS,
MLXSW_THERMAL_TRIP_MASK, MLXSW_THERMAL_TRIP_MASK,
thermal, thermal,
&mlxsw_thermal_ops, &mlxsw_thermal_ops,
NULL, 0, NULL, 0,
MLXSW_THERMAL_POLL_INT); thermal->polling_delay);
if (IS_ERR(thermal->tzdev)) { if (IS_ERR(thermal->tzdev)) {
err = PTR_ERR(thermal->tzdev); err = PTR_ERR(thermal->tzdev);
dev_err(dev, "Failed to register thermal zone\n"); dev_err(dev, "Failed to register thermal zone\n");
......
...@@ -503,6 +503,7 @@ static int mlxsw_i2c_probe(struct i2c_client *client, ...@@ -503,6 +503,7 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
mlxsw_i2c->bus_info.device_kind = id->name; mlxsw_i2c->bus_info.device_kind = id->name;
mlxsw_i2c->bus_info.device_name = client->name; mlxsw_i2c->bus_info.device_name = client->name;
mlxsw_i2c->bus_info.dev = &client->dev; mlxsw_i2c->bus_info.dev = &client->dev;
mlxsw_i2c->bus_info.low_frequency = true;
mlxsw_i2c->dev = &client->dev; mlxsw_i2c->dev = &client->dev;
err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,
......
...@@ -7875,6 +7875,35 @@ static inline void mlxsw_reg_mfsl_unpack(char *payload, u8 tacho, ...@@ -7875,6 +7875,35 @@ static inline void mlxsw_reg_mfsl_unpack(char *payload, u8 tacho,
*p_tach_max = mlxsw_reg_mfsl_tach_max_get(payload); *p_tach_max = mlxsw_reg_mfsl_tach_max_get(payload);
} }
/* FORE - Fan Out of Range Event Register
* --------------------------------------
* This register reports the status of the controlled fans compared to the
* range defined by the MFSL register.
*/
#define MLXSW_REG_FORE_ID 0x9007
#define MLXSW_REG_FORE_LEN 0x0C
MLXSW_REG_DEFINE(fore, MLXSW_REG_FORE_ID, MLXSW_REG_FORE_LEN);
/* fan_under_limit
* Fan speed is below the low limit defined in MFSL register. Each bit relates
* to a single tachometer and indicates the specific tachometer reading is
* below the threshold.
* Access: RO
*/
MLXSW_ITEM32(reg, fore, fan_under_limit, 0x00, 16, 10);
static inline void mlxsw_reg_fore_unpack(char *payload, u8 tacho,
bool *fault)
{
u16 limit;
if (fault) {
limit = mlxsw_reg_fore_fan_under_limit_get(payload);
*fault = limit & BIT(tacho);
}
}
/* MTCAP - Management Temperature Capabilities /* MTCAP - Management Temperature Capabilities
* ------------------------------------------- * -------------------------------------------
* This register exposes the capabilities of the device and * This register exposes the capabilities of the device and
...@@ -8001,6 +8030,80 @@ static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp, ...@@ -8001,6 +8030,80 @@ static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp,
mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name); mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name);
} }
/* MTBR - Management Temperature Bulk Register
* -------------------------------------------
* This register is used for bulk temperature reading.
*/
#define MLXSW_REG_MTBR_ID 0x900F
#define MLXSW_REG_MTBR_BASE_LEN 0x10 /* base length, without records */
#define MLXSW_REG_MTBR_REC_LEN 0x04 /* record length */
#define MLXSW_REG_MTBR_REC_MAX_COUNT 47 /* firmware limitation */
#define MLXSW_REG_MTBR_LEN (MLXSW_REG_MTBR_BASE_LEN + \
MLXSW_REG_MTBR_REC_LEN * \
MLXSW_REG_MTBR_REC_MAX_COUNT)
MLXSW_REG_DEFINE(mtbr, MLXSW_REG_MTBR_ID, MLXSW_REG_MTBR_LEN);
/* reg_mtbr_base_sensor_index
* Base sensors index to access (0 - ASIC sensor, 1-63 - ambient sensors,
* 64-127 are mapped to the SFP+/QSFP modules sequentially).
* Access: Index
*/
MLXSW_ITEM32(reg, mtbr, base_sensor_index, 0x00, 0, 7);
/* reg_mtbr_num_rec
* Request: Number of records to read
* Response: Number of records read
* See above description for more details.
* Range 1..255
* Access: RW
*/
MLXSW_ITEM32(reg, mtbr, num_rec, 0x04, 0, 8);
/* reg_mtbr_rec_max_temp
* The highest measured temperature from the sensor.
* When the bit mte is cleared, the field max_temperature is reserved.
* Access: RO
*/
MLXSW_ITEM32_INDEXED(reg, mtbr, rec_max_temp, MLXSW_REG_MTBR_BASE_LEN, 16,
16, MLXSW_REG_MTBR_REC_LEN, 0x00, false);
/* reg_mtbr_rec_temp
* Temperature reading from the sensor. Reading is in 0..125 Celsius
* degrees units.
* Access: RO
*/
MLXSW_ITEM32_INDEXED(reg, mtbr, rec_temp, MLXSW_REG_MTBR_BASE_LEN, 0, 16,
MLXSW_REG_MTBR_REC_LEN, 0x00, false);
static inline void mlxsw_reg_mtbr_pack(char *payload, u8 base_sensor_index,
u8 num_rec)
{
MLXSW_REG_ZERO(mtbr, payload);
mlxsw_reg_mtbr_base_sensor_index_set(payload, base_sensor_index);
mlxsw_reg_mtbr_num_rec_set(payload, num_rec);
}
/* Error codes from temperatute reading */
enum mlxsw_reg_mtbr_temp_status {
MLXSW_REG_MTBR_NO_CONN = 0x8000,
MLXSW_REG_MTBR_NO_TEMP_SENS = 0x8001,
MLXSW_REG_MTBR_INDEX_NA = 0x8002,
MLXSW_REG_MTBR_BAD_SENS_INFO = 0x8003,
};
/* Base index for reading modules temperature */
#define MLXSW_REG_MTBR_BASE_MODULE_INDEX 64
static inline void mlxsw_reg_mtbr_temp_unpack(char *payload, int rec_ind,
u16 *p_temp, u16 *p_max_temp)
{
if (p_temp)
*p_temp = mlxsw_reg_mtbr_rec_temp_get(payload, rec_ind);
if (p_max_temp)
*p_max_temp = mlxsw_reg_mtbr_rec_max_temp_get(payload, rec_ind);
}
/* MCIA - Management Cable Info Access /* MCIA - Management Cable Info Access
* ----------------------------------- * -----------------------------------
* MCIA register is used to access the SFP+ and QSFP connector's EPROM. * MCIA register is used to access the SFP+ and QSFP connector's EPROM.
...@@ -8055,13 +8158,41 @@ MLXSW_ITEM32(reg, mcia, device_address, 0x04, 0, 16); ...@@ -8055,13 +8158,41 @@ MLXSW_ITEM32(reg, mcia, device_address, 0x04, 0, 16);
*/ */
MLXSW_ITEM32(reg, mcia, size, 0x08, 0, 16); MLXSW_ITEM32(reg, mcia, size, 0x08, 0, 16);
#define MLXSW_SP_REG_MCIA_EEPROM_SIZE 48 #define MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH 256
#define MLXSW_REG_MCIA_EEPROM_SIZE 48
#define MLXSW_REG_MCIA_I2C_ADDR_LOW 0x50
#define MLXSW_REG_MCIA_I2C_ADDR_HIGH 0x51
#define MLXSW_REG_MCIA_PAGE0_LO_OFF 0xa0
#define MLXSW_REG_MCIA_TH_ITEM_SIZE 2
#define MLXSW_REG_MCIA_TH_PAGE_NUM 3
#define MLXSW_REG_MCIA_PAGE0_LO 0
#define MLXSW_REG_MCIA_TH_PAGE_OFF 0x80
enum mlxsw_reg_mcia_eeprom_module_info_rev_id {
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_UNSPC = 0x00,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8436 = 0x01,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636 = 0x03,
};
enum mlxsw_reg_mcia_eeprom_module_info_id {
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP = 0x03,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP = 0x0C,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS = 0x0D,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 = 0x11,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD = 0x18,
};
enum mlxsw_reg_mcia_eeprom_module_info {
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE,
};
/* reg_mcia_eeprom /* reg_mcia_eeprom
* Bytes to read/write. * Bytes to read/write.
* Access: RW * Access: RW
*/ */
MLXSW_ITEM_BUF(reg, mcia, eeprom, 0x10, MLXSW_SP_REG_MCIA_EEPROM_SIZE); MLXSW_ITEM_BUF(reg, mcia, eeprom, 0x10, MLXSW_REG_MCIA_EEPROM_SIZE);
static inline void mlxsw_reg_mcia_pack(char *payload, u8 module, u8 lock, static inline void mlxsw_reg_mcia_pack(char *payload, u8 module, u8 lock,
u8 page_number, u16 device_addr, u8 page_number, u16 device_addr,
...@@ -9749,8 +9880,10 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { ...@@ -9749,8 +9880,10 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(mfsc), MLXSW_REG(mfsc),
MLXSW_REG(mfsm), MLXSW_REG(mfsm),
MLXSW_REG(mfsl), MLXSW_REG(mfsl),
MLXSW_REG(fore),
MLXSW_REG(mtcap), MLXSW_REG(mtcap),
MLXSW_REG(mtmp), MLXSW_REG(mtmp),
MLXSW_REG(mtbr),
MLXSW_REG(mcia), MLXSW_REG(mcia),
MLXSW_REG(mpat), MLXSW_REG(mpat),
MLXSW_REG(mpar), MLXSW_REG(mpar),
......
...@@ -2723,23 +2723,23 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -2723,23 +2723,23 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned int *p_read_size) unsigned int *p_read_size)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char eeprom_tmp[MLXSW_SP_REG_MCIA_EEPROM_SIZE]; char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
char mcia_pl[MLXSW_REG_MCIA_LEN]; char mcia_pl[MLXSW_REG_MCIA_LEN];
u16 i2c_addr; u16 i2c_addr;
int status; int status;
int err; int err;
size = min_t(u16, size, MLXSW_SP_REG_MCIA_EEPROM_SIZE); size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
if (offset < MLXSW_SP_EEPROM_PAGE_LENGTH && if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
offset + size > MLXSW_SP_EEPROM_PAGE_LENGTH) offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
/* Cross pages read, read until offset 256 in low page */ /* Cross pages read, read until offset 256 in low page */
size = MLXSW_SP_EEPROM_PAGE_LENGTH - offset; size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
i2c_addr = MLXSW_SP_I2C_ADDR_LOW; i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
if (offset >= MLXSW_SP_EEPROM_PAGE_LENGTH) { if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
i2c_addr = MLXSW_SP_I2C_ADDR_HIGH; i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
offset -= MLXSW_SP_EEPROM_PAGE_LENGTH; offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
} }
mlxsw_reg_mcia_pack(mcia_pl, mlxsw_sp_port->mapping.module, mlxsw_reg_mcia_pack(mcia_pl, mlxsw_sp_port->mapping.module,
...@@ -2760,55 +2760,37 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -2760,55 +2760,37 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
return 0; return 0;
} }
enum mlxsw_sp_eeprom_module_info_rev_id {
MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_UNSPC = 0x00,
MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8436 = 0x01,
MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636 = 0x03,
};
enum mlxsw_sp_eeprom_module_info_id {
MLXSW_SP_EEPROM_MODULE_INFO_ID_SFP = 0x03,
MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP = 0x0C,
MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS = 0x0D,
MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28 = 0x11,
};
enum mlxsw_sp_eeprom_module_info {
MLXSW_SP_EEPROM_MODULE_INFO_ID,
MLXSW_SP_EEPROM_MODULE_INFO_REV_ID,
MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
};
static int mlxsw_sp_get_module_info(struct net_device *netdev, static int mlxsw_sp_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo) struct ethtool_modinfo *modinfo)
{ {
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
u8 module_info[MLXSW_SP_EEPROM_MODULE_INFO_SIZE]; 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; u8 module_rev_id, module_id;
unsigned int read_size; unsigned int read_size;
int err; int err;
err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0, err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0, offset,
MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
module_info, &read_size); module_info, &read_size);
if (err) if (err)
return err; return err;
if (read_size < MLXSW_SP_EEPROM_MODULE_INFO_SIZE) if (read_size < offset)
return -EIO; return -EIO;
module_rev_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_REV_ID]; module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
module_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_ID]; module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
switch (module_id) { switch (module_id) {
case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP: case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
modinfo->type = ETH_MODULE_SFF_8436; modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
break; break;
case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS: case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28: case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
if (module_id == MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28 || if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
module_rev_id >= MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636) { module_rev_id >=
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
modinfo->type = ETH_MODULE_SFF_8636; modinfo->type = ETH_MODULE_SFF_8636;
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
} else { } else {
...@@ -2816,7 +2798,7 @@ static int mlxsw_sp_get_module_info(struct net_device *netdev, ...@@ -2816,7 +2798,7 @@ static int mlxsw_sp_get_module_info(struct net_device *netdev,
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
} }
break; break;
case MLXSW_SP_EEPROM_MODULE_INFO_ID_SFP: case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
modinfo->type = ETH_MODULE_SFF_8472; modinfo->type = ETH_MODULE_SFF_8472;
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
break; break;
......
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