diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9fc512f6280..07a69f1a583 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -710,6 +710,11 @@ config GPIO_K64 help Enable driver for Freescale K64-based GPIOs. +config PORT_K64_INT_STATUS_OFFSET + hex "Freescale K64-based Port Control interrupt status register offset" + depends on GPIO_K64 + default 0xA0 + config GPIO_K64_A bool "Freescale K64-based GPIO Port A" depends on GPIO_K64 @@ -732,6 +737,20 @@ config PORT_K64_A_BASE_ADDR hex # Freescale K64-based Port Control Port A base address depends on GPIO_K64_A +config GPIO_K64_PORTA_IRQ + int "Freescale K64-based Port A interrupt number" + depends on GPIO_K64_A + default 59 + help + K64 Port A IRQ number for the interrupt controller + +config GPIO_K64_PORTA_PRI + int "Freescale K64-based Port A interrupt priority" + depends on GPIO_K64_A + default 2 + help + K64 Port A IRQ priority + config GPIO_K64_B bool "Freescale K64-based GPIO Port B" depends on GPIO_K64 @@ -754,6 +773,20 @@ config PORT_K64_B_BASE_ADDR hex # Freescale K64-based Port Control Port B base address depends on GPIO_K64_B +config GPIO_K64_PORTB_IRQ + int "Freescale K64-based Port B interrupt number" + depends on GPIO_K64_B + default 60 + help + K64 Port B IRQ number for the interrupt controller + +config GPIO_K64_PORTB_PRI + int "Freescale K64-based Port B interrupt priority" + depends on GPIO_K64_B + default 2 + help + K64 Port B IRQ priority + config GPIO_K64_C bool "Freescale K64-based GPIO Port B" depends on GPIO_K64 @@ -776,6 +809,20 @@ config PORT_K64_C_BASE_ADDR hex # Freescale K64-based Port Control Port C base address depends on GPIO_K64_C +config GPIO_K64_PORTC_IRQ + int "Freescale K64-based Port C interrupt number" + depends on GPIO_K64_C + default 61 + help + K64 Port C IRQ number for the interrupt controller + +config GPIO_K64_PORTC_PRI + int "Freescale K64-based Port C interrupt priority" + depends on GPIO_K64_C + default 2 + help + K64 Port C IRQ priority + config GPIO_K64_D bool "Freescale K64-based GPIO Port D" depends on GPIO_K64 @@ -798,6 +845,20 @@ config PORT_K64_D_BASE_ADDR hex # Freescale K64-based Port Control Port D base address depends on GPIO_K64_D +config GPIO_K64_PORTD_IRQ + int "Freescale K64-based Port D interrupt number" + depends on GPIO_K64_D + default 62 + help + K64 Port D IRQ number for the interrupt controller + +config GPIO_K64_PORTD_PRI + int "Freescale K64-based Port D interrupt priority" + depends on GPIO_K64_D + default 2 + help + K64 Port D IRQ priority + config GPIO_K64_E bool "Freescale K64-based GPIO Port E" depends on GPIO_K64 @@ -820,6 +881,20 @@ config PORT_K64_E_BASE_ADDR hex # Freescale K64-based Port Control Port E base address depends on GPIO_K64_E +config GPIO_K64_PORTE_IRQ + int "Freescale K64-based Port E interrupt number" + depends on GPIO_K64_E + default 63 + help + K64 Port E IRQ number for the interrupt controller + +config GPIO_K64_PORTE_PRI + int "Freescale K64-based Port E interrupt priority" + depends on GPIO_K64_E + default 2 + help + K64 Port E IRQ priority + source "drivers/gpio/Kconfig.atmel_sam3" endif # GPIO diff --git a/drivers/gpio/gpio_k64.c b/drivers/gpio/gpio_k64.c index 70bfcc910e2..2d6a355a32d 100644 --- a/drivers/gpio/gpio_k64.c +++ b/drivers/gpio/gpio_k64.c @@ -39,8 +39,8 @@ static int gpio_k64_config(struct device *dev, int access_op, /* check for an invalid pin configuration */ - if (flags & GPIO_INT) { - /* interrupts not supported */ + if (((flags & GPIO_INT) && (flags & GPIO_DIR_OUT)) || + ((flags & GPIO_DIR_IN) && (flags & GPIO_DIR_OUT))) { return DEV_INVALID_OP; } @@ -82,7 +82,36 @@ static int gpio_k64_config(struct device *dev, int access_op, return DEV_INVALID_OP; } - /* write pull-up/-down configuration settings */ + /* + * Set up interrupt configuration, in Port Control module: + */ + + if (flags & GPIO_INT) { + + /* edge or level */ + + if (flags & GPIO_INT_EDGE) { + + if (flags & GPIO_INT_ACTIVE_HIGH) { + setting |= K64_PINMUX_INT_RISING; + } else if (flags & GPIO_INT_DOUBLE_EDGE) { + setting |= K64_PINMUX_INT_BOTH_EDGE; + } else { + setting |= K64_PINMUX_INT_FALLING; + } + + } else { /* GPIO_INT_LEVEL */ + + if (flags & GPIO_INT_ACTIVE_HIGH) { + setting |= K64_PINMUX_INT_HIGH; + } else { + setting |= K64_PINMUX_INT_LOW; + } + + } + } + + /* write pull-up/-down and, if set, interrupt configuration settings */ if (access_op == GPIO_ACCESS_BY_PIN) { @@ -92,6 +121,10 @@ static int gpio_k64_config(struct device *dev, int access_op, value &= ~(K64_PINMUX_PULL_EN_MASK | K64_PINMUX_PULL_SEL_MASK); + if (flags & GPIO_INT) { + value &= ~K64_PINMUX_INT_MASK; + } + value |= setting; sys_write32(value, @@ -108,6 +141,10 @@ static int gpio_k64_config(struct device *dev, int access_op, value &= ~(K64_PINMUX_PULL_EN_MASK | K64_PINMUX_PULL_SEL_MASK); + if (flags & GPIO_INT) { + value &= ~K64_PINMUX_INT_MASK; + } + value |= setting; sys_write32(value, @@ -164,32 +201,44 @@ static int gpio_k64_read(struct device *dev, int access_op, static int gpio_k64_set_callback(struct device *dev, gpio_callback_t callback) { - ARG_UNUSED(dev); - ARG_UNUSED(callback); + struct gpio_k64_data *data = dev->driver_data; - return DEV_INVALID_OP; + data->callback_func = callback; + + return DEV_OK; } + static int gpio_k64_enable_callback(struct device *dev, int access_op, uint32_t pin) { - ARG_UNUSED(dev); - ARG_UNUSED(access_op); - ARG_UNUSED(pin); + struct gpio_k64_data *data = dev->driver_data; - return DEV_INVALID_OP; + if (access_op == GPIO_ACCESS_BY_PIN) { + data->pin_callback_enables |= (1 << pin); + } else { + data->port_callback_enable = 1; + } + + return DEV_OK; } + static int gpio_k64_disable_callback(struct device *dev, int access_op, uint32_t pin) { - ARG_UNUSED(dev); - ARG_UNUSED(access_op); - ARG_UNUSED(pin); + struct gpio_k64_data *data = dev->driver_data; - return DEV_INVALID_OP; + if (access_op == GPIO_ACCESS_BY_PIN) { + data->pin_callback_enables &= ~(1 << pin); + } else { + data->port_callback_enable = 0; + } + + return DEV_OK; } + static int gpio_k64_suspend_port(struct device *dev) { ARG_UNUSED(dev); @@ -197,6 +246,7 @@ static int gpio_k64_suspend_port(struct device *dev) return DEV_INVALID_OP; } + static int gpio_k64_resume_port(struct device *dev) { ARG_UNUSED(dev); @@ -204,6 +254,61 @@ static int gpio_k64_resume_port(struct device *dev) return DEV_INVALID_OP; } + +/** + * @brief Handler for port interrupts + * @param dev Pointer to device structure for driver instance + * + * @return N/A + */ +static void gpio_k64_port_isr(void *dev) +{ + struct device *port = (struct device *)dev; + struct gpio_k64_data *data = port->driver_data; + struct gpio_k64_config *config = port->config->config_info; + mem_addr_t int_status_reg_addr; + uint32_t enabled_int, int_status, pin; + + if (!data->callback_func) { + return; + } + + int_status_reg_addr = config->port_base_addr + + CONFIG_PORT_K64_INT_STATUS_OFFSET; + + int_status = sys_read32(int_status_reg_addr); + + if (data->port_callback_enable) { + + data->callback_func(port, int_status); + + } else if (data->pin_callback_enables) { + + /* perform callback for each callback-enabled pin with an interrupt */ + + enabled_int = int_status & data->pin_callback_enables; + + while ((pin = find_lsb_set(enabled_int))) { + + pin--; /* normalize the pin number */ + + data->callback_func(port, (1 << pin)); + + /* clear the interrupt status */ + + enabled_int &= ~(1 << pin); + + } + + } + + /* clear the port interrupts */ + + sys_write32(0xFFFFFFFF, int_status_reg_addr); + +} + + static struct gpio_driver_api gpio_k64_drv_api_funcs = { .config = gpio_k64_config, .write = gpio_k64_write, @@ -232,69 +337,139 @@ int gpio_k64_init(struct device *dev) /* Initialization for Port A */ #ifdef CONFIG_GPIO_K64_A +static int gpio_k64_A_init(struct device *dev); + static struct gpio_k64_config gpio_k64_A_cfg = { .gpio_base_addr = CONFIG_GPIO_K64_A_BASE_ADDR, .port_base_addr = CONFIG_PORT_K64_A_BASE_ADDR, }; -DEVICE_INIT(gpio_k64_A, CONFIG_GPIO_K64_A_DEV_NAME, gpio_k64_init, - NULL, &gpio_k64_A_cfg, +static struct gpio_k64_data gpio_data_A; + +DEVICE_INIT(gpio_k64_A, CONFIG_GPIO_K64_A_DEV_NAME, gpio_k64_A_init, + &gpio_data_A, &gpio_k64_A_cfg, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +static int gpio_k64_A_init(struct device *dev) +{ + IRQ_CONNECT(CONFIG_GPIO_K64_PORTA_IRQ, CONFIG_GPIO_K64_PORTA_PRI, + gpio_k64_port_isr, DEVICE_GET(gpio_k64_A), 0); + + irq_enable(CONFIG_GPIO_K64_PORTA_IRQ); + + return gpio_k64_init(dev); +} + #endif /* CONFIG_GPIO_K64_A */ /* Initialization for Port B */ #ifdef CONFIG_GPIO_K64_B +static int gpio_k64_B_init(struct device *dev); + static struct gpio_k64_config gpio_k64_B_cfg = { .gpio_base_addr = CONFIG_GPIO_K64_B_BASE_ADDR, .port_base_addr = CONFIG_PORT_K64_B_BASE_ADDR, }; -DEVICE_INIT(gpio_k64_B, CONFIG_GPIO_K64_B_DEV_NAME, gpio_k64_init, - NULL, &gpio_k64_B_cfg, +static struct gpio_k64_data gpio_data_B; + +DEVICE_INIT(gpio_k64_B, CONFIG_GPIO_K64_B_DEV_NAME, gpio_k64_B_init, + &gpio_data_B, &gpio_k64_B_cfg, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +static int gpio_k64_B_init(struct device *dev) +{ + IRQ_CONNECT(CONFIG_GPIO_K64_PORTB_IRQ, CONFIG_GPIO_K64_PORTB_PRI, + gpio_k64_port_isr, DEVICE_GET(gpio_k64_B), 0); + + irq_enable(CONFIG_GPIO_K64_PORTB_IRQ); + + return gpio_k64_init(dev); +} + #endif /* CONFIG_GPIO_K64_B */ /* Initialization for Port C */ #ifdef CONFIG_GPIO_K64_C +static int gpio_k64_C_init(struct device *dev); + static struct gpio_k64_config gpio_k64_C_cfg = { .gpio_base_addr = CONFIG_GPIO_K64_C_BASE_ADDR, .port_base_addr = CONFIG_PORT_K64_C_BASE_ADDR, }; -DEVICE_INIT(gpio_k64_C, CONFIG_GPIO_K64_C_DEV_NAME, gpio_k64_init, - NULL, &gpio_k64_C_cfg, +static struct gpio_k64_data gpio_data_C; + +DEVICE_INIT(gpio_k64_C, CONFIG_GPIO_K64_C_DEV_NAME, gpio_k64_C_init, + &gpio_data_C, &gpio_k64_C_cfg, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +static int gpio_k64_C_init(struct device *dev) +{ + IRQ_CONNECT(CONFIG_GPIO_K64_PORTC_IRQ, CONFIG_GPIO_K64_PORTC_PRI, + gpio_k64_port_isr, DEVICE_GET(gpio_k64_C), 0); + + irq_enable(CONFIG_GPIO_K64_PORTC_IRQ); + + return gpio_k64_init(dev); +} + #endif /* CONFIG_GPIO_K64_C */ /* Initialization for Port D */ #ifdef CONFIG_GPIO_K64_D +static int gpio_k64_D_init(struct device *dev); + static struct gpio_k64_config gpio_k64_D_cfg = { .gpio_base_addr = CONFIG_GPIO_K64_D_BASE_ADDR, .port_base_addr = CONFIG_PORT_K64_D_BASE_ADDR, }; -DEVICE_INIT(gpio_k64_D, CONFIG_GPIO_K64_D_DEV_NAME, gpio_k64_init, - NULL, &gpio_k64_D_cfg, +static struct gpio_k64_data gpio_data_D; + +DEVICE_INIT(gpio_k64_D, CONFIG_GPIO_K64_D_DEV_NAME, gpio_k64_D_init, + &gpio_data_D, &gpio_k64_D_cfg, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +static int gpio_k64_D_init(struct device *dev) +{ + IRQ_CONNECT(CONFIG_GPIO_K64_PORTD_IRQ, CONFIG_GPIO_K64_PORTD_PRI, + gpio_k64_port_isr, DEVICE_GET(gpio_k64_D), 0); + + irq_enable(CONFIG_GPIO_K64_PORTD_IRQ); + + return gpio_k64_init(dev); +} + #endif /* CONFIG_GPIO_K64_D */ /* Initialization for Port E */ #ifdef CONFIG_GPIO_K64_E +static int gpio_k64_E_init(struct device *dev); + static struct gpio_k64_config gpio_k64_E_cfg = { .gpio_base_addr = CONFIG_GPIO_K64_E_BASE_ADDR, .port_base_addr = CONFIG_PORT_K64_E_BASE_ADDR, }; -DEVICE_INIT(gpio_k64_E, CONFIG_GPIO_K64_E_DEV_NAME, gpio_k64_init, - NULL, &gpio_k64_E_cfg, +static struct gpio_k64_data gpio_data_E; + +DEVICE_INIT(gpio_k64_E, CONFIG_GPIO_K64_E_DEV_NAME, gpio_k64_E_init, + &gpio_data_E, &gpio_k64_E_cfg, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +static int gpio_k64_E_init(struct device *dev) +{ + IRQ_CONNECT(CONFIG_GPIO_K64_PORTE_IRQ, CONFIG_GPIO_K64_PORTE_PRI, + gpio_k64_port_isr, DEVICE_GET(gpio_k64_E), 0); + + irq_enable(CONFIG_GPIO_K64_PORTE_IRQ); + + return gpio_k64_init(dev); +} + #endif /* CONFIG_GPIO_K64_E */ diff --git a/drivers/gpio/gpio_k64.h b/drivers/gpio/gpio_k64.h index 2adea1b2b6c..385adc15749 100644 --- a/drivers/gpio/gpio_k64.h +++ b/drivers/gpio/gpio_k64.h @@ -43,4 +43,12 @@ struct gpio_k64_config { uint32_t port_base_addr; }; +struct gpio_k64_data { + /* port ISR callback routine address */ + gpio_callback_t callback_func; + /* pin callback routine enable flags, by pin number */ + uint32_t pin_callback_enables; + /* port callback routine enable flag */ + uint8_t port_callback_enable; +}; #endif /* _GPIO_K64_H_ */ diff --git a/drivers/pinmux/pinmux_k64.c b/drivers/pinmux/pinmux_k64.c index 25936571090..0033e703aef 100644 --- a/drivers/pinmux/pinmux_k64.c +++ b/drivers/pinmux/pinmux_k64.c @@ -166,8 +166,7 @@ static uint32_t _fsl_k64_set_pin(struct device *dev, bool is_gpio = false; int gpio_setting; - if ((pin_id >= CONFIG_PINMUX_NUM_PINS) || - (func & K64_PINMUX_INT_MASK)) { /* interrupts not supported */ + if (pin_id >= CONFIG_PINMUX_NUM_PINS) { return DEV_INVALID_OP; } @@ -360,4 +359,4 @@ struct fsl_k64_data fsl_k64_pinmux_driver = { /* must be initialized after GPIO */ DEVICE_INIT(pmux, PINMUX_NAME, &pinmux_fsl_k64_initialize, &fsl_k64_pinmux_driver, &fsl_k64_pmux, - PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); + SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);