Commit | Line | Data |
---|---|---|
2abcf87a HS |
1 | /* |
2 | * COMEDI driver for the watchdog subdevice found on some addi-data boards | |
3 | * Copyright (c) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> | |
4 | * | |
5 | * Based on implementations in various addi-data COMEDI drivers. | |
6 | * | |
7 | * COMEDI - Linux Control and Measurement Device Interface | |
8 | * Copyright (C) 1998 David A. Schleef <ds@schleef.org> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | */ | |
24 | ||
25 | #include "../comedidev.h" | |
26 | #include "addi_watchdog.h" | |
27 | ||
28 | /* | |
29 | * Register offsets/defines for the addi-data watchdog | |
30 | */ | |
31 | #define ADDI_WDOG_REG 0x00 | |
32 | #define ADDI_WDOG_RELOAD_REG 0x04 | |
33 | #define ADDI_WDOG_TIMEBASE 0x08 | |
34 | #define ADDI_WDOG_CTRL_REG 0x0c | |
35 | #define ADDI_WDOG_CTRL_ENABLE (1 << 0) | |
36 | #define ADDI_WDOG_CTRL_SW_TRIG (1 << 9) | |
37 | #define ADDI_WDOG_STATUS_REG 0x10 | |
38 | #define ADDI_WDOG_STATUS_ENABLED (1 << 0) | |
39 | #define ADDI_WDOG_STATUS_SW_TRIG (1 << 1) | |
40 | ||
41 | struct addi_watchdog_private { | |
42 | unsigned long iobase; | |
43 | unsigned int wdog_ctrl; | |
44 | }; | |
45 | ||
46 | /* | |
47 | * The watchdog subdevice is configured with two INSN_CONFIG instructions: | |
48 | * | |
49 | * Enable the watchdog and set the reload timeout: | |
50 | * data[0] = INSN_CONFIG_ARM | |
51 | * data[1] = timeout reload value | |
52 | * | |
53 | * Disable the watchdog: | |
54 | * data[0] = INSN_CONFIG_DISARM | |
55 | */ | |
56 | static int addi_watchdog_insn_config(struct comedi_device *dev, | |
57 | struct comedi_subdevice *s, | |
58 | struct comedi_insn *insn, | |
59 | unsigned int *data) | |
60 | { | |
61 | struct addi_watchdog_private *spriv = s->private; | |
62 | unsigned int reload; | |
63 | ||
64 | switch (data[0]) { | |
65 | case INSN_CONFIG_ARM: | |
66 | spriv->wdog_ctrl = ADDI_WDOG_CTRL_ENABLE; | |
67 | reload = data[1] & s->maxdata; | |
c0cd2da1 | 68 | outl(reload, spriv->iobase + ADDI_WDOG_RELOAD_REG); |
2abcf87a HS |
69 | |
70 | /* Time base is 20ms, let the user know the timeout */ | |
71 | dev_info(dev->class_dev, "watchdog enabled, timeout:%dms\n", | |
72 | 20 * reload + 20); | |
73 | break; | |
74 | case INSN_CONFIG_DISARM: | |
75 | spriv->wdog_ctrl = 0; | |
76 | break; | |
77 | default: | |
78 | return -EINVAL; | |
79 | } | |
80 | ||
c0cd2da1 | 81 | outl(spriv->wdog_ctrl, spriv->iobase + ADDI_WDOG_CTRL_REG); |
2abcf87a HS |
82 | |
83 | return insn->n; | |
84 | } | |
85 | ||
86 | static int addi_watchdog_insn_read(struct comedi_device *dev, | |
87 | struct comedi_subdevice *s, | |
88 | struct comedi_insn *insn, | |
89 | unsigned int *data) | |
90 | { | |
91 | struct addi_watchdog_private *spriv = s->private; | |
92 | int i; | |
93 | ||
94 | for (i = 0; i < insn->n; i++) | |
95 | data[i] = inl(spriv->iobase + ADDI_WDOG_STATUS_REG); | |
96 | ||
97 | return insn->n; | |
98 | } | |
99 | ||
100 | static int addi_watchdog_insn_write(struct comedi_device *dev, | |
101 | struct comedi_subdevice *s, | |
102 | struct comedi_insn *insn, | |
103 | unsigned int *data) | |
104 | { | |
105 | struct addi_watchdog_private *spriv = s->private; | |
106 | int i; | |
107 | ||
108 | if (spriv->wdog_ctrl == 0) { | |
109 | dev_warn(dev->class_dev, "watchdog is disabled\n"); | |
110 | return -EINVAL; | |
111 | } | |
112 | ||
113 | /* "ping" the watchdog */ | |
114 | for (i = 0; i < insn->n; i++) { | |
c0cd2da1 | 115 | outl(spriv->wdog_ctrl | ADDI_WDOG_CTRL_SW_TRIG, |
2abcf87a HS |
116 | spriv->iobase + ADDI_WDOG_CTRL_REG); |
117 | } | |
118 | ||
119 | return insn->n; | |
120 | } | |
121 | ||
122 | void addi_watchdog_reset(unsigned long iobase) | |
123 | { | |
124 | outl(0x0, iobase + ADDI_WDOG_CTRL_REG); | |
125 | outl(0x0, iobase + ADDI_WDOG_RELOAD_REG); | |
126 | } | |
127 | EXPORT_SYMBOL_GPL(addi_watchdog_reset); | |
128 | ||
129 | int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase) | |
130 | { | |
131 | struct addi_watchdog_private *spriv; | |
132 | ||
133 | spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); | |
134 | if (!spriv) | |
135 | return -ENOMEM; | |
136 | ||
137 | spriv->iobase = iobase; | |
138 | ||
139 | s->private = spriv; | |
140 | ||
141 | s->type = COMEDI_SUBD_TIMER; | |
142 | s->subdev_flags = SDF_WRITEABLE; | |
143 | s->n_chan = 1; | |
144 | s->maxdata = 0xff; | |
145 | s->insn_config = addi_watchdog_insn_config; | |
146 | s->insn_read = addi_watchdog_insn_read; | |
147 | s->insn_write = addi_watchdog_insn_write; | |
148 | ||
149 | return 0; | |
150 | } | |
151 | EXPORT_SYMBOL_GPL(addi_watchdog_init); | |
152 | ||
2abcf87a HS |
153 | static int __init addi_watchdog_module_init(void) |
154 | { | |
155 | return 0; | |
156 | } | |
157 | module_init(addi_watchdog_module_init); | |
158 | ||
159 | static void __exit addi_watchdog_module_exit(void) | |
160 | { | |
161 | } | |
162 | module_exit(addi_watchdog_module_exit); | |
163 | ||
164 | MODULE_DESCRIPTION("ADDI-DATA Watchdog subdevice"); | |
165 | MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); | |
166 | MODULE_LICENSE("GPL"); |