1 /* $NetBSD: power.c,v 1.3 2019/04/15 20:40:37 skrll Exp $ */
2
3 /*
4 * Copyright (c) 2004 Jochen Kunz.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of Jochen Kunz may not be used to endorse or promote
16 * products derived from this software without specific prior
17 * written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOCHEN KUNZ
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /* $OpenBSD: power.c,v 1.5 2004/06/11 12:53:09 mickey Exp $ */
33
34 /*
35 * Copyright (c) 2003 Michael Shalayeff
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
51 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
52 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
53 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
55 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
56 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57 * THE POSSIBILITY OF SUCH DAMAGE.
58 */
59
60 #include <sys/param.h>
61 #include <sys/kernel.h>
62 #include <sys/systm.h>
63 #include <sys/reboot.h>
64 #include <sys/device.h>
65 #include <sys/sysctl.h>
66 #include <sys/kmem.h>
67
68 #include <machine/reg.h>
69 #include <machine/pdc.h>
70 #include <machine/autoconf.h>
71
72 #include <hppa/dev/cpudevs.h>
73
74 #include <dev/sysmon/sysmon_taskq.h>
75 #include <dev/sysmon/sysmonvar.h>
76
77 /* Enable / disable control over the power switch. */
78 #define PWR_SW_CTRL_DISABLE 0
79 #define PWR_SW_CTRL_ENABLE 1
80 #define PWR_SW_CTRL_LOCK 2
81 #define PWR_SW_CTRL_MAX PWR_SW_CTRL_LOCK
82
83 struct power_softc {
84 device_t sc_dev;
85 bus_space_tag_t sc_bst;
86 bus_space_handle_t sc_bsh;
87
88 void (*sc_kicker)(void *);
89
90 struct callout sc_callout;
91 int sc_timeout;
92
93 int sc_dr_cnt;
94 };
95
96 int powermatch(device_t, cfdata_t, void *);
97 void powerattach(device_t, device_t, void *);
98
99 CFATTACH_DECL_NEW(power, sizeof(struct power_softc),
100 powermatch, powerattach, NULL, NULL);
101
102 static struct pdc_power_info pdc_power_info;
103 static bool pswitch_on; /* power switch */
104 static int pwr_sw_control;
105 static const char *pwr_sw_control_str[] = {"disabled", "enabled", "locked"};
106 static struct sysmon_pswitch *pwr_sw_sysmon;
107
108 static int pwr_sw_sysctl_state(SYSCTLFN_PROTO);
109 static int pwr_sw_sysctl_ctrl(SYSCTLFN_PROTO);
110 static void pwr_sw_sysmon_cb(void *);
111 static void pwr_sw_ctrl(int);
112 static int pwr_sw_init(struct power_softc *);
113
114 void power_thread_dr(void *v);
115 void power_thread_reg(void *v);
116 void power_cold_hook_reg(int);
117
118 int
powermatch(device_t parent,cfdata_t cf,void * aux)119 powermatch(device_t parent, cfdata_t cf, void *aux)
120 {
121 struct confargs *ca = aux;
122
123 if (cf->cf_unit > 0 && !strcmp(ca->ca_name, "power"))
124 return (0);
125
126 return (1);
127 }
128
129 void
powerattach(device_t parent,device_t self,void * aux)130 powerattach(device_t parent, device_t self, void *aux)
131 {
132 struct power_softc *sc = device_private(self);
133 struct confargs *ca = aux;
134 int err;
135
136 sc->sc_dev = self;
137 sc->sc_kicker = NULL;
138
139 err = pdcproc_soft_power_info(&pdc_power_info);
140
141 if (!err)
142 ca->ca_hpa = pdc_power_info.addr;
143
144 switch (cpu_modelno) {
145 case HPPA_BOARD_HP712_60:
146 case HPPA_BOARD_HP712_80:
147 case HPPA_BOARD_HP712_100:
148 case HPPA_BOARD_HP712_120:
149 sc->sc_kicker = power_thread_dr;
150
151 /* Diag Reg. needs software dampening, poll at 0.2 Hz.*/
152 sc->sc_timeout = hz / 5;
153
154 aprint_normal(": DR25\n");
155 break;
156
157 default:
158 if (ca->ca_hpa) {
159 sc->sc_bst = ca->ca_iot;
160 if (bus_space_map(sc->sc_bst, ca->ca_hpa, 4, 0,
161 &sc->sc_bsh) != 0)
162 aprint_error_dev(self,
163 "Can't map power switch status reg.\n");
164
165 cold_hook = power_cold_hook_reg;
166 sc->sc_kicker = power_thread_reg;
167
168 /* Power Reg. is hardware dampened, poll at 1 Hz. */
169 sc->sc_timeout = hz;
170
171 aprint_normal("\n");
172 } else
173 aprint_normal(": not available\n");
174 break;
175 }
176
177 if (sc->sc_kicker) {
178 if (pwr_sw_init(sc))
179 return;
180
181 pswitch_on = true;
182 pwr_sw_control = PWR_SW_CTRL_ENABLE;
183 }
184 }
185
186 /*
187 * If the power switch is turned off we schedule a sysmon task
188 * to register that event for this power switch device.
189 */
190 static void
check_pwr_state(struct power_softc * sc)191 check_pwr_state(struct power_softc *sc)
192 {
193 if (pswitch_on == false && pwr_sw_control != PWR_SW_CTRL_LOCK)
194 sysmon_task_queue_sched(0, pwr_sw_sysmon_cb, NULL);
195 else
196 callout_reset(&sc->sc_callout, sc->sc_timeout,
197 sc->sc_kicker, sc);
198 }
199
200 void
power_thread_dr(void * v)201 power_thread_dr(void *v)
202 {
203 struct power_softc *sc = v;
204 uint32_t r;
205
206 /* Get Power Fail status from CPU Diagnose Register 25 */
207 mfcpu(25, r);
208
209 /*
210 * On power failure, the hardware clears bit DR25_PCXL_POWFAIL
211 * in CPU Diagnose Register 25.
212 */
213 if (r & (1 << DR25_PCXL_POWFAIL))
214 sc->sc_dr_cnt = 0;
215 else
216 sc->sc_dr_cnt++;
217
218 /*
219 * the bit is undampened straight wire from the power
220 * switch and thus we have do dampen it ourselves.
221 */
222 if (sc->sc_dr_cnt == sc->sc_timeout)
223 pswitch_on = false;
224 else
225 pswitch_on = true;
226
227 check_pwr_state(sc);
228 }
229
230 void
power_thread_reg(void * v)231 power_thread_reg(void *v)
232 {
233 struct power_softc *sc = v;
234 uint32_t r;
235
236 r = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 0);
237
238 if (!(r & 1))
239 pswitch_on = false;
240 else
241 pswitch_on = true;
242
243 check_pwr_state(sc);
244 }
245
246 void
power_cold_hook_reg(int on)247 power_cold_hook_reg(int on)
248 {
249 int error;
250
251 error = pdcproc_soft_power_enable(on == HPPA_COLD_HOT);
252 if (error)
253 aprint_error("PDC_SOFT_POWER_ENABLE failed (%d)\n", error);
254 }
255
256 static int
pwr_sw_init(struct power_softc * sc)257 pwr_sw_init(struct power_softc *sc)
258 {
259 struct sysctllog *sysctl_log = NULL;
260 const struct sysctlnode *pwr_sw_node;
261 const char *errmsg;
262 int error = EINVAL;
263
264 /*
265 * Ensure that we are on a PCX-L / PA7100LC CPU if it is a
266 * 712 style machine.
267 */
268 if (pdc_power_info.addr == 0 && hppa_cpu_info->hci_cputype != hpcxl) {
269 aprint_error_dev(sc->sc_dev, "No soft power available.\n");
270 return error;
271 }
272
273 errmsg = "Can't create sysctl machdep.power_switch (or children)\n";
274 error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 0,
275 CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0,
276 CTL_MACHDEP, CTL_EOL);
277
278 if (error)
279 goto err_sysctl;
280
281 error = sysctl_createv(&sysctl_log, 0, NULL, &pwr_sw_node, 0,
282 CTLTYPE_NODE, "power_switch", NULL, NULL, 0, NULL, 0,
283 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
284
285 if (error)
286 goto err_sysctl;
287
288 error = sysctl_createv(&sysctl_log, 0, NULL, NULL,
289 CTLFLAG_READONLY, CTLTYPE_STRING, "state", NULL,
290 pwr_sw_sysctl_state, 0, NULL, 16,
291 CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL);
292
293 if (error)
294 goto err_sysctl;
295
296 error = sysctl_createv(&sysctl_log, 0, NULL, NULL,
297 CTLFLAG_READWRITE, CTLTYPE_STRING, "control", NULL,
298 pwr_sw_sysctl_ctrl, 0, NULL, 16,
299 CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL);
300
301 if (error)
302 goto err_sysctl;
303
304 errmsg = "Can't alloc sysmon power switch.\n";
305 pwr_sw_sysmon = kmem_zalloc(sizeof(*pwr_sw_sysmon), KM_SLEEP);
306 errmsg = "Can't register power switch with sysmon.\n";
307 sysmon_task_queue_init();
308 pwr_sw_sysmon->smpsw_name = "power switch";
309 pwr_sw_sysmon->smpsw_type = PSWITCH_TYPE_POWER;
310 error = sysmon_pswitch_register(pwr_sw_sysmon);
311
312 if (error)
313 goto err_sysmon;
314
315 callout_init(&sc->sc_callout, 0);
316 callout_reset(&sc->sc_callout, sc->sc_timeout, sc->sc_kicker, sc);
317
318 return error;
319
320 err_sysmon:
321 kmem_free(pwr_sw_sysmon, sizeof(*pwr_sw_sysmon));
322
323 err_sysctl:
324 sysctl_teardown(&sysctl_log);
325
326 aprint_error_dev(sc->sc_dev, errmsg);
327
328 return error;
329 }
330
331 static void
pwr_sw_sysmon_cb(void * not_used)332 pwr_sw_sysmon_cb(void *not_used)
333 {
334 sysmon_pswitch_event(pwr_sw_sysmon, PSWITCH_EVENT_PRESSED);
335 }
336
337 static void
pwr_sw_ctrl(int enable)338 pwr_sw_ctrl(int enable)
339 {
340 int on;
341
342 #ifdef DEBUG
343 printf("pwr_sw_control=%d enable=%d\n", pwr_sw_control, enable);
344 #endif /* DEBUG */
345
346 if (cold_hook == NULL)
347 return;
348
349 switch(enable) {
350 case PWR_SW_CTRL_DISABLE:
351 on = HPPA_COLD_OFF;
352 break;
353 case PWR_SW_CTRL_ENABLE:
354 case PWR_SW_CTRL_LOCK:
355 on = HPPA_COLD_HOT;
356 break;
357 default:
358 panic("invalid power state in pwr_sw_control: %d", enable);
359 }
360
361 pwr_sw_control = enable;
362
363 if (cold_hook)
364 (*cold_hook)(on);
365 }
366
367 int
pwr_sw_sysctl_state(SYSCTLFN_ARGS)368 pwr_sw_sysctl_state(SYSCTLFN_ARGS)
369 {
370 struct sysctlnode node;
371 const char *status;
372
373 if (pswitch_on == true)
374 status = "on";
375 else
376 status = "off";
377
378 node = *rnode;
379 node.sysctl_data = __UNCONST(status);
380 return sysctl_lookup(SYSCTLFN_CALL(&node));
381 }
382
383 int
pwr_sw_sysctl_ctrl(SYSCTLFN_ARGS)384 pwr_sw_sysctl_ctrl(SYSCTLFN_ARGS)
385 {
386 struct sysctlnode node;
387 int i, error;
388 char val[16];
389
390 node = *rnode;
391 strcpy(val, pwr_sw_control_str[pwr_sw_control]);
392
393 node.sysctl_data = val;
394
395 error = sysctl_lookup(SYSCTLFN_CALL(&node));
396
397 if (error || newp == NULL)
398 return error;
399
400 for (i = 0; i <= PWR_SW_CTRL_MAX; i++)
401 if (strcmp(val, pwr_sw_control_str[i]) == 0) {
402 pwr_sw_ctrl(i);
403 return 0;
404 }
405
406 return EINVAL;
407 }
408