nsclpcsio_isa.c revision 1.19 1 /* $NetBSD: nsclpcsio_isa.c,v 1.19 2007/09/09 05:26:18 xtraeme Exp $ */
2
3 /*
4 * Copyright (c) 2002
5 * Matthias Drochner. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: nsclpcsio_isa.c,v 1.19 2007/09/09 05:26:18 xtraeme Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/mutex.h>
36 #include <sys/gpio.h>
37 #include <machine/bus.h>
38
39 /* Don't use gpio for now in the LKM */
40 #ifdef _LKM
41 #undef NGPIO
42 #endif
43
44 #include <dev/isa/isareg.h>
45 #include <dev/isa/isavar.h>
46
47 #ifndef _LKM
48 #include "gpio.h"
49 #endif
50 #if NGPIO > 0
51 #include <dev/gpio/gpiovar.h>
52 #endif
53 #include <dev/sysmon/sysmonvar.h>
54
55 static int nsclpcsio_isa_match(struct device *, struct cfdata *, void *);
56 static void nsclpcsio_isa_attach(struct device *, struct device *, void *);
57 static int nsclpcsio_isa_detach(struct device *, int);
58
59 #define GPIO_NPINS 29
60 #define SIO_GPIO_CONF_OUTPUTEN (1 << 0)
61 #define SIO_GPIO_CONF_PUSHPULL (1 << 1)
62 #define SIO_GPIO_CONF_PULLUP (1 << 2)
63
64 struct nsclpcsio_softc {
65 struct device sc_dev;
66 bus_space_tag_t sc_iot, sc_gpio_iot, sc_tms_iot;
67 bus_space_handle_t sc_ioh, sc_gpio_ioh, sc_tms_ioh;
68
69 envsys_data_t sc_data[3];
70 struct sysmon_envsys sc_sysmon;
71 kmutex_t sc_lock;
72
73 #if NGPIO > 0
74 /* GPIO */
75 struct gpio_chipset_tag sc_gpio_gc;
76 struct gpio_pin sc_gpio_pins[GPIO_NPINS];
77 #endif
78 };
79
80 #define GPIO_READ(sc, reg) \
81 bus_space_read_1((sc)->sc_gpio_iot, \
82 (sc)->sc_gpio_ioh, (reg))
83 #define GPIO_WRITE(sc, reg, val) \
84 bus_space_write_1((sc)->sc_gpio_iot, \
85 (sc)->sc_gpio_ioh, (reg), (val))
86
87 CFATTACH_DECL(nsclpcsio_isa, sizeof(struct nsclpcsio_softc),
88 nsclpcsio_isa_match, nsclpcsio_isa_attach, nsclpcsio_isa_detach, NULL);
89
90 static u_int8_t nsread(bus_space_tag_t, bus_space_handle_t, int);
91 static void nswrite(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
92 static int nscheck(bus_space_tag_t, int);
93
94 static void tms_update(struct nsclpcsio_softc *, int);
95 static int tms_gtredata(struct sysmon_envsys *, envsys_data_t *);
96
97 #if NGPIO > 0
98 static void nsclpcsio_gpio_init(struct nsclpcsio_softc *);
99 static void nsclpcsio_gpio_pin_select(struct nsclpcsio_softc *, int);
100 static void nsclpcsio_gpio_pin_write(void *, int, int);
101 static int nsclpcsio_gpio_pin_read(void *, int);
102 static void nsclpcsio_gpio_pin_ctl(void *, int, int);
103 #endif
104
105 static u_int8_t
106 nsread(iot, ioh, idx)
107 bus_space_tag_t iot;
108 bus_space_handle_t ioh;
109 int idx;
110 {
111
112 bus_space_write_1(iot, ioh, 0, idx);
113 return (bus_space_read_1(iot, ioh, 1));
114 }
115
116 static void
117 nswrite(iot, ioh, idx, data)
118 bus_space_tag_t iot;
119 bus_space_handle_t ioh;
120 int idx;
121 u_int8_t data;
122 {
123
124 bus_space_write_1(iot, ioh, 0, idx);
125 bus_space_write_1(iot, ioh, 1, data);
126 }
127
128 static int
129 nscheck(iot, base)
130 bus_space_tag_t iot;
131 int base;
132 {
133 bus_space_handle_t ioh;
134 int rv = 0;
135
136 if (bus_space_map(iot, base, 2, 0, &ioh))
137 return (0);
138
139 /* XXX this is for PC87366 only for now */
140 if (nsread(iot, ioh, 0x20) == 0xe9)
141 rv = 1;
142
143 bus_space_unmap(iot, ioh, 2);
144 return (rv);
145 }
146
147 static int
148 nsclpcsio_isa_match(struct device *parent,
149 struct cfdata *match, void *aux)
150 {
151 struct isa_attach_args *ia = aux;
152 int iobase;
153
154 if (ISA_DIRECT_CONFIG(ia))
155 return (0);
156
157 if (ia->ia_nio > 0 && ia->ia_io[0].ir_addr != ISA_UNKNOWN_PORT) {
158 /* XXX check for legal iobase ??? */
159 if (nscheck(ia->ia_iot, ia->ia_io[0].ir_addr)) {
160 iobase = ia->ia_io[0].ir_addr;
161 goto found;
162 }
163 return (0);
164 }
165
166 /* PC87366 has two possible locations depending on wiring */
167 if (nscheck(ia->ia_iot, 0x2e)) {
168 iobase = 0x2e;
169 goto found;
170 }
171 if (nscheck(ia->ia_iot, 0x4e)) {
172 iobase = 0x4e;
173 goto found;
174 }
175 return (0);
176
177 found:
178 ia->ia_nio = 1;
179 ia->ia_io[0].ir_addr = iobase;
180 ia->ia_io[0].ir_size = 2;
181 ia->ia_niomem = 0;
182 ia->ia_nirq = 0;
183 ia->ia_ndrq = 0;
184 return (1);
185 }
186
187 static void
188 nsclpcsio_isa_attach(struct device *parent, struct device *self,
189 void *aux)
190 {
191 struct nsclpcsio_softc *sc = (void *)self;
192 struct isa_attach_args *ia = aux;
193 #if NGPIO > 0
194 struct gpiobus_attach_args gba;
195 #endif
196 bus_space_tag_t iot;
197 bus_space_handle_t ioh;
198 u_int8_t val;
199 int tms_iobase, gpio_iobase = 0;
200 int i;
201
202 sc->sc_iot = iot = ia->ia_iot;
203 if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, 2, 0, &ioh)) {
204 printf(": can't map i/o space\n");
205 return;
206 }
207 sc->sc_ioh = ioh;
208 printf(": NSC PC87366 rev. %d\n", nsread(iot, ioh, 0x27));
209
210 mutex_init(&sc->sc_lock, MUTEX_DRIVER, IPL_NONE);
211
212 nswrite(iot, ioh, 0x07, 0x07); /* select gpio */
213
214 val = nsread(iot, ioh, 0x30); /* control register */
215 if (!(val & 1)) {
216 printf("%s: GPIO disabled\n", sc->sc_dev.dv_xname);
217 } else {
218 gpio_iobase = (nsread(iot, ioh, 0x60) << 8) |
219 nsread(iot, ioh, 0x61);
220 sc->sc_gpio_iot = iot;
221 if (bus_space_map(iot, gpio_iobase, 0x2c, 0,
222 &sc->sc_gpio_ioh)) {
223 printf("%s: can't map GPIO i/o space\n",
224 sc->sc_dev.dv_xname);
225 return;
226 }
227 printf("%s: GPIO at 0x%x\n", sc->sc_dev.dv_xname, gpio_iobase);
228
229 #if NGPIO > 0
230 nsclpcsio_gpio_init(sc);
231 #endif
232 }
233
234 nswrite(iot, ioh, 0x07, 0x0e); /* select tms */
235
236 val = nsread(iot, ioh, 0x30); /* control register */
237 if (!(val & 1)) {
238 printf("%s: TMS disabled\n", sc->sc_dev.dv_xname);
239 return;
240 }
241
242 tms_iobase = (nsread(iot, ioh, 0x60) << 8) | nsread(iot, ioh, 0x61);
243 sc->sc_tms_iot = iot;
244 if (bus_space_map(iot, tms_iobase, 16, 0, &sc->sc_tms_ioh)) {
245 printf("%s: can't map TMS i/o space\n", sc->sc_dev.dv_xname);
246 return;
247 }
248 printf("%s: TMS at 0x%x\n", sc->sc_dev.dv_xname, tms_iobase);
249
250 if (bus_space_read_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x08) & 1) {
251 printf("%s: TMS in standby mode\n", sc->sc_dev.dv_xname);
252
253 /* Wake up the TMS and enable all temperature sensors. */
254 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x08, 0x00);
255 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x09, 0x00);
256 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x0a, 0x01);
257 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x09, 0x01);
258 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x0a, 0x01);
259 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x09, 0x02);
260 bus_space_write_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x0a, 0x01);
261
262 if (!(bus_space_read_1(sc->sc_tms_iot, sc->sc_tms_ioh, 0x08)
263 & 1)) {
264 printf("%s: TMS awoken\n", sc->sc_dev.dv_xname);
265 } else {
266 return;
267 }
268 }
269
270 /* Initialize sensor meta data */
271 for (i = 0; i < 3; i++) {
272 sc->sc_data[i].sensor = i;
273 sc->sc_data[i].units =ENVSYS_STEMP;
274 }
275 strcpy(sc->sc_data[0].desc, "TSENS1");
276 strcpy(sc->sc_data[1].desc, "TSENS2");
277 strcpy(sc->sc_data[2].desc, "TNSC");
278
279 /* Get initial set of sensor values. */
280 for (i = 0; i < 3; i++)
281 tms_update(sc, i);
282
283 /*
284 * Hook into the System Monitor.
285 */
286 sc->sc_sysmon.sme_name = sc->sc_dev.dv_xname;
287 sc->sc_sysmon.sme_sensor_data = sc->sc_data;
288 sc->sc_sysmon.sme_cookie = sc;
289 sc->sc_sysmon.sme_gtredata = tms_gtredata;
290 sc->sc_sysmon.sme_nsensors = 3;
291
292 if (sysmon_envsys_register(&sc->sc_sysmon))
293 printf("%s: unable to register with sysmon\n",
294 sc->sc_dev.dv_xname);
295
296 #if NGPIO > 0
297 /* attach GPIO framework */
298 if (gpio_iobase != 0) {
299 gba.gba_gc = &sc->sc_gpio_gc;
300 gba.gba_pins = sc->sc_gpio_pins;
301 gba.gba_npins = GPIO_NPINS;
302 config_found_ia(&sc->sc_dev, "gpiobus", &gba, NULL);
303 }
304 #endif
305 return;
306 }
307
308 static int
309 nsclpcsio_isa_detach(struct device *self, int flags)
310 {
311 struct nsclpcsio_softc *sc = device_private(self);
312
313 sysmon_envsys_unregister(&sc->sc_sysmon);
314 bus_space_unmap(sc->sc_iot, sc->sc_ioh, 2);
315 return 0;
316 }
317
318 static void
319 tms_update(sc, chan)
320 struct nsclpcsio_softc *sc;
321 int chan;
322 {
323 bus_space_tag_t iot = sc->sc_tms_iot;
324 bus_space_handle_t ioh = sc->sc_tms_ioh;
325 u_int8_t status;
326 int8_t temp, ctemp; /* signed!! */
327
328 mutex_enter(&sc->sc_lock);
329
330 nswrite(iot, ioh, 0x07, 0x0e); /* select tms */
331
332 bus_space_write_1(iot, ioh, 0x09, chan); /* select */
333
334 status = bus_space_read_1(iot, ioh, 0x0a); /* config/status */
335 if (status & 0x01) {
336 /* enabled */
337 sc->sc_data[chan].state = ENVSYS_SVALID;
338 } else {
339 sc->sc_data[chan].state = ENVSYS_SINVALID;
340 mutex_exit(&sc->sc_lock);
341 return;
342 }
343
344 /*
345 * If the channel is enabled, it is considered valid.
346 * An "open circuit" might be temporary.
347 */
348 #if 0
349 sc->sc_data[chan].state = ENVSYS_SVALID;
350 if (status & 0x40) {
351 /*
352 * open circuit
353 * XXX should have a warning for it
354 */
355 sc->sc_data[chan].warnflags = ENVSYS_WARN_OK; /* XXX */
356 mutex_exit(&sc->sc_lock);
357 return;
358 }
359 #endif
360
361 /* get current temperature in signed degree celsius */
362 temp = bus_space_read_1(iot, ioh, 0x0b);
363 sc->sc_data[chan].value_cur = (int)temp * 1000000 + 273150000;
364 sc->sc_data[chan].state = ENVSYS_SVALID;
365
366 if (status & 0x0e) { /* any temperature warning? */
367 /*
368 * XXX the chip documentation is a bit fuzzy - it doesn't state
369 * that the hardware OTS output depends on the "overtemp"
370 * warning bit.
371 * It seems the output gets cleared if the warning bit is reset.
372 * This sucks.
373 * The hardware might do something useful with output pins, eg
374 * throttling the CPU, so we must do the comparision in
375 * software, and only reset the bits if the reason is gone.
376 */
377 if (status & 0x02) { /* low limit */
378 sc->sc_data[chan].state = ENVSYS_SWARNUNDER;
379 /* read low limit */
380 ctemp = bus_space_read_1(iot, ioh, 0x0d);
381 if (temp <= ctemp) /* still valid, don't reset */
382 status &= ~0x02;
383 }
384 if (status & 0x04) { /* high limit */
385 sc->sc_data[chan].state = ENVSYS_SWARNOVER;
386 /* read high limit */
387 ctemp = bus_space_read_1(iot, ioh, 0x0c);
388 if (temp >= ctemp) /* still valid, don't reset */
389 status &= ~0x04;
390 }
391 if (status & 0x08) { /* overtemperature */
392 sc->sc_data[chan].state = ENVSYS_SCRITOVER;
393 /* read overtemperature limit */
394 ctemp = bus_space_read_1(iot, ioh, 0x0e);
395 if (temp >= ctemp) /* still valid, don't reset */
396 status &= ~0x08;
397 }
398
399 /* clear outdated warnings */
400 if (status & 0x0e)
401 bus_space_write_1(iot, ioh, 0x0a, status);
402 }
403
404 mutex_exit(&sc->sc_lock);
405
406 return;
407 }
408
409 static int
410 tms_gtredata(struct sysmon_envsys *sme, envsys_data_t *data)
411 {
412 struct nsclpcsio_softc *sc = sme->sme_cookie;
413
414 tms_update(sc, data->sensor);
415 return (0);
416 }
417
418 #if NGPIO > 0
419 static void
420 nsclpcsio_gpio_pin_select(struct nsclpcsio_softc *sc, int pin)
421 {
422 u_int8_t v;
423 bus_space_tag_t iot = sc->sc_iot;
424 bus_space_handle_t ioh = sc->sc_ioh;
425
426 v = ((pin / 8) << 4) | (pin % 8);
427
428 nswrite(iot, ioh, 0x07, 0x07); /* select gpio */
429 nswrite(iot, ioh, 0xf0, v);
430
431 return;
432 }
433
434 static void
435 nsclpcsio_gpio_init(struct nsclpcsio_softc *sc)
436 {
437 int i;
438
439 for (i = 0; i < GPIO_NPINS; i++) {
440 sc->sc_gpio_pins[i].pin_num = i;
441 sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
442 GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
443 GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
444 GPIO_PIN_PULLUP;
445 /* safe defaults */
446 sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_TRISTATE;
447 sc->sc_gpio_pins[i].pin_state = GPIO_PIN_LOW;
448 nsclpcsio_gpio_pin_ctl(sc, i, sc->sc_gpio_pins[i].pin_flags);
449 nsclpcsio_gpio_pin_write(sc, i, sc->sc_gpio_pins[i].pin_state);
450 }
451
452 /* create controller tag */
453 sc->sc_gpio_gc.gp_cookie = sc;
454 sc->sc_gpio_gc.gp_pin_read = nsclpcsio_gpio_pin_read;
455 sc->sc_gpio_gc.gp_pin_write = nsclpcsio_gpio_pin_write;
456 sc->sc_gpio_gc.gp_pin_ctl = nsclpcsio_gpio_pin_ctl;
457 }
458
459 static int
460 nsclpcsio_gpio_pin_read(void *aux, int pin)
461 {
462 struct nsclpcsio_softc *sc = (struct nsclpcsio_softc *)aux;
463 int port, shift, reg;
464 u_int8_t v;
465
466 reg = 0x00;
467 port = pin / 8;
468 shift = pin % 8;
469
470 switch (port) {
471 case 0: reg = 0x00; break;
472 case 1: reg = 0x04; break;
473 case 2: reg = 0x08; break;
474 case 3: reg = 0x0a; break;
475 }
476
477 v = GPIO_READ(sc, reg);
478
479 return ((v >> shift) & 0x1);
480 }
481
482 static void
483 nsclpcsio_gpio_pin_write(void *aux, int pin, int v)
484 {
485 struct nsclpcsio_softc *sc = (struct nsclpcsio_softc *)aux;
486 int port, shift, reg;
487 u_int8_t d;
488
489 port = pin / 8;
490 shift = pin % 8;
491
492 switch (port) {
493 case 0: reg = 0x00; break;
494 case 1: reg = 0x04; break;
495 case 2: reg = 0x08; break;
496 case 3: reg = 0x0a; break;
497 default: reg = 0x00; break; /* shouldn't happen */
498 }
499
500 d = GPIO_READ(sc, reg);
501 if (v == 0)
502 d &= ~(1 << shift);
503 else if (v == 1)
504 d |= (1 << shift);
505 GPIO_WRITE(sc, reg, d);
506
507 return;
508 }
509
510 void
511 nsclpcsio_gpio_pin_ctl(void *aux, int pin, int flags)
512 {
513 struct nsclpcsio_softc *sc = (struct nsclpcsio_softc *)aux;
514 u_int8_t conf;
515
516 mutex_enter(&sc->sc_lock);
517
518 nswrite(sc->sc_iot, sc->sc_ioh, 0x07, 0x07); /* select gpio */
519 nsclpcsio_gpio_pin_select(sc, pin);
520 conf = nsread(sc->sc_iot, sc->sc_ioh, 0xf1);
521
522 conf &= ~(SIO_GPIO_CONF_OUTPUTEN | SIO_GPIO_CONF_PUSHPULL |
523 SIO_GPIO_CONF_PULLUP);
524 if ((flags & GPIO_PIN_TRISTATE) == 0)
525 conf |= SIO_GPIO_CONF_OUTPUTEN;
526 if (flags & GPIO_PIN_PUSHPULL)
527 conf |= SIO_GPIO_CONF_PUSHPULL;
528 if (flags & GPIO_PIN_PULLUP)
529 conf |= SIO_GPIO_CONF_PULLUP;
530
531 nswrite(sc->sc_iot, sc->sc_ioh, 0xf1, conf);
532
533 mutex_exit(&sc->sc_lock);
534
535 return;
536 }
537 #endif /* NGPIO */
538