xref: /netbsd-src/sys/arch/sparc64/dev/tadpmu.c (revision 1dc652ef5a0bffbd0917f95e0797bad8c6fc8efd)
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