1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * COMEDI driver for the watchdog subdevice found on some addi-data boards |
4 | * Copyright (c) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> |
5 | * |
6 | * Based on implementations in various addi-data COMEDI drivers. |
7 | * |
8 | * COMEDI - Linux Control and Measurement Device Interface |
9 | * Copyright (C) 1998 David A. Schleef <ds@schleef.org> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/comedi/comedidev.h> |
14 | #include "addi_tcw.h" |
15 | #include "addi_watchdog.h" |
16 | |
17 | struct addi_watchdog_private { |
18 | unsigned long iobase; |
19 | unsigned int wdog_ctrl; |
20 | }; |
21 | |
22 | /* |
23 | * The watchdog subdevice is configured with two INSN_CONFIG instructions: |
24 | * |
25 | * Enable the watchdog and set the reload timeout: |
26 | * data[0] = INSN_CONFIG_ARM |
27 | * data[1] = timeout reload value |
28 | * |
29 | * Disable the watchdog: |
30 | * data[0] = INSN_CONFIG_DISARM |
31 | */ |
32 | static int addi_watchdog_insn_config(struct comedi_device *dev, |
33 | struct comedi_subdevice *s, |
34 | struct comedi_insn *insn, |
35 | unsigned int *data) |
36 | { |
37 | struct addi_watchdog_private *spriv = s->private; |
38 | unsigned int reload; |
39 | |
40 | switch (data[0]) { |
41 | case INSN_CONFIG_ARM: |
42 | spriv->wdog_ctrl = ADDI_TCW_CTRL_ENA; |
43 | reload = data[1] & s->maxdata; |
44 | outl(value: reload, port: spriv->iobase + ADDI_TCW_RELOAD_REG); |
45 | |
46 | /* Time base is 20ms, let the user know the timeout */ |
47 | dev_info(dev->class_dev, "watchdog enabled, timeout:%dms\n" , |
48 | 20 * reload + 20); |
49 | break; |
50 | case INSN_CONFIG_DISARM: |
51 | spriv->wdog_ctrl = 0; |
52 | break; |
53 | default: |
54 | return -EINVAL; |
55 | } |
56 | |
57 | outl(value: spriv->wdog_ctrl, port: spriv->iobase + ADDI_TCW_CTRL_REG); |
58 | |
59 | return insn->n; |
60 | } |
61 | |
62 | static int addi_watchdog_insn_read(struct comedi_device *dev, |
63 | struct comedi_subdevice *s, |
64 | struct comedi_insn *insn, |
65 | unsigned int *data) |
66 | { |
67 | struct addi_watchdog_private *spriv = s->private; |
68 | int i; |
69 | |
70 | for (i = 0; i < insn->n; i++) |
71 | data[i] = inl(port: spriv->iobase + ADDI_TCW_STATUS_REG); |
72 | |
73 | return insn->n; |
74 | } |
75 | |
76 | static int addi_watchdog_insn_write(struct comedi_device *dev, |
77 | struct comedi_subdevice *s, |
78 | struct comedi_insn *insn, |
79 | unsigned int *data) |
80 | { |
81 | struct addi_watchdog_private *spriv = s->private; |
82 | int i; |
83 | |
84 | if (spriv->wdog_ctrl == 0) { |
85 | dev_warn(dev->class_dev, "watchdog is disabled\n" ); |
86 | return -EINVAL; |
87 | } |
88 | |
89 | /* "ping" the watchdog */ |
90 | for (i = 0; i < insn->n; i++) { |
91 | outl(value: spriv->wdog_ctrl | ADDI_TCW_CTRL_TRIG, |
92 | port: spriv->iobase + ADDI_TCW_CTRL_REG); |
93 | } |
94 | |
95 | return insn->n; |
96 | } |
97 | |
98 | void addi_watchdog_reset(unsigned long iobase) |
99 | { |
100 | outl(value: 0x0, port: iobase + ADDI_TCW_CTRL_REG); |
101 | outl(value: 0x0, port: iobase + ADDI_TCW_RELOAD_REG); |
102 | } |
103 | EXPORT_SYMBOL_GPL(addi_watchdog_reset); |
104 | |
105 | int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase) |
106 | { |
107 | struct addi_watchdog_private *spriv; |
108 | |
109 | spriv = comedi_alloc_spriv(s, size: sizeof(*spriv)); |
110 | if (!spriv) |
111 | return -ENOMEM; |
112 | |
113 | spriv->iobase = iobase; |
114 | |
115 | s->type = COMEDI_SUBD_TIMER; |
116 | s->subdev_flags = SDF_WRITABLE; |
117 | s->n_chan = 1; |
118 | s->maxdata = 0xff; |
119 | s->insn_config = addi_watchdog_insn_config; |
120 | s->insn_read = addi_watchdog_insn_read; |
121 | s->insn_write = addi_watchdog_insn_write; |
122 | |
123 | return 0; |
124 | } |
125 | EXPORT_SYMBOL_GPL(addi_watchdog_init); |
126 | |
127 | static int __init addi_watchdog_module_init(void) |
128 | { |
129 | return 0; |
130 | } |
131 | module_init(addi_watchdog_module_init); |
132 | |
133 | static void __exit addi_watchdog_module_exit(void) |
134 | { |
135 | } |
136 | module_exit(addi_watchdog_module_exit); |
137 | |
138 | MODULE_DESCRIPTION("ADDI-DATA Watchdog subdevice" ); |
139 | MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>" ); |
140 | MODULE_LICENSE("GPL" ); |
141 | |