Fri Aug 26 13:29:57 2011 UTC ()
Add a pmf resume function to put the hardware in a defined state after
resume.  Problem found by jmcneill@.


(mbalmer)
diff -r1.3 -r1.4 src/sys/dev/pci/pwdog.c

cvs diff -r1.3 -r1.4 src/sys/dev/pci/pwdog.c (switch to unified diff)

--- src/sys/dev/pci/pwdog.c 2011/08/26 10:30:47 1.3
+++ src/sys/dev/pci/pwdog.c 2011/08/26 13:29:56 1.4
@@ -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
35struct pwdog_softc { 35struct 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
53static int pwdog_match(device_t, cfdata_t, void *); 53static int pwdog_match(device_t, cfdata_t, void *);
54static void pwdog_attach(device_t, device_t, void *); 54static void pwdog_attach(device_t, device_t, void *);
55static int pwdog_detach(device_t, int); 55static int pwdog_detach(device_t, int);
56static bool pwdog_suspend(device_t, const pmf_qual_t *); 56static bool pwdog_suspend(device_t, const pmf_qual_t *);
 57static bool pwdog_resume(device_t, const pmf_qual_t *);
57static int pwdog_setmode(struct sysmon_wdog *); 58static int pwdog_setmode(struct sysmon_wdog *);
58static int pwdog_tickle(struct sysmon_wdog *); 59static int pwdog_tickle(struct sysmon_wdog *);
59 60
60CFATTACH_DECL_NEW( 61CFATTACH_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
69static int 70static int
70pwdog_match(device_t parent, cfdata_t match, void *aux) 71pwdog_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
80void 81void
81pwdog_attach(device_t parent, device_t self, void *aux) 82pwdog_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
114static int 115static int
115pwdog_detach(device_t self, int flags) 116pwdog_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
135static bool 137static bool
 138pwdog_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
 154static bool
136pwdog_suspend(device_t self, const pmf_qual_t *qual) 155pwdog_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
149static int 168static int
150pwdog_setmode(struct sysmon_wdog *smw) 169pwdog_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
168static int 187static int
169pwdog_tickle(struct sysmon_wdog *smw) 188pwdog_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
178MODULE(MODULE_CLASS_DRIVER, pwdog, NULL); 197MODULE(MODULE_CLASS_DRIVER, pwdog, NULL);
179 198
180#include "ioconf.c" 199#include "ioconf.c"
181 200
182static int 201static int
183pwdog_modcmd(modcmd_t cmd, void *opaque) 202pwdog_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 */