xref: /openbsd/sys/arch/loongson/dev/kb3310.c (revision e1f5be9a)
1*e1f5be9aSotto /*	$OpenBSD: kb3310.c,v 1.1 2010/02/23 21:04:16 otto Exp $	*/
2*e1f5be9aSotto /*
3*e1f5be9aSotto  * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net>
4*e1f5be9aSotto  *
5*e1f5be9aSotto  * Permission to use, copy, modify, and distribute this software for any
6*e1f5be9aSotto  * purpose with or without fee is hereby granted, provided that the above
7*e1f5be9aSotto  * copyright notice and this permission notice appear in all copies.
8*e1f5be9aSotto  *
9*e1f5be9aSotto  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*e1f5be9aSotto  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*e1f5be9aSotto  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*e1f5be9aSotto  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*e1f5be9aSotto  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*e1f5be9aSotto  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*e1f5be9aSotto  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*e1f5be9aSotto  */
17*e1f5be9aSotto 
18*e1f5be9aSotto #include <sys/param.h>
19*e1f5be9aSotto #include <sys/kernel.h>
20*e1f5be9aSotto #include <sys/systm.h>
21*e1f5be9aSotto #include <sys/device.h>
22*e1f5be9aSotto #include <sys/sensors.h>
23*e1f5be9aSotto 
24*e1f5be9aSotto #include <machine/bus.h>
25*e1f5be9aSotto #include <dev/isa/isavar.h>
26*e1f5be9aSotto 
27*e1f5be9aSotto struct cfdriver ykbec_cd = {
28*e1f5be9aSotto 	NULL, "ykbec", DV_DULL,
29*e1f5be9aSotto };
30*e1f5be9aSotto 
31*e1f5be9aSotto #define IO_YKBEC		0x381
32*e1f5be9aSotto #define IO_YKBECSIZE		0x3
33*e1f5be9aSotto 
34*e1f5be9aSotto #define KB3310_NUM_SENSORS	10
35*e1f5be9aSotto 
36*e1f5be9aSotto struct ykbec_softc {
37*e1f5be9aSotto 	struct device		sc_dev;
38*e1f5be9aSotto 	bus_space_tag_t		sc_iot;
39*e1f5be9aSotto 	bus_space_handle_t	sc_ioh;
40*e1f5be9aSotto 	struct ksensor		sc_sensor[KB3310_NUM_SENSORS];
41*e1f5be9aSotto 	struct ksensordev	sc_sensordev;
42*e1f5be9aSotto 
43*e1f5be9aSotto };
44*e1f5be9aSotto 
45*e1f5be9aSotto int	ykbec_match(struct device *, void *, void *);
46*e1f5be9aSotto void	ykbec_attach(struct device *, struct device *, void *);
47*e1f5be9aSotto void	ykbec_refresh(void *arg);
48*e1f5be9aSotto 
49*e1f5be9aSotto const struct cfattach ykbec_ca = {
50*e1f5be9aSotto 	sizeof(struct ykbec_softc), ykbec_match, ykbec_attach
51*e1f5be9aSotto };
52*e1f5be9aSotto 
53*e1f5be9aSotto void	ykbec_write(struct ykbec_softc *, u_int, u_int);
54*e1f5be9aSotto u_int	ykbec_read(struct ykbec_softc *, u_int);
55*e1f5be9aSotto u_int	ykbec_read16(struct ykbec_softc *, u_int);
56*e1f5be9aSotto 
57*e1f5be9aSotto int
58*e1f5be9aSotto ykbec_match(struct device *parent, void *match, void *aux)
59*e1f5be9aSotto {
60*e1f5be9aSotto 	struct isa_attach_args *ia = aux;
61*e1f5be9aSotto 	bus_space_handle_t ioh;
62*e1f5be9aSotto 
63*e1f5be9aSotto 	if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_YKBEC) ||
64*e1f5be9aSotto 	    /* (ia->ia_iosize != 0 && ia->ia_iosize != IO_YKBECSIZE) || XXX isa.c */
65*e1f5be9aSotto 	    ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 ||
66*e1f5be9aSotto 	    ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK)
67*e1f5be9aSotto 		return (0);
68*e1f5be9aSotto 
69*e1f5be9aSotto 	if (bus_space_map(ia->ia_iot, IO_YKBEC, IO_YKBECSIZE, 0, &ioh))
70*e1f5be9aSotto 		return (0);
71*e1f5be9aSotto 
72*e1f5be9aSotto 	bus_space_unmap(ia->ia_iot, ioh, IO_YKBECSIZE);
73*e1f5be9aSotto 
74*e1f5be9aSotto 	ia->ia_iobase = IO_YKBEC;
75*e1f5be9aSotto 	ia->ia_iosize = IO_YKBECSIZE;
76*e1f5be9aSotto 
77*e1f5be9aSotto 	return (1);
78*e1f5be9aSotto }
79*e1f5be9aSotto 
80*e1f5be9aSotto 
81*e1f5be9aSotto void
82*e1f5be9aSotto ykbec_attach( struct device *parent, struct device *self, void *aux)
83*e1f5be9aSotto {
84*e1f5be9aSotto 	struct isa_attach_args *ia = aux;
85*e1f5be9aSotto 	struct ykbec_softc *sc = (struct ykbec_softc *)self;
86*e1f5be9aSotto 
87*e1f5be9aSotto 	sc->sc_iot = ia->ia_iot;
88*e1f5be9aSotto 	if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
89*e1f5be9aSotto 	    &sc->sc_ioh)) {
90*e1f5be9aSotto 		printf(": couldn't map I/O space");
91*e1f5be9aSotto 		return;
92*e1f5be9aSotto 	}
93*e1f5be9aSotto 
94*e1f5be9aSotto 	/* Initialize sensor data. */
95*e1f5be9aSotto 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
96*e1f5be9aSotto 	    sizeof(sc->sc_sensordev.xname));
97*e1f5be9aSotto 	if (sensor_task_register(sc, ykbec_refresh, 5) == NULL) {
98*e1f5be9aSotto 		printf(", unable to register update task\n");
99*e1f5be9aSotto 		return;
100*e1f5be9aSotto 	}
101*e1f5be9aSotto 	sc->sc_sensor[0].type = SENSOR_FANRPM;
102*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[0]);
103*e1f5be9aSotto 
104*e1f5be9aSotto 	sc->sc_sensor[1].type = SENSOR_TEMP;
105*e1f5be9aSotto 	strlcpy(sc->sc_sensor[1].desc, "Internal temperature",
106*e1f5be9aSotto 	    sizeof(sc->sc_sensor[1].desc));
107*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[1]);
108*e1f5be9aSotto 
109*e1f5be9aSotto 	sc->sc_sensor[2].type = SENSOR_AMPHOUR;
110*e1f5be9aSotto 	strlcpy(sc->sc_sensor[2].desc, "Battery design capacity",
111*e1f5be9aSotto 	    sizeof(sc->sc_sensor[2].desc));
112*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[2]);
113*e1f5be9aSotto 
114*e1f5be9aSotto 	sc->sc_sensor[3].type = SENSOR_AMPHOUR;
115*e1f5be9aSotto 	strlcpy(sc->sc_sensor[3].desc, "Battery full charge capacity",
116*e1f5be9aSotto 	    sizeof(sc->sc_sensor[3].desc));
117*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[3]);
118*e1f5be9aSotto 
119*e1f5be9aSotto 	sc->sc_sensor[4].type = SENSOR_VOLTS_DC;
120*e1f5be9aSotto 	strlcpy(sc->sc_sensor[4].desc, "Battery design voltage",
121*e1f5be9aSotto 	    sizeof(sc->sc_sensor[4].desc));
122*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[4]);
123*e1f5be9aSotto 
124*e1f5be9aSotto 	sc->sc_sensor[5].type = SENSOR_AMPS;
125*e1f5be9aSotto 	strlcpy(sc->sc_sensor[5].desc, "Battery current",
126*e1f5be9aSotto 	    sizeof(sc->sc_sensor[5].desc));
127*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[5]);
128*e1f5be9aSotto 
129*e1f5be9aSotto 	sc->sc_sensor[6].type = SENSOR_VOLTS_DC;
130*e1f5be9aSotto 	strlcpy(sc->sc_sensor[6].desc, "Battery voltage",
131*e1f5be9aSotto 	    sizeof(sc->sc_sensor[6].desc));
132*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[6]);
133*e1f5be9aSotto 
134*e1f5be9aSotto 	sc->sc_sensor[7].type = SENSOR_TEMP;
135*e1f5be9aSotto 	strlcpy(sc->sc_sensor[7].desc, "Battery temperature",
136*e1f5be9aSotto 	    sizeof(sc->sc_sensor[7].desc));
137*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[7]);
138*e1f5be9aSotto 
139*e1f5be9aSotto 	sc->sc_sensor[8].type = SENSOR_PERCENT;
140*e1f5be9aSotto 	strlcpy(sc->sc_sensor[8].desc, "Battery capacity",
141*e1f5be9aSotto 	    sizeof(sc->sc_sensor[8].desc));
142*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[8]);
143*e1f5be9aSotto 
144*e1f5be9aSotto 	sc->sc_sensor[9].type = SENSOR_INTEGER;
145*e1f5be9aSotto 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[9]);
146*e1f5be9aSotto 
147*e1f5be9aSotto 	sensordev_install(&sc->sc_sensordev);
148*e1f5be9aSotto 
149*e1f5be9aSotto 	printf("\n");
150*e1f5be9aSotto }
151*e1f5be9aSotto 
152*e1f5be9aSotto void
153*e1f5be9aSotto ykbec_write(struct ykbec_softc *mcsc, u_int reg, u_int datum)
154*e1f5be9aSotto {
155*e1f5be9aSotto 	struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
156*e1f5be9aSotto 	bus_space_tag_t iot = sc->sc_iot;
157*e1f5be9aSotto 	bus_space_handle_t ioh = sc->sc_ioh;
158*e1f5be9aSotto 
159*e1f5be9aSotto 	bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
160*e1f5be9aSotto 	bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
161*e1f5be9aSotto 	bus_space_write_1(iot, ioh, 2, datum);
162*e1f5be9aSotto }
163*e1f5be9aSotto 
164*e1f5be9aSotto u_int
165*e1f5be9aSotto ykbec_read(struct ykbec_softc *mcsc, u_int reg)
166*e1f5be9aSotto {
167*e1f5be9aSotto 	struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
168*e1f5be9aSotto 	bus_space_tag_t iot = sc->sc_iot;
169*e1f5be9aSotto 	bus_space_handle_t ioh = sc->sc_ioh;
170*e1f5be9aSotto 
171*e1f5be9aSotto 	bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
172*e1f5be9aSotto 	bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
173*e1f5be9aSotto 	return bus_space_read_1(iot, ioh, 2);
174*e1f5be9aSotto }
175*e1f5be9aSotto 
176*e1f5be9aSotto u_int
177*e1f5be9aSotto ykbec_read16(struct ykbec_softc *mcsc, u_int reg)
178*e1f5be9aSotto {
179*e1f5be9aSotto 	u_int val;
180*e1f5be9aSotto 
181*e1f5be9aSotto 	val = ykbec_read(mcsc, reg);
182*e1f5be9aSotto 	return (val << 8) | ykbec_read(mcsc, reg + 1);
183*e1f5be9aSotto }
184*e1f5be9aSotto 
185*e1f5be9aSotto #define KB3310_FAN_SPEED_DIVIDER	480000
186*e1f5be9aSotto 
187*e1f5be9aSotto #define ECTEMP_CURRENT_REG		0xf458
188*e1f5be9aSotto #define REG_FAN_SPEED_HIGH		0xfe22
189*e1f5be9aSotto #define REG_FAN_SPEED_LOW		0xfe23
190*e1f5be9aSotto 
191*e1f5be9aSotto #define REG_DESIGN_CAP_HIGH		0xf77d
192*e1f5be9aSotto #define REG_DESIGN_CAP_LOW		0xf77e
193*e1f5be9aSotto #define REG_FULLCHG_CAP_HIGH		0xf780
194*e1f5be9aSotto #define REG_FULLCHG_CAP_LOW		0xf781
195*e1f5be9aSotto 
196*e1f5be9aSotto #define REG_DESIGN_VOL_HIGH		0xf782
197*e1f5be9aSotto #define REG_DESIGN_VOL_LOW		0xf783
198*e1f5be9aSotto #define REG_CURRENT_HIGH		0xf784
199*e1f5be9aSotto #define REG_CURRENT_LOW			0xf785
200*e1f5be9aSotto #define REG_VOLTAGE_HIGH		0xf786
201*e1f5be9aSotto #define REG_VOLTAGE_LOW			0xf787
202*e1f5be9aSotto #define REG_TEMPERATURE_HIGH		0xf788
203*e1f5be9aSotto #define REG_TEMPERATURE_LOW		0xf789
204*e1f5be9aSotto #define REG_RELATIVE_CAT_HIGH		0xf492
205*e1f5be9aSotto #define REG_RELATIVE_CAT_LOW		0xf493
206*e1f5be9aSotto #define REG_BAT_VENDOR			0xf4c4
207*e1f5be9aSotto #define REG_BAT_CELL_COUNT		0xf4c6
208*e1f5be9aSotto #define REG_BAT_CHARGE			0xf4a2
209*e1f5be9aSotto #define REG_POWER_FLAG			0xf440
210*e1f5be9aSotto 
211*e1f5be9aSotto #define REG_BAT_STATUS			0xf4b0
212*e1f5be9aSotto #define BAT_STATUS_BAT_EXISTS		(1<<0)
213*e1f5be9aSotto #define BAT_STATUS_BAT_FULL		(1<<1)
214*e1f5be9aSotto #define BAT_STATUS_BAT_DESTROY		(1<<2)
215*e1f5be9aSotto #define BAT_STATUS_BAT_LOW		(1<<5)
216*e1f5be9aSotto 
217*e1f5be9aSotto #define REG_CHARGE_STATUS		0xf4b1
218*e1f5be9aSotto #define CHARGE_STATUS_PRECHARGE		(1<<1)
219*e1f5be9aSotto #define CHARGE_STATUS_OVERHEAT		(1<<2)
220*e1f5be9aSotto 
221*e1f5be9aSotto #define REG_BAT_STATE			0xf482
222*e1f5be9aSotto #define BAT_STATE_DISCHARGING		(1<<0)
223*e1f5be9aSotto #define BAT_STATE_CHARGING		(1<<1)
224*e1f5be9aSotto 
225*e1f5be9aSotto const char *REG_BAT_CHARGE_state[] = {
226*e1f5be9aSotto     "AC-power", "Battery discharging", "Battery charging"
227*e1f5be9aSotto };
228*e1f5be9aSotto 
229*e1f5be9aSotto #define STATUS(a, i)  ((i) >= nitems(a) ? "unknown" : (a)[i])
230*e1f5be9aSotto 
231*e1f5be9aSotto void
232*e1f5be9aSotto ykbec_refresh(void *arg)
233*e1f5be9aSotto {
234*e1f5be9aSotto 	struct ykbec_softc *sc = (struct ykbec_softc *)arg;
235*e1f5be9aSotto 	u_int val;
236*e1f5be9aSotto 	int current;
237*e1f5be9aSotto 
238*e1f5be9aSotto 	val = ykbec_read16(sc, REG_FAN_SPEED_HIGH);
239*e1f5be9aSotto 	if (val != 0)
240*e1f5be9aSotto 		val = KB3310_FAN_SPEED_DIVIDER / val;
241*e1f5be9aSotto 	else
242*e1f5be9aSotto 		val = UINT_MAX;
243*e1f5be9aSotto 	sc->sc_sensor[0].value = val;
244*e1f5be9aSotto 
245*e1f5be9aSotto 	val = ykbec_read(sc, ECTEMP_CURRENT_REG);
246*e1f5be9aSotto 	sc->sc_sensor[1].value = val * 1000000 + 273150000;
247*e1f5be9aSotto 
248*e1f5be9aSotto 	sc->sc_sensor[2].value = ykbec_read16(sc, REG_DESIGN_CAP_HIGH) * 1000;
249*e1f5be9aSotto 	sc->sc_sensor[3].value = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH) * 1000;
250*e1f5be9aSotto 	sc->sc_sensor[4].value = ykbec_read16(sc, REG_DESIGN_VOL_HIGH) * 1000;
251*e1f5be9aSotto 	current = ykbec_read16(sc, REG_CURRENT_HIGH);
252*e1f5be9aSotto 	if (current & 0x8000)
253*e1f5be9aSotto 		sc->sc_sensor[5].value = (0xffff - current) * 1000;
254*e1f5be9aSotto 	else
255*e1f5be9aSotto 		sc->sc_sensor[5].value = current * 1000;
256*e1f5be9aSotto 	sc->sc_sensor[6].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) * 1000;
257*e1f5be9aSotto 
258*e1f5be9aSotto 	val = ykbec_read16(sc, REG_TEMPERATURE_HIGH);
259*e1f5be9aSotto 	sc->sc_sensor[7].value = val * 1000000 + 273150000;
260*e1f5be9aSotto 
261*e1f5be9aSotto 	sc->sc_sensor[8].value = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH) * 1000;
262*e1f5be9aSotto 
263*e1f5be9aSotto 	val = ykbec_read(sc, REG_BAT_CHARGE);
264*e1f5be9aSotto 	strlcpy(sc->sc_sensor[9].desc, STATUS(REG_BAT_CHARGE_state, val),
265*e1f5be9aSotto 	    sizeof(sc->sc_sensor[9].desc));
266*e1f5be9aSotto 
267*e1f5be9aSotto 	val = (val << 8) | ykbec_read(sc, REG_BAT_STATUS);
268*e1f5be9aSotto 	if (!(val & BAT_STATUS_BAT_EXISTS))
269*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",not available",
270*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
271*e1f5be9aSotto 	if (val & BAT_STATUS_BAT_FULL)
272*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",full",
273*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
274*e1f5be9aSotto 	if (val & BAT_STATUS_BAT_DESTROY)
275*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",bad",
276*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
277*e1f5be9aSotto 	if (val & BAT_STATUS_BAT_LOW)
278*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",low",
279*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
280*e1f5be9aSotto 
281*e1f5be9aSotto 	val = (val << 8) | ykbec_read(sc, REG_CHARGE_STATUS);
282*e1f5be9aSotto 	if (val & CHARGE_STATUS_PRECHARGE)
283*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",precharge",
284*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
285*e1f5be9aSotto 	if (val & CHARGE_STATUS_OVERHEAT)
286*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",overheat",
287*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
288*e1f5be9aSotto #if 0
289*e1f5be9aSotto 	val = ykbec_read(sc, REG_BAT_STATE);
290*e1f5be9aSotto 	if (val & BAT_STATE_CHARGING)
291*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",charging",
292*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
293*e1f5be9aSotto 	if (val & BAT_STATE_DISCHARGING)
294*e1f5be9aSotto 		strlcat(sc->sc_sensor[9].desc, ",discharging",
295*e1f5be9aSotto 		    sizeof(sc->sc_sensor[9].desc));
296*e1f5be9aSotto #endif
297*e1f5be9aSotto 	sc->sc_sensor[9].value = val;
298*e1f5be9aSotto }
299