xref: /linux/drivers/watchdog/lenovo_se30_wdt.c (revision bb1556ec94647060c6b52bf434b9fd824724a6f4)
1c284153aSMark Pearson // SPDX-License-Identifier: GPL-2.0-or-later
2c284153aSMark Pearson /*
3c284153aSMark Pearson  * WDT driver for Lenovo SE30 device
4c284153aSMark Pearson  */
5c284153aSMark Pearson 
6c284153aSMark Pearson #define dev_fmt(fmt) KBUILD_MODNAME ": " fmt
7c284153aSMark Pearson 
80ccd5d56SStephen Rothwell #include <linux/io.h>
9c284153aSMark Pearson #include <linux/dmi.h>
10c284153aSMark Pearson #include <linux/delay.h>
11c284153aSMark Pearson #include <linux/iommu.h>
12c284153aSMark Pearson #include <linux/kernel.h>
13c284153aSMark Pearson #include <linux/module.h>
14c284153aSMark Pearson #include <linux/moduleparam.h>
15c284153aSMark Pearson #include <linux/platform_device.h>
16c284153aSMark Pearson #include <linux/watchdog.h>
17c284153aSMark Pearson 
18c284153aSMark Pearson #define IOREGION_OFFSET	4 /* Use EC port 1 */
19c284153aSMark Pearson #define IOREGION_LENGTH	4
20c284153aSMark Pearson 
21c284153aSMark Pearson #define WATCHDOG_TIMEOUT	60
22c284153aSMark Pearson 
23c284153aSMark Pearson #define MIN_TIMEOUT	1
24c284153aSMark Pearson #define MAX_TIMEOUT	255
25c284153aSMark Pearson #define MAX_WAIT	10
26c284153aSMark Pearson 
27c284153aSMark Pearson static int timeout; /* in seconds */
28c284153aSMark Pearson module_param(timeout, int, 0);
29c284153aSMark Pearson MODULE_PARM_DESC(timeout,
30c284153aSMark Pearson 		 "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
31c284153aSMark Pearson 		 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
32c284153aSMark Pearson 
33c284153aSMark Pearson static bool nowayout = WATCHDOG_NOWAYOUT;
34c284153aSMark Pearson module_param(nowayout, bool, 0);
35c284153aSMark Pearson MODULE_PARM_DESC(nowayout,
36c284153aSMark Pearson 		 "Watchdog cannot be stopped once started (default="
37c284153aSMark Pearson 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
38c284153aSMark Pearson 
39c284153aSMark Pearson #define LNV_SE30_NAME	"lenovo-se30-wdt"
40c284153aSMark Pearson #define LNV_SE30_ID	0x0110
41c284153aSMark Pearson #define CHIPID_MASK	0xFFF0
42c284153aSMark Pearson 
43c284153aSMark Pearson #define CHIPID_REG	0x20
44c284153aSMark Pearson #define SIO_REG		0x2e
45c284153aSMark Pearson #define LDN_REG		0x07
46c284153aSMark Pearson #define UNLOCK_KEY	0x87
47c284153aSMark Pearson #define LOCK_KEY	0xAA
48c284153aSMark Pearson #define LD_NUM_SHM	0x0F
49c284153aSMark Pearson #define LD_BASE_ADDR	0xF8
50c284153aSMark Pearson 
51c284153aSMark Pearson #define WDT_MODULE	0x10
52c284153aSMark Pearson #define WDT_CFG_INDEX	0x15 /* WD configuration register */
53c284153aSMark Pearson #define WDT_CNT_INDEX	0x16 /* WD timer count register */
54c284153aSMark Pearson #define WDT_CFG_RESET	0x2
55c284153aSMark Pearson 
56c284153aSMark Pearson /* Host Interface WIN2 offset definition */
57c284153aSMark Pearson #define SHM_WIN_SIZE		0xFF
58c284153aSMark Pearson #define SHM_WIN_MOD_OFFSET	0x01
59c284153aSMark Pearson #define SHM_WIN_CMD_OFFSET	0x02
60c284153aSMark Pearson #define SHM_WIN_SEL_OFFSET	0x03
61c284153aSMark Pearson #define SHM_WIN_CTL_OFFSET	0x04
62c284153aSMark Pearson #define VAL_SHM_WIN_CTRL_WR	0x40
63c284153aSMark Pearson #define VAL_SHM_WIN_CTRL_RD	0x80
64c284153aSMark Pearson #define SHM_WIN_ID_OFFSET	0x08
65c284153aSMark Pearson #define SHM_WIN_DAT_OFFSET	0x10
66c284153aSMark Pearson 
67c284153aSMark Pearson struct nct6692_reg {
68c284153aSMark Pearson 	unsigned char mod;
69c284153aSMark Pearson 	unsigned char cmd;
70c284153aSMark Pearson 	unsigned char sel;
71c284153aSMark Pearson 	unsigned int idx;
72c284153aSMark Pearson };
73c284153aSMark Pearson 
74c284153aSMark Pearson /* Watchdog is based on NCT6692 device */
75c284153aSMark Pearson struct lenovo_se30_wdt {
76c284153aSMark Pearson 	unsigned char __iomem *shm_base_addr;
77c284153aSMark Pearson 	struct nct6692_reg wdt_cfg;
78c284153aSMark Pearson 	struct nct6692_reg wdt_cnt;
79c284153aSMark Pearson 	struct watchdog_device wdt;
80c284153aSMark Pearson };
81c284153aSMark Pearson 
superio_outb(int ioreg,int reg,int val)82c284153aSMark Pearson static inline void superio_outb(int ioreg, int reg, int val)
83c284153aSMark Pearson {
84c284153aSMark Pearson 	outb(reg, ioreg);
85c284153aSMark Pearson 	outb(val, ioreg + 1);
86c284153aSMark Pearson }
87c284153aSMark Pearson 
superio_inb(int ioreg,int reg)88c284153aSMark Pearson static inline int superio_inb(int ioreg, int reg)
89c284153aSMark Pearson {
90c284153aSMark Pearson 	outb(reg, ioreg);
91c284153aSMark Pearson 	return inb(ioreg + 1);
92c284153aSMark Pearson }
93c284153aSMark Pearson 
superio_enter(int key,int addr,const char * name)94c284153aSMark Pearson static inline int superio_enter(int key, int addr, const char *name)
95c284153aSMark Pearson {
96c284153aSMark Pearson 	if (!request_muxed_region(addr, 2, name)) {
97c284153aSMark Pearson 		pr_err("I/O address 0x%04x already in use\n", addr);
98c284153aSMark Pearson 		return -EBUSY;
99c284153aSMark Pearson 	}
100c284153aSMark Pearson 	outb(key, addr); /* Enter extended function mode */
101c284153aSMark Pearson 	outb(key, addr); /* Again according to manual */
102c284153aSMark Pearson 
103c284153aSMark Pearson 	return 0;
104c284153aSMark Pearson }
105c284153aSMark Pearson 
superio_exit(int key,int addr)106c284153aSMark Pearson static inline void superio_exit(int key, int addr)
107c284153aSMark Pearson {
108c284153aSMark Pearson 	outb(key, addr); /* Leave extended function mode */
109c284153aSMark Pearson 	release_region(addr, 2);
110c284153aSMark Pearson }
111c284153aSMark Pearson 
shm_get_ready(unsigned char __iomem * shm_base_addr,const struct nct6692_reg * reg)112c284153aSMark Pearson static int shm_get_ready(unsigned char __iomem *shm_base_addr,
113c284153aSMark Pearson 			 const struct nct6692_reg *reg)
114c284153aSMark Pearson {
115c284153aSMark Pearson 	unsigned char pre_id, new_id;
116c284153aSMark Pearson 	int loop = 0;
117c284153aSMark Pearson 
118c284153aSMark Pearson 	iowrite8(reg->mod, shm_base_addr + SHM_WIN_MOD_OFFSET);
119c284153aSMark Pearson 	iowrite8(reg->cmd, shm_base_addr + SHM_WIN_CMD_OFFSET);
120c284153aSMark Pearson 	iowrite8(reg->sel, shm_base_addr + SHM_WIN_SEL_OFFSET);
121c284153aSMark Pearson 
122c284153aSMark Pearson 	pre_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET);
123c284153aSMark Pearson 	iowrite8(VAL_SHM_WIN_CTRL_RD, shm_base_addr + SHM_WIN_CTL_OFFSET);
124c284153aSMark Pearson 
125c284153aSMark Pearson 	/* Loop checking when interface is ready */
126c284153aSMark Pearson 	while (loop < MAX_WAIT) {
127c284153aSMark Pearson 		new_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET);
128c284153aSMark Pearson 		if (new_id != pre_id)
129c284153aSMark Pearson 			return 0;
130c284153aSMark Pearson 		loop++;
131c284153aSMark Pearson 		usleep_range(10, 125);
132c284153aSMark Pearson 	}
133c284153aSMark Pearson 	return -ETIMEDOUT;
134c284153aSMark Pearson }
135c284153aSMark Pearson 
read_shm_win(unsigned char __iomem * shm_base_addr,const struct nct6692_reg * reg,unsigned char idx_offset,unsigned char * data)136c284153aSMark Pearson static int read_shm_win(unsigned char __iomem *shm_base_addr,
137c284153aSMark Pearson 			const struct nct6692_reg *reg,
138c284153aSMark Pearson 			unsigned char idx_offset,
139c284153aSMark Pearson 			unsigned char *data)
140c284153aSMark Pearson {
141c284153aSMark Pearson 	int err = shm_get_ready(shm_base_addr, reg);
142c284153aSMark Pearson 
143c284153aSMark Pearson 	if (err)
144c284153aSMark Pearson 		return err;
145c284153aSMark Pearson 	*data = ioread8(shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset);
146c284153aSMark Pearson 	return 0;
147c284153aSMark Pearson }
148c284153aSMark Pearson 
write_shm_win(unsigned char __iomem * shm_base_addr,const struct nct6692_reg * reg,unsigned char idx_offset,unsigned char val)149c284153aSMark Pearson static int write_shm_win(unsigned char __iomem *shm_base_addr,
150c284153aSMark Pearson 			 const struct nct6692_reg *reg,
151c284153aSMark Pearson 			 unsigned char idx_offset,
152c284153aSMark Pearson 			 unsigned char val)
153c284153aSMark Pearson {
154c284153aSMark Pearson 	int err = shm_get_ready(shm_base_addr, reg);
155c284153aSMark Pearson 
156c284153aSMark Pearson 	if (err)
157c284153aSMark Pearson 		return err;
158c284153aSMark Pearson 	iowrite8(val, shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset);
159c284153aSMark Pearson 	iowrite8(VAL_SHM_WIN_CTRL_WR, shm_base_addr + SHM_WIN_CTL_OFFSET);
160c284153aSMark Pearson 	err = shm_get_ready(shm_base_addr, reg);
161c284153aSMark Pearson 	return err;
162c284153aSMark Pearson }
163c284153aSMark Pearson 
lenovo_se30_wdt_enable(struct lenovo_se30_wdt * data,unsigned int timeout)164c284153aSMark Pearson static int lenovo_se30_wdt_enable(struct lenovo_se30_wdt *data, unsigned int timeout)
165c284153aSMark Pearson {
166c284153aSMark Pearson 	if (timeout) {
167c284153aSMark Pearson 		int err = write_shm_win(data->shm_base_addr, &data->wdt_cfg, 0, WDT_CFG_RESET);
168c284153aSMark Pearson 
169c284153aSMark Pearson 		if (err)
170c284153aSMark Pearson 			return err;
171c284153aSMark Pearson 	}
172c284153aSMark Pearson 	return write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, timeout);
173c284153aSMark Pearson }
174c284153aSMark Pearson 
lenovo_se30_wdt_start(struct watchdog_device * wdog)175c284153aSMark Pearson static int lenovo_se30_wdt_start(struct watchdog_device *wdog)
176c284153aSMark Pearson {
177c284153aSMark Pearson 	struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog);
178c284153aSMark Pearson 
179c284153aSMark Pearson 	return lenovo_se30_wdt_enable(data, wdog->timeout);
180c284153aSMark Pearson }
181c284153aSMark Pearson 
lenovo_se30_wdt_stop(struct watchdog_device * wdog)182c284153aSMark Pearson static int lenovo_se30_wdt_stop(struct watchdog_device *wdog)
183c284153aSMark Pearson {
184c284153aSMark Pearson 	struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog);
185c284153aSMark Pearson 
186c284153aSMark Pearson 	return lenovo_se30_wdt_enable(data, 0);
187c284153aSMark Pearson }
188c284153aSMark Pearson 
lenovo_se30_wdt_get_timeleft(struct watchdog_device * wdog)189c284153aSMark Pearson static unsigned int lenovo_se30_wdt_get_timeleft(struct watchdog_device *wdog)
190c284153aSMark Pearson {
191c284153aSMark Pearson 	struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog);
192c284153aSMark Pearson 	unsigned char timeleft;
193c284153aSMark Pearson 	int err;
194c284153aSMark Pearson 
195c284153aSMark Pearson 	err = read_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, &timeleft);
196c284153aSMark Pearson 	if (err)
197c284153aSMark Pearson 		return 0;
198c284153aSMark Pearson 	return timeleft;
199c284153aSMark Pearson }
200c284153aSMark Pearson 
lenovo_se30_wdt_ping(struct watchdog_device * wdt)201c284153aSMark Pearson static int lenovo_se30_wdt_ping(struct watchdog_device *wdt)
202c284153aSMark Pearson {
203c284153aSMark Pearson 	struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdt);
204c284153aSMark Pearson 	int err = 0;
205c284153aSMark Pearson 
206c284153aSMark Pearson 	/*
207c284153aSMark Pearson 	 * Device does not support refreshing WDT_TIMER_REG register when
208c284153aSMark Pearson 	 * the watchdog is active.  Need to disable, feed and enable again
209c284153aSMark Pearson 	 */
210c284153aSMark Pearson 	err = lenovo_se30_wdt_enable(data, 0);
211c284153aSMark Pearson 	if (err)
212c284153aSMark Pearson 		return err;
213c284153aSMark Pearson 
214c284153aSMark Pearson 	err = write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, wdt->timeout);
215c284153aSMark Pearson 	if (!err)
216c284153aSMark Pearson 		err = lenovo_se30_wdt_enable(data, wdt->timeout);
217c284153aSMark Pearson 
218c284153aSMark Pearson 	return err;
219c284153aSMark Pearson }
220c284153aSMark Pearson 
221c284153aSMark Pearson static const struct watchdog_info lenovo_se30_wdt_info = {
222c284153aSMark Pearson 	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
223c284153aSMark Pearson 			  WDIOF_MAGICCLOSE,
224c284153aSMark Pearson 	.identity	= "Lenovo SE30 watchdog",
225c284153aSMark Pearson };
226c284153aSMark Pearson 
227c284153aSMark Pearson static const struct watchdog_ops lenovo_se30_wdt_ops = {
228c284153aSMark Pearson 	.owner		= THIS_MODULE,
229c284153aSMark Pearson 	.start		= lenovo_se30_wdt_start,
230c284153aSMark Pearson 	.stop		= lenovo_se30_wdt_stop,
231c284153aSMark Pearson 	.ping		= lenovo_se30_wdt_ping,
232c284153aSMark Pearson 	.get_timeleft	= lenovo_se30_wdt_get_timeleft,
233c284153aSMark Pearson };
234c284153aSMark Pearson 
lenovo_se30_wdt_probe(struct platform_device * pdev)235c284153aSMark Pearson static int lenovo_se30_wdt_probe(struct platform_device *pdev)
236c284153aSMark Pearson {
237c284153aSMark Pearson 	struct device *dev = &pdev->dev;
238c284153aSMark Pearson 	struct lenovo_se30_wdt *priv;
239c284153aSMark Pearson 	unsigned long base_phys;
240c284153aSMark Pearson 	unsigned short val;
241c284153aSMark Pearson 	int err;
242c284153aSMark Pearson 
243c284153aSMark Pearson 	err = superio_enter(UNLOCK_KEY, SIO_REG, LNV_SE30_NAME);
244c284153aSMark Pearson 	if (err)
245c284153aSMark Pearson 		return err;
246c284153aSMark Pearson 
247c284153aSMark Pearson 	val = superio_inb(SIO_REG, CHIPID_REG) << 8;
248c284153aSMark Pearson 	val |= superio_inb(SIO_REG, CHIPID_REG + 1);
249c284153aSMark Pearson 
250c284153aSMark Pearson 	if ((val & CHIPID_MASK) != LNV_SE30_ID) {
251c284153aSMark Pearson 		superio_exit(LOCK_KEY, SIO_REG);
252c284153aSMark Pearson 		return -ENODEV;
253c284153aSMark Pearson 	}
254c284153aSMark Pearson 
255c284153aSMark Pearson 	superio_outb(SIO_REG, LDN_REG, LD_NUM_SHM);
256c284153aSMark Pearson 	base_phys = (superio_inb(SIO_REG, LD_BASE_ADDR) |
257c284153aSMark Pearson 			 (superio_inb(SIO_REG, LD_BASE_ADDR + 1) << 8) |
258c284153aSMark Pearson 			 (superio_inb(SIO_REG, LD_BASE_ADDR + 2) << 16) |
259c284153aSMark Pearson 			 (superio_inb(SIO_REG, LD_BASE_ADDR + 3) << 24)) &
260c284153aSMark Pearson 			0xFFFFFFFF;
261c284153aSMark Pearson 
262c284153aSMark Pearson 	superio_exit(LOCK_KEY, SIO_REG);
263c284153aSMark Pearson 	if (base_phys == 0xFFFFFFFF || base_phys == 0)
264c284153aSMark Pearson 		return -ENODEV;
265c284153aSMark Pearson 
266c284153aSMark Pearson 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
267c284153aSMark Pearson 	if (!priv)
268c284153aSMark Pearson 		return -ENOMEM;
269c284153aSMark Pearson 
270c284153aSMark Pearson 	if (!devm_request_mem_region(dev, base_phys, SHM_WIN_SIZE, LNV_SE30_NAME))
271c284153aSMark Pearson 		return -EBUSY;
272c284153aSMark Pearson 
273c284153aSMark Pearson 	priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE);
274*a4e24014SHenry Martin 	if (!priv->shm_base_addr)
275*a4e24014SHenry Martin 		return -ENOMEM;
276c284153aSMark Pearson 
277c284153aSMark Pearson 	priv->wdt_cfg.mod = WDT_MODULE;
278c284153aSMark Pearson 	priv->wdt_cfg.idx = WDT_CFG_INDEX;
279c284153aSMark Pearson 	priv->wdt_cnt.mod = WDT_MODULE;
280c284153aSMark Pearson 	priv->wdt_cnt.idx = WDT_CNT_INDEX;
281c284153aSMark Pearson 
282c284153aSMark Pearson 	priv->wdt.ops = &lenovo_se30_wdt_ops;
283c284153aSMark Pearson 	priv->wdt.info = &lenovo_se30_wdt_info;
284c284153aSMark Pearson 	priv->wdt.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */
285c284153aSMark Pearson 	priv->wdt.min_timeout = MIN_TIMEOUT;
286c284153aSMark Pearson 	priv->wdt.max_timeout = MAX_TIMEOUT;
287c284153aSMark Pearson 	priv->wdt.parent = dev;
288c284153aSMark Pearson 
289c284153aSMark Pearson 	watchdog_init_timeout(&priv->wdt, timeout, dev);
290c284153aSMark Pearson 	watchdog_set_drvdata(&priv->wdt, priv);
291c284153aSMark Pearson 	watchdog_set_nowayout(&priv->wdt, nowayout);
292c284153aSMark Pearson 	watchdog_stop_on_reboot(&priv->wdt);
293c284153aSMark Pearson 	watchdog_stop_on_unregister(&priv->wdt);
294c284153aSMark Pearson 
295c284153aSMark Pearson 	return devm_watchdog_register_device(dev, &priv->wdt);
296c284153aSMark Pearson }
297c284153aSMark Pearson 
298c284153aSMark Pearson static struct platform_device *pdev;
299c284153aSMark Pearson 
300c284153aSMark Pearson static struct platform_driver lenovo_se30_wdt_driver = {
301c284153aSMark Pearson 	.driver = {
302c284153aSMark Pearson 		.name = LNV_SE30_NAME,
303c284153aSMark Pearson 	},
304c284153aSMark Pearson 	.probe  = lenovo_se30_wdt_probe,
305c284153aSMark Pearson };
306c284153aSMark Pearson 
lenovo_se30_create_platform_device(const struct dmi_system_id * id)307c284153aSMark Pearson static int lenovo_se30_create_platform_device(const struct dmi_system_id *id)
308c284153aSMark Pearson {
309c284153aSMark Pearson 	int err;
310c284153aSMark Pearson 
311c284153aSMark Pearson 	pdev = platform_device_alloc(LNV_SE30_NAME, -1);
312c284153aSMark Pearson 	if (!pdev)
313c284153aSMark Pearson 		return -ENOMEM;
314c284153aSMark Pearson 
315c284153aSMark Pearson 	err = platform_device_add(pdev);
316c284153aSMark Pearson 	if (err)
317c284153aSMark Pearson 		platform_device_put(pdev);
318c284153aSMark Pearson 
319c284153aSMark Pearson 	return err;
320c284153aSMark Pearson }
321c284153aSMark Pearson 
322c284153aSMark Pearson static const struct dmi_system_id lenovo_se30_wdt_dmi_table[] __initconst = {
323c284153aSMark Pearson 	{
324c284153aSMark Pearson 		.ident = "LENOVO-SE30",
325c284153aSMark Pearson 		.matches = {
326c284153aSMark Pearson 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
327c284153aSMark Pearson 			DMI_MATCH(DMI_PRODUCT_NAME, "11NA"),
328c284153aSMark Pearson 		},
329c284153aSMark Pearson 		.callback = lenovo_se30_create_platform_device,
330c284153aSMark Pearson 	},
331c284153aSMark Pearson 	{
332c284153aSMark Pearson 		.ident = "LENOVO-SE30",
333c284153aSMark Pearson 		.matches = {
334c284153aSMark Pearson 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
335c284153aSMark Pearson 			DMI_MATCH(DMI_PRODUCT_NAME, "11NB"),
336c284153aSMark Pearson 		},
337c284153aSMark Pearson 		.callback = lenovo_se30_create_platform_device,
338c284153aSMark Pearson 	},
339c284153aSMark Pearson 	{
340c284153aSMark Pearson 		.ident = "LENOVO-SE30",
341c284153aSMark Pearson 		.matches = {
342c284153aSMark Pearson 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
343c284153aSMark Pearson 			DMI_MATCH(DMI_PRODUCT_NAME, "11NC"),
344c284153aSMark Pearson 		},
345c284153aSMark Pearson 		.callback = lenovo_se30_create_platform_device,
346c284153aSMark Pearson 	},
347c284153aSMark Pearson 	{
348c284153aSMark Pearson 		.ident = "LENOVO-SE30",
349c284153aSMark Pearson 		.matches = {
350c284153aSMark Pearson 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
351c284153aSMark Pearson 			DMI_MATCH(DMI_PRODUCT_NAME, "11NH"),
352c284153aSMark Pearson 		},
353c284153aSMark Pearson 		.callback = lenovo_se30_create_platform_device,
354c284153aSMark Pearson 	},
355c284153aSMark Pearson 	{
356c284153aSMark Pearson 		.ident = "LENOVO-SE30",
357c284153aSMark Pearson 		.matches = {
358c284153aSMark Pearson 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
359c284153aSMark Pearson 			DMI_MATCH(DMI_PRODUCT_NAME, "11NJ"),
360c284153aSMark Pearson 		},
361c284153aSMark Pearson 		.callback = lenovo_se30_create_platform_device,
362c284153aSMark Pearson 	},
363c284153aSMark Pearson 	{
364c284153aSMark Pearson 		.ident = "LENOVO-SE30",
365c284153aSMark Pearson 		.matches = {
366c284153aSMark Pearson 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
367c284153aSMark Pearson 			DMI_MATCH(DMI_PRODUCT_NAME, "11NK"),
368c284153aSMark Pearson 		},
369c284153aSMark Pearson 		.callback = lenovo_se30_create_platform_device,
370c284153aSMark Pearson 	},
371c284153aSMark Pearson 	{}
372c284153aSMark Pearson };
373c284153aSMark Pearson MODULE_DEVICE_TABLE(dmi, lenovo_se30_wdt_dmi_table);
374c284153aSMark Pearson 
lenovo_se30_wdt_init(void)375c284153aSMark Pearson static int __init lenovo_se30_wdt_init(void)
376c284153aSMark Pearson {
377c284153aSMark Pearson 	if (!dmi_check_system(lenovo_se30_wdt_dmi_table))
378c284153aSMark Pearson 		return -ENODEV;
379c284153aSMark Pearson 
380c284153aSMark Pearson 	return platform_driver_register(&lenovo_se30_wdt_driver);
381c284153aSMark Pearson }
382c284153aSMark Pearson 
lenovo_se30_wdt_exit(void)383c284153aSMark Pearson static void __exit lenovo_se30_wdt_exit(void)
384c284153aSMark Pearson {
385c284153aSMark Pearson 	if (pdev)
386c284153aSMark Pearson 		platform_device_unregister(pdev);
387c284153aSMark Pearson 	platform_driver_unregister(&lenovo_se30_wdt_driver);
388c284153aSMark Pearson }
389c284153aSMark Pearson 
390c284153aSMark Pearson module_init(lenovo_se30_wdt_init);
391c284153aSMark Pearson module_exit(lenovo_se30_wdt_exit);
392c284153aSMark Pearson 
393c284153aSMark Pearson MODULE_AUTHOR("Mark Pearson <mpearson-lenovo@squebb.ca>");
394c284153aSMark Pearson MODULE_AUTHOR("David Ober <dober@lenovo.com>");
395c284153aSMark Pearson MODULE_DESCRIPTION("Lenovo SE30 watchdog driver");
396c284153aSMark Pearson MODULE_LICENSE("GPL");
397