1 | /* $$NetBSD: pwdog.c,v 1.9 2016/07/14 10:19:06 msaitoh Exp $ */ |
2 | /* $OpenBSD: pwdog.c,v 1.7 2010/04/08 00:23:53 tedu Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 2006, 2011 Marc Balmer <mbalmer@NetBSD.org> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | #include <sys/types.h> |
21 | #include <sys/param.h> |
22 | #include <sys/device.h> |
23 | #include <sys/kernel.h> |
24 | #include <sys/module.h> |
25 | #include <sys/systm.h> |
26 | |
27 | #include <dev/pci/pcivar.h> |
28 | #include <dev/pci/pcireg.h> |
29 | #include <dev/pci/pcidevs.h> |
30 | |
31 | #include <dev/sysmon/sysmonvar.h> |
32 | |
33 | struct pwdog_softc { |
34 | device_t sc_dev; |
35 | |
36 | bus_space_tag_t sc_iot; |
37 | bus_space_handle_t sc_ioh; |
38 | bus_size_t sc_iosize; |
39 | |
40 | struct sysmon_wdog sc_smw; |
41 | bool sc_smw_valid; |
42 | }; |
43 | |
44 | /* registers */ |
45 | #define PWDOG_ACTIVATE 0 |
46 | #define PWDOG_DISABLE 1 |
47 | |
48 | /* maximum timeout period in seconds */ |
49 | #define PWDOG_MAX_PERIOD (12*60) /* 12 minutes */ |
50 | |
51 | static int pwdog_match(device_t, cfdata_t, void *); |
52 | static void pwdog_attach(device_t, device_t, void *); |
53 | static int pwdog_detach(device_t, int); |
54 | static bool pwdog_suspend(device_t, const pmf_qual_t *); |
55 | static bool pwdog_resume(device_t, const pmf_qual_t *); |
56 | static int pwdog_setmode(struct sysmon_wdog *); |
57 | static int pwdog_tickle(struct sysmon_wdog *); |
58 | |
59 | CFATTACH_DECL_NEW( |
60 | pwdog, |
61 | sizeof(struct pwdog_softc), |
62 | pwdog_match, |
63 | pwdog_attach, |
64 | pwdog_detach, |
65 | NULL |
66 | ); |
67 | |
68 | static int |
69 | pwdog_match(device_t parent, cfdata_t match, void *aux) |
70 | { |
71 | struct pci_attach_args *pa = (struct pci_attach_args *)aux; |
72 | |
73 | if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_QUANCOM && |
74 | PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_QUANCOM_PWDOG1) |
75 | return 1; |
76 | return 0; |
77 | } |
78 | |
79 | void |
80 | pwdog_attach(device_t parent, device_t self, void *aux) |
81 | { |
82 | struct pwdog_softc *sc = device_private(self); |
83 | struct pci_attach_args *const pa = (struct pci_attach_args *)aux; |
84 | pcireg_t memtype; |
85 | |
86 | aprint_naive("\n" ); |
87 | memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); |
88 | if (pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, &sc->sc_iot, |
89 | &sc->sc_ioh, NULL, &sc->sc_iosize)) { |
90 | aprint_error("\n" ); |
91 | aprint_error_dev(self, "PCI %s region not found\n" , |
92 | memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory" ); |
93 | return; |
94 | } |
95 | aprint_normal("\n" ); |
96 | |
97 | sc->sc_dev = self; |
98 | |
99 | pmf_device_register(self, pwdog_suspend, pwdog_resume); |
100 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, PWDOG_DISABLE, 0); |
101 | |
102 | sc->sc_smw.smw_name = device_xname(self); |
103 | sc->sc_smw.smw_cookie = sc; |
104 | sc->sc_smw.smw_setmode = pwdog_setmode; |
105 | sc->sc_smw.smw_tickle = pwdog_tickle; |
106 | sc->sc_smw.smw_period = PWDOG_MAX_PERIOD; |
107 | |
108 | if (sysmon_wdog_register(&sc->sc_smw)) |
109 | aprint_error_dev(self, "couldn't register with sysmon\n" ); |
110 | else |
111 | sc->sc_smw_valid = true; |
112 | } |
113 | |
114 | static int |
115 | pwdog_detach(device_t self, int flags) |
116 | { |
117 | struct pwdog_softc *sc = device_private(self); |
118 | |
119 | /* XXX check flags & DETACH_FORCE (or DETACH_SHUTDOWN)? */ |
120 | if (sc->sc_smw_valid) { |
121 | if ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) |
122 | != WDOG_MODE_DISARMED) |
123 | return EBUSY; |
124 | |
125 | sysmon_wdog_unregister(&sc->sc_smw); |
126 | sc->sc_smw_valid = false; |
127 | } |
128 | |
129 | pmf_device_deregister(self); |
130 | |
131 | if (sc->sc_iosize) |
132 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); |
133 | return 0; |
134 | } |
135 | |
136 | static bool |
137 | pwdog_resume(device_t self, const pmf_qual_t *qual) |
138 | { |
139 | struct pwdog_softc *sc = device_private(self); |
140 | |
141 | if (sc->sc_smw_valid == false) |
142 | return true; |
143 | |
144 | /* |
145 | * suspend is inhibited when the watchdog timer was armed, |
146 | * so when we end up here, the watchdog is disabled; program the |
147 | * hardware accordingly. |
148 | */ |
149 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, PWDOG_DISABLE, 0); |
150 | return true; |
151 | } |
152 | |
153 | static bool |
154 | pwdog_suspend(device_t self, const pmf_qual_t *qual) |
155 | { |
156 | struct pwdog_softc *sc = device_private(self); |
157 | |
158 | if (sc->sc_smw_valid == false) |
159 | return true; |
160 | |
161 | if ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED) |
162 | return false; |
163 | |
164 | return true; |
165 | } |
166 | |
167 | static int |
168 | pwdog_setmode(struct sysmon_wdog *smw) |
169 | { |
170 | struct pwdog_softc *sc = smw->smw_cookie; |
171 | |
172 | switch (smw->smw_mode & WDOG_MODE_MASK) { |
173 | case WDOG_MODE_DISARMED: |
174 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, PWDOG_DISABLE, 0); |
175 | break; |
176 | default: |
177 | /* |
178 | * NB: the timer period set by the user is ignored |
179 | * since the period can only be set in hardware. |
180 | */ |
181 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, PWDOG_ACTIVATE, 0); |
182 | } |
183 | return 0; |
184 | } |
185 | |
186 | static int |
187 | pwdog_tickle(struct sysmon_wdog *smw) |
188 | { |
189 | struct pwdog_softc *sc = smw->smw_cookie; |
190 | |
191 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, PWDOG_ACTIVATE, 0); |
192 | return 0; |
193 | } |
194 | |
195 | MODULE(MODULE_CLASS_DRIVER, pwdog, "pci,sysmon_wdog" ); |
196 | |
197 | #ifdef _MODULE |
198 | #include "ioconf.c" |
199 | #endif |
200 | |
201 | static int |
202 | pwdog_modcmd(modcmd_t cmd, void *opaque) |
203 | { |
204 | int error; |
205 | |
206 | error = 0; |
207 | switch (cmd) { |
208 | case MODULE_CMD_INIT: |
209 | #ifdef _MODULE |
210 | error = config_init_component(cfdriver_ioconf_pwdog, |
211 | cfattach_ioconf_pwdog, cfdata_ioconf_pwdog); |
212 | if (error) |
213 | aprint_error("%s: unable to init component\n" , |
214 | pwdog_cd.cd_name); |
215 | #endif |
216 | break; |
217 | case MODULE_CMD_FINI: |
218 | #ifdef _MODULE |
219 | config_fini_component(cfdriver_ioconf_pwdog, |
220 | cfattach_ioconf_pwdog, cfdata_ioconf_pwdog); |
221 | #endif |
222 | break; |
223 | default: |
224 | error = ENOTTY; |
225 | } |
226 | return error; |
227 | } |
228 | |