Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/rockchip/rk_gpio.c
Context not available. | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||
* | * | ||||
* Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> | * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> | ||||
* Copyright (c) 2021 Soren Schmidt <sos@deepcore.dk> | |||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
Context not available. | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/proc.h> | |||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
Context not available. | |||||
#include <dev/extres/clk/clk.h> | #include <dev/extres/clk/clk.h> | ||||
#include "gpio_if.h" | #include "gpio_if.h" | ||||
#include "pic_if.h" | |||||
#include "fdt_pinctrl_if.h" | enum gpio_regs { | ||||
RK_GPIO_SWPORTA_DR = 1, /* Data register */ | |||||
RK_GPIO_SWPORTA_DDR, /* Data direction register */ | |||||
RK_GPIO_INTEN, /* Interrupt enable register */ | |||||
RK_GPIO_INTMASK, /* Interrupt mask register */ | |||||
RK_GPIO_INTTYPE_LEVEL, /* Interrupt level register */ | |||||
RK_GPIO_INTTYPE_BOTH, /* Both rise and falling edge */ | |||||
RK_GPIO_INT_POLARITY, /* Interrupt polarity register */ | |||||
RK_GPIO_INT_STATUS, /* Interrupt status register */ | |||||
RK_GPIO_INT_RAWSTATUS, /* Raw Interrupt status register */ | |||||
RK_GPIO_DEBOUNCE, /* Debounce enable register */ | |||||
RK_GPIO_PORTA_EOI, /* Clear interrupt register */ | |||||
RK_GPIO_EXT_PORTA, /* External port register */ | |||||
RK_GPIO_REGNUM | |||||
}; | |||||
#define RK_GPIO_SWPORTA_DR 0x00 /* Data register */ | #define RK_GPIO_COUNT 32 | ||||
#define RK_GPIO_SWPORTA_DDR 0x04 /* Data direction register */ | |||||
#define RK_GPIO_INTEN 0x30 /* Interrupt enable register */ | static struct resource_spec rk_gpio_spec[] = { | ||||
#define RK_GPIO_INTMASK 0x34 /* Interrupt mask register */ | { SYS_RES_MEMORY, 0, RF_ACTIVE }, | ||||
#define RK_GPIO_INTTYPE_LEVEL 0x38 /* Interrupt level register */ | { SYS_RES_IRQ, 0, RF_ACTIVE }, | ||||
#define RK_GPIO_INT_POLARITY 0x3C /* Interrupt polarity register */ | { -1, 0 } | ||||
#define RK_GPIO_INT_STATUS 0x40 /* Interrupt status register */ | }; | ||||
manu: Please do not move code without reason, makes reviewing harder. | |||||
#define RK_GPIO_INT_RAWSTATUS 0x44 /* Raw Interrupt status register */ | |||||
#define RK_GPIO_DEBOUNCE 0x48 /* Debounce enable register */ | struct rk_pin_irqsrc { | ||||
struct intr_irqsrc isrc; | |||||
uint32_t irq; | |||||
uint32_t mode; | |||||
}; | |||||
#define RK_GPIO_PORTA_EOI 0x4C /* Clear interrupt register */ | |||||
#define RK_GPIO_EXT_PORTA 0x50 /* External port register */ | |||||
#define RK_GPIO_LS_SYNC 0x60 /* Level sensitive syncronization enable register */ | |||||
#define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ | |||||
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) | |||||
struct rk_gpio_softc { | struct rk_gpio_softc { | ||||
device_t sc_dev; | device_t dev; | ||||
device_t sc_busdev; | device_t busdev; | ||||
struct mtx sc_mtx; | device_t pinctrl; | ||||
struct resource *sc_res[2]; | uint32_t version; | ||||
bus_space_tag_t sc_bst; | struct mtx mtx; | ||||
Not Done Inline ActionsPlease do not change variable names, it makes reviewing much harder. manu: Please do not change variable names, it makes reviewing much harder. | |||||
bus_space_handle_t sc_bsh; | |||||
clk_t clk; | clk_t clk; | ||||
device_t pinctrl; | struct resource *res[nitems(rk_gpio_spec)]; | ||||
void *ihandle; | |||||
uint8_t regs[RK_GPIO_REGNUM]; | |||||
struct rk_pin_irqsrc isrcs[RK_GPIO_COUNT]; | |||||
}; | }; | ||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"rockchip,gpio-bank", 1}, | {"rockchip,gpio-bank", 1}, | ||||
{NULL, 0} | {NULL, 0} | ||||
}; | }; | ||||
static struct resource_spec rk_gpio_spec[] = { | #define RK_GPIO_VERSION 0x78 | ||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, | #define RK_GPIO_TYPE_V1 0x00000000 | ||||
{ SYS_RES_IRQ, 0, RF_ACTIVE }, | #define RK_GPIO_TYPE_V2 0x01000c2b | ||||
{ -1, 0 } | #define RK_GPIO_ISRC(sc, irq) (&(sc->isrcs[irq].isrc)) | ||||
}; | |||||
static int rk_gpio_detach(device_t dev); | |||||
#define RK_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) | static int | ||||
#define RK_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) | rk_gpio_read_bit(struct rk_gpio_softc *sc, int reg, int bit) | ||||
#define RK_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) | { | ||||
int offset = sc->regs[reg]; | |||||
uint32_t value; | |||||
#define RK_GPIO_WRITE(_sc, _off, _val) \ | mtx_lock_spin(&sc->mtx); | ||||
bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) | if (sc->version == RK_GPIO_TYPE_V1) { | ||||
#define RK_GPIO_READ(_sc, _off) \ | value = bus_read_4(sc->res[0], offset); | ||||
bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) | value >>= bit; | ||||
} else { | |||||
value = bus_read_4(sc->res[0], bit > 15 ? offset + 4 : offset); | |||||
value >>= (bit % 16); | |||||
} | |||||
mtx_unlock_spin(&sc->mtx); | |||||
return (value & 1); | |||||
} | |||||
static void | |||||
rk_gpio_write_bit(struct rk_gpio_softc *sc, int reg, int bit, int data) | |||||
{ | |||||
int offset = sc->regs[reg]; | |||||
uint32_t value; | |||||
mtx_lock_spin(&sc->mtx); | |||||
if (sc->version == RK_GPIO_TYPE_V1) { | |||||
value = bus_read_4(sc->res[0], offset); | |||||
if (data) | |||||
value |= (1 << bit); | |||||
else | |||||
value &= ~(1 << bit); | |||||
bus_write_4(sc->res[0], offset, value); | |||||
} else { | |||||
if (data) | |||||
value = (1 << (bit % 16)); | |||||
else | |||||
value = 0; | |||||
value |= (1 << ((bit % 16) + 16)); | |||||
bus_write_4(sc->res[0], bit > 15 ? offset + 4 : offset, value); | |||||
} | |||||
mtx_unlock_spin(&sc->mtx); | |||||
} | |||||
static uint32_t | |||||
rk_gpio_read_4(struct rk_gpio_softc *sc, int reg) | |||||
{ | |||||
int offset = sc->regs[reg]; | |||||
uint32_t value; | |||||
mtx_lock_spin(&sc->mtx); | |||||
if (sc->version == RK_GPIO_TYPE_V1) | |||||
value = bus_read_4(sc->res[0], offset); | |||||
else | |||||
value = (bus_read_4(sc->res[0], offset) & 0xffff) | | |||||
(bus_read_4(sc->res[0], offset + 4) << 16); | |||||
mtx_unlock_spin(&sc->mtx); | |||||
return (value); | |||||
} | |||||
static void | |||||
rk_gpio_write_4(struct rk_gpio_softc *sc, int reg, uint32_t value) | |||||
{ | |||||
int offset = sc->regs[reg]; | |||||
mtx_lock_spin(&sc->mtx); | |||||
if (sc->version == RK_GPIO_TYPE_V1) | |||||
bus_write_4(sc->res[0], offset, value); | |||||
else { | |||||
bus_write_4(sc->res[0], offset, (value & 0xffff) | 0xffff0000); | |||||
bus_write_4(sc->res[0], offset + 4, (value >> 16) | 0xffff0000); | |||||
} | |||||
mtx_unlock_spin(&sc->mtx); | |||||
} | |||||
static int | static int | ||||
rk_gpio_intr(void *arg) | |||||
{ | |||||
struct rk_gpio_softc *sc = (struct rk_gpio_softc *)arg;; | |||||
struct trapframe *tf = curthread->td_intr_frame; | |||||
uint32_t status; | |||||
status = rk_gpio_read_4(sc, RK_GPIO_INT_STATUS); | |||||
rk_gpio_write_4(sc, RK_GPIO_PORTA_EOI, status); | |||||
while (status) { | |||||
int pin = ffs(status) - 1; | |||||
status &= ~(1 << pin); | |||||
if (intr_isrc_dispatch(RK_GPIO_ISRC(sc, pin), tf)) { | |||||
device_printf(sc->dev, "Interrupt pin=%d unhandled\n", | |||||
pin); | |||||
continue; | |||||
} | |||||
if ((sc->version == RK_GPIO_TYPE_V1) && | |||||
(sc->isrcs[pin].mode & GPIO_INTR_EDGE_BOTH)) { | |||||
if (rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin)) | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, | |||||
(1 << pin), 0); | |||||
else | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, | |||||
(1 << pin), 1); | |||||
} | |||||
} | |||||
return (FILTER_HANDLED); | |||||
} | |||||
static int | |||||
rk_gpio_probe(device_t dev) | rk_gpio_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) | if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, "RockChip GPIO controller"); | |||||
device_set_desc(dev, "RockChip GPIO Bank controller"); | |||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static int | static int | ||||
rk_gpio_detach(device_t dev) | |||||
{ | |||||
struct rk_gpio_softc *sc = device_get_softc(dev); | |||||
if (sc->busdev) | |||||
gpiobus_detach_bus(dev); | |||||
bus_release_resources(dev, rk_gpio_spec, sc->res); | |||||
mtx_destroy(&sc->mtx); | |||||
clk_disable(sc->clk); | |||||
return (0); | |||||
} | |||||
static int | |||||
rk_gpio_attach(device_t dev) | rk_gpio_attach(device_t dev) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc; | ||||
phandle_t node; | phandle_t node; | ||||
int err; | int irq, err; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->sc_dev = dev; | sc->dev = dev; | ||||
sc->pinctrl = device_get_parent(dev); | sc->pinctrl = device_get_parent(dev); | ||||
node = ofw_bus_get_node(sc->sc_dev); | node = ofw_bus_get_node(sc->dev); | ||||
if (!OF_hasprop(node, "gpio-controller")) | if (!OF_hasprop(node, "gpio-controller")) | ||||
return (ENXIO); | return (ENXIO); | ||||
mtx_init(&sc->sc_mtx, "rk gpio", "gpio", MTX_SPIN); | mtx_init(&sc->mtx, "rk gpio", "gpio", MTX_SPIN); | ||||
if (bus_alloc_resources(dev, rk_gpio_spec, sc->sc_res)) { | if (bus_alloc_resources(dev, rk_gpio_spec, sc->res)) { | ||||
device_printf(dev, "could not allocate resources\n"); | device_printf(dev, "Could not allocate resources\n"); | ||||
bus_release_resources(dev, rk_gpio_spec, sc->sc_res); | bus_release_resources(dev, rk_gpio_spec, sc->res); | ||||
mtx_destroy(&sc->sc_mtx); | mtx_destroy(&sc->mtx); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
sc->sc_bst = rman_get_bustag(sc->sc_res[0]); | |||||
sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); | |||||
if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) { | if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) { | ||||
device_printf(dev, "Cannot get clock\n"); | device_printf(dev, "Can not get clock\n"); | ||||
rk_gpio_detach(dev); | bus_release_resources(dev, rk_gpio_spec, sc->res); | ||||
mtx_destroy(&sc->mtx); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
err = clk_enable(sc->clk); | if ((err = clk_enable(sc->clk))) { | ||||
if (err != 0) { | |||||
device_printf(dev, "Could not enable clock %s\n", | device_printf(dev, "Could not enable clock %s\n", | ||||
clk_get_name(sc->clk)); | clk_get_name(sc->clk)); | ||||
bus_release_resources(dev, rk_gpio_spec, sc->res); | |||||
mtx_destroy(&sc->mtx); | |||||
return (ENXIO); | |||||
} | |||||
if ((err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, | |||||
rk_gpio_intr, NULL, sc, &sc->ihandle))) { | |||||
device_printf(dev, "Can not setup IRQ\n"); | |||||
rk_gpio_detach(dev); | rk_gpio_detach(dev); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
sc->sc_busdev = gpiobus_attach_bus(dev); | switch ((sc->version = bus_read_4(sc->res[0], RK_GPIO_VERSION))) { | ||||
if (sc->sc_busdev == NULL) { | case RK_GPIO_TYPE_V1: | ||||
sc->regs[RK_GPIO_SWPORTA_DR] = 0x00; | |||||
sc->regs[RK_GPIO_SWPORTA_DDR] = 0x04; | |||||
sc->regs[RK_GPIO_INTEN] = 0x30; | |||||
sc->regs[RK_GPIO_INTMASK] = 0x34; | |||||
sc->regs[RK_GPIO_INTTYPE_LEVEL] = 0x38; | |||||
sc->regs[RK_GPIO_INT_POLARITY] = 0x3c; | |||||
sc->regs[RK_GPIO_INT_STATUS] = 0x40; | |||||
sc->regs[RK_GPIO_INT_RAWSTATUS] = 0x44; | |||||
sc->regs[RK_GPIO_DEBOUNCE] = 0x48; | |||||
sc->regs[RK_GPIO_PORTA_EOI] = 0x4c; | |||||
sc->regs[RK_GPIO_EXT_PORTA] = 0x50; | |||||
break; | |||||
case RK_GPIO_TYPE_V2: | |||||
sc->regs[RK_GPIO_SWPORTA_DR] = 0x00; | |||||
sc->regs[RK_GPIO_SWPORTA_DDR] = 0x08; | |||||
sc->regs[RK_GPIO_INTEN] = 0x10; | |||||
sc->regs[RK_GPIO_INTMASK] = 0x18; | |||||
sc->regs[RK_GPIO_INTTYPE_LEVEL] = 0x20; | |||||
sc->regs[RK_GPIO_INTTYPE_BOTH] = 0x30; | |||||
sc->regs[RK_GPIO_INT_POLARITY] = 0x28; | |||||
sc->regs[RK_GPIO_INT_STATUS] = 0x50; | |||||
sc->regs[RK_GPIO_INT_RAWSTATUS] = 0x58; | |||||
sc->regs[RK_GPIO_DEBOUNCE] = 0x38; | |||||
sc->regs[RK_GPIO_PORTA_EOI] = 0x60; | |||||
sc->regs[RK_GPIO_EXT_PORTA] = 0x70; | |||||
break; | |||||
default: | |||||
device_printf(dev, "Unknown gpio version %08x\n", sc->version); | |||||
rk_gpio_detach(dev); | rk_gpio_detach(dev); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
return (0); | for (irq = 0; irq < RK_GPIO_COUNT; irq++) { | ||||
} | sc->isrcs[irq].irq = irq; | ||||
sc->isrcs[irq].mode = GPIO_INTR_CONFORM; | |||||
if ((err = intr_isrc_register(RK_GPIO_ISRC(sc, irq), | |||||
dev, 0, "%s", device_get_nameunit(dev)))) { | |||||
device_printf(dev, "Can not register isrc %d\n", err); | |||||
rk_gpio_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
} | |||||
static int | if (!intr_pic_register(dev, OF_xref_from_node(node))) { | ||||
rk_gpio_detach(device_t dev) | device_printf(dev, "Can not register pic\n"); | ||||
{ | rk_gpio_detach(dev); | ||||
struct rk_gpio_softc *sc; | return (ENXIO); | ||||
} | |||||
sc = device_get_softc(dev); | if (!(sc->busdev = gpiobus_attach_bus(dev))) { | ||||
device_printf(dev, "Can not attach bus\n"); | |||||
if (sc->sc_busdev) | rk_gpio_detach(dev); | ||||
gpiobus_detach_bus(dev); | return (ENXIO); | ||||
bus_release_resources(dev, rk_gpio_spec, sc->sc_res); | } | ||||
mtx_destroy(&sc->sc_mtx); | return (0); | ||||
clk_disable(sc->clk); | |||||
return(0); | |||||
} | } | ||||
static device_t | static device_t | ||||
rk_gpio_get_bus(device_t dev) | rk_gpio_get_bus(device_t dev) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc = device_get_softc(dev); | ||||
sc = device_get_softc(dev); | return (sc->busdev); | ||||
return (sc->sc_busdev); | |||||
} | } | ||||
static int | static int | ||||
Context not available. | |||||
rk_gpio_pin_max(device_t dev, int *maxpin) | rk_gpio_pin_max(device_t dev, int *maxpin) | ||||
{ | { | ||||
/* Each bank have always 32 pins */ | *maxpin = RK_GPIO_COUNT - 1; | ||||
/* XXX not true*/ | |||||
Not Done Inline ActionsThis comment is still true. manu: This comment is still true. | |||||
*maxpin = 31; | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
rk_gpio_pin_getname(device_t dev, uint32_t pin, char *name) | rk_gpio_pin_getname(device_t dev, uint32_t pin, char *name) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | |||||
uint32_t bank; | |||||
sc = device_get_softc(dev); | if (pin >= RK_GPIO_COUNT) | ||||
return EINVAL; | |||||
if (pin >= 32) | snprintf(name, GPIOMAXNAME, "gpio%d", pin); | ||||
return (EINVAL); | |||||
bank = pin / 8; | |||||
pin = pin - (bank * 8); | |||||
RK_GPIO_LOCK(sc); | |||||
snprintf(name, GPIOMAXNAME, "P%c%d", bank + 'A', pin); | |||||
RK_GPIO_UNLOCK(sc); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
rk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) | rk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | |||||
uint32_t reg; | |||||
int rv; | |||||
bool is_gpio; | |||||
sc = device_get_softc(dev); | if (pin >= RK_GPIO_COUNT) | ||||
return EINVAL; | |||||
rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio); | *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ | ||||
if (rv != 0) | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_EDGE_BOTH | \ | ||||
return (rv); | GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \ | ||||
if (!is_gpio) | GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW); | ||||
return (EINVAL); | |||||
*flags = 0; | |||||
rv = FDT_PINCTRL_GET_FLAGS(sc->pinctrl, dev, pin, flags); | |||||
if (rv != 0) | |||||
return (rv); | |||||
RK_GPIO_LOCK(sc); | |||||
reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); | |||||
RK_GPIO_UNLOCK(sc); | |||||
if (reg & (1 << pin)) | |||||
*flags |= GPIO_PIN_OUTPUT; | |||||
else | |||||
*flags |= GPIO_PIN_INPUT; | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
rk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) | rk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) | ||||
{ | { | ||||
struct rk_gpio_softc *sc = device_get_softc(dev); | |||||
*caps = RK_GPIO_DEFAULT_CAPS; | if (pin >= RK_GPIO_COUNT) | ||||
return (EINVAL); | |||||
if (rk_gpio_read_bit(sc, RK_GPIO_SWPORTA_DDR, pin)) | |||||
*flags = GPIO_PIN_OUTPUT; | |||||
else | |||||
*flags = GPIO_PIN_INPUT; | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
rk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) | rk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc = device_get_softc(dev); | ||||
uint32_t reg; | |||||
int rv; | |||||
bool is_gpio; | |||||
sc = device_get_softc(dev); | if (pin >= RK_GPIO_COUNT) | ||||
rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio); | |||||
if (rv != 0) | |||||
return (rv); | |||||
if (!is_gpio) | |||||
return (EINVAL); | return (EINVAL); | ||||
rv = FDT_PINCTRL_SET_FLAGS(sc->pinctrl, dev, pin, flags); | rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, | ||||
if (rv != 0) | (flags & GPIO_PIN_OUTPUT ? 1 : 0)); | ||||
return (rv); | |||||
RK_GPIO_LOCK(sc); | |||||
reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); | |||||
if (flags & GPIO_PIN_INPUT) | |||||
reg &= ~(1 << pin); | |||||
else if (flags & GPIO_PIN_OUTPUT) | |||||
reg |= (1 << pin); | |||||
RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DDR, reg); | |||||
RK_GPIO_UNLOCK(sc); | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
rk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) | rk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc = device_get_softc(dev); | ||||
uint32_t reg; | |||||
sc = device_get_softc(dev); | if (pin >= RK_GPIO_COUNT) | ||||
return (EINVAL); | |||||
RK_GPIO_LOCK(sc); | *val = rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin); | ||||
reg = RK_GPIO_READ(sc, RK_GPIO_EXT_PORTA); | |||||
RK_GPIO_UNLOCK(sc); | |||||
*val = reg & (1 << pin) ? 1 : 0; | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
rk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) | rk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc = device_get_softc(dev); | ||||
uint32_t reg; | |||||
sc = device_get_softc(dev); | if (pin >= RK_GPIO_COUNT) | ||||
return (EINVAL); | |||||
RK_GPIO_LOCK(sc); | rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DR, pin, value); | ||||
reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR); | |||||
if (value) | |||||
reg |= (1 << pin); | |||||
else | |||||
reg &= ~(1 << pin); | |||||
RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg); | |||||
RK_GPIO_UNLOCK(sc); | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
rk_gpio_pin_toggle(device_t dev, uint32_t pin) | rk_gpio_pin_toggle(device_t dev, uint32_t pin) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc = device_get_softc(dev); | ||||
uint32_t reg; | int value; | ||||
sc = device_get_softc(dev); | if (pin >= RK_GPIO_COUNT) | ||||
return (EINVAL); | |||||
RK_GPIO_LOCK(sc); | value = rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin); | ||||
reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR); | rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DR, pin, !value); | ||||
if (reg & (1 << pin)) | |||||
reg &= ~(1 << pin); | |||||
else | |||||
reg |= (1 << pin); | |||||
RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg); | |||||
RK_GPIO_UNLOCK(sc); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
rk_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, | rk_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, | ||||
uint32_t change_pins, uint32_t *orig_pins) | pcell_t *gpios, uint32_t *pin, uint32_t *flags) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | |||||
uint32_t reg; | |||||
sc = device_get_softc(dev); | *pin = gpios[0]; | ||||
*flags = gpios[1]; | |||||
return (0); | |||||
} | |||||
RK_GPIO_LOCK(sc); | static phandle_t | ||||
reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR); | rk_gpio_get_node(device_t bus, device_t dev) | ||||
if (orig_pins) | { | ||||
*orig_pins = reg; | |||||
if ((clear_pins | change_pins) != 0) { | return (ofw_bus_get_node(bus)); | ||||
reg = (reg & ~clear_pins) ^ change_pins; | } | ||||
RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg); | |||||
static int | |||||
rk_pic_map_intr(device_t dev, struct intr_map_data *data, | |||||
struct intr_irqsrc **isrcp) | |||||
{ | |||||
struct rk_gpio_softc *sc = device_get_softc(dev); | |||||
struct intr_map_data_gpio *gdata; | |||||
uint32_t irq; | |||||
if (data->type != INTR_MAP_DATA_GPIO) { | |||||
device_printf(dev, "Wrong type\n"); | |||||
return (ENOTSUP); | |||||
} | } | ||||
RK_GPIO_UNLOCK(sc); | gdata = (struct intr_map_data_gpio *)data; | ||||
irq = gdata->gpio_pin_num; | |||||
if (irq >= RK_GPIO_COUNT) { | |||||
device_printf(dev, "Invalid interrupt %u\n", irq); | |||||
return (EINVAL); | |||||
} | |||||
*isrcp = RK_GPIO_ISRC(sc, irq); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
rk_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, | rk_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, | ||||
uint32_t *pin_flags) | struct resource *res, struct intr_map_data *data) | ||||
{ | { | ||||
struct rk_gpio_softc *sc; | struct rk_gpio_softc *sc = device_get_softc(dev); | ||||
uint32_t reg, set, mask, flags; | struct rk_pin_irqsrc *rkisrc = (struct rk_pin_irqsrc *)isrc; | ||||
int i; | struct intr_map_data_gpio *gdata; | ||||
uint32_t mode; | |||||
uint8_t pin; | |||||
sc = device_get_softc(dev); | if (!data) { | ||||
device_printf(dev, "No map data\n"); | |||||
return (ENOTSUP); | |||||
} | |||||
gdata = (struct intr_map_data_gpio *)data; | |||||
mode = gdata->gpio_intr_mode; | |||||
pin = gdata->gpio_pin_num; | |||||
if (first_pin != 0 || num_pins > 32) | if (rkisrc->irq != gdata->gpio_pin_num) { | ||||
device_printf(dev, "Interrupts don't match\n"); | |||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
set = 0; | if (isrc->isrc_handlers != 0) { | ||||
mask = 0; | device_printf(dev, "Handler already attached\n"); | ||||
for (i = 0; i < num_pins; i++) { | return (rkisrc->mode == mode ? 0 : EINVAL); | ||||
mask = (mask << 1) | 1; | |||||
flags = pin_flags[i]; | |||||
if (flags & GPIO_PIN_INPUT) { | |||||
set &= ~(1 << i); | |||||
} else if (flags & GPIO_PIN_OUTPUT) { | |||||
set |= (1 << i); | |||||
} | |||||
} | } | ||||
rkisrc->mode = mode; | |||||
RK_GPIO_LOCK(sc); | switch (mode & GPIO_INTR_MASK) { | ||||
reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); | case GPIO_INTR_EDGE_RISING: | ||||
reg &= ~mask; | rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); | ||||
reg |= set; | rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1); | ||||
RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DDR, reg); | rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 1); | ||||
RK_GPIO_UNLOCK(sc); | break; | ||||
case GPIO_INTR_EDGE_FALLING: | |||||
rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 0); | |||||
break; | |||||
case GPIO_INTR_EDGE_BOTH: | |||||
rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1); | |||||
if (sc->version == RK_GPIO_TYPE_V1) { | |||||
if (rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin)) | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, | |||||
pin, 0); | |||||
else | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, | |||||
pin, 1); | |||||
} else | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_BOTH, pin, 1); | |||||
break; | |||||
case GPIO_INTR_LEVEL_HIGH: | |||||
rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 1); | |||||
break; | |||||
case GPIO_INTR_LEVEL_LOW: | |||||
rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 0); | |||||
break; | |||||
default: | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTMASK, pin, 1); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTEN, pin, 0); | |||||
return (EINVAL); | |||||
} | |||||
rk_gpio_write_bit(sc, RK_GPIO_DEBOUNCE, pin, 1); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTMASK, pin, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTEN, pin, 1); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
rk_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, | rk_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, | ||||
pcell_t *gpios, uint32_t *pin, uint32_t *flags) | struct resource *res, struct intr_map_data *data) | ||||
{ | { | ||||
struct rk_gpio_softc *sc = device_get_softc(dev); | |||||
struct rk_pin_irqsrc *irqsrc; | |||||
/* The gpios are mapped as <pin flags> */ | irqsrc = (struct rk_pin_irqsrc *)isrc; | ||||
*pin = gpios[0]; | |||||
*flags = gpios[1]; | if (isrc->isrc_handlers == 0) { | ||||
irqsrc->mode = GPIO_INTR_CONFORM; | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTEN, irqsrc->irq, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_INTMASK, irqsrc->irq, 0); | |||||
rk_gpio_write_bit(sc, RK_GPIO_DEBOUNCE, irqsrc->irq, 0); | |||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
static phandle_t | |||||
rk_gpio_get_node(device_t bus, device_t dev) | |||||
{ | |||||
/* We only have one child, the GPIO bus, which needs our own node. */ | |||||
return (ofw_bus_get_node(bus)); | |||||
} | |||||
static device_method_t rk_gpio_methods[] = { | static device_method_t rk_gpio_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, rk_gpio_probe), | DEVMETHOD(device_probe, rk_gpio_probe), | ||||
Context not available. | |||||
DEVMETHOD(gpio_get_bus, rk_gpio_get_bus), | DEVMETHOD(gpio_get_bus, rk_gpio_get_bus), | ||||
DEVMETHOD(gpio_pin_max, rk_gpio_pin_max), | DEVMETHOD(gpio_pin_max, rk_gpio_pin_max), | ||||
DEVMETHOD(gpio_pin_getname, rk_gpio_pin_getname), | DEVMETHOD(gpio_pin_getname, rk_gpio_pin_getname), | ||||
DEVMETHOD(gpio_pin_getcaps, rk_gpio_pin_getcaps), | |||||
DEVMETHOD(gpio_pin_getflags, rk_gpio_pin_getflags), | DEVMETHOD(gpio_pin_getflags, rk_gpio_pin_getflags), | ||||
DEVMETHOD(gpio_pin_getcaps, rk_gpio_pin_getcaps), | |||||
DEVMETHOD(gpio_pin_setflags, rk_gpio_pin_setflags), | DEVMETHOD(gpio_pin_setflags, rk_gpio_pin_setflags), | ||||
DEVMETHOD(gpio_pin_get, rk_gpio_pin_get), | DEVMETHOD(gpio_pin_get, rk_gpio_pin_get), | ||||
DEVMETHOD(gpio_pin_set, rk_gpio_pin_set), | DEVMETHOD(gpio_pin_set, rk_gpio_pin_set), | ||||
DEVMETHOD(gpio_pin_toggle, rk_gpio_pin_toggle), | DEVMETHOD(gpio_pin_toggle, rk_gpio_pin_toggle), | ||||
DEVMETHOD(gpio_pin_access_32, rk_gpio_pin_access_32), | |||||
DEVMETHOD(gpio_pin_config_32, rk_gpio_pin_config_32), | |||||
DEVMETHOD(gpio_map_gpios, rk_gpio_map_gpios), | DEVMETHOD(gpio_map_gpios, rk_gpio_map_gpios), | ||||
/* ofw_bus interface */ | /* Interrupt controller interface */ | ||||
DEVMETHOD(pic_map_intr, rk_pic_map_intr), | |||||
DEVMETHOD(pic_setup_intr, rk_pic_setup_intr), | |||||
DEVMETHOD(pic_teardown_intr, rk_pic_teardown_intr), | |||||
/* OFW bus interface */ | |||||
DEVMETHOD(ofw_bus_get_node, rk_gpio_get_node), | DEVMETHOD(ofw_bus_get_node, rk_gpio_get_node), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
Context not available. |
Please do not move code without reason, makes reviewing harder.