1ccb3b2f2Sbouyer /* $OpenBSD: kb3310.c,v 1.16 2010/10/14 21:23:04 pirofti Exp $ */
2ccb3b2f2Sbouyer /*
3ccb3b2f2Sbouyer * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net>
4ccb3b2f2Sbouyer *
5ccb3b2f2Sbouyer * Permission to use, copy, modify, and distribute this software for any
6ccb3b2f2Sbouyer * purpose with or without fee is hereby granted, provided that the above
7ccb3b2f2Sbouyer * copyright notice and this permission notice appear in all copies.
8ccb3b2f2Sbouyer *
9ccb3b2f2Sbouyer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ccb3b2f2Sbouyer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ccb3b2f2Sbouyer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ccb3b2f2Sbouyer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ccb3b2f2Sbouyer * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ccb3b2f2Sbouyer * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ccb3b2f2Sbouyer * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ccb3b2f2Sbouyer */
17ccb3b2f2Sbouyer
18ccb3b2f2Sbouyer #include <sys/param.h>
19ccb3b2f2Sbouyer #include <sys/kernel.h>
20ccb3b2f2Sbouyer #include <sys/systm.h>
21ccb3b2f2Sbouyer #include <sys/device.h>
22ccb3b2f2Sbouyer #include <sys/sensors.h>
23ccb3b2f2Sbouyer #include <sys/timeout.h>
24ccb3b2f2Sbouyer
25ccb3b2f2Sbouyer #include <mips64/archtype.h>
26ccb3b2f2Sbouyer #include <machine/apmvar.h>
27ccb3b2f2Sbouyer #include <evbmips/loongson/autoconf.h>
28ccb3b2f2Sbouyer #include <machine/bus.h>
29ccb3b2f2Sbouyer #include <dev/isa/isavar.h>
30ccb3b2f2Sbouyer
31ccb3b2f2Sbouyer #include <dev/pci/glxreg.h>
32ccb3b2f2Sbouyer
33ccb3b2f2Sbouyer #include <loongson/dev/bonitoreg.h>
34ccb3b2f2Sbouyer #include <loongson/dev/kb3310var.h>
35ccb3b2f2Sbouyer
36ccb3b2f2Sbouyer #include "apm.h"
37ccb3b2f2Sbouyer #include "pckbd.h"
38ccb3b2f2Sbouyer #include "hidkbd.h"
39ccb3b2f2Sbouyer
40ccb3b2f2Sbouyer #if NPCKBD > 0 || NHIDKBD > 0
41ccb3b2f2Sbouyer #include <dev/ic/pckbcvar.h>
42ccb3b2f2Sbouyer #include <dev/pckbc/pckbdvar.h>
43ccb3b2f2Sbouyer #include <dev/usb/hidkbdvar.h>
44ccb3b2f2Sbouyer #endif
45ccb3b2f2Sbouyer
46ccb3b2f2Sbouyer struct cfdriver ykbec_cd = {
47ccb3b2f2Sbouyer NULL, "ykbec", DV_DULL,
48ccb3b2f2Sbouyer };
49ccb3b2f2Sbouyer
50ccb3b2f2Sbouyer #ifdef KB3310_DEBUG
51ccb3b2f2Sbouyer #define DPRINTF(x) printf x
52ccb3b2f2Sbouyer #else
53ccb3b2f2Sbouyer #define DPRINTF(x)
54ccb3b2f2Sbouyer #endif
55ccb3b2f2Sbouyer
56ccb3b2f2Sbouyer #define IO_YKBEC 0x381
57ccb3b2f2Sbouyer #define IO_YKBECSIZE 0x3
58ccb3b2f2Sbouyer
59ccb3b2f2Sbouyer static const struct {
60ccb3b2f2Sbouyer const char *desc;
61ccb3b2f2Sbouyer int type;
62ccb3b2f2Sbouyer } ykbec_table[] = {
63ccb3b2f2Sbouyer #define YKBEC_FAN 0
64ccb3b2f2Sbouyer { NULL, SENSOR_FANRPM },
65ccb3b2f2Sbouyer #define YKBEC_ITEMP 1
66ccb3b2f2Sbouyer { "Internal temperature", SENSOR_TEMP },
67ccb3b2f2Sbouyer #define YKBEC_FCAP 2
68ccb3b2f2Sbouyer { "Battery full charge capacity", SENSOR_AMPHOUR },
69ccb3b2f2Sbouyer #define YKBEC_BCURRENT 3
70ccb3b2f2Sbouyer { "Battery current", SENSOR_AMPS },
71ccb3b2f2Sbouyer #define YKBEC_BVOLT 4
72ccb3b2f2Sbouyer { "Battery voltage", SENSOR_VOLTS_DC },
73ccb3b2f2Sbouyer #define YKBEC_BTEMP 5
74ccb3b2f2Sbouyer { "Battery temperature", SENSOR_TEMP },
75ccb3b2f2Sbouyer #define YKBEC_CAP 6
76ccb3b2f2Sbouyer { "Battery capacity", SENSOR_PERCENT },
77ccb3b2f2Sbouyer #define YKBEC_CHARGING 7
78ccb3b2f2Sbouyer { "Battery charging", SENSOR_INDICATOR },
79ccb3b2f2Sbouyer #define YKBEC_AC 8
80ccb3b2f2Sbouyer { "AC-Power", SENSOR_INDICATOR }
81ccb3b2f2Sbouyer #define YKBEC_NSENSORS 9
82ccb3b2f2Sbouyer };
83ccb3b2f2Sbouyer
84ccb3b2f2Sbouyer struct ykbec_softc {
85ccb3b2f2Sbouyer bus_space_tag_t sc_iot;
86ccb3b2f2Sbouyer bus_space_handle_t sc_ioh;
87ccb3b2f2Sbouyer struct ksensor sc_sensor[YKBEC_NSENSORS];
88ccb3b2f2Sbouyer struct ksensordev sc_sensordev;
89ccb3b2f2Sbouyer #if NPCKBD > 0 || NHIDKBD > 0
90ccb3b2f2Sbouyer struct timeout sc_bell_tmo;
91ccb3b2f2Sbouyer #endif
92ccb3b2f2Sbouyer };
93ccb3b2f2Sbouyer
94ccb3b2f2Sbouyer static struct ykbec_softc *ykbec_sc;
95ccb3b2f2Sbouyer static int ykbec_chip_config;
96ccb3b2f2Sbouyer
97ccb3b2f2Sbouyer extern void loongson_set_isa_imr(uint);
98ccb3b2f2Sbouyer
99cbab9cadSchs int ykbec_match(device_t, cfdata_t, void *);
100cbab9cadSchs void ykbec_attach(device_t, device_t, void *);
101ccb3b2f2Sbouyer
102*a500e081Sriastradh CFATTACH_DECL_NEW(ykbec, sizeof(struct ykbec_softc),
103*a500e081Sriastradh ykbec_match, ykbec_attach, NULL, NULL);
104ccb3b2f2Sbouyer
105ccb3b2f2Sbouyer int ykbec_apminfo(struct apm_power_info *);
106ccb3b2f2Sbouyer void ykbec_bell(void *, u_int, u_int, u_int, int);
107ccb3b2f2Sbouyer void ykbec_bell_stop(void *);
108ccb3b2f2Sbouyer void ykbec_print_bat_info(struct ykbec_softc *);
109ccb3b2f2Sbouyer u_int ykbec_read(struct ykbec_softc *, u_int);
110ccb3b2f2Sbouyer u_int ykbec_read16(struct ykbec_softc *, u_int);
111ccb3b2f2Sbouyer void ykbec_refresh(void *arg);
112ccb3b2f2Sbouyer void ykbec_write(struct ykbec_softc *, u_int, u_int);
113ccb3b2f2Sbouyer
114ccb3b2f2Sbouyer #if NAPM > 0
115ccb3b2f2Sbouyer struct apm_power_info ykbec_apmdata;
116ccb3b2f2Sbouyer const char *ykbec_batstate[] = {
117ccb3b2f2Sbouyer "high",
118ccb3b2f2Sbouyer "low",
119ccb3b2f2Sbouyer "critical",
120ccb3b2f2Sbouyer "charging",
121ccb3b2f2Sbouyer "unknown"
122ccb3b2f2Sbouyer };
123ccb3b2f2Sbouyer #define BATTERY_STRING(x) ((x) < nitems(ykbec_batstate) ? \
124ccb3b2f2Sbouyer ykbec_batstate[x] : ykbec_batstate[4])
125ccb3b2f2Sbouyer #endif
126ccb3b2f2Sbouyer
127ccb3b2f2Sbouyer int
ykbec_match(device_t parent,cfdata_t match,void * aux)128cbab9cadSchs ykbec_match(device_t parent, cfdata_t match, void *aux)
129ccb3b2f2Sbouyer {
130ccb3b2f2Sbouyer struct isa_attach_args *ia = aux;
131ccb3b2f2Sbouyer bus_space_handle_t ioh;
132ccb3b2f2Sbouyer
133ccb3b2f2Sbouyer if (sys_platform->system_type != LOONGSON_YEELOONG)
134ccb3b2f2Sbouyer return (0);
135ccb3b2f2Sbouyer
136ccb3b2f2Sbouyer if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_YKBEC) ||
137ccb3b2f2Sbouyer /* (ia->ia_iosize != 0 && ia->ia_iosize != IO_YKBECSIZE) || XXX isa.c */
138ccb3b2f2Sbouyer ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 ||
139ccb3b2f2Sbouyer ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK)
140ccb3b2f2Sbouyer return (0);
141ccb3b2f2Sbouyer
142ccb3b2f2Sbouyer if (bus_space_map(ia->ia_iot, IO_YKBEC, IO_YKBECSIZE, 0, &ioh))
143ccb3b2f2Sbouyer return (0);
144ccb3b2f2Sbouyer
145ccb3b2f2Sbouyer bus_space_unmap(ia->ia_iot, ioh, IO_YKBECSIZE);
146ccb3b2f2Sbouyer
147ccb3b2f2Sbouyer ia->ia_iobase = IO_YKBEC;
148ccb3b2f2Sbouyer ia->ia_iosize = IO_YKBECSIZE;
149ccb3b2f2Sbouyer
150ccb3b2f2Sbouyer return (1);
151ccb3b2f2Sbouyer }
152ccb3b2f2Sbouyer
153ccb3b2f2Sbouyer void
ykbec_attach(device_t parent,device_t self,void * aux)154cbab9cadSchs ykbec_attach(device_t parent, device_t self, void *aux)
155ccb3b2f2Sbouyer {
156ccb3b2f2Sbouyer struct isa_attach_args *ia = aux;
157cbab9cadSchs struct ykbec_softc *sc = device_private(self);
158ccb3b2f2Sbouyer int i;
159ccb3b2f2Sbouyer
160ccb3b2f2Sbouyer sc->sc_iot = ia->ia_iot;
161ccb3b2f2Sbouyer if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
162ccb3b2f2Sbouyer &sc->sc_ioh)) {
163ccb3b2f2Sbouyer aprint_error(": couldn't map I/O space");
164ccb3b2f2Sbouyer return;
165ccb3b2f2Sbouyer }
166ccb3b2f2Sbouyer
167ccb3b2f2Sbouyer /* Initialize sensor data. */
168cbab9cadSchs strlcpy(sc->sc_sensordev.xname, device_xname(self),
169ccb3b2f2Sbouyer sizeof(sc->sc_sensordev.xname));
170ccb3b2f2Sbouyer if (sensor_task_register(sc, ykbec_refresh, 5) == NULL) {
171ccb3b2f2Sbouyer aprint_error(", unable to register update task\n");
172ccb3b2f2Sbouyer return;
173ccb3b2f2Sbouyer }
174ccb3b2f2Sbouyer
175ccb3b2f2Sbouyer #ifdef DEBUG
176ccb3b2f2Sbouyer ykbec_print_bat_info(sc);
177ccb3b2f2Sbouyer #endif
178ccb3b2f2Sbouyer aprint_normal("\n");
179ccb3b2f2Sbouyer
180ccb3b2f2Sbouyer for (i = 0; i < YKBEC_NSENSORS; i++) {
181ccb3b2f2Sbouyer sc->sc_sensor[i].type = ykbec_table[i].type;
182ccb3b2f2Sbouyer if (ykbec_table[i].desc)
183ccb3b2f2Sbouyer strlcpy(sc->sc_sensor[i].desc, ykbec_table[i].desc,
184ccb3b2f2Sbouyer sizeof(sc->sc_sensor[i].desc));
185ccb3b2f2Sbouyer sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
186ccb3b2f2Sbouyer }
187ccb3b2f2Sbouyer
188ccb3b2f2Sbouyer sensordev_install(&sc->sc_sensordev);
189ccb3b2f2Sbouyer
190ccb3b2f2Sbouyer #if NAPM > 0
191ccb3b2f2Sbouyer /* make sure we have the apm state initialized before apm attaches */
192ccb3b2f2Sbouyer ykbec_refresh(sc);
193ccb3b2f2Sbouyer apm_setinfohook(ykbec_apminfo);
194ccb3b2f2Sbouyer #endif
195ccb3b2f2Sbouyer #if NPCKBD > 0 || NHIDKBD > 0
196ccb3b2f2Sbouyer timeout_set(&sc->sc_bell_tmo, ykbec_bell_stop, sc);
197ccb3b2f2Sbouyer #if NPCKBD > 0
198ccb3b2f2Sbouyer pckbd_hookup_bell(ykbec_bell, sc);
199ccb3b2f2Sbouyer #endif
200ccb3b2f2Sbouyer #if NHIDKBD > 0
201ccb3b2f2Sbouyer hidkbd_hookup_bell(ykbec_bell, sc);
202ccb3b2f2Sbouyer #endif
203ccb3b2f2Sbouyer #endif
204ccb3b2f2Sbouyer ykbec_sc = sc;
205ccb3b2f2Sbouyer }
206ccb3b2f2Sbouyer
207ccb3b2f2Sbouyer void
ykbec_write(struct ykbec_softc * mcsc,u_int reg,u_int datum)208ccb3b2f2Sbouyer ykbec_write(struct ykbec_softc *mcsc, u_int reg, u_int datum)
209ccb3b2f2Sbouyer {
210ccb3b2f2Sbouyer struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
211ccb3b2f2Sbouyer bus_space_tag_t iot = sc->sc_iot;
212ccb3b2f2Sbouyer bus_space_handle_t ioh = sc->sc_ioh;
213ccb3b2f2Sbouyer
214ccb3b2f2Sbouyer bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
215ccb3b2f2Sbouyer bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
216ccb3b2f2Sbouyer bus_space_write_1(iot, ioh, 2, datum);
217ccb3b2f2Sbouyer }
218ccb3b2f2Sbouyer
219ccb3b2f2Sbouyer u_int
ykbec_read(struct ykbec_softc * mcsc,u_int reg)220ccb3b2f2Sbouyer ykbec_read(struct ykbec_softc *mcsc, u_int reg)
221ccb3b2f2Sbouyer {
222ccb3b2f2Sbouyer struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
223ccb3b2f2Sbouyer bus_space_tag_t iot = sc->sc_iot;
224ccb3b2f2Sbouyer bus_space_handle_t ioh = sc->sc_ioh;
225ccb3b2f2Sbouyer
226ccb3b2f2Sbouyer bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
227ccb3b2f2Sbouyer bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
228ccb3b2f2Sbouyer return bus_space_read_1(iot, ioh, 2);
229ccb3b2f2Sbouyer }
230ccb3b2f2Sbouyer
231ccb3b2f2Sbouyer u_int
ykbec_read16(struct ykbec_softc * mcsc,u_int reg)232ccb3b2f2Sbouyer ykbec_read16(struct ykbec_softc *mcsc, u_int reg)
233ccb3b2f2Sbouyer {
234ccb3b2f2Sbouyer u_int val;
235ccb3b2f2Sbouyer
236ccb3b2f2Sbouyer val = ykbec_read(mcsc, reg);
237ccb3b2f2Sbouyer return (val << 8) | ykbec_read(mcsc, reg + 1);
238ccb3b2f2Sbouyer }
239ccb3b2f2Sbouyer
240ccb3b2f2Sbouyer #define KB3310_FAN_SPEED_DIVIDER 480000
241ccb3b2f2Sbouyer
242ccb3b2f2Sbouyer #define ECTEMP_CURRENT_REG 0xf458
243ccb3b2f2Sbouyer #define REG_FAN_SPEED_HIGH 0xfe22
244ccb3b2f2Sbouyer #define REG_FAN_SPEED_LOW 0xfe23
245ccb3b2f2Sbouyer
246ccb3b2f2Sbouyer #define REG_DESIGN_CAP_HIGH 0xf77d
247ccb3b2f2Sbouyer #define REG_DESIGN_CAP_LOW 0xf77e
248ccb3b2f2Sbouyer #define REG_FULLCHG_CAP_HIGH 0xf780
249ccb3b2f2Sbouyer #define REG_FULLCHG_CAP_LOW 0xf781
250ccb3b2f2Sbouyer
251ccb3b2f2Sbouyer #define REG_DESIGN_VOL_HIGH 0xf782
252ccb3b2f2Sbouyer #define REG_DESIGN_VOL_LOW 0xf783
253ccb3b2f2Sbouyer #define REG_CURRENT_HIGH 0xf784
254ccb3b2f2Sbouyer #define REG_CURRENT_LOW 0xf785
255ccb3b2f2Sbouyer #define REG_VOLTAGE_HIGH 0xf786
256ccb3b2f2Sbouyer #define REG_VOLTAGE_LOW 0xf787
257ccb3b2f2Sbouyer #define REG_TEMPERATURE_HIGH 0xf788
258ccb3b2f2Sbouyer #define REG_TEMPERATURE_LOW 0xf789
259ccb3b2f2Sbouyer #define REG_RELATIVE_CAT_HIGH 0xf492
260ccb3b2f2Sbouyer #define REG_RELATIVE_CAT_LOW 0xf493
261ccb3b2f2Sbouyer #define REG_BAT_VENDOR 0xf4c4
262ccb3b2f2Sbouyer #define REG_BAT_CELL_COUNT 0xf4c6
263ccb3b2f2Sbouyer
264ccb3b2f2Sbouyer #define REG_BAT_CHARGE 0xf4a2
265ccb3b2f2Sbouyer #define BAT_CHARGE_AC 0x00
266ccb3b2f2Sbouyer #define BAT_CHARGE_DISCHARGE 0x01
267ccb3b2f2Sbouyer #define BAT_CHARGE_CHARGE 0x02
268ccb3b2f2Sbouyer
269ccb3b2f2Sbouyer #define REG_POWER_FLAG 0xf440
270ccb3b2f2Sbouyer #define POWER_FLAG_ADAPTER_IN (1<<0)
271ccb3b2f2Sbouyer #define POWER_FLAG_POWER_ON (1<<1)
272ccb3b2f2Sbouyer #define POWER_FLAG_ENTER_SUS (1<<2)
273ccb3b2f2Sbouyer
274ccb3b2f2Sbouyer #define REG_BAT_STATUS 0xf4b0
275ccb3b2f2Sbouyer #define BAT_STATUS_BAT_EXISTS (1<<0)
276ccb3b2f2Sbouyer #define BAT_STATUS_BAT_FULL (1<<1)
277ccb3b2f2Sbouyer #define BAT_STATUS_BAT_DESTROY (1<<2)
278ccb3b2f2Sbouyer #define BAT_STATUS_BAT_LOW (1<<5)
279ccb3b2f2Sbouyer
280ccb3b2f2Sbouyer #define REG_CHARGE_STATUS 0xf4b1
281ccb3b2f2Sbouyer #define CHARGE_STATUS_PRECHARGE (1<<1)
282ccb3b2f2Sbouyer #define CHARGE_STATUS_OVERHEAT (1<<2)
283ccb3b2f2Sbouyer
284ccb3b2f2Sbouyer #define REG_BAT_STATE 0xf482
285ccb3b2f2Sbouyer #define BAT_STATE_DISCHARGING (1<<0)
286ccb3b2f2Sbouyer #define BAT_STATE_CHARGING (1<<1)
287ccb3b2f2Sbouyer
288ccb3b2f2Sbouyer #define REG_BEEP_CONTROL 0xf4d0
289ccb3b2f2Sbouyer #define BEEP_ENABLE (1<<0)
290ccb3b2f2Sbouyer
291ccb3b2f2Sbouyer #define REG_PMUCFG 0xff0c
292ccb3b2f2Sbouyer #define PMUCFG_STOP_MODE (1<<7)
293ccb3b2f2Sbouyer #define PMUCFG_IDLE_MODE (1<<6)
294ccb3b2f2Sbouyer #define PMUCFG_LPC_WAKEUP (1<<5)
295ccb3b2f2Sbouyer #define PMUCFG_RESET_8051 (1<<4)
296ccb3b2f2Sbouyer #define PMUCFG_SCI_WAKEUP (1<<3)
297ccb3b2f2Sbouyer #define PMUCFG_WDT_WAKEUP (1<<2)
298ccb3b2f2Sbouyer #define PMUCFG_GPWU_WAKEUP (1<<1)
299ccb3b2f2Sbouyer #define PMUCFG_IRQ_IDLE (1<<0)
300ccb3b2f2Sbouyer
301ccb3b2f2Sbouyer #define REG_USB0 0xf461
302ccb3b2f2Sbouyer #define REG_USB1 0xf462
303ccb3b2f2Sbouyer #define REG_USB2 0xf463
304ccb3b2f2Sbouyer #define USB_FLAG_ON 1
305ccb3b2f2Sbouyer #define USB_FLAG_OFF 0
306ccb3b2f2Sbouyer
307ccb3b2f2Sbouyer #define REG_FAN_CONTROL 0xf4d2
308ccb3b2f2Sbouyer #define REG_FAN_ON 1
309ccb3b2f2Sbouyer #define REG_FAN_OFF 0
310ccb3b2f2Sbouyer
311ccb3b2f2Sbouyer #define YKBEC_SCI_IRQ 0xa
312ccb3b2f2Sbouyer
313ccb3b2f2Sbouyer #ifdef DEBUG
314ccb3b2f2Sbouyer void
ykbec_print_bat_info(struct ykbec_softc * sc)315ccb3b2f2Sbouyer ykbec_print_bat_info(struct ykbec_softc *sc)
316ccb3b2f2Sbouyer {
317ccb3b2f2Sbouyer uint bat_status, count, dvolt, dcap;
318ccb3b2f2Sbouyer
319ccb3b2f2Sbouyer printf(": battery ");
320ccb3b2f2Sbouyer bat_status = ykbec_read(sc, REG_BAT_STATUS);
321ccb3b2f2Sbouyer if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) {
322ccb3b2f2Sbouyer printf("absent");
323ccb3b2f2Sbouyer return;
324ccb3b2f2Sbouyer }
325ccb3b2f2Sbouyer
326ccb3b2f2Sbouyer count = ykbec_read(sc, REG_BAT_CELL_COUNT);
327ccb3b2f2Sbouyer dvolt = ykbec_read16(sc, REG_DESIGN_VOL_HIGH);
328ccb3b2f2Sbouyer dcap = ykbec_read16(sc, REG_DESIGN_CAP_HIGH);
329ccb3b2f2Sbouyer printf("%d cells, design capacity %dmV %dmAh", count, dvolt, dcap);
330ccb3b2f2Sbouyer }
331ccb3b2f2Sbouyer #endif
332ccb3b2f2Sbouyer
333ccb3b2f2Sbouyer void
ykbec_refresh(void * arg)334ccb3b2f2Sbouyer ykbec_refresh(void *arg)
335ccb3b2f2Sbouyer {
336ccb3b2f2Sbouyer struct ykbec_softc *sc = (struct ykbec_softc *)arg;
337ccb3b2f2Sbouyer u_int val, bat_charge, bat_status, charge_status, bat_state, power_flag;
338ccb3b2f2Sbouyer u_int cap_pct, fullcap;
339ccb3b2f2Sbouyer int current;
340ccb3b2f2Sbouyer #if NAPM > 0
341ccb3b2f2Sbouyer struct apm_power_info old;
342ccb3b2f2Sbouyer #endif
343ccb3b2f2Sbouyer
344ccb3b2f2Sbouyer val = ykbec_read16(sc, REG_FAN_SPEED_HIGH) & 0xfffff;
345ccb3b2f2Sbouyer if (val != 0) {
346ccb3b2f2Sbouyer val = KB3310_FAN_SPEED_DIVIDER / val;
347ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_FAN].value = val;
348ccb3b2f2Sbouyer CLR(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID);
349ccb3b2f2Sbouyer } else
350ccb3b2f2Sbouyer SET(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID);
351ccb3b2f2Sbouyer
352ccb3b2f2Sbouyer val = ykbec_read(sc, ECTEMP_CURRENT_REG);
353ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_ITEMP].value = val * 1000000 + 273150000;
354ccb3b2f2Sbouyer
355ccb3b2f2Sbouyer fullcap = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH);
356ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_FCAP].value = fullcap * 1000;
357ccb3b2f2Sbouyer
358ccb3b2f2Sbouyer current = ykbec_read16(sc, REG_CURRENT_HIGH);
359ccb3b2f2Sbouyer /* sign extend short -> int, int -> int64 will be done next statement */
360ccb3b2f2Sbouyer current |= -(current & 0x8000);
361ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_BCURRENT].value = -1000 * current;
362ccb3b2f2Sbouyer
363ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_BVOLT].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) *
364ccb3b2f2Sbouyer 1000;
365ccb3b2f2Sbouyer
366ccb3b2f2Sbouyer val = ykbec_read16(sc, REG_TEMPERATURE_HIGH);
367ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_BTEMP].value = val * 1000000 + 273150000;
368ccb3b2f2Sbouyer
369ccb3b2f2Sbouyer cap_pct = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH);
370ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_CAP].value = cap_pct * 1000;
371ccb3b2f2Sbouyer
372ccb3b2f2Sbouyer bat_charge = ykbec_read(sc, REG_BAT_CHARGE);
373ccb3b2f2Sbouyer bat_status = ykbec_read(sc, REG_BAT_STATUS);
374ccb3b2f2Sbouyer charge_status = ykbec_read(sc, REG_CHARGE_STATUS);
375ccb3b2f2Sbouyer bat_state = ykbec_read(sc, REG_BAT_STATE);
376ccb3b2f2Sbouyer power_flag = ykbec_read(sc, REG_POWER_FLAG);
377ccb3b2f2Sbouyer
378ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_CHARGING].value = !!ISSET(bat_state,
379ccb3b2f2Sbouyer BAT_STATE_CHARGING);
380ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_AC].value = !!ISSET(power_flag,
381ccb3b2f2Sbouyer POWER_FLAG_ADAPTER_IN);
382ccb3b2f2Sbouyer
383ccb3b2f2Sbouyer sc->sc_sensor[YKBEC_CAP].status = ISSET(bat_status, BAT_STATUS_BAT_LOW) ?
384ccb3b2f2Sbouyer SENSOR_S_CRIT : SENSOR_S_OK;
385ccb3b2f2Sbouyer
386ccb3b2f2Sbouyer #if NAPM > 0
387ccb3b2f2Sbouyer bcopy(&ykbec_apmdata, &old, sizeof(old));
388ccb3b2f2Sbouyer ykbec_apmdata.battery_life = cap_pct;
389ccb3b2f2Sbouyer ykbec_apmdata.ac_state = ISSET(power_flag, POWER_FLAG_ADAPTER_IN) ?
390ccb3b2f2Sbouyer APM_AC_ON : APM_AC_OFF;
391ccb3b2f2Sbouyer if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) {
392ccb3b2f2Sbouyer ykbec_apmdata.battery_state = APM_BATTERY_ABSENT;
393ccb3b2f2Sbouyer ykbec_apmdata.minutes_left = 0;
394ccb3b2f2Sbouyer ykbec_apmdata.battery_life = 0;
395ccb3b2f2Sbouyer } else {
396ccb3b2f2Sbouyer if (ISSET(bat_state, BAT_STATE_CHARGING))
397ccb3b2f2Sbouyer ykbec_apmdata.battery_state = APM_BATT_CHARGING;
398ccb3b2f2Sbouyer else if (ISSET(bat_status, BAT_STATUS_BAT_LOW))
399ccb3b2f2Sbouyer ykbec_apmdata.battery_state = APM_BATT_CRITICAL;
400ccb3b2f2Sbouyer /* XXX arbitrary */
401ccb3b2f2Sbouyer else if (cap_pct > 60)
402ccb3b2f2Sbouyer ykbec_apmdata.battery_state = APM_BATT_HIGH;
403ccb3b2f2Sbouyer else
404ccb3b2f2Sbouyer ykbec_apmdata.battery_state = APM_BATT_LOW;
405ccb3b2f2Sbouyer
406ccb3b2f2Sbouyer /* if charging, current is positive */
407ccb3b2f2Sbouyer if (ISSET(bat_state, BAT_STATE_CHARGING))
408ccb3b2f2Sbouyer current = 0;
409ccb3b2f2Sbouyer else
410ccb3b2f2Sbouyer current = -current;
411ccb3b2f2Sbouyer /* XXX Yeeloong draw is about 1A */
412ccb3b2f2Sbouyer if (current <= 0)
413ccb3b2f2Sbouyer current = 1000;
414ccb3b2f2Sbouyer /* XXX at 5?%, the Yeeloong shuts down */
415ccb3b2f2Sbouyer if (cap_pct <= 5)
416ccb3b2f2Sbouyer cap_pct = 0;
417ccb3b2f2Sbouyer else
418ccb3b2f2Sbouyer cap_pct -= 5;
419ccb3b2f2Sbouyer fullcap = cap_pct * 60 * fullcap / 100;
420ccb3b2f2Sbouyer ykbec_apmdata.minutes_left = fullcap / current;
421ccb3b2f2Sbouyer
422ccb3b2f2Sbouyer }
423ccb3b2f2Sbouyer if (old.ac_state != ykbec_apmdata.ac_state)
424ccb3b2f2Sbouyer apm_record_event(APM_POWER_CHANGE, "AC power",
425ccb3b2f2Sbouyer ykbec_apmdata.ac_state ? "restored" : "lost");
426ccb3b2f2Sbouyer if (old.battery_state != ykbec_apmdata.battery_state)
427ccb3b2f2Sbouyer apm_record_event(APM_POWER_CHANGE, "battery",
428ccb3b2f2Sbouyer BATTERY_STRING(ykbec_apmdata.battery_state));
429ccb3b2f2Sbouyer #endif
430ccb3b2f2Sbouyer }
431ccb3b2f2Sbouyer
432ccb3b2f2Sbouyer
433ccb3b2f2Sbouyer #if NAPM > 0
434ccb3b2f2Sbouyer int
ykbec_apminfo(struct apm_power_info * info)435ccb3b2f2Sbouyer ykbec_apminfo(struct apm_power_info *info)
436ccb3b2f2Sbouyer {
437ccb3b2f2Sbouyer bcopy(&ykbec_apmdata, info, sizeof(struct apm_power_info));
438ccb3b2f2Sbouyer return 0;
439ccb3b2f2Sbouyer }
440ccb3b2f2Sbouyer
441ccb3b2f2Sbouyer int
ykbec_suspend()442ccb3b2f2Sbouyer ykbec_suspend()
443ccb3b2f2Sbouyer {
444ccb3b2f2Sbouyer struct ykbec_softc *sc = ykbec_sc;
445ccb3b2f2Sbouyer int ctrl;
446ccb3b2f2Sbouyer
447ccb3b2f2Sbouyer /*
448ccb3b2f2Sbouyer * Set up wakeup sources: currently only the internal keyboard.
449ccb3b2f2Sbouyer */
450ccb3b2f2Sbouyer loongson_set_isa_imr(1 << 1);
451ccb3b2f2Sbouyer
452ccb3b2f2Sbouyer /* USB */
453ccb3b2f2Sbouyer DPRINTF(("USB\n"));
454ccb3b2f2Sbouyer ykbec_write(sc, REG_USB0, USB_FLAG_OFF);
455ccb3b2f2Sbouyer ykbec_write(sc, REG_USB1, USB_FLAG_OFF);
456ccb3b2f2Sbouyer ykbec_write(sc, REG_USB2, USB_FLAG_OFF);
457ccb3b2f2Sbouyer
458ccb3b2f2Sbouyer /* EC */
459ccb3b2f2Sbouyer DPRINTF(("REG_PMUCFG\n"));
460ccb3b2f2Sbouyer ctrl = PMUCFG_SCI_WAKEUP | PMUCFG_WDT_WAKEUP | PMUCFG_GPWU_WAKEUP |
461ccb3b2f2Sbouyer PMUCFG_LPC_WAKEUP | PMUCFG_STOP_MODE | PMUCFG_RESET_8051;
462ccb3b2f2Sbouyer ykbec_write(sc, REG_PMUCFG, ctrl);
463ccb3b2f2Sbouyer
464ccb3b2f2Sbouyer /* FAN */
465ccb3b2f2Sbouyer DPRINTF(("FAN\n"));
466ccb3b2f2Sbouyer ykbec_write(sc, REG_FAN_CONTROL, REG_FAN_OFF);
467ccb3b2f2Sbouyer
468ccb3b2f2Sbouyer /* CPU */
469ccb3b2f2Sbouyer DPRINTF(("CPU\n"));
470ccb3b2f2Sbouyer ykbec_chip_config = REGVAL(LOONGSON_CHIP_CONFIG0);
471ccb3b2f2Sbouyer enableintr();
472ccb3b2f2Sbouyer REGVAL(LOONGSON_CHIP_CONFIG0) = ykbec_chip_config & ~0x7;
473ccb3b2f2Sbouyer (void)REGVAL(LOONGSON_CHIP_CONFIG0);
474ccb3b2f2Sbouyer
475ccb3b2f2Sbouyer /*
476ccb3b2f2Sbouyer * When a resume interrupt fires, we will enter the interrupt
477ccb3b2f2Sbouyer * dispatcher, which will do nothing because we are at splhigh,
478ccb3b2f2Sbouyer * and execution flow will return here and continue.
479ccb3b2f2Sbouyer */
480ccb3b2f2Sbouyer (void)disableintr();
481ccb3b2f2Sbouyer
482ccb3b2f2Sbouyer return 0;
483ccb3b2f2Sbouyer }
484ccb3b2f2Sbouyer
485ccb3b2f2Sbouyer int
ykbec_resume()486ccb3b2f2Sbouyer ykbec_resume()
487ccb3b2f2Sbouyer {
488ccb3b2f2Sbouyer struct ykbec_softc *sc = ykbec_sc;
489ccb3b2f2Sbouyer
490ccb3b2f2Sbouyer /* CPU */
491ccb3b2f2Sbouyer DPRINTF(("CPU\n"));
492ccb3b2f2Sbouyer REGVAL(LOONGSON_CHIP_CONFIG0) = ykbec_chip_config;
493ccb3b2f2Sbouyer (void)REGVAL(LOONGSON_CHIP_CONFIG0);
494ccb3b2f2Sbouyer
495ccb3b2f2Sbouyer /* FAN */
496ccb3b2f2Sbouyer DPRINTF(("FAN\n"));
497ccb3b2f2Sbouyer ykbec_write(sc, REG_FAN_CONTROL, REG_FAN_ON);
498ccb3b2f2Sbouyer
499ccb3b2f2Sbouyer /* USB */
500ccb3b2f2Sbouyer DPRINTF(("USB\n"));
501ccb3b2f2Sbouyer ykbec_write(sc, REG_USB0, USB_FLAG_ON);
502ccb3b2f2Sbouyer ykbec_write(sc, REG_USB1, USB_FLAG_ON);
503ccb3b2f2Sbouyer ykbec_write(sc, REG_USB2, USB_FLAG_ON);
504ccb3b2f2Sbouyer
505ccb3b2f2Sbouyer ykbec_refresh(sc);
506ccb3b2f2Sbouyer
507ccb3b2f2Sbouyer return 0;
508ccb3b2f2Sbouyer }
509ccb3b2f2Sbouyer #endif
510ccb3b2f2Sbouyer
511ccb3b2f2Sbouyer #if NPCKBD > 0 || NHIDKBD > 0
512ccb3b2f2Sbouyer void
ykbec_bell(void * arg,u_int pitch,u_int period,u_int volume,int poll)513ccb3b2f2Sbouyer ykbec_bell(void *arg, u_int pitch, u_int period, u_int volume, int poll)
514ccb3b2f2Sbouyer {
515ccb3b2f2Sbouyer struct ykbec_softc *sc = (struct ykbec_softc *)arg;
516ccb3b2f2Sbouyer int bctrl;
517ccb3b2f2Sbouyer int s;
518ccb3b2f2Sbouyer
519ccb3b2f2Sbouyer s = spltty();
520ccb3b2f2Sbouyer bctrl = ykbec_read(sc, REG_BEEP_CONTROL);
521ccb3b2f2Sbouyer if (volume == 0 || timeout_pending(&sc->sc_bell_tmo)) {
522ccb3b2f2Sbouyer timeout_del(&sc->sc_bell_tmo);
523ccb3b2f2Sbouyer /* inline ykbec_bell_stop(arg); */
524ccb3b2f2Sbouyer ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE);
525ccb3b2f2Sbouyer }
526ccb3b2f2Sbouyer
527ccb3b2f2Sbouyer if (volume != 0) {
528ccb3b2f2Sbouyer ykbec_write(sc, REG_BEEP_CONTROL, bctrl | BEEP_ENABLE);
529ccb3b2f2Sbouyer if (poll) {
530ccb3b2f2Sbouyer delay(period * 1000);
531ccb3b2f2Sbouyer ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE);
532ccb3b2f2Sbouyer } else {
533ccb3b2f2Sbouyer timeout_add_msec(&sc->sc_bell_tmo, period);
534ccb3b2f2Sbouyer }
535ccb3b2f2Sbouyer }
536ccb3b2f2Sbouyer splx(s);
537ccb3b2f2Sbouyer }
538ccb3b2f2Sbouyer
539ccb3b2f2Sbouyer void
ykbec_bell_stop(void * arg)540ccb3b2f2Sbouyer ykbec_bell_stop(void *arg)
541ccb3b2f2Sbouyer {
542ccb3b2f2Sbouyer struct ykbec_softc *sc = (struct ykbec_softc *)arg;
543ccb3b2f2Sbouyer int s;
544ccb3b2f2Sbouyer
545ccb3b2f2Sbouyer s = spltty();
546ccb3b2f2Sbouyer ykbec_write(sc, REG_BEEP_CONTROL,
547ccb3b2f2Sbouyer ykbec_read(sc, REG_BEEP_CONTROL) & ~BEEP_ENABLE);
548ccb3b2f2Sbouyer splx(s);
549ccb3b2f2Sbouyer }
550ccb3b2f2Sbouyer #endif
551