Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2020 ROHM Semiconductors
0004  *
0005  * ROHM BD9576MUF and BD9573MUF Watchdog driver
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     /* Pulse */
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 /* Unit is hundreds of uS */
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     /* convert to 100uS */
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");