0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0015
0016 #include <linux/module.h>
0017 #include <linux/moduleparam.h>
0018 #include <linux/errno.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/watchdog.h>
0022 #include <linux/fs.h>
0023 #include <linux/ioport.h>
0024 #include <linux/io.h>
0025 #include <linux/uaccess.h>
0026 #include <linux/clk.h>
0027
0028 #include <asm/addrspace.h>
0029 #include <asm/mach-ar7/ar7.h>
0030
0031 #define LONGNAME "TI AR7 Watchdog Timer"
0032
0033 MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
0034 MODULE_DESCRIPTION(LONGNAME);
0035 MODULE_LICENSE("GPL");
0036
0037 static int margin = 60;
0038 module_param(margin, int, 0);
0039 MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
0040
0041 static bool nowayout = WATCHDOG_NOWAYOUT;
0042 module_param(nowayout, bool, 0);
0043 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
0044
0045 #define READ_REG(x) readl((void __iomem *)&(x))
0046 #define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
0047
0048 struct ar7_wdt {
0049 u32 kick_lock;
0050 u32 kick;
0051 u32 change_lock;
0052 u32 change;
0053 u32 disable_lock;
0054 u32 disable;
0055 u32 prescale_lock;
0056 u32 prescale;
0057 };
0058
0059 static unsigned long wdt_is_open;
0060 static unsigned expect_close;
0061 static DEFINE_SPINLOCK(wdt_lock);
0062
0063
0064 #define prescale_value 0xffff
0065
0066
0067 static struct ar7_wdt *ar7_wdt;
0068
0069 static struct clk *vbus_clk;
0070
0071 static void ar7_wdt_kick(u32 value)
0072 {
0073 WRITE_REG(ar7_wdt->kick_lock, 0x5555);
0074 if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
0075 WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
0076 if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
0077 WRITE_REG(ar7_wdt->kick, value);
0078 return;
0079 }
0080 }
0081 pr_err("failed to unlock WDT kick reg\n");
0082 }
0083
0084 static void ar7_wdt_prescale(u32 value)
0085 {
0086 WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
0087 if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
0088 WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
0089 if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
0090 WRITE_REG(ar7_wdt->prescale, value);
0091 return;
0092 }
0093 }
0094 pr_err("failed to unlock WDT prescale reg\n");
0095 }
0096
0097 static void ar7_wdt_change(u32 value)
0098 {
0099 WRITE_REG(ar7_wdt->change_lock, 0x6666);
0100 if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
0101 WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
0102 if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
0103 WRITE_REG(ar7_wdt->change, value);
0104 return;
0105 }
0106 }
0107 pr_err("failed to unlock WDT change reg\n");
0108 }
0109
0110 static void ar7_wdt_disable(u32 value)
0111 {
0112 WRITE_REG(ar7_wdt->disable_lock, 0x7777);
0113 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
0114 WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
0115 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
0116 WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
0117 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
0118 WRITE_REG(ar7_wdt->disable, value);
0119 return;
0120 }
0121 }
0122 }
0123 pr_err("failed to unlock WDT disable reg\n");
0124 }
0125
0126 static void ar7_wdt_update_margin(int new_margin)
0127 {
0128 u32 change;
0129 u32 vbus_rate;
0130
0131 vbus_rate = clk_get_rate(vbus_clk);
0132 change = new_margin * (vbus_rate / prescale_value);
0133 if (change < 1)
0134 change = 1;
0135 if (change > 0xffff)
0136 change = 0xffff;
0137 ar7_wdt_change(change);
0138 margin = change * prescale_value / vbus_rate;
0139 pr_info("timer margin %d seconds (prescale %d, change %d, freq %d)\n",
0140 margin, prescale_value, change, vbus_rate);
0141 }
0142
0143 static void ar7_wdt_enable_wdt(void)
0144 {
0145 pr_debug("enabling watchdog timer\n");
0146 ar7_wdt_disable(1);
0147 ar7_wdt_kick(1);
0148 }
0149
0150 static void ar7_wdt_disable_wdt(void)
0151 {
0152 pr_debug("disabling watchdog timer\n");
0153 ar7_wdt_disable(0);
0154 }
0155
0156 static int ar7_wdt_open(struct inode *inode, struct file *file)
0157 {
0158
0159 if (test_and_set_bit(0, &wdt_is_open))
0160 return -EBUSY;
0161 ar7_wdt_enable_wdt();
0162 expect_close = 0;
0163
0164 return stream_open(inode, file);
0165 }
0166
0167 static int ar7_wdt_release(struct inode *inode, struct file *file)
0168 {
0169 if (!expect_close)
0170 pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
0171 else if (!nowayout)
0172 ar7_wdt_disable_wdt();
0173 clear_bit(0, &wdt_is_open);
0174 return 0;
0175 }
0176
0177 static ssize_t ar7_wdt_write(struct file *file, const char *data,
0178 size_t len, loff_t *ppos)
0179 {
0180
0181 if (len) {
0182 size_t i;
0183
0184 spin_lock(&wdt_lock);
0185 ar7_wdt_kick(1);
0186 spin_unlock(&wdt_lock);
0187
0188 expect_close = 0;
0189 for (i = 0; i < len; ++i) {
0190 char c;
0191 if (get_user(c, data + i))
0192 return -EFAULT;
0193 if (c == 'V')
0194 expect_close = 1;
0195 }
0196
0197 }
0198 return len;
0199 }
0200
0201 static long ar7_wdt_ioctl(struct file *file,
0202 unsigned int cmd, unsigned long arg)
0203 {
0204 static const struct watchdog_info ident = {
0205 .identity = LONGNAME,
0206 .firmware_version = 1,
0207 .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
0208 WDIOF_MAGICCLOSE),
0209 };
0210 int new_margin;
0211
0212 switch (cmd) {
0213 case WDIOC_GETSUPPORT:
0214 if (copy_to_user((struct watchdog_info *)arg, &ident,
0215 sizeof(ident)))
0216 return -EFAULT;
0217 return 0;
0218 case WDIOC_GETSTATUS:
0219 case WDIOC_GETBOOTSTATUS:
0220 if (put_user(0, (int *)arg))
0221 return -EFAULT;
0222 return 0;
0223 case WDIOC_KEEPALIVE:
0224 ar7_wdt_kick(1);
0225 return 0;
0226 case WDIOC_SETTIMEOUT:
0227 if (get_user(new_margin, (int *)arg))
0228 return -EFAULT;
0229 if (new_margin < 1)
0230 return -EINVAL;
0231
0232 spin_lock(&wdt_lock);
0233 ar7_wdt_update_margin(new_margin);
0234 ar7_wdt_kick(1);
0235 spin_unlock(&wdt_lock);
0236 fallthrough;
0237 case WDIOC_GETTIMEOUT:
0238 if (put_user(margin, (int *)arg))
0239 return -EFAULT;
0240 return 0;
0241 default:
0242 return -ENOTTY;
0243 }
0244 }
0245
0246 static const struct file_operations ar7_wdt_fops = {
0247 .owner = THIS_MODULE,
0248 .write = ar7_wdt_write,
0249 .unlocked_ioctl = ar7_wdt_ioctl,
0250 .compat_ioctl = compat_ptr_ioctl,
0251 .open = ar7_wdt_open,
0252 .release = ar7_wdt_release,
0253 .llseek = no_llseek,
0254 };
0255
0256 static struct miscdevice ar7_wdt_miscdev = {
0257 .minor = WATCHDOG_MINOR,
0258 .name = "watchdog",
0259 .fops = &ar7_wdt_fops,
0260 };
0261
0262 static int ar7_wdt_probe(struct platform_device *pdev)
0263 {
0264 int rc;
0265
0266 ar7_wdt = devm_platform_ioremap_resource_byname(pdev, "regs");
0267 if (IS_ERR(ar7_wdt))
0268 return PTR_ERR(ar7_wdt);
0269
0270 vbus_clk = clk_get(NULL, "vbus");
0271 if (IS_ERR(vbus_clk)) {
0272 pr_err("could not get vbus clock\n");
0273 return PTR_ERR(vbus_clk);
0274 }
0275
0276 ar7_wdt_disable_wdt();
0277 ar7_wdt_prescale(prescale_value);
0278 ar7_wdt_update_margin(margin);
0279
0280 rc = misc_register(&ar7_wdt_miscdev);
0281 if (rc) {
0282 pr_err("unable to register misc device\n");
0283 goto out;
0284 }
0285 return 0;
0286
0287 out:
0288 clk_put(vbus_clk);
0289 vbus_clk = NULL;
0290 return rc;
0291 }
0292
0293 static int ar7_wdt_remove(struct platform_device *pdev)
0294 {
0295 misc_deregister(&ar7_wdt_miscdev);
0296 clk_put(vbus_clk);
0297 vbus_clk = NULL;
0298 return 0;
0299 }
0300
0301 static void ar7_wdt_shutdown(struct platform_device *pdev)
0302 {
0303 if (!nowayout)
0304 ar7_wdt_disable_wdt();
0305 }
0306
0307 static struct platform_driver ar7_wdt_driver = {
0308 .probe = ar7_wdt_probe,
0309 .remove = ar7_wdt_remove,
0310 .shutdown = ar7_wdt_shutdown,
0311 .driver = {
0312 .name = "ar7_wdt",
0313 },
0314 };
0315
0316 module_platform_driver(ar7_wdt_driver);