Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  *  Watchdog driver for the SA11x0/PXA2xx
0004  *
0005  *  (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
0006  *      Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
0007  *
0008  *  Neither Oleg Drokin nor iXcelerator.com admit liability nor provide
0009  *  warranty for any of this software. This material is provided
0010  *  "AS-IS" and at no charge.
0011  *
0012  *  (c) Copyright 2000           Oleg Drokin <green@crimea.edu>
0013  *
0014  *  27/11/2000 Initial release
0015  */
0016 
0017 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0018 
0019 #include <linux/module.h>
0020 #include <linux/moduleparam.h>
0021 #include <linux/clk.h>
0022 #include <linux/types.h>
0023 #include <linux/kernel.h>
0024 #include <linux/fs.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/miscdevice.h>
0027 #include <linux/watchdog.h>
0028 #include <linux/init.h>
0029 #include <linux/io.h>
0030 #include <linux/bitops.h>
0031 #include <linux/uaccess.h>
0032 #include <linux/timex.h>
0033 
0034 #define REG_OSMR0   0x0000  /* OS timer Match Reg. 0 */
0035 #define REG_OSMR1   0x0004  /* OS timer Match Reg. 1 */
0036 #define REG_OSMR2   0x0008  /* OS timer Match Reg. 2 */
0037 #define REG_OSMR3   0x000c  /* OS timer Match Reg. 3 */
0038 #define REG_OSCR    0x0010  /* OS timer Counter Reg. */
0039 #define REG_OSSR    0x0014  /* OS timer Status Reg. */
0040 #define REG_OWER    0x0018  /* OS timer Watch-dog Enable Reg. */
0041 #define REG_OIER    0x001C  /* OS timer Interrupt Enable Reg. */
0042 
0043 #define OSSR_M3     (1 << 3)    /* Match status channel 3 */
0044 #define OSSR_M2     (1 << 2)    /* Match status channel 2 */
0045 #define OSSR_M1     (1 << 1)    /* Match status channel 1 */
0046 #define OSSR_M0     (1 << 0)    /* Match status channel 0 */
0047 
0048 #define OWER_WME    (1 << 0)    /* Watchdog Match Enable */
0049 
0050 #define OIER_E3     (1 << 3)    /* Interrupt enable channel 3 */
0051 #define OIER_E2     (1 << 2)    /* Interrupt enable channel 2 */
0052 #define OIER_E1     (1 << 1)    /* Interrupt enable channel 1 */
0053 #define OIER_E0     (1 << 0)    /* Interrupt enable channel 0 */
0054 
0055 static unsigned long oscr_freq;
0056 static unsigned long sa1100wdt_users;
0057 static unsigned int pre_margin;
0058 static int boot_status;
0059 static void __iomem *reg_base;
0060 
0061 static inline void sa1100_wr(u32 val, u32 offset)
0062 {
0063     writel_relaxed(val, reg_base + offset);
0064 }
0065 
0066 static inline u32 sa1100_rd(u32 offset)
0067 {
0068     return readl_relaxed(reg_base + offset);
0069 }
0070 
0071 /*
0072  *  Allow only one person to hold it open
0073  */
0074 static int sa1100dog_open(struct inode *inode, struct file *file)
0075 {
0076     if (test_and_set_bit(1, &sa1100wdt_users))
0077         return -EBUSY;
0078 
0079     /* Activate SA1100 Watchdog timer */
0080     sa1100_wr(sa1100_rd(REG_OSCR) + pre_margin, REG_OSMR3);
0081     sa1100_wr(OSSR_M3, REG_OSSR);
0082     sa1100_wr(OWER_WME, REG_OWER);
0083     sa1100_wr(sa1100_rd(REG_OIER) | OIER_E3, REG_OIER);
0084     return stream_open(inode, file);
0085 }
0086 
0087 /*
0088  * The watchdog cannot be disabled.
0089  *
0090  * Previous comments suggested that turning off the interrupt by
0091  * clearing REG_OIER[E3] would prevent the watchdog timing out but this
0092  * does not appear to be true (at least on the PXA255).
0093  */
0094 static int sa1100dog_release(struct inode *inode, struct file *file)
0095 {
0096     pr_crit("Device closed - timer will not stop\n");
0097     clear_bit(1, &sa1100wdt_users);
0098     return 0;
0099 }
0100 
0101 static ssize_t sa1100dog_write(struct file *file, const char __user *data,
0102                         size_t len, loff_t *ppos)
0103 {
0104     if (len)
0105         /* Refresh OSMR3 timer. */
0106         sa1100_wr(sa1100_rd(REG_OSCR) + pre_margin, REG_OSMR3);
0107     return len;
0108 }
0109 
0110 static const struct watchdog_info ident = {
0111     .options    = WDIOF_CARDRESET | WDIOF_SETTIMEOUT
0112                 | WDIOF_KEEPALIVEPING,
0113     .identity   = "SA1100/PXA255 Watchdog",
0114     .firmware_version   = 1,
0115 };
0116 
0117 static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
0118                             unsigned long arg)
0119 {
0120     int ret = -ENOTTY;
0121     int time;
0122     void __user *argp = (void __user *)arg;
0123     int __user *p = argp;
0124 
0125     switch (cmd) {
0126     case WDIOC_GETSUPPORT:
0127         ret = copy_to_user(argp, &ident,
0128                    sizeof(ident)) ? -EFAULT : 0;
0129         break;
0130 
0131     case WDIOC_GETSTATUS:
0132         ret = put_user(0, p);
0133         break;
0134 
0135     case WDIOC_GETBOOTSTATUS:
0136         ret = put_user(boot_status, p);
0137         break;
0138 
0139     case WDIOC_KEEPALIVE:
0140         sa1100_wr(sa1100_rd(REG_OSCR) + pre_margin, REG_OSMR3);
0141         ret = 0;
0142         break;
0143 
0144     case WDIOC_SETTIMEOUT:
0145         ret = get_user(time, p);
0146         if (ret)
0147             break;
0148 
0149         if (time <= 0 || (oscr_freq * (long long)time >= 0xffffffff)) {
0150             ret = -EINVAL;
0151             break;
0152         }
0153 
0154         pre_margin = oscr_freq * time;
0155         sa1100_wr(sa1100_rd(REG_OSCR) + pre_margin, REG_OSMR3);
0156         fallthrough;
0157 
0158     case WDIOC_GETTIMEOUT:
0159         ret = put_user(pre_margin / oscr_freq, p);
0160         break;
0161     }
0162     return ret;
0163 }
0164 
0165 static const struct file_operations sa1100dog_fops = {
0166     .owner      = THIS_MODULE,
0167     .llseek     = no_llseek,
0168     .write      = sa1100dog_write,
0169     .unlocked_ioctl = sa1100dog_ioctl,
0170     .compat_ioctl   = compat_ptr_ioctl,
0171     .open       = sa1100dog_open,
0172     .release    = sa1100dog_release,
0173 };
0174 
0175 static struct miscdevice sa1100dog_miscdev = {
0176     .minor      = WATCHDOG_MINOR,
0177     .name       = "watchdog",
0178     .fops       = &sa1100dog_fops,
0179 };
0180 
0181 static int margin = 60;     /* (secs) Default is 1 minute */
0182 static struct clk *clk;
0183 
0184 static int sa1100dog_probe(struct platform_device *pdev)
0185 {
0186     int ret;
0187     int *platform_data;
0188     struct resource *res;
0189 
0190     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0191     if (!res)
0192         return -ENXIO;
0193     reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
0194     ret = PTR_ERR_OR_ZERO(reg_base);
0195     if (ret)
0196         return ret;
0197 
0198     clk = clk_get(NULL, "OSTIMER0");
0199     if (IS_ERR(clk)) {
0200         pr_err("SA1100/PXA2xx Watchdog Timer: clock not found: %d\n",
0201                (int) PTR_ERR(clk));
0202         return PTR_ERR(clk);
0203     }
0204 
0205     ret = clk_prepare_enable(clk);
0206     if (ret) {
0207         pr_err("SA1100/PXA2xx Watchdog Timer: clock failed to prepare+enable: %d\n",
0208                ret);
0209         goto err;
0210     }
0211 
0212     oscr_freq = clk_get_rate(clk);
0213 
0214     platform_data = pdev->dev.platform_data;
0215     if (platform_data && *platform_data)
0216         boot_status = WDIOF_CARDRESET;
0217     pre_margin = oscr_freq * margin;
0218 
0219     ret = misc_register(&sa1100dog_miscdev);
0220     if (ret == 0) {
0221         pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
0222             margin);
0223         return 0;
0224     }
0225 
0226     clk_disable_unprepare(clk);
0227 err:
0228     clk_put(clk);
0229     return ret;
0230 }
0231 
0232 static int sa1100dog_remove(struct platform_device *pdev)
0233 {
0234     misc_deregister(&sa1100dog_miscdev);
0235     clk_disable_unprepare(clk);
0236     clk_put(clk);
0237 
0238     return 0;
0239 }
0240 
0241 struct platform_driver sa1100dog_driver = {
0242     .driver.name = "sa1100_wdt",
0243     .probe    = sa1100dog_probe,
0244     .remove   = sa1100dog_remove,
0245 };
0246 module_platform_driver(sa1100dog_driver);
0247 
0248 MODULE_AUTHOR("Oleg Drokin <green@crimea.edu>");
0249 MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog");
0250 
0251 module_param(margin, int, 0);
0252 MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
0253 
0254 MODULE_LICENSE("GPL");