1 /*/* $NetBSD: tadpmu.c,v 1.6 2023/12/20 05:33:58 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Michael Lorenz <macallan@netbsd.org>
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /* a driver for the PMU found in Tadpole Viper and SPARCle laptops */
30
31 #include "opt_tadpmu.h"
32 #ifdef HAVE_TADPMU
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/device.h>
37 #include <sys/proc.h>
38 #include <sys/bus.h>
39 #include <sys/intr.h>
40 #include <sys/kthread.h>
41 #include <sys/mutex.h>
42
43 #include <dev/sysmon/sysmonvar.h>
44 #include <dev/sysmon/sysmon_taskq.h>
45
46 #include <sparc64/dev/tadpmureg.h>
47 #include <sparc64/dev/tadpmuvar.h>
48
49 #ifdef TADPMU_DEBUG
50 #define DPRINTF printf
51 #else
52 #define DPRINTF while (0) printf
53 #endif
54
55 static bus_space_tag_t tadpmu_iot;
56 static bus_space_handle_t tadpmu_hcmd;
57 static bus_space_handle_t tadpmu_hdata;
58 static struct sysmon_envsys *tadpmu_sens_sme;
59 static struct sysmon_envsys *tadpmu_acad_sme;
60 static struct sysmon_envsys *tadpmu_batt_sme;
61 static envsys_data_t tadpmu_sensors[8];
62 static uint8_t idata = 0xff;
63 static uint8_t ivalid = 0;
64 static uint8_t ev_data = 0;
65 static wchan_t tadpmu, tadpmuev;
66 static struct sysmon_pswitch tadpmu_pbutton, tadpmu_lidswitch, tadpmu_dcpower;
67 static kmutex_t tadpmu_lock, data_lock;
68 static lwp_t *tadpmu_thread;
69 static int tadpmu_dying = 0;
70
71 static inline void
tadpmu_cmd(uint8_t d)72 tadpmu_cmd(uint8_t d)
73 {
74 bus_space_write_1(tadpmu_iot, tadpmu_hcmd, 0, d);
75 }
76
77 static inline void
tadpmu_wdata(uint8_t d)78 tadpmu_wdata(uint8_t d)
79 {
80 bus_space_write_1(tadpmu_iot, tadpmu_hdata, 0, d);
81 }
82
83 static inline uint8_t
tadpmu_status(void)84 tadpmu_status(void)
85 {
86 return bus_space_read_1(tadpmu_iot, tadpmu_hcmd, 0);
87 }
88
89 static inline uint8_t
tadpmu_data(void)90 tadpmu_data(void)
91 {
92 return bus_space_read_1(tadpmu_iot, tadpmu_hdata, 0);
93 }
94
95 static void
tadpmu_flush(void)96 tadpmu_flush(void)
97 {
98 volatile uint8_t junk, d;
99 int bail = 0;
100
101 d = tadpmu_status();
102 while (d & STATUS_HAVE_DATA) {
103 junk = tadpmu_data();
104 __USE(junk);
105 delay(10);
106 bail++;
107 if (bail > 100) {
108 printf("%s: timeout waiting for data out to clear %2x\n",
109 __func__, d);
110 break;
111 }
112 d = tadpmu_status();
113 }
114 bail = 0;
115 d = tadpmu_status();
116 while (d & STATUS_SEND_DATA) {
117 bus_space_write_1(tadpmu_iot, tadpmu_hdata, 0, 0);
118 delay(10);
119 bail++;
120 if (bail > 100) {
121 printf("%s: timeout waiting for data in to clear %02x\n",
122 __func__, d);
123 break;
124 }
125 d = tadpmu_status();
126 }
127 }
128
129 static void
tadpmu_send_cmd(uint8_t cmd)130 tadpmu_send_cmd(uint8_t cmd)
131 {
132 int bail = 0;
133 uint8_t d;
134
135 ivalid = 0;
136 tadpmu_cmd(cmd);
137
138 d = tadpmu_status();
139 while ((d & STATUS_CMD_IN_PROGRESS) == 0) {
140 delay(10);
141 bail++;
142 if (bail > 100) {
143 printf("%s: timeout waiting for command to start\n",
144 __func__);
145 break;
146 }
147 d = tadpmu_status();
148 }
149 }
150
151 static uint8_t
tadpmu_recv(void)152 tadpmu_recv(void)
153 {
154 int bail = 0;
155 uint8_t d;
156
157 if (cold) {
158 d = tadpmu_status();
159 while ((d & STATUS_HAVE_DATA) == 0) {
160 delay(10);
161 bail++;
162 if (bail > 1000) {
163 printf("%s: timeout waiting for data %02x\n",
164 __func__, d);
165 break;
166 }
167 d = tadpmu_status();
168 }
169 return bus_space_read_1(tadpmu_iot, tadpmu_hdata, 0);
170 } else {
171 while (ivalid == 0)
172 tsleep(tadpmu, 0, "pmucmd", 1);
173 return idata;
174 }
175 }
176
177 static void
tadpmu_send(uint8_t v)178 tadpmu_send(uint8_t v)
179 {
180 int bail = 0;
181 uint8_t d;
182
183 d = tadpmu_status();
184 while ((d & STATUS_SEND_DATA) == 0) {
185 delay(10);
186 bail++;
187 if (bail > 1000) {
188 printf("%s: timeout waiting for PMU ready %02x\n", __func__, d);
189 break;
190 }
191 d = tadpmu_status();
192 }
193
194 tadpmu_wdata(v);
195
196 while ((d & STATUS_SEND_DATA) != 0) {
197 delay(10);
198 bail++;
199 if (bail > 1000) {
200 printf("%s: timeout waiting for accept data %02x\n", __func__, d);
201 break;
202 }
203 d = tadpmu_status();
204 }
205 }
206
207 static uint32_t
tadpmu_battery_capacity(uint8_t gstat)208 tadpmu_battery_capacity(uint8_t gstat)
209 {
210 uint8_t res;
211
212 if (gstat == GENSTAT_STATE_BATTERY_FULL) {
213 return ENVSYS_BATTERY_CAPACITY_NORMAL;
214 }
215
216 mutex_enter(&tadpmu_lock);
217 tadpmu_flush();
218 tadpmu_send_cmd(CMD_READ_VBATT);
219 res = tadpmu_recv();
220 mutex_exit(&tadpmu_lock);
221
222 if (gstat & GENSTAT_STATE_BATTERY_DISCHARGE) {
223 if (res < TADPMU_BATT_DIS_CAP_CRIT)
224 return ENVSYS_BATTERY_CAPACITY_CRITICAL;
225 if (res < TADPMU_BATT_DIS_CAP_WARN)
226 return ENVSYS_BATTERY_CAPACITY_WARNING;
227 if (res < TADPMU_BATT_DIS_CAP_LOW)
228 return ENVSYS_BATTERY_CAPACITY_LOW;
229 else
230 return ENVSYS_BATTERY_CAPACITY_NORMAL;
231 } else if (gstat == GENSTAT_STATE_BATTERY_CHARGE) {
232 if (res < TADPMU_BATT_CHG_CAP_CRIT)
233 return ENVSYS_BATTERY_CAPACITY_CRITICAL;
234 else if (res < TADPMU_BATT_CHG_CAP_WARN)
235 return ENVSYS_BATTERY_CAPACITY_WARNING;
236 else if (res < TADPMU_BATT_CHG_CAP_LOW)
237 return ENVSYS_BATTERY_CAPACITY_LOW;
238 else
239 return ENVSYS_BATTERY_CAPACITY_NORMAL;
240 } else {
241 DPRINTF("%s unknown battery state %02x\n",
242 __func__, gstat);
243 return ENVSYS_BATTERY_CAPACITY_NORMAL;
244 }
245 }
246
247 /* The data to read is calculated from the command and the units */
248 static void
tadpmu_sensors_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)249 tadpmu_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
250 {
251 int res;
252
253 if (edata->private > 0) {
254 mutex_enter(&tadpmu_lock);
255 tadpmu_flush();
256 tadpmu_send_cmd(edata->private);
257 res = tadpmu_recv();
258 mutex_exit(&tadpmu_lock);
259 if (edata->units == ENVSYS_STEMP) {
260 edata->value_cur = res * 1000000 + 273150000;
261 } else if (edata->units == ENVSYS_SVOLTS_DC) {
262 edata->value_cur = res * 100000;
263 } else if (edata->units == ENVSYS_BATTERY_CHARGE) {
264 if (res & GENSTAT_BATTERY_CHARGING)
265 edata->value_cur = ENVSYS_INDICATOR_TRUE;
266 else
267 edata->value_cur = ENVSYS_INDICATOR_FALSE;
268 } else if (edata->units == ENVSYS_BATTERY_CAPACITY) {
269 edata->value_cur = tadpmu_battery_capacity(res);
270 } else {
271 if (edata->units == ENVSYS_INDICATOR &&
272 edata->private == CMD_READ_GENSTAT) {
273 if (res & GENSTAT_DC_PRESENT)
274 edata->value_cur =
275 ENVSYS_INDICATOR_TRUE;
276 else
277 edata->value_cur =
278 ENVSYS_INDICATOR_FALSE;
279 } else {
280 edata->value_cur = res;
281 }
282 }
283 edata->state = ENVSYS_SVALID;
284 } else {
285 edata->state = ENVSYS_SINVALID;
286 }
287 }
288
289 static void
tadpmu_events(void * cookie)290 tadpmu_events(void *cookie)
291 {
292 uint8_t events, gs, vb;
293
294 while (!tadpmu_dying) {
295 mutex_enter(&tadpmu_lock);
296 tadpmu_flush();
297 tadpmu_send_cmd(CMD_READ_GENSTAT);
298 gs = tadpmu_recv();
299 tadpmu_send_cmd(CMD_READ_VBATT);
300 vb = tadpmu_recv();
301 mutex_exit(&tadpmu_lock);
302
303 mutex_enter(&data_lock);
304 events = ev_data;
305 mutex_exit(&data_lock);
306 DPRINTF("%s event %02x, status %02x/%02x\n", __func__,
307 events, gs, vb);
308
309 if (events & TADPMU_EV_PWRBUTT) {
310 mutex_enter(&data_lock);
311 ev_data &= ~TADPMU_EV_PWRBUTT;
312 mutex_exit(&data_lock);
313 sysmon_pswitch_event(&tadpmu_pbutton,
314 PSWITCH_EVENT_PRESSED);
315 }
316
317 if (events & TADPMU_EV_LID) {
318 mutex_enter(&data_lock);
319 ev_data &= ~TADPMU_EV_LID;
320 mutex_exit(&data_lock);
321 sysmon_pswitch_event(&tadpmu_lidswitch,
322 gs & GENSTAT_LID_CLOSED ?
323 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED);
324 }
325
326 if (events & TADPMU_EV_DCPOWER) {
327 mutex_enter(&data_lock);
328 ev_data &= ~TADPMU_EV_DCPOWER;
329 mutex_exit(&data_lock);
330 sysmon_pswitch_event(&tadpmu_dcpower,
331 gs & GENSTAT_DC_PRESENT ?
332 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED);
333 }
334
335 if (events & TADPMU_EV_BATTCHANGE) {
336 mutex_enter(&data_lock);
337 ev_data &= ~TADPMU_EV_BATTCHANGE;
338 mutex_exit(&data_lock);
339 if (gs == GENSTAT_STATE_BATTERY_DISCHARGE) {
340 if (vb < TADPMU_BATT_DIS_CAP_CRIT)
341 printf("Battery critical!\n");
342 else if (vb < TADPMU_BATT_DIS_CAP_WARN)
343 printf("Battery warning!\n");
344 }
345 }
346
347 if (events & TADPMU_EV_BATTCHARGED) {
348 mutex_enter(&data_lock);
349 ev_data &= ~TADPMU_EV_BATTCHARGED;
350 mutex_exit(&data_lock);
351 printf("Battery charged\n");
352 }
353
354 tsleep(tadpmuev, 0, "tadpmuev", hz);
355 }
356 kthread_exit(0);
357 }
358
359 int
tadpmu_intr(void * cookie)360 tadpmu_intr(void *cookie)
361 {
362 uint8_t s = tadpmu_status(), d;
363 if (s & STATUS_INTR) {
364 /* interrupt message */
365 d = tadpmu_data();
366 DPRINTF("%s status change %02x\n", __func__, d);
367
368 switch (d) {
369 case TADPMU_INTR_POWERBUTTON:
370 mutex_enter(&data_lock);
371 ev_data |= TADPMU_EV_PWRBUTT;;
372 mutex_exit(&data_lock);
373 break;
374 case TADPMU_INTR_LID:
375 mutex_enter(&data_lock);
376 ev_data |= TADPMU_EV_LID;
377 mutex_exit(&data_lock);
378 break;
379 case TADPMU_INTR_DCPOWER:
380 mutex_enter(&data_lock);
381 ev_data |= TADPMU_EV_DCPOWER;
382 mutex_exit(&data_lock);
383 break;
384 case TADPMU_INTR_BATTERY_STATE:
385 mutex_enter(&data_lock);
386 ev_data |= TADPMU_EV_BATTCHANGE;
387 mutex_exit(&data_lock);
388 break;
389 case TADPMU_INTR_BATTERY_CHARGED:
390 mutex_enter(&data_lock);
391 ev_data |= TADPMU_EV_BATTCHARGED;
392 mutex_exit(&data_lock);
393 break;
394 }
395 /* Report events */
396 if (ev_data)
397 wakeup(tadpmuev);
398 }
399 s = tadpmu_status();
400 if (s & STATUS_HAVE_DATA) {
401 idata = tadpmu_data();
402 ivalid = 1;
403 wakeup(tadpmu);
404 DPRINTF("%s data %02x\n", __func__, idata);
405 }
406
407 return 1;
408 }
409
410 int
tadpmu_init(bus_space_tag_t t,bus_space_handle_t hcmd,bus_space_handle_t hdata)411 tadpmu_init(bus_space_tag_t t, bus_space_handle_t hcmd, bus_space_handle_t hdata)
412 {
413 uint8_t ver;
414
415 tadpmu_iot = t;
416 tadpmu_hcmd = hcmd;
417 tadpmu_hdata = hdata;
418
419 tadpmu_flush();
420 delay(1000);
421
422 tadpmu_send_cmd(CMD_READ_VERSION);
423 ver = tadpmu_recv();
424 printf("Tadpole PMU Version 1.%d\n", ver);
425
426 tadpmu_send_cmd(CMD_SET_OPMODE);
427 tadpmu_send(OPMODE_UNIX);
428
429 #ifdef TADPMU_DEBUG
430 tadpmu_send_cmd(CMD_READ_SYSTEMP);
431 ver = tadpmu_recv();
432 printf("Temperature 0x%02x\n", ver);
433
434 tadpmu_send_cmd(CMD_READ_VBATT);
435 ver = tadpmu_recv();
436 printf("Battery voltage 0x%02x\n", ver);
437
438 tadpmu_send_cmd(CMD_READ_GENSTAT);
439 ver = tadpmu_recv();
440 printf("status 0x%02x\n", ver);
441 #endif
442
443 mutex_init(&tadpmu_lock, MUTEX_DEFAULT, IPL_NONE);
444 mutex_init(&data_lock, MUTEX_DEFAULT, IPL_HIGH);
445
446 tadpmu_sens_sme = sysmon_envsys_create();
447 tadpmu_sens_sme->sme_name = "tadpmu";
448 tadpmu_sens_sme->sme_cookie = NULL;
449 tadpmu_sens_sme->sme_refresh = tadpmu_sensors_refresh;
450
451 tadpmu_acad_sme = sysmon_envsys_create();
452 tadpmu_acad_sme->sme_name = "ac adapter";
453 tadpmu_acad_sme->sme_cookie = NULL;
454 tadpmu_acad_sme->sme_refresh = tadpmu_sensors_refresh;
455 tadpmu_acad_sme->sme_class = SME_CLASS_ACADAPTER;
456
457 tadpmu_batt_sme = sysmon_envsys_create();
458 tadpmu_batt_sme->sme_name = "battery";
459 tadpmu_batt_sme->sme_cookie = NULL;
460 tadpmu_batt_sme->sme_refresh = tadpmu_sensors_refresh;
461 tadpmu_batt_sme->sme_class = SME_CLASS_BATTERY;
462
463 tadpmu_sensors[0].state = ENVSYS_SINVALID;
464 tadpmu_sensors[0].units = ENVSYS_STEMP;
465 tadpmu_sensors[0].private = CMD_READ_SYSTEMP;
466 strcpy(tadpmu_sensors[0].desc, "systemp");
467 sysmon_envsys_sensor_attach(tadpmu_sens_sme, &tadpmu_sensors[0]);
468
469 tadpmu_sensors[1].state = ENVSYS_SINVALID;
470 tadpmu_sensors[1].units = ENVSYS_INDICATOR;
471 tadpmu_sensors[1].private = CMD_READ_FAN_EN;
472 strcpy(tadpmu_sensors[1].desc, "fan on");
473 sysmon_envsys_sensor_attach(tadpmu_sens_sme, &tadpmu_sensors[1]);
474
475 tadpmu_sensors[2].state = ENVSYS_SINVALID;
476 tadpmu_sensors[2].units = ENVSYS_INDICATOR;
477 tadpmu_sensors[2].private = CMD_READ_GENSTAT;
478 strcpy(tadpmu_sensors[2].desc, "DC power");
479 sysmon_envsys_sensor_attach(tadpmu_acad_sme, &tadpmu_sensors[2]);
480
481 tadpmu_sensors[3].state = ENVSYS_SINVALID;
482 tadpmu_sensors[3].units = ENVSYS_SVOLTS_DC;
483 tadpmu_sensors[3].private = CMD_READ_VBATT;
484 strcpy(tadpmu_sensors[3].desc, "Vbatt");
485 sysmon_envsys_sensor_attach(tadpmu_batt_sme, &tadpmu_sensors[3]);
486
487 tadpmu_sensors[4].state = ENVSYS_SINVALID;
488 tadpmu_sensors[4].units = ENVSYS_BATTERY_CAPACITY;
489 tadpmu_sensors[4].private = CMD_READ_GENSTAT;
490 /* We must provide an initial value for battery capacity */
491 tadpmu_sensors[4].value_cur = ENVSYS_BATTERY_CAPACITY_NORMAL;
492 strcpy(tadpmu_sensors[4].desc, "capacity");
493 sysmon_envsys_sensor_attach(tadpmu_batt_sme, &tadpmu_sensors[4]);
494
495 tadpmu_sensors[5].state = ENVSYS_SINVALID;
496 tadpmu_sensors[5].units = ENVSYS_BATTERY_CHARGE;
497 tadpmu_sensors[5].private = CMD_READ_GENSTAT;
498 strcpy(tadpmu_sensors[5].desc, "charging");
499 sysmon_envsys_sensor_attach(tadpmu_batt_sme, &tadpmu_sensors[5]);
500
501 #ifdef TADPMU_DEBUG
502 tadpmu_sensors[6].state = ENVSYS_SINVALID;
503 tadpmu_sensors[6].units = ENVSYS_INTEGER;
504 tadpmu_sensors[6].private = CMD_READ_GENSTAT;
505 strcpy(tadpmu_sensors[6].desc, "genstat");
506 sysmon_envsys_sensor_attach(tadpmu_sens_sme, &tadpmu_sensors[6]);
507 #endif
508
509 sysmon_envsys_register(tadpmu_sens_sme);
510 sysmon_envsys_register(tadpmu_acad_sme);
511 sysmon_envsys_register(tadpmu_batt_sme);
512
513 sysmon_task_queue_init();
514 memset(&tadpmu_pbutton, 0, sizeof(struct sysmon_pswitch));
515 tadpmu_pbutton.smpsw_name = "power";
516 tadpmu_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
517 if (sysmon_pswitch_register(&tadpmu_pbutton) != 0)
518 aprint_error(
519 "unable to register power button with sysmon\n");
520
521 memset(&tadpmu_lidswitch, 0, sizeof(struct sysmon_pswitch));
522 tadpmu_lidswitch.smpsw_name = "lid";
523 tadpmu_lidswitch.smpsw_type = PSWITCH_TYPE_LID;
524 if (sysmon_pswitch_register(&tadpmu_lidswitch) != 0)
525 aprint_error(
526 "unable to register lid switch with sysmon\n");
527
528 memset(&tadpmu_dcpower, 0, sizeof(struct sysmon_pswitch));
529 tadpmu_dcpower.smpsw_name = "AC adapter";
530 tadpmu_dcpower.smpsw_type = PSWITCH_TYPE_ACADAPTER;
531 if (sysmon_pswitch_register(&tadpmu_dcpower) != 0)
532 aprint_error(
533 "unable to register AC adapter with sysmon\n");
534
535 kthread_create(PRI_NONE, 0, curcpu(), tadpmu_events, NULL,
536 &tadpmu_thread, "tadpmu_events");
537
538 return 0;
539 }
540 #endif /* HAVE_TADPMU */
541