1*c7fb772bSthorpej /* $NetBSD: tcu.c,v 1.6 2021/08/07 16:19:16 thorpej Exp $ */
2cf039764Schristos
3cf039764Schristos /*-
4cf039764Schristos * Copyright (c) 2016, Felix Deichmann
5cf039764Schristos * All rights reserved.
6cf039764Schristos *
7cf039764Schristos * Redistribution and use in source and binary forms, with or without
8cf039764Schristos * modification, are permitted provided that the following conditions
9cf039764Schristos * are met:
10cf039764Schristos * 1. Redistributions of source code must retain the above copyright
11cf039764Schristos * notice, this list of conditions and the following disclaimer.
12cf039764Schristos * 2. Redistributions in binary form must reproduce the above copyright
13cf039764Schristos * notice, this list of conditions and the following disclaimer in the
14cf039764Schristos * documentation and/or other materials provided with the distribution.
15cf039764Schristos *
16cf039764Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17cf039764Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18cf039764Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19cf039764Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20cf039764Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21cf039764Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22cf039764Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23cf039764Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24cf039764Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25cf039764Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26cf039764Schristos * POSSIBILITY OF SUCH DAMAGE.
27cf039764Schristos */
28cf039764Schristos
29cf039764Schristos /*
30cf039764Schristos * flxd TC-USB - TURBOchannel USB host option
31cf039764Schristos */
32cf039764Schristos
33cf039764Schristos #include <sys/cdefs.h>
34*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: tcu.c,v 1.6 2021/08/07 16:19:16 thorpej Exp $");
35cf039764Schristos
36cf039764Schristos #include <sys/param.h>
37cf039764Schristos #include <sys/systm.h>
38cf039764Schristos #include <sys/device.h>
39cf039764Schristos #include <sys/gpio.h>
40cf039764Schristos
41cf039764Schristos #include <sys/bus.h>
42cf039764Schristos
43cf039764Schristos #include <dev/tc/tcvar.h>
44cf039764Schristos
45cf039764Schristos #include <dev/gpio/gpiovar.h>
46cf039764Schristos
47cf039764Schristos #include "gpio.h"
48cf039764Schristos #include "slhci_tcu.h"
49cf039764Schristos
50cf039764Schristos #define TCU_GPIO_NPINS 8
519d57a94cSchristos
529d57a94cSchristos #define TCU_CPLD_OFFS 0x80
539d57a94cSchristos #define TCU_CPLD_SIZE (4 * 4)
549d57a94cSchristos
559d57a94cSchristos #define TCU_CFG 0x0
569d57a94cSchristos #define TCU_CFG_RUN __BIT(7) /* write-only */
57cb798baaSflxd #define TCU_CFG_S1_1 __BIT(3) /* read-only */
58cb798baaSflxd #define TCU_CFG_S1_2 __BIT(2) /* read-only */
59cb798baaSflxd #define TCU_CFG_S1_3 __BIT(1) /* read-only */
60cb798baaSflxd #define TCU_CFG_S1_4 __BIT(0) /* read-only */
619d57a94cSchristos #define TCU_GPIO_DIR 0x4
629d57a94cSchristos #define TCU_GPIO_IN 0x8
639d57a94cSchristos #define TCU_GPIO_OUT 0xc
64cf039764Schristos
65cf039764Schristos struct tcu_softc {
66cf039764Schristos #if NGPIO > 0
67cf039764Schristos kmutex_t sc_gpio_mtx;
68cf039764Schristos struct gpio_chipset_tag sc_gpio_gc;
69cf039764Schristos gpio_pin_t sc_gpio_pins[TCU_GPIO_NPINS];
70cf039764Schristos bus_space_tag_t sc_gpio_iot;
71cf039764Schristos bus_space_handle_t sc_gpio_ioh;
72cf039764Schristos #endif /* NGPIO > 0 */
73cf039764Schristos };
74cf039764Schristos
75cf039764Schristos static int tcu_match(device_t, cfdata_t, void *);
76cf039764Schristos static void tcu_attach(device_t, device_t, void *);
77cf039764Schristos
78cf039764Schristos #if NSLHCI_TCU > 0
79cf039764Schristos static int tcu_print(void *, const char *);
80cf039764Schristos #endif /* NSLHCI_TCU > 0 */
81cf039764Schristos #if NGPIO > 0
82cf039764Schristos static void tcu_gpio_attach(device_t, device_t, void *);
83cf039764Schristos static int tcu_gpio_read(void *, int);
84cf039764Schristos static void tcu_gpio_write(void *, int, int);
85cf039764Schristos static void tcu_gpio_ctl(void *, int, int);
86cf039764Schristos #endif /* NGPIO > 0 */
87cf039764Schristos
88cf039764Schristos CFATTACH_DECL_NEW(tcu, sizeof(struct tcu_softc),
89cf039764Schristos tcu_match, tcu_attach, NULL, NULL);
90cf039764Schristos
91cf039764Schristos static int
tcu_match(device_t parent,cfdata_t cf,void * aux)92cf039764Schristos tcu_match(device_t parent, cfdata_t cf, void *aux)
93cf039764Schristos {
94cf039764Schristos struct tc_attach_args *ta = aux;
95cf039764Schristos
96cf039764Schristos if (strncmp("TC-USB ", ta->ta_modname, TC_ROM_LLEN))
97cf039764Schristos return 0;
98cf039764Schristos
99cf039764Schristos return 1;
100cf039764Schristos }
101cf039764Schristos
102cf039764Schristos static void
tcu_attach(device_t parent,device_t self,void * aux)103cf039764Schristos tcu_attach(device_t parent, device_t self, void *aux)
104cf039764Schristos {
1059d57a94cSchristos struct tc_attach_args *ta = aux;
1069d57a94cSchristos bus_space_tag_t iot = ta->ta_memt;
1079d57a94cSchristos bus_space_handle_t ioh;
1089d57a94cSchristos int error;
1099d57a94cSchristos uint8_t cfg;
1109d57a94cSchristos char buf[30];
111cf039764Schristos
112cf039764Schristos printf(": TC-USB\n");
1139d57a94cSchristos
1149d57a94cSchristos error = bus_space_map(iot, ta->ta_addr + TCU_CPLD_OFFS, TCU_CPLD_SIZE,
1159d57a94cSchristos 0, &ioh);
1169d57a94cSchristos if (error) {
1179d57a94cSchristos aprint_error_dev(self, "bus_space_map() failed (%d)\n", error);
1189d57a94cSchristos return;
1199d57a94cSchristos }
1209d57a94cSchristos
1219d57a94cSchristos /*
1229d57a94cSchristos * Force reset in case system didn't. SL811 reset pulse and hold time
1239d57a94cSchristos * must be min. 16 clocks long (at 48 MHz clock) each.
1249d57a94cSchristos */
1259d57a94cSchristos bus_space_write_1(iot, ioh, TCU_CFG, 0);
1269d57a94cSchristos DELAY(1000);
1279d57a94cSchristos bus_space_write_1(iot, ioh, TCU_CFG, TCU_CFG_RUN);
1289d57a94cSchristos DELAY(1000);
1299d57a94cSchristos
1309d57a94cSchristos cfg = bus_space_read_1(iot, ioh, TCU_CFG);
1319d57a94cSchristos
1329d57a94cSchristos bus_space_unmap(iot, ioh, TCU_CPLD_SIZE);
1339d57a94cSchristos
1349d57a94cSchristos /* Display DIP switch configuration. */
1359d57a94cSchristos (void)snprintb(buf, sizeof(buf),
1369d57a94cSchristos "\177\020"
1379d57a94cSchristos "b\3S1-1\0"
1389d57a94cSchristos "b\2S1-2\0"
1399d57a94cSchristos "b\1S1-3\0"
1409d57a94cSchristos "b\0S1-4\0"
1419d57a94cSchristos "\0", cfg);
1429d57a94cSchristos aprint_normal_dev(self, "config %s\n", buf);
1439d57a94cSchristos
144cb798baaSflxd if ((cfg & TCU_CFG_S1_1) != 0 && ta->ta_busspeed != TC_SPEED_12_5_MHZ)
145cb798baaSflxd aprint_error_dev(self, "warning: switch S1-1 asserted with "
14611a5313aSflxd "clock != 12.5 MHz\n");
147cb798baaSflxd
148cf039764Schristos #if NSLHCI_TCU > 0
149cf039764Schristos /* Attach slhci. */
1502685996bSthorpej (void)config_found(self, aux, tcu_print,
151*c7fb772bSthorpej CFARGS(.iattr = "tcu"));
152cf039764Schristos #endif /* NSLHCI_TCU > 0 */
153cf039764Schristos #if NGPIO > 0
154cf039764Schristos /* Attach gpio(bus). */
155cf039764Schristos tcu_gpio_attach(parent, self, aux);
156cf039764Schristos #endif /* NGPIO > 0 */
157cf039764Schristos }
158cf039764Schristos
159cf039764Schristos #if NSLHCI_TCU > 0
160cf039764Schristos static int
tcu_print(void * aux,const char * pnp)161cf039764Schristos tcu_print(void *aux, const char *pnp)
162cf039764Schristos {
163cf039764Schristos
164cf039764Schristos /* This function is only used for "slhci at tcu". */
165cf039764Schristos if (pnp)
166cf039764Schristos aprint_normal("slhci at %s", pnp);
167cf039764Schristos
168cf039764Schristos return UNCONF;
169cf039764Schristos }
170cf039764Schristos #endif /* NSLHCI_TCU > 0 */
171cf039764Schristos
172cf039764Schristos #if NGPIO > 0
173cf039764Schristos static void
tcu_gpio_attach(device_t parent,device_t self,void * aux)174cf039764Schristos tcu_gpio_attach(device_t parent, device_t self, void *aux)
175cf039764Schristos {
176cf039764Schristos struct tcu_softc *sc = device_private(self);
177cf039764Schristos struct tc_attach_args *ta = aux;
178cf039764Schristos struct gpiobus_attach_args gba;
179cf039764Schristos bus_space_tag_t iot = ta->ta_memt;
180cf039764Schristos uint32_t v;
181cf039764Schristos int error;
182cf039764Schristos
183cf039764Schristos sc->sc_gpio_iot = iot;
184cf039764Schristos
1859d57a94cSchristos error = bus_space_map(iot, ta->ta_addr + TCU_CPLD_OFFS, TCU_CPLD_SIZE,
186cf039764Schristos 0, &sc->sc_gpio_ioh);
187cf039764Schristos if (error) {
188cf039764Schristos aprint_error_dev(self, "bus_space_map() failed (%d)\n", error);
189cf039764Schristos return;
190cf039764Schristos }
191cf039764Schristos
192cf039764Schristos mutex_init(&sc->sc_gpio_mtx, MUTEX_DEFAULT, IPL_NONE);
193cf039764Schristos
194cf039764Schristos v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR);
195cf039764Schristos
196cf039764Schristos for (int pin = 0; pin < TCU_GPIO_NPINS; pin++) {
197cf039764Schristos sc->sc_gpio_pins[pin].pin_num = pin;
198cf039764Schristos sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
199cf039764Schristos GPIO_PIN_OUTPUT;
200cf039764Schristos sc->sc_gpio_pins[pin].pin_flags = (v & (UINT32_C(1) << pin)) ?
201cf039764Schristos GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
202cf039764Schristos sc->sc_gpio_pins[pin].pin_state = tcu_gpio_read(sc, pin);
203cf039764Schristos }
204cf039764Schristos
205cf039764Schristos sc->sc_gpio_gc.gp_cookie = sc;
206cf039764Schristos sc->sc_gpio_gc.gp_pin_read = tcu_gpio_read;
207cf039764Schristos sc->sc_gpio_gc.gp_pin_write = tcu_gpio_write;
208cf039764Schristos sc->sc_gpio_gc.gp_pin_ctl = tcu_gpio_ctl;
209cf039764Schristos
210cf039764Schristos memset(&gba, 0, sizeof(gba));
211cf039764Schristos
212cf039764Schristos gba.gba_gc = &sc->sc_gpio_gc;
213cf039764Schristos gba.gba_pins = sc->sc_gpio_pins;
214cf039764Schristos gba.gba_npins = TCU_GPIO_NPINS;
215cf039764Schristos
2162685996bSthorpej config_found(self, &gba, gpiobus_print,
217*c7fb772bSthorpej CFARGS(.iattr = "gpiobus"));
218cf039764Schristos }
219cf039764Schristos
220cf039764Schristos static int
tcu_gpio_read(void * arg,int pin)221cf039764Schristos tcu_gpio_read(void *arg, int pin)
222cf039764Schristos {
223cf039764Schristos struct tcu_softc *sc = arg;
224cf039764Schristos uint32_t v;
225cf039764Schristos
226cf039764Schristos mutex_enter(&sc->sc_gpio_mtx);
227cf039764Schristos v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_IN);
228cf039764Schristos mutex_exit(&sc->sc_gpio_mtx);
229cf039764Schristos
230cf039764Schristos return (v & (UINT32_C(1) << pin)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
231cf039764Schristos }
232cf039764Schristos
233cf039764Schristos static void
tcu_gpio_write(void * arg,int pin,int val)234cf039764Schristos tcu_gpio_write(void *arg, int pin, int val)
235cf039764Schristos {
236cf039764Schristos struct tcu_softc *sc = arg;
237cf039764Schristos uint32_t v;
238cf039764Schristos
239cf039764Schristos mutex_enter(&sc->sc_gpio_mtx);
240cf039764Schristos
241cf039764Schristos v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_OUT);
242cf039764Schristos
243cf039764Schristos if (val == GPIO_PIN_LOW)
244cf039764Schristos v &= ~(UINT32_C(1) << pin);
245cf039764Schristos else if (val == GPIO_PIN_HIGH)
246cf039764Schristos v |= (UINT32_C(1) << pin);
247cf039764Schristos
248cf039764Schristos bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_OUT, v);
249cf039764Schristos
250cf039764Schristos mutex_exit(&sc->sc_gpio_mtx);
251cf039764Schristos }
252cf039764Schristos
253cf039764Schristos static void
tcu_gpio_ctl(void * arg,int pin,int flags)254cf039764Schristos tcu_gpio_ctl(void *arg, int pin, int flags)
255cf039764Schristos {
256cf039764Schristos struct tcu_softc *sc = arg;
257cf039764Schristos uint32_t v;
258cf039764Schristos
259cf039764Schristos mutex_enter(&sc->sc_gpio_mtx);
260cf039764Schristos
261cf039764Schristos v = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR);
262cf039764Schristos
263cf039764Schristos if (flags & GPIO_PIN_INPUT)
264cf039764Schristos v &= ~(UINT32_C(1) << pin);
265cf039764Schristos if (flags & GPIO_PIN_OUTPUT)
266cf039764Schristos v |= (UINT32_C(1) << pin);
267cf039764Schristos
268cf039764Schristos bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, TCU_GPIO_DIR, v);
269cf039764Schristos
270cf039764Schristos mutex_exit(&sc->sc_gpio_mtx);
271cf039764Schristos }
272cf039764Schristos #endif /* NGPIO > 0 */
273