1 /* $NetBSD: argpio.c,v 1.3 2006/09/04 05:17:26 gdamore 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.3 2006/09/04 05:17:26 gdamore Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/conf.h> 39 #include <sys/kernel.h> 40 #include <sys/types.h> 41 #include <sys/device.h> 42 #include <sys/gpio.h> 43 44 #include <machine/bus.h> 45 #include <machine/intr.h> 46 47 #include <mips/atheros/include/arbusvar.h> 48 49 #include <dev/gpio/gpiovar.h> 50 #include <dev/sysmon/sysmonvar.h> 51 #include <dev/sysmon/sysmon_taskq.h> 52 53 #include <mips/atheros/dev/argpioreg.h> 54 55 /* 56 * General Plan: 57 * 58 * Register GPIOs for all pins that are _not_ associated with the reset 59 * pin. (Possibly also not the sytem LED.) 60 */ 61 62 struct argpio_softc { 63 struct device sc_dev; 64 struct gpio_chipset_tag sc_gc; 65 gpio_pin_t sc_pins[ARGPIO_NPINS]; 66 int sc_npins; 67 bus_space_tag_t sc_st; 68 bus_space_handle_t sc_sh; 69 bus_size_t sc_size; 70 int sc_caps; 71 struct sysmon_pswitch sc_resetbtn; 72 void *sc_ih; 73 int sc_rstpin; 74 int sc_ledpin; 75 }; 76 77 static int argpio_match(struct device *, struct cfdata *, void *); 78 static void argpio_attach(struct device *, struct device *, void *); 79 static int argpio_intr(void *); 80 static void argpio_reset_pressed(void *); 81 static void argpio_ctl(void *, int, int); 82 static void argpio_write(void *, int, int); 83 static int argpio_read(void *, int); 84 85 CFATTACH_DECL(argpio, sizeof (struct argpio_softc), argpio_match, 86 argpio_attach, NULL, NULL); 87 88 #define INPUT(pin) (1 << (pin)) /* input bit */ 89 #define INTR(pin) (1 << ((pin) + 8)) /* interrupt bit */ 90 #define SERIAL(pin) (1 << ((pin) + 16)) /* serial mux bit */ 91 92 #define GETREG(sc, o) bus_space_read_4(sc->sc_st, sc->sc_sh, o) 93 #define PUTREG(sc, o, v) bus_space_write_4(sc->sc_st, sc->sc_sh, o, v) 94 #define FLUSH(sc) bus_space_barrier(sc->sc_st, sc->sc_sh, \ 95 0, 12, BUS_SPACE_BARRIER_SYNC) 96 97 int 98 argpio_match(struct device *parent, struct cfdata *match, void *aux) 99 { 100 struct arbus_attach_args *aa = aux; 101 102 return ((strcmp(aa->aa_name, "argpio") == 0) ? 1 : 0); 103 } 104 105 void 106 argpio_attach(struct device *parent, struct device *self, void *aux) 107 { 108 struct argpio_softc *sc = (struct argpio_softc *)self; 109 struct arbus_attach_args *aa = aux; 110 struct gpiobus_attach_args gba; 111 prop_number_t pn; 112 int i; 113 uint32_t reg; 114 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 printf("\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("%s: couldn't establish interrupt\n", 153 sc->sc_dev.dv_xname); 154 } 155 } 156 157 if (sc->sc_ih) { 158 sysmon_task_queue_init(); 159 160 sc->sc_resetbtn.smpsw_name = sc->sc_dev.dv_xname; 161 sc->sc_resetbtn.smpsw_type = PSWITCH_TYPE_RESET; 162 if (sysmon_pswitch_register(&sc->sc_resetbtn) != 0) 163 printf("%s: unable to register reset button\n", 164 sc->sc_dev.dv_xname); 165 } 166 167 reg = GETREG(sc, GPIO_CR); 168 169 for (i = 0; i < sc->sc_npins; i++) { 170 gpio_pin_t *pp; 171 172 pp = &sc->sc_pins[i]; 173 174 if (i == sc->sc_rstpin) { 175 /* configure as interrupt for reset */ 176 pp->pin_caps = GPIO_PIN_INPUT; 177 reg &= ~SERIAL(i); 178 reg |= INPUT(i); 179 /* only if we were able to set up the handler, tho' */ 180 if (sc->sc_ih != NULL) 181 reg |= INTR(i); 182 183 } else if (i == sc->sc_ledpin) { 184 /* configure as output for LED */ 185 pp->pin_caps = GPIO_PIN_OUTPUT; 186 reg &= ~SERIAL(i); 187 reg &= ~INPUT(i); 188 reg &= ~INTR(i); 189 190 } else { 191 if (reg & SERIAL(i)) { 192 /* pin multiplexed with serial bit */ 193 pp->pin_caps = 0; 194 } else { 195 pp->pin_caps = GPIO_PIN_INPUT | 196 GPIO_PIN_OUTPUT; 197 } 198 } 199 } 200 201 PUTREG(sc, GPIO_CR, reg); 202 FLUSH(sc); 203 204 gba.gba_gc = &sc->sc_gc; 205 gba.gba_pins = sc->sc_pins; 206 gba.gba_npins = sc->sc_npins; 207 config_found_ia(&sc->sc_dev, "gpiobus", &gba, gpiobus_print); 208 } 209 210 void 211 argpio_ctl(void *arg, int pin, int flags) 212 { 213 struct argpio_softc *sc = arg; 214 uint32_t reg; 215 216 reg = GETREG(sc, GPIO_CR); 217 if (reg & (SERIAL(pin) | INTR(pin))) { 218 printf("pin %d cannot be changed!\n", pin); 219 /* don't allow changes to these bits */ 220 return; 221 } 222 if (flags & GPIO_PIN_INPUT) { 223 reg |= INPUT(pin); 224 } else { 225 reg &= ~INPUT(pin); 226 } 227 228 PUTREG(sc, GPIO_CR, reg); 229 FLUSH(sc); 230 } 231 232 void 233 argpio_write(void *arg, int pin, int value) 234 { 235 struct argpio_softc *sc = arg; 236 uint32_t reg; 237 238 reg = GETREG(sc, GPIO_DO); 239 if (value) 240 reg &= ~(1 << pin); 241 else 242 reg |= (1 << pin); 243 PUTREG(sc, GPIO_DO, reg); 244 FLUSH(sc); 245 } 246 247 int 248 argpio_read(void *arg, int pin) 249 { 250 struct argpio_softc *sc = arg; 251 252 return ((GETREG(sc, GPIO_DI) & (1 << pin)) ? 253 GPIO_PIN_HIGH : GPIO_PIN_LOW); 254 } 255 256 void 257 argpio_reset_pressed(void *arg) 258 { 259 struct argpio_softc *sc = arg; 260 int x; 261 262 sysmon_pswitch_event(&sc->sc_resetbtn, PSWITCH_EVENT_PRESSED); 263 264 /* reenable the interrupt */ 265 x = splhigh(); 266 PUTREG(sc, GPIO_CR, 267 GETREG(sc, GPIO_CR) | INTR(sc->sc_rstpin)); 268 splx(x); 269 } 270 271 int 272 argpio_intr(void *arg) 273 { 274 struct argpio_softc *sc = arg; 275 276 if (sc->sc_rstpin < 0) 277 return 0; 278 279 /* this is an edge triggered interrupt, so disable it for now */ 280 PUTREG(sc, GPIO_CR, GETREG(sc, GPIO_CR) & ~INTR(sc->sc_rstpin)); 281 282 /* no other interrupt on this, so we have to claim it */ 283 sysmon_task_queue_sched(0, argpio_reset_pressed, sc); 284 285 return 1; 286 } 287