1 /* $NetBSD: psh3pwr.c,v 1.8 2024/09/20 19:32:19 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.8 2024/09/20 19:32:19 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 72 static inline int __attribute__((__always_inline__)) 73 psh3pwr_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 90 struct psh3pwr_softc { 91 device_t sc_dev; 92 93 void *sc_ih_pin; 94 void *sc_ih_pout; 95 }; 96 97 static int psh3pwr_match(device_t, struct cfdata *, void *); 98 static void psh3pwr_attach(device_t, device_t, void *); 99 100 CFATTACH_DECL_NEW(psh3pwr, sizeof(struct psh3pwr_softc), 101 psh3pwr_match, psh3pwr_attach, NULL, NULL); 102 103 static int psh3pwr_intr_plug_out(void *); 104 static int psh3pwr_intr_plug_in(void *); 105 static void psh3pwr_sleep(void *); 106 static int psh3pwr_apm_getpower_hook(void *, int, long, void *); 107 static int psh3pwr_get_battery(void); 108 109 110 static int 111 psh3pwr_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 124 static void 125 psh3pwr_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 /* register 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 166 static int 167 psh3pwr_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 186 static int 187 psh3pwr_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 205 void 206 psh3pwr_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 222 static int 223 psh3pwr_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 264 static int 265 psh3pwr_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