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