Home | History | Annotate | Line # | Download | only in dev
tadpmu.c revision 1.4
      1 /*/* $NetBSD: tadpmu.c,v 1.4 2018/10/14 05:08:39 macallan Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 Michael Lorenz <macallan (at) 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 Wiper and possibly 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/malloc.h>
     38 #include <sys/proc.h>
     39 #include <sys/bus.h>
     40 #include <sys/intr.h>
     41 #include <sys/kthread.h>
     42 #include <sys/mutex.h>
     43 
     44 #include <dev/sysmon/sysmonvar.h>
     45 #include <dev/sysmon/sysmon_taskq.h>
     46 
     47 #include <sparc64/dev/tadpmureg.h>
     48 #include <sparc64/dev/tadpmuvar.h>
     49 
     50 #ifdef TADPMU_DEBUG
     51 #define DPRINTF printf
     52 #else
     53 #define DPRINTF while (0) printf
     54 #endif
     55 
     56 static bus_space_tag_t tadpmu_iot;
     57 static bus_space_handle_t tadpmu_hcmd;
     58 static bus_space_handle_t tadpmu_hdata;
     59 static struct sysmon_envsys *tadpmu_sme;
     60 static envsys_data_t tadpmu_sensors[5];
     61 static uint8_t idata = 0xff;
     62 static uint8_t ivalid = 0;
     63 static wchan_t tadpmu, tadpmuev;
     64 static struct sysmon_pswitch tadpmu_pbutton, tadpmu_lidswitch;
     65 static kmutex_t tadpmu_lock;
     66 static lwp_t *tadpmu_thread;
     67 static int tadpmu_dying = 0;
     68 
     69 static inline void
     70 tadpmu_cmd(uint8_t d)
     71 {
     72 	bus_space_write_1(tadpmu_iot, tadpmu_hcmd, 0, d);
     73 }
     74 
     75 static inline void
     76 tadpmu_wdata(uint8_t d)
     77 {
     78 	bus_space_write_1(tadpmu_iot, tadpmu_hdata, 0, d);
     79 }
     80 
     81 static inline uint8_t
     82 tadpmu_status(void)
     83 {
     84 	return bus_space_read_1(tadpmu_iot, tadpmu_hcmd, 0);
     85 }
     86 
     87 static inline uint8_t
     88 tadpmu_data(void)
     89 {
     90 	return bus_space_read_1(tadpmu_iot, tadpmu_hdata, 0);
     91 }
     92 
     93 static void
     94 tadpmu_flush(void)
     95 {
     96 	volatile uint8_t junk, d;
     97 	int bail = 0;
     98 
     99 	d = tadpmu_status();
    100 	while (d & STATUS_HAVE_DATA) {
    101 		junk = tadpmu_data();
    102 		__USE(junk);
    103 		delay(10);
    104 		bail++;
    105 		if (bail > 100) {
    106 			printf("%s: timeout waiting for data out to clear %2x\n",
    107 			    __func__, d);
    108 			break;
    109 		}
    110 		d = tadpmu_status();
    111 	}
    112 	bail = 0;
    113 	d = tadpmu_status();
    114 	while (d & STATUS_SEND_DATA) {
    115 		bus_space_write_1(tadpmu_iot, tadpmu_hdata, 0, 0);
    116 		delay(10);
    117 		bail++;
    118 		if (bail > 100) {
    119 			printf("%s: timeout waiting for data in to clear %02x\n",
    120 			    __func__, d);
    121 			break;
    122 		}
    123 		d = tadpmu_status();
    124 	}
    125 }
    126 
    127 static void
    128 tadpmu_send_cmd(uint8_t cmd)
    129 {
    130 	int bail = 0;
    131 	uint8_t d;
    132 
    133 	ivalid = 0;
    134 	tadpmu_cmd(cmd);
    135 
    136 	d = tadpmu_status();
    137 	while ((d & STATUS_CMD_IN_PROGRESS) == 0) {
    138 		delay(10);
    139 		bail++;
    140 		if (bail > 100) {
    141 			printf("%s: timeout waiting for command to start\n",
    142 			    __func__);
    143 			break;
    144 		}
    145 		d = tadpmu_status();
    146 	}
    147 }
    148 
    149 static uint8_t
    150 tadpmu_recv(void)
    151 {
    152 	int bail = 0;
    153 	uint8_t d;
    154 
    155 	if (cold) {
    156 		d = tadpmu_status();
    157 		while ((d & STATUS_HAVE_DATA) == 0) {
    158 			delay(10);
    159 			bail++;
    160 			if (bail > 1000) {
    161 				printf("%s: timeout waiting for data %02x\n",
    162 				    __func__, d);
    163 				break;
    164 			}
    165 			d = tadpmu_status();
    166 		}
    167 		return bus_space_read_1(tadpmu_iot, tadpmu_hdata, 0);
    168 	} else {
    169 		while (ivalid == 0)
    170 			tsleep(tadpmu, 0, "pmucmd", 1);
    171 		return idata;
    172 	}
    173 }
    174 
    175 static void
    176 tadpmu_send(uint8_t v)
    177 {
    178 	int bail = 0;
    179 	uint8_t d;
    180 
    181 	d = tadpmu_status();
    182 	while ((d & STATUS_SEND_DATA) == 0) {
    183 		delay(10);
    184 		bail++;
    185 		if (bail > 1000) {
    186 			printf("%s: timeout waiting for PMU ready %02x\n", __func__, d);
    187 			break;
    188 		}
    189 		d = tadpmu_status();
    190 	}
    191 
    192 	tadpmu_wdata(v);
    193 
    194 	while ((d & STATUS_SEND_DATA) != 0) {
    195 		delay(10);
    196 		bail++;
    197 		if (bail > 1000) {
    198 			printf("%s: timeout waiting for accept data %02x\n", __func__, d);
    199 			break;
    200 		}
    201 		d = tadpmu_status();
    202 	}
    203 }
    204 
    205 static void
    206 tadpmu_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    207 {
    208 	int res;
    209 	if (edata->private > 0) {
    210 		mutex_enter(&tadpmu_lock);
    211 		tadpmu_flush();
    212 		tadpmu_send_cmd(edata->private);
    213 		res = tadpmu_recv();
    214 		mutex_exit(&tadpmu_lock);
    215 		if (edata->units == ENVSYS_STEMP) {
    216 			edata->value_cur = res * 1000000 + 273150000;
    217 		} else {
    218 			edata->value_cur = res;
    219 		}
    220 		edata->state = ENVSYS_SVALID;
    221 	} else {
    222 		edata->state = ENVSYS_SINVALID;
    223 	}
    224 }
    225 
    226 static void
    227 tadpmu_events(void *cookie)
    228 {
    229 	uint8_t res, ores = 0;
    230 	while (!tadpmu_dying) {
    231 		mutex_enter(&tadpmu_lock);
    232 		tadpmu_flush();
    233 		tadpmu_send_cmd(CMD_READ_GENSTAT);
    234 		res = tadpmu_recv();
    235 		mutex_exit(&tadpmu_lock);
    236 		res &= GENSTAT_LID_CLOSED;
    237 		if (res != ores) {
    238 			ores = res;
    239 			sysmon_pswitch_event(&tadpmu_lidswitch,
    240 				    (res & GENSTAT_LID_CLOSED) ?
    241 				        PSWITCH_EVENT_PRESSED :
    242 				        PSWITCH_EVENT_RELEASED);
    243 		}
    244 		tsleep(tadpmuev, 0, "tadpmuev", hz);
    245 	}
    246 	kthread_exit(0);
    247 }
    248 
    249 
    250 int
    251 tadpmu_intr(void *cookie)
    252 {
    253 	uint8_t s = tadpmu_status(), d;
    254 	if (s & STATUS_INTR) {
    255 		/* interrupt message */
    256 		d = tadpmu_data();
    257 		DPRINTF("status change %02x\n", d);
    258 		switch (d) {
    259 			case TADPMU_POWERBUTTON:
    260 				sysmon_pswitch_event(&tadpmu_pbutton,
    261 				    PSWITCH_EVENT_PRESSED);
    262 				break;
    263 			case TADPMU_LID:
    264 				/* read genstat and report lid */
    265 				wakeup(tadpmuev);
    266 				break;
    267 		}
    268 	}
    269 	s = tadpmu_status();
    270 	if (s & STATUS_HAVE_DATA) {
    271 		idata = tadpmu_data();
    272 		ivalid = 1;
    273 		wakeup(tadpmu);
    274 		DPRINTF("%s data %02x\n", __func__, idata);
    275 	}
    276 
    277 	return 1;
    278 }
    279 
    280 int
    281 tadpmu_init(bus_space_tag_t t, bus_space_handle_t hcmd, bus_space_handle_t hdata)
    282 {
    283 	int ver;
    284 
    285 	tadpmu_iot = t;
    286 	tadpmu_hcmd = hcmd;
    287 	tadpmu_hdata = hdata;
    288 
    289 	tadpmu_flush();
    290 	delay(1000);
    291 
    292 	tadpmu_send_cmd(CMD_READ_VERSION);
    293 	ver = tadpmu_recv();
    294 	printf("Tadpole PMU Version 1.%d\n", ver);
    295 
    296 	tadpmu_send_cmd(CMD_SET_OPMODE);
    297 	tadpmu_send(0x75);
    298 
    299 	tadpmu_send_cmd(CMD_READ_SYSTEMP);
    300 	ver = tadpmu_recv();
    301 	printf("Temperature %d\n", ver);
    302 
    303 	tadpmu_send_cmd(CMD_READ_VBATT);
    304 	ver = tadpmu_recv();
    305 	printf("Battery voltage %d\n", ver);
    306 
    307 	tadpmu_send_cmd(CMD_READ_GENSTAT);
    308 	ver = tadpmu_recv();
    309 	printf("status %02x\n", ver);
    310 
    311 	mutex_init(&tadpmu_lock, MUTEX_DEFAULT, IPL_NONE);
    312 
    313 	tadpmu_sme = sysmon_envsys_create();
    314 	tadpmu_sme->sme_name = "tadpmu";
    315 	tadpmu_sme->sme_cookie = NULL;
    316 	tadpmu_sme->sme_refresh = tadpmu_sensors_refresh;
    317 
    318 	tadpmu_sensors[0].units = ENVSYS_STEMP;
    319 	tadpmu_sensors[0].private = CMD_READ_SYSTEMP;
    320 	strcpy(tadpmu_sensors[0].desc, "systemp");
    321 	sysmon_envsys_sensor_attach(tadpmu_sme, &tadpmu_sensors[0]);
    322 #ifdef TADPMU_DEBUG
    323 	tadpmu_sensors[1].units = ENVSYS_INTEGER;
    324 	tadpmu_sensors[1].private = 0x17;
    325 	strcpy(tadpmu_sensors[1].desc, "reg 17");
    326 	sysmon_envsys_sensor_attach(tadpmu_sme, &tadpmu_sensors[1]);
    327 	tadpmu_sensors[2].units = ENVSYS_INTEGER;
    328 	tadpmu_sensors[2].private = 0x18;
    329 	strcpy(tadpmu_sensors[2].desc, "reg 18");
    330 	sysmon_envsys_sensor_attach(tadpmu_sme, &tadpmu_sensors[2]);
    331 	tadpmu_sensors[3].units = ENVSYS_INTEGER;
    332 	tadpmu_sensors[3].private = CMD_READ_GENSTAT;
    333 	strcpy(tadpmu_sensors[3].desc, "genstat");
    334 	sysmon_envsys_sensor_attach(tadpmu_sme, &tadpmu_sensors[3]);
    335 #if 0
    336 	tadpmu_sensors[4].units = ENVSYS_INTEGER;
    337 	tadpmu_sensors[4].private = CMD_READ_VBATT;
    338 	strcpy(tadpmu_sensors[4].desc, "Vbatt");
    339 	sysmon_envsys_sensor_attach(tadpmu_sme, &tadpmu_sensors[4]);
    340 #endif
    341 #endif
    342 	sysmon_envsys_register(tadpmu_sme);
    343 
    344 	sysmon_task_queue_init();
    345 	memset(&tadpmu_pbutton, 0, sizeof(struct sysmon_pswitch));
    346 	tadpmu_pbutton.smpsw_name = "power";
    347 	tadpmu_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
    348 	if (sysmon_pswitch_register(&tadpmu_pbutton) != 0)
    349 		aprint_error(
    350 		    "unable to register power button with sysmon\n");
    351 
    352 	tadpmu_lidswitch.smpsw_name = "lid";
    353 	tadpmu_lidswitch.smpsw_type = PSWITCH_TYPE_LID;
    354 	if (sysmon_pswitch_register(&tadpmu_lidswitch) != 0)
    355 		aprint_error(
    356 		    "unable to register lid switch with sysmon\n");
    357 
    358 	kthread_create(PRI_NONE, 0, curcpu(), tadpmu_events, NULL,
    359 	    &tadpmu_thread, "tadpmu_events");
    360 
    361 	return 0;
    362 }
    363 #endif /* HAVE_TADPMU */
    364