platform/x86: mlx-platform: Add mlx-wdt platform driver activation
authorVadim Pasternak <vadimp@mellanox.com>
Mon, 18 Mar 2019 10:58:23 +0000 (10:58 +0000)
committerAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Mon, 6 May 2019 14:54:42 +0000 (17:54 +0300)
Add mlx-wdt platform driver activation. Watchdog driver uses the same
regmap infrastructure as others Mellanox platform drivers. Specific
registers description for watchdog platform data configuration are
added to mlx-platform. There are the registers for watchdog timer
manipulation, and action setting on watchdog timer expiration.
The watchdog action function could be configured to perform one of the
following: system reset, setting PWM to full speed or counter
increment.
Two types of watchdog devices are supported main and auxiliary.
These devices are co-exist and each of them could be configured to
handle the specific action.

Signed-off-by: Michael Shych <michealsh@mellanox.com>
Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
drivers/platform/x86/mlx-platform.c

index 3f129a28ee43068bbe95fcdf22b0e5d5d8a55f7d..cee039f574994133b02e2e24400f73e134931968 100644 (file)
 #define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET                0x88
 #define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET  0x89
 #define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET   0x8a
+#define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET   0xc7
+#define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET        0xc8
+#define MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET    0xc9
+#define MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET    0xcb
+#define MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET    0xcd
+#define MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET  0xce
+#define MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET    0xcf
+#define MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET    0xd1
+#define MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET  0xd2
+#define MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET    0xd3
 #define MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET       0xe3
 #define MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET     0xe4
 #define MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET     0xe5
 #define MLXPLAT_CPLD_FAN3_DEFAULT_NR           13
 #define MLXPLAT_CPLD_FAN4_DEFAULT_NR           14
 
+/* Masks and default values for watchdogs */
+#define MLXPLAT_CPLD_WD1_CLEAR_MASK    GENMASK(7, 1)
+#define MLXPLAT_CPLD_WD2_CLEAR_MASK    (GENMASK(7, 0) & ~BIT(1))
+
+#define MLXPLAT_CPLD_WD_TYPE1_TO_MASK  GENMASK(7, 4)
+#define MLXPLAT_CPLD_WD_TYPE2_TO_MASK  0
+#define MLXPLAT_CPLD_WD_RESET_ACT_MASK GENMASK(7, 1)
+#define MLXPLAT_CPLD_WD_FAN_ACT_MASK   (GENMASK(7, 0) & ~BIT(4))
+#define MLXPLAT_CPLD_WD_COUNT_ACT_MASK (GENMASK(7, 0) & ~BIT(7))
+#define MLXPLAT_CPLD_WD_DFLT_TIMEOUT   30
+#define MLXPLAT_CPLD_WD_MAX_DEVS       2
+
 /* mlxplat_priv - platform private data
  * @pdev_i2c - i2c controller platform device
  * @pdev_mux - array of mux platform devices
  * @pdev_led - led platform devices
  * @pdev_io_regs - register access platform devices
  * @pdev_fan - FAN platform devices
+ * @pdev_wd - array of watchdog platform devices
  */
 struct mlxplat_priv {
        struct platform_device *pdev_i2c;
@@ -144,6 +167,7 @@ struct mlxplat_priv {
        struct platform_device *pdev_led;
        struct platform_device *pdev_io_regs;
        struct platform_device *pdev_fan;
+       struct platform_device *pdev_wd[MLXPLAT_CPLD_WD_MAX_DEVS];
 };
 
 /* Regions for LPC I2C controller and LPC base register space */
@@ -1351,6 +1375,148 @@ static struct mlxreg_core_platform_data mlxplat_default_fan_data = {
                .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_fan_data),
 };
 
+/* Watchdog type1: hardware implementation version1
+ * (MSN2700, MSN2410, MSN2740, MSN2100 and MSN2140 systems).
+ */
+static struct mlxreg_core_data mlxplat_mlxcpld_wd_main_regs_type1[] = {
+       {
+               .label = "action",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_RESET_ACT_MASK,
+               .bit = 0,
+       },
+       {
+               .label = "timeout",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_TYPE1_TO_MASK,
+               .health_cntr = MLXPLAT_CPLD_WD_DFLT_TIMEOUT,
+       },
+       {
+               .label = "ping",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET,
+               .mask = MLXPLAT_CPLD_WD1_CLEAR_MASK,
+               .bit = 0,
+       },
+       {
+               .label = "reset",
+               .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+               .mask = GENMASK(7, 0) & ~BIT(6),
+               .bit = 6,
+       },
+};
+
+static struct mlxreg_core_data mlxplat_mlxcpld_wd_aux_regs_type1[] = {
+       {
+               .label = "action",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_FAN_ACT_MASK,
+               .bit = 4,
+       },
+       {
+               .label = "timeout",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_TYPE1_TO_MASK,
+               .health_cntr = MLXPLAT_CPLD_WD_DFLT_TIMEOUT,
+       },
+       {
+               .label = "ping",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET,
+               .mask = MLXPLAT_CPLD_WD1_CLEAR_MASK,
+               .bit = 1,
+       },
+};
+
+static struct mlxreg_core_platform_data mlxplat_mlxcpld_wd_set_type1[] = {
+       {
+               .data = mlxplat_mlxcpld_wd_main_regs_type1,
+               .counter = ARRAY_SIZE(mlxplat_mlxcpld_wd_main_regs_type1),
+               .version = MLX_WDT_TYPE1,
+               .identity = "mlx-wdt-main",
+       },
+       {
+               .data = mlxplat_mlxcpld_wd_aux_regs_type1,
+               .counter = ARRAY_SIZE(mlxplat_mlxcpld_wd_aux_regs_type1),
+               .version = MLX_WDT_TYPE1,
+               .identity = "mlx-wdt-aux",
+       },
+};
+
+/* Watchdog type2: hardware implementation version 2
+ * (all systems except (MSN2700, MSN2410, MSN2740, MSN2100 and MSN2140).
+ */
+static struct mlxreg_core_data mlxplat_mlxcpld_wd_main_regs_type2[] = {
+       {
+               .label = "action",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_RESET_ACT_MASK,
+               .bit = 0,
+       },
+       {
+               .label = "timeout",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK,
+               .health_cntr = MLXPLAT_CPLD_WD_DFLT_TIMEOUT,
+       },
+       {
+               .label = "timeleft",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK,
+       },
+       {
+               .label = "ping",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_RESET_ACT_MASK,
+               .bit = 0,
+       },
+       {
+               .label = "reset",
+               .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+               .mask = GENMASK(7, 0) & ~BIT(6),
+               .bit = 6,
+       },
+};
+
+static struct mlxreg_core_data mlxplat_mlxcpld_wd_aux_regs_type2[] = {
+       {
+               .label = "action",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_FAN_ACT_MASK,
+               .bit = 4,
+       },
+       {
+               .label = "timeout",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK,
+               .health_cntr = MLXPLAT_CPLD_WD_DFLT_TIMEOUT,
+       },
+       {
+               .label = "timeleft",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK,
+       },
+       {
+               .label = "ping",
+               .reg = MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET,
+               .mask = MLXPLAT_CPLD_WD_FAN_ACT_MASK,
+               .bit = 4,
+       },
+};
+
+static struct mlxreg_core_platform_data mlxplat_mlxcpld_wd_set_type2[] = {
+       {
+               .data = mlxplat_mlxcpld_wd_main_regs_type2,
+               .counter = ARRAY_SIZE(mlxplat_mlxcpld_wd_main_regs_type2),
+               .version = MLX_WDT_TYPE2,
+               .identity = "mlx-wdt-main",
+       },
+       {
+               .data = mlxplat_mlxcpld_wd_aux_regs_type2,
+               .counter = ARRAY_SIZE(mlxplat_mlxcpld_wd_aux_regs_type2),
+               .version = MLX_WDT_TYPE2,
+               .identity = "mlx-wdt-aux",
+       },
+};
+
 static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -1373,6 +1539,14 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
        case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET:
                return true;
@@ -1416,6 +1590,16 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
        case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET:
@@ -1473,6 +1657,10 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
        case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET:
        case MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET:
@@ -1500,6 +1688,7 @@ static const struct reg_default mlxplat_mlxcpld_regmap_default[] = {
        { MLXPLAT_CPLD_LPC_REG_WP1_OFFSET, 0x00 },
        { MLXPLAT_CPLD_LPC_REG_WP2_OFFSET, 0x00 },
        { MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, 0x00 },
+       { MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET, 0x00 },
 };
 
 struct mlxplat_mlxcpld_regmap_context {
@@ -1549,6 +1738,8 @@ 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 struct mlxreg_core_platform_data *mlxplat_fan;
+static struct mlxreg_core_platform_data
+       *mlxplat_wd_data[MLXPLAT_CPLD_WD_MAX_DEVS];
 
 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
 {
@@ -1564,6 +1755,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
                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;
+       mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0];
 
        return 1;
 };
@@ -1582,6 +1774,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
                mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
        mlxplat_led = &mlxplat_msn21xx_led_data;
        mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data;
+       mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0];
 
        return 1;
 };
@@ -1600,6 +1793,7 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
                mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
        mlxplat_led = &mlxplat_default_led_data;
        mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data;
+       mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0];
 
        return 1;
 };
@@ -1618,6 +1812,7 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
                mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
        mlxplat_led = &mlxplat_msn21xx_led_data;
        mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data;
+       mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0];
 
        return 1;
 };
@@ -1637,6 +1832,8 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
        mlxplat_led = &mlxplat_default_ng_led_data;
        mlxplat_regs_io = &mlxplat_default_ng_regs_io_data;
        mlxplat_fan = &mlxplat_default_fan_data;
+       for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++)
+               mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i];
 
        return 1;
 };
@@ -1919,15 +2116,33 @@ static int __init mlxplat_init(void)
                }
        }
 
+       /* Add WD drivers. */
+       for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) {
+               if (mlxplat_wd_data[j]) {
+                       mlxplat_wd_data[j]->regmap = mlxplat_hotplug->regmap;
+                       priv->pdev_wd[j] = platform_device_register_resndata(
+                                               &mlxplat_dev->dev, "mlx-wdt",
+                                               j, NULL, 0,
+                                               mlxplat_wd_data[j],
+                                               sizeof(*mlxplat_wd_data[j]));
+                       if (IS_ERR(priv->pdev_wd[j])) {
+                               err = PTR_ERR(priv->pdev_wd[j]);
+                               goto fail_platform_wd_register;
+                       }
+               }
+       }
+
        /* Sync registers with hardware. */
        regcache_mark_dirty(mlxplat_hotplug->regmap);
        err = regcache_sync(mlxplat_hotplug->regmap);
        if (err)
-               goto fail_platform_fan_register;
+               goto fail_platform_wd_register;
 
        return 0;
 
-fail_platform_fan_register:
+fail_platform_wd_register:
+       while (--j >= 0)
+               platform_device_unregister(priv->pdev_wd[j]);
        if (mlxplat_fan)
                platform_device_unregister(priv->pdev_fan);
 fail_platform_io_regs_register:
@@ -1953,6 +2168,8 @@ static void __exit mlxplat_exit(void)
        struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
        int i;
 
+       for (i = MLXPLAT_CPLD_WD_MAX_DEVS - 1; i >= 0 ; i--)
+               platform_device_unregister(priv->pdev_wd[i]);
        if (priv->pdev_fan)
                platform_device_unregister(priv->pdev_fan);
        if (priv->pdev_io_regs)