Merge tag 'platform-drivers-x86-v4.18-1' of git://git.infradead.org/linux-platform...
[sfrench/cifs-2.6.git] / drivers / platform / mellanox / mlxreg-hotplug.c
index ea9e7f4479cad668dc261ee4386dee272f729888..ac97aa020db326dfc32ce821943ff37e2d77749c 100644 (file)
 #define MLXREG_HOTPLUG_RST_CNTR                3
 
 #define MLXREG_HOTPLUG_ATTRS_MAX       24
+#define MLXREG_HOTPLUG_NOT_ASSERT      3
 
 /**
  * struct mlxreg_hotplug_priv_data - platform private data:
  * @irq: platform device interrupt number;
+ * @dev: basic device;
  * @pdev: platform device;
  * @plat: platform data;
- * @dwork: delayed work template;
+ * @regmap: register map handle;
+ * @dwork_irq: delayed work template;
  * @lock: spin lock;
  * @hwmon: hwmon device;
  * @mlxreg_hotplug_attr: sysfs attributes array;
@@ -71,6 +74,8 @@
  * @cell: location of top aggregation interrupt register;
  * @mask: top aggregation interrupt common mask;
  * @aggr_cache: last value of aggregation register status;
+ * @after_probe: flag indication probing completion;
+ * @not_asserted: number of entries in workqueue with no signal assertion;
  */
 struct mlxreg_hotplug_priv_data {
        int irq;
@@ -79,7 +84,6 @@ struct mlxreg_hotplug_priv_data {
        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_MAX + 1];
@@ -91,6 +95,7 @@ struct mlxreg_hotplug_priv_data {
        u32 mask;
        u32 aggr_cache;
        bool after_probe;
+       u8 not_asserted;
 };
 
 static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
@@ -217,7 +222,8 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
                }
        }
 
-       priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs *
+       priv->group.attrs = devm_kcalloc(&priv->pdev->dev,
+                                        num_attrs,
                                         sizeof(struct attribute *),
                                         GFP_KERNEL);
        if (!priv->group.attrs)
@@ -408,6 +414,18 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work)
        aggr_asserted = priv->aggr_cache ^ regval;
        priv->aggr_cache = regval;
 
+       /*
+        * Handler is invoked, but no assertion is detected at top aggregation
+        * status level. Set aggr_asserted to mask value to allow handler extra
+        * run over all relevant signals to recover any missed signal.
+        */
+       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) {
@@ -418,27 +436,26 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work)
                }
        }
 
-       if (aggr_asserted) {
-               spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->lock, flags);
 
-               /*
-                * It is possible, that some signals have been inserted, while
-                * interrupt has been masked by mlxreg_hotplug_work_handler.
-                * In this case such signals will be missed. In order to handle
-                * these signals delayed work is canceled and work task
-                * re-scheduled for immediate execution. It allows to handle
-                * missed signals, if any. In other case work handler just
-                * validates that no new signals have been received during
-                * masking.
-                */
-               cancel_delayed_work(&priv->dwork_irq);
-               schedule_delayed_work(&priv->dwork_irq, 0);
+       /*
+        * It is possible, that some signals have been inserted, while
+        * interrupt has been masked by mlxreg_hotplug_work_handler. In this
+        * case such signals will be missed. In order to handle these signals
+        * delayed work is canceled and work task re-scheduled for immediate
+        * execution. It allows to handle missed signals, if any. In other case
+        * work handler just validates that no new signals have been received
+        * during masking.
+        */
+       cancel_delayed_work(&priv->dwork_irq);
+       schedule_delayed_work(&priv->dwork_irq, 0);
 
-               spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->lock, flags);
 
-               return;
-       }
+       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);