Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2013 Broadcom Corporation
0004  *
0005  */
0006 
0007 #include <linux/debugfs.h>
0008 #include <linux/delay.h>
0009 #include <linux/err.h>
0010 #include <linux/io.h>
0011 #include <linux/module.h>
0012 #include <linux/of_address.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/watchdog.h>
0015 
0016 #define SECWDOG_CTRL_REG        0x00000000
0017 #define SECWDOG_COUNT_REG       0x00000004
0018 
0019 #define SECWDOG_RESERVED_MASK       0x1dffffff
0020 #define SECWDOG_WD_LOAD_FLAG        0x10000000
0021 #define SECWDOG_EN_MASK         0x08000000
0022 #define SECWDOG_SRSTEN_MASK     0x04000000
0023 #define SECWDOG_RES_MASK        0x00f00000
0024 #define SECWDOG_COUNT_MASK      0x000fffff
0025 
0026 #define SECWDOG_MAX_COUNT       SECWDOG_COUNT_MASK
0027 #define SECWDOG_CLKS_SHIFT      20
0028 #define SECWDOG_MAX_RES         15
0029 #define SECWDOG_DEFAULT_RESOLUTION  4
0030 #define SECWDOG_MAX_TRY         1000
0031 
0032 #define SECS_TO_TICKS(x, w)     ((x) << (w)->resolution)
0033 #define TICKS_TO_SECS(x, w)     ((x) >> (w)->resolution)
0034 
0035 #define BCM_KONA_WDT_NAME       "bcm_kona_wdt"
0036 
0037 struct bcm_kona_wdt {
0038     void __iomem *base;
0039     /*
0040      * One watchdog tick is 1/(2^resolution) seconds. Resolution can take
0041      * the values 0-15, meaning one tick can be 1s to 30.52us. Our default
0042      * resolution of 4 means one tick is 62.5ms.
0043      *
0044      * The watchdog counter is 20 bits. Depending on resolution, the maximum
0045      * counter value of 0xfffff expires after about 12 days (resolution 0)
0046      * down to only 32s (resolution 15). The default resolution of 4 gives
0047      * us a maximum of about 18 hours and 12 minutes before the watchdog
0048      * times out.
0049      */
0050     int resolution;
0051     spinlock_t lock;
0052 #ifdef CONFIG_BCM_KONA_WDT_DEBUG
0053     unsigned long busy_count;
0054     struct dentry *debugfs;
0055 #endif
0056 };
0057 
0058 static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset)
0059 {
0060     uint32_t val;
0061     unsigned count = 0;
0062 
0063     /*
0064      * If the WD_LOAD_FLAG is set, the watchdog counter field is being
0065      * updated in hardware. Once the WD timer is updated in hardware, it
0066      * gets cleared.
0067      */
0068     do {
0069         if (unlikely(count > 1))
0070             udelay(5);
0071         val = readl_relaxed(wdt->base + offset);
0072         count++;
0073     } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY);
0074 
0075 #ifdef CONFIG_BCM_KONA_WDT_DEBUG
0076     /* Remember the maximum number iterations due to WD_LOAD_FLAG */
0077     if (count > wdt->busy_count)
0078         wdt->busy_count = count;
0079 #endif
0080 
0081     /* This is the only place we return a negative value. */
0082     if (val & SECWDOG_WD_LOAD_FLAG)
0083         return -ETIMEDOUT;
0084 
0085     /* We always mask out reserved bits. */
0086     val &= SECWDOG_RESERVED_MASK;
0087 
0088     return val;
0089 }
0090 
0091 #ifdef CONFIG_BCM_KONA_WDT_DEBUG
0092 
0093 static int bcm_kona_show(struct seq_file *s, void *data)
0094 {
0095     int ctl_val, cur_val;
0096     unsigned long flags;
0097     struct bcm_kona_wdt *wdt = s->private;
0098 
0099     if (!wdt) {
0100         seq_puts(s, "No device pointer\n");
0101         return 0;
0102     }
0103 
0104     spin_lock_irqsave(&wdt->lock, flags);
0105     ctl_val = secure_register_read(wdt, SECWDOG_CTRL_REG);
0106     cur_val = secure_register_read(wdt, SECWDOG_COUNT_REG);
0107     spin_unlock_irqrestore(&wdt->lock, flags);
0108 
0109     if (ctl_val < 0 || cur_val < 0) {
0110         seq_puts(s, "Error accessing hardware\n");
0111     } else {
0112         int ctl, cur, ctl_sec, cur_sec, res;
0113 
0114         ctl = ctl_val & SECWDOG_COUNT_MASK;
0115         res = (ctl_val & SECWDOG_RES_MASK) >> SECWDOG_CLKS_SHIFT;
0116         cur = cur_val & SECWDOG_COUNT_MASK;
0117         ctl_sec = TICKS_TO_SECS(ctl, wdt);
0118         cur_sec = TICKS_TO_SECS(cur, wdt);
0119         seq_printf(s,
0120                "Resolution: %d / %d\n"
0121                "Control: %d s / %d (%#x) ticks\n"
0122                "Current: %d s / %d (%#x) ticks\n"
0123                "Busy count: %lu\n",
0124                res, wdt->resolution,
0125                ctl_sec, ctl, ctl,
0126                cur_sec, cur, cur,
0127                wdt->busy_count);
0128     }
0129 
0130     return 0;
0131 }
0132 
0133 DEFINE_SHOW_ATTRIBUTE(bcm_kona);
0134 
0135 static void bcm_kona_wdt_debug_init(struct platform_device *pdev)
0136 {
0137     struct dentry *dir;
0138     struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev);
0139 
0140     if (!wdt)
0141         return;
0142 
0143     wdt->debugfs = NULL;
0144 
0145     dir = debugfs_create_dir(BCM_KONA_WDT_NAME, NULL);
0146 
0147     debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt,
0148                 &bcm_kona_fops);
0149     wdt->debugfs = dir;
0150 }
0151 
0152 static void bcm_kona_wdt_debug_exit(struct platform_device *pdev)
0153 {
0154     struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev);
0155 
0156     if (wdt)
0157         debugfs_remove_recursive(wdt->debugfs);
0158 }
0159 
0160 #else
0161 
0162 static void bcm_kona_wdt_debug_init(struct platform_device *pdev) {}
0163 static void bcm_kona_wdt_debug_exit(struct platform_device *pdev) {}
0164 
0165 #endif /* CONFIG_BCM_KONA_WDT_DEBUG */
0166 
0167 static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt,
0168                     unsigned mask, unsigned newval)
0169 {
0170     int val;
0171     unsigned long flags;
0172     int ret = 0;
0173 
0174     spin_lock_irqsave(&wdt->lock, flags);
0175 
0176     val = secure_register_read(wdt, SECWDOG_CTRL_REG);
0177     if (val < 0) {
0178         ret = val;
0179     } else {
0180         val &= ~mask;
0181         val |= newval;
0182         writel_relaxed(val, wdt->base + SECWDOG_CTRL_REG);
0183     }
0184 
0185     spin_unlock_irqrestore(&wdt->lock, flags);
0186 
0187     return ret;
0188 }
0189 
0190 static int bcm_kona_wdt_set_resolution_reg(struct bcm_kona_wdt *wdt)
0191 {
0192     if (wdt->resolution > SECWDOG_MAX_RES)
0193         return -EINVAL;
0194 
0195     return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_RES_MASK,
0196                     wdt->resolution << SECWDOG_CLKS_SHIFT);
0197 }
0198 
0199 static int bcm_kona_wdt_set_timeout_reg(struct watchdog_device *wdog,
0200                     unsigned watchdog_flags)
0201 {
0202     struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
0203 
0204     return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_COUNT_MASK,
0205                     SECS_TO_TICKS(wdog->timeout, wdt) |
0206                     watchdog_flags);
0207 }
0208 
0209 static int bcm_kona_wdt_set_timeout(struct watchdog_device *wdog,
0210     unsigned int t)
0211 {
0212     wdog->timeout = t;
0213     return 0;
0214 }
0215 
0216 static unsigned int bcm_kona_wdt_get_timeleft(struct watchdog_device *wdog)
0217 {
0218     struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
0219     int val;
0220     unsigned long flags;
0221 
0222     spin_lock_irqsave(&wdt->lock, flags);
0223     val = secure_register_read(wdt, SECWDOG_COUNT_REG);
0224     spin_unlock_irqrestore(&wdt->lock, flags);
0225 
0226     if (val < 0)
0227         return val;
0228 
0229     return TICKS_TO_SECS(val & SECWDOG_COUNT_MASK, wdt);
0230 }
0231 
0232 static int bcm_kona_wdt_start(struct watchdog_device *wdog)
0233 {
0234     return bcm_kona_wdt_set_timeout_reg(wdog,
0235                     SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK);
0236 }
0237 
0238 static int bcm_kona_wdt_stop(struct watchdog_device *wdog)
0239 {
0240     struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
0241 
0242     return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_EN_MASK |
0243                         SECWDOG_SRSTEN_MASK, 0);
0244 }
0245 
0246 static const struct watchdog_ops bcm_kona_wdt_ops = {
0247     .owner =    THIS_MODULE,
0248     .start =    bcm_kona_wdt_start,
0249     .stop =     bcm_kona_wdt_stop,
0250     .set_timeout =  bcm_kona_wdt_set_timeout,
0251     .get_timeleft = bcm_kona_wdt_get_timeleft,
0252 };
0253 
0254 static const struct watchdog_info bcm_kona_wdt_info = {
0255     .options =  WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
0256             WDIOF_KEEPALIVEPING,
0257     .identity = "Broadcom Kona Watchdog Timer",
0258 };
0259 
0260 static struct watchdog_device bcm_kona_wdt_wdd = {
0261     .info =     &bcm_kona_wdt_info,
0262     .ops =      &bcm_kona_wdt_ops,
0263     .min_timeout =  1,
0264     .max_timeout =  SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
0265     .timeout =  SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
0266 };
0267 
0268 static int bcm_kona_wdt_probe(struct platform_device *pdev)
0269 {
0270     struct device *dev = &pdev->dev;
0271     struct bcm_kona_wdt *wdt;
0272     int ret;
0273 
0274     wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0275     if (!wdt)
0276         return -ENOMEM;
0277 
0278     spin_lock_init(&wdt->lock);
0279 
0280     wdt->base = devm_platform_ioremap_resource(pdev, 0);
0281     if (IS_ERR(wdt->base))
0282         return PTR_ERR(wdt->base);
0283 
0284     wdt->resolution = SECWDOG_DEFAULT_RESOLUTION;
0285     ret = bcm_kona_wdt_set_resolution_reg(wdt);
0286     if (ret) {
0287         dev_err(dev, "Failed to set resolution (error: %d)", ret);
0288         return ret;
0289     }
0290 
0291     platform_set_drvdata(pdev, wdt);
0292     watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
0293     bcm_kona_wdt_wdd.parent = dev;
0294 
0295     ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
0296     if (ret) {
0297         dev_err(dev, "Failed set watchdog timeout");
0298         return ret;
0299     }
0300 
0301     watchdog_stop_on_reboot(&bcm_kona_wdt_wdd);
0302     watchdog_stop_on_unregister(&bcm_kona_wdt_wdd);
0303     ret = devm_watchdog_register_device(dev, &bcm_kona_wdt_wdd);
0304     if (ret)
0305         return ret;
0306 
0307     bcm_kona_wdt_debug_init(pdev);
0308     dev_dbg(dev, "Broadcom Kona Watchdog Timer");
0309 
0310     return 0;
0311 }
0312 
0313 static int bcm_kona_wdt_remove(struct platform_device *pdev)
0314 {
0315     bcm_kona_wdt_debug_exit(pdev);
0316     dev_dbg(&pdev->dev, "Watchdog driver disabled");
0317 
0318     return 0;
0319 }
0320 
0321 static const struct of_device_id bcm_kona_wdt_of_match[] = {
0322     { .compatible = "brcm,kona-wdt", },
0323     {},
0324 };
0325 MODULE_DEVICE_TABLE(of, bcm_kona_wdt_of_match);
0326 
0327 static struct platform_driver bcm_kona_wdt_driver = {
0328     .driver = {
0329             .name = BCM_KONA_WDT_NAME,
0330             .of_match_table = bcm_kona_wdt_of_match,
0331           },
0332     .probe = bcm_kona_wdt_probe,
0333     .remove = bcm_kona_wdt_remove,
0334 };
0335 
0336 module_platform_driver(bcm_kona_wdt_driver);
0337 
0338 MODULE_ALIAS("platform:" BCM_KONA_WDT_NAME);
0339 MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
0340 MODULE_DESCRIPTION("Broadcom Kona Watchdog Driver");
0341 MODULE_LICENSE("GPL v2");