xref: /netbsd-src/sys/dev/tc/tcu.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1 /* $NetBSD: tcu.c,v 1.6 2021/08/07 16:19:16 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.6 2021/08/07 16:19:16 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 
65 struct 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 
75 static int  tcu_match(device_t, cfdata_t, void *);
76 static void tcu_attach(device_t, device_t, void *);
77 
78 #if NSLHCI_TCU > 0
79 static int  tcu_print(void *, const char *);
80 #endif /* NSLHCI_TCU > 0 */
81 #if NGPIO > 0
82 static void tcu_gpio_attach(device_t, device_t, void *);
83 static int  tcu_gpio_read(void *, int);
84 static void tcu_gpio_write(void *, int, int);
85 static void tcu_gpio_ctl(void *, int, int);
86 #endif /* NGPIO > 0 */
87 
88 CFATTACH_DECL_NEW(tcu, sizeof(struct tcu_softc),
89     tcu_match, tcu_attach, NULL, NULL);
90 
91 static int
tcu_match(device_t parent,cfdata_t cf,void * aux)92 tcu_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 
102 static void
tcu_attach(device_t parent,device_t self,void * aux)103 tcu_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 	    CFARGS(.iattr = "tcu"));
152 #endif /* NSLHCI_TCU > 0 */
153 #if NGPIO > 0
154 	/* Attach gpio(bus). */
155 	tcu_gpio_attach(parent, self, aux);
156 #endif /* NGPIO > 0 */
157 }
158 
159 #if NSLHCI_TCU > 0
160 static int
tcu_print(void * aux,const char * pnp)161 tcu_print(void *aux, const char *pnp)
162 {
163 
164 	/* This function is only used for "slhci at tcu". */
165 	if (pnp)
166 		aprint_normal("slhci at %s", pnp);
167 
168 	return UNCONF;
169 }
170 #endif /* NSLHCI_TCU > 0 */
171 
172 #if NGPIO > 0
173 static void
tcu_gpio_attach(device_t parent,device_t self,void * aux)174 tcu_gpio_attach(device_t parent, device_t self, void *aux)
175 {
176 	struct tcu_softc *sc = device_private(self);
177 	struct tc_attach_args *ta = aux;
178 	struct gpiobus_attach_args gba;
179 	bus_space_tag_t iot = ta->ta_memt;
180 	uint32_t v;
181 	int error;
182 
183 	sc->sc_gpio_iot = iot;
184 
185 	error = bus_space_map(iot, ta->ta_addr + TCU_CPLD_OFFS, TCU_CPLD_SIZE,
186 	    0, &sc->sc_gpio_ioh);
187 	if (error) {
188 		aprint_error_dev(self, "bus_space_map() failed (%d)\n", error);
189 		return;
190 	}
191 
192 	mutex_init(&sc->sc_gpio_mtx, MUTEX_DEFAULT, IPL_NONE);
193 
194 	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR);
195 
196 	for (int pin = 0; pin < TCU_GPIO_NPINS; pin++) {
197 		sc->sc_gpio_pins[pin].pin_num = pin;
198 		sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
199 		    GPIO_PIN_OUTPUT;
200 		sc->sc_gpio_pins[pin].pin_flags = (v & (UINT32_C(1) << pin)) ?
201 		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
202 		sc->sc_gpio_pins[pin].pin_state = tcu_gpio_read(sc, pin);
203 	}
204 
205 	sc->sc_gpio_gc.gp_cookie = sc;
206 	sc->sc_gpio_gc.gp_pin_read = tcu_gpio_read;
207 	sc->sc_gpio_gc.gp_pin_write = tcu_gpio_write;
208 	sc->sc_gpio_gc.gp_pin_ctl = tcu_gpio_ctl;
209 
210 	memset(&gba, 0, sizeof(gba));
211 
212 	gba.gba_gc = &sc->sc_gpio_gc;
213 	gba.gba_pins = sc->sc_gpio_pins;
214 	gba.gba_npins = TCU_GPIO_NPINS;
215 
216 	config_found(self, &gba, gpiobus_print,
217 	    CFARGS(.iattr = "gpiobus"));
218 }
219 
220 static int
tcu_gpio_read(void * arg,int pin)221 tcu_gpio_read(void *arg, int pin)
222 {
223 	struct tcu_softc *sc = arg;
224 	uint32_t v;
225 
226 	mutex_enter(&sc->sc_gpio_mtx);
227 	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_IN);
228 	mutex_exit(&sc->sc_gpio_mtx);
229 
230 	return (v & (UINT32_C(1) << pin)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
231 }
232 
233 static void
tcu_gpio_write(void * arg,int pin,int val)234 tcu_gpio_write(void *arg, int pin, int val)
235 {
236 	struct tcu_softc *sc = arg;
237 	uint32_t v;
238 
239 	mutex_enter(&sc->sc_gpio_mtx);
240 
241 	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_OUT);
242 
243 	if (val == GPIO_PIN_LOW)
244 		v &= ~(UINT32_C(1) << pin);
245 	else if (val == GPIO_PIN_HIGH)
246 		v |= (UINT32_C(1) << pin);
247 
248 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_OUT, v);
249 
250 	mutex_exit(&sc->sc_gpio_mtx);
251 }
252 
253 static void
tcu_gpio_ctl(void * arg,int pin,int flags)254 tcu_gpio_ctl(void *arg, int pin, int flags)
255 {
256 	struct tcu_softc *sc = arg;
257 	uint32_t v;
258 
259 	mutex_enter(&sc->sc_gpio_mtx);
260 
261 	v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR);
262 
263 	if (flags & GPIO_PIN_INPUT)
264 		v &= ~(UINT32_C(1) << pin);
265 	if (flags & GPIO_PIN_OUTPUT)
266 		v |= (UINT32_C(1) << pin);
267 
268 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR, v);
269 
270 	mutex_exit(&sc->sc_gpio_mtx);
271 }
272 #endif /* NGPIO > 0 */
273