0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/bitops.h>
0010 #include <linux/device.h>
0011 #include <linux/errno.h>
0012 #include <linux/log2.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_data/mlxreg.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/regmap.h>
0017 #include <linux/spinlock.h>
0018 #include <linux/types.h>
0019 #include <linux/watchdog.h>
0020
0021 #define MLXREG_WDT_CLOCK_SCALE 1000
0022 #define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32
0023 #define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255
0024 #define MLXREG_WDT_MAX_TIMEOUT_TYPE3 65535
0025 #define MLXREG_WDT_MIN_TIMEOUT 1
0026 #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
0027 WDIOF_SETTIMEOUT)
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 struct mlxreg_wdt {
0045 struct watchdog_device wdd;
0046 struct mlxreg_core_platform_data *pdata;
0047 void *regmap;
0048 int action_idx;
0049 int timeout_idx;
0050 int tleft_idx;
0051 int ping_idx;
0052 int reset_idx;
0053 int regmap_val_sz;
0054 enum mlxreg_wdt_type wdt_type;
0055 };
0056
0057 static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
0058 {
0059 struct mlxreg_core_data *reg_data;
0060 u32 regval;
0061 int rc;
0062
0063 if (wdt->reset_idx == -EINVAL)
0064 return;
0065
0066 if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
0067 return;
0068
0069 reg_data = &wdt->pdata->data[wdt->reset_idx];
0070 rc = regmap_read(wdt->regmap, reg_data->reg, ®val);
0071 if (!rc) {
0072 if (regval & ~reg_data->mask) {
0073 wdt->wdd.bootstatus = WDIOF_CARDRESET;
0074 dev_info(wdt->wdd.parent,
0075 "watchdog previously reset the CPU\n");
0076 }
0077 }
0078 }
0079
0080 static int mlxreg_wdt_start(struct watchdog_device *wdd)
0081 {
0082 struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0083 struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
0084
0085 return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
0086 BIT(reg_data->bit));
0087 }
0088
0089 static int mlxreg_wdt_stop(struct watchdog_device *wdd)
0090 {
0091 struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0092 struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
0093
0094 return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
0095 ~BIT(reg_data->bit));
0096 }
0097
0098 static int mlxreg_wdt_ping(struct watchdog_device *wdd)
0099 {
0100 struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0101 struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
0102
0103 return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
0104 BIT(reg_data->bit));
0105 }
0106
0107 static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
0108 unsigned int timeout)
0109 {
0110 struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0111 struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
0112 u32 regval, set_time, hw_timeout;
0113 int rc;
0114
0115 switch (wdt->wdt_type) {
0116 case MLX_WDT_TYPE1:
0117 rc = regmap_read(wdt->regmap, reg_data->reg, ®val);
0118 if (rc)
0119 return rc;
0120
0121 hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
0122 regval = (regval & reg_data->mask) | hw_timeout;
0123
0124 set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
0125 rc = regmap_write(wdt->regmap, reg_data->reg, regval);
0126 break;
0127 case MLX_WDT_TYPE2:
0128 set_time = timeout;
0129 rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
0130 break;
0131 case MLX_WDT_TYPE3:
0132
0133 set_time = timeout;
0134 if (wdt->regmap_val_sz == 1) {
0135 regval = timeout & 0xff;
0136 rc = regmap_write(wdt->regmap, reg_data->reg, regval);
0137 if (!rc) {
0138 regval = (timeout & 0xff00) >> 8;
0139 rc = regmap_write(wdt->regmap,
0140 reg_data->reg + 1, regval);
0141 }
0142 } else {
0143 rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
0144 }
0145 break;
0146 default:
0147 return -EINVAL;
0148 }
0149
0150 wdd->timeout = set_time;
0151 if (!rc) {
0152
0153
0154
0155
0156 if (watchdog_active(wdd)) {
0157 rc = mlxreg_wdt_stop(wdd);
0158 if (!rc)
0159 rc = mlxreg_wdt_start(wdd);
0160 }
0161 }
0162
0163 return rc;
0164 }
0165
0166 static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
0167 {
0168 struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0169 struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
0170 u32 regval, msb, lsb;
0171 int rc;
0172
0173 if (wdt->wdt_type == MLX_WDT_TYPE2) {
0174 rc = regmap_read(wdt->regmap, reg_data->reg, ®val);
0175 } else {
0176
0177 if (wdt->regmap_val_sz == 1) {
0178 rc = regmap_read(wdt->regmap, reg_data->reg, &lsb);
0179 if (!rc) {
0180 rc = regmap_read(wdt->regmap,
0181 reg_data->reg + 1, &msb);
0182 regval = (msb & 0xff) << 8 | (lsb & 0xff);
0183 }
0184 } else {
0185 rc = regmap_read(wdt->regmap, reg_data->reg, ®val);
0186 }
0187 }
0188
0189
0190 return rc == 0 ? regval : 0;
0191 }
0192
0193 static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
0194 .start = mlxreg_wdt_start,
0195 .stop = mlxreg_wdt_stop,
0196 .ping = mlxreg_wdt_ping,
0197 .set_timeout = mlxreg_wdt_set_timeout,
0198 .owner = THIS_MODULE,
0199 };
0200
0201 static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
0202 .start = mlxreg_wdt_start,
0203 .stop = mlxreg_wdt_stop,
0204 .ping = mlxreg_wdt_ping,
0205 .set_timeout = mlxreg_wdt_set_timeout,
0206 .get_timeleft = mlxreg_wdt_get_timeleft,
0207 .owner = THIS_MODULE,
0208 };
0209
0210 static const struct watchdog_info mlxreg_wdt_main_info = {
0211 .options = MLXREG_WDT_OPTIONS_BASE
0212 | WDIOF_CARDRESET,
0213 .identity = "mlx-wdt-main",
0214 };
0215
0216 static const struct watchdog_info mlxreg_wdt_aux_info = {
0217 .options = MLXREG_WDT_OPTIONS_BASE
0218 | WDIOF_ALARMONLY,
0219 .identity = "mlx-wdt-aux",
0220 };
0221
0222 static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
0223 struct mlxreg_core_platform_data *pdata)
0224 {
0225 struct mlxreg_core_data *data = pdata->data;
0226 int i;
0227
0228 wdt->reset_idx = -EINVAL;
0229 for (i = 0; i < pdata->counter; i++, data++) {
0230 if (strnstr(data->label, "action", sizeof(data->label)))
0231 wdt->action_idx = i;
0232 else if (strnstr(data->label, "timeout", sizeof(data->label)))
0233 wdt->timeout_idx = i;
0234 else if (strnstr(data->label, "timeleft", sizeof(data->label)))
0235 wdt->tleft_idx = i;
0236 else if (strnstr(data->label, "ping", sizeof(data->label)))
0237 wdt->ping_idx = i;
0238 else if (strnstr(data->label, "reset", sizeof(data->label)))
0239 wdt->reset_idx = i;
0240 }
0241
0242 wdt->pdata = pdata;
0243 if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
0244 sizeof(mlxreg_wdt_main_info.identity)))
0245 wdt->wdd.info = &mlxreg_wdt_main_info;
0246 else
0247 wdt->wdd.info = &mlxreg_wdt_aux_info;
0248
0249 wdt->wdt_type = pdata->version;
0250 switch (wdt->wdt_type) {
0251 case MLX_WDT_TYPE1:
0252 wdt->wdd.ops = &mlxreg_wdt_ops_type1;
0253 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
0254 break;
0255 case MLX_WDT_TYPE2:
0256 wdt->wdd.ops = &mlxreg_wdt_ops_type2;
0257 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
0258 break;
0259 case MLX_WDT_TYPE3:
0260 wdt->wdd.ops = &mlxreg_wdt_ops_type2;
0261 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3;
0262 break;
0263 default:
0264 break;
0265 }
0266
0267 wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
0268 }
0269
0270 static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
0271 struct mlxreg_core_platform_data *pdata)
0272 {
0273 u32 timeout;
0274
0275 timeout = pdata->data[wdt->timeout_idx].health_cntr;
0276 return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
0277 }
0278
0279 static int mlxreg_wdt_probe(struct platform_device *pdev)
0280 {
0281 struct device *dev = &pdev->dev;
0282 struct mlxreg_core_platform_data *pdata;
0283 struct mlxreg_wdt *wdt;
0284 int rc;
0285
0286 pdata = dev_get_platdata(dev);
0287 if (!pdata) {
0288 dev_err(dev, "Failed to get platform data.\n");
0289 return -EINVAL;
0290 }
0291 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0292 if (!wdt)
0293 return -ENOMEM;
0294
0295 wdt->wdd.parent = dev;
0296 wdt->regmap = pdata->regmap;
0297 rc = regmap_get_val_bytes(wdt->regmap);
0298 if (rc < 0)
0299 return -EINVAL;
0300
0301 wdt->regmap_val_sz = rc;
0302 mlxreg_wdt_config(wdt, pdata);
0303
0304 if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
0305 watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
0306 watchdog_stop_on_reboot(&wdt->wdd);
0307 watchdog_stop_on_unregister(&wdt->wdd);
0308 watchdog_set_drvdata(&wdt->wdd, wdt);
0309 rc = mlxreg_wdt_init_timeout(wdt, pdata);
0310 if (rc)
0311 goto register_error;
0312
0313 if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
0314 rc = mlxreg_wdt_start(&wdt->wdd);
0315 if (rc)
0316 goto register_error;
0317 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
0318 }
0319 mlxreg_wdt_check_card_reset(wdt);
0320 rc = devm_watchdog_register_device(dev, &wdt->wdd);
0321
0322 register_error:
0323 if (rc)
0324 dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
0325 return rc;
0326 }
0327
0328 static struct platform_driver mlxreg_wdt_driver = {
0329 .probe = mlxreg_wdt_probe,
0330 .driver = {
0331 .name = "mlx-wdt",
0332 },
0333 };
0334
0335 module_platform_driver(mlxreg_wdt_driver);
0336
0337 MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
0338 MODULE_DESCRIPTION("Mellanox watchdog driver");
0339 MODULE_LICENSE("GPL");
0340 MODULE_ALIAS("platform:mlx-wdt");