0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/err.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/mfd/rohm-bd957x.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/regmap.h>
0015 #include <linux/watchdog.h>
0016
0017 static bool nowayout;
0018 module_param(nowayout, bool, 0);
0019 MODULE_PARM_DESC(nowayout,
0020 "Watchdog cannot be stopped once started (default=\"false\")");
0021
0022 #define HW_MARGIN_MIN 2
0023 #define HW_MARGIN_MAX 4416
0024 #define BD957X_WDT_DEFAULT_MARGIN 4416
0025 #define WATCHDOG_TIMEOUT 30
0026
0027 struct bd9576_wdt_priv {
0028 struct gpio_desc *gpiod_ping;
0029 struct gpio_desc *gpiod_en;
0030 struct device *dev;
0031 struct regmap *regmap;
0032 bool always_running;
0033 struct watchdog_device wdd;
0034 };
0035
0036 static void bd9576_wdt_disable(struct bd9576_wdt_priv *priv)
0037 {
0038 gpiod_set_value_cansleep(priv->gpiod_en, 0);
0039 }
0040
0041 static int bd9576_wdt_ping(struct watchdog_device *wdd)
0042 {
0043 struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd);
0044
0045
0046 gpiod_set_value_cansleep(priv->gpiod_ping, 1);
0047 gpiod_set_value_cansleep(priv->gpiod_ping, 0);
0048
0049 return 0;
0050 }
0051
0052 static int bd9576_wdt_start(struct watchdog_device *wdd)
0053 {
0054 struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd);
0055
0056 gpiod_set_value_cansleep(priv->gpiod_en, 1);
0057
0058 return bd9576_wdt_ping(wdd);
0059 }
0060
0061 static int bd9576_wdt_stop(struct watchdog_device *wdd)
0062 {
0063 struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd);
0064
0065 if (!priv->always_running)
0066 bd9576_wdt_disable(priv);
0067 else
0068 set_bit(WDOG_HW_RUNNING, &wdd->status);
0069
0070 return 0;
0071 }
0072
0073 static const struct watchdog_info bd957x_wdt_ident = {
0074 .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
0075 WDIOF_SETTIMEOUT,
0076 .identity = "BD957x Watchdog",
0077 };
0078
0079 static const struct watchdog_ops bd957x_wdt_ops = {
0080 .owner = THIS_MODULE,
0081 .start = bd9576_wdt_start,
0082 .stop = bd9576_wdt_stop,
0083 .ping = bd9576_wdt_ping,
0084 };
0085
0086
0087 #define FASTNG_MIN 23
0088
0089 static int find_closest_fast(int target, int *sel, int *val)
0090 {
0091 int i;
0092 int window = FASTNG_MIN;
0093
0094 for (i = 0; i < 8 && window < target; i++)
0095 window <<= 1;
0096
0097 *val = window;
0098 *sel = i;
0099
0100 if (i == 8)
0101 return -EINVAL;
0102
0103 return 0;
0104
0105 }
0106
0107 static int find_closest_slow_by_fast(int fast_val, int target, int *slowsel)
0108 {
0109 int sel;
0110 static const int multipliers[] = {2, 3, 7, 15};
0111
0112 for (sel = 0; sel < ARRAY_SIZE(multipliers) &&
0113 multipliers[sel] * fast_val < target; sel++)
0114 ;
0115
0116 if (sel == ARRAY_SIZE(multipliers))
0117 return -EINVAL;
0118
0119 *slowsel = sel;
0120
0121 return 0;
0122 }
0123
0124 static int find_closest_slow(int target, int *slow_sel, int *fast_sel)
0125 {
0126 static const int multipliers[] = {2, 3, 7, 15};
0127 int i, j;
0128 int val = 0;
0129 int window = FASTNG_MIN;
0130
0131 for (i = 0; i < 8; i++) {
0132 for (j = 0; j < ARRAY_SIZE(multipliers); j++) {
0133 int slow;
0134
0135 slow = window * multipliers[j];
0136 if (slow >= target && (!val || slow < val)) {
0137 val = slow;
0138 *fast_sel = i;
0139 *slow_sel = j;
0140 }
0141 }
0142 window <<= 1;
0143 }
0144 if (!val)
0145 return -EINVAL;
0146
0147 return 0;
0148 }
0149
0150 #define BD957X_WDG_TYPE_WINDOW BIT(5)
0151 #define BD957X_WDG_TYPE_SLOW 0
0152 #define BD957X_WDG_TYPE_MASK BIT(5)
0153 #define BD957X_WDG_NG_RATIO_MASK 0x18
0154 #define BD957X_WDG_FASTNG_MASK 0x7
0155
0156 static int bd957x_set_wdt_mode(struct bd9576_wdt_priv *priv, int hw_margin,
0157 int hw_margin_min)
0158 {
0159 int ret, fastng, slowng, type, reg, mask;
0160 struct device *dev = priv->dev;
0161
0162
0163 hw_margin *= 10;
0164 hw_margin_min *= 10;
0165 if (hw_margin_min) {
0166 int min;
0167
0168 type = BD957X_WDG_TYPE_WINDOW;
0169 dev_dbg(dev, "Setting type WINDOW 0x%x\n", type);
0170 ret = find_closest_fast(hw_margin_min, &fastng, &min);
0171 if (ret) {
0172 dev_err(dev, "bad WDT window for fast timeout\n");
0173 return ret;
0174 }
0175
0176 ret = find_closest_slow_by_fast(min, hw_margin, &slowng);
0177 if (ret) {
0178 dev_err(dev, "bad WDT window\n");
0179 return ret;
0180 }
0181
0182 } else {
0183 type = BD957X_WDG_TYPE_SLOW;
0184 dev_dbg(dev, "Setting type SLOW 0x%x\n", type);
0185 ret = find_closest_slow(hw_margin, &slowng, &fastng);
0186 if (ret) {
0187 dev_err(dev, "bad WDT window\n");
0188 return ret;
0189 }
0190 }
0191
0192 slowng <<= ffs(BD957X_WDG_NG_RATIO_MASK) - 1;
0193 reg = type | slowng | fastng;
0194 mask = BD957X_WDG_TYPE_MASK | BD957X_WDG_NG_RATIO_MASK |
0195 BD957X_WDG_FASTNG_MASK;
0196 ret = regmap_update_bits(priv->regmap, BD957X_REG_WDT_CONF,
0197 mask, reg);
0198
0199 return ret;
0200 }
0201
0202 static int bd9576_wdt_probe(struct platform_device *pdev)
0203 {
0204 struct device *dev = &pdev->dev;
0205 struct device_node *np = dev->parent->of_node;
0206 struct bd9576_wdt_priv *priv;
0207 u32 hw_margin[2];
0208 u32 hw_margin_max = BD957X_WDT_DEFAULT_MARGIN, hw_margin_min = 0;
0209 int ret;
0210
0211 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0212 if (!priv)
0213 return -ENOMEM;
0214
0215 platform_set_drvdata(pdev, priv);
0216
0217 priv->dev = dev;
0218 priv->regmap = dev_get_regmap(dev->parent, NULL);
0219 if (!priv->regmap) {
0220 dev_err(dev, "No regmap found\n");
0221 return -ENODEV;
0222 }
0223
0224 priv->gpiod_en = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
0225 "rohm,watchdog-enable-gpios",
0226 0, GPIOD_OUT_LOW,
0227 "watchdog-enable");
0228 if (IS_ERR(priv->gpiod_en))
0229 return dev_err_probe(dev, PTR_ERR(priv->gpiod_en),
0230 "getting watchdog-enable GPIO failed\n");
0231
0232 priv->gpiod_ping = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
0233 "rohm,watchdog-ping-gpios",
0234 0, GPIOD_OUT_LOW,
0235 "watchdog-ping");
0236 if (IS_ERR(priv->gpiod_ping))
0237 return dev_err_probe(dev, PTR_ERR(priv->gpiod_ping),
0238 "getting watchdog-ping GPIO failed\n");
0239
0240 ret = of_property_read_variable_u32_array(np, "rohm,hw-timeout-ms",
0241 &hw_margin[0], 1, 2);
0242 if (ret < 0 && ret != -EINVAL)
0243 return ret;
0244
0245 if (ret == 1)
0246 hw_margin_max = hw_margin[0];
0247
0248 if (ret == 2) {
0249 hw_margin_max = hw_margin[1];
0250 hw_margin_min = hw_margin[0];
0251 }
0252
0253 ret = bd957x_set_wdt_mode(priv, hw_margin_max, hw_margin_min);
0254 if (ret)
0255 return ret;
0256
0257 priv->always_running = of_property_read_bool(np, "always-running");
0258
0259 watchdog_set_drvdata(&priv->wdd, priv);
0260
0261 priv->wdd.info = &bd957x_wdt_ident;
0262 priv->wdd.ops = &bd957x_wdt_ops;
0263 priv->wdd.min_hw_heartbeat_ms = hw_margin_min;
0264 priv->wdd.max_hw_heartbeat_ms = hw_margin_max;
0265 priv->wdd.parent = dev;
0266 priv->wdd.timeout = WATCHDOG_TIMEOUT;
0267
0268 watchdog_init_timeout(&priv->wdd, 0, dev);
0269 watchdog_set_nowayout(&priv->wdd, nowayout);
0270
0271 watchdog_stop_on_reboot(&priv->wdd);
0272
0273 if (priv->always_running)
0274 bd9576_wdt_start(&priv->wdd);
0275
0276 return devm_watchdog_register_device(dev, &priv->wdd);
0277 }
0278
0279 static struct platform_driver bd9576_wdt_driver = {
0280 .driver = {
0281 .name = "bd9576-wdt",
0282 },
0283 .probe = bd9576_wdt_probe,
0284 };
0285
0286 module_platform_driver(bd9576_wdt_driver);
0287
0288 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
0289 MODULE_DESCRIPTION("ROHM BD9576/BD9573 Watchdog driver");
0290 MODULE_LICENSE("GPL");
0291 MODULE_ALIAS("platform:bd9576-wdt");