psh3pwr.c revision 1.2
1/* $NetBSD: psh3pwr.c,v 1.2 2007/09/24 18:08:53 kiyohara 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.2 2007/09/24 18:08:53 kiyohara 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 struct device sc_dev; 92 93 void *sc_ih_pin; 94 void *sc_ih_pout; 95}; 96 97static int psh3pwr_match(struct device *, struct cfdata *, void *); 98static void psh3pwr_attach(struct device *, struct device *, void *); 99 100CFATTACH_DECL(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(struct device *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(struct device *parent, struct device *self, void *aux) 126{ 127 extern void (*__sleep_func)(void *); 128 extern void *__sleep_ctx; 129 struct psh3pwr_softc *sc = (struct psh3pwr_softc *)self; 130 uint8_t phdr; 131 132 /* arrange for hpcapm to call us when power status is requested */ 133 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_ACADAPTER, 134 CONFIG_HOOK_EXCLUSIVE, psh3pwr_apm_getpower_hook, sc); 135 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CHARGE, 136 CONFIG_HOOK_EXCLUSIVE, psh3pwr_apm_getpower_hook, sc); 137 config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BATTERYVAL, 138 CONFIG_HOOK_EXCLUSIVE, psh3pwr_apm_getpower_hook, sc); 139 140 /* regisiter sleep function to APM */ 141 __sleep_func = psh3pwr_sleep; 142 __sleep_ctx = self; 143 144 phdr = _reg_read_1(SH7709_PHDR); 145 _reg_write_1(SH7709_PHDR, phdr | PSH3_GREEN_LED_ON); 146 147 printf("\n"); 148 149 sc->sc_ih_pout = intc_intr_establish(SH7709_INTEVT2_IRQ0, 150 IST_EDGE, IPL_TTY, psh3pwr_intr_plug_out, sc); 151 sc->sc_ih_pin = intc_intr_establish(SH7709_INTEVT2_IRQ1, 152 IST_EDGE, IPL_TTY, psh3pwr_intr_plug_in, sc); 153 154 /* XXXX: WindowsCE sets this bit. */ 155 printf("%s: plug status: %s\n", 156 device_xname(&sc->sc_dev), psh3pwr_ac_is_off() ? "out" : "in"); 157} 158 159 160static int 161psh3pwr_intr_plug_out(void *self) 162{ 163 struct psh3pwr_softc *sc __attribute__((__unused__)) = 164 (struct psh3pwr_softc *)self; 165 uint8_t irr0, scpdr; 166 167 irr0 = _reg_read_1(SH7709_IRR0); 168 if (!(irr0 & IRR0_IRQ0)) { 169 return 0; 170 } 171 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ0); 172 173 /* XXXX: WindowsCE sets this bit. */ 174 scpdr = _reg_read_1(SH7709_SCPDR); 175 _reg_write_1(SH7709_SCPDR, scpdr | PSH3PWR_PLUG_OUT); 176 177 DPRINTF(("%s: plug out\n", device_xname(&sc->sc_dev))); 178 179 return 1; 180} 181 182static int 183psh3pwr_intr_plug_in(void *self) 184{ 185 struct psh3pwr_softc *sc __attribute__((__unused__)) = 186 (struct psh3pwr_softc *)self; 187 uint8_t irr0, scpdr; 188 189 irr0 = _reg_read_1(SH7709_IRR0); 190 if (!(irr0 & IRR0_IRQ1)) 191 return 0; 192 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ1); 193 194 /* XXXX: WindowsCE sets this bit. */ 195 scpdr = _reg_read_1(SH7709_SCPDR); 196 _reg_write_1(SH7709_SCPDR, scpdr & ~PSH3PWR_PLUG_OUT); 197 198 DPRINTF(("%s: plug in\n", device_xname(&sc->sc_dev))); 199 200 return 1; 201} 202 203void 204psh3pwr_sleep(void *self) 205{ 206 /* splhigh on entry */ 207 extern void pfckbd_poll_hitachi_power(void); 208 209 uint8_t phdr; 210 211 phdr = _reg_read_1(SH7709_PHDR); 212 _reg_write_1(SH7709_PHDR, phdr & ~PSH3_GREEN_LED_ON); 213 214 pfckbd_poll_hitachi_power(); 215 216 phdr = _reg_read_1(SH7709_PHDR); 217 _reg_write_1(SH7709_PHDR, phdr | PSH3_GREEN_LED_ON); 218} 219 220static int 221psh3pwr_apm_getpower_hook(void *ctx, int type, long id, void *msg) 222{ 223 /* struct psh0pwr_softc * const sc = ctx; */ 224 int * const pval = msg; 225 int battery, state; 226 227 if (type != CONFIG_HOOK_GET) 228 return EINVAL; 229 230 switch (id) { 231 232 case CONFIG_HOOK_ACADAPTER: 233 *pval = psh3pwr_ac_is_off() ? APM_AC_OFF : APM_AC_ON; 234 return 0; 235 236 case CONFIG_HOOK_CHARGE: 237 battery = psh3pwr_get_battery(); 238 if (battery < PSH3PWR_BATTERY_CRITICAL) 239 state = APM_BATT_FLAG_CRITICAL; 240 else if (battery < PSH3PWR_BATTERY_LOW) 241 state = APM_BATT_FLAG_LOW; 242 else 243 state = APM_BATT_FLAG_HIGH; /* XXX? */ 244 *pval = state; 245 return 0; 246 247 case CONFIG_HOOK_BATTERYVAL: 248 battery = psh3pwr_get_battery(); 249 if (battery > PSH3PWR_BATTERY_FULL) 250 state = 100; 251 else 252 state = 100 * (battery - PSH3PWR_BATTERY_MIN) / 253 (PSH3PWR_BATTERY_FULL - PSH3PWR_BATTERY_MIN); 254 *pval = state; 255 return 0; 256 } 257 258 return EINVAL; 259} 260 261 262static int 263psh3pwr_get_battery(void) 264{ 265 int battery; 266 int s; 267 268 s = spltty(); 269 battery = adc_sample_channel(ADC_CHANNEL_BATTERY); 270 splx(s); 271 272 return battery; 273} 274