diff --git a/packages/base/any/kernels/4.9-lts/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/4.9-lts/configs/x86_64-all/x86_64-all.config index b25358ec..90778fc2 100644 --- a/packages/base/any/kernels/4.9-lts/configs/x86_64-all/x86_64-all.config +++ b/packages/base/any/kernels/4.9-lts/configs/x86_64-all/x86_64-all.config @@ -3526,6 +3526,7 @@ CONFIG_MLX_PLATFORM=y # CONFIG_CHROME_PLATFORMS is not set CONFIG_MELLANOX_PLATFORM=y CONFIG_MLXREG_HOTPLUG=y +CONFIG_MLXREG_IO=y # # Hardware Spinlock drivers diff --git a/packages/base/any/kernels/4.9-lts/patches/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch b/packages/base/any/kernels/4.9-lts/patches/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch new file mode 100644 index 00000000..db22b149 --- /dev/null +++ b/packages/base/any/kernels/4.9-lts/patches/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch @@ -0,0 +1,685 @@ +From 2c7476ab57dd42d8cba6c417ff32a77252964858 Mon Sep 17 00:00:00 2001 +From: Vadim Pasternak +Date: Thu, 16 Nov 2017 17:22:56 +0000 +Subject: [v4.9 backport 38/38] platform: mellonox: introduce mlxreg-io driver + and add driver activation to mlx-platform + +Patch introduces new module mlxreg-io, which exposes the registers of the +programmable devices, equipped on Melanox systems to sysfs. These are the +registers, which are used for system resets operation, system reset causes +monitoring, select operation and version info. + +Signed-off-by: Vadim Pasternak +--- + drivers/leds/leds-mlxreg.c | 10 +- + drivers/platform/mellanox/Kconfig | 11 ++ + drivers/platform/mellanox/Makefile | 1 + + drivers/platform/mellanox/mlxreg-io.c | 211 ++++++++++++++++++++++++++++++++++ + drivers/platform/x86/mlx-platform.c | 193 +++++++++++++++++++++++++++++-- + include/linux/platform_data/mlxreg.h | 6 +- + 6 files changed, 418 insertions(+), 14 deletions(-) + create mode 100644 drivers/platform/mellanox/mlxreg-io.c + +diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c +index a932f20..036c214 100644 +--- a/drivers/leds/leds-mlxreg.c ++++ b/drivers/leds/leds-mlxreg.c +@@ -79,7 +79,7 @@ struct mlxreg_led_data { + */ + struct mlxreg_led_priv_data { + struct platform_device *pdev; +- struct mlxreg_core_led_platform_data *pdata; ++ struct mlxreg_core_platform_data *pdata; + struct mutex access_lock; /* protect IO operations */ + }; + +@@ -87,7 +87,7 @@ static int + mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset) + { + struct mlxreg_led_priv_data *priv = led_data->data_parent; +- struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_platform_data *led_pdata = priv->pdata; + struct mlxreg_core_data *data = led_data->data; + u32 regval; + u32 nib; +@@ -125,7 +125,7 @@ static enum led_brightness + mlxreg_led_get_hw(struct mlxreg_led_data *led_data) + { + struct mlxreg_led_priv_data *priv = led_data->data_parent; +- struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_platform_data *led_pdata = priv->pdata; + struct mlxreg_core_data *data = led_data->data; + u32 regval; + int ret; +@@ -212,7 +212,7 @@ mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on, + + static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) + { +- struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_platform_data *led_pdata = priv->pdata; + struct mlxreg_core_data *data = led_pdata->data; + struct mlxreg_led_data *led_data; + struct led_classdev *led_cdev; +@@ -266,7 +266,7 @@ static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) + + static int mlxreg_led_probe(struct platform_device *pdev) + { +- struct mlxreg_core_led_platform_data *led_pdata; ++ struct mlxreg_core_platform_data *led_pdata; + struct mlxreg_led_priv_data *priv; + + led_pdata = dev_get_platdata(&pdev->dev); +diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig +index b197cc1..5c6dc29 100644 +--- a/drivers/platform/mellanox/Kconfig ++++ b/drivers/platform/mellanox/Kconfig +@@ -22,4 +22,15 @@ config MLXREG_HOTPLUG + This driver handles hot-plug events for the power suppliers, power + cables and fans on the wide range Mellanox IB and Ethernet systems. + ++config MLXREG_IO ++ tristate "Mellanox platform register driver support" ++ depends on REGMAP ++ depends on HWMON ++ ---help--- ++ This driver allows access to Mellanox programmable device register ++ space trough sysfs interface. The set of registers for sysfs access ++ are defined per system type bases and includes the registers related ++ to system resets operation, system reset causes monitoring and some ++ kinds of mux selection. ++ + endif # MELLANOX_PLATFORM +diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile +index f58d089..b9a2692 100644 +--- a/drivers/platform/mellanox/Makefile ++++ b/drivers/platform/mellanox/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o ++obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o +diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c +new file mode 100644 +index 0000000..f7434ca +--- /dev/null ++++ b/drivers/platform/mellanox/mlxreg-io.c +@@ -0,0 +1,211 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Attribute parameters. */ ++#define MLXREG_IO_ATT_SIZE 10 ++#define MLXREG_IO_ATT_NUM 48 ++ ++/** ++ * struct mlxreg_io_priv_data - driver's private data: ++ * ++ * @pdev: platform device; ++ * @pdata: platform data; ++ * @hwmon: hwmon device; ++ * @mlxreg_io_attr: sysfs attributes array; ++ * @mlxreg_io_dev_attr: sysfs sensor device attribute array; ++ * @group: sysfs attribute group; ++ * @groups: list of sysfs attribute group for hwmon registration; ++ */ ++struct mlxreg_io_priv_data { ++ struct platform_device *pdev; ++ struct mlxreg_core_platform_data *pdata; ++ struct device *hwmon; ++ struct attribute *mlxreg_io_attr[MLXREG_IO_ATT_NUM + 1]; ++ struct sensor_device_attribute mlxreg_io_dev_attr[MLXREG_IO_ATT_NUM]; ++ struct attribute_group group; ++ const struct attribute_group *groups[2]; ++}; ++ ++static ssize_t ++mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev); ++ int index = to_sensor_dev_attr(attr)->index; ++ struct mlxreg_core_data *data = priv->pdata->data + index; ++ u32 regval = 0; ++ int ret; ++ ++ ret = regmap_read(priv->pdata->regmap, data->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ if (!data->bit) ++ regval = !!(regval & ~data->mask); ++ ++ return sprintf(buf, "%u\n", regval); ++ ++access_error: ++ return ret; ++} ++ ++static ssize_t ++mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev); ++ int index = to_sensor_dev_attr(attr)->index; ++ struct mlxreg_core_data *data = priv->pdata->data + index; ++ u32 val, regval; ++ int ret; ++ ++ ret = kstrtou32(buf, MLXREG_IO_ATT_SIZE, &val); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(priv->pdata->regmap, data->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ regval &= data->mask; ++ ++ val = !!val; ++ if (val) ++ regval |= ~data->mask; ++ else ++ regval &= data->mask; ++ ++ ret = regmap_write(priv->pdata->regmap, data->reg, regval); ++ if (ret) ++ goto access_error; ++ ++ return len; ++ ++access_error: ++ dev_err(&priv->pdev->dev, "Bus access error\n"); ++ return ret; ++} ++ ++static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv) ++{ ++ int i; ++ ++ priv->group.attrs = devm_kzalloc(&priv->pdev->dev, ++ priv->pdata->counter * ++ sizeof(struct attribute *), ++ GFP_KERNEL); ++ if (!priv->group.attrs) ++ return -ENOMEM; ++ ++ for (i = 0; i < priv->pdata->counter; i++) { ++ priv->mlxreg_io_attr[i] = ++ &priv->mlxreg_io_dev_attr[i].dev_attr.attr; ++ ++ /* Set attribute name as a label. */ ++ priv->mlxreg_io_attr[i]->name = ++ devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, ++ priv->pdata->data[i].label); ++ ++ if (!priv->mlxreg_io_attr[i]->name) { ++ dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n", ++ i + 1); ++ return -ENOMEM; ++ } ++ ++ priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode = ++ priv->pdata->data[i].mode; ++ switch (priv->pdata->data[i].mode) { ++ case 0200: ++ priv->mlxreg_io_dev_attr[i].dev_attr.store = ++ mlxreg_io_attr_store; ++ break; ++ ++ case 0444: ++ priv->mlxreg_io_dev_attr[i].dev_attr.show = ++ mlxreg_io_attr_show; ++ break; ++ ++ case 0644: ++ priv->mlxreg_io_dev_attr[i].dev_attr.show = ++ mlxreg_io_attr_show; ++ priv->mlxreg_io_dev_attr[i].dev_attr.store = ++ mlxreg_io_attr_store; ++ break; ++ ++ default: ++ dev_err(&priv->pdev->dev, "Bad access mode %u for attribute %s.\n", ++ priv->pdata->data[i].mode, ++ priv->mlxreg_io_attr[i]->name); ++ return -EINVAL; ++ } ++ ++ priv->mlxreg_io_dev_attr[i].dev_attr.attr.name = ++ priv->mlxreg_io_attr[i]->name; ++ priv->mlxreg_io_dev_attr[i].index = i; ++ sysfs_attr_init(&priv->mlxreg_io_dev_attr[i].dev_attr.attr); ++ } ++ ++ priv->group.attrs = priv->mlxreg_io_attr; ++ priv->groups[0] = &priv->group; ++ priv->groups[1] = NULL; ++ ++ return 0; ++} ++ ++static int mlxreg_io_probe(struct platform_device *pdev) ++{ ++ struct mlxreg_io_priv_data *priv; ++ int err; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->pdata = dev_get_platdata(&pdev->dev); ++ if (!priv->pdata) { ++ dev_err(&pdev->dev, "Failed to get platform data.\n"); ++ return -EINVAL; ++ } ++ ++ priv->pdev = pdev; ++ ++ err = mlxreg_io_attr_init(priv); ++ if (err) { ++ dev_err(&priv->pdev->dev, "Failed to allocate attributes: %d\n", ++ err); ++ return err; ++ } ++ ++ priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, ++ "mlxreg_io", priv, priv->groups); ++ if (IS_ERR(priv->hwmon)) { ++ dev_err(&pdev->dev, "Failed to register hwmon device %ld\n", ++ PTR_ERR(priv->hwmon)); ++ return PTR_ERR(priv->hwmon); ++ } ++ ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ return 0; ++} ++ ++static struct platform_driver mlxreg_io_driver = { ++ .driver = { ++ .name = "mlxreg-io", ++ }, ++ .probe = mlxreg_io_probe, ++}; ++ ++module_platform_driver(mlxreg_io_driver); ++ ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox regmap I/O access driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:mlxreg-io"); +diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c +index 49721c2..61cbe35 100644 +--- a/drivers/platform/x86/mlx-platform.c ++++ b/drivers/platform/x86/mlx-platform.c +@@ -47,16 +47,31 @@ + /* LPC bus IO offsets */ + #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 + #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 ++#define MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF 0x00 ++#define MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF 0x01 ++#define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF 0x1d + #define MLXPLAT_CPLD_LPC_REG_LED1_OFF 0x20 + #define MLXPLAT_CPLD_LPC_REG_LED2_OFF 0x21 + #define MLXPLAT_CPLD_LPC_REG_LED3_OFF 0x22 + #define MLXPLAT_CPLD_LPC_REG_LED4_OFF 0x23 + #define MLXPLAT_CPLD_LPC_REG_LED5_OFF 0x24 ++#define MLXPLAT_CPLD_LPC_REG_GP1_OFF 0x30 ++#define MLXPLAT_CPLD_LPC_REG_WP1_OFF 0x31 ++#define MLXPLAT_CPLD_LPC_REG_GP2_OFF 0x32 ++#define MLXPLAT_CPLD_LPC_REG_WP2_OFF 0x33 + #define MLXPLAT_CPLD_LPC_REG_AGGR_OFF 0x3a ++#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF 0x3b + #define MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF 0x40 ++#define MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF 0x41 + #define MLXPLAT_CPLD_LPC_REG_PSU_OFF 0x58 ++#define MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF 0x59 ++#define MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF 0x5a + #define MLXPLAT_CPLD_LPC_REG_PWR_OFF 0x64 ++#define MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF 0x65 ++#define MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF 0x66 + #define MLXPLAT_CPLD_LPC_REG_FAN_OFF 0x88 ++#define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF 0x89 ++#define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF 0x8a + #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 + #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb + #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda +@@ -100,12 +115,14 @@ + * @pdev_mux - array of mux platform devices + * @pdev_hotplug - hotplug platform devices + * @pdev_led - led platform devices ++ * @pdev_io_regs - register access platform devices + */ + struct mlxplat_priv { + struct platform_device *pdev_i2c; + struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS]; + struct platform_device *pdev_hotplug; + struct platform_device *pdev_led; ++ struct platform_device *pdev_io_regs; + }; + + /* Regions for LPC I2C controller and LPC base register space */ +@@ -643,7 +660,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = { + }, + }; + +-static struct mlxreg_core_led_platform_data mlxplat_default_led_data = { ++static struct mlxreg_core_platform_data mlxplat_default_led_data = { + .data = mlxplat_mlxcpld_default_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_data), + }; +@@ -697,7 +714,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = { + }, + }; + +-static struct mlxreg_core_led_platform_data mlxplat_msn21xx_led_data = { ++static struct mlxreg_core_platform_data mlxplat_msn21xx_led_data = { + .data = mlxplat_mlxcpld_msn21xx_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_led_data), + }; +@@ -786,11 +803,105 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = { + }, + }; + +-static struct mlxreg_core_led_platform_data mlxplat_default_ng_led_data = { ++static struct mlxreg_core_platform_data mlxplat_default_ng_led_data = { + .data = mlxplat_mlxcpld_default_ng_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_led_data), + }; + ++static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case MLXPLAT_CPLD_LPC_REG_LED1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED3_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED4_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED5_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF: ++ return true; ++ } ++ return false; ++} ++ ++static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED3_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED4_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED5_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF: ++ return true; ++ } ++ return false; ++} ++ ++static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED3_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED4_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED5_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF: ++ return true; ++ } ++ return false; ++} ++ ++static const struct reg_default mlxplat_mlxcpld_regmap_default[] = { ++ { MLXPLAT_CPLD_LPC_REG_WP1_OFF, 0x00 }, ++ { MLXPLAT_CPLD_LPC_REG_WP2_OFF, 0x00 }, ++}; ++ + static int + mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val) + { +@@ -809,6 +920,12 @@ const struct regmap_config mlxplat_mlxcpld_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 255, ++ .cache_type = REGCACHE_FLAT, ++ .writeable_reg = mlxplat_mlxcpld_writeable_reg, ++ .readable_reg = mlxplat_mlxcpld_readable_reg, ++ .volatile_reg = mlxplat_mlxcpld_volatile_reg, ++ .reg_defaults = mlxplat_mlxcpld_regmap_default, ++ .num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_default), + .reg_read = mlxplat_mlxcpld_reg_read, + .reg_write = mlxplat_mlxcpld_reg_write, + }; +@@ -817,9 +934,38 @@ static struct resource mlxplat_mlxcpld_resources[] = { + [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"), + }; + +-struct platform_device *mlxplat_dev; +-struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; +-struct mlxreg_core_led_platform_data *mlxplat_led; ++static struct mlxreg_core_data mlxplat_mlxcpld_default_regs_io_data[] = { ++ { "cpld1_version", MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF, 0x00, ++ GENMASK(7, 0), 0444 }, ++ { "cpld2_version", MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF, 0x00, ++ GENMASK(7, 0), 0444 }, ++ { "cause_long_pb", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(0), 0x00, 0444 }, ++ { "cause_short_pb", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(1), 0x00, 0444 }, ++ { "cause_pwr_aux", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(2), 0x00, 0444 }, ++ { "cause_pwr_fail", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(3), 0x00, 0444 }, ++ { "psu1_on", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(0), ++ 0x00, 0200 }, ++ { "psu2_on", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(1), ++ 0x00, 0200 }, ++ { "pwr_cycle", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(2), ++ 0x00, 0200 }, ++ { "select_iio", MLXPLAT_CPLD_LPC_REG_GP2_OFF, GENMASK(7, 0) & ~BIT(6), ++ 0x00, 0644 }, ++}; ++ ++static struct mlxreg_core_platform_data mlxplat_default_regs_io_data = { ++ .data = mlxplat_mlxcpld_default_regs_io_data, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_regs_io_data), ++}; ++ ++static struct platform_device *mlxplat_dev; ++static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; ++static struct mlxreg_core_platform_data *mlxplat_led; ++static struct mlxreg_core_platform_data *mlxplat_regs_io; + + static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) + { +@@ -832,6 +978,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_data; + mlxplat_led = &mlxplat_default_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -847,6 +994,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; + mlxplat_led = &mlxplat_msn21xx_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -862,6 +1010,7 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data; + mlxplat_led = &mlxplat_default_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -877,6 +1026,7 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data; + mlxplat_led = &mlxplat_default_ng_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -892,6 +1042,7 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data; + mlxplat_led = &mlxplat_msn21xx_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -974,7 +1125,7 @@ static int __init mlxplat_init(void) + { + struct mlxplat_priv *priv; + void __iomem *base; +- int i, err = 0; ++ int i, j, err = 0; + + if (!dmi_check_system(mlxplat_dmi_table)) + return -ENODEV; +@@ -1023,6 +1174,15 @@ static int __init mlxplat_init(void) + if (IS_ERR(mlxplat_hotplug->regmap)) + goto fail_platform_mux_register; + ++ /* Set default registers. */ ++ for (j = 0; j < mlxplat_mlxcpld_regmap_config.num_reg_defaults; j++) { ++ err = regmap_write(mlxplat_hotplug->regmap, ++ mlxplat_mlxcpld_regmap_default[j].reg, ++ mlxplat_mlxcpld_regmap_default[j].def); ++ if (err) ++ goto fail_platform_mux_register; ++ } ++ + priv->pdev_hotplug = platform_device_register_resndata( + &mlxplat_dev->dev, "mlxreg-hotplug", + PLATFORM_DEVID_NONE, +@@ -1044,8 +1204,26 @@ static int __init mlxplat_init(void) + goto fail_platform_hotplug_register; + } + ++ mlxplat_regs_io->regmap = mlxplat_hotplug->regmap; ++ priv->pdev_io_regs = platform_device_register_resndata( ++ &mlxplat_dev->dev, "mlxreg-io", ++ PLATFORM_DEVID_NONE, NULL, 0, ++ mlxplat_regs_io, sizeof(*mlxplat_regs_io)); ++ if (IS_ERR(priv->pdev_io_regs)) { ++ err = PTR_ERR(priv->pdev_io_regs); ++ goto fail_platform_led_register; ++ } ++ ++ /* Sync registers with hardware. */ ++ regcache_mark_dirty(mlxplat_hotplug->regmap); ++ err = regcache_sync(mlxplat_hotplug->regmap); ++ if (err) ++ goto fail_platform_led_register; ++ + return 0; + ++fail_platform_led_register: ++ platform_device_unregister(priv->pdev_led); + fail_platform_hotplug_register: + platform_device_unregister(priv->pdev_hotplug); + fail_platform_mux_register: +@@ -1064,6 +1242,7 @@ static void __exit mlxplat_exit(void) + struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); + int i; + ++ platform_device_unregister(priv->pdev_io_regs); + platform_device_unregister(priv->pdev_led); + platform_device_unregister(priv->pdev_hotplug); + +diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h +index dd471c5..c25623b 100644 +--- a/include/linux/platform_data/mlxreg.h ++++ b/include/linux/platform_data/mlxreg.h +@@ -61,6 +61,7 @@ struct mlxreg_hotplug_device { + * @label: attribute register offset; + * @reg: attribute register; + * @mask: attribute access mask; ++ * @mode: access mode; + * @bit: attribute effective bit; + * @np - pointer to node platform associated with attribute; + * @hpdev - hotplug device data; +@@ -72,6 +73,7 @@ struct mlxreg_core_data { + u32 reg; + u32 mask; + u32 bit; ++ umode_t mode; + struct device_node *np; + struct mlxreg_hotplug_device hpdev; + u8 health_cntr; +@@ -104,13 +106,13 @@ struct mlxreg_core_item { + }; + + /** +- * struct mlxreg_core_led_platform_data - led platform data: ++ * struct mlxreg_core_platform_data - platform data: + * + * @led_data: led private data; + * @regmap: register map of parent device; + * @counter: number of led instances; + */ +-struct mlxreg_core_led_platform_data { ++struct mlxreg_core_platform_data { + struct mlxreg_core_data *data; + void *regmap; + int counter; +-- +2.1.4 + diff --git a/packages/base/any/kernels/4.9-lts/patches/0010-platform-mellanox-mlxreg-hotplug-driver-add-check-fo.patch b/packages/base/any/kernels/4.9-lts/patches/0010-platform-mellanox-mlxreg-hotplug-driver-add-check-fo.patch new file mode 100644 index 00000000..ef29d134 --- /dev/null +++ b/packages/base/any/kernels/4.9-lts/patches/0010-platform-mellanox-mlxreg-hotplug-driver-add-check-fo.patch @@ -0,0 +1,53 @@ +From c794f8ffa6521c47bfbff813e7f713561d7da7bd Mon Sep 17 00:00:00 2001 +From: Vadim Pasternak +Date: Mon, 11 Dec 2017 19:02:19 +0000 +Subject: [v4.9 backport 09/29] platform/mellanox: mlxreg-hotplug driver add + check for low aggregation register mask + +It adds verification for low aggregation register mask offset. Only +non-zero offset is considered as valid. + +Signed-off-by: Vadim Pasternak +--- + drivers/platform/mellanox/mlxreg-hotplug.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c +index 94fdb6b..ba9241e 100644 +--- a/drivers/platform/mellanox/mlxreg-hotplug.c ++++ b/drivers/platform/mellanox/mlxreg-hotplug.c +@@ -550,10 +550,13 @@ static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) + goto access_error; + + /* Keep low aggregation initial status as zero and unmask events. */ +- ret = regmap_write(priv->regmap, pdata->cell_low + +- MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask_low); +- if (ret) +- goto access_error; ++ if (pdata->cell_low) { ++ ret = regmap_write(priv->regmap, pdata->cell_low + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, ++ pdata->mask_low); ++ if (ret) ++ goto access_error; ++ } + + /* Invoke work handler for initializing hot plug devices setting. */ + mlxreg_hotplug_work_handler(&priv->dwork_irq.work); +@@ -582,9 +585,10 @@ static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv) + disable_irq(priv->irq); + cancel_delayed_work_sync(&priv->dwork_irq); + +- /* Mask low aggregation event. */ +- regmap_write(priv->regmap, pdata->cell_low + +- MLXREG_HOTPLUG_AGGR_MASK_OFF, 0); ++ /* Mask low aggregation event, if defined. */ ++ if (pdata->cell_low) ++ regmap_write(priv->regmap, pdata->cell_low + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, 0); + + /* Mask aggregation event. */ + regmap_write(priv->regmap, pdata->cell + MLXREG_HOTPLUG_AGGR_MASK_OFF, +-- +2.1.4 + diff --git a/packages/base/any/kernels/4.9-lts/patches/0011-platform-x86-mlx-platform-new-features.patch b/packages/base/any/kernels/4.9-lts/patches/0011-platform-x86-mlx-platform-new-features.patch new file mode 100644 index 00000000..f2d4e116 --- /dev/null +++ b/packages/base/any/kernels/4.9-lts/patches/0011-platform-x86-mlx-platform-new-features.patch @@ -0,0 +1,472 @@ +diff --git a/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c b/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c +index 3bc6cf8..07cc7ea 100644 +--- a/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c ++++ b/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c +@@ -33,6 +33,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -49,7 +50,8 @@ + #define MLXSW_QSFP_MAX_NUM 64 + #define MLXSW_QSFP_MIN_REQ_LEN 4 + #define MLXSW_QSFP_STATUS_VALID_TIME (120 * HZ) +-#define MLXSW_QSFP_MAX_CPLD_NUM 1 ++#define MLXSW_QSFP_MAX_CPLD_NUM 3 ++#define MLXSW_QSFP_MIN_CPLD_NUM 1 + + static const u8 mlxsw_qsfp_page_number[] = { 0xa0, 0x00, 0x01, 0x02, 0x03 }; + static const u16 mlxsw_qsfp_page_shift[] = { 0x00, 0x80, 0x80, 0x80, 0x80 }; +@@ -85,6 +87,8 @@ struct mlxsw_qsfp { + struct device_attribute *cpld_dev_attrs; + }; + ++static int mlxsw_qsfp_cpld_num = MLXSW_QSFP_MIN_CPLD_NUM; ++ + static int + mlxsw_qsfp_query_module_eeprom(struct mlxsw_qsfp *mlxsw_qsfp, u8 index, + loff_t off, size_t count, int page, char *buf) +@@ -210,11 +214,11 @@ mlxsw_qsfp_cpld_show(struct device *dev, struct device_attribute *attr, + u32 version, i; + int err; + +- for (i = 0; i < MLXSW_QSFP_MAX_CPLD_NUM; i++) { ++ for (i = 0; i < mlxsw_qsfp_cpld_num; i++) { + if ((mlxsw_qsfp->cpld_dev_attrs + i) == attr) + break; + } +- if (i == MLXSW_QSFP_MAX_CPLD_NUM) ++ if (i == mlxsw_qsfp_cpld_num) + return -EINVAL; + + mlxsw_reg_msci_pack(msci_pl, i); +@@ -227,6 +231,32 @@ mlxsw_qsfp_cpld_show(struct device *dev, struct device_attribute *attr, + return sprintf(buf, "%u\n", version); + } + ++static int __init mlxsw_qsfp_dmi_set_cpld_num(const struct dmi_system_id *dmi) ++{ ++ mlxsw_qsfp_cpld_num = MLXSW_QSFP_MAX_CPLD_NUM; ++ ++ return 1; ++}; ++ ++static struct dmi_system_id mlxsw_qsfp_dmi_table[] __initdata = { ++ { ++ .callback = mlxsw_qsfp_dmi_set_cpld_num, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"), ++ }, ++ }, ++ { ++ .callback = mlxsw_qsfp_dmi_set_cpld_num, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"), ++ }, ++ }, ++ { } ++}; ++MODULE_DEVICE_TABLE(dmi, mlxsw_qsfp_dmi_table); ++ + int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + struct mlxsw_qsfp **p_qsfp) +@@ -242,6 +272,8 @@ int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, + if (!strcmp(mlxsw_bus_info->device_kind, "i2c")) + return 0; + ++ dmi_check_system(mlxsw_qsfp_dmi_table); ++ + mlxsw_qsfp = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_qsfp), + GFP_KERNEL); + if (!mlxsw_qsfp) +@@ -285,7 +317,7 @@ int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, + return -ENOMEM; + + mlxsw_qsfp->cpld_dev_attrs = devm_kzalloc(mlxsw_bus_info->dev, +- MLXSW_QSFP_MAX_CPLD_NUM * ++ mlxsw_qsfp_cpld_num * + sizeof(*mlxsw_qsfp->cpld_dev_attrs), + GFP_KERNEL); + if (!mlxsw_qsfp->cpld_dev_attrs) +@@ -323,7 +355,7 @@ int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, + } + + cpld_dev_attr = mlxsw_qsfp->cpld_dev_attrs; +- for (i = 0; i < MLXSW_QSFP_MAX_CPLD_NUM; i++, cpld_dev_attr++) { ++ for (i = 0; i < mlxsw_qsfp_cpld_num; i++, cpld_dev_attr++) { + cpld_dev_attr->show = mlxsw_qsfp_cpld_show; + cpld_dev_attr->attr.mode = 0444; + cpld_dev_attr->attr.name = devm_kasprintf(mlxsw_bus_info->dev, +diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c +index ba9241e..5c13591 100644 +--- a/drivers/platform/mellanox/mlxreg-hotplug.c ++++ b/drivers/platform/mellanox/mlxreg-hotplug.c +@@ -58,6 +58,7 @@ + #define MLXREG_HOTPLUG_PROP_STATUS "status" + + #define MLXREG_HOTPLUG_ATTRS_MAX 24 ++#define MLXREG_HOTPLUG_NOT_ASSERT 3 + + /** + * struct mlxreg_hotplug_priv_data - platform private data: +@@ -74,6 +75,7 @@ + * @cell: location of top aggregation interrupt register; + * @mask: top aggregation interrupt common mask; + * @aggr_cache: last value of aggregation register status; ++ * @not_asserted: number of entries in workqueue with no signal assertion; + */ + struct mlxreg_hotplug_priv_data { + int irq; +@@ -94,6 +96,7 @@ struct mlxreg_hotplug_priv_data { + u32 mask; + u32 aggr_cache; + bool after_probe; ++ u8 not_asserted; + }; + + #if defined(CONFIG_OF_DYNAMIC) +@@ -441,7 +444,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, + * *---* + * + * In case some system changed are detected: FAN in/out, PSU in/out, power +- * cable attached/detached, ASIC helath good/bad, relevant device is created ++ * cable attached/detached, ASIC health good/bad, relevant device is created + * or destroyed. + */ + static void mlxreg_hotplug_work_handler(struct work_struct *work) +@@ -472,6 +475,13 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work) + aggr_asserted = priv->aggr_cache ^ regval; + priv->aggr_cache = regval; + ++ if (priv->not_asserted == MLXREG_HOTPLUG_NOT_ASSERT) { ++ priv->not_asserted = 0; ++ aggr_asserted = pdata->mask; ++ } ++ if (!aggr_asserted) ++ goto unmask_event; ++ + /* Handle topology and health configuration changes. */ + for (i = 0; i < pdata->counter; i++, item++) { + if (aggr_asserted & item->aggr_mask) { +@@ -503,6 +513,8 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work) + return; + } + ++unmask_event: ++ priv->not_asserted++; + /* Unmask aggregation event (no need acknowledge). */ + ret = regmap_write(priv->regmap, pdata->cell + + MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask); +@@ -626,6 +638,7 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) + { + struct mlxreg_core_hotplug_platform_data *pdata; + struct mlxreg_hotplug_priv_data *priv; ++ struct i2c_adapter *deferred_adap; + int err; + + pdata = dev_get_platdata(&pdev->dev); +@@ -634,6 +647,12 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) + return -EINVAL; + } + ++ /* Defer probing if the necessary adapter is not configured yet. */ ++ deferred_adap = i2c_get_adapter(pdata->deferred_nr); ++ if (!deferred_adap) ++ return -EPROBE_DEFER; ++ i2c_put_adapter(deferred_adap); ++ + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; +diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c +index 61cbe35..e03f03f 100644 +--- a/drivers/platform/x86/mlx-platform.c ++++ b/drivers/platform/x86/mlx-platform.c +@@ -99,6 +99,15 @@ + #define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4) + #define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0) + ++/* Default I2C parent bus number */ ++#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1 ++ ++/* Maximum number of possible physical buses equipped on system */ ++#define MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM 16 ++ ++/* Number of channels in group */ ++#define MLXPLAT_CPLD_GRP_CHNL_NUM 8 ++ + /* Start channel numbers */ + #define MLXPLAT_CPLD_CH1 2 + #define MLXPLAT_CPLD_CH2 10 +@@ -106,7 +115,8 @@ + /* Number of LPC attached MUX platform devices */ + #define MLXPLAT_CPLD_LPC_MUX_DEVS 2 + +-/* PSU adapter numbers */ ++/* Hotplug devices adapter numbers */ ++#define MLXPLAT_CPLD_NR_NONE -1 + #define MLXPLAT_CPLD_PSU_DEFAULT_NR 10 + #define MLXPLAT_CPLD_PSU_MSNXXXX_NR 4 + +@@ -137,7 +147,7 @@ static const struct resource mlxplat_lpc_resources[] = { + }; + + /* Platform default channels */ +-static const int mlxplat_default_channels[][8] = { ++static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = { + { + MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2, + MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 + +@@ -472,14 +482,14 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = { + { + .label = "fan5", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, +- .mask = BIT(3), ++ .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, + .hpdev.nr = 15, + }, + { + .label = "fan6", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, +- .mask = BIT(3), ++ .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, + .hpdev.nr = 16, + }, +@@ -943,10 +953,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_regs_io_data[] = { + GENMASK(7, 0) & ~BIT(0), 0x00, 0444 }, + { "cause_short_pb", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, + GENMASK(7, 0) & ~BIT(1), 0x00, 0444 }, +- { "cause_pwr_aux", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ { "cause_aux_pwr_or_refresh", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, + GENMASK(7, 0) & ~BIT(2), 0x00, 0444 }, +- { "cause_pwr_fail", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ { "cause_main_pwr_fail", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, + GENMASK(7, 0) & ~BIT(3), 0x00, 0444 }, ++ { "cause_sw_reset", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(4), 0x00, 0444 }, ++ { "cause_fw_reset", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(5), 0x00, 0444 }, ++ { "cause_hotswap_or_wd", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(6), 0x00, 0444 }, ++ { "cause_asic_thermal", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(7), 0x00, 0444 }, + { "psu1_on", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(0), + 0x00, 0200 }, + { "psu2_on", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(1), +@@ -977,6 +995,8 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) + ARRAY_SIZE(mlxplat_default_channels[i]); + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_data; ++ mlxplat_hotplug->deferred_nr = ++ mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_default_led_data; + mlxplat_regs_io = &mlxplat_default_regs_io_data; + +@@ -993,6 +1013,8 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) + ARRAY_SIZE(mlxplat_msn21xx_channels); + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; ++ mlxplat_hotplug->deferred_nr = ++ mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_msn21xx_led_data; + mlxplat_regs_io = &mlxplat_default_regs_io_data; + +@@ -1009,6 +1031,8 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) + ARRAY_SIZE(mlxplat_msn21xx_channels); + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data; ++ mlxplat_hotplug->deferred_nr = ++ mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_default_led_data; + mlxplat_regs_io = &mlxplat_default_regs_io_data; + +@@ -1025,6 +1049,8 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) + ARRAY_SIZE(mlxplat_msn21xx_channels); + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data; ++ mlxplat_hotplug->deferred_nr = ++ mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_default_ng_led_data; + mlxplat_regs_io = &mlxplat_default_regs_io_data; + +@@ -1041,13 +1067,15 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) + ARRAY_SIZE(mlxplat_msn21xx_channels); + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data; ++ mlxplat_hotplug->deferred_nr = ++ mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_msn21xx_led_data; + mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; + +-static struct dmi_system_id mlxplat_dmi_table[] __initdata = { ++static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { + { + .callback = mlxplat_dmi_msn274x_matched, + .matches = { +@@ -1118,14 +1146,84 @@ static struct dmi_system_id mlxplat_dmi_table[] __initdata = { + DMI_MATCH(DMI_PRODUCT_NAME, "SN34"), + }, + }, ++ { ++ .callback = mlxplat_dmi_default_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_msn21xx_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_msn274x_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_msn201x_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_qmb7xx_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"), ++ }, ++ }, + { } + }; + ++MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table); ++ ++static int mlxplat_mlxcpld_verify_bus_topology(int *nr) ++{ ++ struct i2c_adapter *search_adap; ++ int shift, i; ++ ++ /* Scan adapters from expected id to verify it is free. */ ++ *nr = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; ++ for (i = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; i < ++ MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; i++) { ++ search_adap = i2c_get_adapter(i); ++ if (search_adap) { ++ i2c_put_adapter(search_adap); ++ continue; ++ } ++ ++ /* Return if expected parent adapter is free. */ ++ if (i == MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR) ++ return 0; ++ break; ++ } ++ ++ /* Return with error if free id for adapter is not found. */ ++ if (i == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ++ return -ENODEV; ++ ++ /* Shift adapter ids, since expected parent adapter is not free. */ ++ *nr = i; ++ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { ++ shift = *nr - mlxplat_mux_data[i].parent; ++ mlxplat_mux_data[i].parent = *nr; ++ mlxplat_mux_data[i].base_nr += shift; ++ if (shift > 0) ++ mlxplat_hotplug->shift_nr = shift; ++ } ++ ++ return 0; ++} ++ + static int __init mlxplat_init(void) + { + struct mlxplat_priv *priv; + void __iomem *base; +- int i, j, err = 0; ++ int i, j, nr, err = 0; + + if (!dmi_check_system(mlxplat_dmi_table)) + return -ENODEV; +@@ -1145,7 +1243,12 @@ static int __init mlxplat_init(void) + } + platform_set_drvdata(mlxplat_dev, priv); + +- priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1, ++ err = mlxplat_mlxcpld_verify_bus_topology(&nr); ++ if (nr < 0) ++ goto fail_alloc; ++ ++ nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr; ++ priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr, + NULL, 0); + if (IS_ERR(priv->pdev_i2c)) { + err = PTR_ERR(priv->pdev_i2c); +@@ -1166,13 +1269,17 @@ static int __init mlxplat_init(void) + + base = devm_ioport_map(&mlxplat_dev->dev, + mlxplat_lpc_resources[1].start, 1); +- if (IS_ERR(base)) ++ if (!base) { ++ err = -ENOMEM; + goto fail_platform_mux_register; ++ } + + mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL, + base, &mlxplat_mlxcpld_regmap_config); +- if (IS_ERR(mlxplat_hotplug->regmap)) ++ if (IS_ERR(mlxplat_hotplug->regmap)) { ++ err = PTR_ERR(mlxplat_hotplug->regmap); + goto fail_platform_mux_register; ++ } + + /* Set default registers. */ + for (j = 0; j < mlxplat_mlxcpld_regmap_config.num_reg_defaults; j++) { +@@ -1257,13 +1364,3 @@ module_exit(mlxplat_exit); + MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)"); + MODULE_DESCRIPTION("Mellanox platform driver"); + MODULE_LICENSE("Dual BSD/GPL"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*MSN274*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*MSN201*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*QMB7*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*SN37*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*QM34*:"); +diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h +index c25623b..b77c7a5 100644 +--- a/include/linux/platform_data/mlxreg.h ++++ b/include/linux/platform_data/mlxreg.h +@@ -129,6 +129,8 @@ struct mlxreg_core_platform_data { + * @mask: top aggregation interrupt common mask; + * @cell_low: location of low aggregation interrupt register; + * @mask_low: low aggregation interrupt common mask; ++ * @deferred_nr: I2C adapter number must be exist prior probing execution; ++ * @shift_nr: I2C adapter numbers must be incremented by this value; + */ + struct mlxreg_core_hotplug_platform_data { + struct mlxreg_core_item *items; +@@ -139,6 +141,8 @@ struct mlxreg_core_hotplug_platform_data { + u32 mask; + u32 cell_low; + u32 mask_low; ++ int deferred_nr; ++ int shift_nr; + }; + + #endif /* __LINUX_PLATFORM_DATA_MLXREG_H */ diff --git a/packages/base/any/kernels/4.9-lts/patches/0012-i2c-busses-Add-capabilities-to-i2c-mlxcpld.patch b/packages/base/any/kernels/4.9-lts/patches/0012-i2c-busses-Add-capabilities-to-i2c-mlxcpld.patch new file mode 100644 index 00000000..866593a2 --- /dev/null +++ b/packages/base/any/kernels/4.9-lts/patches/0012-i2c-busses-Add-capabilities-to-i2c-mlxcpld.patch @@ -0,0 +1,199 @@ +From 23c8535af1dd9dcaecb5aaf4097129bfb7e24570 Mon Sep 17 00:00:00 2001 +From: Vadim Pasternak +Date: Thu, 15 Mar 2018 18:38:18 +0000 +Subject: [backport 4.9 7/7] i2c: busses: Add capabilities to i2c-mlxcpld + +It adds support for extended length of read and write transactions. +New CPLD logic allows double size of the read and write transactions +length. This feature is verified through capability register, which is +renamed from unclear LPF_REG to CPBLTY_REG. Two bits 5 and 6 of these +register are used for length capability detection, while only 01 +combination indicates support of extended transaction length. Others mean +lack of such support. + +It adds support for smbus block read transaction. CPLD smbus block read +bit of capability register is verified during driver initialization, and +driver data is updated if such capability is available. In case an upper +layer requests a read transaction of length one and expects that length +will be the first received byte, driver will notify CPLD about SMBus block +read transaction flavor, so CPLD will know to execute such kind of +transaction. + +It fixes report about supported functionality. +Functionality can be different up to CPLD capability. + +It allows mlxcpld driver to be connected to pre-defined adapter number +equal or greater than one, in order to avoid current limitation, assuming +usage of id number one only. + +Author: Michael Shych +Patches are sent to i2c-next. + +Signed-off-by: Vadim Pasternak +--- + drivers/i2c/busses/i2c-mlxcpld.c | 70 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 60 insertions(+), 10 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c +index d271e6a..745ed43 100644 +--- a/drivers/i2c/busses/i2c-mlxcpld.c ++++ b/drivers/i2c/busses/i2c-mlxcpld.c +@@ -45,13 +45,16 @@ + #define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD) + #define MLXCPLD_I2C_BUS_NUM 1 + #define MLXCPLD_I2C_DATA_REG_SZ 36 ++#define MLXCPLD_I2C_DATA_SZ_BIT BIT(5) ++#define MLXCPLD_I2C_DATA_SZ_MASK GENMASK(6, 5) ++#define MLXCPLD_I2C_SMBUS_BLK_BIT BIT(7) + #define MLXCPLD_I2C_MAX_ADDR_LEN 4 + #define MLXCPLD_I2C_RETR_NUM 2 + #define MLXCPLD_I2C_XFER_TO 500000 /* usec */ + #define MLXCPLD_I2C_POLL_TIME 2000 /* usec */ + + /* LPC I2C registers */ +-#define MLXCPLD_LPCI2C_LPF_REG 0x0 ++#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0 + #define MLXCPLD_LPCI2C_CTRL_REG 0x1 + #define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4 + #define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5 +@@ -83,6 +86,7 @@ struct mlxcpld_i2c_priv { + struct mutex lock; + struct mlxcpld_i2c_curr_xfer xfer; + struct device *dev; ++ bool smbus_block; + }; + + static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr) +@@ -230,7 +234,7 @@ static void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv, + * All upper layers currently are never use transfer with more than + * 2 messages. Actually, it's also not so relevant in Mellanox systems + * because of HW limitation. Max size of transfer is not more than 32 +- * bytes in the current x86 LPCI2C bridge. ++ * or 68 bytes in the current x86 LPCI2C bridge. + */ + priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD; + +@@ -295,7 +299,7 @@ static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv) + static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv) + { + int status, i, timeout = 0; +- u8 datalen; ++ u8 datalen, val; + + do { + usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME); +@@ -324,9 +328,22 @@ static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv) + * Actual read data len will be always the same as + * requested len. 0xff (line pull-up) will be returned + * if slave has no data to return. Thus don't read +- * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD. ++ * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD. Only in case of ++ * SMBus block read transaction data len can be different, ++ * check this case. + */ +- datalen = priv->xfer.data_len; ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, ++ 1); ++ if (priv->smbus_block && (val & MLXCPLD_I2C_SMBUS_BLK_BIT)) { ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG, ++ &datalen, 1); ++ if (unlikely(datalen > (I2C_SMBUS_BLOCK_MAX + 1))) { ++ dev_err(priv->dev, "Incorrect smbus block read message len\n"); ++ return -E2BIG; ++ } ++ } else { ++ datalen = priv->xfer.data_len; ++ } + + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG, + priv->xfer.msg[i].buf, datalen); +@@ -344,12 +361,20 @@ static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv) + static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv) + { + int i, len = 0; +- u8 cmd; ++ u8 cmd, val; + + mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG, + &priv->xfer.data_len, 1); +- mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, +- &priv->xfer.addr_width, 1); ++ ++ val = priv->xfer.addr_width; ++ /* Notify HW about SMBus block read transaction */ ++ if (priv->smbus_block && priv->xfer.msg_num >= 2 && ++ priv->xfer.msg[1].len == 1 && ++ (priv->xfer.msg[1].flags & I2C_M_RECV_LEN) && ++ (priv->xfer.msg[1].flags & I2C_M_RD)) ++ val |= MLXCPLD_I2C_SMBUS_BLK_BIT; ++ ++ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, 1); + + for (i = 0; i < priv->xfer.msg_num; i++) { + if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) { +@@ -425,7 +450,14 @@ static int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + + static u32 mlxcpld_i2c_func(struct i2c_adapter *adap) + { +- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; ++ struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap); ++ ++ if (priv->smbus_block) ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | ++ I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA; ++ else ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | ++ I2C_FUNC_SMBUS_I2C_BLOCK; + } + + static const struct i2c_algorithm mlxcpld_i2c_algo = { +@@ -433,13 +465,20 @@ static const struct i2c_algorithm mlxcpld_i2c_algo = { + .functionality = mlxcpld_i2c_func + }; + +-static struct i2c_adapter_quirks mlxcpld_i2c_quirks = { ++static const struct i2c_adapter_quirks mlxcpld_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN, + .max_write_len = MLXCPLD_I2C_DATA_REG_SZ, + .max_comb_1st_msg_len = 4, + }; + ++static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = { ++ .flags = I2C_AQ_COMB_WRITE_THEN_READ, ++ .max_read_len = MLXCPLD_I2C_DATA_REG_SZ * 2 - MLXCPLD_I2C_MAX_ADDR_LEN, ++ .max_write_len = MLXCPLD_I2C_DATA_REG_SZ * 2, ++ .max_comb_1st_msg_len = 4, ++}; ++ + static struct i2c_adapter mlxcpld_i2c_adapter = { + .owner = THIS_MODULE, + .name = "i2c-mlxcpld", +@@ -454,6 +493,7 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev) + { + struct mlxcpld_i2c_priv *priv; + int err; ++ u8 val; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -466,6 +506,16 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev) + + /* Register with i2c layer */ + mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO); ++ /* Read capability register */ ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CPBLTY_REG, &val, 1); ++ /* Check support for extended transaction length */ ++ if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_SZ_BIT) ++ mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext; ++ /* Check support for smbus block transaction */ ++ if (val & MLXCPLD_I2C_SMBUS_BLK_BIT) ++ priv->smbus_block = true; ++ if (pdev->id >= -1) ++ mlxcpld_i2c_adapter.nr = pdev->id; + priv->adap = mlxcpld_i2c_adapter; + priv->adap.dev.parent = &pdev->dev; + priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR; +-- +2.1.4 + diff --git a/packages/base/any/kernels/4.9-lts/patches/series b/packages/base/any/kernels/4.9-lts/patches/series index b16099f0..e83cb1cb 100644 --- a/packages/base/any/kernels/4.9-lts/patches/series +++ b/packages/base/any/kernels/4.9-lts/patches/series @@ -7,3 +7,7 @@ driver-support-intel-igb-bcm5461-phy.patch 0006-Mellanox-switch-drivers-changes.patch 0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch 0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch +0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch +0010-platform-mellanox-mlxreg-hotplug-driver-add-check-fo.patch +0011-platform-x86-mlx-platform-new-features.patch +0012-i2c-busses-Add-capabilities-to-i2c-mlxcpld.patch