psm.c revision 1.1 1 /*
2 * Copyright (c) 2006 Itronix Inc.
3 * All rights reserved.
4 *
5 * Ported from Tadpole Solaris sources by Garrett D'Amore for Itronix Inc.
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 Itronix Inc. may not be used to endorse
16 * or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 * 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 ITRONIX INC. BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * 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 * Tadpole-RDI Ultrabook IIi (huxley) power management. Note that
33 * there is a lot of stuff still missing here, due in part to the confusion
34 * that exists with the NetBSD power management framework. I'm not wasting
35 * time with APM at this point, and some of sysmon seems "lacking".
36 */
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: psm.c,v 1.1 2006/07/10 17:54:09 gdamore Exp $");
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/proc.h>
43 #include <sys/kernel.h>
44 #include <sys/kthread.h>
45 #include <sys/types.h>
46 #include <sys/device.h>
47 #include <sys/poll.h>
48 #include <sys/kauth.h>
49
50 #include <machine/autoconf.h>
51 #include <machine/bus.h>
52 #include <machine/intr.h>
53
54 #include <dev/ebus/ebusreg.h>
55 #include <dev/ebus/ebusvar.h>
56
57 #include <dev/sysmon/sysmonvar.h>
58
59 #include <sparc64/dev/psmreg.h>
60
61 struct psm_softc {
62 struct device sc_dev;
63 bus_space_tag_t sc_memt;
64 bus_space_handle_t sc_memh;
65
66 int sc_event;
67 int sc_flags;
68 struct sysmon_pswitch sc_sm_pbutton;
69 struct sysmon_pswitch sc_sm_lid;
70 struct sysmon_pswitch sc_sm_ac;
71 struct evcnt sc_intrcnt;
72 struct proc *sc_thread;
73 };
74
75 #define PUT8(sc, r, v) \
76 bus_space_write_1(sc->sc_memt, sc->sc_memh, r, v)
77 #define GET8(sc, r) \
78 bus_space_read_1(sc->sc_memt, sc->sc_memh, r)
79
80 #define WAIT_DELAY 1000
81 #define WAIT_RETRIES 1000
82
83 #define RESET_DELAY 200
84 #define CMD_DELAY 10
85 #define CMD_RETRIES 5
86
87 #ifdef DEBUG
88 #define STATIC
89 #else
90 #define STATIC static
91 #endif
92
93 /* flags indicating state */
94 #define PSM_FLAG_ACPWR 0x1
95 #define PSM_FLAG_LIDCLOSED 0x2
96 #define PSM_FLAG_DOCKED 0x4
97
98 /* system events -- causes activity in the event thread */
99 #define PSM_EV_PBUTTON 0x1
100 #define PSM_EV_LID 0x2
101 #define PSM_EV_ACPWR 0x4
102 #define PSM_EV_BATT 0x8
103 #define PSM_EV_TEMP 0x10
104
105 STATIC void psm_sysmon_setup(struct psm_softc *);
106 STATIC void psm_create_event_thread(void *);
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);
115 #if 0
116 STATIC int psm_ecmd_rd16(struct psm_softc *, uint16_t *, uint8_t, uint8_t,
117 uint8_t);
118 #endif
119 STATIC int psm_ecmd_rd8(struct psm_softc *, uint8_t *, uint8_t, uint8_t,
120 uint8_t);
121 STATIC int psm_ecmd_wr8(struct psm_softc *, uint8_t, uint8_t, uint8_t,
122 uint8_t);
123 STATIC int psm_match(struct device *, struct cfdata *, void *);
124 STATIC void psm_attach(struct device *, struct device *, void *);
125
126 CFATTACH_DECL(psm, sizeof(struct psm_softc),
127 psm_match, psm_attach, NULL, NULL);
128
129
130 static int
131 psm_match(struct device *parent, struct cfdata *cf, void *aux)
132 {
133 struct ebus_attach_args *ea = aux;
134
135 if (strcmp(ea->ea_name, "psm") != 0)
136 return (0);
137 return (1);
138 }
139
140 static void
141 psm_attach(struct device *parent, struct device *self, void *aux)
142 {
143 struct psm_softc *sc = (struct psm_softc *)self;
144 struct ebus_attach_args *ea = aux;
145 bus_addr_t devaddr;
146 char *xname;
147
148 xname = sc->sc_dev.dv_xname;
149
150 sc->sc_memt = ea->ea_bustag;
151 devaddr = EBUS_ADDR_FROM_REG(&ea->ea_reg[0]);
152
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");
156 return;
157 }
158 if (psm_init(sc) != 0) {
159 printf(": unable to initialize device\n");
160 return;
161 }
162
163 printf(": UltraBook IIi power control\n");
164
165 psm_sysmon_setup(sc);
166
167 /* create the event thread */
168 kthread_create(psm_create_event_thread, sc);
169
170 /*
171 * Establish device interrupts
172 */
173 (void) bus_intr_establish(sc->sc_memt, ea->ea_intr[0], IPL_HIGH,
174 psm_intr, sc);
175 evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
176 sc->sc_dev.dv_xname, "intr");
177 }
178
179 /*
180 * Register sensors and events with sysmon.
181 */
182 void
183 psm_sysmon_setup(struct psm_softc *sc)
184 {
185 const char *xname = sc->sc_dev.dv_xname;
186
187
188 /*
189 * XXX: Register sysmon environment.
190 */
191
192 /*
193 * Register sysmon events
194 */
195 sc->sc_sm_pbutton.smpsw_name = xname;
196 sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
197 if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0)
198 printf("%s: unable to register power button\n", xname);
199
200 sc->sc_sm_lid.smpsw_name = xname;
201 sc->sc_sm_lid.smpsw_type = PSWITCH_TYPE_LID;
202 if (sysmon_pswitch_register(&sc->sc_sm_lid) != 0)
203 printf("%s: unable to register lid switch\n", xname);
204
205 sc->sc_sm_ac.smpsw_name = xname;
206 sc->sc_sm_ac.smpsw_type = PSWITCH_TYPE_ACADAPTER;
207 if (sysmon_pswitch_register(&sc->sc_sm_ac) != 0)
208 printf("%s: unable to register AC adapter\n", xname);
209 }
210
211 void
212 psm_create_event_thread(void *arg)
213 {
214 struct psm_softc *sc = arg;
215
216 if (kthread_create1(psm_event_thread, sc, &sc->sc_thread, "%s",
217 sc->sc_dev.dv_xname) != 0) {
218 printf("%s: unable to create event kthread\n",
219 sc->sc_dev.dv_xname);
220 }
221 }
222
223 void
224 psm_event_thread(void *arg)
225 {
226 struct psm_softc *sc = arg;
227 int x;
228 int event;
229 int flags;
230
231 for (;;) {
232 x = splhigh();
233 /* check for AC power. sets event if there is a change */
234 psm_poll_acpower(sc);
235
236 /* read and clear events */
237 event = sc->sc_event;
238 flags = sc->sc_flags;
239 sc->sc_event = 0;
240 splx(x);
241
242 if (event & PSM_EV_PBUTTON) {
243 sysmon_pswitch_event(&sc->sc_sm_pbutton,
244 PSWITCH_EVENT_PRESSED);
245 }
246
247 if (event & PSM_EV_LID) {
248 sysmon_pswitch_event(&sc->sc_sm_lid,
249 flags & PSM_FLAG_LIDCLOSED ?
250 PSWITCH_STATE_PRESSED : PSWITCH_STATE_RELEASED);
251 }
252
253 if (event & PSM_EV_ACPWR) {
254 sysmon_pswitch_event(&sc->sc_sm_ac,
255 flags & PSM_FLAG_ACPWR ?
256 PSWITCH_STATE_PRESSED : PSWITCH_STATE_RELEASED);
257 }
258
259 /* XXX: handle PSM_EV_TEMP */
260
261 /* one second interval between probes of power */
262 tsleep(&sc, PWAIT, "psm", hz);
263 }
264 }
265
266 int
267 psm_init(struct psm_softc *sc)
268 {
269 int x;
270 uint8_t batt;
271
272 /* clear interrupts */
273 x = splhigh();
274 PUT8(sc, PSM_ICR, 0xff);
275 splx(x);
276
277 /* enable interrupts */
278 if (psm_misc_wr(sc, PSM_MISC_IMR, PSM_IMR_ALL))
279 return (-1);
280
281 /* make sure that UPS battery is reasonable */
282 if (psm_misc_rd(sc, PSM_MISC_UPS, &batt) || (batt > PSM_MAX_BATTERIES))
283 if (psm_misc_wr(sc, PSM_MISC_UPS, batt))
284 printf("%s: cannot set UPS battery",
285 sc->sc_dev.dv_xname);
286
287 return (0);
288 }
289
290 void
291 psm_reset(struct psm_softc *sc)
292 {
293
294 PUT8(sc, PSM_MCR, PSM_MCR_RST);
295 delay(RESET_DELAY);
296 }
297
298 void
299 psm_poll_acpower(struct psm_softc *sc)
300 {
301 int flags = sc->sc_flags;
302
303 if (GET8(sc, PSM_STAT) & PSM_STAT_AC) {
304 sc->sc_flags |= PSM_FLAG_ACPWR;
305 } else {
306 sc->sc_flags &= ~PSM_FLAG_ACPWR;
307 }
308 if (flags != sc->sc_flags)
309 sc->sc_event |= PSM_EV_ACPWR;
310 }
311
312 int
313 psm_misc_rd(struct psm_softc *sc, uint8_t mreg, uint8_t *data)
314 {
315
316 return (psm_ecmd_rd8(sc, data, mreg, PSM_MODE_MISC, 0));
317 }
318
319 int
320 psm_misc_wr(struct psm_softc *sc, uint8_t mreg, uint8_t data)
321 {
322
323 return (psm_ecmd_wr8(sc, data, mreg, PSM_MODE_MISC, 0));
324 }
325
326 int
327 psm_wait(struct psm_softc *sc, uint8_t flag)
328 {
329 int retr = WAIT_RETRIES;
330
331 while (GET8(sc, PSM_STAT) & flag) {
332 if (!(retr--)) {
333 return (-1);
334 }
335 delay(WAIT_DELAY);
336 }
337 return (0);
338 }
339
340 #if 0
341 int
342 psm_ecmd_rd16(struct psm_softc *sc, uint16_t *data, uint8_t iar, uint8_t mode,
343 uint8_t addr)
344 {
345 uint8_t cmr = PSM_CMR_DATA(mode, PSM_L_16, PSM_D_RD, addr);
346 int x, rc, retr = CMD_RETRIES;
347
348 x = splhigh();
349
350 do {
351 if ((rc = psm_wait(sc, PSM_STAT_RDA)) != 0) {
352 psm_reset(sc);
353 continue;
354 }
355
356 PUT8(sc, PSM_IAR, iar);
357 PUT8(sc, PSM_CMR, cmr);
358
359 delay(CMD_DELAY);
360
361 if ((rc = psm_wait(sc, PSM_STAT_RDA)) == 0) {
362 *data = GET8(sc, PSM_PWDL) | (GET8(sc, PSM_PWDU) << 8);
363 break;
364 }
365
366 psm_reset(sc);
367
368 } while (--retr);
369
370 splx(x);
371 return (rc);
372 }
373 #endif
374
375 int
376 psm_ecmd_rd8(struct psm_softc *sc, uint8_t *data, uint8_t iar, uint8_t mode,
377 uint8_t addr)
378 {
379 uint8_t cmr = PSM_CMR_DATA(mode, PSM_L_8, PSM_D_RD, addr);
380 int x, rc, retr = CMD_RETRIES;
381
382 x = splhigh();
383
384 do {
385 if ((rc = psm_wait(sc, PSM_STAT_RDA)) != 0) {
386 psm_reset(sc);
387 continue;
388 }
389
390 PUT8(sc, PSM_IAR, iar);
391 PUT8(sc, PSM_CMR, cmr);
392
393 delay(CMD_DELAY);
394
395 if ((rc = psm_wait(sc, PSM_STAT_RDA)) == 0) {
396 (void) GET8(sc, PSM_PWDU);
397 *data = GET8(sc, PSM_PWDL);
398 break;
399 }
400
401 psm_reset(sc);
402
403 } while (--retr);
404
405 splx(x);
406 return (rc);
407 }
408
409 int
410 psm_ecmd_wr8(struct psm_softc *sc, uint8_t data, uint8_t iar, uint8_t mode,
411 uint8_t addr)
412 {
413 uint8_t cmr = PSM_CMR_DATA(mode, PSM_L_8, PSM_D_WR, addr);
414 int x, rc, retr = CMD_RETRIES;
415
416 x = splhigh();
417
418 do {
419 if ((rc = psm_wait(sc, PSM_STAT_WBF)) != 0) {
420 psm_reset(sc);
421 continue;
422 }
423
424 PUT8(sc, PSM_PWDU, 0);
425 PUT8(sc, PSM_PWDL, data);
426 PUT8(sc, PSM_IAR, iar);
427 PUT8(sc, PSM_CMR, cmr);
428
429 delay(CMD_DELAY);
430
431 if ((rc = psm_wait(sc, PSM_STAT_WBF)) == 0) {
432 break;
433 }
434
435 psm_reset(sc);
436 } while (--retr);
437
438 splx(x);
439
440 return (rc);
441 }
442
443 int
444 psm_intr(void *arg)
445 {
446 struct psm_softc *sc = arg;
447 uint8_t isr;
448
449 isr = GET8(sc, PSM_ISR);
450 if (isr & PSM_ISR_PO) {
451 PUT8(sc, PSM_ICR, PSM_ISR_PO);
452 sc->sc_event |= PSM_EV_PBUTTON;
453 }
454 if (isr & PSM_ISR_DK) {
455 PUT8(sc, PSM_ICR, PSM_ISR_DK);
456 sc->sc_flags |= PSM_FLAG_DOCKED;
457 }
458 if (isr & PSM_ISR_UDK) {
459 PUT8(sc, PSM_ICR, PSM_ISR_UDK);
460 sc->sc_flags &= ~PSM_FLAG_DOCKED;
461 }
462 if (isr & PSM_ISR_LIDC) {
463 PUT8(sc, PSM_ICR, PSM_ISR_LIDC);
464 sc->sc_flags |= PSM_FLAG_LIDCLOSED;
465 sc->sc_event |= PSM_EV_LID;
466 }
467 if (isr & PSM_ISR_LIDO) {
468 PUT8(sc, PSM_ICR, PSM_ISR_LIDO);
469 sc->sc_flags &= ~PSM_FLAG_LIDCLOSED;
470 sc->sc_event |= PSM_EV_LID;
471 }
472 if (isr & PSM_ISR_TMP) {
473 /* Over temperature */
474 PUT8(sc, PSM_ICR, PSM_ISR_TMP);
475 sc->sc_event |= PSM_EV_TEMP;
476 }
477 if (isr & PSM_ISR_BCC) {
478 /* battery config changed */
479 PUT8(sc, PSM_ICR, PSM_ISR_BCC);
480 sc->sc_event |= PSM_EV_BATT;
481 }
482 if (isr & PSM_ISR_RPD) {
483 /* request to power down */
484 sc->sc_event |= PSM_EV_PBUTTON;
485 }
486 if (sc->sc_event) {
487 /* wake up the thread */
488 wakeup(sc);
489 }
490 return (1);
491 }
492