1 /* $NetBSD: psm.c,v 1.6 2007/10/17 19:57:29 garbled Exp $ */
3 * Copyright (c) 2006 Itronix Inc.
6 * Ported from Tadpole Solaris sources by Garrett D'Amore for Itronix Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of Itronix Inc. may not be used to endorse
17 * or promote products derived from this software without specific
18 * prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
33 * Tadpole-RDI Ultrabook IIi (huxley) power management. Note that
34 * there is a lot of stuff still missing here, due in part to the confusion
35 * that exists with the NetBSD power management framework. I'm not wasting
36 * time with APM at this point, and some of sysmon seems "lacking".
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: psm.c,v 1.6 2007/10/17 19:57:29 garbled Exp $");
41 #include <sys/param.h>
42 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/kthread.h>
46 #include <sys/types.h>
47 #include <sys/device.h>
49 #include <sys/kauth.h>
51 #include <machine/autoconf.h>
52 #include <machine/bus.h>
53 #include <machine/intr.h>
55 #include <dev/ebus/ebusreg.h>
56 #include <dev/ebus/ebusvar.h>
58 #include <dev/sysmon/sysmonvar.h>
60 #include <sparc64/dev/psmreg.h>
64 bus_space_tag_t sc_memt
;
65 bus_space_handle_t sc_memh
;
69 struct sysmon_pswitch sc_sm_pbutton
;
70 struct sysmon_pswitch sc_sm_lid
;
71 struct sysmon_pswitch sc_sm_ac
;
72 struct evcnt sc_intrcnt
;
76 #define PUT8(sc, r, v) \
77 bus_space_write_1(sc->sc_memt, sc->sc_memh, r, v)
79 bus_space_read_1(sc->sc_memt, sc->sc_memh, r)
81 #define WAIT_DELAY 1000
82 #define WAIT_RETRIES 1000
84 #define RESET_DELAY 200
94 /* flags indicating state */
95 #define PSM_FLAG_ACPWR 0x1
96 #define PSM_FLAG_LIDCLOSED 0x2
97 #define PSM_FLAG_DOCKED 0x4
99 /* system events -- causes activity in the event thread */
100 #define PSM_EV_PBUTTON 0x1
101 #define PSM_EV_LID 0x2
102 #define PSM_EV_ACPWR 0x4
103 #define PSM_EV_BATT 0x8
104 #define PSM_EV_TEMP 0x10
106 STATIC
void psm_sysmon_setup(struct psm_softc
*);
107 STATIC
void psm_event_thread(void *);
108 STATIC
int psm_init(struct psm_softc
*);
109 STATIC
void psm_reset(struct psm_softc
*);
110 STATIC
void psm_poll_acpower(struct psm_softc
*);
111 STATIC
int psm_intr(void *);
112 STATIC
int psm_misc_rd(struct psm_softc
*, uint8_t, uint8_t *);
113 STATIC
int psm_misc_wr(struct psm_softc
*, uint8_t, uint8_t);
114 STATIC
int psm_wait(struct psm_softc
*, uint8_t);
116 STATIC
int psm_ecmd_rd16(struct psm_softc
*, uint16_t *, uint8_t, uint8_t,
119 STATIC
int psm_ecmd_rd8(struct psm_softc
*, uint8_t *, uint8_t, uint8_t,
121 STATIC
int psm_ecmd_wr8(struct psm_softc
*, uint8_t, uint8_t, uint8_t,
123 STATIC
int psm_match(struct device
*, struct cfdata
*, void *);
124 STATIC
void psm_attach(struct device
*, struct device
*, void *);
126 CFATTACH_DECL(psm
, sizeof(struct psm_softc
),
127 psm_match
, psm_attach
, NULL
, NULL
);
131 psm_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
133 struct ebus_attach_args
*ea
= aux
;
135 if (strcmp(ea
->ea_name
, "psm") != 0)
141 psm_attach(struct device
*parent
, struct device
*self
, void *aux
)
143 struct psm_softc
*sc
= (struct psm_softc
*)self
;
144 struct ebus_attach_args
*ea
= aux
;
148 xname
= device_xname(&sc
->sc_dev
);
150 sc
->sc_memt
= ea
->ea_bustag
;
151 devaddr
= EBUS_ADDR_FROM_REG(&ea
->ea_reg
[0]);
153 if (bus_space_map(sc
->sc_memt
, devaddr
, ea
->ea_reg
[0].size
,
154 0, &sc
->sc_memh
) != 0) {
155 printf(": unable to map device registers\n");
158 if (psm_init(sc
) != 0) {
159 printf(": unable to initialize device\n");
163 printf(": UltraBook IIi power control\n");
165 psm_sysmon_setup(sc
);
167 if (kthread_create(PRI_NONE
, 0, NULL
, psm_event_thread
, sc
,
168 &sc
->sc_thread
, "%s", device_xname(&sc
->sc_dev
)) != 0) {
169 aprint_error_dev(&sc
->sc_dev
, "unable to create event kthread\n");
173 * Establish device interrupts
175 (void) bus_intr_establish(sc
->sc_memt
, ea
->ea_intr
[0], IPL_HIGH
,
177 evcnt_attach_dynamic(&sc
->sc_intrcnt
, EVCNT_TYPE_INTR
, NULL
,
178 device_xname(&sc
->sc_dev
), "intr");
182 * Register sensors and events with sysmon.
185 psm_sysmon_setup(struct psm_softc
*sc
)
187 const char *xname
= device_xname(&sc
->sc_dev
);
191 * XXX: Register sysmon environment.
195 * Register sysmon events
197 sc
->sc_sm_pbutton
.smpsw_name
= xname
;
198 sc
->sc_sm_pbutton
.smpsw_type
= PSWITCH_TYPE_POWER
;
199 if (sysmon_pswitch_register(&sc
->sc_sm_pbutton
) != 0)
200 printf("%s: unable to register power button\n", xname
);
202 sc
->sc_sm_lid
.smpsw_name
= xname
;
203 sc
->sc_sm_lid
.smpsw_type
= PSWITCH_TYPE_LID
;
204 if (sysmon_pswitch_register(&sc
->sc_sm_lid
) != 0)
205 printf("%s: unable to register lid switch\n", xname
);
207 sc
->sc_sm_ac
.smpsw_name
= xname
;
208 sc
->sc_sm_ac
.smpsw_type
= PSWITCH_TYPE_ACADAPTER
;
209 if (sysmon_pswitch_register(&sc
->sc_sm_ac
) != 0)
210 printf("%s: unable to register AC adapter\n", xname
);
214 psm_event_thread(void *arg
)
216 struct psm_softc
*sc
= arg
;
223 /* check for AC power. sets event if there is a change */
224 psm_poll_acpower(sc
);
226 /* read and clear events */
227 event
= sc
->sc_event
;
228 flags
= sc
->sc_flags
;
232 if (event
& PSM_EV_PBUTTON
) {
233 sysmon_pswitch_event(&sc
->sc_sm_pbutton
,
234 PSWITCH_EVENT_PRESSED
);
237 if (event
& PSM_EV_LID
) {
238 sysmon_pswitch_event(&sc
->sc_sm_lid
,
239 flags
& PSM_FLAG_LIDCLOSED
?
240 PSWITCH_EVENT_PRESSED
: PSWITCH_EVENT_RELEASED
);
243 if (event
& PSM_EV_ACPWR
) {
244 sysmon_pswitch_event(&sc
->sc_sm_ac
,
245 flags
& PSM_FLAG_ACPWR
?
246 PSWITCH_EVENT_PRESSED
: PSWITCH_EVENT_RELEASED
);
249 /* XXX: handle PSM_EV_TEMP */
251 /* one second interval between probes of power */
252 tsleep(&sc
, PWAIT
, "psm", hz
);
257 psm_init(struct psm_softc
*sc
)
260 uint8_t batt
= 0xff; /* keep GCC 4.x happy */
262 /* clear interrupts */
264 PUT8(sc
, PSM_ICR
, 0xff);
267 /* enable interrupts */
268 if (psm_misc_wr(sc
, PSM_MISC_IMR
, PSM_IMR_ALL
))
271 /* make sure that UPS battery is reasonable */
272 if (psm_misc_rd(sc
, PSM_MISC_UPS
, &batt
) || (batt
> PSM_MAX_BATTERIES
))
273 if (psm_misc_wr(sc
, PSM_MISC_UPS
, batt
))
274 aprint_error_dev(&sc
->sc_dev
, "cannot set UPS battery");
280 psm_reset(struct psm_softc
*sc
)
283 PUT8(sc
, PSM_MCR
, PSM_MCR_RST
);
288 psm_poll_acpower(struct psm_softc
*sc
)
290 int flags
= sc
->sc_flags
;
292 if (GET8(sc
, PSM_STAT
) & PSM_STAT_AC
) {
293 sc
->sc_flags
|= PSM_FLAG_ACPWR
;
295 sc
->sc_flags
&= ~PSM_FLAG_ACPWR
;
297 if (flags
!= sc
->sc_flags
)
298 sc
->sc_event
|= PSM_EV_ACPWR
;
302 psm_misc_rd(struct psm_softc
*sc
, uint8_t mreg
, uint8_t *data
)
305 return (psm_ecmd_rd8(sc
, data
, mreg
, PSM_MODE_MISC
, 0));
309 psm_misc_wr(struct psm_softc
*sc
, uint8_t mreg
, uint8_t data
)
312 return (psm_ecmd_wr8(sc
, data
, mreg
, PSM_MODE_MISC
, 0));
316 psm_wait(struct psm_softc
*sc
, uint8_t flag
)
318 int retr
= WAIT_RETRIES
;
320 while (GET8(sc
, PSM_STAT
) & flag
) {
331 psm_ecmd_rd16(struct psm_softc
*sc
, uint16_t *data
, uint8_t iar
, uint8_t mode
,
334 uint8_t cmr
= PSM_CMR_DATA(mode
, PSM_L_16
, PSM_D_RD
, addr
);
335 int x
, rc
, retr
= CMD_RETRIES
;
340 if ((rc
= psm_wait(sc
, PSM_STAT_RDA
)) != 0) {
345 PUT8(sc
, PSM_IAR
, iar
);
346 PUT8(sc
, PSM_CMR
, cmr
);
350 if ((rc
= psm_wait(sc
, PSM_STAT_RDA
)) == 0) {
351 *data
= GET8(sc
, PSM_PWDL
) | (GET8(sc
, PSM_PWDU
) << 8);
365 psm_ecmd_rd8(struct psm_softc
*sc
, uint8_t *data
, uint8_t iar
, uint8_t mode
,
368 uint8_t cmr
= PSM_CMR_DATA(mode
, PSM_L_8
, PSM_D_RD
, addr
);
369 int x
, rc
, retr
= CMD_RETRIES
;
374 if ((rc
= psm_wait(sc
, PSM_STAT_RDA
)) != 0) {
379 PUT8(sc
, PSM_IAR
, iar
);
380 PUT8(sc
, PSM_CMR
, cmr
);
384 if ((rc
= psm_wait(sc
, PSM_STAT_RDA
)) == 0) {
385 (void) GET8(sc
, PSM_PWDU
);
386 *data
= GET8(sc
, PSM_PWDL
);
399 psm_ecmd_wr8(struct psm_softc
*sc
, uint8_t data
, uint8_t iar
, uint8_t mode
,
402 uint8_t cmr
= PSM_CMR_DATA(mode
, PSM_L_8
, PSM_D_WR
, addr
);
403 int x
, rc
, retr
= CMD_RETRIES
;
408 if ((rc
= psm_wait(sc
, PSM_STAT_WBF
)) != 0) {
413 PUT8(sc
, PSM_PWDU
, 0);
414 PUT8(sc
, PSM_PWDL
, data
);
415 PUT8(sc
, PSM_IAR
, iar
);
416 PUT8(sc
, PSM_CMR
, cmr
);
420 if ((rc
= psm_wait(sc
, PSM_STAT_WBF
)) == 0) {
435 struct psm_softc
*sc
= arg
;
438 isr
= GET8(sc
, PSM_ISR
);
439 if (isr
& PSM_ISR_PO
) {
440 PUT8(sc
, PSM_ICR
, PSM_ISR_PO
);
441 sc
->sc_event
|= PSM_EV_PBUTTON
;
443 if (isr
& PSM_ISR_DK
) {
444 PUT8(sc
, PSM_ICR
, PSM_ISR_DK
);
445 sc
->sc_flags
|= PSM_FLAG_DOCKED
;
447 if (isr
& PSM_ISR_UDK
) {
448 PUT8(sc
, PSM_ICR
, PSM_ISR_UDK
);
449 sc
->sc_flags
&= ~PSM_FLAG_DOCKED
;
451 if (isr
& PSM_ISR_LIDC
) {
452 PUT8(sc
, PSM_ICR
, PSM_ISR_LIDC
);
453 sc
->sc_flags
|= PSM_FLAG_LIDCLOSED
;
454 sc
->sc_event
|= PSM_EV_LID
;
456 if (isr
& PSM_ISR_LIDO
) {
457 PUT8(sc
, PSM_ICR
, PSM_ISR_LIDO
);
458 sc
->sc_flags
&= ~PSM_FLAG_LIDCLOSED
;
459 sc
->sc_event
|= PSM_EV_LID
;
461 if (isr
& PSM_ISR_TMP
) {
462 /* Over temperature */
463 PUT8(sc
, PSM_ICR
, PSM_ISR_TMP
);
464 sc
->sc_event
|= PSM_EV_TEMP
;
466 if (isr
& PSM_ISR_BCC
) {
467 /* battery config changed */
468 PUT8(sc
, PSM_ICR
, PSM_ISR_BCC
);
469 sc
->sc_event
|= PSM_EV_BATT
;
471 if (isr
& PSM_ISR_RPD
) {
472 /* request to power down */
473 sc
->sc_event
|= PSM_EV_PBUTTON
;
476 /* wake up the thread */