0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/moduleparam.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/io.h>
0013 #include <linux/kernel.h>
0014 #include <linux/types.h>
0015 #include <linux/watchdog.h>
0016 #include <linux/seq_file.h>
0017 #include <linux/debugfs.h>
0018 #include <linux/uaccess.h>
0019 #include <linux/spinlock.h>
0020
0021 #define DRIVER_NAME "ie6xx_wdt"
0022
0023 #define PV1 0x00
0024 #define PV2 0x04
0025
0026 #define RR0 0x0c
0027 #define RR1 0x0d
0028 #define WDT_RELOAD 0x01
0029 #define WDT_TOUT 0x02
0030
0031 #define WDTCR 0x10
0032 #define WDT_PRE_SEL 0x04
0033 #define WDT_RESET_SEL 0x08
0034 #define WDT_RESET_EN 0x10
0035 #define WDT_TOUT_EN 0x20
0036
0037 #define DCR 0x14
0038
0039 #define WDTLR 0x18
0040 #define WDT_LOCK 0x01
0041 #define WDT_ENABLE 0x02
0042 #define WDT_TOUT_CNF 0x03
0043
0044 #define MIN_TIME 1
0045 #define MAX_TIME (10 * 60)
0046 #define DEFAULT_TIME 60
0047
0048 static unsigned int timeout = DEFAULT_TIME;
0049 module_param(timeout, uint, 0);
0050 MODULE_PARM_DESC(timeout,
0051 "Default Watchdog timer setting ("
0052 __MODULE_STRING(DEFAULT_TIME) "s)."
0053 "The range is from 1 to 600");
0054
0055 static bool nowayout = WATCHDOG_NOWAYOUT;
0056 module_param(nowayout, bool, 0);
0057 MODULE_PARM_DESC(nowayout,
0058 "Watchdog cannot be stopped once started (default="
0059 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0060
0061 static u8 resetmode = 0x10;
0062 module_param(resetmode, byte, 0);
0063 MODULE_PARM_DESC(resetmode,
0064 "Resetmode bits: 0x08 warm reset (cold reset otherwise), "
0065 "0x10 reset enable, 0x20 disable toggle GPIO[4] (default=0x10)");
0066
0067 static struct {
0068 unsigned short sch_wdtba;
0069 spinlock_t unlock_sequence;
0070 #ifdef CONFIG_DEBUG_FS
0071 struct dentry *debugfs;
0072 #endif
0073 } ie6xx_wdt_data;
0074
0075
0076
0077
0078
0079
0080 static void ie6xx_wdt_unlock_registers(void)
0081 {
0082 outb(0x80, ie6xx_wdt_data.sch_wdtba + RR0);
0083 outb(0x86, ie6xx_wdt_data.sch_wdtba + RR0);
0084 }
0085
0086 static int ie6xx_wdt_ping(struct watchdog_device *wdd)
0087 {
0088 spin_lock(&ie6xx_wdt_data.unlock_sequence);
0089 ie6xx_wdt_unlock_registers();
0090 outb(WDT_RELOAD, ie6xx_wdt_data.sch_wdtba + RR1);
0091 spin_unlock(&ie6xx_wdt_data.unlock_sequence);
0092 return 0;
0093 }
0094
0095 static int ie6xx_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
0096 {
0097 u32 preload;
0098 u64 clock;
0099 u8 wdtcr;
0100
0101
0102 clock = 33000000;
0103
0104 preload = (t * clock) >> 15;
0105
0106
0107
0108
0109 preload -= 1;
0110
0111 spin_lock(&ie6xx_wdt_data.unlock_sequence);
0112
0113
0114 wdtcr = resetmode & 0x38;
0115 outb(wdtcr, ie6xx_wdt_data.sch_wdtba + WDTCR);
0116
0117 ie6xx_wdt_unlock_registers();
0118 outl(0, ie6xx_wdt_data.sch_wdtba + PV1);
0119
0120 ie6xx_wdt_unlock_registers();
0121 outl(preload, ie6xx_wdt_data.sch_wdtba + PV2);
0122
0123 ie6xx_wdt_unlock_registers();
0124 outb(WDT_RELOAD | WDT_TOUT, ie6xx_wdt_data.sch_wdtba + RR1);
0125
0126 spin_unlock(&ie6xx_wdt_data.unlock_sequence);
0127
0128 wdd->timeout = t;
0129 return 0;
0130 }
0131
0132 static int ie6xx_wdt_start(struct watchdog_device *wdd)
0133 {
0134 ie6xx_wdt_set_timeout(wdd, wdd->timeout);
0135
0136
0137 spin_lock(&ie6xx_wdt_data.unlock_sequence);
0138 outb(WDT_ENABLE, ie6xx_wdt_data.sch_wdtba + WDTLR);
0139 spin_unlock(&ie6xx_wdt_data.unlock_sequence);
0140
0141 return 0;
0142 }
0143
0144 static int ie6xx_wdt_stop(struct watchdog_device *wdd)
0145 {
0146 if (inb(ie6xx_wdt_data.sch_wdtba + WDTLR) & WDT_LOCK)
0147 return -1;
0148
0149
0150 spin_lock(&ie6xx_wdt_data.unlock_sequence);
0151 outb(0, ie6xx_wdt_data.sch_wdtba + WDTLR);
0152 spin_unlock(&ie6xx_wdt_data.unlock_sequence);
0153
0154 return 0;
0155 }
0156
0157 static const struct watchdog_info ie6xx_wdt_info = {
0158 .identity = "Intel Atom E6xx Watchdog",
0159 .options = WDIOF_SETTIMEOUT |
0160 WDIOF_MAGICCLOSE |
0161 WDIOF_KEEPALIVEPING,
0162 };
0163
0164 static const struct watchdog_ops ie6xx_wdt_ops = {
0165 .owner = THIS_MODULE,
0166 .start = ie6xx_wdt_start,
0167 .stop = ie6xx_wdt_stop,
0168 .ping = ie6xx_wdt_ping,
0169 .set_timeout = ie6xx_wdt_set_timeout,
0170 };
0171
0172 static struct watchdog_device ie6xx_wdt_dev = {
0173 .info = &ie6xx_wdt_info,
0174 .ops = &ie6xx_wdt_ops,
0175 .min_timeout = MIN_TIME,
0176 .max_timeout = MAX_TIME,
0177 };
0178
0179 #ifdef CONFIG_DEBUG_FS
0180
0181 static int ie6xx_wdt_show(struct seq_file *s, void *unused)
0182 {
0183 seq_printf(s, "PV1 = 0x%08x\n",
0184 inl(ie6xx_wdt_data.sch_wdtba + PV1));
0185 seq_printf(s, "PV2 = 0x%08x\n",
0186 inl(ie6xx_wdt_data.sch_wdtba + PV2));
0187 seq_printf(s, "RR = 0x%08x\n",
0188 inw(ie6xx_wdt_data.sch_wdtba + RR0));
0189 seq_printf(s, "WDTCR = 0x%08x\n",
0190 inw(ie6xx_wdt_data.sch_wdtba + WDTCR));
0191 seq_printf(s, "DCR = 0x%08x\n",
0192 inl(ie6xx_wdt_data.sch_wdtba + DCR));
0193 seq_printf(s, "WDTLR = 0x%08x\n",
0194 inw(ie6xx_wdt_data.sch_wdtba + WDTLR));
0195
0196 seq_printf(s, "\n");
0197 return 0;
0198 }
0199
0200 DEFINE_SHOW_ATTRIBUTE(ie6xx_wdt);
0201
0202 static void ie6xx_wdt_debugfs_init(void)
0203 {
0204
0205 ie6xx_wdt_data.debugfs = debugfs_create_file("ie6xx_wdt",
0206 S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_fops);
0207 }
0208
0209 static void ie6xx_wdt_debugfs_exit(void)
0210 {
0211 debugfs_remove(ie6xx_wdt_data.debugfs);
0212 }
0213
0214 #else
0215 static void ie6xx_wdt_debugfs_init(void)
0216 {
0217 }
0218
0219 static void ie6xx_wdt_debugfs_exit(void)
0220 {
0221 }
0222 #endif
0223
0224 static int ie6xx_wdt_probe(struct platform_device *pdev)
0225 {
0226 struct resource *res;
0227 u8 wdtlr;
0228 int ret;
0229
0230 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
0231 if (!res)
0232 return -ENODEV;
0233
0234 if (!request_region(res->start, resource_size(res), pdev->name)) {
0235 dev_err(&pdev->dev, "Watchdog region 0x%llx already in use!\n",
0236 (u64)res->start);
0237 return -EBUSY;
0238 }
0239
0240 ie6xx_wdt_data.sch_wdtba = res->start;
0241 dev_dbg(&pdev->dev, "WDT = 0x%X\n", ie6xx_wdt_data.sch_wdtba);
0242
0243 ie6xx_wdt_dev.timeout = timeout;
0244 watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
0245 ie6xx_wdt_dev.parent = &pdev->dev;
0246
0247 spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
0248
0249 wdtlr = inb(ie6xx_wdt_data.sch_wdtba + WDTLR);
0250 if (wdtlr & WDT_LOCK)
0251 dev_warn(&pdev->dev,
0252 "Watchdog Timer is Locked (Reg=0x%x)\n", wdtlr);
0253
0254 ie6xx_wdt_debugfs_init();
0255
0256 ret = watchdog_register_device(&ie6xx_wdt_dev);
0257 if (ret)
0258 goto misc_register_error;
0259
0260 return 0;
0261
0262 misc_register_error:
0263 ie6xx_wdt_debugfs_exit();
0264 release_region(res->start, resource_size(res));
0265 ie6xx_wdt_data.sch_wdtba = 0;
0266 return ret;
0267 }
0268
0269 static int ie6xx_wdt_remove(struct platform_device *pdev)
0270 {
0271 struct resource *res;
0272
0273 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
0274 ie6xx_wdt_stop(NULL);
0275 watchdog_unregister_device(&ie6xx_wdt_dev);
0276 ie6xx_wdt_debugfs_exit();
0277 release_region(res->start, resource_size(res));
0278 ie6xx_wdt_data.sch_wdtba = 0;
0279
0280 return 0;
0281 }
0282
0283 static struct platform_driver ie6xx_wdt_driver = {
0284 .probe = ie6xx_wdt_probe,
0285 .remove = ie6xx_wdt_remove,
0286 .driver = {
0287 .name = DRIVER_NAME,
0288 },
0289 };
0290
0291 static int __init ie6xx_wdt_init(void)
0292 {
0293
0294
0295 if ((timeout < MIN_TIME) ||
0296 (timeout > MAX_TIME)) {
0297 pr_err("Watchdog timer: value of timeout %d (dec) "
0298 "is out of range from %d to %d (dec)\n",
0299 timeout, MIN_TIME, MAX_TIME);
0300 return -EINVAL;
0301 }
0302
0303 return platform_driver_register(&ie6xx_wdt_driver);
0304 }
0305
0306 static void __exit ie6xx_wdt_exit(void)
0307 {
0308 platform_driver_unregister(&ie6xx_wdt_driver);
0309 }
0310
0311 late_initcall(ie6xx_wdt_init);
0312 module_exit(ie6xx_wdt_exit);
0313
0314 MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
0315 MODULE_DESCRIPTION("Intel Atom E6xx Watchdog Device Driver");
0316 MODULE_LICENSE("GPL");
0317 MODULE_ALIAS("platform:" DRIVER_NAME);