1/* $NetBSD: psh3pwr.c,v 1.7 2024/05/10 20:08:04 andvar Exp $ */ 2/* 3 * Copyright (c) 2005, 2007 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: psh3pwr.c,v 1.7 2024/05/10 20:08:04 andvar Exp $"); 31 32#include <sys/param.h> 33#include <sys/kernel.h> 34#include <sys/device.h> 35#include <sys/systm.h> 36#include <sys/callout.h> 37 38#include <machine/config_hook.h> 39#include <machine/platid.h> 40#include <machine/platid_mask.h> 41 42#include <dev/apm/apmbios.h> 43 44#include <sh3/exception.h> 45#include <sh3/intcreg.h> 46#include <sh3/pfcreg.h> 47 48#include <sh3/dev/adcvar.h> 49 50 51#ifdef PSH3PWR_DEBUG 52#define DPRINTF(arg) printf arg 53#else 54#define DPRINTF(arg) ((void)0) 55#endif 56 57 58/* A/D converter channels to get power stats from */ 59#define ADC_CHANNEL_BATTERY 3 60 61/* On/Off bit for Green LED. pin 7 in SH7709 GPIO port H. */ 62#define PSH3_GREEN_LED_ON 0x80 63 64/* 65 * XXXX: 66 * WindowsCE seem to be using this as a flag. 67 * pin 6 in SH7709 GPIO port SCPDR. 68 */ 69#define PSH3PWR_PLUG_OUT 0x40 70 71 72static inline int __attribute__((__always_inline__)) 73psh3pwr_ac_is_off(void) 74{ 75 76 return _reg_read_1(SH7709_SCPDR) & PSH3PWR_PLUG_OUT; 77} 78 79 80/* 81 * Empirical range of battery values. 82 * Thanks to Joseph Heenan for measurements. 83 */ 84#define PSH3PWR_BATTERY_MIN 630 85#define PSH3PWR_BATTERY_CRITICAL 635 86#define PSH3PWR_BATTERY_LOW 645 87#define PSH3PWR_BATTERY_FULL 910 /* can be slightly more */ 88 89 90struct psh3pwr_softc { 91 device_t sc_dev; 92 93 void *sc_ih_pin; 94 void *sc_ih_pout; 95}; 96 97static int psh3pwr_match(device_t, struct cfdata *, void *); 98static void psh3pwr_attach(device_t, device_t, void *); 99 100CFATTACH_DECL_NEW(psh3pwr, sizeof(struct psh3pwr_softc), 101 psh3pwr_match, psh3pwr_attach, NULL, NULL); 102 103static int psh3pwr_intr_plug_out(void *); 104static int psh3pwr_intr_plug_in(void *); 105static void psh3pwr_sleep(void *); 106static int psh3pwr_apm_getpower_hook(void *, int, long, void *); 107static int psh3pwr_get_battery(void); 108 109 110static int 111psh3pwr_match(device_t parent, struct cfdata *cfp, void *aux) 112{ 113 114 if (!platid_match(&platid, &platid_mask_MACH_HITACHI_PERSONA)) 115 return 0; 116 117 if (strcmp(cfp->cf_name, "psh3pwr") != 0) 118 return 0; 119 120 return 1; 121} 122 123 124static void 125psh3pwr_attach(device_t parent, device_t self, void *aux) 126{ 127 extern void (*__sleep_func)(void *); 128 extern void *__sleep_ctx; 129 struct psh3pwr_softc *sc = device_private(self); 130 uint8_t phdr; 131 132 sc->sc_dev = self; 133 134 /* arrange for hpcapm to call us when power status is requested */ 135 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_ACADAPTER, 136 CONFIG_HOOK_EXCLUSIVE, psh3pwr_apm_getpower_hook, sc); 137 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CHARGE, 138 CONFIG_HOOK_EXCLUSIVE, psh3pwr_apm_getpower_hook, sc); 139 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BATTERYVAL, 140 CONFIG_HOOK_EXCLUSIVE, psh3pwr_apm_getpower_hook, sc); 141 142 /* regisiter sleep function to APM */ 143 __sleep_func = psh3pwr_sleep; 144 __sleep_ctx = self; 145 146 phdr = _reg_read_1(SH7709_PHDR); 147 _reg_write_1(SH7709_PHDR, phdr | PSH3_GREEN_LED_ON); 148 149 aprint_naive("\n"); 150 aprint_normal("\n"); 151 152 sc->sc_ih_pout = intc_intr_establish(SH7709_INTEVT2_IRQ0, 153 IST_EDGE, IPL_TTY, psh3pwr_intr_plug_out, self); 154 sc->sc_ih_pin = intc_intr_establish(SH7709_INTEVT2_IRQ1, 155 IST_EDGE, IPL_TTY, psh3pwr_intr_plug_in, self); 156 157 /* XXXX: WindowsCE sets this bit. */ 158 aprint_normal_dev(self, "plug status: %s\n", 159 psh3pwr_ac_is_off() ? "out" : "in"); 160 161 if (!pmf_device_register(self, NULL, NULL)) 162 aprint_error_dev(self, "unable to establish power handler\n"); 163} 164 165 166static int 167psh3pwr_intr_plug_out(void *dev) 168{ 169 uint8_t irr0, scpdr; 170 171 irr0 = _reg_read_1(SH7709_IRR0); 172 if (!(irr0 & IRR0_IRQ0)) { 173 return 0; 174 } 175 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ0); 176 177 /* XXXX: WindowsCE sets this bit. */ 178 scpdr = _reg_read_1(SH7709_SCPDR); 179 _reg_write_1(SH7709_SCPDR, scpdr | PSH3PWR_PLUG_OUT); 180 181 DPRINTF(("%s: plug out\n", device_xname(dev))); 182 183 return 1; 184} 185 186static int 187psh3pwr_intr_plug_in(void *dev) 188{ 189 uint8_t irr0, scpdr; 190 191 irr0 = _reg_read_1(SH7709_IRR0); 192 if (!(irr0 & IRR0_IRQ1)) 193 return 0; 194 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ1); 195 196 /* XXXX: WindowsCE sets this bit. */ 197 scpdr = _reg_read_1(SH7709_SCPDR); 198 _reg_write_1(SH7709_SCPDR, scpdr & ~PSH3PWR_PLUG_OUT); 199 200 DPRINTF(("%s: plug in\n", device_xname(dev))); 201 202 return 1; 203} 204 205void 206psh3pwr_sleep(void *v) 207{ 208 /* splhigh on entry */ 209 extern void pfckbd_poll_hitachi_power(void); 210 211 uint8_t phdr; 212 213 phdr = _reg_read_1(SH7709_PHDR); 214 _reg_write_1(SH7709_PHDR, phdr & ~PSH3_GREEN_LED_ON); 215 216 pfckbd_poll_hitachi_power(); 217 218 phdr = _reg_read_1(SH7709_PHDR); 219 _reg_write_1(SH7709_PHDR, phdr | PSH3_GREEN_LED_ON); 220} 221 222static int 223psh3pwr_apm_getpower_hook(void *ctx, int type, long id, void *msg) 224{ 225 /* struct psh0pwr_softc * const sc = ctx; */ 226 int * const pval = msg; 227 int battery, state; 228 229 if (type != CONFIG_HOOK_GET) 230 return EINVAL; 231 232 switch (id) { 233 234 case CONFIG_HOOK_ACADAPTER: 235 *pval = psh3pwr_ac_is_off() ? APM_AC_OFF : APM_AC_ON; 236 return 0; 237 238 case CONFIG_HOOK_CHARGE: 239 battery = psh3pwr_get_battery(); 240 if (battery < PSH3PWR_BATTERY_CRITICAL) 241 state = APM_BATT_FLAG_CRITICAL; 242 else if (battery < PSH3PWR_BATTERY_LOW) 243 state = APM_BATT_FLAG_LOW; 244 else 245 state = APM_BATT_FLAG_HIGH; /* XXX? */ 246 *pval = state; 247 return 0; 248 249 case CONFIG_HOOK_BATTERYVAL: 250 battery = psh3pwr_get_battery(); 251 if (battery > PSH3PWR_BATTERY_FULL) 252 state = 100; 253 else 254 state = 100 * (battery - PSH3PWR_BATTERY_MIN) / 255 (PSH3PWR_BATTERY_FULL - PSH3PWR_BATTERY_MIN); 256 *pval = state; 257 return 0; 258 } 259 260 return EINVAL; 261} 262 263 264static int 265psh3pwr_get_battery(void) 266{ 267 int battery; 268 int s; 269 270 s = spltty(); 271 battery = adc_sample_channel(ADC_CHANNEL_BATTERY); 272 splx(s); 273 274 return battery; 275} 276