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