mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-25 17:27:01 +00:00
Merge pull request #346 from mshych/master
4 new kernel patches for Mellanox drivers.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,685 @@
|
||||
From 2c7476ab57dd42d8cba6c417ff32a77252964858 Mon Sep 17 00:00:00 2001
|
||||
From: Vadim Pasternak <vadimp@mellanox.com>
|
||||
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 <vadimp@mellanox.com>
|
||||
---
|
||||
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 <linux/bitops.h>
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/hwmon.h>
|
||||
+#include <linux/hwmon-sysfs.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/platform_data/mlxreg.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+/* 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 <vadimp@mellanox.com>");
|
||||
+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
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
From c794f8ffa6521c47bfbff813e7f713561d7da7bd Mon Sep 17 00:00:00 2001
|
||||
From: Vadim Pasternak <vadimp@mellanox.com>
|
||||
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 <vadimp@mellanox.com>
|
||||
---
|
||||
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
|
||||
|
||||
@@ -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 <linux/device.h>
|
||||
+#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysfs.h>
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,199 @@
|
||||
From 23c8535af1dd9dcaecb5aaf4097129bfb7e24570 Mon Sep 17 00:00:00 2001
|
||||
From: Vadim Pasternak <vadimp@mellanox.com>
|
||||
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 <michaelsh@mellanox.com>
|
||||
Patches are sent to i2c-next.
|
||||
|
||||
Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
|
||||
---
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user