diff options
author | Vadim Pasternak <vadimp@mellanox.com> | 2018-01-22 19:55:11 -0800 |
---|---|---|
committer | Darren Hart (VMware) <dvhart@infradead.org> | 2018-01-31 10:36:49 -0800 |
commit | c6acad68eb2dbffd0497f91b206de5c362f59ee4 (patch) | |
tree | 2115350d7eb993b12a15d4f18352d29681ab1278 /drivers/platform/mellanox | |
parent | 752849e69715fdc2190945f5532d70e8c80bf384 (diff) | |
download | linux-stable-c6acad68eb2dbffd0497f91b206de5c362f59ee4.tar.gz linux-stable-c6acad68eb2dbffd0497f91b206de5c362f59ee4.tar.bz2 linux-stable-c6acad68eb2dbffd0497f91b206de5c362f59ee4.zip |
platform/mellanox: mlxreg-hotplug: Modify to use a regmap interface
Restructure mlxreg header for unification of hotplug item definitions.
Unify hotplug items to allow any kind of item (power controller, fan
eeprom, psu eeprom, asic health) in common way.
Use a hardware independent regmap interface, enabling the support of
hotplug events over programmable devices attached to different bus
types, such as I2C, LPC, or SPI. Add a device node to the
mlxreg_core_data structure.
Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com>
[dvhart: spelling corrections, refactor device node introduction]
Signed-off-by: Darren Hart (VMware) <dvhart@infradead.org>
Diffstat (limited to 'drivers/platform/mellanox')
-rw-r--r-- | drivers/platform/mellanox/Kconfig | 1 | ||||
-rw-r--r-- | drivers/platform/mellanox/mlxreg-hotplug.c | 614 |
2 files changed, 367 insertions, 248 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); |