psh3pwr.c revision 1.5
1/* $NetBSD: psh3pwr.c,v 1.5 2012/10/27 17:17:56 chs 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.5 2012/10/27 17:17:56 chs 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 covnerter 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, sc); 154 sc->sc_ih_pin = intc_intr_establish(SH7709_INTEVT2_IRQ1, 155 IST_EDGE, IPL_TTY, psh3pwr_intr_plug_in, sc); 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 *self) 168{ 169 struct psh3pwr_softc *sc __attribute__((__unused__)) = 170 (struct psh3pwr_softc *)self; 171 uint8_t irr0, scpdr; 172 173 irr0 = _reg_read_1(SH7709_IRR0); 174 if (!(irr0 & IRR0_IRQ0)) { 175 return 0; 176 } 177 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ0); 178 179 /* XXXX: WindowsCE sets this bit. */ 180 scpdr = _reg_read_1(SH7709_SCPDR); 181 _reg_write_1(SH7709_SCPDR, scpdr | PSH3PWR_PLUG_OUT); 182 183 DPRINTF(("%s: plug out\n", device_xname(sc->sc_dev))); 184 185 return 1; 186} 187 188static int 189psh3pwr_intr_plug_in(void *self) 190{ 191 struct psh3pwr_softc *sc __attribute__((__unused__)) = 192 (struct psh3pwr_softc *)self; 193 uint8_t irr0, scpdr; 194 195 irr0 = _reg_read_1(SH7709_IRR0); 196 if (!(irr0 & IRR0_IRQ1)) 197 return 0; 198 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ1); 199 200 /* XXXX: WindowsCE sets this bit. */ 201 scpdr = _reg_read_1(SH7709_SCPDR); 202 _reg_write_1(SH7709_SCPDR, scpdr & ~PSH3PWR_PLUG_OUT); 203 204 DPRINTF(("%s: plug in\n", device_xname(sc->sc_dev))); 205 206 return 1; 207} 208 209void 210psh3pwr_sleep(void *self) 211{ 212 /* splhigh on entry */ 213 extern void pfckbd_poll_hitachi_power(void); 214 215 uint8_t phdr; 216 217 phdr = _reg_read_1(SH7709_PHDR); 218 _reg_write_1(SH7709_PHDR, phdr & ~PSH3_GREEN_LED_ON); 219 220 pfckbd_poll_hitachi_power(); 221 222 phdr = _reg_read_1(SH7709_PHDR); 223 _reg_write_1(SH7709_PHDR, phdr | PSH3_GREEN_LED_ON); 224} 225 226static int 227psh3pwr_apm_getpower_hook(void *ctx, int type, long id, void *msg) 228{ 229 /* struct psh0pwr_softc * const sc = ctx; */ 230 int * const pval = msg; 231 int battery, state; 232 233 if (type != CONFIG_HOOK_GET) 234 return EINVAL; 235 236 switch (id) { 237 238 case CONFIG_HOOK_ACADAPTER: 239 *pval = psh3pwr_ac_is_off() ? APM_AC_OFF : APM_AC_ON; 240 return 0; 241 242 case CONFIG_HOOK_CHARGE: 243 battery = psh3pwr_get_battery(); 244 if (battery < PSH3PWR_BATTERY_CRITICAL) 245 state = APM_BATT_FLAG_CRITICAL; 246 else if (battery < PSH3PWR_BATTERY_LOW) 247 state = APM_BATT_FLAG_LOW; 248 else 249 state = APM_BATT_FLAG_HIGH; /* XXX? */ 250 *pval = state; 251 return 0; 252 253 case CONFIG_HOOK_BATTERYVAL: 254 battery = psh3pwr_get_battery(); 255 if (battery > PSH3PWR_BATTERY_FULL) 256 state = 100; 257 else 258 state = 100 * (battery - PSH3PWR_BATTERY_MIN) / 259 (PSH3PWR_BATTERY_FULL - PSH3PWR_BATTERY_MIN); 260 *pval = state; 261 return 0; 262 } 263 264 return EINVAL; 265} 266 267 268static int 269psh3pwr_get_battery(void) 270{ 271 int battery; 272 int s; 273 274 s = spltty(); 275 battery = adc_sample_channel(ADC_CHANNEL_BATTERY); 276 splx(s); 277 278 return battery; 279} 280