0001
0002
0003
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
0041
0042
0043
0044
0045
0046
0047
0048
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
0065
0066
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
0077 if (count > wdt->busy_count)
0078 wdt->busy_count = count;
0079 #endif
0080
0081
0082 if (val & SECWDOG_WD_LOAD_FLAG)
0083 return -ETIMEDOUT;
0084
0085
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
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");