1 /* $NetBSD: argpio.c,v 1.8 2020/07/11 13:07:01 nia Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Garrett D'Amore 5 * All rights reserved. 6 * 7 * Written by Garrett D'Amore. 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 the author 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 THE AUTHOR ``AS IS'' AND ANY EXPRESS 22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: argpio.c,v 1.8 2020/07/11 13:07:01 nia Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/bus.h> 39 #include <sys/conf.h> 40 #include <sys/device.h> 41 #include <sys/gpio.h> 42 #include <sys/intr.h> 43 #include <sys/kernel.h> 44 #include <sys/types.h> 45 46 #include <mips/atheros/include/arbusvar.h> 47 48 #include <dev/gpio/gpiovar.h> 49 #include <dev/sysmon/sysmonvar.h> 50 #include <dev/sysmon/sysmon_taskq.h> 51 52 #include <mips/atheros/dev/argpioreg.h> 53 54 /* 55 * General Plan: 56 * 57 * Register GPIOs for all pins that are _not_ associated with the reset 58 * pin. (Possibly also not the system LED.) 59 */ 60 61 struct argpio_softc { 62 device_t sc_dev; 63 struct gpio_chipset_tag sc_gc; 64 gpio_pin_t sc_pins[ARGPIO_NPINS]; 65 int sc_npins; 66 bus_space_tag_t sc_st; 67 bus_space_handle_t sc_sh; 68 bus_size_t sc_size; 69 int sc_caps; 70 struct sysmon_pswitch sc_resetbtn; 71 void *sc_ih; 72 int sc_rstpin; 73 int sc_ledpin; 74 }; 75 76 static int argpio_match(device_t, cfdata_t, void *); 77 static void argpio_attach(device_t, device_t, void *); 78 static int argpio_intr(void *); 79 static void argpio_reset_pressed(void *); 80 static void argpio_ctl(void *, int, int); 81 static void argpio_write(void *, int, int); 82 static int argpio_read(void *, int); 83 84 CFATTACH_DECL_NEW(argpio, sizeof (struct argpio_softc), argpio_match, 85 argpio_attach, NULL, NULL); 86 87 #define INPUT(pin) (1 << (pin)) /* input bit */ 88 #define INTR(pin) (1 << ((pin) + 8)) /* interrupt bit */ 89 #define SERIAL(pin) (1 << ((pin) + 16)) /* serial mux bit */ 90 91 #define GETREG(sc, o) bus_space_read_4(sc->sc_st, sc->sc_sh, o) 92 #define PUTREG(sc, o, v) bus_space_write_4(sc->sc_st, sc->sc_sh, o, v) 93 #define FLUSH(sc) bus_space_barrier(sc->sc_st, sc->sc_sh, \ 94 0, 12, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) 95 96 int 97 argpio_match(device_t parent, cfdata_t match, void *aux) 98 { 99 struct arbus_attach_args *aa = aux; 100 101 return ((strcmp(aa->aa_name, "argpio") == 0) ? 1 : 0); 102 } 103 104 void 105 argpio_attach(device_t parent, device_t self, void *aux) 106 { 107 struct argpio_softc *sc = device_private(self); 108 struct arbus_attach_args *aa = aux; 109 struct gpiobus_attach_args gba; 110 prop_number_t pn; 111 int i; 112 uint32_t reg; 113 114 sc->sc_dev = self; 115 sc->sc_st = aa->aa_bst; 116 sc->sc_npins = ARGPIO_NPINS; 117 sc->sc_size = aa->aa_size; 118 sc->sc_ledpin = -1; 119 sc->sc_rstpin = -1; 120 121 if (bus_space_map(sc->sc_st, aa->aa_addr, sc->sc_size, 0, 122 &sc->sc_sh) != 0) { 123 printf(": unable to map registers!\n"); 124 return; 125 } 126 127 sc->sc_gc.gp_cookie = sc; 128 sc->sc_gc.gp_pin_read = argpio_read; 129 sc->sc_gc.gp_pin_write = argpio_write; 130 sc->sc_gc.gp_pin_ctl = argpio_ctl; 131 132 aprint_normal(": Atheros AR531X GPIO"); 133 pn = prop_dictionary_get(device_properties(sc->sc_dev), "reset-pin"); 134 if (pn != NULL) { 135 KASSERT(prop_object_type(pn) == PROP_TYPE_NUMBER); 136 sc->sc_rstpin = (int)prop_number_integer_value(pn); 137 aprint_normal(", reset button pin %d", sc->sc_rstpin); 138 } 139 pn = prop_dictionary_get(device_properties(sc->sc_dev), "sysled-pin"); 140 if (pn != NULL) { 141 KASSERT(prop_object_type(pn) == PROP_TYPE_NUMBER); 142 sc->sc_ledpin = (int)prop_number_integer_value(pn); 143 aprint_normal(", system led pin %d", sc->sc_ledpin); 144 } 145 146 aprint_normal("\n"); 147 148 if (sc->sc_ledpin) { 149 sc->sc_ih = arbus_intr_establish(aa->aa_cirq, aa->aa_mirq, 150 argpio_intr, sc); 151 if (sc->sc_ih == NULL) { 152 aprint_error_dev(sc->sc_dev, 153 "couldn't establish interrupt\n"); 154 } 155 } 156 157 if (sc->sc_ih) { 158 sysmon_task_queue_init(); 159 160 sc->sc_resetbtn.smpsw_name = device_xname(sc->sc_dev); 161 sc->sc_resetbtn.smpsw_type = PSWITCH_TYPE_RESET; 162 if (sysmon_pswitch_register(&sc->sc_resetbtn) != 0) { 163 aprint_normal_dev(sc->sc_dev, 164 "unable to register reset button\n"); 165 } 166 } 167 168 reg = GETREG(sc, GPIO_CR); 169 170 for (i = 0; i < sc->sc_npins; i++) { 171 gpio_pin_t *pp; 172 173 pp = &sc->sc_pins[i]; 174 175 if (i == sc->sc_rstpin) { 176 /* configure as interrupt for reset */ 177 pp->pin_caps = GPIO_PIN_INPUT; 178 reg &= ~SERIAL(i); 179 reg |= INPUT(i); 180 /* only if we were able to set up the handler, tho' */ 181 if (sc->sc_ih != NULL) 182 reg |= INTR(i); 183 184 } else if (i == sc->sc_ledpin) { 185 /* configure as output for LED */ 186 pp->pin_caps = GPIO_PIN_OUTPUT; 187 reg &= ~SERIAL(i); 188 reg &= ~INPUT(i); 189 reg &= ~INTR(i); 190 191 } else { 192 if (reg & SERIAL(i)) { 193 /* pin multiplexed with serial bit */ 194 pp->pin_caps = 0; 195 } else { 196 pp->pin_caps = GPIO_PIN_INPUT | 197 GPIO_PIN_OUTPUT; 198 } 199 } 200 } 201 202 PUTREG(sc, GPIO_CR, reg); 203 FLUSH(sc); 204 205 gba.gba_gc = &sc->sc_gc; 206 gba.gba_pins = sc->sc_pins; 207 gba.gba_npins = sc->sc_npins; 208 config_found_ia(sc->sc_dev, "gpiobus", &gba, gpiobus_print); 209 } 210 211 void 212 argpio_ctl(void *arg, int pin, int flags) 213 { 214 struct argpio_softc *sc = arg; 215 uint32_t reg; 216 217 reg = GETREG(sc, GPIO_CR); 218 if (reg & (SERIAL(pin) | INTR(pin))) { 219 printf("pin %d cannot be changed!\n", pin); 220 /* don't allow changes to these bits */ 221 return; 222 } 223 if (flags & GPIO_PIN_INPUT) { 224 reg |= INPUT(pin); 225 } else { 226 reg &= ~INPUT(pin); 227 } 228 229 PUTREG(sc, GPIO_CR, reg); 230 FLUSH(sc); 231 } 232 233 void 234 argpio_write(void *arg, int pin, int value) 235 { 236 struct argpio_softc *sc = arg; 237 uint32_t reg; 238 239 reg = GETREG(sc, GPIO_DO); 240 if (value) 241 reg &= ~(1 << pin); 242 else 243 reg |= (1 << pin); 244 PUTREG(sc, GPIO_DO, reg); 245 FLUSH(sc); 246 } 247 248 int 249 argpio_read(void *arg, int pin) 250 { 251 struct argpio_softc *sc = arg; 252 253 return ((GETREG(sc, GPIO_DI) & (1 << pin)) ? 254 GPIO_PIN_HIGH : GPIO_PIN_LOW); 255 } 256 257 void 258 argpio_reset_pressed(void *arg) 259 { 260 struct argpio_softc *sc = arg; 261 int x; 262 263 sysmon_pswitch_event(&sc->sc_resetbtn, PSWITCH_EVENT_PRESSED); 264 265 /* reenable the interrupt */ 266 x = splhigh(); 267 PUTREG(sc, GPIO_CR, 268 GETREG(sc, GPIO_CR) | INTR(sc->sc_rstpin)); 269 splx(x); 270 } 271 272 int 273 argpio_intr(void *arg) 274 { 275 struct argpio_softc *sc = arg; 276 277 if (sc->sc_rstpin < 0) 278 return 0; 279 280 /* this is an edge triggered interrupt, so disable it for now */ 281 PUTREG(sc, GPIO_CR, GETREG(sc, GPIO_CR) & ~INTR(sc->sc_rstpin)); 282 283 /* no other interrupt on this, so we have to claim it */ 284 sysmon_task_queue_sched(0, argpio_reset_pressed, sc); 285 286 return 1; 287 } 288