diff options
-rw-r--r-- | drivers/platform/mellanox/Kconfig | 1 | ||||
-rw-r--r-- | drivers/platform/mellanox/mlxreg-hotplug.c | 614 | ||||
-rw-r--r-- | drivers/platform/x86/mlx-platform.c | 231 | ||||
-rw-r--r-- | include/linux/platform_data/mlxreg.h | 126 |
4 files changed, 635 insertions, 337 deletions
diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index d73529203327..0fb2568716bb 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -16,6 +16,7 @@ if MELLANOX_PLATFORM config MLXREG_HOTPLUG tristate "Mellanox platform hotplug driver support" + depends on REGMAP depends on HWMON depends on I2C ---help--- diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index e4f7e8efd397..bcb564fd9f04 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -37,99 +37,97 @@ #include <linux/hwmon-sysfs.h> #include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/io.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_data/mlxreg.h> #include <linux/platform_device.h> #include <linux/spinlock.h> +#include <linux/regmap.h> #include <linux/workqueue.h> -/* Offset of event and mask registers from status register */ +/* Offset of event and mask registers from status register. */ #define MLXREG_HOTPLUG_EVENT_OFF 1 -#define MLXREG_HOTPLUG_MASK_OFF 2 +#define MLXREG_HOTPLUG_MASK_OFF 2 #define MLXREG_HOTPLUG_AGGR_MASK_OFF 1 -#define MLXREG_HOTPLUG_ATTRS_NUM 8 +/* ASIC health parameters. */ +#define MLXREG_HOTPLUG_HEALTH_MASK 0x02 +#define MLXREG_HOTPLUG_RST_CNTR 3 -/** - * enum mlxreg_hotplug_attr_type - sysfs attributes for hotplug events: - * @MLXREG_HOTPLUG_ATTR_TYPE_PSU: power supply unit attribute; - * @MLXREG_HOTPLUG_ATTR_TYPE_PWR: power cable attribute; - * @MLXREG_HOTPLUG_ATTR_TYPE_FAN: FAN drawer attribute; - */ -enum mlxreg_hotplug_attr_type { - MLXREG_HOTPLUG_ATTR_TYPE_PSU, - MLXREG_HOTPLUG_ATTR_TYPE_PWR, - MLXREG_HOTPLUG_ATTR_TYPE_FAN, -}; +#define MLXREG_HOTPLUG_ATTRS_MAX 24 /** * struct mlxreg_hotplug_priv_data - platform private data: - * @irq: platform interrupt number; + * @irq: platform device interrupt number; * @pdev: platform device; * @plat: platform data; + * @dwork: delayed work template; + * @lock: spin lock; * @hwmon: hwmon device; * @mlxreg_hotplug_attr: sysfs attributes array; * @mlxreg_hotplug_dev_attr: sysfs sensor device attribute array; * @group: sysfs attribute group; * @groups: list of sysfs attribute group for hwmon registration; - * @dwork: delayed work template; - * @lock: spin lock; + * @cell: location of top aggregation interrupt register; + * @mask: top aggregation interrupt common mask; * @aggr_cache: last value of aggregation register status; - * @psu_cache: last value of PSU register status; - * @pwr_cache: last value of power register status; - * @fan_cache: last value of FAN register status; */ struct mlxreg_hotplug_priv_data { int irq; + struct device *dev; struct platform_device *pdev; struct mlxreg_hotplug_platform_data *plat; + struct regmap *regmap; + struct delayed_work dwork_irq; + struct delayed_work dwork; + spinlock_t lock; /* sync with interrupt */ struct device *hwmon; - struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_NUM + 1]; + struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_MAX + 1]; struct sensor_device_attribute_2 - mlxreg_hotplug_dev_attr[MLXREG_HOTPLUG_ATTRS_NUM]; + mlxreg_hotplug_dev_attr[MLXREG_HOTPLUG_ATTRS_MAX]; struct attribute_group group; const struct attribute_group *groups[2]; - struct delayed_work dwork; - spinlock_t lock; - u8 aggr_cache; - u8 psu_cache; - u8 pwr_cache; - u8 fan_cache; + u32 cell; + u32 mask; + u32 aggr_cache; + bool after_probe; }; static int mlxreg_hotplug_device_create(struct device *dev, - struct mlxreg_hotplug_device *item) + struct mlxreg_core_data *data) { - item->adapter = i2c_get_adapter(item->nr); - if (!item->adapter) { + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); + if (!data->hpdev.adapter) { dev_err(dev, "Failed to get adapter for bus %d\n", - item->nr); + data->hpdev.nr); return -EFAULT; } - item->client = i2c_new_device(item->adapter, &item->brdinfo); - if (!item->client) { + data->hpdev.client = i2c_new_device(data->hpdev.adapter, + data->hpdev.brdinfo); + if (!data->hpdev.client) { dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n", - item->brdinfo.type, item->nr, item->brdinfo.addr); - i2c_put_adapter(item->adapter); - item->adapter = NULL; + data->hpdev.brdinfo->type, data->hpdev.nr, + data->hpdev.brdinfo->addr); + + i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; return -EFAULT; } return 0; } -static void mlxreg_hotplug_device_destroy(struct mlxreg_hotplug_device *item) +static void mlxreg_hotplug_device_destroy(struct mlxreg_core_data *data) { - if (item->client) { - i2c_unregister_device(item->client); - item->client = NULL; + if (data->hpdev.client) { + i2c_unregister_device(data->hpdev.client); + data->hpdev.client = NULL; } - if (item->adapter) { - i2c_put_adapter(item->adapter); - item->adapter = NULL; + if (data->hpdev.adapter) { + i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; } } @@ -137,41 +135,76 @@ static ssize_t mlxreg_hotplug_attr_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct platform_device *pdev = to_platform_device(dev); - struct mlxreg_hotplug_priv_data *priv = platform_get_drvdata(pdev); + struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(dev); + struct mlxreg_core_hotplug_platform_data *pdata; int index = to_sensor_dev_attr_2(attr)->index; int nr = to_sensor_dev_attr_2(attr)->nr; - u8 reg_val = 0; - - switch (nr) { - case MLXREG_HOTPLUG_ATTR_TYPE_PSU: - /* Bit = 0 : PSU is present. */ - reg_val = !!!(inb(priv->plat->psu_reg_offset) & BIT(index)); - break; - - case MLXREG_HOTPLUG_ATTR_TYPE_PWR: - /* Bit = 1 : power cable is attached. */ - reg_val = !!(inb(priv->plat->pwr_reg_offset) & BIT(index % - priv->plat->pwr_count)); - break; - - case MLXREG_HOTPLUG_ATTR_TYPE_FAN: - /* Bit = 0 : FAN is present. */ - reg_val = !!!(inb(priv->plat->fan_reg_offset) & BIT(index % - priv->plat->fan_count)); - break; + struct mlxreg_core_item *item; + struct mlxreg_core_data *data; + u32 regval; + int ret; + + pdata = dev_get_platdata(&priv->pdev->dev); + item = pdata->items + nr; + data = item->data + index; + + ret = regmap_read(priv->regmap, data->reg, ®val); + if (ret) + return ret; + + if (item->health) { + regval &= data->mask; + } else { + /* Bit = 0 : functional if item->inversed is true. */ + if (item->inversed) + regval = !(regval & data->mask); + else + regval = !!(regval & data->mask); } - return sprintf(buf, "%u\n", reg_val); + return sprintf(buf, "%u\n", regval); } #define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i] #define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i] + static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv) { - int num_attrs = priv->plat->psu_count + priv->plat->pwr_count + - priv->plat->fan_count; - int i; + struct mlxreg_core_hotplug_platform_data *pdata; + struct mlxreg_core_item *item; + struct mlxreg_core_data *data; + int num_attrs = 0, id = 0, i, j; + + pdata = dev_get_platdata(&priv->pdev->dev); + item = pdata->items; + + /* Go over all kinds of items - psu, pwr, fan. */ + for (i = 0; i < pdata->counter; i++, item++) { + num_attrs += item->count; + data = item->data; + /* Go over all units within the item. */ + for (j = 0; j < item->count; j++, data++, id++) { + PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr; + PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev, + GFP_KERNEL, + data->label); + + if (!PRIV_ATTR(id)->name) { + dev_err(priv->dev, "Memory allocation failed for attr %d.\n", + id); + return -ENOMEM; + } + + PRIV_DEV_ATTR(id).dev_attr.attr.name = + PRIV_ATTR(id)->name; + PRIV_DEV_ATTR(id).dev_attr.attr.mode = 0444; + PRIV_DEV_ATTR(id).dev_attr.show = + mlxreg_hotplug_attr_show; + PRIV_DEV_ATTR(id).nr = i; + PRIV_DEV_ATTR(id).index = j; + sysfs_attr_init(&PRIV_DEV_ATTR(id).dev_attr.attr); + } + } priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs * sizeof(struct attribute *), @@ -179,38 +212,6 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv) if (!priv->group.attrs) return -ENOMEM; - for (i = 0; i < num_attrs; i++) { - PRIV_ATTR(i) = &PRIV_DEV_ATTR(i).dev_attr.attr; - - if (i < priv->plat->psu_count) { - PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev, - GFP_KERNEL, "psu%u", i + 1); - PRIV_DEV_ATTR(i).nr = MLXREG_HOTPLUG_ATTR_TYPE_PSU; - } else if (i < priv->plat->psu_count + priv->plat->pwr_count) { - PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev, - GFP_KERNEL, "pwr%u", i % - priv->plat->pwr_count + 1); - PRIV_DEV_ATTR(i).nr = MLXREG_HOTPLUG_ATTR_TYPE_PWR; - } else { - PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev, - GFP_KERNEL, "fan%u", i % - priv->plat->fan_count + 1); - PRIV_DEV_ATTR(i).nr = MLXREG_HOTPLUG_ATTR_TYPE_FAN; - } - - if (!PRIV_ATTR(i)->name) { - dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n", - i + 1); - return -ENOMEM; - } - - PRIV_DEV_ATTR(i).dev_attr.attr.name = PRIV_ATTR(i)->name; - PRIV_DEV_ATTR(i).dev_attr.attr.mode = S_IRUGO; - PRIV_DEV_ATTR(i).dev_attr.show = mlxreg_hotplug_attr_show; - PRIV_DEV_ATTR(i).index = i; - sysfs_attr_init(&PRIV_DEV_ATTR(i).dev_attr.attr); - } - priv->group.attrs = priv->mlxreg_hotplug_attr; priv->groups[0] = &priv->group; priv->groups[1] = NULL; @@ -218,20 +219,13 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv) return 0; } -static inline void -mlxreg_hotplug_work_helper(struct device *dev, - struct mlxreg_hotplug_device *item, u8 is_inverse, - u16 offset, u8 mask, u8 *cache) +static void +mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv, + struct mlxreg_core_item *item) { - u8 val, asserted; - int bit; - - /* Mask event. */ - outb(0, offset + MLXREG_HOTPLUG_MASK_OFF); - /* Read status. */ - val = inb(offset) & mask; - asserted = *cache ^ val; - *cache = val; + struct mlxreg_core_data *data; + u32 asserted, regval, bit; + int ret; /* * Validate if item related to received signal type is valid. @@ -241,86 +235,177 @@ mlxreg_hotplug_work_helper(struct device *dev, * signals from other devices if any. */ if (unlikely(!item)) { - dev_err(dev, "False signal is received: register at offset 0x%02x, mask 0x%02x.\n", - offset, mask); + dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n", + item->reg, item->mask); + return; } + /* Mask event. */ + ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF, + 0); + if (ret) + goto out; + + /* Read status. */ + ret = regmap_read(priv->regmap, item->reg, ®val); + if (ret) + goto out; + + /* Set asserted bits and save last status. */ + regval &= item->mask; + asserted = item->cache ^ regval; + item->cache = regval; + for_each_set_bit(bit, (unsigned long *)&asserted, 8) { - if (val & BIT(bit)) { - if (is_inverse) - mlxreg_hotplug_device_destroy(item + bit); + data = item->data + bit; + if (regval & BIT(bit)) { + if (item->inversed) + mlxreg_hotplug_device_destroy(data); else - mlxreg_hotplug_device_create(dev, item + bit); + mlxreg_hotplug_device_create(priv->dev, data); } else { - if (is_inverse) - mlxreg_hotplug_device_create(dev, item + bit); + if (item->inversed) + mlxreg_hotplug_device_create(priv->dev, data); else - mlxreg_hotplug_device_destroy(item + bit); + mlxreg_hotplug_device_destroy(data); } } /* Acknowledge event. */ - outb(0, offset + MLXREG_HOTPLUG_EVENT_OFF); + ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_EVENT_OFF, + 0); + if (ret) + goto out; + /* Unmask event. */ - outb(mask, offset + MLXREG_HOTPLUG_MASK_OFF); + ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF, + item->mask); + + out: + if (ret) + dev_err(priv->dev, "Failed to complete workqueue.\n"); +} + +static void +mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, + struct mlxreg_core_item *item) +{ + struct mlxreg_core_data *data = item->data; + u32 regval; + int i, ret; + + for (i = 0; i < item->count; i++, data++) { + /* Mask event. */ + ret = regmap_write(priv->regmap, data->reg + + MLXREG_HOTPLUG_MASK_OFF, 0); + if (ret) + goto out; + + /* Read status. */ + ret = regmap_read(priv->regmap, data->reg, ®val); + if (ret) + goto out; + + regval &= data->mask; + item->cache = regval; + if (regval == MLXREG_HOTPLUG_HEALTH_MASK) { + if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) || + !priv->after_probe) { + mlxreg_hotplug_device_create(priv->dev, data); + data->attached = true; + } + } else { + if (data->attached) { + mlxreg_hotplug_device_destroy(data); + data->attached = false; + data->health_cntr = 0; + } + } + + /* Acknowledge event. */ + ret = regmap_write(priv->regmap, data->reg + + MLXREG_HOTPLUG_EVENT_OFF, 0); + if (ret) + goto out; + + /* Unmask event. */ + ret = regmap_write(priv->regmap, data->reg + + MLXREG_HOTPLUG_MASK_OFF, data->mask); + if (ret) + goto out; + } + + out: + if (ret) + dev_err(priv->dev, "Failed to complete workqueue.\n"); } /* - * mlxreg_hotplug_work_handler - performs traversing of CPLD interrupt + * mlxreg_hotplug_work_handler - performs traversing of device interrupt * registers according to the below hierarchy schema: * - * Aggregation registers (status/mask) - * PSU registers: *---* - * *-----------------* | | - * |status/event/mask|----->| * | - * *-----------------* | | - * Power registers: | | - * *-----------------* | | - * |status/event/mask|----->| * |---> CPU - * *-----------------* | | - * FAN registers: - * *-----------------* | | - * |status/event/mask|----->| * | - * *-----------------* | | - * *---* + * Aggregation registers (status/mask) + * PSU registers: *---* + * *-----------------* | | + * |status/event/mask|-----> | * | + * *-----------------* | | + * Power registers: | | + * *-----------------* | | + * |status/event/mask|-----> | * | + * *-----------------* | | + * FAN registers: | |--> CPU + * *-----------------* | | + * |status/event/mask|-----> | * | + * *-----------------* | | + * ASIC registers: | | + * *-----------------* | | + * |status/event/mask|-----> | * | + * *-----------------* | | + * *---* + * * In case some system changed are detected: FAN in/out, PSU in/out, power - * cable attached/detached, relevant device is created or destroyed. + * cable attached/detached, ASIC health good/bad, relevant device is created + * or destroyed. */ static void mlxreg_hotplug_work_handler(struct work_struct *work) { - struct mlxreg_hotplug_priv_data *priv = container_of(work, - struct mlxreg_hotplug_priv_data, dwork.work); - u8 val, aggr_asserted; + struct mlxreg_core_hotplug_platform_data *pdata; + struct mlxreg_hotplug_priv_data *priv; + struct mlxreg_core_item *item; + u32 regval, aggr_asserted; unsigned long flags; + int i, ret; + + priv = container_of(work, struct mlxreg_hotplug_priv_data, + dwork_irq.work); + pdata = dev_get_platdata(&priv->pdev->dev); + item = pdata->items; /* Mask aggregation event. */ - outb(0, priv->plat->top_aggr_offset + MLXREG_HOTPLUG_AGGR_MASK_OFF); + ret = regmap_write(priv->regmap, pdata->cell + + MLXREG_HOTPLUG_AGGR_MASK_OFF, 0); + if (ret < 0) + goto out; + /* Read aggregation status. */ - val = inb(priv->plat->top_aggr_offset) & priv->plat->top_aggr_mask; - aggr_asserted = priv->aggr_cache ^ val; - priv->aggr_cache = val; - - /* Handle PSU configuration changes. */ - if (aggr_asserted & priv->plat->top_aggr_psu_mask) - mlxreg_hotplug_work_helper(&priv->pdev->dev, priv->plat->psu, - 1, priv->plat->psu_reg_offset, - priv->plat->psu_mask, - &priv->psu_cache); - - /* Handle power cable configuration changes. */ - if (aggr_asserted & priv->plat->top_aggr_pwr_mask) - mlxreg_hotplug_work_helper(&priv->pdev->dev, priv->plat->pwr, - 0, priv->plat->pwr_reg_offset, - priv->plat->pwr_mask, - &priv->pwr_cache); - - /* Handle FAN configuration changes. */ - if (aggr_asserted & priv->plat->top_aggr_fan_mask) - mlxreg_hotplug_work_helper(&priv->pdev->dev, priv->plat->fan, - 1, priv->plat->fan_reg_offset, - priv->plat->fan_mask, - &priv->fan_cache); + ret = regmap_read(priv->regmap, pdata->cell, ®val); + if (ret) + goto out; + + regval &= pdata->mask; + aggr_asserted = priv->aggr_cache ^ regval; + priv->aggr_cache = regval; + + /* Handle topology and health configuration changes. */ + for (i = 0; i < pdata->counter; i++, item++) { + if (aggr_asserted & item->aggr_mask) { + if (item->health) + mlxreg_hotplug_health_work_helper(priv, item); + else + mlxreg_hotplug_work_helper(priv, item); + } + } if (aggr_asserted) { spin_lock_irqsave(&priv->lock, flags); @@ -335,8 +420,8 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work) * validates that no new signals have been received during * masking. */ - cancel_delayed_work(&priv->dwork); - schedule_delayed_work(&priv->dwork, 0); + cancel_delayed_work(&priv->dwork_irq); + schedule_delayed_work(&priv->dwork_irq, 0); spin_unlock_irqrestore(&priv->lock, flags); @@ -344,92 +429,119 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work) } /* Unmask aggregation event (no need acknowledge). */ - outb(priv->plat->top_aggr_mask, priv->plat->top_aggr_offset + - MLXREG_HOTPLUG_AGGR_MASK_OFF); + ret = regmap_write(priv->regmap, pdata->cell + + MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask); + + out: + if (ret) + dev_err(priv->dev, "Failed to complete workqueue.\n"); } -static void mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) +static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) { - /* Clear psu presense event. */ - outb(0, priv->plat->psu_reg_offset + MLXREG_HOTPLUG_EVENT_OFF); - /* Set psu initial status as mask and unmask psu event. */ - priv->psu_cache = priv->plat->psu_mask; - outb(priv->plat->psu_mask, priv->plat->psu_reg_offset + - MLXREG_HOTPLUG_MASK_OFF); - - /* Clear power cable event. */ - outb(0, priv->plat->pwr_reg_offset + MLXREG_HOTPLUG_EVENT_OFF); - /* Keep power initial status as zero and unmask power event. */ - outb(priv->plat->pwr_mask, priv->plat->pwr_reg_offset + - MLXREG_HOTPLUG_MASK_OFF); - - /* Clear fan presense event. */ - outb(0, priv->plat->fan_reg_offset + MLXREG_HOTPLUG_EVENT_OFF); - /* Set fan initial status as mask and unmask fan event. */ - priv->fan_cache = priv->plat->fan_mask; - outb(priv->plat->fan_mask, priv->plat->fan_reg_offset + - MLXREG_HOTPLUG_MASK_OFF); + struct mlxreg_core_hotplug_platform_data *pdata; + struct mlxreg_core_item *item; + int i, ret; + + pdata = dev_get_platdata(&priv->pdev->dev); + item = pdata->items; + + for (i = 0; i < pdata->counter; i++, item++) { + /* Clear group presense event. */ + ret = regmap_write(priv->regmap, item->reg + + MLXREG_HOTPLUG_EVENT_OFF, 0); + if (ret) + goto out; + + /* Set group initial status as mask and unmask group event. */ + if (item->inversed) { + item->cache = item->mask; + ret = regmap_write(priv->regmap, item->reg + + MLXREG_HOTPLUG_MASK_OFF, + item->mask); + if (ret) + goto out; + } + } /* Keep aggregation initial status as zero and unmask events. */ - outb(priv->plat->top_aggr_mask, priv->plat->top_aggr_offset + - MLXREG_HOTPLUG_AGGR_MASK_OFF); + ret = regmap_write(priv->regmap, pdata->cell + + MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask); + if (ret) + goto out; + + /* Keep low aggregation initial status as zero and unmask events. */ + if (pdata->cell_low) { + ret = regmap_write(priv->regmap, pdata->cell_low + + MLXREG_HOTPLUG_AGGR_MASK_OFF, + pdata->mask_low); + if (ret) + goto out; + } /* Invoke work handler for initializing hot plug devices setting. */ - mlxreg_hotplug_work_handler(&priv->dwork.work); + mlxreg_hotplug_work_handler(&priv->dwork_irq.work); + out: + if (ret) + dev_err(priv->dev, "Failed to set interrupts.\n"); enable_irq(priv->irq); + return ret; } static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv) { - int i; + struct mlxreg_core_hotplug_platform_data *pdata; + struct mlxreg_core_item *item; + struct mlxreg_core_data *data; + int count, i, j; + pdata = dev_get_platdata(&priv->pdev->dev); + item = pdata->items; disable_irq(priv->irq); - cancel_delayed_work_sync(&priv->dwork); - - /* Mask aggregation event. */ - outb(0, priv->plat->top_aggr_offset + MLXREG_HOTPLUG_AGGR_MASK_OFF); - - /* Mask psu presense event. */ - outb(0, priv->plat->psu_reg_offset + MLXREG_HOTPLUG_MASK_OFF); - /* Clear psu presense event. */ - outb(0, priv->plat->psu_reg_offset + MLXREG_HOTPLUG_EVENT_OFF); - - /* Mask power cable event. */ - outb(0, priv->plat->pwr_reg_offset + MLXREG_HOTPLUG_MASK_OFF); - /* Clear power cable event. */ - outb(0, priv->plat->pwr_reg_offset + MLXREG_HOTPLUG_EVENT_OFF); - - /* Mask fan presense event. */ - outb(0, priv->plat->fan_reg_offset + MLXREG_HOTPLUG_MASK_OFF); - /* Clear fan presense event. */ - outb(0, priv->plat->fan_reg_offset + MLXREG_HOTPLUG_EVENT_OFF); - - /* Remove all the attached devices. */ - for (i = 0; i < priv->plat->psu_count; i++) - mlxreg_hotplug_device_destroy(priv->plat->psu + i); + cancel_delayed_work_sync(&priv->dwork_irq); - for (i = 0; i < priv->plat->pwr_count; i++) - mlxreg_hotplug_device_destroy(priv->plat->pwr + i); + /* Mask low aggregation event, if defined. */ + if (pdata->cell_low) + regmap_write(priv->regmap, pdata->cell_low + + MLXREG_HOTPLUG_AGGR_MASK_OFF, 0); - for (i = 0; i < priv->plat->fan_count; i++) - mlxreg_hotplug_device_destroy(priv->plat->fan + i); + /* Mask aggregation event. */ + regmap_write(priv->regmap, pdata->cell + MLXREG_HOTPLUG_AGGR_MASK_OFF, + 0); + + /* Clear topology configurations. */ + for (i = 0; i < pdata->counter; i++, item++) { + data = item->data; + /* Mask group presense event. */ + regmap_write(priv->regmap, data->reg + MLXREG_HOTPLUG_MASK_OFF, + 0); + /* Clear group presense event. */ + regmap_write(priv->regmap, data->reg + + MLXREG_HOTPLUG_EVENT_OFF, 0); + + /* Remove all the attached devices in group. */ + count = item->count; + for (j = 0; j < count; j++, data++) + mlxreg_hotplug_device_destroy(data); + } } static irqreturn_t mlxreg_hotplug_irq_handler(int irq, void *dev) { - struct mlxreg_hotplug_priv_data *priv = - (struct mlxreg_hotplug_priv_data *)dev; + struct mlxreg_hotplug_priv_data *priv; + + priv = (struct mlxreg_hotplug_priv_data *)dev; /* Schedule work task for immediate execution.*/ - schedule_delayed_work(&priv->dwork, 0); + schedule_delayed_work(&priv->dwork_irq, 0); return IRQ_HANDLED; } static int mlxreg_hotplug_probe(struct platform_device *pdev) { - struct mlxreg_hotplug_platform_data *pdata; + struct mlxreg_core_hotplug_platform_data *pdata; struct mlxreg_hotplug_priv_data *priv; int err; @@ -443,31 +555,42 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->pdev = pdev; - priv->plat = pdata; - - priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(&pdev->dev, "Failed to get platform irq: %d\n", - priv->irq); - return priv->irq; + if (pdata->irq) { + priv->irq = pdata->irq; + } else { + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", + priv->irq); + return priv->irq; + } } + priv->regmap = pdata->regmap; + priv->dev = pdev->dev.parent; + priv->pdev = pdev; + err = devm_request_irq(&pdev->dev, priv->irq, - mlxreg_hotplug_irq_handler, 0, pdev->name, - priv); + mlxreg_hotplug_irq_handler, IRQF_TRIGGER_FALLING + | IRQF_SHARED, "mlxreg-hotplug", priv); if (err) { dev_err(&pdev->dev, "Failed to request irq: %d\n", err); return err; } - disable_irq(priv->irq); - INIT_DELAYED_WORK(&priv->dwork, mlxreg_hotplug_work_handler); + disable_irq(priv->irq); spin_lock_init(&priv->lock); + INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler); + /* Perform initial interrupts setup. */ + mlxreg_hotplug_set_irq(priv); + + priv->after_probe = true; + dev_set_drvdata(&pdev->dev, priv); err = mlxreg_hotplug_attr_init(priv); if (err) { - dev_err(&pdev->dev, "Failed to allocate attributes: %d\n", err); + dev_err(&pdev->dev, "Failed to allocate attributes: %d\n", + err); return err; } @@ -479,17 +602,12 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) return PTR_ERR(priv->hwmon); } - platform_set_drvdata(pdev, priv); - - /* Perform initial interrupts setup. */ - mlxreg_hotplug_set_irq(priv); - return 0; } static int mlxreg_hotplug_remove(struct platform_device *pdev) { - struct mlxreg_hotplug_priv_data *priv = platform_get_drvdata(pdev); + struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(&pdev->dev); /* Clean interrupts setup. */ mlxreg_hotplug_unset_irq(priv); diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 56017143d6a9..03c9e7a76d89 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -35,20 +35,22 @@ #include <linux/dmi.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/platform_data/i2c-mux-reg.h> #include <linux/platform_data/mlxreg.h> +#include <linux/regmap.h> #define MLX_PLAT_DEVICE_NAME "mlxplat" /* 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_AGGR_ADRR 0x253a -#define MLXPLAT_CPLD_LPC_REG_PSU_ADRR 0x2558 -#define MLXPLAT_CPLD_LPC_REG_PWR_ADRR 0x2564 -#define MLXPLAT_CPLD_LPC_REG_FAN_ADRR 0x2588 +#define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a +#define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET 0x58 +#define MLXPLAT_CPLD_LPC_REG_PWR_OFFSET 0x64 +#define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET 0x88 #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda @@ -138,78 +140,194 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { }; /* Platform hotplug devices */ -static struct mlxreg_hotplug_device mlxplat_mlxcpld_psu[] = { +static struct i2c_board_info mlxplat_mlxcpld_psu[] = { { - .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) }, - .nr = 10, + I2C_BOARD_INFO("24c02", 0x51), }, { - .brdinfo = { I2C_BOARD_INFO("24c02", 0x50) }, - .nr = 10, + I2C_BOARD_INFO("24c02", 0x50), }, }; -static struct mlxreg_hotplug_device mlxplat_mlxcpld_pwr[] = { +static struct i2c_board_info mlxplat_mlxcpld_pwr[] = { { - .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) }, - .nr = 10, + I2C_BOARD_INFO("dps460", 0x59), }, { - .brdinfo = { I2C_BOARD_INFO("dps460", 0x58) }, - .nr = 10, + I2C_BOARD_INFO("dps460", 0x58), }, }; -static struct mlxreg_hotplug_device mlxplat_mlxcpld_fan[] = { +static struct i2c_board_info mlxplat_mlxcpld_fan[] = { { - .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, - .nr = 11, + I2C_BOARD_INFO("24c32", 0x50), }, { - .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, - .nr = 12, + I2C_BOARD_INFO("24c32", 0x50), }, { - .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, - .nr = 13, + I2C_BOARD_INFO("24c32", 0x50), }, { - .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, - .nr = 14, + I2C_BOARD_INFO("24c32", 0x50), }, }; /* Platform hotplug default data */ +static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = { + { + .label = "psu1", + .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0], + .hpdev.nr = 10, + }, + { + .label = "psu2", + .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1], + .hpdev.nr = 10, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = { + { + .label = "pwr1", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0], + .hpdev.nr = 10, + }, + { + .label = "pwr2", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1], + .hpdev.nr = 10, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = { + { + .label = "fan1", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_fan[0], + .hpdev.nr = 11, + }, + { + .label = "fan2", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_fan[1], + .hpdev.nr = 12, + }, + { + .label = "fan3", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_fan[2], + .hpdev.nr = 13, + }, + { + .label = "fan4", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_fan[3], + .hpdev.nr = 14, + }, +}; + +static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = { + { + .data = mlxplat_mlxcpld_default_psu_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, + .mask = MLXPLAT_CPLD_PSU_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_psu), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_pwr_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = MLXPLAT_CPLD_PWR_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_fan_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = MLXPLAT_CPLD_FAN_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_fan), + .inversed = 1, + .health = false, + }, +}; + static -struct mlxreg_hotplug_platform_data mlxplat_mlxcpld_default_data = { - .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, - .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_DEF, - .top_aggr_psu_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, - .psu_reg_offset = MLXPLAT_CPLD_LPC_REG_PSU_ADRR, - .psu_mask = MLXPLAT_CPLD_PSU_MASK, - .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_psu), - .psu = mlxplat_mlxcpld_psu, - .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, - .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, - .pwr_mask = MLXPLAT_CPLD_PWR_MASK, - .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), - .pwr = mlxplat_mlxcpld_pwr, - .top_aggr_fan_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, - .fan_reg_offset = MLXPLAT_CPLD_LPC_REG_FAN_ADRR, - .fan_mask = MLXPLAT_CPLD_FAN_MASK, - .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_fan), - .fan = mlxplat_mlxcpld_fan, +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = { + .items = mlxplat_mlxcpld_default_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, }; /* Platform hotplug MSN21xx system family data */ +static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = { + { + .data = mlxplat_mlxcpld_default_pwr_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = MLXPLAT_CPLD_PWR_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), + .inversed = 0, + .health = false, + }, +}; + static -struct mlxreg_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = { - .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, - .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, - .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, - .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, - .pwr_mask = MLXPLAT_CPLD_PWR_MASK, - .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = { + .items = mlxplat_mlxcpld_msn21xx_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, +}; + +struct mlxplat_mlxcpld_regmap_context { + void __iomem *base; +}; + +static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx; + +static int +mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct mlxplat_mlxcpld_regmap_context *ctx = context; + + *val = ioread8(ctx->base + reg); + return 0; +} + +static int +mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct mlxplat_mlxcpld_regmap_context *ctx = context; + + iowrite8(val, ctx->base + reg); + return 0; +} + +static const struct regmap_config mlxplat_mlxcpld_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 255, + .reg_read = mlxplat_mlxcpld_reg_read, + .reg_write = mlxplat_mlxcpld_reg_write, }; static struct resource mlxplat_mlxcpld_resources[] = { @@ -217,7 +335,7 @@ static struct resource mlxplat_mlxcpld_resources[] = { }; static struct platform_device *mlxplat_dev; -static struct mlxreg_hotplug_platform_data *mlxplat_hotplug; +static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) { @@ -328,6 +446,21 @@ static int __init mlxplat_init(void) } } + mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev, + mlxplat_lpc_resources[1].start, 1); + if (IS_ERR(mlxplat_mlxcpld_regmap_ctx.base)) { + err = PTR_ERR(mlxplat_mlxcpld_regmap_ctx.base); + goto fail_platform_mux_register; + } + + mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL, + &mlxplat_mlxcpld_regmap_ctx, + &mlxplat_mlxcpld_regmap_config); + if (IS_ERR(mlxplat_hotplug->regmap)) { + err = PTR_ERR(mlxplat_hotplug->regmap); + goto fail_platform_mux_register; + } + priv->pdev_hotplug = platform_device_register_resndata( &mlxplat_dev->dev, "mlxreg-hotplug", PLATFORM_DEVID_NONE, diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index ffbcb7886c62..fcdc707eab99 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -34,8 +34,11 @@ #ifndef __LINUX_PLATFORM_DATA_MLXREG_H #define __LINUX_PLATFORM_DATA_MLXREG_H +#define MLXREG_CORE_LABEL_MAX_SIZE 32 + /** * struct mlxreg_hotplug_device - I2C device data: + * * @adapter: I2C device adapter; * @client: I2C device client; * @brdinfo: device board information; @@ -47,52 +50,95 @@ struct mlxreg_hotplug_device { struct i2c_adapter *adapter; struct i2c_client *client; - struct i2c_board_info brdinfo; + struct i2c_board_info *brdinfo; int nr; }; /** - * struct mlxreg_hotplug_platform_data - device platform data: - * @top_aggr_offset: offset of top aggregation interrupt register; - * @top_aggr_mask: top aggregation interrupt common mask; - * @top_aggr_psu_mask: top aggregation interrupt PSU mask; - * @psu_reg_offset: offset of PSU interrupt register; - * @psu_mask: PSU interrupt mask; - * @psu_count: number of equipped replaceable PSUs; - * @psu: pointer to PSU devices data array; - * @top_aggr_pwr_mask: top aggregation interrupt power mask; - * @pwr_reg_offset: offset of power interrupt register - * @pwr_mask: power interrupt mask; - * @pwr_count: number of power sources; - * @pwr: pointer to power devices data array; - * @top_aggr_fan_mask: top aggregation interrupt FAN mask; - * @fan_reg_offset: offset of FAN interrupt register; - * @fan_mask: FAN interrupt mask; - * @fan_count: number of equipped replaceable FANs; - * @fan: pointer to FAN devices data array; + * struct mlxreg_core_data - attributes control data: + * + * @label: attribute label; + * @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; + * @health_cntr: dynamic device health indication counter; + * @attached: true if device has been attached after good health indication; + */ +struct mlxreg_core_data { + char label[MLXREG_CORE_LABEL_MAX_SIZE]; + u32 reg; + u32 mask; + u32 bit; + umode_t mode; + struct device_node *np; + struct mlxreg_hotplug_device hpdev; + u8 health_cntr; + bool attached; +}; + +/** + * struct mlxreg_core_item - same type components controlled by the driver: + * + * @data: component data; + * @aggr_mask: group aggregation mask; + * @reg: group interrupt status register; + * @mask: group interrupt mask; + * @cache: last status value for elements fro the same group; + * @count: number of available elements in the group; + * @ind: element's index inside the group; + * @inversed: if 0: 0 for signal status is OK, if 1 - 1 is OK; + * @health: true if device has health indication, false in other case; + */ +struct mlxreg_core_item { + struct mlxreg_core_data *data; + u32 aggr_mask; + u32 reg; + u32 mask; + u32 cache; + u8 count; + u8 ind; + u8 inversed; + u8 health; +}; + +/** + * 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_platform_data { + struct mlxreg_core_data *data; + void *regmap; + int counter; +}; + +/** + * struct mlxreg_core_hotplug_platform_data - hotplug platform data: * - * Structure represents board platform data, related to system hotplug events, - * like FAN, PSU, power cable insertion and removing. This data provides the - * number of hot-pluggable devices and hardware description for event handling. + * @items: same type components with the hotplug capability; + * @irq: platform interrupt number; + * @regmap: register map of parent device; + * @counter: number of the components with the hotplug capability; + * @cell: location of top aggregation interrupt register; + * @mask: top aggregation interrupt common mask; + * @cell_low: location of low aggregation interrupt register; + * @mask_low: low aggregation interrupt common mask; */ -struct mlxreg_hotplug_platform_data { - u16 top_aggr_offset; - u8 top_aggr_mask; - u8 top_aggr_psu_mask; - u16 psu_reg_offset; - u8 psu_mask; - u8 psu_count; - struct mlxreg_hotplug_device *psu; - u8 top_aggr_pwr_mask; - u16 pwr_reg_offset; - u8 pwr_mask; - u8 pwr_count; - struct mlxreg_hotplug_device *pwr; - u8 top_aggr_fan_mask; - u16 fan_reg_offset; - u8 fan_mask; - u8 fan_count; - struct mlxreg_hotplug_device *fan; +struct mlxreg_core_hotplug_platform_data { + struct mlxreg_core_item *items; + int irq; + void *regmap; + int counter; + u32 cell; + u32 mask; + u32 cell_low; + u32 mask_low; }; #endif /* __LINUX_PLATFORM_DATA_MLXREG_H */ |