[mediatek] [gpio] add PPS / interrupt support.

Submitted by:	Hiroki Mori <yamori813@yahoo.co.jp>
Reviewed by:	mizhka
Differential Revision:	https://reviews.freebsd.org/D9784
This commit is contained in:
adrian 2017-05-06 06:22:14 +00:00
parent cfbff08319
commit b1ad5e6543

View File

@ -292,7 +292,8 @@ mtk_gpio_attach(device_t dev)
for (i = 0; i < sc->num_pins; i++) {
sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
GPIO_PIN_INVIN | GPIO_PIN_INVOUT |
GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING;
sc->pins[i].intr_polarity = INTR_POLARITY_HIGH;
sc->pins[i].intr_trigger = INTR_TRIGGER_EDGE;
@ -499,23 +500,79 @@ mtk_gpio_pin_toggle(device_t dev, uint32_t pin)
return (ret);
}
static int
mtk_gpio_pic_map_fdt(struct mtk_gpio_softc *sc,
struct intr_map_data_fdt *daf, u_int *irqp, uint32_t *modep)
{
u_int irq;
if (daf->ncells != 1) {
device_printf(sc->dev, "Invalid #interrupt-cells\n");
return (EINVAL);
}
irq = daf->cells[0];
if (irq >= sc->num_pins) {
device_printf(sc->dev, "Invalid interrupt number %u\n", irq);
return (EINVAL);
}
*irqp = irq;
if (modep != NULL)
*modep = GPIO_INTR_EDGE_BOTH;
return (0);
}
static int
mtk_gpio_pic_map_gpio(struct mtk_gpio_softc *sc,
struct intr_map_data_gpio *dag, u_int *irqp, uint32_t *modep)
{
u_int irq;
irq = dag->gpio_pin_num;
if (irq >= sc->num_pins) {
device_printf(sc->dev, "Invalid interrupt number %u\n", irq);
return (EINVAL);
}
*irqp = irq;
if (modep != NULL)
*modep = dag->gpio_intr_mode;
return (0);
}
static int
mtk_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
struct intr_irqsrc **isrcp)
{
struct intr_map_data_fdt *daf;
int error;
u_int irq;
struct mtk_gpio_softc *sc;
if (data->type != INTR_MAP_DATA_FDT)
return (ENOTSUP);
sc = device_get_softc(dev);
daf = (struct intr_map_data_fdt *)data;
switch (data->type) {
case INTR_MAP_DATA_FDT:
error = (mtk_gpio_pic_map_fdt(sc,
(struct intr_map_data_fdt *)data, &irq, NULL));
break;
case INTR_MAP_DATA_GPIO:
error = (mtk_gpio_pic_map_gpio(sc,
(struct intr_map_data_gpio *)data, &irq, NULL));
break;
default:
error = EINVAL;
break;
}
if (daf->ncells != 1 || daf->cells[0] >= sc->num_pins)
return (EINVAL);
if (error != 0) {
device_printf(dev, "Invalid map type\n");
return (error);
}
*isrcp = PIC_INTR_ISRC(sc, daf->cells[0]);
*isrcp = PIC_INTR_ISRC(sc, irq);
return (0);
}
@ -599,6 +656,51 @@ mtk_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
MTK_GPIO_UNLOCK(sc);
}
static int
mtk_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
struct resource *res, struct intr_map_data *data)
{
struct mtk_gpio_softc *sc;
uint32_t val;
int error;
uint32_t mode;
u_int irq;
if (data == NULL)
return (ENOTSUP);
sc = device_get_softc(dev);
switch (data->type) {
case INTR_MAP_DATA_FDT:
error = mtk_gpio_pic_map_fdt(sc,
(struct intr_map_data_fdt *)data, &irq, &mode);
break;
case INTR_MAP_DATA_GPIO:
error = mtk_gpio_pic_map_gpio(sc,
(struct intr_map_data_gpio *)data, &irq, &mode);
break;
default:
error = ENOTSUP;
break;
}
if (error != 0)
return (error);
MTK_GPIO_LOCK(sc);
if (mode == GPIO_INTR_EDGE_BOTH || mode == GPIO_INTR_EDGE_RISING) {
val = MTK_READ_4(sc, GPIO_PIORENA) | (1u << irq);
MTK_WRITE_4(sc, GPIO_PIORENA, val);
}
if (mode == GPIO_INTR_EDGE_BOTH || mode == GPIO_INTR_EDGE_FALLING) {
val = MTK_READ_4(sc, GPIO_PIOFENA) | (1u << irq);
MTK_WRITE_4(sc, GPIO_PIOFENA, val);
}
MTK_GPIO_UNLOCK(sc);
return (0);
}
static int
mtk_gpio_intr(void *arg)
{
@ -607,6 +709,7 @@ mtk_gpio_intr(void *arg)
sc = arg;
interrupts = MTK_READ_4(sc, GPIO_PIOINT);
MTK_WRITE_4(sc, GPIO_PIOINT, interrupts);
for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
if ((interrupts & 0x1) == 0)
@ -649,6 +752,7 @@ static device_method_t mtk_gpio_methods[] = {
DEVMETHOD(pic_disable_intr, mtk_gpio_pic_disable_intr),
DEVMETHOD(pic_enable_intr, mtk_gpio_pic_enable_intr),
DEVMETHOD(pic_map_intr, mtk_gpio_pic_map_intr),
DEVMETHOD(pic_setup_intr, mtk_gpio_pic_setup_intr),
DEVMETHOD(pic_post_filter, mtk_gpio_pic_post_filter),
DEVMETHOD(pic_post_ithread, mtk_gpio_pic_post_ithread),
DEVMETHOD(pic_pre_ithread, mtk_gpio_pic_pre_ithread),