1157089Simp/*- 2157089Simp * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3157089Simp * 4157089Simp * Redistribution and use in source and binary forms, with or without 5157089Simp * modification, are permitted provided that the following conditions 6157089Simp * are met: 7157089Simp * 1. Redistributions of source code must retain the above copyright 8157089Simp * notice, this list of conditions and the following disclaimer. 9157089Simp * 2. Redistributions in binary form must reproduce the above copyright 10157089Simp * notice, this list of conditions and the following disclaimer in the 11157089Simp * documentation and/or other materials provided with the distribution. 12157089Simp * 13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16185265Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23185265Simp * SUCH DAMAGE. 24157089Simp */ 25157089Simp 26157089Simp#include <sys/cdefs.h> 27157089Simp__FBSDID("$FreeBSD$"); 28157089Simp 29157089Simp#include <sys/param.h> 30157089Simp#include <sys/systm.h> 31157089Simp#include <sys/bus.h> 32157089Simp#include <sys/conf.h> 33157089Simp#include <sys/kernel.h> 34157089Simp#include <sys/lock.h> 35157089Simp#include <sys/mbuf.h> 36157089Simp#include <sys/malloc.h> 37157089Simp#include <sys/module.h> 38157089Simp#include <sys/mutex.h> 39157089Simp#include <sys/rman.h> 40157089Simp#include <machine/bus.h> 41157089Simp 42213496Scognet#include <arm/at91/at91reg.h> 43157089Simp#include <arm/at91/at91_pioreg.h> 44160072Simp#include <arm/at91/at91_piovar.h> 45157089Simp 46157089Simpstruct at91_pio_softc 47157089Simp{ 48157089Simp device_t dev; /* Myself */ 49157089Simp void *intrhand; /* Interrupt handle */ 50157089Simp struct resource *irq_res; /* IRQ resource */ 51157089Simp struct resource *mem_res; /* Memory resource */ 52157089Simp struct mtx sc_mtx; /* basically a perimeter lock */ 53157089Simp struct cdev *cdev; 54157089Simp int flags; 55236080Smarius#define OPENED 1 56157089Simp}; 57157089Simp 58157089Simpstatic inline uint32_t 59157089SimpRD4(struct at91_pio_softc *sc, bus_size_t off) 60157089Simp{ 61236080Smarius 62213496Scognet return (bus_read_4(sc->mem_res, off)); 63157089Simp} 64157089Simp 65157089Simpstatic inline void 66157089SimpWR4(struct at91_pio_softc *sc, bus_size_t off, uint32_t val) 67157089Simp{ 68236080Smarius 69157089Simp bus_write_4(sc->mem_res, off, val); 70157089Simp} 71157089Simp 72236080Smarius#define AT91_PIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 73157089Simp#define AT91_PIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 74236080Smarius#define AT91_PIO_LOCK_INIT(_sc) \ 75157089Simp mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 76157089Simp "pio", MTX_SPIN) 77236080Smarius#define AT91_PIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 78236080Smarius#define AT91_PIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 79236080Smarius#define AT91_PIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 80236080Smarius#define CDEV2SOFTC(dev) ((dev)->si_drv1) 81157089Simp 82157089Simpstatic devclass_t at91_pio_devclass; 83157089Simp 84157089Simp/* bus entry points */ 85157089Simp 86157089Simpstatic int at91_pio_probe(device_t dev); 87157089Simpstatic int at91_pio_attach(device_t dev); 88157089Simpstatic int at91_pio_detach(device_t dev); 89166901Spisostatic int at91_pio_intr(void *); 90157089Simp 91157089Simp/* helper routines */ 92157089Simpstatic int at91_pio_activate(device_t dev); 93157089Simpstatic void at91_pio_deactivate(device_t dev); 94157089Simp 95157089Simp/* cdev routines */ 96157089Simpstatic d_open_t at91_pio_open; 97157089Simpstatic d_close_t at91_pio_close; 98157089Simpstatic d_ioctl_t at91_pio_ioctl; 99157089Simp 100157089Simpstatic struct cdevsw at91_pio_cdevsw = 101157089Simp{ 102157089Simp .d_version = D_VERSION, 103157089Simp .d_open = at91_pio_open, 104157089Simp .d_close = at91_pio_close, 105157089Simp .d_ioctl = at91_pio_ioctl 106157089Simp}; 107157089Simp 108157089Simpstatic int 109157089Simpat91_pio_probe(device_t dev) 110157089Simp{ 111160072Simp const char *name; 112160072Simp 113160072Simp switch (device_get_unit(dev)) { 114160072Simp case 0: 115160072Simp name = "PIOA"; 116160072Simp break; 117160072Simp case 1: 118160072Simp name = "PIOB"; 119160072Simp break; 120160072Simp case 2: 121160072Simp name = "PIOC"; 122160072Simp break; 123160072Simp case 3: 124160072Simp name = "PIOD"; 125160072Simp break; 126160072Simp default: 127160072Simp name = "PIO"; 128160072Simp break; 129160072Simp } 130160072Simp device_set_desc(dev, name); 131157089Simp return (0); 132157089Simp} 133157089Simp 134157089Simpstatic int 135157089Simpat91_pio_attach(device_t dev) 136157089Simp{ 137236080Smarius struct at91_pio_softc *sc; 138157089Simp int err; 139157089Simp 140236080Smarius sc = device_get_softc(dev); 141157089Simp sc->dev = dev; 142157089Simp err = at91_pio_activate(dev); 143157089Simp if (err) 144157089Simp goto out; 145157089Simp 146160072Simp device_printf(dev, "ABSR: %#x OSR: %#x PSR:%#x ODSR: %#x\n", 147160072Simp RD4(sc, PIO_ABSR), RD4(sc, PIO_OSR), RD4(sc, PIO_PSR), 148160072Simp RD4(sc, PIO_ODSR)); 149157089Simp AT91_PIO_LOCK_INIT(sc); 150157089Simp 151157089Simp /* 152236080Smarius * Activate the interrupt, but disable all interrupts in the hardware. 153157089Simp */ 154157089Simp WR4(sc, PIO_IDR, 0xffffffff); 155166901Spiso err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, 156166901Spiso at91_pio_intr, NULL, sc, &sc->intrhand); 157157089Simp if (err) { 158157089Simp AT91_PIO_LOCK_DESTROY(sc); 159157089Simp goto out; 160157089Simp } 161164745Simp sc->cdev = make_dev(&at91_pio_cdevsw, device_get_unit(dev), UID_ROOT, 162164745Simp GID_WHEEL, 0600, "pio%d", device_get_unit(dev)); 163157089Simp if (sc->cdev == NULL) { 164157089Simp err = ENOMEM; 165157089Simp goto out; 166157089Simp } 167157089Simp sc->cdev->si_drv1 = sc; 168237093Smariusout: 169157089Simp if (err) 170157089Simp at91_pio_deactivate(dev); 171157089Simp return (err); 172157089Simp} 173157089Simp 174157089Simpstatic int 175157089Simpat91_pio_detach(device_t dev) 176157089Simp{ 177236080Smarius 178157089Simp return (EBUSY); /* XXX */ 179157089Simp} 180157089Simp 181157089Simpstatic int 182157089Simpat91_pio_activate(device_t dev) 183157089Simp{ 184157089Simp struct at91_pio_softc *sc; 185157089Simp int rid; 186157089Simp 187157089Simp sc = device_get_softc(dev); 188157089Simp rid = 0; 189157089Simp sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 190157089Simp RF_ACTIVE); 191157089Simp if (sc->mem_res == NULL) 192157089Simp goto errout; 193157089Simp rid = 0; 194157089Simp sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 195157089Simp RF_ACTIVE | RF_SHAREABLE); 196157089Simp if (sc->irq_res == NULL) 197157089Simp goto errout; 198157089Simp return (0); 199157089Simperrout: 200157089Simp at91_pio_deactivate(dev); 201157089Simp return (ENOMEM); 202157089Simp} 203157089Simp 204157089Simpstatic void 205157089Simpat91_pio_deactivate(device_t dev) 206157089Simp{ 207157089Simp struct at91_pio_softc *sc; 208157089Simp 209157089Simp sc = device_get_softc(dev); 210157089Simp if (sc->intrhand) 211157089Simp bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 212157089Simp sc->intrhand = 0; 213157089Simp bus_generic_detach(sc->dev); 214157089Simp if (sc->mem_res) 215157089Simp bus_release_resource(dev, SYS_RES_IOPORT, 216157089Simp rman_get_rid(sc->mem_res), sc->mem_res); 217157089Simp sc->mem_res = 0; 218157089Simp if (sc->irq_res) 219157089Simp bus_release_resource(dev, SYS_RES_IRQ, 220157089Simp rman_get_rid(sc->irq_res), sc->irq_res); 221157089Simp sc->irq_res = 0; 222157089Simp} 223157089Simp 224166901Spisostatic int 225157089Simpat91_pio_intr(void *xsc) 226157089Simp{ 227157089Simp struct at91_pio_softc *sc = xsc; 228157089Simp#if 0 229157089Simp uint32_t status; 230157089Simp 231236080Smarius /* Reading the status also clears the interrupt. */ 232157089Simp status = RD4(sc, PIO_SR); 233157089Simp if (status == 0) 234157089Simp return; 235157089Simp AT91_PIO_LOCK(sc); 236157089Simp AT91_PIO_UNLOCK(sc); 237157089Simp#endif 238157089Simp wakeup(sc); 239166901Spiso return (FILTER_HANDLED); 240157089Simp} 241157089Simp 242236080Smariusstatic int 243157089Simpat91_pio_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 244157089Simp{ 245157089Simp struct at91_pio_softc *sc; 246157089Simp 247157089Simp sc = CDEV2SOFTC(dev); 248157089Simp AT91_PIO_LOCK(sc); 249157089Simp if (!(sc->flags & OPENED)) { 250157089Simp sc->flags |= OPENED; 251157089Simp#if 0 252236080Smarius /* Enable interrupts. */ 253157089Simp#endif 254157089Simp } 255157089Simp AT91_PIO_UNLOCK(sc); 256236080Smarius return (0); 257157089Simp} 258157089Simp 259157089Simpstatic int 260157089Simpat91_pio_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 261157089Simp{ 262157089Simp struct at91_pio_softc *sc; 263157089Simp 264157089Simp sc = CDEV2SOFTC(dev); 265157089Simp AT91_PIO_LOCK(sc); 266157089Simp sc->flags &= ~OPENED; 267157089Simp#if 0 268236080Smarius /* Disable interrupts. */ 269157089Simp#endif 270157089Simp AT91_PIO_UNLOCK(sc); 271157089Simp return (0); 272157089Simp} 273157089Simp 274157089Simpstatic int 275157089Simpat91_pio_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 276157089Simp struct thread *td) 277157089Simp{ 278236080Smarius 279157089Simp return (ENXIO); 280157089Simp} 281157089Simp 282160072Simp/* 283160072Simp * The following functions are called early in the boot process, so 284160072Simp * don't use bus_space, as that isn't yet available when we need to use 285160072Simp * them. 286160072Simp */ 287236080Smarius 288160072Simpvoid 289160363Simpat91_pio_use_periph_a(uint32_t pio, uint32_t periph_a_mask, int use_pullup) 290160072Simp{ 291213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 292160072Simp 293160072Simp PIO[PIO_ASR / 4] = periph_a_mask; 294160072Simp PIO[PIO_PDR / 4] = periph_a_mask; 295160363Simp if (use_pullup) 296160363Simp PIO[PIO_PUER / 4] = periph_a_mask; 297160363Simp else 298160363Simp PIO[PIO_PUDR / 4] = periph_a_mask; 299160072Simp} 300160072Simp 301160072Simpvoid 302160363Simpat91_pio_use_periph_b(uint32_t pio, uint32_t periph_b_mask, int use_pullup) 303160072Simp{ 304213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 305160072Simp 306160072Simp PIO[PIO_BSR / 4] = periph_b_mask; 307160072Simp PIO[PIO_PDR / 4] = periph_b_mask; 308160363Simp if (use_pullup) 309160363Simp PIO[PIO_PUER / 4] = periph_b_mask; 310160363Simp else 311160363Simp PIO[PIO_PUDR / 4] = periph_b_mask; 312160072Simp} 313160072Simp 314160072Simpvoid 315160072Simpat91_pio_use_gpio(uint32_t pio, uint32_t gpio_mask) 316160072Simp{ 317213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 318160072Simp 319160072Simp PIO[PIO_PER / 4] = gpio_mask; 320160072Simp} 321160072Simp 322160072Simpvoid 323160072Simpat91_pio_gpio_input(uint32_t pio, uint32_t input_enable_mask) 324160072Simp{ 325213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 326160072Simp 327160072Simp PIO[PIO_ODR / 4] = input_enable_mask; 328160072Simp} 329160072Simp 330160072Simpvoid 331160363Simpat91_pio_gpio_output(uint32_t pio, uint32_t output_enable_mask, int use_pullup) 332160072Simp{ 333213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 334160072Simp 335160072Simp PIO[PIO_OER / 4] = output_enable_mask; 336160363Simp if (use_pullup) 337160363Simp PIO[PIO_PUER / 4] = output_enable_mask; 338160363Simp else 339160363Simp PIO[PIO_PUDR / 4] = output_enable_mask; 340160072Simp} 341160072Simp 342160072Simpvoid 343160072Simpat91_pio_gpio_set(uint32_t pio, uint32_t data_mask) 344160072Simp{ 345213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 346160072Simp 347160072Simp PIO[PIO_SODR / 4] = data_mask; 348160072Simp} 349160072Simp 350160072Simpvoid 351160072Simpat91_pio_gpio_clear(uint32_t pio, uint32_t data_mask) 352160072Simp{ 353213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 354160072Simp 355160072Simp PIO[PIO_CODR / 4] = data_mask; 356160072Simp} 357160072Simp 358181884Simpuint8_t 359181884Simpat91_pio_gpio_get(uint32_t pio, uint32_t data_mask) 360181884Simp{ 361213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 362181884Simp 363181884Simp data_mask &= PIO[PIO_PDSR / 4]; 364181884Simp 365181884Simp return (data_mask ? 1 : 0); 366181884Simp} 367181884Simp 368181884Simpvoid 369181884Simpat91_pio_gpio_set_deglitch(uint32_t pio, uint32_t data_mask, int use_deglitch) 370181884Simp{ 371213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 372181884Simp 373181884Simp if (use_deglitch) 374181884Simp PIO[PIO_IFER / 4] = data_mask; 375181884Simp else 376181884Simp PIO[PIO_IFDR / 4] = data_mask; 377181884Simp} 378181884Simp 379181884Simpvoid 380236080Smariusat91_pio_gpio_set_interrupt(uint32_t pio, uint32_t data_mask, 381181884Simp int enable_interrupt) 382181884Simp{ 383213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 384181884Simp 385181884Simp if (enable_interrupt) 386181884Simp PIO[PIO_IER / 4] = data_mask; 387181884Simp else 388181884Simp PIO[PIO_IDR / 4] = data_mask; 389181884Simp} 390181884Simp 391181884Simpuint32_t 392181884Simpat91_pio_gpio_clear_interrupt(uint32_t pio) 393181884Simp{ 394213496Scognet uint32_t *PIO = (uint32_t *)(AT91_BASE + pio); 395236080Smarius 396236080Smarius /* Reading this register will clear the interrupts. */ 397181884Simp return (PIO[PIO_ISR / 4]); 398181884Simp} 399181884Simp 400157089Simpstatic device_method_t at91_pio_methods[] = { 401157089Simp /* Device interface */ 402157089Simp DEVMETHOD(device_probe, at91_pio_probe), 403157089Simp DEVMETHOD(device_attach, at91_pio_attach), 404157089Simp DEVMETHOD(device_detach, at91_pio_detach), 405157089Simp 406236080Smarius DEVMETHOD_END 407157089Simp}; 408157089Simp 409157089Simpstatic driver_t at91_pio_driver = { 410157089Simp "at91_pio", 411157089Simp at91_pio_methods, 412157089Simp sizeof(struct at91_pio_softc), 413157089Simp}; 414157089Simp 415236080SmariusDRIVER_MODULE(at91_pio, atmelarm, at91_pio_driver, at91_pio_devclass, NULL, 416236080Smarius NULL); 417