Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/powermac/pmu.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2006 Michael Lorenz
5
* Copyright 2008 by Nathan Whitehorn
6
* All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
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
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*
29
*/
30
31
#include <sys/param.h>
32
#include <sys/systm.h>
33
#include <sys/module.h>
34
#include <sys/bus.h>
35
#include <sys/conf.h>
36
#include <sys/eventhandler.h>
37
#include <sys/kernel.h>
38
#include <sys/kthread.h>
39
#include <sys/lock.h>
40
#include <sys/mutex.h>
41
#include <sys/clock.h>
42
#include <sys/proc.h>
43
#include <sys/reboot.h>
44
#include <sys/sysctl.h>
45
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/openfirm.h>
48
#include <dev/led/led.h>
49
50
#include <machine/_inttypes.h>
51
#include <machine/bus.h>
52
#include <machine/cpu.h>
53
#include <machine/hid.h>
54
#include <machine/intr_machdep.h>
55
#include <machine/md_var.h>
56
#include <machine/pcb.h>
57
#include <machine/pio.h>
58
#include <machine/resource.h>
59
60
#include <vm/vm.h>
61
#include <vm/pmap.h>
62
63
#include <sys/rman.h>
64
65
#include <dev/adb/adb.h>
66
67
#include "clock_if.h"
68
#include "pmuvar.h"
69
#include "viareg.h"
70
#include "uninorthvar.h" /* For unin_chip_sleep()/unin_chip_wake() */
71
72
#define PMU_DEFAULTS PMU_INT_TICK | PMU_INT_ADB | \
73
PMU_INT_PCEJECT | PMU_INT_SNDBRT | \
74
PMU_INT_BATTERY | PMU_INT_ENVIRONMENT
75
76
/*
77
* Bus interface
78
*/
79
static int pmu_probe(device_t);
80
static int pmu_attach(device_t);
81
static int pmu_detach(device_t);
82
83
/*
84
* Clock interface
85
*/
86
static int pmu_gettime(device_t dev, struct timespec *ts);
87
static int pmu_settime(device_t dev, struct timespec *ts);
88
89
/*
90
* ADB Interface
91
*/
92
93
static u_int pmu_adb_send(device_t dev, u_char command_byte, int len,
94
u_char *data, u_char poll);
95
static u_int pmu_adb_autopoll(device_t dev, uint16_t mask);
96
static u_int pmu_poll(device_t dev);
97
98
/*
99
* Power interface
100
*/
101
102
static void pmu_shutdown(void *xsc, int howto);
103
static void pmu_set_sleepled(void *xsc, int onoff);
104
static int pmu_server_mode(SYSCTL_HANDLER_ARGS);
105
static int pmu_acline_state(SYSCTL_HANDLER_ARGS);
106
static int pmu_query_battery(struct pmu_softc *sc, int batt,
107
struct pmu_battstate *info);
108
static int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS);
109
static int pmu_battmon(SYSCTL_HANDLER_ARGS);
110
static void pmu_battquery_proc(void);
111
static void pmu_battery_notify(struct pmu_battstate *batt,
112
struct pmu_battstate *old);
113
114
/*
115
* List of battery-related sysctls we might ask for
116
*/
117
118
enum {
119
PMU_BATSYSCTL_PRESENT = 1 << 8,
120
PMU_BATSYSCTL_CHARGING = 2 << 8,
121
PMU_BATSYSCTL_CHARGE = 3 << 8,
122
PMU_BATSYSCTL_MAXCHARGE = 4 << 8,
123
PMU_BATSYSCTL_CURRENT = 5 << 8,
124
PMU_BATSYSCTL_VOLTAGE = 6 << 8,
125
PMU_BATSYSCTL_TIME = 7 << 8,
126
PMU_BATSYSCTL_LIFE = 8 << 8
127
};
128
129
static device_method_t pmu_methods[] = {
130
/* Device interface */
131
DEVMETHOD(device_probe, pmu_probe),
132
DEVMETHOD(device_attach, pmu_attach),
133
DEVMETHOD(device_detach, pmu_detach),
134
DEVMETHOD(device_shutdown, bus_generic_shutdown),
135
136
/* ADB bus interface */
137
DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send),
138
DEVMETHOD(adb_hb_controller_poll, pmu_poll),
139
DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
140
141
/* Clock interface */
142
DEVMETHOD(clock_gettime, pmu_gettime),
143
DEVMETHOD(clock_settime, pmu_settime),
144
145
DEVMETHOD_END
146
};
147
148
static driver_t pmu_driver = {
149
"pmu",
150
pmu_methods,
151
sizeof(struct pmu_softc),
152
};
153
154
EARLY_DRIVER_MODULE(pmu, macio, pmu_driver, 0, 0, BUS_PASS_RESOURCE);
155
DRIVER_MODULE(adb, pmu, adb_driver, 0, 0);
156
157
static int pmuextint_probe(device_t);
158
static int pmuextint_attach(device_t);
159
160
static device_method_t pmuextint_methods[] = {
161
/* Device interface */
162
DEVMETHOD(device_probe, pmuextint_probe),
163
DEVMETHOD(device_attach, pmuextint_attach),
164
{0,0}
165
};
166
167
static driver_t pmuextint_driver = {
168
"pmuextint",
169
pmuextint_methods,
170
0
171
};
172
173
EARLY_DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, 0, 0,
174
BUS_PASS_RESOURCE);
175
176
/* Make sure uhid is loaded, as it turns off some of the ADB emulation */
177
MODULE_DEPEND(pmu, usb, 1, 1, 1);
178
179
static void pmu_intr(void *arg);
180
static void pmu_in(struct pmu_softc *sc);
181
static void pmu_out(struct pmu_softc *sc);
182
static void pmu_ack_on(struct pmu_softc *sc);
183
static void pmu_ack_off(struct pmu_softc *sc);
184
static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
185
int rlen, uint8_t *out_msg);
186
static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
187
static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
188
static int pmu_intr_state(struct pmu_softc *);
189
190
/* these values shows that number of data returned after 'send' cmd is sent */
191
static signed char pm_send_cmd_type[] = {
192
-1, -1, -1, -1, -1, -1, -1, -1,
193
-1, -1, -1, -1, -1, -1, -1, -1,
194
0x01, 0x01, -1, -1, -1, -1, -1, -1,
195
0x00, 0x00, -1, -1, -1, -1, -1, 0x00,
196
-1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1,
197
0x00, -1, -1, -1, -1, -1, -1, -1,
198
0x04, 0x14, -1, 0x03, -1, -1, -1, -1,
199
0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1,
200
0x01, 0x01, -1, -1, -1, -1, -1, -1,
201
0x00, 0x00, -1, -1, 0x01, -1, -1, -1,
202
0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01,
203
0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1,
204
0x02, -1, -1, -1, -1, -1, -1, -1,
205
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1,
206
0x01, 0x01, 0x01, -1, -1, -1, -1, -1,
207
0x00, 0x00, -1, -1, -1, 0x05, 0x04, 0x04,
208
0x04, -1, 0x00, -1, -1, -1, -1, -1,
209
0x00, -1, -1, -1, -1, -1, -1, -1,
210
0x01, 0x02, -1, -1, -1, -1, -1, -1,
211
0x00, 0x00, -1, -1, -1, -1, -1, -1,
212
0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1,
213
0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1,
214
-1, -1, -1, -1, -1, -1, -1, -1,
215
-1, -1, -1, -1, -1, -1, -1, -1,
216
-1, -1, -1, -1, -1, -1, -1, -1,
217
-1, -1, -1, -1, -1, -1, -1, -1,
218
0x00, -1, -1, -1, -1, -1, -1, -1,
219
0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1,
220
-1, 0x04, 0x00, -1, -1, -1, -1, -1,
221
0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00,
222
-1, -1, -1, -1, -1, -1, -1, -1,
223
-1, -1, -1, -1, -1, -1, -1, -1
224
};
225
226
/* these values shows that number of data returned after 'receive' cmd is sent */
227
static signed char pm_receive_cmd_type[] = {
228
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229
-1, -1, -1, -1, -1, -1, -1, -1,
230
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231
0x02, 0x02, -1, -1, -1, -1, -1, 0x00,
232
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233
-1, -1, -1, -1, -1, -1, -1, -1,
234
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235
0x05, 0x15, -1, 0x02, -1, -1, -1, -1,
236
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237
0x02, 0x02, -1, -1, -1, -1, -1, -1,
238
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239
0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1,
240
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241
0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1,
242
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243
-1, -1, -1, -1, -1, 0x01, 0x01, 0x01,
244
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245
0x06, -1, -1, -1, -1, -1, -1, -1,
246
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247
0x02, 0x02, -1, -1, -1, -1, -1, -1,
248
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249
0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1,
250
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251
-1, -1, -1, -1, -1, -1, -1, -1,
252
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253
-1, -1, -1, -1, -1, -1, -1, -1,
254
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255
0x02, 0x02, -1, -1, 0x02, -1, -1, -1,
256
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
257
-1, -1, 0x02, -1, -1, -1, -1, 0x00,
258
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259
-1, -1, -1, -1, -1, -1, -1, -1,
260
};
261
262
static int pmu_battmon_enabled = 1;
263
static struct proc *pmubattproc;
264
static struct kproc_desc pmu_batt_kp = {
265
"pmu_batt",
266
pmu_battquery_proc,
267
&pmubattproc
268
};
269
270
/* We only have one of each device, so globals are safe */
271
static device_t pmu = NULL;
272
static device_t pmu_extint = NULL;
273
274
static int
275
pmuextint_probe(device_t dev)
276
{
277
const char *type = ofw_bus_get_type(dev);
278
279
if (strcmp(type, "extint-gpio1") != 0)
280
return (ENXIO);
281
282
device_set_desc(dev, "Apple PMU99 External Interrupt");
283
return (0);
284
}
285
286
static int
287
pmu_probe(device_t dev)
288
{
289
const char *type = ofw_bus_get_type(dev);
290
291
if (strcmp(type, "via-pmu") != 0)
292
return (ENXIO);
293
294
device_set_desc(dev, "Apple PMU99 Controller");
295
return (0);
296
}
297
298
static int
299
setup_pmu_intr(device_t dev, device_t extint)
300
{
301
struct pmu_softc *sc;
302
sc = device_get_softc(dev);
303
304
sc->sc_irqrid = 0;
305
sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
306
RF_ACTIVE);
307
if (sc->sc_irq == NULL) {
308
device_printf(dev, "could not allocate interrupt\n");
309
return (ENXIO);
310
}
311
312
if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
313
| INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
314
device_printf(dev, "could not setup interrupt\n");
315
bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
316
sc->sc_irq);
317
return (ENXIO);
318
}
319
320
return (0);
321
}
322
323
static int
324
pmuextint_attach(device_t dev)
325
{
326
pmu_extint = dev;
327
if (pmu)
328
return (setup_pmu_intr(pmu,dev));
329
330
return (0);
331
}
332
333
static int
334
pmu_attach(device_t dev)
335
{
336
struct pmu_softc *sc;
337
338
int i;
339
uint8_t reg;
340
uint8_t cmd[2] = {2, 0};
341
uint8_t resp[16];
342
phandle_t node,child;
343
struct sysctl_ctx_list *ctx;
344
struct sysctl_oid *tree;
345
346
sc = device_get_softc(dev);
347
sc->sc_dev = dev;
348
349
sc->sc_memrid = 0;
350
sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
351
&sc->sc_memrid, RF_ACTIVE);
352
353
mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
354
355
if (sc->sc_memr == NULL) {
356
device_printf(dev, "Could not alloc mem resource!\n");
357
return (ENXIO);
358
}
359
360
/*
361
* Our interrupt is attached to a GPIO pin. Depending on probe order,
362
* we may not have found it yet. If we haven't, it will find us, and
363
* attach our interrupt then.
364
*/
365
pmu = dev;
366
if (pmu_extint != NULL) {
367
if (setup_pmu_intr(dev,pmu_extint) != 0)
368
return (ENXIO);
369
}
370
371
sc->sc_autopoll = 0;
372
sc->sc_batteries = 0;
373
sc->adb_bus = NULL;
374
sc->sc_leddev = NULL;
375
376
/* Init PMU */
377
378
pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4);
379
pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3);
380
381
reg = PMU_DEFAULTS;
382
pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
383
384
pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */
385
386
pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
387
pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp);
388
389
/* Initialize child buses (ADB) */
390
node = ofw_bus_get_node(dev);
391
392
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
393
char name[32];
394
395
memset(name, 0, sizeof(name));
396
OF_getprop(child, "name", name, sizeof(name));
397
398
if (bootverbose)
399
device_printf(dev, "PMU child <%s>\n",name);
400
401
if (strncmp(name, "adb", 4) == 0) {
402
sc->adb_bus = device_add_child(dev,"adb",DEVICE_UNIT_ANY);
403
}
404
405
if (strncmp(name, "power-mgt", 9) == 0) {
406
uint32_t prim_info[9];
407
408
if (OF_getprop(child, "prim-info", prim_info,
409
sizeof(prim_info)) >= 7)
410
sc->sc_batteries = (prim_info[6] >> 16) & 0xff;
411
412
if (bootverbose && sc->sc_batteries > 0)
413
device_printf(dev, "%d batteries detected\n",
414
sc->sc_batteries);
415
}
416
}
417
418
/*
419
* Set up sysctls
420
*/
421
422
ctx = device_get_sysctl_ctx(dev);
423
tree = device_get_sysctl_tree(dev);
424
425
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
426
"server_mode", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
427
pmu_server_mode, "I", "Enable reboot after power failure");
428
429
if (sc->sc_batteries > 0) {
430
struct sysctl_oid *oid, *battroot;
431
char battnum[2];
432
433
/* Only start the battery monitor if we have a battery. */
434
kproc_start(&pmu_batt_kp);
435
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
436
"monitor_batteries",
437
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
438
pmu_battmon, "I", "Post battery events to devd");
439
440
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
441
"acline", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
442
0, pmu_acline_state, "I", "AC Line Status");
443
444
battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
445
"batteries", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
446
"Battery Information");
447
448
for (i = 0; i < sc->sc_batteries; i++) {
449
battnum[0] = i + '0';
450
battnum[1] = '\0';
451
452
oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot),
453
OID_AUTO, battnum, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
454
"Battery Information");
455
456
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
457
"present",
458
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
459
PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl,
460
"I", "Battery present");
461
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
462
"charging",
463
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
464
PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl,
465
"I", "Battery charging");
466
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
467
"charge",
468
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
469
PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl,
470
"I", "Battery charge (mAh)");
471
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
472
"maxcharge",
473
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
474
PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl,
475
"I", "Maximum battery capacity (mAh)");
476
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
477
"rate",
478
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
479
PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl,
480
"I", "Battery discharge rate (mA)");
481
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
482
"voltage",
483
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
484
PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl,
485
"I", "Battery voltage (mV)");
486
487
/* Knobs for mental compatibility with ACPI */
488
489
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
490
"time",
491
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
492
PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl,
493
"I", "Time Remaining (minutes)");
494
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
495
"life",
496
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
497
PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl,
498
"I", "Capacity remaining (percent)");
499
}
500
}
501
502
/*
503
* Set up LED interface
504
*/
505
506
sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled");
507
508
/*
509
* Register RTC
510
*/
511
512
clock_register(dev, 1000);
513
514
/*
515
* Register power control handler
516
*/
517
EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc,
518
SHUTDOWN_PRI_LAST);
519
520
bus_attach_children(dev);
521
return (0);
522
}
523
524
static int
525
pmu_detach(device_t dev)
526
{
527
struct pmu_softc *sc;
528
int error;
529
530
error = bus_generic_detach(dev);
531
if (error != 0)
532
return (error);
533
534
sc = device_get_softc(dev);
535
536
if (sc->sc_leddev != NULL)
537
led_destroy(sc->sc_leddev);
538
539
bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
540
bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
541
bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
542
mtx_destroy(&sc->sc_mutex);
543
544
return (0);
545
}
546
547
static uint8_t
548
pmu_read_reg(struct pmu_softc *sc, u_int offset)
549
{
550
return (bus_read_1(sc->sc_memr, offset));
551
}
552
553
static void
554
pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value)
555
{
556
bus_write_1(sc->sc_memr, offset, value);
557
}
558
559
static int
560
pmu_send_byte(struct pmu_softc *sc, uint8_t data)
561
{
562
563
pmu_out(sc);
564
pmu_write_reg(sc, vSR, data);
565
pmu_ack_off(sc);
566
/* wait for intr to come up */
567
/* XXX should add a timeout and bail if it expires */
568
do {} while (pmu_intr_state(sc) == 0);
569
pmu_ack_on(sc);
570
do {} while (pmu_intr_state(sc));
571
pmu_ack_on(sc);
572
return 0;
573
}
574
575
static inline int
576
pmu_read_byte(struct pmu_softc *sc, uint8_t *data)
577
{
578
pmu_in(sc);
579
(void)pmu_read_reg(sc, vSR);
580
pmu_ack_off(sc);
581
/* wait for intr to come up */
582
do {} while (pmu_intr_state(sc) == 0);
583
pmu_ack_on(sc);
584
do {} while (pmu_intr_state(sc));
585
*data = pmu_read_reg(sc, vSR);
586
return 0;
587
}
588
589
static int
590
pmu_intr_state(struct pmu_softc *sc)
591
{
592
return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
593
}
594
595
static int
596
pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
597
uint8_t *out_msg)
598
{
599
struct pmu_softc *sc = cookie;
600
int i, rcv_len = -1;
601
uint8_t out_len, intreg;
602
603
intreg = pmu_read_reg(sc, vIER);
604
intreg &= 0x10;
605
pmu_write_reg(sc, vIER, intreg);
606
607
/* wait idle */
608
do {} while (pmu_intr_state(sc));
609
610
/* send command */
611
pmu_send_byte(sc, cmd);
612
613
/* send length if necessary */
614
if (pm_send_cmd_type[cmd] < 0) {
615
pmu_send_byte(sc, length);
616
}
617
618
for (i = 0; i < length; i++) {
619
pmu_send_byte(sc, in_msg[i]);
620
}
621
622
/* see if there's data to read */
623
rcv_len = pm_receive_cmd_type[cmd];
624
if (rcv_len == 0)
625
goto done;
626
627
/* read command */
628
if (rcv_len == 1) {
629
pmu_read_byte(sc, out_msg);
630
goto done;
631
} else
632
out_msg[0] = cmd;
633
if (rcv_len < 0) {
634
pmu_read_byte(sc, &out_len);
635
rcv_len = out_len + 1;
636
}
637
for (i = 1; i < min(rcv_len, rlen); i++)
638
pmu_read_byte(sc, &out_msg[i]);
639
640
done:
641
pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
642
643
return rcv_len;
644
}
645
646
static u_int
647
pmu_poll(device_t dev)
648
{
649
pmu_intr(dev);
650
return (0);
651
}
652
653
static void
654
pmu_in(struct pmu_softc *sc)
655
{
656
uint8_t reg;
657
658
reg = pmu_read_reg(sc, vACR);
659
reg &= ~vSR_OUT;
660
reg |= 0x0c;
661
pmu_write_reg(sc, vACR, reg);
662
}
663
664
static void
665
pmu_out(struct pmu_softc *sc)
666
{
667
uint8_t reg;
668
669
reg = pmu_read_reg(sc, vACR);
670
reg |= vSR_OUT;
671
reg |= 0x0c;
672
pmu_write_reg(sc, vACR, reg);
673
}
674
675
static void
676
pmu_ack_off(struct pmu_softc *sc)
677
{
678
uint8_t reg;
679
680
reg = pmu_read_reg(sc, vBufB);
681
reg &= ~vPB4;
682
pmu_write_reg(sc, vBufB, reg);
683
}
684
685
static void
686
pmu_ack_on(struct pmu_softc *sc)
687
{
688
uint8_t reg;
689
690
reg = pmu_read_reg(sc, vBufB);
691
reg |= vPB4;
692
pmu_write_reg(sc, vBufB, reg);
693
}
694
695
static void
696
pmu_intr(void *arg)
697
{
698
device_t dev;
699
struct pmu_softc *sc;
700
701
unsigned int len;
702
uint8_t resp[16];
703
uint8_t junk[16];
704
705
dev = (device_t)arg;
706
sc = device_get_softc(dev);
707
708
mtx_lock(&sc->sc_mutex);
709
710
pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */
711
len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
712
713
mtx_unlock(&sc->sc_mutex);
714
715
if ((len < 1) || (resp[1] == 0)) {
716
return;
717
}
718
719
if (resp[1] & PMU_INT_ADB) {
720
/*
721
* the PMU will turn off autopolling after each command that
722
* it did not issue, so we assume any but TALK R0 is ours and
723
* re-enable autopoll here whenever we receive an ACK for a
724
* non TR0 command.
725
*/
726
mtx_lock(&sc->sc_mutex);
727
728
if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
729
if (sc->sc_autopoll) {
730
uint8_t cmd[] = {0, PMU_SET_POLL_MASK,
731
(sc->sc_autopoll >> 8) & 0xff,
732
sc->sc_autopoll & 0xff};
733
734
pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
735
}
736
}
737
738
mtx_unlock(&sc->sc_mutex);
739
740
adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
741
len - 3,&resp[3]);
742
}
743
if (resp[1] & PMU_INT_ENVIRONMENT) {
744
/* if the lid was just closed, notify devd. */
745
if ((resp[2] & PMU_ENV_LID_CLOSED) && (!sc->lid_closed)) {
746
sc->lid_closed = 1;
747
devctl_notify("PMU", "lid", "close", NULL);
748
}
749
else if (!(resp[2] & PMU_ENV_LID_CLOSED) && (sc->lid_closed)) {
750
/* if the lid was just opened, notify devd. */
751
sc->lid_closed = 0;
752
devctl_notify("PMU", "lid", "open", NULL);
753
}
754
if (resp[2] & PMU_ENV_POWER)
755
devctl_notify("PMU", "Button", "pressed", NULL);
756
}
757
}
758
759
static u_int
760
pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
761
u_char poll)
762
{
763
struct pmu_softc *sc = device_get_softc(dev);
764
int i;
765
uint8_t packet[16], resp[16];
766
767
/* construct an ADB command packet and send it */
768
769
packet[0] = command_byte;
770
771
packet[1] = 0;
772
packet[2] = len;
773
for (i = 0; i < len; i++)
774
packet[i + 3] = data[i];
775
776
mtx_lock(&sc->sc_mutex);
777
pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
778
mtx_unlock(&sc->sc_mutex);
779
780
if (poll)
781
pmu_poll(dev);
782
783
return 0;
784
}
785
786
static u_int
787
pmu_adb_autopoll(device_t dev, uint16_t mask)
788
{
789
struct pmu_softc *sc = device_get_softc(dev);
790
791
/* magical incantation to re-enable autopolling */
792
uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
793
uint8_t resp[16];
794
795
mtx_lock(&sc->sc_mutex);
796
797
if (sc->sc_autopoll == mask) {
798
mtx_unlock(&sc->sc_mutex);
799
return 0;
800
}
801
802
sc->sc_autopoll = mask & 0xffff;
803
804
if (mask)
805
pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
806
else
807
pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
808
809
mtx_unlock(&sc->sc_mutex);
810
811
return 0;
812
}
813
814
static void
815
pmu_shutdown(void *xsc, int howto)
816
{
817
struct pmu_softc *sc = xsc;
818
uint8_t cmd[] = {'M', 'A', 'T', 'T'};
819
820
if ((howto & RB_POWEROFF) != 0)
821
pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL);
822
else if ((howto & RB_HALT) == 0)
823
pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL);
824
else
825
return;
826
827
for (;;);
828
}
829
830
static void
831
pmu_set_sleepled(void *xsc, int onoff)
832
{
833
struct pmu_softc *sc = xsc;
834
uint8_t cmd[] = {4, 0, 0};
835
836
cmd[2] = onoff;
837
838
mtx_lock(&sc->sc_mutex);
839
pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL);
840
mtx_unlock(&sc->sc_mutex);
841
}
842
843
static int
844
pmu_server_mode(SYSCTL_HANDLER_ARGS)
845
{
846
struct pmu_softc *sc = arg1;
847
848
u_int server_mode = 0;
849
uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS};
850
uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT};
851
uint8_t resp[3];
852
int error, len;
853
854
mtx_lock(&sc->sc_mutex);
855
len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp);
856
mtx_unlock(&sc->sc_mutex);
857
858
if (len == 3)
859
server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0;
860
861
error = sysctl_handle_int(oidp, &server_mode, 0, req);
862
863
if (len != 3)
864
return (EINVAL);
865
866
if (error || !req->newptr)
867
return (error);
868
869
if (server_mode == 1)
870
setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS;
871
else if (server_mode == 0)
872
setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS;
873
else
874
return (EINVAL);
875
876
setcmd[1] = resp[1];
877
878
mtx_lock(&sc->sc_mutex);
879
pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp);
880
mtx_unlock(&sc->sc_mutex);
881
882
return (0);
883
}
884
885
static int
886
pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info)
887
{
888
uint8_t reg;
889
uint8_t resp[16];
890
int len;
891
892
reg = batt + 1;
893
894
mtx_lock(&sc->sc_mutex);
895
len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, &reg, 16, resp);
896
mtx_unlock(&sc->sc_mutex);
897
898
if (len < 3)
899
return (-1);
900
901
/* All PMU battery info replies share a common header:
902
* Byte 1 Payload Format
903
* Byte 2 Battery Flags
904
*/
905
906
info->state = resp[2];
907
908
switch (resp[1]) {
909
case 3:
910
case 4:
911
/*
912
* Formats 3 and 4 appear to be the same:
913
* Byte 3 Charge
914
* Byte 4 Max Charge
915
* Byte 5 Current
916
* Byte 6 Voltage
917
*/
918
919
info->charge = resp[3];
920
info->maxcharge = resp[4];
921
/* Current can be positive or negative */
922
info->current = (int8_t)resp[5];
923
info->voltage = resp[6];
924
break;
925
case 5:
926
/*
927
* Formats 5 is a wider version of formats 3 and 4
928
* Byte 3-4 Charge
929
* Byte 5-6 Max Charge
930
* Byte 7-8 Current
931
* Byte 9-10 Voltage
932
*/
933
934
info->charge = (resp[3] << 8) | resp[4];
935
info->maxcharge = (resp[5] << 8) | resp[6];
936
/* Current can be positive or negative */
937
info->current = (int16_t)((resp[7] << 8) | resp[8]);
938
info->voltage = (resp[9] << 8) | resp[10];
939
break;
940
default:
941
device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n",
942
resp[1]);
943
return (-1);
944
}
945
946
return (0);
947
}
948
949
static void
950
pmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old)
951
{
952
char notify_buf[16];
953
int new_acline, old_acline;
954
955
new_acline = (batt->state & PMU_PWR_AC_PRESENT) ? 1 : 0;
956
old_acline = (old->state & PMU_PWR_AC_PRESENT) ? 1 : 0;
957
958
if (new_acline != old_acline) {
959
snprintf(notify_buf, sizeof(notify_buf),
960
"notify=0x%02x", new_acline);
961
devctl_notify("PMU", "POWER", "ACLINE", notify_buf);
962
}
963
}
964
965
static void
966
pmu_battquery_proc(void)
967
{
968
struct pmu_softc *sc;
969
struct pmu_battstate batt;
970
struct pmu_battstate cur_batt;
971
int error;
972
973
sc = device_get_softc(pmu);
974
975
bzero(&cur_batt, sizeof(cur_batt));
976
while (1) {
977
kproc_suspend_check(curproc);
978
error = pmu_query_battery(sc, 0, &batt);
979
if (error == 0) {
980
pmu_battery_notify(&batt, &cur_batt);
981
cur_batt = batt;
982
}
983
pause("pmu_batt", hz);
984
}
985
}
986
987
static int
988
pmu_battmon(SYSCTL_HANDLER_ARGS)
989
{
990
int error, result;
991
992
result = pmu_battmon_enabled;
993
994
error = sysctl_handle_int(oidp, &result, 0, req);
995
996
if (error || !req->newptr)
997
return (error);
998
999
if (!result && pmu_battmon_enabled)
1000
error = kproc_suspend(pmubattproc, hz);
1001
else if (result && pmu_battmon_enabled == 0)
1002
error = kproc_resume(pmubattproc);
1003
pmu_battmon_enabled = (result != 0);
1004
1005
return (error);
1006
}
1007
1008
static int
1009
pmu_acline_state(SYSCTL_HANDLER_ARGS)
1010
{
1011
struct pmu_softc *sc;
1012
struct pmu_battstate batt;
1013
int error, result;
1014
1015
sc = arg1;
1016
1017
/* The PMU treats the AC line status as a property of the battery */
1018
error = pmu_query_battery(sc, 0, &batt);
1019
1020
if (error != 0)
1021
return (error);
1022
1023
result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0;
1024
error = sysctl_handle_int(oidp, &result, 0, req);
1025
1026
return (error);
1027
}
1028
1029
static int
1030
pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS)
1031
{
1032
struct pmu_softc *sc;
1033
struct pmu_battstate batt;
1034
int error, result;
1035
1036
sc = arg1;
1037
1038
error = pmu_query_battery(sc, arg2 & 0x00ff, &batt);
1039
1040
if (error != 0)
1041
return (error);
1042
1043
switch (arg2 & 0xff00) {
1044
case PMU_BATSYSCTL_PRESENT:
1045
result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0;
1046
break;
1047
case PMU_BATSYSCTL_CHARGING:
1048
result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0;
1049
break;
1050
case PMU_BATSYSCTL_CHARGE:
1051
result = batt.charge;
1052
break;
1053
case PMU_BATSYSCTL_MAXCHARGE:
1054
result = batt.maxcharge;
1055
break;
1056
case PMU_BATSYSCTL_CURRENT:
1057
result = batt.current;
1058
break;
1059
case PMU_BATSYSCTL_VOLTAGE:
1060
result = batt.voltage;
1061
break;
1062
case PMU_BATSYSCTL_TIME:
1063
/* Time remaining until full charge/discharge, in minutes */
1064
1065
if (batt.current >= 0)
1066
result = (batt.maxcharge - batt.charge) /* mAh */ * 60
1067
/ batt.current /* mA */;
1068
else
1069
result = (batt.charge /* mAh */ * 60)
1070
/ (-batt.current /* mA */);
1071
break;
1072
case PMU_BATSYSCTL_LIFE:
1073
/* Battery charge fraction, in percent */
1074
result = (batt.charge * 100) / batt.maxcharge;
1075
break;
1076
default:
1077
/* This should never happen */
1078
result = -1;
1079
}
1080
1081
error = sysctl_handle_int(oidp, &result, 0, req);
1082
1083
return (error);
1084
}
1085
1086
#define DIFF19041970 2082844800
1087
1088
static int
1089
pmu_gettime(device_t dev, struct timespec *ts)
1090
{
1091
struct pmu_softc *sc = device_get_softc(dev);
1092
uint8_t resp[16];
1093
uint32_t sec;
1094
1095
mtx_lock(&sc->sc_mutex);
1096
pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp);
1097
mtx_unlock(&sc->sc_mutex);
1098
1099
memcpy(&sec, &resp[1], 4);
1100
ts->tv_sec = sec - DIFF19041970;
1101
ts->tv_nsec = 0;
1102
1103
return (0);
1104
}
1105
1106
static int
1107
pmu_settime(device_t dev, struct timespec *ts)
1108
{
1109
struct pmu_softc *sc = device_get_softc(dev);
1110
uint32_t sec;
1111
1112
sec = ts->tv_sec + DIFF19041970;
1113
1114
mtx_lock(&sc->sc_mutex);
1115
pmu_send(sc, PMU_SET_RTC, sizeof(sec), (uint8_t *)&sec, 0, NULL);
1116
mtx_unlock(&sc->sc_mutex);
1117
1118
return (0);
1119
}
1120
1121
int
1122
pmu_set_speed(int low_speed)
1123
{
1124
struct pmu_softc *sc;
1125
uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0};
1126
uint8_t resp[16];
1127
1128
sc = device_get_softc(pmu);
1129
pmu_write_reg(sc, vIER, 0x10);
1130
spinlock_enter();
1131
mtdec(0x7fffffff);
1132
mb();
1133
mtdec(0x7fffffff);
1134
1135
sleepcmd[4] = low_speed;
1136
pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp);
1137
unin_chip_sleep(NULL, 1);
1138
platform_sleep();
1139
unin_chip_wake(NULL);
1140
1141
mtdec(1); /* Force a decrementer exception */
1142
spinlock_exit();
1143
pmu_write_reg(sc, vIER, 0x90);
1144
1145
return (0);
1146
}
1147
1148