diff --git a/Makefile b/Makefile index e25606177cf1f8..14e362fece9e11 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 5 PATCHLEVEL = 15 SUBLEVEL = 107 -EXTRAVERSION = -datum.2 +EXTRAVERSION = -datum.10 NAME = Trick or Treat # *DOCUMENTATION* diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d71ff7fc9d9a36..8a7620cbd12dba 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1122,6 +1122,20 @@ config SENSORS_MCP9902 This driver can also be built as a module. If so, the module will be called mcp9902. +config SENSORS_MCP9902_DATUM + tristate "Microchip MCP9902 and compatibles with Datum SPI/I2C clocks shorted" + depends on I2C + help + If you say yes here you get support for MCP9902. + The MCP9902 is a single channel temperature sensor + with internal thermal diode. + + This driver can also be built as a module. If so, the module + will be called mcp9902-datum. + + This driver is for use with the Datum Systems Inc. SPI/I2C shorted bus + management system that fixes the I2C and SPI bus clocks shorted together. + config SENSORS_MLXREG_FAN tristate "Mellanox FAN driver" depends on MELLANOX_PLATFORM @@ -1873,6 +1887,22 @@ config SENSORS_INA2XX This driver can also be built as a module. If so, the module will be called ina2xx. +config SENSORS_INA2XX_DATUM + tristate "Texas Instruments INA219 and compatibles with Datum SPI/I2C clocks shorted" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for INA219, INA220, INA226, + INA230, and INA231 power monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. + Default value for Rshunt is 10 mOhms. + This driver can also be built as a module. If so, the module + will be called ina2xx-datum. + This driver is for use with the Datum Systems Inc. SPI/I2C shorted bus + management system that fixes the I2C and SPI bus clocks shorted together. + config SENSORS_INA3221 tristate "Texas Instruments INA3221 Triple Power Monitor" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d93128d89485a4..3192f8208ff70e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o obj-$(CONFIG_SENSORS_INA209) += ina209.o obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o +obj-$(CONFIG_SENSORS_INA2XX_DATUM) += ina2xx_datum.o obj-$(CONFIG_SENSORS_INA3221) += ina3221.o obj-$(CONFIG_SENSORS_INTEL_M10_BMC_HWMON) += intel-m10-bmc-hwmon.o obj-$(CONFIG_SENSORS_IT87) += it87.o @@ -144,6 +145,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MCP9902) += mcp9902.o +obj-$(CONFIG_SENSORS_MCP9902_DATUM) += mcp9902_datum.o obj-$(CONFIG_SENSORS_TC654) += tc654.o obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o diff --git a/drivers/hwmon/ina2xx_datum.c b/drivers/hwmon/ina2xx_datum.c new file mode 100644 index 00000000000000..eb91dc936b1ce2 --- /dev/null +++ b/drivers/hwmon/ina2xx_datum.c @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Texas Instruments INA219, INA226 power monitor chips + * + * INA219: + * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: https://www.ti.com/product/ina219 + * + * INA220: + * Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: https://www.ti.com/product/ina220 + * + * INA226: + * Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: https://www.ti.com/product/ina226 + * + * INA230: + * Bi-directional Current/Power Monitor with I2C Interface + * Datasheet: https://www.ti.com/product/ina230 + * + * Copyright (C) 2012 Lothar Felten + * Thanks to Jan Volkering + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* common register definitions */ +#define INA2XX_CONFIG 0x00 +#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ +#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */ +#define INA2XX_POWER 0x03 /* readonly */ +#define INA2XX_CURRENT 0x04 /* readonly */ +#define INA2XX_CALIBRATION 0x05 + +/* INA226 register definitions */ +#define INA226_MASK_ENABLE 0x06 +#define INA226_ALERT_LIMIT 0x07 +#define INA226_DIE_ID 0xFF + +/* register count */ +#define INA219_REGISTERS 6 +#define INA226_REGISTERS 8 + +#define INA2XX_MAX_REGISTERS 8 + +/* settings - depend on use case */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ + +/* worst case is 68.10 ms (~14.6Hz, ina219) */ +#define INA2XX_CONVERSION_RATE 15 +#define INA2XX_MAX_DELAY 69 /* worst case delay in ms */ + +#define INA2XX_RSHUNT_DEFAULT 10000 + +/* bit mask for reading the averaging setting in the configuration register */ +#define INA226_AVG_RD_MASK 0x0E00 + +#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9) +#define INA226_SHIFT_AVG(val) ((val) << 9) + +/* bit number of alert functions in Mask/Enable Register */ +#define INA226_SHUNT_OVER_VOLTAGE_BIT 15 +#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14 +#define INA226_BUS_OVER_VOLTAGE_BIT 13 +#define INA226_BUS_UNDER_VOLTAGE_BIT 12 +#define INA226_POWER_OVER_LIMIT_BIT 11 + +/* bit mask for alert config bits of Mask/Enable Register */ +#define INA226_ALERT_CONFIG_MASK 0xFC00 +#define INA226_ALERT_FUNCTION_FLAG BIT(4) + +/* common attrs, ina226 attrs and NULL */ +#define INA2XX_MAX_ATTRIBUTE_GROUPS 3 + +/* + * Both bus voltage and shunt voltage conversion times for ina226 are set + * to 0b0100 on POR, which translates to 2200 microseconds in total. + */ +#define INA226_TOTAL_CONV_TIME_DEFAULT 2200 + +extern struct mutex datum_b53_spi_mutex; + +static struct regmap_config ina2xx_regmap_config = { + .reg_bits = 8, + .val_bits = 16, +}; + +enum ina2xx_ids { ina219, ina226 }; + +struct ina2xx_config { + u16 config_default; + int calibration_value; + int registers; + int shunt_div; + int bus_voltage_shift; + int bus_voltage_lsb; /* uV */ + int power_lsb_factor; +}; + +struct ina2xx_data { + const struct ina2xx_config *config; + + long rshunt; + long current_lsb_uA; + long power_lsb_uW; + struct mutex config_lock; + struct regmap *regmap; + + const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; +}; + +static const struct ina2xx_config ina2xx_config[] = { + [ina219] = { + .config_default = INA219_CONFIG_DEFAULT, + .calibration_value = 4096, + .registers = INA219_REGISTERS, + .shunt_div = 100, + .bus_voltage_shift = 3, + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .registers = INA226_REGISTERS, + .shunt_div = 400, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + }, +}; + +/* + * Available averaging rates for ina226. The indices correspond with + * the bit values expected by the chip (according to the ina226 datasheet, + * table 3 AVG bit settings, found at + * https://www.ti.com/lit/ds/symlink/ina226.pdf. + */ +static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + +static inline void spi_lock(void) { mutex_lock(&datum_b53_spi_mutex); } + +static inline void spi_unlock(struct device *dev) +{ + if(!pm_runtime_suspended(dev)) + usleep_range(100, 200); + mutex_unlock(&datum_b53_spi_mutex); +} + +static int ina226_reg_to_interval(u16 config) +{ + int avg = ina226_avg_tab[INA226_READ_AVG(config)]; + + /* + * Multiply the total conversion time by the number of averages. + * Return the result in milliseconds. + */ + return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000); +} + +/* + * Return the new, shifted AVG field value of CONFIG register, + * to use with regmap_update_bits + */ +static u16 ina226_interval_to_reg(int interval) +{ + int avg, avg_bits; + + avg = DIV_ROUND_CLOSEST(interval * 1000, + INA226_TOTAL_CONV_TIME_DEFAULT); + avg_bits = find_closest(avg, ina226_avg_tab, + ARRAY_SIZE(ina226_avg_tab)); + + return INA226_SHIFT_AVG(avg_bits); +} + +/* + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. + * According to datasheet (eq. 3) the best values are 2048 for + * ina226 and 4096 for ina219. They are hardcoded as calibration_value. + */ +static int ina2xx_calibrate(struct ina2xx_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + struct device *device = dev->parent->parent; + int retval; + + spi_lock(); + retval = regmap_write(data->regmap, INA2XX_CALIBRATION, + data->config->calibration_value); + + spi_unlock(device); + + return retval; +} + +/* + * Initialize the configuration and calibration registers. + */ +static int ina2xx_init(struct ina2xx_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + struct device *device = dev->parent->parent; + int ret; + + spi_lock(); + ret = regmap_write(data->regmap, INA2XX_CONFIG, + data->config->config_default); + spi_unlock(device); + if (ret < 0) + return ret; + + return ina2xx_calibrate(data); +} + +static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) +{ + struct device *device = dev->parent->parent->parent; + struct ina2xx_data *data = dev_get_drvdata(dev); + int ret, retry; + + dev_dbg(dev, "Starting register %d read\n", reg); + + for (retry = 5; retry; retry--) { + + spi_lock(); + ret = regmap_read(data->regmap, reg, regval); + spi_unlock(device); + + if (ret < 0) + return ret; + + dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval); + + /* + * If the current value in the calibration register is 0, the + * power and current registers will also remain at 0. In case + * the chip has been reset let's check the calibration + * register and reinitialize if needed. + * We do that extra read of the calibration register if there + * is some hint of a chip reset. + */ + if (*regval == 0) { + unsigned int cal; + + spi_lock(); + ret = regmap_read(data->regmap, INA2XX_CALIBRATION, + &cal); + spi_unlock(device); + + if (ret < 0) + return ret; + + if (cal == 0) { + dev_warn(dev, "chip not calibrated, reinitializing\n"); + + ret = ina2xx_init(data); + if (ret < 0) + return ret; + /* + * Let's make sure the power and current + * registers have been updated before trying + * again. + */ + msleep(INA2XX_MAX_DELAY); + continue; + } + } + return 0; + } + + /* + * If we're here then although all write operations succeeded, the + * chip still returns 0 in the calibration register. Nothing more we + * can do here. + */ + dev_err(dev, "unable to reinitialize the chip\n"); + return -ENODEV; +} + +static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) +{ + int val; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ + val = (s16)regval * 1000; + val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: + val = (regval >> data->config->bus_voltage_shift) + * data->config->bus_voltage_lsb; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_POWER: + val = regval * data->power_lsb_uW; + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ + val = (s16)regval * data->current_lsb_uA; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_CALIBRATION: + val = regval; + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ina2xx_value_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned int regval; + + int err = ina2xx_read_reg(dev, attr->index, ®val); + + if (err < 0) + return err; + + return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval)); +} + +static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval) +{ + int reg; + + switch (bit) { + case INA226_SHUNT_OVER_VOLTAGE_BIT: + case INA226_SHUNT_UNDER_VOLTAGE_BIT: + reg = INA2XX_SHUNT_VOLTAGE; + break; + case INA226_BUS_OVER_VOLTAGE_BIT: + case INA226_BUS_UNDER_VOLTAGE_BIT: + reg = INA2XX_BUS_VOLTAGE; + break; + case INA226_POWER_OVER_LIMIT_BIT: + reg = INA2XX_POWER; + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + return 0; + } + + return ina2xx_get_value(data, reg, regval); +} + +/* + * Turns alert limit values into register values. + * Opposite of the formula in ina2xx_get_value(). + */ +static s16 ina226_alert_to_reg(struct ina2xx_data *data, u8 bit, int val) +{ + switch (bit) { + case INA226_SHUNT_OVER_VOLTAGE_BIT: + case INA226_SHUNT_UNDER_VOLTAGE_BIT: + val *= data->config->shunt_div; + return clamp_val(val, SHRT_MIN, SHRT_MAX); + case INA226_BUS_OVER_VOLTAGE_BIT: + case INA226_BUS_UNDER_VOLTAGE_BIT: + val = (val * 1000) << data->config->bus_voltage_shift; + val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); + return clamp_val(val, 0, SHRT_MAX); + case INA226_POWER_OVER_LIMIT_BIT: + val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); + return clamp_val(val, 0, USHRT_MAX); + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + return 0; + } +} + +static ssize_t ina226_alert_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct device *device = dev->parent->parent->parent; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + int regval; + int val = 0; + int ret; + + mutex_lock(&data->config_lock); + spi_lock(); + ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val); + if (ret) + goto abort; + + if (regval & BIT(attr->index)) { + ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, ®val); + if (ret) + goto abort; + val = ina226_reg_to_alert(data, attr->index, regval); + } + + ret = sysfs_emit(buf, "%d\n", val); +abort: + spi_unlock(device); + mutex_unlock(&data->config_lock); + return ret; +} + +static ssize_t ina226_alert_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct device *device = dev->parent->parent->parent; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + /* + * Clear all alerts first to avoid accidentally triggering ALERT pin + * due to register write sequence. Then, only enable the alert + * if the value is non-zero. + */ + mutex_lock(&data->config_lock); + spi_lock(); + ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + INA226_ALERT_CONFIG_MASK, 0); + if (ret < 0) + goto abort; + + + ret = regmap_write(data->regmap, INA226_ALERT_LIMIT, + ina226_alert_to_reg(data, attr->index, val)); + + if (ret < 0) + goto abort; + + if (val != 0) { + ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + INA226_ALERT_CONFIG_MASK, + BIT(attr->index)); + if (ret < 0) + goto abort; + } + + ret = count; +abort: + spi_unlock(device); + mutex_unlock(&data->config_lock); + return ret; +} + +static ssize_t ina226_alarm_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct device *device = dev->parent->parent->parent; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + int regval; + int alarm = 0; + int ret; + + spi_lock(); + ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val); + spi_unlock(device); + if (ret) + return ret; + + alarm = (regval & BIT(attr->index)) && + (regval & INA226_ALERT_FUNCTION_FLAG); + return sysfs_emit(buf, "%d\n", alarm); +} + +/* + * In order to keep calibration register value fixed, the product + * of current_lsb and shunt_resistor should also be fixed and equal + * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order + * to keep the scale. + */ +static int ina2xx_set_shunt(struct ina2xx_data *data, long val) +{ + unsigned int dividend = DIV_ROUND_CLOSEST(1000000000, + data->config->shunt_div); + if (val <= 0 || val > dividend) + return -EINVAL; + + mutex_lock(&data->config_lock); + data->rshunt = val; + data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val); + data->power_lsb_uW = data->config->power_lsb_factor * + data->current_lsb_uA; + mutex_unlock(&data->config_lock); + + return 0; +} + +static ssize_t ina2xx_shunt_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%li\n", data->rshunt); +} + +static ssize_t ina2xx_shunt_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int status; + struct ina2xx_data *data = dev_get_drvdata(dev); + + status = kstrtoul(buf, 10, &val); + if (status < 0) + return status; + + status = ina2xx_set_shunt(data, val); + if (status < 0) + return status; + return count; +} + +static ssize_t ina226_interval_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct device *device = dev->parent->parent->parent; + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned long val; + int status; + + status = kstrtoul(buf, 10, &val); + if (status < 0) + return status; + + if (val > INT_MAX || val == 0) + return -EINVAL; + + spi_lock(); + status = regmap_update_bits(data->regmap, INA2XX_CONFIG, + INA226_AVG_RD_MASK, + ina226_interval_to_reg(val)); + + spi_unlock(device); + if (status < 0) + return status; + + return count; +} + +static ssize_t ina226_interval_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct device *device = dev->parent->parent->parent; + struct ina2xx_data *data = dev_get_drvdata(dev); + int status; + unsigned int regval; + + spi_lock(); + status = regmap_read(data->regmap, INA2XX_CONFIG, ®val); + spi_unlock(device); + if (status) + return status; + + return sysfs_emit(buf, "%d\n", ina226_reg_to_interval(regval)); +} + +/* shunt voltage */ +static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE); +/* shunt voltage over/under voltage alert setting and alarm */ +static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert, + INA226_SHUNT_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert, + INA226_SHUNT_UNDER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm, + INA226_SHUNT_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm, + INA226_SHUNT_UNDER_VOLTAGE_BIT); + +/* bus voltage */ +static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE); +/* bus voltage over/under voltage alert setting and alarm */ +static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert, + INA226_BUS_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert, + INA226_BUS_UNDER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm, + INA226_BUS_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm, + INA226_BUS_UNDER_VOLTAGE_BIT); + +/* calculated current */ +static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT); + +/* calculated power */ +static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER); +/* over-limit power alert setting and alarm */ +static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert, + INA226_POWER_OVER_LIMIT_BIT); +static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm, + INA226_POWER_OVER_LIMIT_BIT); + +/* shunt resistance */ +static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION); + +/* update interval (ina226 only) */ +static SENSOR_DEVICE_ATTR_RW(update_interval, ina226_interval, 0); + +/* pointers to created device attributes */ +static struct attribute *ina2xx_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_shunt_resistor.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ina2xx_group = { + .attrs = ina2xx_attrs, +}; + +static struct attribute *ina226_attrs[] = { + &sensor_dev_attr_in0_crit.dev_attr.attr, + &sensor_dev_attr_in0_lcrit.dev_attr.attr, + &sensor_dev_attr_in0_crit_alarm.dev_attr.attr, + &sensor_dev_attr_in0_lcrit_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit.dev_attr.attr, + &sensor_dev_attr_in1_lcrit.dev_attr.attr, + &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_in1_lcrit_alarm.dev_attr.attr, + &sensor_dev_attr_power1_crit.dev_attr.attr, + &sensor_dev_attr_power1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_update_interval.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ina226_group = { + .attrs = ina226_attrs, +}; + +static const struct i2c_device_id ina2xx_id[]; + +static int ina2xx_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ina2xx_data *data; + struct device *hwmon_dev; + u32 val; + int ret, group = 0; + enum ina2xx_ids chip; + + if (client->dev.of_node) + chip = (enum ina2xx_ids)of_device_get_match_data(&client->dev); + else + chip = i2c_match_id(ina2xx_id, client)->driver_data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* set the device type */ + data->config = &ina2xx_config[chip]; + mutex_init(&data->config_lock); + + if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { + struct ina2xx_platform_data *pdata = dev_get_platdata(dev); + + if (pdata) + val = pdata->shunt_uohms; + else + val = INA2XX_RSHUNT_DEFAULT; + } + + pm_runtime_set_autosuspend_delay(dev->parent->parent, 0); + ina2xx_set_shunt(data, val); + + ina2xx_regmap_config.max_register = data->config->registers; + + data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + ret = ina2xx_init(data); + if (ret < 0) { + dev_err(dev, "error configuring the device: %d\n", ret); + return -ENODEV; + } + + data->groups[group++] = &ina2xx_group; + if (chip == ina226) + data->groups[group++] = &ina226_group; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", + client->name, data->rshunt); + + return 0; +} + +static const struct i2c_device_id ina2xx_id[] = { + { "ina219-datum", ina219 }, + { "ina220-datum", ina219 }, + { "ina226-datum", ina226 }, + { "ina230-datum", ina226 }, + { "ina231-datum", ina226 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina2xx_id); + +static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + { + .compatible = "ti,ina219-datum", + .data = (void *)ina219 + }, + { + .compatible = "ti,ina220-datum", + .data = (void *)ina219 + }, + { + .compatible = "ti,ina226-datum", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina230-datum", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina231-datum", + .data = (void *)ina226 + }, + { }, +}; +MODULE_DEVICE_TABLE(of, ina2xx_of_match); + +static struct i2c_driver ina2xx_driver = { + .driver = { + .name = "ina2xx-datum", + .of_match_table = of_match_ptr(ina2xx_of_match), + }, + .probe_new = ina2xx_probe, + .id_table = ina2xx_id, +}; + +module_i2c_driver(ina2xx_driver); + +MODULE_AUTHOR("Lothar Felten "); +MODULE_DESCRIPTION("ina2xx driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/mcp9902_datum.c b/drivers/hwmon/mcp9902_datum.c new file mode 100644 index 00000000000000..0e72ccb6af3296 --- /dev/null +++ b/drivers/hwmon/mcp9902_datum.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mcp9902.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * + * Based on the lm90 driver. The MCP9902 is a sensor chip made by Microchip. + * It reports up to two temperatures (its own plus up to + * one external one). Complete datasheet can be + * obtained from Microchips's website at: + * http://ww1.microchip.com/downloads/en/DeviceDoc/20005382C.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const unsigned short normal_i2c[] = { + 0x1C, 0x4C, I2C_CLIENT_END }; + +/* + * The MCP9902 registers + */ + +#define MCP9902_REG_R_CHIP_ID 0xFD +#define MCP9902_REG_R_MAN_ID 0xFE +#define MCP9902_REG_R_REV_ID 0xFF +#define MCP9902_REG_R_CONFIG 0x03 +#define MCP9902_REG_W_CONFIG 0x09 +#define MCP9902_REG_R_CONVRATE 0x04 +#define MCP9902_REG_W_CONVRATE 0x0A +#define MCP9902_REG_R_STATUS 0x02 +#define MCP9902_REG_R_LOCAL_TEMP 0x00 +#define MCP9902_REG_R_LOCAL_TEMP_FRACTION 0x29 +#define MCP9902_REG_R_REMOTE_TEMP 0x01 +#define MCP9902_REG_R_REMOTE_TEMP_FRACTION 0x10 +#define MCP9902_REG_R_LOCAL_HIGH 0x05 +#define MCP9902_REG_W_LOCAL_HIGH 0x0B +#define MCP9902_REG_R_LOCAL_LOW 0x06 +#define MCP9902_REG_W_LOCAL_LOW 0x0C +#define MCP9902_REG_R_REMOTE_HIGH 0x07 +#define MCP9902_REG_W_REMOTE_HIGH 0x0D +#define MCP9902_REG_R_REMOTE_LOW 0x08 +#define MCP9902_REG_W_REMOTE_LOW 0x0E +#define MCP9902_REG_R_REMOTE_CRIT 0x19 +#define MCP9902_REG_W_REMOTE_CRIT 0x19 +#define MCP9902_REG_R_LOCAL_CRIT 0x20 +#define MCP9902_REG_W_LOCAL_CRIT 0x20 +#define MCP9902_REG_R_TCRIT_HYST 0x21 +#define MCP9902_REG_W_TCRIT_HYST 0x21 + +/* + * I2C3-SPI2 Clocks Shorted Mutex + */ + +extern struct mutex datum_b53_spi_mutex; + +static inline void spi_lock(void) { mutex_lock(&datum_b53_spi_mutex); } + +static inline void spi_unlock(struct device *dev) +{ + if(!pm_runtime_suspended(dev)) + usleep_range(100, 200); + mutex_unlock(&datum_b53_spi_mutex); +} + +/* + * Conversions + */ + +static int temp_from_reg(int val, int fraction) +{ + /* milli-degree C */ + return (((val - 64) * 1000) + (((fraction & 0xE0) >> 5) * 125)); +} + +static int temp_to_reg(int val) +{ + return (val + 64) / 1000; +} + +enum temp_index { + t_input1 = 0, + t_input1_fraction, + t_input2, + t_input2_fraction, + t_low1, + t_high1, + t_crit1, + t_low2, + t_high2, + t_crit2, + t_hyst, + t_num_regs +}; + +/* + * Client data (each client gets its own) + */ + +struct mcp9902_data { + struct i2c_client *client; + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 temp[t_num_regs]; /* index with enum temp_index */ + u8 alarms; +}; + +static const u8 regs_read[t_num_regs] = { + [t_input1] = MCP9902_REG_R_LOCAL_TEMP, + [t_input1_fraction] = MCP9902_REG_R_LOCAL_TEMP_FRACTION, + [t_input2] = MCP9902_REG_R_REMOTE_TEMP, + [t_input2_fraction] = MCP9902_REG_R_REMOTE_TEMP_FRACTION, + [t_low1] = MCP9902_REG_R_LOCAL_LOW, + [t_high1] = MCP9902_REG_R_LOCAL_HIGH, + [t_crit1] = MCP9902_REG_R_LOCAL_CRIT, + [t_low2] = MCP9902_REG_R_REMOTE_LOW, + [t_high2] = MCP9902_REG_R_REMOTE_HIGH, + [t_crit2] = MCP9902_REG_R_REMOTE_CRIT, + [t_hyst] = MCP9902_REG_R_TCRIT_HYST, +}; + +static const u8 regs_write[t_num_regs] = { + [t_low1] = MCP9902_REG_W_LOCAL_LOW, + [t_high1] = MCP9902_REG_W_LOCAL_HIGH, + [t_crit1] = MCP9902_REG_W_LOCAL_CRIT, + [t_low2] = MCP9902_REG_W_REMOTE_LOW, + [t_high2] = MCP9902_REG_W_REMOTE_HIGH, + [t_crit2] = MCP9902_REG_W_REMOTE_CRIT, + [t_hyst] = MCP9902_REG_W_TCRIT_HYST, +}; + +static struct mcp9902_data *mcp9902_update_device(struct device *dev) +{ + struct mcp9902_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct i2c_adapter *adapter = client->adapter; + int i; + int j; + + mutex_lock(&data->update_lock); + spi_lock(); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(&client->dev, "Updating mcp9902 data.\n"); + // printk("Updating mcp9902 data.\n"); + + for (j = 0; j < 20; j++) + { + for (i = 0; i < t_num_regs; i++) + data->temp[i] = i2c_smbus_read_byte_data(client, + regs_read[i]); + data->alarms = i2c_smbus_read_byte_data(client, + MCP9902_REG_R_STATUS); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + spi_unlock(adapter->dev.parent); + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Sysfs stuff + */ + +static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct mcp9902_data *data = mcp9902_update_device(dev); + u8 fraction_reg; + + switch(attr->index) + { + case t_input1: + fraction_reg = data->temp[t_input1_fraction]; + break; + case t_input2: + fraction_reg = data->temp[t_input2_fraction]; + break; + default: + fraction_reg = 0; + break; + } + return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index], fraction_reg)); +} + +static ssize_t temp_store(struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct mcp9902_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct i2c_adapter *adapter = client->adapter; + long val; + int err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->temp[attr->index] = temp_to_reg(val); + spi_lock(); + i2c_smbus_write_byte_data(client, regs_write[attr->index], + data->temp[attr->index]); + spi_unlock(adapter->dev.parent); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mcp9902_data *data = mcp9902_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} + +static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int bitnr = to_sensor_dev_attr(attr)->index; + struct mcp9902_data *data = mcp9902_update_device(dev); + return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +} + +static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1); +static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_low1); +static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_high1); +static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit1); +static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2); +static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2); +static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2); +static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2); +static SENSOR_DEVICE_ATTR_RW(temp_crit_hyst, temp, t_hyst); + +static DEVICE_ATTR_RO(alarms); +static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 0); +static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1); +static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); +static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); +static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); +static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 5); +static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6); + +static struct attribute *mcp9902_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp_crit_hyst.dev_attr.attr, + + &dev_attr_alarms.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(mcp9902); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mcp9902_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + u8 man_id, chip_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* identification */ + spi_lock(); + man_id = i2c_smbus_read_byte_data(client, MCP9902_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, MCP9902_REG_R_CHIP_ID); + spi_unlock(adapter->dev.parent); + if (man_id != 0x5D || chip_id != 0x04) { + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", + man_id, chip_id); + return -ENODEV; + } + + strlcpy(info->type, "mcp9902", I2C_NAME_SIZE); + + return 0; +} + +static void mcp9902_init_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + + pm_runtime_set_autosuspend_delay(adapter->dev.parent, 0); + + /* + * Start the conversions. + */ + spi_lock(); + i2c_smbus_write_byte_data(client, MCP9902_REG_W_CONVRATE, 5); /* 2 Hz */ + i2c_smbus_write_byte_data(client, MCP9902_REG_W_CONFIG, 0x9F); /* run - extended temp */ + spi_unlock(adapter->dev.parent); +} + +static int mcp9902_probe(struct i2c_client *new_client, + const struct i2c_device_id *id) +{ + struct mcp9902_data *data; + struct device *hwmon_dev; + + data = devm_kzalloc(&new_client->dev, sizeof(struct mcp9902_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = new_client; + mutex_init(&data->update_lock); + + /* Initialize the MCP9902 chip */ + mcp9902_init_client(new_client); + + hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, + new_client->name, + data, + mcp9902_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id mcp9902_id[] = { + { "mcp9902-datum", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp9902_id); + +#ifdef CONFIG_OF +static const struct of_device_id mcp9902_of_match[] = { + { .compatible = "microchip,mcp9902-datum", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mcp9902_of_match); +#endif + +static struct i2c_driver mcp9902_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "mcp9902-datum", + .of_match_table = of_match_ptr(mcp9902_of_match), + }, + .probe = mcp9902_probe, + .id_table = mcp9902_id, + .detect = mcp9902_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mcp9902_driver); + +MODULE_AUTHOR("Mark Carlin "); +MODULE_DESCRIPTION("MCP9902 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/b53/Kconfig b/drivers/net/dsa/b53/Kconfig index 90b525160b7112..47c5ea7ffaf7dc 100644 --- a/drivers/net/dsa/b53/Kconfig +++ b/drivers/net/dsa/b53/Kconfig @@ -16,6 +16,14 @@ config B53_SPI_DRIVER help Select to enable support for registering switches configured through SPI. +config B53_SPI_DATUM_DRIVER + tristate "B53 SPI connected switch driver with Datum SPI/I2C clocks shorted" + depends on B53 && SPI + help + Select to enable support for registering switches configured through SPI. + This driver is for use with the Datum Systems Inc. SPI/I2C shorted bus + management system that fixes the I2C and SPI bus clocks shorted together. + config B53_MDIO_DRIVER tristate "B53 MDIO connected switch driver" depends on B53 diff --git a/drivers/net/dsa/b53/Makefile b/drivers/net/dsa/b53/Makefile index b1be13023ae4c7..9d7d09343209c5 100644 --- a/drivers/net/dsa/b53/Makefile +++ b/drivers/net/dsa/b53/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_B53) += b53_common.o obj-$(CONFIG_B53_SPI_DRIVER) += b53_spi.o +obj-$(CONFIG_B53_SPI_DATUM_DRIVER) += b53_spi_datum.o obj-$(CONFIG_B53_MDIO_DRIVER) += b53_mdio.o obj-$(CONFIG_B53_MMAP_DRIVER) += b53_mmap.o obj-$(CONFIG_B53_SRAB_DRIVER) += b53_srab.o diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 66f6e7d33d6193..f2a23811c6b68c 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -38,134 +39,8 @@ struct b53_mib_desc { const char *name; }; -/* BCM5365 MIB counters */ -static const struct b53_mib_desc b53_mibs_65[] = { - { 8, 0x00, "TxOctets" }, - { 4, 0x08, "TxDropPkts" }, - { 4, 0x10, "TxBroadcastPkts" }, - { 4, 0x14, "TxMulticastPkts" }, - { 4, 0x18, "TxUnicastPkts" }, - { 4, 0x1c, "TxCollisions" }, - { 4, 0x20, "TxSingleCollision" }, - { 4, 0x24, "TxMultipleCollision" }, - { 4, 0x28, "TxDeferredTransmit" }, - { 4, 0x2c, "TxLateCollision" }, - { 4, 0x30, "TxExcessiveCollision" }, - { 4, 0x38, "TxPausePkts" }, - { 8, 0x44, "RxOctets" }, - { 4, 0x4c, "RxUndersizePkts" }, - { 4, 0x50, "RxPausePkts" }, - { 4, 0x54, "Pkts64Octets" }, - { 4, 0x58, "Pkts65to127Octets" }, - { 4, 0x5c, "Pkts128to255Octets" }, - { 4, 0x60, "Pkts256to511Octets" }, - { 4, 0x64, "Pkts512to1023Octets" }, - { 4, 0x68, "Pkts1024to1522Octets" }, - { 4, 0x6c, "RxOversizePkts" }, - { 4, 0x70, "RxJabbers" }, - { 4, 0x74, "RxAlignmentErrors" }, - { 4, 0x78, "RxFCSErrors" }, - { 8, 0x7c, "RxGoodOctets" }, - { 4, 0x84, "RxDropPkts" }, - { 4, 0x88, "RxUnicastPkts" }, - { 4, 0x8c, "RxMulticastPkts" }, - { 4, 0x90, "RxBroadcastPkts" }, - { 4, 0x94, "RxSAChanges" }, - { 4, 0x98, "RxFragments" }, -}; - -#define B53_MIBS_65_SIZE ARRAY_SIZE(b53_mibs_65) - -/* BCM63xx MIB counters */ -static const struct b53_mib_desc b53_mibs_63xx[] = { - { 8, 0x00, "TxOctets" }, - { 4, 0x08, "TxDropPkts" }, - { 4, 0x0c, "TxQoSPkts" }, - { 4, 0x10, "TxBroadcastPkts" }, - { 4, 0x14, "TxMulticastPkts" }, - { 4, 0x18, "TxUnicastPkts" }, - { 4, 0x1c, "TxCollisions" }, - { 4, 0x20, "TxSingleCollision" }, - { 4, 0x24, "TxMultipleCollision" }, - { 4, 0x28, "TxDeferredTransmit" }, - { 4, 0x2c, "TxLateCollision" }, - { 4, 0x30, "TxExcessiveCollision" }, - { 4, 0x38, "TxPausePkts" }, - { 8, 0x3c, "TxQoSOctets" }, - { 8, 0x44, "RxOctets" }, - { 4, 0x4c, "RxUndersizePkts" }, - { 4, 0x50, "RxPausePkts" }, - { 4, 0x54, "Pkts64Octets" }, - { 4, 0x58, "Pkts65to127Octets" }, - { 4, 0x5c, "Pkts128to255Octets" }, - { 4, 0x60, "Pkts256to511Octets" }, - { 4, 0x64, "Pkts512to1023Octets" }, - { 4, 0x68, "Pkts1024to1522Octets" }, - { 4, 0x6c, "RxOversizePkts" }, - { 4, 0x70, "RxJabbers" }, - { 4, 0x74, "RxAlignmentErrors" }, - { 4, 0x78, "RxFCSErrors" }, - { 8, 0x7c, "RxGoodOctets" }, - { 4, 0x84, "RxDropPkts" }, - { 4, 0x88, "RxUnicastPkts" }, - { 4, 0x8c, "RxMulticastPkts" }, - { 4, 0x90, "RxBroadcastPkts" }, - { 4, 0x94, "RxSAChanges" }, - { 4, 0x98, "RxFragments" }, - { 4, 0xa0, "RxSymbolErrors" }, - { 4, 0xa4, "RxQoSPkts" }, - { 8, 0xa8, "RxQoSOctets" }, - { 4, 0xb0, "Pkts1523to2047Octets" }, - { 4, 0xb4, "Pkts2048to4095Octets" }, - { 4, 0xb8, "Pkts4096to8191Octets" }, - { 4, 0xbc, "Pkts8192to9728Octets" }, - { 4, 0xc0, "RxDiscarded" }, -}; - -#define B53_MIBS_63XX_SIZE ARRAY_SIZE(b53_mibs_63xx) - -/* MIB counters */ -static const struct b53_mib_desc b53_mibs[] = { - { 8, 0x00, "TxOctets" }, - { 4, 0x08, "TxDropPkts" }, - { 4, 0x10, "TxBroadcastPkts" }, - { 4, 0x14, "TxMulticastPkts" }, - { 4, 0x18, "TxUnicastPkts" }, - { 4, 0x1c, "TxCollisions" }, - { 4, 0x20, "TxSingleCollision" }, - { 4, 0x24, "TxMultipleCollision" }, - { 4, 0x28, "TxDeferredTransmit" }, - { 4, 0x2c, "TxLateCollision" }, - { 4, 0x30, "TxExcessiveCollision" }, - { 4, 0x38, "TxPausePkts" }, - { 8, 0x50, "RxOctets" }, - { 4, 0x58, "RxUndersizePkts" }, - { 4, 0x5c, "RxPausePkts" }, - { 4, 0x60, "Pkts64Octets" }, - { 4, 0x64, "Pkts65to127Octets" }, - { 4, 0x68, "Pkts128to255Octets" }, - { 4, 0x6c, "Pkts256to511Octets" }, - { 4, 0x70, "Pkts512to1023Octets" }, - { 4, 0x74, "Pkts1024to1522Octets" }, - { 4, 0x78, "RxOversizePkts" }, - { 4, 0x7c, "RxJabbers" }, - { 4, 0x80, "RxAlignmentErrors" }, - { 4, 0x84, "RxFCSErrors" }, - { 8, 0x88, "RxGoodOctets" }, - { 4, 0x90, "RxDropPkts" }, - { 4, 0x94, "RxUnicastPkts" }, - { 4, 0x98, "RxMulticastPkts" }, - { 4, 0x9c, "RxBroadcastPkts" }, - { 4, 0xa0, "RxSAChanges" }, - { 4, 0xa4, "RxFragments" }, - { 4, 0xa8, "RxJumboPkts" }, - { 4, 0xac, "RxSymbolErrors" }, - { 4, 0xc0, "RxDiscarded" }, -}; - -#define B53_MIBS_SIZE ARRAY_SIZE(b53_mibs) - -static const struct b53_mib_desc b53_mibs_58xx[] = { +/* BCM53134 MIB counters */ +static const struct b53_mib_desc b53_mibs_134[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x0c, "TxQPKTQ0" }, @@ -222,105 +97,7 @@ static const struct b53_mib_desc b53_mibs_58xx[] = { { 4, 0xe4, "TxPkts1024toMaxPktOcets" }, }; -#define B53_MIBS_58XX_SIZE ARRAY_SIZE(b53_mibs_58xx) - -static int b53_do_vlan_op(struct b53_device *dev, u8 op) -{ - unsigned int i; - - b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op); - - for (i = 0; i < 10; i++) { - u8 vta; - - b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta); - if (!(vta & VTA_START_CMD)) - return 0; - - usleep_range(100, 200); - } - - return -EIO; -} - -static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, - struct b53_vlan *vlan) -{ - if (is5325(dev)) { - u32 entry = 0; - - if (vlan->members) { - entry = ((vlan->untag & VA_UNTAG_MASK_25) << - VA_UNTAG_S_25) | vlan->members; - if (dev->core_rev >= 3) - entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S; - else - entry |= VA_VALID_25; - } - - b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry); - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | - VTA_RW_STATE_WR | VTA_RW_OP_EN); - } else if (is5365(dev)) { - u16 entry = 0; - - if (vlan->members) - entry = ((vlan->untag & VA_UNTAG_MASK_65) << - VA_UNTAG_S_65) | vlan->members | VA_VALID_65; - - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry); - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | - VTA_RW_STATE_WR | VTA_RW_OP_EN); - } else { - b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); - b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], - (vlan->untag << VTE_UNTAG_S) | vlan->members); - - b53_do_vlan_op(dev, VTA_CMD_WRITE); - } - - dev_dbg(dev->ds->dev, "VID: %d, members: 0x%04x, untag: 0x%04x\n", - vid, vlan->members, vlan->untag); -} - -static void b53_get_vlan_entry(struct b53_device *dev, u16 vid, - struct b53_vlan *vlan) -{ - if (is5325(dev)) { - u32 entry = 0; - - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | - VTA_RW_STATE_RD | VTA_RW_OP_EN); - b53_read32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, &entry); - - if (dev->core_rev >= 3) - vlan->valid = !!(entry & VA_VALID_25_R4); - else - vlan->valid = !!(entry & VA_VALID_25); - vlan->members = entry & VA_MEMBER_MASK; - vlan->untag = (entry >> VA_UNTAG_S_25) & VA_UNTAG_MASK_25; - - } else if (is5365(dev)) { - u16 entry = 0; - - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | - VTA_RW_STATE_WR | VTA_RW_OP_EN); - b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, &entry); - - vlan->valid = !!(entry & VA_VALID_65); - vlan->members = entry & VA_MEMBER_MASK; - vlan->untag = (entry >> VA_UNTAG_S_65) & VA_UNTAG_MASK_65; - } else { - u32 entry = 0; - - b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); - b53_do_vlan_op(dev, VTA_CMD_READ); - b53_read32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], &entry); - vlan->members = entry & VTE_MEMBERS; - vlan->untag = (entry >> VTE_UNTAG_S) & VTE_MEMBERS; - vlan->valid = true; - } -} +#define B53_MIBS_134_SIZE ARRAY_SIZE(b53_mibs_134) static void b53_set_forwarding(struct b53_device *dev, int enable) { @@ -349,110 +126,18 @@ static void b53_set_forwarding(struct b53_device *dev, int enable) b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); } -static void b53_enable_vlan(struct b53_device *dev, int port, bool enable, - bool enable_filtering) +static int b53_set_jumbo(struct b53_device *dev, int port, bool enable, bool allow_10_100) { - u8 mgmt, vc0, vc1, vc4 = 0, vc5; - - b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0); - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1); - - if (is5325(dev) || is5365(dev)) { - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5); - } else if (is63xx(dev)) { - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4); - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5); - } else { - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4); - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); - } - - if (enable) { - vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; - vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN; - vc4 &= ~VC4_ING_VID_CHECK_MASK; - if (enable_filtering) { - vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; - vc5 |= VC5_DROP_VTABLE_MISS; - } else { - vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; - vc5 &= ~VC5_DROP_VTABLE_MISS; - } - - if (is5325(dev)) - vc0 &= ~VC0_RESERVED_1; - - if (is5325(dev) || is5365(dev)) - vc1 |= VC1_RX_MCST_TAG_EN; - - } else { - vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID); - vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN); - vc4 &= ~VC4_ING_VID_CHECK_MASK; - vc5 &= ~VC5_DROP_VTABLE_MISS; - - if (is5325(dev) || is5365(dev)) - vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; - else - vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S; - - if (is5325(dev) || is5365(dev)) - vc1 &= ~VC1_RX_MCST_TAG_EN; - } - - if (!is5325(dev) && !is5365(dev)) - vc5 &= ~VC5_VID_FFF_EN; - - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0); - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1); - - if (is5325(dev) || is5365(dev)) { - /* enable the high 8 bit vid check on 5325 */ - if (is5325(dev) && enable) - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, - VC3_HIGH_8BIT_EN); - else - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); - - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4); - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5); - } else if (is63xx(dev)) { - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0); - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4); - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5); - } else { - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4); - b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5); - } - - b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); - - dev->vlan_enabled = enable; - - dev_dbg(dev->dev, "Port %d VLAN enabled: %d, filtering: %d\n", - port, enable, enable_filtering); -} - -static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100) -{ - u32 port_mask = 0; - u16 max_size = JMS_MIN_SIZE; - - if (is5325(dev) || is5365(dev)) - return -EINVAL; - - if (enable) { - port_mask = dev->enabled_ports; - max_size = JMS_MAX_SIZE; - if (allow_10_100) - port_mask |= JPM_10_100_JUMBO_EN; - } - - b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask); - return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size); + u32 port_mask; + b53_read32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, &port_mask); + port_mask &= ~JPM_RESERVED_BITS; + if (enable) + port_mask |= BIT(port); + else + port_mask &= ~BIT(port); + if (allow_10_100) + port_mask |= JPM_10_100_JUMBO_EN; + return b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask); } static int b53_flush_arl(struct b53_device *dev, u8 mask) @@ -488,19 +173,12 @@ static int b53_fast_age_port(struct b53_device *dev, int port) return b53_flush_arl(dev, FAST_AGE_PORT); } -static int b53_fast_age_vlan(struct b53_device *dev, u16 vid) -{ - b53_write16(dev, B53_CTRL_PAGE, B53_FAST_AGE_VID_CTRL, vid); - - return b53_flush_arl(dev, FAST_AGE_VLAN); -} - void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) { struct b53_device *dev = ds->priv; unsigned int i; u16 pvlan; - + /* Enable the IMP port to be in the same VLAN as the other ports * on a per-port basis such that we only have Port i and IMP in * the same VLAN. @@ -593,7 +271,7 @@ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) pvlan |= dev->ports[port].vlan_ctl_mask; b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); - b53_imp_vlan_setup(ds, cpu_port); + //b53_imp_vlan_setup(ds, cpu_port); /* If EEE was enabled, restore it */ if (dev->ports[port].eee.eee_enabled) @@ -621,73 +299,40 @@ EXPORT_SYMBOL(b53_disable_port); void b53_brcm_hdr_setup(struct dsa_switch *ds, int port) { struct b53_device *dev = ds->priv; - bool tag_en = !(dev->tag_protocol == DSA_TAG_PROTO_NONE); + bool tag_en; u8 hdr_ctl, val; - u16 reg; /* Resolve which bit controls the Broadcom tag */ switch (port) { case 8: val = BRCM_HDR_P8_EN; + tag_en = !(dev->tag_protocol_imp == DSA_TAG_PROTO_NONE); break; case 7: val = BRCM_HDR_P7_EN; + tag_en = false; break; case 5: val = BRCM_HDR_P5_EN; + tag_en = !(dev->tag_protocol == DSA_TAG_PROTO_NONE); break; default: val = 0; break; } - /* Enable management mode if tagging is requested */ + /* Always disable management mode */ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &hdr_ctl); - if (tag_en) - hdr_ctl |= SM_SW_FWD_MODE; - else - hdr_ctl &= ~SM_SW_FWD_MODE; + hdr_ctl &= ~SM_SW_FWD_MODE; b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, hdr_ctl); - /* Configure the appropriate IMP port */ - b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &hdr_ctl); - if (port == 8) - hdr_ctl |= GC_FRM_MGMT_PORT_MII; - else if (port == 5) - hdr_ctl |= GC_FRM_MGMT_PORT_M; - b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, hdr_ctl); - - /* Enable Broadcom tags for IMP port */ + /* Configure Broadcom tags for IMP/CPU port */ b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); if (tag_en) hdr_ctl |= val; else hdr_ctl &= ~val; b53_write8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, hdr_ctl); - - /* Registers below are only accessible on newer devices */ - if (!is58xx(dev)) - return; - - /* Enable reception Broadcom tag for CPU TX (switch RX) to - * allow us to tag outgoing frames - */ - b53_read16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_RX_DIS, ®); - if (tag_en) - reg &= ~BIT(port); - else - reg |= BIT(port); - b53_write16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_RX_DIS, reg); - - /* Enable transmission of Broadcom tags from the switch (CPU RX) to - * allow delivering frames to the per-port net_devices - */ - b53_read16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_TX_DIS, ®); - if (tag_en) - reg &= ~BIT(port); - else - reg |= BIT(port); - b53_write16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_TX_DIS, reg); } EXPORT_SYMBOL(b53_brcm_hdr_setup); @@ -695,9 +340,21 @@ static void b53_enable_cpu_port(struct b53_device *dev, int port) { u8 port_ctrl; - /* BCM5325 CPU port is at 8 */ - if ((is5325(dev) || is5365(dev)) && port == B53_CPU_PORT_25) - port = B53_CPU_PORT; + port_ctrl = PORT_CTRL_RX_BCST_EN | + PORT_CTRL_RX_MCST_EN | + PORT_CTRL_RX_UCST_EN; + b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), port_ctrl); + + b53_brcm_hdr_setup(dev->ds, port); + + b53_port_set_ucast_flood(dev, port, true); + b53_port_set_mcast_flood(dev, port, true); + b53_port_set_learning(dev, port, false); +} + +static void b53_enable_imp_port(struct b53_device *dev, int port) +{ + u8 port_ctrl; port_ctrl = PORT_CTRL_RX_BCST_EN | PORT_CTRL_RX_MCST_EN | @@ -720,68 +377,13 @@ static void b53_enable_mib(struct b53_device *dev) b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc); } -static u16 b53_default_pvid(struct b53_device *dev) -{ - if (is5325(dev) || is5365(dev)) - return 1; - else - return 0; -} - -static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port) -{ - struct b53_device *dev = ds->priv; - - return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port); -} int b53_configure_vlan(struct dsa_switch *ds) { struct b53_device *dev = ds->priv; - struct b53_vlan vl = { 0 }; - struct b53_vlan *v; - int i, def_vid; - u16 vid; - def_vid = b53_default_pvid(dev); - - /* clear all vlan entries */ - if (is5325(dev) || is5365(dev)) { - for (i = def_vid; i < dev->num_vlans; i++) - b53_set_vlan_entry(dev, i, &vl); - } else { - b53_do_vlan_op(dev, VTA_CMD_CLEAR); - } - - b53_enable_vlan(dev, -1, dev->vlan_enabled, ds->vlan_filtering); - - /* Create an untagged VLAN entry for the default PVID in case - * CONFIG_VLAN_8021Q is disabled and there are no calls to - * dsa_slave_vlan_rx_add_vid() to create the default VLAN - * entry. Do this only when the tagging protocol is not - * DSA_TAG_PROTO_NONE - */ - b53_for_each_port(dev, i) { - v = &dev->vlans[def_vid]; - v->members |= BIT(i); - if (!b53_vlan_port_needs_forced_tagged(ds, i)) - v->untag = v->members; - b53_write16(dev, B53_VLAN_PAGE, - B53_VLAN_PORT_DEF_TAG(i), def_vid); - } - - /* Upon initial call we have not set-up any VLANs, but upon - * system resume, we need to restore all VLAN entries. - */ - for (vid = def_vid; vid < dev->num_vlans; vid++) { - v = &dev->vlans[vid]; - - if (!v->members) - continue; - - b53_set_vlan_entry(dev, vid, v); - b53_fast_age_vlan(dev, vid); - } + /* Join all VLANs for all ports (promiscuous mode) */ + b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, VJA_ALL_PORTS_MASK); return 0; } @@ -805,6 +407,24 @@ static void b53_switch_reset_gpio(struct b53_device *dev) dev->current_page = 0xff; } +static void b53_switch_reset_gpiod(struct b53_device *dev) +{ + struct gpio_desc *gpiod = dev->reset_gpiod; + + if (IS_ERR(gpiod)) + return; + + /* Reset sequence: RESET low(5ms)->high(10ms) + */ + gpiod_set_value(gpiod, 1); // Reset Asserted (Voltage Low) + mdelay(5); + + gpiod_set_value(gpiod, 0); // Reset De-Asserted (Voltage High) + mdelay(10); + + dev->current_page = 0xff; +} + static int b53_switch_reset(struct b53_device *dev) { unsigned int timeout = 1000; @@ -812,35 +432,22 @@ static int b53_switch_reset(struct b53_device *dev) b53_switch_reset_gpio(dev); - if (is539x(dev)) { - b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83); - b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00); - } + b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, ®); + reg |= SW_RST | EN_SW_RST | EN_CH_RST; + b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, reg); - /* This is specific to 58xx devices here, do not use is58xx() which - * covers the larger Starfigther 2 family, including 7445/7278 which - * still use this driver as a library and need to perform the reset - * earlier. - */ - if (dev->chip_id == BCM58XX_DEVICE_ID || - dev->chip_id == BCM583XX_DEVICE_ID) { + do { b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, ®); - reg |= SW_RST | EN_SW_RST | EN_CH_RST; - b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, reg); - - do { - b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, ®); - if (!(reg & SW_RST)) - break; + if (!(reg & SW_RST)) + break; - usleep_range(1000, 2000); - } while (timeout-- > 0); + usleep_range(1000, 2000); + } while (timeout-- > 0); - if (timeout == 0) { - dev_err(dev->dev, - "Timeout waiting for SW_RST to clear!\n"); - return -ETIMEDOUT; - } + if (timeout == 0) { + dev_err(dev->dev, + "Timeout waiting for SW_RST to clear!\n"); + return -ETIMEDOUT; } b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); @@ -926,26 +533,12 @@ static void b53_reset_mib(struct b53_device *priv) static const struct b53_mib_desc *b53_get_mib(struct b53_device *dev) { - if (is5365(dev)) - return b53_mibs_65; - else if (is63xx(dev)) - return b53_mibs_63xx; - else if (is58xx(dev)) - return b53_mibs_58xx; - else - return b53_mibs; + return b53_mibs_134; } static unsigned int b53_get_mib_size(struct b53_device *dev) { - if (is5365(dev)) - return B53_MIBS_65_SIZE; - else if (is63xx(dev)) - return B53_MIBS_63XX_SIZE; - else if (is58xx(dev)) - return B53_MIBS_58XX_SIZE; - else - return B53_MIBS_SIZE; + return B53_MIBS_134_SIZE; } static struct phy_device *b53_get_phy_device(struct dsa_switch *ds, int port) @@ -993,8 +586,8 @@ void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) unsigned int i; u64 val = 0; - if (is5365(dev) && port == 5) - port = 8; + // if (is5365(dev) && port == 5) + // port = 8; mutex_lock(&dev->stats_mutex); @@ -1096,6 +689,11 @@ int b53_setup_devlink_resources(struct dsa_switch *ds) } EXPORT_SYMBOL(b53_setup_devlink_resources); +static inline bool dsa_is_imp_port(struct dsa_switch *ds, int p) +{ + return p == B53_CPU_PORT; +} + static int b53_setup(struct dsa_switch *ds) { struct b53_device *dev = ds->priv; @@ -1125,11 +723,20 @@ static int b53_setup(struct dsa_switch *ds) * ports will be configured with .port_enable */ for (port = 0; port < dev->num_ports; port++) { + dsa_is_cpu_port(ds, port); if (dsa_is_cpu_port(ds, port)) b53_enable_cpu_port(dev, port); + else if(dsa_is_imp_port(ds, port)) + b53_enable_imp_port(dev, port); else b53_disable_port(ds, port); } + /* Disable all port based VLANs on powerup. Userspace configuration + * will enable them as needed. Note, PVLAN bit mask for each port + * must be set to include itself per data sheet. + */ + for (port = 0; port < dev->num_ports; port++) + b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), BIT(port)); return b53_setup_devlink_resources(ds); } @@ -1214,17 +821,13 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, { struct b53_device *dev = ds->priv; struct ethtool_eee *p = &dev->ports[port].eee; - u8 rgmii_ctrl = 0, reg = 0, off; + u8 rgmii_ctrl = 0, off; bool tx_pause = false; bool rx_pause = false; if (!phy_is_pseudo_fixed_link(phydev)) return; - /* Enable flow control on BCM5301x's CPU port */ - if (is5301x(dev) && port == dev->cpu_port) - tx_pause = rx_pause = true; - if (phydev->pause) { if (phydev->asym_pause) tx_pause = true; @@ -1235,7 +838,7 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, tx_pause, rx_pause); b53_force_link(dev, port, phydev->link); - if (is531x5(dev) && phy_interface_is_rgmii(phydev)) { + if (phy_interface_is_rgmii(phydev)) { if (port == dev->imp_port) off = B53_RGMII_CTRL_IMP; else @@ -1244,9 +847,7 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, /* Configure the port RGMII clock delay by DLL disabled and * tx_clk aligned timing (restoring to reset defaults) */ - b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); - rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC | - RGMII_CTRL_TIMING_SEL); + rgmii_ctrl = 0x00; /* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make * sure that we enable the port TX clock internal delay to @@ -1264,39 +865,15 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, */ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) rgmii_ctrl |= RGMII_CTRL_DLL_TXC; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII) - rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC; - rgmii_ctrl |= RGMII_CTRL_TIMING_SEL; - b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + rgmii_ctrl |= RGMII_CTRL_DLL_RXC; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + rgmii_ctrl |= (RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC); - dev_info(ds->dev, "Configured port %d for %s\n", port, - phy_modes(phydev->interface)); - } + b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); - /* configure MII port if necessary */ - if (is5325(dev)) { - b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, - ®); - - /* reverse mii needs to be enabled */ - if (!(reg & PORT_OVERRIDE_RV_MII_25)) { - b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, - reg | PORT_OVERRIDE_RV_MII_25); - b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, - ®); - - if (!(reg & PORT_OVERRIDE_RV_MII_25)) { - dev_err(ds->dev, - "Failed to enable reverse MII mode\n"); - return; - } - } - } else if (is5301x(dev)) { - if (port != dev->cpu_port) { - b53_force_port_config(dev, dev->cpu_port, 2000, - DUPLEX_FULL, true, true); - b53_force_link(dev, dev->cpu_port, 1); - } + dev_info(ds->dev, "Configured port %d for %s (reg=0x%02x)\n", port, + phy_modes(phydev->interface), rgmii_ctrl); } /* Re-negotiate EEE if it was enabled already */ @@ -1336,8 +913,7 @@ void b53_phylink_validate(struct dsa_switch *ds, int port, */ if (state->interface != PHY_INTERFACE_MODE_MII && state->interface != PHY_INTERFACE_MODE_REVMII && - !phy_interface_mode_is_8023z(state->interface) && - !(is5325(dev) || is5365(dev))) { + !phy_interface_mode_is_8023z(state->interface)) { phylink_set(mask, 1000baseT_Full); phylink_set(mask, 1000baseT_Half); } @@ -1440,115 +1016,10 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, if (phy_interface_mode_is_8023z(interface) && dev->ops->serdes_link_set) dev->ops->serdes_link_set(dev, port, mode, interface, true); -} -EXPORT_SYMBOL(b53_phylink_mac_link_up); - -int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, - struct netlink_ext_ack *extack) -{ - struct b53_device *dev = ds->priv; - - b53_enable_vlan(dev, port, dev->vlan_enabled, vlan_filtering); - - return 0; -} -EXPORT_SYMBOL(b53_vlan_filtering); - -static int b53_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct b53_device *dev = ds->priv; - - if ((is5325(dev) || is5365(dev)) && vlan->vid == 0) - return -EOPNOTSUPP; - - /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of - * receiving VLAN tagged frames at all, we can still allow the port to - * be configured for egress untagged. - */ - if (dev->chip_id == BCM7278_DEVICE_ID && port == 7 && - !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) - return -EINVAL; - - if (vlan->vid >= dev->num_vlans) - return -ERANGE; - - b53_enable_vlan(dev, port, true, ds->vlan_filtering); - - return 0; -} - -int b53_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) -{ - struct b53_device *dev = ds->priv; - bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; - bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - struct b53_vlan *vl; - int err; - err = b53_vlan_prepare(ds, port, vlan); - if (err) - return err; - - vl = &dev->vlans[vlan->vid]; - - b53_get_vlan_entry(dev, vlan->vid, vl); - - if (vlan->vid == 0 && vlan->vid == b53_default_pvid(dev)) - untagged = true; - - vl->members |= BIT(port); - if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port)) - vl->untag |= BIT(port); - else - vl->untag &= ~BIT(port); - - b53_set_vlan_entry(dev, vlan->vid, vl); - b53_fast_age_vlan(dev, vlan->vid); - if (pvid && !dsa_is_cpu_port(ds, port)) { - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), - vlan->vid); - b53_fast_age_vlan(dev, vlan->vid); - } - - return 0; } -EXPORT_SYMBOL(b53_vlan_add); - -int b53_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct b53_device *dev = ds->priv; - bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; - struct b53_vlan *vl; - u16 pvid; - - b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); - - vl = &dev->vlans[vlan->vid]; - - b53_get_vlan_entry(dev, vlan->vid, vl); - - vl->members &= ~BIT(port); - - if (pvid == vlan->vid) - pvid = b53_default_pvid(dev); - - if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port)) - vl->untag &= ~(BIT(port)); - - b53_set_vlan_entry(dev, vlan->vid, vl); - b53_fast_age_vlan(dev, vlan->vid); - - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), pvid); - b53_fast_age_vlan(dev, pvid); - - return 0; -} -EXPORT_SYMBOL(b53_vlan_del); +EXPORT_SYMBOL(b53_phylink_mac_link_up); /* Address Resolution Logic routines */ static int b53_arl_op_wait(struct b53_device *dev) @@ -1716,12 +1187,6 @@ int b53_fdb_add(struct dsa_switch *ds, int port, { struct b53_device *priv = ds->priv; - /* 5325 and 5365 require some more massaging, but could - * be supported eventually - */ - if (is5325(priv) || is5365(priv)) - return -EOPNOTSUPP; - return b53_arl_op(priv, 0, port, addr, vid, true); } EXPORT_SYMBOL(b53_fdb_add); @@ -1823,12 +1288,6 @@ int b53_mdb_add(struct dsa_switch *ds, int port, { struct b53_device *priv = ds->priv; - /* 5325 and 5365 require some more massaging, but could - * be supported eventually - */ - if (is5325(priv) || is5365(priv)) - return -EOPNOTSUPP; - return b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true); } EXPORT_SYMBOL(b53_mdb_add); @@ -1847,137 +1306,6 @@ int b53_mdb_del(struct dsa_switch *ds, int port, } EXPORT_SYMBOL(b53_mdb_del); -int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) -{ - struct b53_device *dev = ds->priv; - s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - u16 pvlan, reg; - unsigned int i; - - /* On 7278, port 7 which connects to the ASP should only receive - * traffic from matching CFP rules. - */ - if (dev->chip_id == BCM7278_DEVICE_ID && port == 7) - return -EINVAL; - - /* Make this port leave the all VLANs join since we will have proper - * VLAN entries from now on - */ - if (is58xx(dev)) { - b53_read16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, ®); - reg &= ~BIT(port); - if ((reg & BIT(cpu_port)) == BIT(cpu_port)) - reg &= ~BIT(cpu_port); - b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, reg); - } - - b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); - - b53_for_each_port(dev, i) { - if (dsa_to_port(ds, i)->bridge_dev != br) - continue; - - /* Add this local port to the remote port VLAN control - * membership and update the remote port bitmask - */ - b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); - reg |= BIT(port); - b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), reg); - dev->ports[i].vlan_ctl_mask = reg; - - pvlan |= BIT(i); - } - - /* Configure the local port VLAN control membership to include - * remote ports and update the local port bitmask - */ - b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); - dev->ports[port].vlan_ctl_mask = pvlan; - - return 0; -} -EXPORT_SYMBOL(b53_br_join); - -void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) -{ - struct b53_device *dev = ds->priv; - struct b53_vlan *vl = &dev->vlans[0]; - s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - unsigned int i; - u16 pvlan, reg, pvid; - - b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); - - b53_for_each_port(dev, i) { - /* Don't touch the remaining ports */ - if (dsa_to_port(ds, i)->bridge_dev != br) - continue; - - b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); - reg &= ~BIT(port); - b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), reg); - dev->ports[port].vlan_ctl_mask = reg; - - /* Prevent self removal to preserve isolation */ - if (port != i) - pvlan &= ~BIT(i); - } - - b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); - dev->ports[port].vlan_ctl_mask = pvlan; - - pvid = b53_default_pvid(dev); - - /* Make this port join all VLANs without VLAN entries */ - if (is58xx(dev)) { - b53_read16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, ®); - reg |= BIT(port); - if (!(reg & BIT(cpu_port))) - reg |= BIT(cpu_port); - b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, reg); - } else { - b53_get_vlan_entry(dev, pvid, vl); - vl->members |= BIT(port) | BIT(cpu_port); - vl->untag |= BIT(port) | BIT(cpu_port); - b53_set_vlan_entry(dev, pvid, vl); - } -} -EXPORT_SYMBOL(b53_br_leave); - -void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state) -{ - struct b53_device *dev = ds->priv; - u8 hw_state; - u8 reg; - - switch (state) { - case BR_STATE_DISABLED: - hw_state = PORT_CTRL_DIS_STATE; - break; - case BR_STATE_LISTENING: - hw_state = PORT_CTRL_LISTEN_STATE; - break; - case BR_STATE_LEARNING: - hw_state = PORT_CTRL_LEARN_STATE; - break; - case BR_STATE_FORWARDING: - hw_state = PORT_CTRL_FWD_STATE; - break; - case BR_STATE_BLOCKING: - hw_state = PORT_CTRL_BLOCK_STATE; - break; - default: - dev_err(ds->dev, "invalid STP state: %d\n", state); - return; - } - - b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), ®); - reg &= ~PORT_CTRL_STP_STATE_MASK; - reg |= hw_state; - b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg); -} -EXPORT_SYMBOL(b53_br_set_stp_state); - void b53_br_fast_age(struct dsa_switch *ds, int port) { struct b53_device *dev = ds->priv; @@ -2021,6 +1349,7 @@ static bool b53_possible_cpu_port(struct dsa_switch *ds, int port) /* Broadcom switches will accept enabling Broadcom tags on the * following ports: 5, 7 and 8, any other port is not supported */ + switch (port) { case B53_CPU_PORT_25: case 7: @@ -2047,7 +1376,8 @@ static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port, case DSA_TAG_PROTO_BRCM_PREPEND: dev_warn(ds->dev, "Port %d is stacked to Broadcom tag switch\n", port); - ret = false; + //ret = false; + ret = true; break; default: ret = true; @@ -2062,31 +1392,36 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, { struct b53_device *dev = ds->priv; + if(port == B53_CPU_PORT) + return dev->tag_protocol_imp; + if (!b53_can_enable_brcm_tags(ds, port, mprot)) { dev->tag_protocol = DSA_TAG_PROTO_NONE; goto out; } - /* Older models require a different 6 byte tag */ - if (is5325(dev) || is5365(dev) || is63xx(dev)) { - dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY; - goto out; - } - - /* Broadcom BCM58xx chips have a flow accelerator on Port 8 - * which requires us to use the prepended Broadcom tag type - */ - if (dev->chip_id == BCM58XX_DEVICE_ID && port == B53_CPU_PORT) { - dev->tag_protocol = DSA_TAG_PROTO_BRCM_PREPEND; - goto out; - } - - dev->tag_protocol = DSA_TAG_PROTO_BRCM; out: return dev->tag_protocol; } EXPORT_SYMBOL(b53_get_tag_protocol); +int b53_change_imp_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprot) +{ + struct b53_device *dev = ds->priv; + + if (!b53_can_enable_brcm_tags(ds, port, mprot)) + return -EOPNOTSUPP; + if(dev->tag_protocol_imp == mprot) + return 0; + dev->tag_protocol_imp = mprot; + b53_brcm_hdr_setup(ds, port); + + return 0; +} +EXPORT_SYMBOL(b53_change_imp_tag_protocol); + + int b53_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress) { @@ -2188,9 +1523,6 @@ int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) struct ethtool_eee *p = &dev->ports[port].eee; u16 reg; - if (is5325(dev) || is5365(dev)) - return -EOPNOTSUPP; - b53_read16(dev, B53_EEE_PAGE, B53_EEE_LPI_INDICATE, ®); e->eee_enabled = p->eee_enabled; e->eee_active = !!(reg & BIT(port)); @@ -2204,9 +1536,6 @@ int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) struct b53_device *dev = ds->priv; struct ethtool_eee *p = &dev->ports[port].eee; - if (is5325(dev) || is5365(dev)) - return -EOPNOTSUPP; - p->eee_enabled = e->eee_enabled; b53_eee_enable_set(ds, port, e->eee_enabled); @@ -2219,14 +1548,10 @@ static int b53_change_mtu(struct dsa_switch *ds, int port, int mtu) struct b53_device *dev = ds->priv; bool enable_jumbo; bool allow_10_100; - - if (is5325(dev) || is5365(dev)) - return -EOPNOTSUPP; - enable_jumbo = (mtu >= JMS_MIN_SIZE); - allow_10_100 = (dev->chip_id == BCM583XX_DEVICE_ID); + allow_10_100 = true; - return b53_set_jumbo(dev, enable_jumbo, allow_10_100); + return b53_set_jumbo(dev, port, enable_jumbo, allow_10_100); } static int b53_get_max_mtu(struct dsa_switch *ds, int port) @@ -2234,8 +1559,84 @@ static int b53_get_max_mtu(struct dsa_switch *ds, int port) return JMS_MAX_SIZE; } +static int b53_port_change_pvlan(struct dsa_switch *ds, int port, u16 mask) +{ + struct b53_device *dev = ds->priv; + + b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), mask); + dev->ports[port].vlan_ctl_mask = mask; + return 0; +} + +static int b53_port_get_pvlan(struct dsa_switch *ds, int port, u16 *mask) +{ + struct b53_device *dev = ds->priv; + u16 reg; + b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), ®); + *mask = reg & 0x1ff; + + return 0; +} + +static int b53_switch_setup_get_reg(struct dsa_switch *ds, u8 page, u8 reg, u8 size) +{ + struct b53_device *dev = ds->priv; + + dev->page = page; + dev->reg = reg; + dev->size = size; + + return 0; +} + +static int b53_switch_get_reg(struct dsa_switch *ds, u8 *size, u64 *value) +{ + struct b53_device *dev = ds->priv; + + *size = dev->size; + switch(dev->size) { + case 1: + return b53_read8(dev, dev->page, dev->reg, (u8 *)value); + case 2: + return b53_read16(dev, dev->page, dev->reg, (u16 *)value); + case 4: + return b53_read32(dev, dev->page, dev->reg, (u32 *)value); + case 6: + return b53_read48(dev, dev->page, dev->reg, value); + case 8: + return b53_read64(dev, dev->page, dev->reg, value); + default: + return -EINVAL; + } + + return 0; +} + +static int b53_switch_set_reg(struct dsa_switch *ds, u8 page, u8 reg, u8 size, u64 value) +{ + struct b53_device *dev = ds->priv; + + switch(size) { + case 1: + return b53_write8(dev, page, reg, (u8)value); + case 2: + return b53_write16(dev, page, reg, (u16)value); + case 4: + return b53_write32(dev, page, reg, (u32)value); + case 6: + return b53_write48(dev, page, reg, (u64)value); + case 8: + return b53_write64(dev, page, reg, (u64)value); + default: + return -EINVAL; + } + + return 0; +} + static const struct dsa_switch_ops b53_switch_ops = { .get_tag_protocol = b53_get_tag_protocol, + .change_tag_protocol = b53_change_imp_tag_protocol, .setup = b53_setup, .teardown = b53_teardown, .get_strings = b53_get_strings, @@ -2255,15 +1656,9 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_disable = b53_disable_port, .get_mac_eee = b53_get_mac_eee, .set_mac_eee = b53_set_mac_eee, - .port_bridge_join = b53_br_join, - .port_bridge_leave = b53_br_leave, .port_pre_bridge_flags = b53_br_flags_pre, .port_bridge_flags = b53_br_flags, - .port_stp_state_set = b53_br_set_stp_state, .port_fast_age = b53_br_fast_age, - .port_vlan_filtering = b53_vlan_filtering, - .port_vlan_add = b53_vlan_add, - .port_vlan_del = b53_vlan_del, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, @@ -2273,6 +1668,11 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_mdb_del = b53_mdb_del, .port_max_mtu = b53_get_max_mtu, .port_change_mtu = b53_change_mtu, + .port_change_pvlan = b53_port_change_pvlan, + .port_get_pvlan = b53_port_get_pvlan, + .switch_setup_get_reg = b53_switch_setup_get_reg, + .switch_get_reg = b53_switch_get_reg, + .switch_set_reg = b53_switch_set_reg }; struct b53_chip_data { @@ -2579,7 +1979,7 @@ static const struct b53_chip_data b53_switch_chips[] = { .vlans = 4096, .enabled_ports = 0x12f, .imp_port = 8, - .cpu_port = B53_CPU_PORT, + .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ .vta_regs = B53_VTA_REGS, .arl_bins = 4, .arl_buckets = 1024, @@ -2594,6 +1994,7 @@ static int b53_switch_init(struct b53_device *dev) { unsigned int i; int ret; + u16 phyreg; for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) { const struct b53_chip_data *chip = &b53_switch_chips[i]; @@ -2616,65 +2017,48 @@ static int b53_switch_init(struct b53_device *dev) } } - /* check which BCM5325x version we have */ - if (is5325(dev)) { - u8 vc4; - - b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); - - /* check reserved bits */ - switch (vc4 & 3) { - case 1: - /* BCM5325E */ - break; - case 3: - /* BCM5325F - do not use port 4 */ - dev->enabled_ports &= ~BIT(4); - break; - default: -/* On the BCM47XX SoCs this is the supported internal switch.*/ -#ifndef CONFIG_BCM47XX - /* BCM5325M */ - return -EINVAL; -#else - break; -#endif - } - } else if (dev->chip_id == BCM53115_DEVICE_ID) { - u64 strap_value; - - b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value); - /* use second IMP port if GMII is enabled */ - if (strap_value & SV_GMII_CTRL_115) - dev->cpu_port = 5; - } - dev->enabled_ports |= BIT(dev->cpu_port); dev->num_ports = fls(dev->enabled_ports); dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS); /* Include non standard CPU port built-in PHYs to be probed */ - if (is539x(dev) || is531x5(dev)) { - for (i = 0; i < dev->num_ports; i++) { - if (!(dev->ds->phys_mii_mask & BIT(i)) && - !b53_possible_cpu_port(dev->ds, i)) - dev->ds->phys_mii_mask |= BIT(i); - } + for (i = 0; i < dev->num_ports; i++) { + if (!(dev->ds->phys_mii_mask & BIT(i)) && + !b53_possible_cpu_port(dev->ds, i)) + dev->ds->phys_mii_mask |= BIT(i); } - + /* Default cpu to no tagging, imp Port to brcm tagging */ + dev->tag_protocol = DSA_TAG_PROTO_NONE; + dev->tag_protocol_imp = DSA_TAG_PROTO_BRCM; dev->ports = devm_kcalloc(dev->dev, dev->num_ports, sizeof(struct b53_port), GFP_KERNEL); if (!dev->ports) return -ENOMEM; + /* Disable integrated PHY ports */ + for(i = 0; i < dev->num_ports; i++) + { + if((dev->enabled_ports & BIT(i)) && (i != dev->cpu_port) && (i != dev->imp_port)) + { + ret = b53_read16(dev, B53_PORT_MII_PAGE(i), B53_GMII_CTL, &phyreg); + if(ret) + return ret; + phyreg |= GMII_PWR_DOWN; + ret = b53_write16(dev, B53_PORT_MII_PAGE(i), B53_GMII_CTL, phyreg); + if(ret) + return ret; + } + } + dev->vlans = devm_kcalloc(dev->dev, dev->num_vlans, sizeof(struct b53_vlan), GFP_KERNEL); if (!dev->vlans) return -ENOMEM; + dev->reset_gpio = b53_switch_get_reset_gpio(dev); if (dev->reset_gpio >= 0) { ret = devm_gpio_request_one(dev->dev, dev->reset_gpio, @@ -2712,7 +2096,7 @@ struct b53_device *b53_switch_alloc(struct device *base, ds->ops = &b53_switch_ops; dev->vlan_enabled = true; /* Let DSA handle the case were multiple bridges span the same switch - * device and different VLAN awareness settings are requested, which + * device and different VLAN awareness settings are requested, whichdev * would be breaking filtering semantics for any of the other bridge * devices. (not hardware supported) */ @@ -2795,7 +2179,17 @@ EXPORT_SYMBOL(b53_switch_detect); int b53_switch_register(struct b53_device *dev) { + + int i; int ret; + u16 phyreg; + + dev->reset_gpiod = devm_gpiod_get(dev->dev, "reset", GPIOD_OUT_HIGH); // Reset Asserted (Voltage Low) + if (IS_ERR(dev->reset_gpiod)) { + dev_err(dev->ds->dev, "Failed to get reset GPIO\n"); + return PTR_ERR(dev->reset_gpiod); + } + b53_switch_reset_gpiod(dev); if (dev->pdata) { dev->chip_id = dev->pdata->chip_id; @@ -2812,7 +2206,18 @@ int b53_switch_register(struct b53_device *dev) dev_info(dev->dev, "found switch: %s, rev %i\n", dev->name, dev->core_rev); - return dsa_register_switch(dev->ds); + ret = dsa_register_switch(dev->ds); + /* Disable integrated PHY ports */ + for(i = 0; i < dev->num_ports; i++) + { + if((dev->enabled_ports & BIT(i)) && (i != dev->cpu_port) && (i != dev->imp_port)) + { + b53_read16(dev, B53_PORT_MII_PAGE(i), B53_GMII_CTL, &phyreg); + phyreg |= GMII_PWR_DOWN; + b53_write16(dev, B53_PORT_MII_PAGE(i), B53_GMII_CTL, phyreg); + } + } + return ret; } EXPORT_SYMBOL(b53_switch_register); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 57ca977e80ca45..a17504932cf838 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -109,6 +109,7 @@ struct b53_device { struct mutex reg_mutex; struct mutex stats_mutex; const struct b53_io_ops *ops; + struct gpio_desc *reset_gpiod; /* chip specific data */ u32 chip_id; @@ -121,6 +122,12 @@ struct b53_device { u8 num_arl_bins; u16 num_arl_buckets; enum dsa_tag_protocol tag_protocol; + enum dsa_tag_protocol tag_protocol_imp; + + /* raw register read data*/ + u8 page; + u8 reg; + u8 size; /* used ports mask */ u16 enabled_ports; @@ -152,63 +159,63 @@ struct b53_device { if (dev->enabled_ports & BIT(i)) -static inline int is5325(struct b53_device *dev) -{ - return dev->chip_id == BCM5325_DEVICE_ID; -} - -static inline int is5365(struct b53_device *dev) -{ -#ifdef CONFIG_BCM47XX - return dev->chip_id == BCM5365_DEVICE_ID; -#else - return 0; -#endif -} - -static inline int is5397_98(struct b53_device *dev) -{ - return dev->chip_id == BCM5397_DEVICE_ID || - dev->chip_id == BCM5398_DEVICE_ID; -} - -static inline int is539x(struct b53_device *dev) -{ - return dev->chip_id == BCM5395_DEVICE_ID || - dev->chip_id == BCM5397_DEVICE_ID || - dev->chip_id == BCM5398_DEVICE_ID; -} - -static inline int is531x5(struct b53_device *dev) -{ - return dev->chip_id == BCM53115_DEVICE_ID || - dev->chip_id == BCM53125_DEVICE_ID || - dev->chip_id == BCM53128_DEVICE_ID || - dev->chip_id == BCM53134_DEVICE_ID; -} - -static inline int is63xx(struct b53_device *dev) -{ - return dev->chip_id == BCM63XX_DEVICE_ID; -} - -static inline int is5301x(struct b53_device *dev) -{ - return dev->chip_id == BCM53010_DEVICE_ID || - dev->chip_id == BCM53011_DEVICE_ID || - dev->chip_id == BCM53012_DEVICE_ID || - dev->chip_id == BCM53018_DEVICE_ID || - dev->chip_id == BCM53019_DEVICE_ID; -} - -static inline int is58xx(struct b53_device *dev) -{ - return dev->chip_id == BCM58XX_DEVICE_ID || - dev->chip_id == BCM583XX_DEVICE_ID || - dev->chip_id == BCM7445_DEVICE_ID || - dev->chip_id == BCM7278_DEVICE_ID || - dev->chip_id == BCM53134_DEVICE_ID; -} +// static inline int is5325(struct b53_device *dev) +// { +// return dev->chip_id == BCM5325_DEVICE_ID; +// } + +// static inline int is5365(struct b53_device *dev) +// { +// #ifdef CONFIG_BCM47XX +// return dev->chip_id == BCM5365_DEVICE_ID; +// #else +// return 0; +// #endif +// } + +// static inline int is5397_98(struct b53_device *dev) +// { +// return dev->chip_id == BCM5397_DEVICE_ID || +// dev->chip_id == BCM5398_DEVICE_ID; +// } + +// static inline int is539x(struct b53_device *dev) +// { +// return dev->chip_id == BCM5395_DEVICE_ID || +// dev->chip_id == BCM5397_DEVICE_ID || +// dev->chip_id == BCM5398_DEVICE_ID; +// } + +// static inline int is531x5(struct b53_device *dev) +// { +// return dev->chip_id == BCM53115_DEVICE_ID || +// dev->chip_id == BCM53125_DEVICE_ID || +// dev->chip_id == BCM53128_DEVICE_ID || +// dev->chip_id == BCM53134_DEVICE_ID; +// } + +// static inline int is63xx(struct b53_device *dev) +// { +// return dev->chip_id == BCM63XX_DEVICE_ID; +// } + +// static inline int is5301x(struct b53_device *dev) +// { +// return dev->chip_id == BCM53010_DEVICE_ID || +// dev->chip_id == BCM53011_DEVICE_ID || +// dev->chip_id == BCM53012_DEVICE_ID || +// dev->chip_id == BCM53018_DEVICE_ID || +// dev->chip_id == BCM53019_DEVICE_ID; +// } + +// static inline int is58xx(struct b53_device *dev) +// { +// return dev->chip_id == BCM58XX_DEVICE_ID || +// dev->chip_id == BCM583XX_DEVICE_ID || +// dev->chip_id == BCM7445_DEVICE_ID || +// dev->chip_id == BCM7278_DEVICE_ID || +// dev->chip_id == BCM53134_DEVICE_ID; +// } #define B53_CPU_PORT_25 5 #define B53_CPU_PORT 8 @@ -378,6 +385,8 @@ int b53_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress); enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mprot); +int b53_change_imp_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprot); void b53_mirror_del(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h index b2c539a4215450..3d7930684aa3a2 100644 --- a/drivers/net/dsa/b53/b53_regs.h +++ b/drivers/net/dsa/b53/b53_regs.h @@ -24,16 +24,12 @@ #define B53_CTRL_PAGE 0x00 /* Control */ #define B53_STAT_PAGE 0x01 /* Status */ #define B53_MGMT_PAGE 0x02 /* Management Mode */ -#define B53_MIB_AC_PAGE 0x03 /* MIB Autocast */ +#define B53_ICR PAGE 0x03 /* Interrrupt Control Register */ #define B53_ARLCTRL_PAGE 0x04 /* ARL Control */ #define B53_ARLIO_PAGE 0x05 /* ARL Access */ -#define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */ -#define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */ /* PHY Registers */ #define B53_PORT_MII_PAGE(i) (0x10 + (i)) /* Port i MII Registers */ -#define B53_IM_PORT_PAGE 0x18 /* Inverse MII Port (to EMAC) */ -#define B53_ALL_PORT_PAGE 0x19 /* All ports MII (broadcast) */ /* MIB registers */ #define B53_MIB_PAGE(i) (0x20 + (i)) @@ -354,6 +350,45 @@ #define B53_ARL_SRCH_RSTL_MACVID(x) (B53_ARL_SRCH_RSTL_0_MACVID + ((x) * 0x10)) #define B53_ARL_SRCH_RSTL(x) (B53_ARL_SRCH_RSTL_0 + ((x) * 0x10)) +/************************************************************************* + * Port MII Registers + *************************************************************************/ + #define B53_GMII_CTL 0x00 + #define GMII_CTL_RESET BIT(15) + #define GMII_CTL_LOOPBACK BIT(14) + #define GMII_CTL_SPD_SEL_LSB BIT(13) + #define GMII_AN_EN BIT(12) + #define GMII_PWR_DOWN BIT(11) + #define GMII_ISOLATE BIT(10) + #define GMII_RESTART_AN BIT(9) + #define GMII_DUPLEX BIT(8) + #define GMII_COLL_TEST BIT(7) + #define GMII_SPD_SEL_MSB BIT(6) + #define B53_GMII_STS 0x02 + #define B53_PHY_IDH 0x04 + #define B53_PHY_IDL 0x06 + #define B53_AN_ADV 0x08 + #define B53_AN_LPA 0x0a + #define B53_AN_EXP 0x0c + #define B53_AN_NXP 0x0e + #define B53_LP_NXP 0x10 + #define B53_B1000T_CTL 0x12 + #define B53_B1000T_STS 0x14 + #define B53_EXT_STS 0x1e + #define B53_PHY_EXT_CTL 0x20 + #define B53_PHY_EXT_STS 0x22 + #define B53_REC_ERR_CNT 0x24 + #define B53_FALSE_CARR_CNT 0x26 + #define B53_REC_NOTOK_CNT 0x28 + #define B53_DSP_COEFFICIENT 0x2a + #define B53_DSP_COEFFICIENT_ADDR 0x2e + #define B53_AUX_CTL 0x30 + #define B53_AUX_STS 0x32 + #define B53_INTERRUPT_STS 0x34 + #define B53_INTERRUPT_MSK 0x36 + #define B53_MISC_SHADOW 0x38 + #define B53_LED_CTRL 0x3a + /************************************************************************* * Port VLAN Registers *************************************************************************/ @@ -363,6 +398,8 @@ /* Join all VLANs register (16 bit) */ #define B53_JOIN_ALL_VLAN_EN 0x50 +#define VJA_ALL_PORTS_MASK 0x12f + /************************************************************************* * 802.1Q Page Registers @@ -472,6 +509,7 @@ #define B53_JUMBO_PORT_MASK 0x01 #define B53_JUMBO_PORT_MASK_63XX 0x04 #define JPM_10_100_JUMBO_EN BIT(24) /* GigE always enabled */ +#define JPM_RESERVED_BITS 0xfefffe00 /* Good Frame Max Size without 802.1Q TAG (16 bit) */ #define B53_JUMBO_MAX_SIZE 0x05 diff --git a/drivers/net/dsa/b53/b53_spi.c b/drivers/net/dsa/b53/b53_spi.c index 4626bb6a9bd2e8..abfeea5d47e966 100644 --- a/drivers/net/dsa/b53/b53_spi.c +++ b/drivers/net/dsa/b53/b53_spi.c @@ -18,6 +18,8 @@ #include +#include + #include #include #include @@ -297,6 +299,10 @@ static int b53_spi_probe(struct spi_device *spi) { struct b53_device *dev; int ret; + struct net_device *ndev = dev_get_by_name(&init_net, "eth0"); + + if(!ndev) + return -EPROBE_DEFER; dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi); if (!dev) @@ -356,7 +362,7 @@ static const struct spi_device_id b53_spi_ids[] = { { .name = "bcm5395" }, { .name = "bcm5397" }, { .name = "bcm5398" }, -{ .name = "bcm53115" }, + { .name = "bcm53115" }, { .name = "bcm53125" }, { .name = "bcm53128" }, { .name = "bcm53134" }, diff --git a/drivers/net/dsa/b53/b53_spi_datum.c b/drivers/net/dsa/b53/b53_spi_datum.c new file mode 100644 index 00000000000000..1141b70c8b6048 --- /dev/null +++ b/drivers/net/dsa/b53/b53_spi_datum.c @@ -0,0 +1,423 @@ +/* + * B53 register access through SPI + * + * Copyright (C) 2011-2013 Jonas Gorski + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "b53_priv.h" + +#define B53_SPI_DATA 0xf0 + +#define B53_SPI_STATUS 0xfe +#define B53_SPI_CMD_SPIF BIT(7) +#define B53_SPI_CMD_RACK BIT(5) + +#define B53_SPI_CMD_READ 0x00 +#define B53_SPI_CMD_WRITE 0x01 +#define B53_SPI_CMD_NORMAL 0x60 +#define B53_SPI_CMD_FAST 0x10 + +#define B53_SPI_PAGE_SELECT 0xff + +DEFINE_MUTEX(datum_b53_spi_mutex); +EXPORT_SYMBOL(datum_b53_spi_mutex); + +static inline void spi_lock(void) { mutex_lock(&datum_b53_spi_mutex); } + +static inline void spi_unlock(struct device *dev) +{ + if(!pm_runtime_suspended(dev)) + usleep_range(100, 200); + mutex_unlock(&datum_b53_spi_mutex); +} + +static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val, + unsigned int len) +{ + u8 txbuf[2]; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ; + txbuf[1] = reg; + + return spi_write_then_read(spi, txbuf, 2, val, len); +} + +static inline int b53_spi_clear_status(struct spi_device *spi) +{ + unsigned int i; + u8 rxbuf; + int ret; + + for (i = 0; i < 10; i++) { + ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); + if (ret) + return ret; + + if (!(rxbuf & B53_SPI_CMD_SPIF)) + break; + + mdelay(1); + } + + if (i == 10) + return -EIO; + + return 0; +} + +static inline int b53_spi_set_page(struct spi_device *spi, u8 page) +{ + u8 txbuf[3]; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = B53_SPI_PAGE_SELECT; + txbuf[2] = page; + + return spi_write(spi, txbuf, sizeof(txbuf)); +} + +static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page) +{ + int ret = b53_spi_clear_status(spi); + + if (ret) + return ret; + + return b53_spi_set_page(spi, page); +} + +static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg) +{ + u8 rxbuf; + int retry_count; + int ret; + + ret = b53_spi_read_reg(spi, reg, &rxbuf, 1); + if (ret) + return ret; + + for (retry_count = 0; retry_count < 10; retry_count++) { + ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); + if (ret) + return ret; + + if (rxbuf & B53_SPI_CMD_RACK) + break; + + mdelay(1); + } + + if (retry_count == 10) + return -EIO; + + return 0; +} + +static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data, + unsigned int len) +{ + struct spi_device *spi = dev->priv; + int ret; + + spi_lock(); + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + ret = b53_spi_prepare_reg_read(spi, reg); + if (ret) + return ret; + + ret = b53_spi_read_reg(spi, B53_SPI_DATA, data, len); + spi_unlock(spi->dev.parent->parent); + + return ret; +} + +static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) +{ + return b53_spi_read(dev, page, reg, val, 1); +} + +static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) +{ + __le16 value; + int ret; + + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 2); + + if (!ret) + *val = le16_to_cpu(value); + + return ret; +} + +static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) +{ + __le32 value; + int ret; + + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 4); + + if (!ret) + *val = le32_to_cpu(value); + + return ret; +} + +static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + __le64 value; + int ret; + + *val = 0; + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 6); + if (!ret) + *val = le64_to_cpu(value); + + return ret; +} + +static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + __le64 value; + int ret; + + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 8); + + if (!ret) + *val = le64_to_cpu(value); + + return ret; +} + +static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) +{ + struct spi_device *spi = dev->priv; + struct device *device = spi->dev.parent->parent; + int ret; + u8 txbuf[3]; + int retval; + + spi_lock(); + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + txbuf[2] = value; + + retval = spi_write(spi, txbuf, sizeof(txbuf)); + spi_unlock(device); + + return retval; +} + +static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value) +{ + struct spi_device *spi = dev->priv; + struct device *device = spi->dev.parent->parent; + int ret; + u8 txbuf[4]; + int retval; + + spi_lock(); + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le16(value, &txbuf[2]); + + retval = spi_write(spi, txbuf, sizeof(txbuf)); + spi_unlock(device); + + return retval; +} + +static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value) +{ + struct spi_device *spi = dev->priv; + struct device *device = spi->dev.parent->parent; + int ret; + u8 txbuf[6]; + int retval; + + spi_lock(); + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le32(value, &txbuf[2]); + + retval = spi_write(spi, txbuf, sizeof(txbuf)); + spi_unlock(device); + + return retval; +} + +static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value) +{ + struct spi_device *spi = dev->priv; + struct device *device = spi->dev.parent->parent; + int ret; + u8 txbuf[10]; + int retval; + + spi_lock(); + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le64(value, &txbuf[2]); + + retval = spi_write(spi, txbuf, sizeof(txbuf) - 2); + spi_unlock(device); + + return retval; +} + +static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value) +{ + struct spi_device *spi = dev->priv; + struct device *device = spi->dev.parent->parent; + int ret; + u8 txbuf[10]; + int retval; + + spi_lock(); + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le64(value, &txbuf[2]); + + retval = spi_write(spi, txbuf, sizeof(txbuf)); + spi_unlock(device); + + return retval; +} + +static const struct b53_io_ops b53_spi_ops = { + .read8 = b53_spi_read8, + .read16 = b53_spi_read16, + .read32 = b53_spi_read32, + .read48 = b53_spi_read48, + .read64 = b53_spi_read64, + .write8 = b53_spi_write8, + .write16 = b53_spi_write16, + .write32 = b53_spi_write32, + .write48 = b53_spi_write48, + .write64 = b53_spi_write64, +}; + +static int b53_spi_probe(struct spi_device *spi) +{ + struct device *device = spi->dev.parent->parent; + struct b53_device *dev; + int ret; + struct net_device *ndev = dev_get_by_name(&init_net, "eth0"); + + if(!ndev) + return -EPROBE_DEFER; + + dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi); + if (!dev) + return -ENOMEM; + + if (spi->dev.platform_data) + dev->pdata = spi->dev.platform_data; + + pm_runtime_set_autosuspend_delay(device, 0); + + ret = b53_switch_register(dev); + if (ret) + return ret; + + spi_set_drvdata(spi, dev); + + return 0; +} + +static int b53_spi_remove(struct spi_device *spi) +{ + struct b53_device *dev = spi_get_drvdata(spi); + + if (dev) + b53_switch_remove(dev); + + spi_set_drvdata(spi, NULL); + + return 0; +} + +static void b53_spi_shutdown(struct spi_device *spi) +{ + struct b53_device *dev = spi_get_drvdata(spi); + + if (dev) + b53_switch_shutdown(dev); + + spi_set_drvdata(spi, NULL); +} + +static const struct of_device_id b53_spi_of_match[] = { + { .compatible = "brcm,bcm53134-datum" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, b53_spi_of_match); + +static const struct spi_device_id b53_spi_ids[] = { + { .name = "bcm53134-datum" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, b53_spi_ids); + +static struct spi_driver b53_spi_driver = { + .driver = { + .name = "b53-switch-datum", + .of_match_table = b53_spi_of_match, + }, + .probe = b53_spi_probe, + .remove = b53_spi_remove, + .shutdown = b53_spi_shutdown, + .id_table = b53_spi_ids, +}; + +module_spi_driver(b53_spi_driver); + +MODULE_AUTHOR("Jonas Gorski "); +MODULE_DESCRIPTION("B53 SPI access driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index d76b2377d66ef1..9c3ca5f76a6c92 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1336,7 +1336,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) unsigned int i; u32 reg, rev; int ret; - + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c index f0849cf4266f4a..a1843ac1eabd85 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c @@ -409,6 +409,7 @@ static int stm32_dwmac_probe(struct platform_device *pdev) const struct stm32_ops *data; int ret; + printk("stm32_dwmac_probe()\n"); ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1135e63a4a76e1..42e1c484cea841 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -953,7 +953,6 @@ void phy_stop_machine(struct phy_device *phydev) void phy_error(struct phy_device *phydev) { WARN_ON(1); - mutex_lock(&phydev->lock); phydev->state = PHY_HALTED; mutex_unlock(&phydev->lock); @@ -1223,7 +1222,10 @@ void phy_state_machine(struct work_struct *work) */ mutex_lock(&phydev->lock); if (phy_polling_mode(phydev) && phy_is_started(phydev)) + { + // printk("phydev poll - port=%d\n", phydev->port); phy_queue_state_machine(phydev, PHY_STATE_TIME); + } mutex_unlock(&phydev->lock); } diff --git a/include/net/dsa.h b/include/net/dsa.h index d784e76113b8d4..b4781a9a8bafcc 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -241,6 +241,7 @@ struct dsa_port { DSA_PORT_TYPE_CPU, DSA_PORT_TYPE_DSA, DSA_PORT_TYPE_USER, + DSA_PORT_TYPE_IMP } type; struct dsa_switch *ds; @@ -444,7 +445,7 @@ static inline bool dsa_port_is_cpu(struct dsa_port *port) static inline bool dsa_port_is_user(struct dsa_port *dp) { - return dp->type == DSA_PORT_TYPE_USER; + return (dp->type == DSA_PORT_TYPE_USER) || (dp->type == DSA_PORT_TYPE_IMP); } static inline bool dsa_port_is_unused(struct dsa_port *dp) @@ -469,7 +470,7 @@ static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) static inline bool dsa_is_user_port(struct dsa_switch *ds, int p) { - return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER; + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER || dsa_to_port(ds, p)->type == DSA_PORT_TYPE_IMP; } static inline u32 dsa_user_ports(struct dsa_switch *ds) @@ -905,6 +906,19 @@ struct dsa_switch_ops { int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid, u16 flags); int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid); + + /* + * Port-based VLAN operations + */ + int (*port_change_pvlan)(struct dsa_switch *ds, int port, u16 mask); + int (*port_get_pvlan)(struct dsa_switch *ds, int port, u16 *mask); + + /* + * Switch low level register read/write + */ + int (*switch_setup_get_reg)(struct dsa_switch *ds, u8 page, u8 reg, u8 size); + int (*switch_get_reg)(struct dsa_switch *ds, u8 *size, u64 *value); + int (*switch_set_reg)(struct dsa_switch *ds, u8 page, u8 reg, u8 size, u64 value); }; #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \ diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 41f36ad8b0ec67..f9a3656681ef0d 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -284,7 +284,7 @@ static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) { const struct dsa_port *dp = dsa_to_port(ds, p); - return dp->type == DSA_PORT_TYPE_USER && dp->slave; + return (dp->type == DSA_PORT_TYPE_USER || dp->type == DSA_PORT_TYPE_IMP) && dp->slave; } int dsa_switch_suspend(struct dsa_switch *ds) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 34763f575c308c..a409496c555960 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -460,6 +460,24 @@ static int dsa_port_setup(struct dsa_port *dp) break; dsa_port_enabled = true; + break; + case DSA_PORT_TYPE_IMP: + err = dsa_port_link_register_of(dp); + if (err) + break; + dsa_port_link_registered = true; + + err = dsa_port_enable(dp, NULL); + if (err) + break; + dsa_port_enabled = true; + + of_get_mac_address(dp->dn, dp->mac); + err = dsa_slave_create(dp); + if (err) + break; + + devlink_port_type_eth_set(dlp, dp->slave); break; case DSA_PORT_TYPE_DSA: err = dsa_port_link_register_of(dp); @@ -526,6 +544,9 @@ static int dsa_port_devlink_setup(struct dsa_port *dp) case DSA_PORT_TYPE_DSA: attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA; break; + case DSA_PORT_TYPE_IMP: + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + break; case DSA_PORT_TYPE_USER: attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; break; @@ -561,6 +582,19 @@ static void dsa_port_teardown(struct dsa_port *dp) dsa_port_disable(dp); dsa_port_link_unregister_of(dp); break; + case DSA_PORT_TYPE_IMP: + if (dp->slave) { + dsa_slave_destroy(dp->slave); + dp->slave = NULL; + } + break; + // dsa_port_disable(dp); + // dsa_port_link_unregister_of(dp); + // if (dp->slave) { + // dsa_slave_destroy(dp->slave); + // dp->slave = NULL; + // } + // break; case DSA_PORT_TYPE_DSA: dsa_port_disable(dp); dsa_port_link_unregister_of(dp); @@ -1224,6 +1258,17 @@ static int dsa_port_parse_dsa(struct dsa_port *dp) return 0; } +static int dsa_port_parse_imp(struct dsa_port *dp, const char *name) +{ + if (!name) + name = "imp%d"; + + dp->type = DSA_PORT_TYPE_IMP; + dp->name = name; + + return 0; +} + static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp, struct net_device *master) { @@ -1247,6 +1292,8 @@ static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp, /* If the master device is not itself a DSA slave in a disjoint DSA * tree, then return immediately. */ + tag_protocol = ds->ops->get_tag_protocol(ds, dp->index, tag_protocol); + return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol); } @@ -1335,6 +1382,7 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master, return 0; } + static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) { struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); @@ -1342,7 +1390,6 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) bool link = of_property_read_bool(dn, "link"); dp->dn = dn; - if (ethernet) { struct net_device *master; const char *user_protocol; @@ -1356,12 +1403,17 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) return dsa_port_parse_cpu(dp, master, user_protocol); } + if(of_property_read_bool(dn, "port-imp")) { + return dsa_port_parse_imp(dp, name); + } + if (link) return dsa_port_parse_dsa(dp); return dsa_port_parse_user(dp, name); } + static int dsa_switch_parse_ports_of(struct dsa_switch *ds, struct device_node *dn) { @@ -1369,6 +1421,7 @@ static int dsa_switch_parse_ports_of(struct dsa_switch *ds, struct dsa_port *dp; int err = 0; u32 reg; + int np = 0; ports = of_get_child_by_name(dn, "ports"); if (!ports) { @@ -1402,6 +1455,7 @@ static int dsa_switch_parse_ports_of(struct dsa_switch *ds, of_node_put(port); goto out_put_node; } + np++; } out_put_node: @@ -1465,7 +1519,8 @@ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) if (err) return err; - return dsa_switch_parse_ports_of(ds, dn); + err = dsa_switch_parse_ports_of(ds, dn); + return err; } static int dsa_port_parse(struct dsa_port *dp, const char *name, @@ -1592,7 +1647,6 @@ static int dsa_switch_probe(struct dsa_switch *ds) dsa_switch_release_ports(ds); dsa_tree_put(dst); } - return err; } diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index e91265434354e5..f5fa17d5976b96 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -191,7 +191,7 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, list_for_each_entry(dp, &dst->ports, list) if (dp->ds->index == device && dp->index == port && - dp->type == DSA_PORT_TYPE_USER) + (dp->type == DSA_PORT_TYPE_USER || dp->type == DSA_PORT_TYPE_IMP)) return dp->slave; return NULL; diff --git a/net/dsa/master.c b/net/dsa/master.c index 69ec510abe83cb..02d7ee8f37f1c8 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -271,7 +271,7 @@ static void dsa_master_set_promiscuity(struct net_device *dev, int inc) rtnl_unlock(); } -static ssize_t tagging_show(struct device *d, struct device_attribute *attr, +static ssize_t tagging_cpu_show(struct device *d, struct device_attribute *attr, char *buf) { struct net_device *dev = to_net_dev(d); @@ -281,7 +281,7 @@ static ssize_t tagging_show(struct device *d, struct device_attribute *attr, dsa_tag_protocol_to_str(cpu_dp->tag_ops)); } -static ssize_t tagging_store(struct device *d, struct device_attribute *attr, +static ssize_t tagging_cpu_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { const struct dsa_device_ops *new_tag_ops, *old_tag_ops; @@ -317,10 +317,198 @@ static ssize_t tagging_store(struct device *d, struct device_attribute *attr, dsa_tag_driver_put(old_tag_ops); return count; } -static DEVICE_ATTR_RW(tagging); +static DEVICE_ATTR_RW(tagging_cpu); + +static ssize_t tagging_imp_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch *ds = cpu_dp->ds; + const struct dsa_device_ops *tag_ops; + enum dsa_tag_protocol proto; + + proto = ds->ops->get_tag_protocol(ds, 8, DSA_TAG_PROTO_NONE); + tag_ops = dsa_tag_driver_get(proto); + return sprintf(buf, "%s\n", dsa_tag_protocol_to_str(tag_ops)); +} + +static ssize_t tagging_imp_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + const struct dsa_device_ops *new_tag_ops; + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch *ds = cpu_dp->ds; + + new_tag_ops = dsa_find_tagger_by_name(buf); + /* Bad tagger name, or module is not loaded? */ + if (IS_ERR(new_tag_ops)) + return PTR_ERR(new_tag_ops); + + if(!ds->ops->change_tag_protocol) + return -EOPNOTSUPP; + + ds->ops->change_tag_protocol(ds, 8, new_tag_ops->proto); + return count; +} +static DEVICE_ATTR_RW(tagging_imp); + +static ssize_t pvlan_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->ds->dst; + struct dsa_port *dp; + int len = 0; + u16 value; + + list_for_each_entry(dp, &dst->ports, list) { + if (dp->type |= DSA_PORT_TYPE_UNUSED) { + if(!dp->ds->ops->port_change_pvlan) + return -EOPNOTSUPP; + dp->ds->ops->port_get_pvlan(dp->ds, dp->index, &value); + len += sprintf(buf + len, "%d:%03hx ", dp->index, value); + } + } + + len += sprintf(buf + len, "\n"); + + return len; +} + +static ssize_t pvlan_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->ds->dst; + struct dsa_switch *ds = cpu_dp->ds; + struct dsa_port *dp; + int index; + u16 value; + int err; + + // Parse the input + err = sscanf(buf, "%d:%hx", &index, &value); + if (err != 2) + return -EINVAL; // Invalid format + + // Find the port with the given index + list_for_each_entry(dp, &dst->ports, list) { + if (dp->index == index && dp->type != DSA_PORT_TYPE_UNUSED) { + if(!dp->ds->ops->port_get_pvlan) + return -EOPNOTSUPP; + // Apply the new value + dp->ds->ops->port_change_pvlan(ds, index, value); + return count; + } + } + + // No port with the given index was found + return -EINVAL; +} + +static DEVICE_ATTR_RW(pvlan); + +static ssize_t rdreg_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch *ds = cpu_dp->ds; + u8 size; + u64 value; + int len = 0; + + if(ds->ops->switch_get_reg(ds, &size, &value)) + return -EIO; + switch(size) { + case 1: + len += sprintf(buf + len, "%hhx\n", (u8)value); + break; + case 2: + len += sprintf(buf + len, "%hx\n", (u16)value); + break; + case 4: + len += sprintf(buf + len, "%x\n", (u32)value); + break; + case 6: + len += sprintf(buf + len, "%llx\n", value); + break; + case 8: + len += sprintf(buf + len, "%llx\n", value); + break; + default: + return -EIO; + } + return len; +} + +static ssize_t rdreg_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch *ds = cpu_dp->ds; + u8 page; + u8 reg; + u8 size; + int err; + + // Parse the input + err = sscanf(buf, "%hhx:%hhx:%hhx" , &page, ®, &size); + if (err != 3) + return -EINVAL; // Invalid format + + // Write the value to the private data + if(ds->ops->switch_setup_get_reg(ds, page, reg, size)) + return -EIO; + + return count; +} + +static DEVICE_ATTR_RW(rdreg); + +static ssize_t wrreg_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + return -EPERM; +} + +static ssize_t wrreg_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *dev = to_net_dev(d); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch *ds = cpu_dp->ds; + u8 page; + u8 reg; + u8 size; + u64 value; + int err; + + // Parse the input + err = sscanf(buf, "%hhx:%hhx:%hhx:%llx" , &page, ®, &size, &value); + if (err != 4) + return -EINVAL; // Invalid format + + // Write the value to the switch + if(ds->ops->switch_set_reg(ds, page, reg, size, value)) + return -EIO; + + return count; +} + +static DEVICE_ATTR_RW(wrreg); static struct attribute *dsa_slave_attrs[] = { - &dev_attr_tagging.attr, + &dev_attr_tagging_cpu.attr, + &dev_attr_tagging_imp.attr, + &dev_attr_pvlan.attr, + &dev_attr_rdreg.attr, + &dev_attr_wrreg.attr, NULL }; @@ -329,6 +517,7 @@ static const struct attribute_group dsa_group = { .attrs = dsa_slave_attrs, }; + static void dsa_master_reset_mtu(struct net_device *dev) { int err; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 11ec9e689589b0..080a9121f6a2bf 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1546,7 +1546,7 @@ static void dsa_bridge_mtu_normalization(struct dsa_port *dp) struct dsa_hw_port *hw_port; struct net_device *slave; - if (other_dp->type != DSA_PORT_TYPE_USER) + if ((other_dp->type != DSA_PORT_TYPE_USER) && (other_dp->type != DSA_PORT_TYPE_IMP)) continue; if (other_dp->bridge_dev != dp->bridge_dev) @@ -1589,98 +1589,30 @@ static void dsa_bridge_mtu_normalization(struct dsa_port *dp) dsa_hw_port_list_free(&hw_port_list); } +/* Datum - rewrite to unlink slave (switch ports) from master (eth0) */ int dsa_slave_change_mtu(struct net_device *dev, int new_mtu) { - struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_switch *ds = p->dp->ds; - struct dsa_port *dp_iter; - struct dsa_port *cpu_dp; - int port = p->dp->index; - int largest_mtu = 0; - int new_master_mtu; - int old_master_mtu; int mtu_limit; - int cpu_mtu; int err; if (!ds->ops->port_change_mtu) return -EOPNOTSUPP; - list_for_each_entry(dp_iter, &ds->dst->ports, list) { - int slave_mtu; - - if (!dsa_port_is_user(dp_iter)) - continue; - - /* During probe, this function will be called for each slave - * device, while not all of them have been allocated. That's - * ok, it doesn't change what the maximum is, so ignore it. - */ - if (!dp_iter->slave) - continue; - - /* Pretend that we already applied the setting, which we - * actually haven't (still haven't done all integrity checks) - */ - if (dp_iter == dp) - slave_mtu = new_mtu; - else - slave_mtu = dp_iter->slave->mtu; - - if (largest_mtu < slave_mtu) - largest_mtu = slave_mtu; - } - - cpu_dp = dsa_to_port(ds, port)->cpu_dp; + mtu_limit = dev->max_mtu; - mtu_limit = min_t(int, master->max_mtu, dev->max_mtu); - old_master_mtu = master->mtu; - new_master_mtu = largest_mtu + dsa_tag_protocol_overhead(cpu_dp->tag_ops); - if (new_master_mtu > mtu_limit) + if(new_mtu > mtu_limit) return -ERANGE; - /* If the master MTU isn't over limit, there's no need to check the CPU - * MTU, since that surely isn't either. - */ - cpu_mtu = largest_mtu; - - /* Start applying stuff */ - if (new_master_mtu != old_master_mtu) { - err = dev_set_mtu(master, new_master_mtu); - if (err < 0) - goto out_master_failed; - - /* We only need to propagate the MTU of the CPU port to - * upstream switches, so create a non-targeted notifier which - * updates all switches. - */ - err = dsa_port_mtu_change(cpu_dp, cpu_mtu, false); - if (err) - goto out_cpu_failed; - } - err = dsa_port_mtu_change(dp, new_mtu, true); if (err) - goto out_port_failed; + return err; dev->mtu = new_mtu; - dsa_bridge_mtu_normalization(dp); - return 0; - -out_port_failed: - if (new_master_mtu != old_master_mtu) - dsa_port_mtu_change(cpu_dp, old_master_mtu - - dsa_tag_protocol_overhead(cpu_dp->tag_ops), - false); -out_cpu_failed: - if (new_master_mtu != old_master_mtu) - dev_set_mtu(master, old_master_mtu); -out_master_failed: - return err; } static const struct ethtool_ops dsa_slave_ethtool_ops = {