1/* $NetBSD: nsclpcsio_isa.c,v 1.34 2021/08/07 16:19:12 thorpej 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/*
30 * National Semiconductor PC87366 LPC Super I/O driver.
31 * Supported logical devices: GPIO, TMS, VLM.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: nsclpcsio_isa.c,v 1.34 2021/08/07 16:19:12 thorpej Exp $");
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/device.h>
40#include <sys/mutex.h>
41#include <sys/gpio.h>
42#include <sys/bus.h>
43#include <sys/module.h>
44
45/* Don't use gpio for now in the module */
46#ifdef _MODULE
47#undef NGPIO
48#endif
49
50#include <dev/isa/isareg.h>
51#include <dev/isa/isavar.h>
52
53#ifndef _MODULE
54#include "gpio.h"
55#endif
56#if NGPIO > 0
57#include <dev/gpio/gpiovar.h>
58#endif
59#include <dev/sysmon/sysmonvar.h>
60
61#define SIO_REG_SID	0x20	/* Super I/O ID */
62#define SIO_SID_PC87366	0xE9	/* PC87366 is identified by 0xE9.*/
63
64#define SIO_REG_SRID	0x27	/* Super I/O Revision */
65
66#define SIO_REG_LDN	0x07	/* Logical Device Number */
67#define SIO_LDN_FDC	0x00	/* Floppy Disk Controller (FDC) */
68#define SIO_LDN_PP	0x01	/* Parallel Port (PP) */
69#define SIO_LDN_SP2	0x02	/* Serial Port 2 with IR (SP2) */
70#define SIO_LDN_SP1	0x03	/* Serial Port 1 (SP1) */
71#define SIO_LDN_SWC	0x04	/* System Wake-Up Control (SWC) */
72#define SIO_LDN_KBCM	0x05	/* Mouse Controller (KBC) */
73#define SIO_LDN_KBCK	0x06	/* Keyboard Controller (KBC) */
74#define SIO_LDN_GPIO	0x07	/* General-Purpose I/O (GPIO) Ports */
75#define SIO_LDN_ACB	0x08	/* ACCESS.bus Interface (ACB) */
76#define SIO_LDN_FSCM	0x09	/* Fan Speed Control and Monitor (FSCM) */
77#define SIO_LDN_WDT	0x0A	/* WATCHDOG Timer (WDT) */
78#define SIO_LDN_GMP	0x0B	/* Game Port (GMP) */
79#define SIO_LDN_MIDI	0x0C	/* Musical Instrument Digital Interface */
80#define SIO_LDN_VLM	0x0D	/* Voltage Level Monitor (VLM) */
81#define SIO_LDN_TMS	0x0E	/* Temperature Sensor (TMS) */
82
83#define SIO_REG_ACTIVE	0x30	/* Logical Device Activate Register */
84#define SIO_ACTIVE_EN		0x01	/* enabled */
85
86#define SIO_REG_IO_MSB	0x60	/* I/O Port Base, bits 15-8 */
87#define SIO_REG_IO_LSB	0x61	/* I/O Port Base, bits 7-0 */
88
89#define SIO_LDNUM	15	/* total number of logical devices */
90
91/* Supported logical devices description */
92static const struct {
93	const char *ld_name;
94	int ld_num;
95	int ld_iosize;
96} sio_ld[] = {
97	{ "GPIO",	SIO_LDN_GPIO,	16 },
98	{ "VLM",	SIO_LDN_VLM,	16 },
99	{ "TMS",	SIO_LDN_TMS,	16 }
100};
101
102/* GPIO */
103#define SIO_GPIO_PINSEL	0xf0
104#define SIO_GPIO_PINCFG	0xf1
105#define SIO_GPIO_PINEV	0xf2
106
107#define	SIO_GPIO_CONF_OUTPUTEN	(1 << 0)
108#define	SIO_GPIO_CONF_PUSHPULL	(1 << 1)
109#define	SIO_GPIO_CONF_PULLUP	(1 << 2)
110
111#define SIO_GPDO0	0x00
112#define SIO_GPDI0	0x01
113#define SIO_GPEVEN0	0x02
114#define SIO_GPEVST0	0x03
115#define SIO_GPDO1	0x04
116#define SIO_GPDI1	0x05
117#define SIO_GPEVEN1	0x06
118#define SIO_GPEVST1	0x07
119#define SIO_GPDO2	0x08
120#define SIO_GPDI2	0x09
121#define SIO_GPDO3	0x0a
122#define SIO_GPDI3	0x0b
123
124#define SIO_GPIO_NPINS	29
125
126/* TMS */
127#define SIO_TEVSTS	0x00	/* Temperature Event Status */
128#define SIO_TEVSMI	0x02	/* Temperature Event to SMI */
129#define SIO_TEVIRQ	0x04	/* Temperature Event to IRQ */
130#define SIO_TMSCFG	0x08	/* TMS Configuration */
131#define SIO_TMSBS	0x09	/* TMS Bank Select */
132#define SIO_TCHCFST	0x0a	/* Temperature Channel Config and Status */
133#define SIO_RDCHT	0x0b	/* Read Channel Temperature */
134#define SIO_CHTH	0x0c	/* Channel Temperature High Limit */
135#define SIO_CHTL	0x0d	/* Channel Temperature Low Limit */
136#define SIO_CHOTL	0x0e	/* Channel Overtemperature Limit */
137
138/* VLM */
139#define SIO_VEVSTS0	0x00	/* Voltage Event Status 0 */
140#define SIO_VEVSTS1	0x01	/* Voltage Event Status 1 */
141#define SIO_VEVSMI0	0x02	/* Voltage Event to SMI 0 */
142#define SIO_VEVSMI1	0x03	/* Voltage Event to SMI 1 */
143#define SIO_VEVIRQ0	0x04	/* Voltage Event to IRQ 0 */
144#define SIO_VEVIRQ1	0x05	/* Voltage Event to IRQ 1 */
145#define SIO_VID 	0x06	/* Voltage ID */
146#define SIO_VCNVR	0x07	/* Voltage Conversion Rate */
147#define SIO_VLMCFG	0x08	/* VLM Configuration */
148#define SIO_VLMBS	0x09	/* VLM Bank Select */
149#define SIO_VCHCFST	0x0a	/* Voltage Channel Config and Status */
150#define SIO_RDCHV	0x0b	/* Read Channel Voltage */
151#define SIO_CHVH	0x0c	/* Channel Voltage High Limit */
152#define SIO_CHVL	0x0d	/* Channel Voltage Low Limit */
153#define SIO_OTSL	0x0e	/* Overtemperature Shutdown Limit */
154
155#define SIO_REG_SIOCF1	0x21
156#define SIO_REG_SIOCF2	0x22
157#define SIO_REG_SIOCF3	0x23
158#define SIO_REG_SIOCF4	0x24
159#define SIO_REG_SIOCF5	0x25
160#define SIO_REG_SIOCF8	0x28
161#define SIO_REG_SIOCFA	0x2a
162#define SIO_REG_SIOCFB	0x2b
163#define SIO_REG_SIOCFC	0x2c
164#define SIO_REG_SIOCFD	0x2d
165
166#define SIO_VLM_OFF	3
167#define SIO_NUM_SENSORS	(SIO_VLM_OFF + 14)
168#define SIO_VREF	1235	/* 1000.0 * VREF */
169
170struct nsclpcsio_softc {
171	device_t sc_dev;
172
173	bus_space_tag_t sc_iot;
174	bus_space_handle_t sc_ioh;
175
176	bus_space_handle_t sc_ld_ioh[SIO_LDNUM];
177	int sc_ld_en[SIO_LDNUM];
178
179	/* TMS and VLM */
180	struct sysmon_envsys *sc_sme;
181	envsys_data_t sc_sensor[SIO_NUM_SENSORS];
182
183	kmutex_t sc_lock;
184#if NGPIO > 0
185	/* GPIO */
186	struct gpio_chipset_tag sc_gpio_gc;
187	struct gpio_pin sc_gpio_pins[SIO_GPIO_NPINS];
188#endif
189};
190
191#define GPIO_READ(sc, reg)			\
192	bus_space_read_1((sc)->sc_iot,			\
193	    (sc)->sc_ld_ioh[SIO_LDN_GPIO], (reg))
194#define GPIO_WRITE(sc, reg, val)		\
195	bus_space_write_1((sc)->sc_iot,			\
196	    (sc)->sc_ld_ioh[SIO_LDN_GPIO], (reg), (val))
197#define TMS_WRITE(sc, reg, val)				\
198	bus_space_write_1((sc)->sc_iot,			\
199	    (sc)->sc_ld_ioh[SIO_LDN_TMS], (reg), (val))
200#define TMS_READ(sc, reg)				\
201	bus_space_read_1((sc)->sc_iot,			\
202	    (sc)->sc_ld_ioh[SIO_LDN_TMS], (reg))
203#define VLM_WRITE(sc, reg, val)				\
204	bus_space_write_1((sc)->sc_iot,			\
205	    (sc)->sc_ld_ioh[SIO_LDN_VLM], (reg), (val))
206#define VLM_READ(sc, reg)				\
207	bus_space_read_1((sc)->sc_iot,			\
208	    (sc)->sc_ld_ioh[SIO_LDN_VLM], (reg))
209
210static int	nsclpcsio_isa_match(device_t, cfdata_t, void *);
211static void	nsclpcsio_isa_attach(device_t, device_t, void *);
212static int	nsclpcsio_isa_detach(device_t, int);
213
214CFATTACH_DECL_NEW(nsclpcsio_isa, sizeof(struct nsclpcsio_softc),
215    nsclpcsio_isa_match, nsclpcsio_isa_attach, nsclpcsio_isa_detach, NULL);
216
217static uint8_t	nsread(bus_space_tag_t, bus_space_handle_t, int);
218static void	nswrite(bus_space_tag_t, bus_space_handle_t, int, uint8_t);
219static int	nscheck(bus_space_tag_t, int);
220
221static void	nsclpcsio_tms_init(struct nsclpcsio_softc *);
222static void	nsclpcsio_vlm_init(struct nsclpcsio_softc *);
223static void	nsclpcsio_refresh(struct sysmon_envsys *, envsys_data_t *);
224
225#if NGPIO > 0
226static void nsclpcsio_gpio_init(struct nsclpcsio_softc *);
227static void nsclpcsio_gpio_pin_select(struct nsclpcsio_softc *, int);
228static void nsclpcsio_gpio_pin_write(void *, int, int);
229static int nsclpcsio_gpio_pin_read(void *, int);
230static void nsclpcsio_gpio_pin_ctl(void *, int, int);
231#endif
232
233static uint8_t
234nsread(bus_space_tag_t iot, bus_space_handle_t ioh, int idx)
235{
236	bus_space_write_1(iot, ioh, 0, idx);
237	return bus_space_read_1(iot, ioh, 1);
238}
239
240static void
241nswrite(bus_space_tag_t iot, bus_space_handle_t ioh, int idx, uint8_t data)
242{
243	bus_space_write_1(iot, ioh, 0, idx);
244	bus_space_write_1(iot, ioh, 1, data);
245}
246
247static int
248nscheck(bus_space_tag_t iot, int base)
249{
250	bus_space_handle_t ioh;
251	int rv = 0;
252
253	if (bus_space_map(iot, base, 2, 0, &ioh))
254		return 0;
255
256	/* XXX this is for PC87366 only for now */
257	if (nsread(iot, ioh, SIO_REG_SID) == SIO_SID_PC87366)
258		rv = 1;
259
260	bus_space_unmap(iot, ioh, 2);
261	return rv;
262}
263
264static int
265nsclpcsio_isa_match(device_t parent, cfdata_t match, void *aux)
266{
267	struct isa_attach_args *ia = aux;
268	int iobase;
269
270	if (ISA_DIRECT_CONFIG(ia))
271		return 0;
272
273	if (ia->ia_nio > 0 && ia->ia_io[0].ir_addr != ISA_UNKNOWN_PORT) {
274		/* XXX check for legal iobase ??? */
275		if (nscheck(ia->ia_iot, ia->ia_io[0].ir_addr)) {
276			iobase = ia->ia_io[0].ir_addr;
277			goto found;
278		}
279		return 0;
280	}
281
282	/* PC87366 has two possible locations depending on wiring */
283	if (nscheck(ia->ia_iot, 0x2e)) {
284		iobase = 0x2e;
285		goto found;
286	}
287	if (nscheck(ia->ia_iot, 0x4e)) {
288		iobase = 0x4e;
289		goto found;
290	}
291
292	return 0;
293
294found:
295	ia->ia_nio = 1;
296	ia->ia_io[0].ir_addr = iobase;
297	ia->ia_io[0].ir_size = 2;
298	ia->ia_niomem = 0;
299	ia->ia_nirq = 0;
300	ia->ia_ndrq = 0;
301
302	return 1;
303}
304
305static struct sysmon_envsys *
306nsclpcsio_envsys_init(struct nsclpcsio_softc *sc)
307{
308	int i;
309	struct sysmon_envsys *sme;
310
311	sme = sysmon_envsys_create();
312	for (i = 0; i < SIO_NUM_SENSORS; i++) {
313		sc->sc_sensor[i].state = ENVSYS_SINVALID;
314		if (sysmon_envsys_sensor_attach(sme, &sc->sc_sensor[i]) != 0) {
315			aprint_error_dev(sc->sc_dev,
316			    "could not attach sensor %d", i);
317			goto err;
318		}
319	}
320
321	/*
322	 * Hook into the System Monitor.
323	 */
324	sme->sme_name = device_xname(sc->sc_dev);
325	sme->sme_cookie = sc;
326	sme->sme_refresh = nsclpcsio_refresh;
327
328	if ((i = sysmon_envsys_register(sme)) != 0) {
329		aprint_error_dev(sc->sc_dev,
330		    "unable to register with sysmon (%d)\n", i);
331		goto err;
332	}
333	return sme;
334err:
335	sysmon_envsys_destroy(sme);
336	return NULL;
337}
338
339static void
340nsclpcsio_isa_attach(device_t parent, device_t self, void *aux)
341{
342	struct nsclpcsio_softc *sc = device_private(self);
343	struct isa_attach_args *ia = aux;
344#if NGPIO > 0
345	struct gpiobus_attach_args gba;
346#endif
347	int i, iobase;
348
349	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
350
351	sc->sc_dev = self;
352	sc->sc_iot = ia->ia_iot;
353	iobase = ia->ia_io[0].ir_addr;
354
355	if (bus_space_map(ia->ia_iot, iobase, 2, 0, &sc->sc_ioh)) {
356		aprint_error(": can't map i/o space\n");
357		return;
358	}
359
360	aprint_normal(": NSC PC87366 rev. %d ",
361	    nsread(sc->sc_iot, sc->sc_ioh, SIO_REG_SRID));
362
363	/* Configure all supported logical devices */
364	for (i = 0; i < __arraycount(sio_ld); i++) {
365		sc->sc_ld_en[sio_ld[i].ld_num] = 0;
366
367		/* Select the device and check if it's activated */
368		nswrite(sc->sc_iot, sc->sc_ioh, SIO_REG_LDN, sio_ld[i].ld_num);
369		if ((nsread(sc->sc_iot, sc->sc_ioh,
370		    SIO_REG_ACTIVE) & SIO_ACTIVE_EN) == 0)
371			continue;
372
373		/* Map I/O space if necessary */
374		if (sio_ld[i].ld_iosize != 0) {
375			iobase = (nsread(sc->sc_iot, sc->sc_ioh,
376			    SIO_REG_IO_MSB) << 8);
377			iobase |= nsread(sc->sc_iot, sc->sc_ioh,
378			    SIO_REG_IO_LSB);
379			if (bus_space_map(sc->sc_iot, iobase,
380			    sio_ld[i].ld_iosize, 0,
381			    &sc->sc_ld_ioh[sio_ld[i].ld_num]))
382				continue;
383		}
384
385		sc->sc_ld_en[sio_ld[i].ld_num] = 1;
386		aprint_normal("%s ", sio_ld[i].ld_name);
387	}
388
389	aprint_normal("\n");
390
391#if NGPIO > 0
392	nsclpcsio_gpio_init(sc);
393#endif
394	nsclpcsio_tms_init(sc);
395	nsclpcsio_vlm_init(sc);
396	sc->sc_sme = nsclpcsio_envsys_init(sc);
397
398#if NGPIO > 0
399	/* attach GPIO framework */
400	if (sc->sc_ld_en[SIO_LDN_GPIO]) {
401		gba.gba_gc = &sc->sc_gpio_gc;
402		gba.gba_pins = sc->sc_gpio_pins;
403		gba.gba_npins = SIO_GPIO_NPINS;
404		config_found(self, &gba, NULL, CFARGS_NONE);
405	}
406#endif
407}
408
409static int
410nsclpcsio_isa_detach(device_t self, int flags)
411{
412	int i, rc;
413	struct nsclpcsio_softc *sc = device_private(self);
414
415	if ((rc = config_detach_children(self, flags)) != 0)
416		return rc;
417
418	if (sc->sc_sme != NULL)
419		sysmon_envsys_unregister(sc->sc_sme);
420	mutex_destroy(&sc->sc_lock);
421
422	for (i = 0; i < __arraycount(sio_ld); i++) {
423		if (sc->sc_ld_en[sio_ld[i].ld_num] &&
424		    sio_ld[i].ld_iosize != 0) {
425			bus_space_unmap(sc->sc_iot,
426			    sc->sc_ld_ioh[sio_ld[i].ld_num],
427			    sio_ld[i].ld_iosize);
428		}
429	}
430
431	bus_space_unmap(sc->sc_iot, sc->sc_ioh, 2);
432
433	return 0;
434}
435
436static void
437nsclpcsio_tms_init(struct nsclpcsio_softc *sc)
438{
439	int i;
440
441	/* Initialisation, PC87366.pdf, page 208 */
442	TMS_WRITE(sc, 0x08, 0x00);
443	TMS_WRITE(sc, 0x09, 0x0f);
444	TMS_WRITE(sc, 0x0a, 0x08);
445	TMS_WRITE(sc, 0x0b, 0x04);
446	TMS_WRITE(sc, 0x0c, 0x35);
447	TMS_WRITE(sc, 0x0d, 0x05);
448	TMS_WRITE(sc, 0x0e, 0x05);
449
450	TMS_WRITE(sc, SIO_TMSCFG, 0x00);
451
452	for (i = 0; i < SIO_VLM_OFF; i++) {
453		TMS_WRITE(sc, SIO_TMSBS, i);
454		TMS_WRITE(sc, SIO_TCHCFST, 0x01);
455		sc->sc_sensor[i].units = ENVSYS_STEMP;
456	}
457
458#define COPYDESCR(x, y)					\
459	do {						\
460		(void)strlcpy((x), (y), sizeof(x));	\
461	} while (/* CONSTCOND */ 0)
462
463	COPYDESCR(sc->sc_sensor[0].desc, "TSENS1");
464	COPYDESCR(sc->sc_sensor[1].desc, "TSENS2");
465	COPYDESCR(sc->sc_sensor[2].desc, "TNSC");
466}
467
468static void
469nsclpcsio_vlm_init(struct nsclpcsio_softc *sc)
470{
471	int i;
472	char tmp[16];
473	envsys_data_t *sensor = &sc->sc_sensor[SIO_VLM_OFF];
474
475	for (i = 0; i < SIO_NUM_SENSORS - SIO_VLM_OFF; i++) {
476		VLM_WRITE(sc, SIO_VLMBS, i);
477		VLM_WRITE(sc, SIO_VCHCFST, 0x01);
478		sensor[i].units = ENVSYS_SVOLTS_DC;
479	}
480
481	for (i = 0; i < 7; i++) {
482		(void)snprintf(tmp, sizeof(tmp), "VSENS%d", i);
483		COPYDESCR(sensor[i].desc, tmp);
484	}
485
486	COPYDESCR(sensor[7 ].desc, "VSB");
487	COPYDESCR(sensor[8 ].desc, "VDD");
488	COPYDESCR(sensor[9 ].desc, "VBAT");
489	COPYDESCR(sensor[10].desc, "AVDD");
490	COPYDESCR(sensor[11].desc, "TS1");
491	COPYDESCR(sensor[12].desc, "TS2");
492	COPYDESCR(sensor[13].desc, "TS3");
493}
494
495
496static void
497nsclpcsio_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
498{
499	struct nsclpcsio_softc *sc = sme->sme_cookie;
500	uint8_t status, data;
501	int8_t sdata = 0;
502	int scale, rfact;
503
504	scale = rfact = 0;
505	status = data = 0;
506
507	mutex_enter(&sc->sc_lock);
508	/* TMS */
509	if (edata->sensor < SIO_VLM_OFF && sc->sc_ld_en[SIO_LDN_TMS]) {
510		TMS_WRITE(sc, SIO_TMSBS, edata->sensor);
511		status = TMS_READ(sc, SIO_TCHCFST);
512		if (!(status & 0x01))
513			edata->state = ENVSYS_SINVALID;
514
515		sdata = TMS_READ(sc, SIO_RDCHT);
516		edata->value_cur = sdata * 1000000 + 273150000;
517		edata->state = ENVSYS_SVALID;
518	/* VLM */
519	} else if (edata->sensor >= SIO_VLM_OFF &&
520		   edata->sensor < SIO_NUM_SENSORS &&
521		   sc->sc_ld_en[SIO_LDN_VLM]) {
522		VLM_WRITE(sc, SIO_VLMBS, edata->sensor - SIO_VLM_OFF);
523		status = VLM_READ(sc, SIO_VCHCFST);
524		if (!(status & 0x01)) {
525			edata->state = ENVSYS_SINVALID;
526		} else {
527			data = VLM_READ(sc, SIO_RDCHV);
528			scale = 1;
529			switch (edata->sensor - SIO_VLM_OFF) {
530			case 7:
531			case 8:
532			case 10:
533				scale = 2;
534				break;
535			}
536			/* Vi = (2.45�0.05)*VREF *RDCHVi / 256 */
537			rfact = 10 * scale * ((245 * SIO_VREF) >> 8);
538			edata->value_cur = data * rfact;
539			edata->state = ENVSYS_SVALID;
540		}
541	}
542	mutex_exit(&sc->sc_lock);
543}
544
545#if NGPIO > 0
546static void
547nsclpcsio_gpio_pin_select(struct nsclpcsio_softc *sc, int pin)
548{
549	uint8_t v;
550
551	v = ((pin / 8) << 4) | (pin % 8);
552
553	nswrite(sc->sc_iot, sc->sc_ioh, SIO_REG_LDN, SIO_LDN_GPIO);
554	nswrite(sc->sc_iot, sc->sc_ioh, SIO_GPIO_PINSEL, v);
555}
556
557static void
558nsclpcsio_gpio_init(struct nsclpcsio_softc *sc)
559{
560	int i;
561
562	for (i = 0; i < SIO_GPIO_NPINS; i++) {
563		sc->sc_gpio_pins[i].pin_num = i;
564		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
565		    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
566		    GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
567		    GPIO_PIN_PULLUP;
568		/* safe defaults */
569		sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_TRISTATE;
570		sc->sc_gpio_pins[i].pin_state = GPIO_PIN_LOW;
571		nsclpcsio_gpio_pin_ctl(sc, i, sc->sc_gpio_pins[i].pin_flags);
572		nsclpcsio_gpio_pin_write(sc, i, sc->sc_gpio_pins[i].pin_state);
573	}
574
575	/* create controller tag */
576	sc->sc_gpio_gc.gp_cookie = sc;
577	sc->sc_gpio_gc.gp_pin_read = nsclpcsio_gpio_pin_read;
578	sc->sc_gpio_gc.gp_pin_write = nsclpcsio_gpio_pin_write;
579	sc->sc_gpio_gc.gp_pin_ctl = nsclpcsio_gpio_pin_ctl;
580}
581
582static int
583nsclpcsio_gpio_pin_read(void *aux, int pin)
584{
585	struct nsclpcsio_softc *sc = (struct nsclpcsio_softc *)aux;
586	int port, shift, reg;
587	uint8_t v;
588
589	port = pin / 8;
590	shift = pin % 8;
591
592	switch (port) {
593	case 0:
594		reg = SIO_GPDI0;
595		break;
596	case 1:
597		reg = SIO_GPDI1;
598		break;
599	case 2:
600		reg = SIO_GPDI2;
601		break;
602	case 3:
603		reg = SIO_GPDI3;
604		break;
605	default:
606		reg = SIO_GPDI0;
607		break;
608	}
609
610	v = GPIO_READ(sc, reg);
611
612	return ((v >> shift) & 0x1);
613}
614
615static void
616nsclpcsio_gpio_pin_write(void *aux, int pin, int v)
617{
618	struct nsclpcsio_softc *sc = (struct nsclpcsio_softc *)aux;
619	int port, shift, reg;
620	uint8_t d;
621
622	port = pin / 8;
623	shift = pin % 8;
624
625	switch (port) {
626	case 0:
627		reg = SIO_GPDO0;
628		break;
629	case 1:
630		reg = SIO_GPDO1;
631		break;
632	case 2:
633		reg = SIO_GPDO2;
634		break;
635	case 3:
636		reg = SIO_GPDO3;
637		break;
638	default:
639		reg = SIO_GPDO0;
640		break; /* shouldn't happen */
641	}
642
643	d = GPIO_READ(sc, reg);
644	if (v == 0)
645		d &= ~(1 << shift);
646	else if (v == 1)
647		d |= (1 << shift);
648	GPIO_WRITE(sc, reg, d);
649}
650
651void
652nsclpcsio_gpio_pin_ctl(void *aux, int pin, int flags)
653{
654	struct nsclpcsio_softc *sc = (struct nsclpcsio_softc *)aux;
655	uint8_t conf;
656
657	mutex_enter(&sc->sc_lock);
658
659	nswrite(sc->sc_iot, sc->sc_ioh, SIO_REG_LDN, SIO_LDN_GPIO);
660	nsclpcsio_gpio_pin_select(sc, pin);
661	conf = nsread(sc->sc_iot, sc->sc_ioh, SIO_GPIO_PINCFG);
662
663	conf &= ~(SIO_GPIO_CONF_OUTPUTEN | SIO_GPIO_CONF_PUSHPULL |
664	    SIO_GPIO_CONF_PULLUP);
665	if ((flags & GPIO_PIN_TRISTATE) == 0)
666		conf |= SIO_GPIO_CONF_OUTPUTEN;
667	if (flags & GPIO_PIN_PUSHPULL)
668		conf |= SIO_GPIO_CONF_PUSHPULL;
669	if (flags & GPIO_PIN_PULLUP)
670		conf |= SIO_GPIO_CONF_PULLUP;
671
672	nswrite(sc->sc_iot, sc->sc_ioh, SIO_GPIO_PINCFG, conf);
673
674	mutex_exit(&sc->sc_lock);
675}
676#endif /* NGPIO */
677
678MODULE(MODULE_CLASS_DRIVER, nsclpcsio, "sysmon_envsys");
679
680#ifdef _MODULE
681#include "ioconf.c"
682#endif
683
684static int
685nsclpcsio_modcmd(modcmd_t cmd, void *opaque)
686{
687	switch (cmd) {
688	case MODULE_CMD_INIT:
689#ifdef _MODULE
690		return config_init_component(cfdriver_ioconf_nsclpcsio,
691		    cfattach_ioconf_nsclpcsio, cfdata_ioconf_nsclpcsio);
692#else
693		return 0;
694#endif
695	case MODULE_CMD_FINI:
696#ifdef _MODULE
697		return config_fini_component(cfdriver_ioconf_nsclpcsio,
698		    cfattach_ioconf_nsclpcsio, cfdata_ioconf_nsclpcsio);
699#else
700		return 0;
701#endif
702	default:
703		return ENOTTY;
704	}
705}
706