tcu.c revision 1.5
1/* $NetBSD: tcu.c,v 1.5 2021/04/24 23:36:59 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2016, Felix Deichmann
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 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * flxd TC-USB - TURBOchannel USB host option
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: tcu.c,v 1.5 2021/04/24 23:36:59 thorpej Exp $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/device.h>
39#include <sys/gpio.h>
40
41#include <sys/bus.h>
42
43#include <dev/tc/tcvar.h>
44
45#include <dev/gpio/gpiovar.h>
46
47#include "gpio.h"
48#include "slhci_tcu.h"
49
50#define TCU_GPIO_NPINS	8
51
52#define TCU_CPLD_OFFS	0x80
53#define TCU_CPLD_SIZE	(4 * 4)
54
55#define TCU_CFG		0x0
56#define   TCU_CFG_RUN	__BIT(7)	/* write-only */
57#define   TCU_CFG_S1_1	__BIT(3)	/* read-only */
58#define   TCU_CFG_S1_2	__BIT(2)	/* read-only */
59#define   TCU_CFG_S1_3	__BIT(1)	/* read-only */
60#define   TCU_CFG_S1_4	__BIT(0)	/* read-only */
61#define TCU_GPIO_DIR	0x4
62#define TCU_GPIO_IN	0x8
63#define TCU_GPIO_OUT	0xc
64
65struct tcu_softc {
66#if NGPIO > 0
67	kmutex_t		sc_gpio_mtx;
68	struct gpio_chipset_tag	sc_gpio_gc;
69	gpio_pin_t		sc_gpio_pins[TCU_GPIO_NPINS];
70	bus_space_tag_t		sc_gpio_iot;
71	bus_space_handle_t	sc_gpio_ioh;
72#endif /* NGPIO > 0 */
73};
74
75static int  tcu_match(device_t, cfdata_t, void *);
76static void tcu_attach(device_t, device_t, void *);
77
78#if NSLHCI_TCU > 0
79static int  tcu_print(void *, const char *);
80#endif /* NSLHCI_TCU > 0 */
81#if NGPIO > 0
82static void tcu_gpio_attach(device_t, device_t, void *);
83static int  tcu_gpio_read(void *, int);
84static void tcu_gpio_write(void *, int, int);
85static void tcu_gpio_ctl(void *, int, int);
86#endif /* NGPIO > 0 */
87
88CFATTACH_DECL_NEW(tcu, sizeof(struct tcu_softc),
89    tcu_match, tcu_attach, NULL, NULL);
90
91static int
92tcu_match(device_t parent, cfdata_t cf, void *aux)
93{
94	struct tc_attach_args *ta = aux;
95
96	if (strncmp("TC-USB  ", ta->ta_modname, TC_ROM_LLEN))
97		return 0;
98
99	return 1;
100}
101
102static void
103tcu_attach(device_t parent, device_t self, void *aux)
104{
105	struct tc_attach_args *ta = aux;
106	bus_space_tag_t iot = ta->ta_memt;
107	bus_space_handle_t ioh;
108	int error;
109	uint8_t cfg;
110	char buf[30];
111
112	printf(": TC-USB\n");
113
114	error = bus_space_map(iot, ta->ta_addr + TCU_CPLD_OFFS, TCU_CPLD_SIZE,
115	    0, &ioh);
116	if (error) {
117		aprint_error_dev(self, "bus_space_map() failed (%d)\n", error);
118		return;
119	}
120
121	/*
122	 * Force reset in case system didn't. SL811 reset pulse and hold time
123	 * must be min. 16 clocks long (at 48 MHz clock) each.
124	 */
125	bus_space_write_1(iot, ioh, TCU_CFG, 0);
126	DELAY(1000);
127	bus_space_write_1(iot, ioh, TCU_CFG, TCU_CFG_RUN);
128	DELAY(1000);
129
130	cfg = bus_space_read_1(iot, ioh, TCU_CFG);
131
132	bus_space_unmap(iot, ioh, TCU_CPLD_SIZE);
133
134	/* Display DIP switch configuration. */
135	(void)snprintb(buf, sizeof(buf),
136	    "\177\020"
137	    "b\3S1-1\0"
138	    "b\2S1-2\0"
139	    "b\1S1-3\0"
140	    "b\0S1-4\0"
141	    "\0", cfg);
142	aprint_normal_dev(self, "config %s\n", buf);
143
144	if ((cfg & TCU_CFG_S1_1) != 0 && ta->ta_busspeed != TC_SPEED_12_5_MHZ)
145		aprint_error_dev(self, "warning: switch S1-1 asserted with "
146		    "clock != 12.5 MHz\n");
147
148#if NSLHCI_TCU > 0
149	/* Attach slhci. */
150	(void)config_found(self, aux, tcu_print,
151	    CFARG_IATTR, "tcu",
152	    CFARG_EOL);
153#endif /* NSLHCI_TCU > 0 */
154#if NGPIO > 0
155	/* Attach gpio(bus). */
156	tcu_gpio_attach(parent, self, aux);
157#endif /* NGPIO > 0 */
158}
159
160#if NSLHCI_TCU > 0
161static int
162tcu_print(void *aux, const char *pnp)
163{
164
165	/* This function is only used for "slhci at tcu". */
166	if (pnp)
167		aprint_normal("slhci at %s", pnp);
168
169	return UNCONF;
170}
171#endif /* NSLHCI_TCU > 0 */
172
173#if NGPIO > 0
174static void
175tcu_gpio_attach(device_t parent, device_t self, void *aux)
176{
177	struct tcu_softc *sc = device_private(self);
178	struct tc_attach_args *ta = aux;
179	struct gpiobus_attach_args gba;
180	bus_space_tag_t iot = ta->ta_memt;
181	uint32_t v;
182	int error;
183
184	sc->sc_gpio_iot = iot;
185
186	error = bus_space_map(iot, ta->ta_addr + TCU_CPLD_OFFS, TCU_CPLD_SIZE,
187	    0, &sc->sc_gpio_ioh);
188	if (error) {
189		aprint_error_dev(self, "bus_space_map() failed (%d)\n", error);
190		return;
191	}
192
193	mutex_init(&sc->sc_gpio_mtx, MUTEX_DEFAULT, IPL_NONE);
194
195	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR);
196
197	for (int pin = 0; pin < TCU_GPIO_NPINS; pin++) {
198		sc->sc_gpio_pins[pin].pin_num = pin;
199		sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
200		    GPIO_PIN_OUTPUT;
201		sc->sc_gpio_pins[pin].pin_flags = (v & (UINT32_C(1) << pin)) ?
202		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
203		sc->sc_gpio_pins[pin].pin_state = tcu_gpio_read(sc, pin);
204	}
205
206	sc->sc_gpio_gc.gp_cookie = sc;
207	sc->sc_gpio_gc.gp_pin_read = tcu_gpio_read;
208	sc->sc_gpio_gc.gp_pin_write = tcu_gpio_write;
209	sc->sc_gpio_gc.gp_pin_ctl = tcu_gpio_ctl;
210
211	memset(&gba, 0, sizeof(gba));
212
213	gba.gba_gc = &sc->sc_gpio_gc;
214	gba.gba_pins = sc->sc_gpio_pins;
215	gba.gba_npins = TCU_GPIO_NPINS;
216
217	config_found(self, &gba, gpiobus_print,
218	    CFARG_IATTR, "gpiobus",
219	    CFARG_EOL);
220}
221
222static int
223tcu_gpio_read(void *arg, int pin)
224{
225	struct tcu_softc *sc = arg;
226	uint32_t v;
227
228	mutex_enter(&sc->sc_gpio_mtx);
229	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_IN);
230	mutex_exit(&sc->sc_gpio_mtx);
231
232	return (v & (UINT32_C(1) << pin)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
233}
234
235static void
236tcu_gpio_write(void *arg, int pin, int val)
237{
238	struct tcu_softc *sc = arg;
239	uint32_t v;
240
241	mutex_enter(&sc->sc_gpio_mtx);
242
243	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_OUT);
244
245	if (val == GPIO_PIN_LOW)
246		v &= ~(UINT32_C(1) << pin);
247	else if (val == GPIO_PIN_HIGH)
248		v |= (UINT32_C(1) << pin);
249
250	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_OUT, v);
251
252	mutex_exit(&sc->sc_gpio_mtx);
253}
254
255static void
256tcu_gpio_ctl(void *arg, int pin, int flags)
257{
258	struct tcu_softc *sc = arg;
259	uint32_t v;
260
261	mutex_enter(&sc->sc_gpio_mtx);
262
263	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR);
264
265	if (flags & GPIO_PIN_INPUT)
266		v &= ~(UINT32_C(1) << pin);
267	if (flags & GPIO_PIN_OUTPUT)
268		v |= (UINT32_C(1) << pin);
269
270	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR, v);
271
272	mutex_exit(&sc->sc_gpio_mtx);
273}
274#endif /* NGPIO > 0 */
275