1/*	$NetBSD: gpio_opb.c,v 1.10 2021/08/07 16:19:03 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 2004 Shigeyuki Fukushima.
5 * 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
13 *    copyright notice, this list of conditions and the following
14 *    disclaimer in the documentation and/or other materials provided
15 *    with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 *    products derived from this software without specific prior
18 *    written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "locators.h"
34
35#include <sys/param.h>
36#include <sys/device.h>
37#include <sys/systm.h>
38
39#include <machine/pio.h>
40
41#include <sys/gpio.h>
42#include <dev/gpio/gpiovar.h>
43
44#include <powerpc/ibm4xx/dev/opbvar.h>
45#include <powerpc/ibm4xx/dev/gpioreg.h>
46
47struct gpio_opb_softc {
48	device_t		sc_dev;		/* device generic */
49	/* GPIO interface */
50	bus_space_tag_t		sc_gpio_iot;
51	bus_space_handle_t	sc_gpio_ioh;
52	struct gpio_chipset_tag	sc_gpio_gc;
53	gpio_pin_t		sc_gpio_pins[GPIO_NPINS];
54};
55
56static int	gpio_opb_match(device_t, cfdata_t, void *);
57static void	gpio_opb_attach(device_t, device_t, void *);
58
59CFATTACH_DECL_NEW(opbgpio, sizeof(struct gpio_opb_softc),
60	gpio_opb_match, gpio_opb_attach, NULL, NULL);
61
62static int	gpio_opb_pin_read(void *, int);
63static void	gpio_opb_pin_write(void *, int, int);
64static void	gpio_opb_pin_ctl(void *, int, int);
65
66static inline uint32_t
67gpio_read(struct gpio_opb_softc *sc, bus_size_t o)
68{
69	return bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, o);
70}
71
72static inline void
73gpio_write(struct gpio_opb_softc *sc, bus_size_t o, uint32_t v)
74{
75	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, o, v);
76}
77
78static inline void
79gpio_set(struct gpio_opb_softc *sc, bus_size_t o, uint32_t v)
80{
81	gpio_write(sc, o, gpio_read(sc, o) | v);
82}
83
84static inline void
85gpio_clear(struct gpio_opb_softc *sc, bus_size_t o, uint32_t v)
86{
87	gpio_write(sc, o, gpio_read(sc, o) & ~v);
88}
89
90static int
91gpio_opb_match(device_t parent, cfdata_t cf, void *aux)
92{
93	struct opb_attach_args * const oaa = aux;
94
95	if (strcmp(oaa->opb_name, cf->cf_name) != 0)
96		return 0;
97
98	return 1;
99}
100
101static void
102gpio_opb_attach(device_t parent, device_t self, void *aux)
103{
104	struct gpio_opb_softc * const sc = device_private(self);
105	struct opb_attach_args * const oaa = aux;
106	struct gpiobus_attach_args gba;
107	uint32_t reg_ir, reg_tcr, reg_odr;
108
109	aprint_naive(": GPIO controller\n");
110	aprint_normal(": On-Chip GPIO controller\n");
111
112	sc->sc_dev = self;
113
114	/* Map GPIO I/O space */
115	sc->sc_gpio_iot = oaa->opb_bt;
116	bus_space_map(sc->sc_gpio_iot, oaa->opb_addr,
117		GPIO_NREG, 0, &sc->sc_gpio_ioh);
118
119	/* Read current register status */
120	reg_ir  = gpio_read(sc, GPIO_IR);
121	reg_tcr = gpio_read(sc, GPIO_TCR);
122	reg_odr = gpio_read(sc, GPIO_ODR);
123
124	/* Initialize pins array */
125	gpio_pin_t *pin = sc->sc_gpio_pins;
126	for (u_int i = 0 ; i < GPIO_NPINS ; i++, pin++) {
127		const uint32_t pin_mask = 1 << GPIO_PIN_SHIFT(i + 1);
128		pin->pin_num = i;
129		pin->pin_caps = GPIO_PIN_INOUT
130				 | GPIO_PIN_OPENDRAIN
131				 | GPIO_PIN_TRISTATE;
132
133		/* current defaults */
134		pin->pin_flags =
135		    (reg_odr & pin_mask)
136			? GPIO_PIN_OPENDRAIN
137			: ((reg_tcr & pin_mask)
138			    ? GPIO_PIN_INOUT
139			    : GPIO_PIN_TRISTATE);
140		pin->pin_state = (reg_ir & pin_mask) != 0;
141		pin->pin_mapped = 0;
142	}
143
144	/* Create controller tag */
145	sc->sc_gpio_gc.gp_cookie = sc;
146	sc->sc_gpio_gc.gp_pin_read = gpio_opb_pin_read;
147	sc->sc_gpio_gc.gp_pin_write = gpio_opb_pin_write;
148	sc->sc_gpio_gc.gp_pin_ctl = gpio_opb_pin_ctl;
149
150	gba.gba_gc = &sc->sc_gpio_gc;
151	gba.gba_pins = sc->sc_gpio_pins;
152	gba.gba_npins = GPIO_NPINS;
153
154	/* Attach GPIO framework */
155	(void) config_found(self, &gba, gpiobus_print, CFARGS_NONE);
156}
157
158static int
159gpio_opb_pin_read(void *arg, int pin)
160{
161	struct gpio_opb_softc * const sc = arg;
162	const u_int p = (pin % GPIO_NPINS) + 1;
163	uint32_t reg_ir = gpio_read(sc, GPIO_IR);
164
165	return (reg_ir >> GPIO_PIN_SHIFT(p)) & 0x01;
166}
167
168static void
169gpio_opb_pin_write(void *arg, int pin, int value)
170{
171	struct gpio_opb_softc * const sc = arg;
172	const u_int p = (pin % GPIO_NPINS) + 1;
173	const uint32_t pin_mask = 1 << GPIO_PIN_SHIFT(p);
174
175	if (value == 0) {
176		gpio_clear(sc, GPIO_OR, pin_mask);
177	} else if (value == 1) {
178		gpio_set(sc, GPIO_OR, pin_mask);
179	}
180}
181
182static void
183gpio_opb_pin_ctl(void *arg, int pin, int flags)
184{
185	struct gpio_opb_softc * const sc = arg;
186	const u_int p = (pin % GPIO_NPINS) + 1;
187	const uint32_t pin_mask = 1 << GPIO_PIN_SHIFT(p);
188
189	if (flags & GPIO_PIN_INOUT) {
190		/* GPIOn_ODR register bit is 0 */
191		gpio_clear(sc, GPIO_ODR, pin_mask);
192
193		/* GPIOn_TCR register bit is 1 */
194		gpio_set(sc, GPIO_TCR, pin_mask);
195	}
196
197	if (flags & GPIO_PIN_TRISTATE) {
198		/* GPIOn_ODR register bit is 0 */
199		gpio_clear(sc, GPIO_ODR, pin_mask);
200
201		/* GPIOn_TCR register bit is 0 */
202		gpio_clear(sc, GPIO_TCR, pin_mask);
203	}
204
205	if (flags & GPIO_PIN_OPENDRAIN) {
206		/* GPIOn_ODR register bit is 1 */
207		gpio_set(sc, GPIO_ODR, pin_mask);
208	}
209}
210