No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / hp700 / dev / power.c
blobcbd81ef7fb1637a609db202589a8020fc334a769
1 /* $NetBSD: power.c,v 1.3 2009/05/08 09:33:58 skrll Exp $ */
3 /*
4 * Copyright (c) 2004 Jochen Kunz.
5 * All rights reserved.
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.
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.
32 /* $OpenBSD: power.c,v 1.5 2004/06/11 12:53:09 mickey Exp $ */
35 * Copyright (c) 2003 Michael Shalayeff
36 * All rights reserved.
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.
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.
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>
68 #include <machine/reg.h>
69 #include <machine/pdc.h>
70 #include <machine/autoconf.h>
72 #include <hp700/dev/cpudevs.h>
73 #include <hp700/hp700/intr.h>
75 #include <dev/sysmon/sysmon_taskq.h>
76 #include <dev/sysmon/sysmonvar.h>
78 /* Enable / disable control over the power switch. */
79 #define PWR_SW_CTRL_DISABLE 0
80 #define PWR_SW_CTRL_ENABLE 1
81 #define PWR_SW_CTRL_LOCK 2
82 #define PWR_SW_CTRL_MAX PWR_SW_CTRL_LOCK
84 struct power_softc {
85 device_t sc_dev;
86 bus_space_tag_t sc_bst;
87 bus_space_handle_t sc_bsh;
89 void (*sc_kicker)(void *);
91 struct callout sc_callout;
92 int sc_timeout;
94 int sc_dr_cnt;
97 int powermatch(device_t, cfdata_t, void *);
98 void powerattach(device_t, device_t, void *);
100 CFATTACH_DECL_NEW(power, sizeof(struct power_softc),
101 powermatch, powerattach, NULL, NULL);
103 static struct pdc_power_info pdc_power_info PDC_ALIGNMENT;
104 static bool pswitch_on; /* power switch */
105 static int pwr_sw_control;
106 static const char *pwr_sw_control_str[] = {"disabled", "enabled", "locked"};
107 static struct sysmon_pswitch *pwr_sw_sysmon;
109 static int pwr_sw_sysctl_state(SYSCTLFN_PROTO);
110 static int pwr_sw_sysctl_ctrl(SYSCTLFN_PROTO);
111 static void pwr_sw_sysmon_cb(void *);
112 static void pwr_sw_ctrl(int);
113 static int pwr_sw_init(struct power_softc *);
115 void power_thread_dr(void *v);
116 void power_thread_reg(void *v);
117 void power_cold_hook_reg(int);
120 powermatch(device_t parent, cfdata_t cf, void *aux)
122 struct confargs *ca = aux;
124 if (cf->cf_unit > 0 && !strcmp(ca->ca_name, "power"))
125 return (0);
127 return (1);
130 void
131 powerattach(device_t parent, device_t self, void *aux)
133 struct power_softc *sc = device_private(self);
134 struct confargs *ca = aux;
136 sc->sc_dev = self;
137 sc->sc_kicker = NULL;
139 if (!pdc_call((iodcio_t)pdc, 0, PDC_SOFT_POWER,
140 PDC_SOFT_POWER_INFO, &pdc_power_info, 0)) {
141 ca->ca_hpa = pdc_power_info.addr;
144 switch (cpu_hvers) {
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;
151 /* Diag Reg. needs software dampening, poll at 0.2 Hz.*/
152 sc->sc_timeout = hz / 5;
154 aprint_normal(": DR25\n");
155 break;
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");
165 cold_hook = power_cold_hook_reg;
166 sc->sc_kicker = power_thread_reg;
168 /* Power Reg. is hardware dampened, poll at 1 Hz. */
169 sc->sc_timeout = hz;
171 aprint_normal("\n");
172 } else
173 aprint_normal(": not available\n");
174 break;
177 if (sc->sc_kicker) {
178 if (pwr_sw_init(sc))
179 return;
181 pswitch_on = true;
182 pwr_sw_control = PWR_SW_CTRL_ENABLE;
187 * If the power switch is turned off we schedule a sysmon task
188 * to register that event for this power switch device.
190 static void
191 check_pwr_state(struct power_softc *sc)
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);
200 void
201 power_thread_dr(void *v)
203 struct power_softc *sc = v;
204 uint32_t r;
206 /* Get Power Fail status from CPU Diagnose Register 25 */
207 mfcpu(25, r);
210 * On power failure, the hardware clears bit DR25_PCXL_POWFAIL
211 * in CPU Diagnose Register 25.
213 if (r & (1 << DR25_PCXL_POWFAIL))
214 sc->sc_dr_cnt = 0;
215 else
216 sc->sc_dr_cnt++;
219 * the bit is undampened straight wire from the power
220 * switch and thus we have do dampen it ourselves.
222 if (sc->sc_dr_cnt == sc->sc_timeout)
223 pswitch_on = false;
224 else
225 pswitch_on = true;
227 check_pwr_state(sc);
230 void
231 power_thread_reg(void *v)
233 struct power_softc *sc = v;
234 uint32_t r;
236 r = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 0);
238 if (!(r & 1))
239 pswitch_on = false;
240 else
241 pswitch_on = true;
243 check_pwr_state(sc);
246 void
247 power_cold_hook_reg(int on)
249 int error;
251 if ((error = pdc_call((iodcio_t)pdc, 0, PDC_SOFT_POWER,
252 PDC_SOFT_POWER_ENABLE, &pdc_power_info,
253 on == HPPA_COLD_HOT)))
254 aprint_error("PDC_SOFT_POWER_ENABLE failed (%d)\n", error);
257 static int
258 pwr_sw_init(struct power_softc *sc)
260 struct sysctllog *sysctl_log = NULL;
261 const struct sysctlnode *pwr_sw_node;
262 const char *errmsg;
263 int error = EINVAL;
266 * Ensure that we are on a PCX-L / PA7100LC CPU if it is a
267 * 712 style machine.
269 if (pdc_power_info.addr == 0 && hppa_cpu_info->hci_cputype != hpcxl) {
270 aprint_error_dev(sc->sc_dev, "No soft power available.\n");
271 return error;
274 errmsg = "Can't create sysctl machdep.power_switch (or children)\n";
275 error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 0,
276 CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0,
277 CTL_MACHDEP, CTL_EOL);
279 if (error)
280 goto err_sysctl;
282 error = sysctl_createv(&sysctl_log, 0, NULL, &pwr_sw_node, 0,
283 CTLTYPE_NODE, "power_switch", NULL, NULL, 0, NULL, 0,
284 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
286 if (error)
287 goto err_sysctl;
289 error = sysctl_createv(&sysctl_log, 0, NULL, NULL,
290 CTLFLAG_READONLY, CTLTYPE_STRING, "state", NULL,
291 pwr_sw_sysctl_state, 0, NULL, 16,
292 CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL);
294 if (error)
295 goto err_sysctl;
297 error = sysctl_createv(&sysctl_log, 0, NULL, NULL,
298 CTLFLAG_READWRITE, CTLTYPE_STRING, "control", NULL,
299 pwr_sw_sysctl_ctrl, 0, NULL, 16,
300 CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL);
302 if (error)
303 goto err_sysctl;
305 errmsg = "Can't alloc sysmon power switch.\n";
306 pwr_sw_sysmon = kmem_zalloc(sizeof(*pwr_sw_sysmon), KM_SLEEP);
307 if (pwr_sw_sysmon == NULL) {
308 error = ENOMEM;
309 goto err_kmem;
312 errmsg = "Can't register power switch with sysmon.\n";
313 sysmon_task_queue_init();
314 pwr_sw_sysmon->smpsw_name = "power switch";
315 pwr_sw_sysmon->smpsw_type = PSWITCH_TYPE_POWER;
316 error = sysmon_pswitch_register(pwr_sw_sysmon);
318 if (error)
319 goto err_sysmon;
321 callout_init(&sc->sc_callout, 0);
322 callout_reset(&sc->sc_callout, sc->sc_timeout, sc->sc_kicker, sc);
324 return error;
326 err_sysmon:
327 /* Nothing to do */
329 err_kmem:
330 kmem_free(pwr_sw_sysmon, sizeof(*pwr_sw_sysmon));
332 err_sysctl:
333 sysctl_teardown(&sysctl_log);
335 aprint_error_dev(sc->sc_dev, errmsg);
337 return error;
340 static void
341 pwr_sw_sysmon_cb(void *not_used)
343 sysmon_pswitch_event(pwr_sw_sysmon, PSWITCH_EVENT_PRESSED);
346 static void
347 pwr_sw_ctrl(int enable)
349 int on;
351 #ifdef DEBUG
352 printf("pwr_sw_control=%d enable=%d\n", pwr_sw_control, enable);
353 #endif /* DEBUG */
355 if (cold_hook == NULL)
356 return;
358 switch(enable) {
359 case PWR_SW_CTRL_DISABLE:
360 on = HPPA_COLD_OFF;
361 break;
362 case PWR_SW_CTRL_ENABLE:
363 case PWR_SW_CTRL_LOCK:
364 on = HPPA_COLD_HOT;
365 break;
366 default:
367 panic("invalid power state in pwr_sw_control: %d", enable);
370 pwr_sw_control = enable;
372 if (cold_hook)
373 (*cold_hook)(on);
377 pwr_sw_sysctl_state(SYSCTLFN_ARGS)
379 struct sysctlnode node;
380 const char *status;
382 if (pswitch_on == true)
383 status = "on";
384 else
385 status = "off";
387 node = *rnode;
388 node.sysctl_data = __UNCONST(status);
389 return sysctl_lookup(SYSCTLFN_CALL(&node));
393 pwr_sw_sysctl_ctrl(SYSCTLFN_ARGS)
395 struct sysctlnode node;
396 int i, error;
397 char val[16];
399 node = *rnode;
400 strcpy(val, pwr_sw_control_str[pwr_sw_control]);
402 node.sysctl_data = val;
404 error = sysctl_lookup(SYSCTLFN_CALL(&node));
406 if (error || newp == NULL)
407 return error;
409 for (i = 0; i <= PWR_SW_CTRL_MAX; i++)
410 if (strcmp(val, pwr_sw_control_str[i]) == 0) {
411 pwr_sw_ctrl(i);
412 return 0;
415 return EINVAL;