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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-02-13 22:33:03 -08:00
commit 79e088239c
9 changed files with 617 additions and 65 deletions

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.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_THERMAL) += core_thermal.o
obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o

View File

@ -344,6 +344,7 @@ struct mlxsw_bus_info {
struct mlxsw_fw_rev fw_rev;
u8 vsd[MLXSW_CMD_BOARDINFO_VSD_LEN];
u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN];
u8 low_frequency;
};
struct mlxsw_hwmon;

View File

@ -0,0 +1,117 @@
// 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;
}

View File

@ -0,0 +1,10 @@
/* 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

View File

@ -7,8 +7,10 @@
#include <linux/sysfs.h>
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/sfp.h>
#include "core.h"
#include "core_env.h"
#define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
@ -30,6 +32,7 @@ struct mlxsw_hwmon {
struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
unsigned int attrs_count;
u8 sensor_count;
};
static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
@ -121,6 +124,27 @@ static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
}
static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
char fore_pl[MLXSW_REG_FORE_LEN];
bool fault;
int err;
err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(fore), fore_pl);
if (err) {
dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
return err;
}
mlxsw_reg_fore_unpack(fore_pl, mlwsw_hwmon_attr->type_index, &fault);
return sprintf(buf, "%u\n", fault);
}
static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@ -167,12 +191,160 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
return len;
}
static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
u16 temp;
u8 module;
int err;
module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
1);
err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
if (err) {
dev_err(dev, "Failed to query module temprature sensor\n");
return err;
}
mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
/* Update status and temperature cache. */
switch (temp) {
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;
break;
case MLXSW_REG_MTBR_BAD_SENS_INFO:
/* Untrusted cable is connected. Reading temperature from its
* sensor is faulty.
*/
temp = 0;
break;
default:
temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
break;
}
return sprintf(buf, "%u\n", temp);
}
static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
u8 module, fault;
u16 temp;
int err;
module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
1);
err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
if (err) {
dev_err(dev, "Failed to query module temprature sensor\n");
return err;
}
mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
/* Update status and temperature cache. */
switch (temp) {
case MLXSW_REG_MTBR_BAD_SENS_INFO:
/* Untrusted cable is connected. Reading temperature from its
* sensor is faulty.
*/
fault = 1;
break;
case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
case MLXSW_REG_MTBR_INDEX_NA:
default:
fault = 0;
break;
}
return sprintf(buf, "%u\n", fault);
}
static ssize_t
mlxsw_hwmon_module_temp_critical_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
int temp;
u8 module;
int err;
module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
SFP_TEMP_HIGH_WARN, &temp);
if (err) {
dev_err(dev, "Failed to query module temprature thresholds\n");
return err;
}
return sprintf(buf, "%u\n", temp);
}
static ssize_t
mlxsw_hwmon_module_temp_emergency_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
u8 module;
int temp;
int err;
module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
SFP_TEMP_HIGH_ALARM, &temp);
if (err) {
dev_err(dev, "Failed to query module temprature thresholds\n");
return err;
}
return sprintf(buf, "%u\n", temp);
}
static ssize_t
mlxsw_hwmon_module_temp_label_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
return sprintf(buf, "front panel %03u\n",
mlwsw_hwmon_attr->type_index);
}
enum mlxsw_hwmon_attr_type {
MLXSW_HWMON_ATTR_TYPE_TEMP,
MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
MLXSW_HWMON_ATTR_TYPE_PWM,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
};
static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
@ -209,6 +381,12 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"fan%u_input", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_FAN_FAULT:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_fault_show;
mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"fan%u_fault", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_PWM:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
@ -216,6 +394,40 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"pwm%u", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_module_temp_show;
mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_input", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT:
mlxsw_hwmon_attr->dev_attr.show =
mlxsw_hwmon_module_temp_fault_show;
mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_fault", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT:
mlxsw_hwmon_attr->dev_attr.show =
mlxsw_hwmon_module_temp_critical_show;
mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_crit", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG:
mlxsw_hwmon_attr->dev_attr.show =
mlxsw_hwmon_module_temp_emergency_show;
mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_emergency", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL:
mlxsw_hwmon_attr->dev_attr.show =
mlxsw_hwmon_module_temp_label_show;
mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_label", num + 1);
break;
default:
WARN_ON(1);
}
@ -233,7 +445,6 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
{
char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};
char mtmp_pl[MLXSW_REG_MTMP_LEN];
u8 sensor_count;
int i;
int err;
@ -242,8 +453,8 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
return err;
}
sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
for (i = 0; i < sensor_count; i++) {
mlxsw_hwmon->sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
for (i = 0; i < mlxsw_hwmon->sensor_count; i++) {
mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
err = mlxsw_reg_write(mlxsw_hwmon->core,
MLXSW_REG(mtmp), mtmp_pl);
@ -280,10 +491,14 @@ static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
num = 0;
for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
if (tacho_active & BIT(type_index))
if (tacho_active & BIT(type_index)) {
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
type_index, num);
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
type_index, num++);
}
}
num = 0;
for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
@ -295,6 +510,53 @@ static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
return 0;
}
static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon)
{
unsigned int module_count = mlxsw_core_max_ports(mlxsw_hwmon->core);
char pmlp_pl[MLXSW_REG_PMLP_LEN] = {0};
int i, index;
u8 width;
int err;
/* Add extra attributes for module temperature. Sensor index is
* assigned to sensor_count value, while all indexed before
* sensor_count are already utilized by the sensors connected through
* mtmp register by mlxsw_hwmon_temp_init().
*/
index = mlxsw_hwmon->sensor_count;
for (i = 1; i < module_count; i++) {
mlxsw_reg_pmlp_pack(pmlp_pl, i);
err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(pmlp),
pmlp_pl);
if (err) {
dev_err(mlxsw_hwmon->bus_info->dev, "Failed to read module index %d\n",
i);
return err;
}
width = mlxsw_reg_pmlp_width_get(pmlp_pl);
if (!width)
continue;
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, index,
index);
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
index, index);
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
index, index);
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
index, index);
mlxsw_hwmon_attr_add(mlxsw_hwmon,
MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
index, index);
index++;
}
return 0;
}
int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info,
struct mlxsw_hwmon **p_hwmon)
@ -317,6 +579,10 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
if (err)
goto err_fans_init;
err = mlxsw_hwmon_module_init(mlxsw_hwmon);
if (err)
goto err_temp_module_init;
mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
@ -333,6 +599,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
return 0;
err_hwmon_register:
err_temp_module_init:
err_fans_init:
err_temp_init:
kfree(mlxsw_hwmon);

View File

@ -13,7 +13,12 @@
#include "core.h"
#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_DUTY 255
/* Minimum and maximum fan allowed speed in percent: from 20% to 100%. Values
@ -26,9 +31,15 @@
#define MLXSW_THERMAL_SPEED_MAX (MLXSW_THERMAL_MAX_STATE * 2)
#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 {
int type;
int temp;
int hyst;
int min_state;
int max_state;
};
@ -36,32 +47,29 @@ struct mlxsw_thermal_trip {
static const struct mlxsw_thermal_trip default_thermal_trips[] = {
{ /* In range - 0-40% PWM */
.type = THERMAL_TRIP_ACTIVE,
.temp = 75000,
.temp = MLXSW_THERMAL_ASIC_TEMP_NORM,
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
.min_state = 0,
.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,
.temp = 85000,
.min_state = MLXSW_THERMAL_MAX_STATE,
.temp = MLXSW_THERMAL_ASIC_TEMP_HIGH,
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
.min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
.max_state = MLXSW_THERMAL_MAX_STATE,
},
{ /* Warning */
.type = THERMAL_TRIP_HOT,
.temp = 105000,
.temp = MLXSW_THERMAL_ASIC_TEMP_HOT,
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
.min_state = MLXSW_THERMAL_MAX_STATE,
.max_state = MLXSW_THERMAL_MAX_STATE,
},
{ /* Critical - soft poweroff */
.type = THERMAL_TRIP_CRITICAL,
.temp = MLXSW_THERMAL_MAX_TEMP,
.temp = MLXSW_THERMAL_ASIC_TEMP_CRIT,
.min_state = MLXSW_THERMAL_MAX_STATE,
.max_state = MLXSW_THERMAL_MAX_STATE,
}
@ -76,6 +84,7 @@ struct mlxsw_thermal {
struct mlxsw_core *core;
const struct mlxsw_bus_info *bus_info;
struct thermal_zone_device *tzdev;
int polling_delay;
struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
@ -103,6 +112,13 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
if (thermal->cdevs[i] == cdev)
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;
}
@ -172,7 +188,7 @@ static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev,
mutex_lock(&tzdev->lock);
if (mode == THERMAL_DEVICE_ENABLED)
tzdev->polling_delay = MLXSW_THERMAL_POLL_INT;
tzdev->polling_delay = thermal->polling_delay;
else
tzdev->polling_delay = 0;
@ -237,13 +253,31 @@ static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev,
struct mlxsw_thermal *thermal = tzdev->devdata;
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS ||
temp > MLXSW_THERMAL_MAX_TEMP)
temp > MLXSW_THERMAL_ASIC_TEMP_CRIT)
return -EINVAL;
thermal->trips[trip].temp = temp;
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 = {
.bind = mlxsw_thermal_bind,
.unbind = mlxsw_thermal_unbind,
@ -253,6 +287,8 @@ static struct thermal_zone_device_ops mlxsw_thermal_ops = {
.get_trip_type = mlxsw_thermal_get_trip_type,
.get_trip_temp = mlxsw_thermal_get_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,
@ -407,8 +443,9 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
if (pwm_active & BIT(i)) {
struct thermal_cooling_device *cdev;
cdev = thermal_cooling_device_register("Fan", thermal,
&mlxsw_cooling_ops);
cdev = thermal_cooling_device_register("mlxsw_fan",
thermal,
&mlxsw_cooling_ops);
if (IS_ERR(cdev)) {
err = PTR_ERR(cdev);
dev_err(dev, "Failed to register cooling device\n");
@ -423,13 +460,17 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
thermal->cooling_levels[i] = max(MLXSW_THERMAL_SPEED_MIN_LEVEL,
i);
thermal->polling_delay = bus_info->low_frequency ?
MLXSW_THERMAL_SLOW_POLL_INT :
MLXSW_THERMAL_POLL_INT;
thermal->tzdev = thermal_zone_device_register("mlxsw",
MLXSW_THERMAL_NUM_TRIPS,
MLXSW_THERMAL_TRIP_MASK,
thermal,
&mlxsw_thermal_ops,
NULL, 0,
MLXSW_THERMAL_POLL_INT);
thermal->polling_delay);
if (IS_ERR(thermal->tzdev)) {
err = PTR_ERR(thermal->tzdev);
dev_err(dev, "Failed to register thermal zone\n");

View File

@ -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_name = client->name;
mlxsw_i2c->bus_info.dev = &client->dev;
mlxsw_i2c->bus_info.low_frequency = true;
mlxsw_i2c->dev = &client->dev;
err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,

View File

@ -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);
}
/* 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
* -------------------------------------------
* 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,
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 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);
*/
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
* Bytes to read/write.
* 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,
u8 page_number, u16 device_addr,
@ -9749,8 +9880,10 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(mfsc),
MLXSW_REG(mfsm),
MLXSW_REG(mfsl),
MLXSW_REG(fore),
MLXSW_REG(mtcap),
MLXSW_REG(mtmp),
MLXSW_REG(mtbr),
MLXSW_REG(mcia),
MLXSW_REG(mpat),
MLXSW_REG(mpar),

View File

@ -2723,23 +2723,23 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned int *p_read_size)
{
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];
u16 i2c_addr;
int status;
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 &&
offset + size > MLXSW_SP_EEPROM_PAGE_LENGTH)
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_SP_EEPROM_PAGE_LENGTH - offset;
size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
i2c_addr = MLXSW_SP_I2C_ADDR_LOW;
if (offset >= MLXSW_SP_EEPROM_PAGE_LENGTH) {
i2c_addr = MLXSW_SP_I2C_ADDR_HIGH;
offset -= MLXSW_SP_EEPROM_PAGE_LENGTH;
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,
@ -2760,55 +2760,37 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
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,
struct ethtool_modinfo *modinfo)
{
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;
unsigned int read_size;
int err;
err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0,
MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0, offset,
module_info, &read_size);
if (err)
return err;
if (read_size < MLXSW_SP_EEPROM_MODULE_INFO_SIZE)
if (read_size < offset)
return -EIO;
module_rev_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_REV_ID];
module_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_ID];
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_SP_EEPROM_MODULE_INFO_ID_QSFP:
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_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28:
if (module_id == MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28 ||
module_rev_id >= MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636) {
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 {
@ -2816,7 +2798,7 @@ static int mlxsw_sp_get_module_info(struct net_device *netdev,
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
}
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->eeprom_len = ETH_MODULE_SFF_8472_LEN;
break;