1/* $NetBSD: augpio.c,v 1.10 2021/08/07 16:18:58 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2006 Itronix Inc.
5 * All rights reserved.
6 *
7 * Written by Garrett D'Amore for Itronix Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of Itronix Inc. may not be used to endorse
18 *    or promote products derived from this software without specific
19 *    prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: augpio.c,v 1.10 2021/08/07 16:18:58 thorpej Exp $");
36
37#include <sys/types.h>
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/errno.h>
41#include <sys/device.h>
42#include <sys/gpio.h>
43#include <sys/kernel.h>
44#include <sys/bus.h>
45
46#include <dev/gpio/gpiovar.h>
47
48#include <mips/locore.h>
49
50#include <mips/alchemy/include/aubusvar.h>
51#include <mips/alchemy/include/aureg.h>
52#include <mips/alchemy/dev/augpioreg.h>
53#include <mips/alchemy/dev/augpiovar.h>
54
55struct augpio_softc {
56	device_t			sc_dev;
57	struct gpio_chipset_tag		sc_gc;
58	gpio_pin_t			sc_pins[AUGPIO_NPINS];
59	int				sc_npins;
60	bus_space_tag_t			sc_bst;
61	int				sc_caps;
62	const char 			*sc_name;
63	int				(*sc_getctl)(void *, int);
64};
65
66static int augpio_match(device_t, struct cfdata *, void *);
67static void augpio_attach(device_t, device_t, void *);
68
69CFATTACH_DECL_NEW(augpio, sizeof(struct augpio_softc),
70    augpio_match, augpio_attach, NULL, NULL);
71
72#define	GETREG(x)	\
73	(*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(x))
74#define	PUTREG(x, v)	\
75	((*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(x)) = (v))
76
77#define	GETGPIO(x)	GETREG(GPIO_BASE + (x))
78#define	PUTGPIO(x,v)	PUTREG(GPIO_BASE + (x), (v))
79#define	GETGPIO2(x)	GETREG(GPIO2_BASE + (x))
80#define	PUTGPIO2(x,v)	PUTREG(GPIO2_BASE + (x), (v))
81
82int
83augpio_match(device_t parent, struct cfdata *match, void *aux)
84{
85	struct aubus_attach_args *aa = (struct aubus_attach_args *)aux;
86
87	if (strcmp(aa->aa_name, "augpio") != 0)
88		return 0;
89
90	return 1;
91}
92
93void
94augpio_attach(device_t parent, device_t self, void *aux)
95{
96	int	pin;
97
98	struct augpio_softc *sc = device_private(self);
99	struct aubus_attach_args *aa = aux;
100	struct gpiobus_attach_args gba;
101
102	sc->sc_dev = self;
103	sc->sc_bst = aa->aa_st;
104	sc->sc_npins = aa->aa_addrs[1];
105	sc->sc_gc.gp_cookie = sc;
106
107	if (aa->aa_addrs[0] == GPIO_BASE) {
108
109		sc->sc_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
110		    GPIO_PIN_TRISTATE;
111		sc->sc_gc.gp_pin_read = augpio_read;
112		sc->sc_gc.gp_pin_write = augpio_write;
113		sc->sc_gc.gp_pin_ctl = augpio_ctl;
114		sc->sc_getctl = augpio_getctl;
115		sc->sc_name = "primary block";
116
117	} else if (aa->aa_addrs[0] == GPIO2_BASE) {
118		/*
119		 * We rely on firmware (or platform init code) to initialize
120		 * the GPIO2 block.  We can't do it ourselves, because
121		 * resetting the GPIO2 block can have nasty effects (e.g.
122		 * reset PCI bus...)
123		 */
124		sc->sc_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
125		sc->sc_gc.gp_pin_read = augpio2_read;
126		sc->sc_gc.gp_pin_write = augpio2_write;
127		sc->sc_gc.gp_pin_ctl = augpio2_ctl;
128		sc->sc_getctl = augpio2_getctl;
129		sc->sc_name = "secondary block";
130
131	} else {
132		aprint_error(": unidentified block\n");
133		return;
134	}
135
136	for (pin = 0; pin < sc->sc_npins; pin++) {
137		gpio_pin_t	*pp = &sc->sc_pins[pin];
138
139		pp->pin_num = pin;
140		pp->pin_caps = sc->sc_caps;
141		pp->pin_flags = sc->sc_getctl(sc, pin);
142		pp->pin_state = sc->sc_gc.gp_pin_read(sc, pin);
143	}
144
145	gba.gba_gc = &sc->sc_gc;
146	gba.gba_pins = sc->sc_pins;
147	gba.gba_npins = sc->sc_npins;
148
149	aprint_normal(": Alchemy GPIO, %s\n", sc->sc_name);
150	aprint_naive("\n");
151	config_found(self, &gba, gpiobus_print, CFARGS_NONE);
152}
153
154int
155augpio_read(void *arg, int pin)
156{
157
158	pin = 1 << pin;
159
160	if (GETGPIO(AUGPIO_PINSTATERD) & pin)
161		return GPIO_PIN_HIGH;
162	else
163		return GPIO_PIN_LOW;
164}
165
166void
167augpio_write(void *arg, int pin, int value)
168{
169
170	pin = 1 << pin;
171	PUTGPIO(value ? AUGPIO_OUTPUTSET : AUGPIO_OUTPUTCLR, pin);
172}
173
174void
175augpio_ctl(void *arg, int pin, int flags)
176{
177	bus_addr_t		reg;
178
179	pin = 1 << pin;
180
181	if (flags & (GPIO_PIN_TRISTATE|GPIO_PIN_INPUT)) {
182		reg = AUGPIO_TRIOUTCLR;
183	} else if (flags & GPIO_PIN_OUTPUT) {
184		uint32_t		out;
185		out = GETGPIO(AUGPIO_OUTPUTRD);
186		reg = pin & out ? AUGPIO_OUTPUTSET : AUGPIO_OUTPUTCLR;
187	} else {
188		return;
189	}
190
191	PUTGPIO(reg, pin);
192}
193
194int
195augpio_getctl(void *arg, int pin)
196{
197
198	if (GETGPIO(AUGPIO_TRIOUTRD) & pin)
199		return GPIO_PIN_OUTPUT;
200	else
201		return GPIO_PIN_INPUT;
202}
203
204int
205augpio2_read(void *arg, int pin)
206{
207
208	pin = 1 << pin;
209
210	if (GETGPIO2(AUGPIO2_PINSTATE) & pin)
211		return GPIO_PIN_HIGH;
212	else
213		return GPIO_PIN_LOW;
214}
215
216void
217augpio2_write(void *arg, int pin, int value)
218{
219
220	pin = 1 << pin;
221
222	if (value) {
223		pin = pin | (pin << 16);
224	} else {
225		pin = (pin << 16);
226	}
227
228	PUTGPIO2(AUGPIO2_OUTPUT, pin);
229}
230
231void
232augpio2_ctl(void *arg, int pin, int flags)
233{
234	uint32_t		dir;
235
236	pin = 1 << pin;
237
238	dir = GETGPIO2(AUGPIO2_DIR);
239
240	if (flags & GPIO_PIN_INPUT) {
241		dir |= pin;
242	} else if (flags & GPIO_PIN_OUTPUT) {
243		dir &= ~pin;
244	}
245	PUTGPIO2(AUGPIO2_DIR, dir);
246}
247
248int
249augpio2_getctl(void *arg, int pin)
250{
251	uint32_t		dir;
252
253	pin = 1 << pin;
254
255	dir = GETGPIO2(AUGPIO2_DIR);
256	if (dir & (uint32_t)pin) {
257		return GPIO_PIN_OUTPUT;
258	} else {
259		return GPIO_PIN_INPUT;
260	}
261}
262
263