From f8f1a91cf70fd45f3b2db46b08eb463d68e0b885 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Fri, 4 Oct 2019 13:56:59 -0700 Subject: [PATCH] drivers: gpio_dw: update to use new GPIO API Update driver code and board files to use new GPIO configuration flags such as GPIO_ACTIVE_LOW. Also add implementation of new port_* driver API as well as gpio_pin_interrupt_configure function. The interrupt triggering on both edges is a remnant from the old Quark SE which has a customized DesignWare GPIO block. So remove the support for both edges as the board is no longer supported. Signed-off-by: Daniel Leung --- drivers/gpio/gpio_dw.c | 330 +++++++++++++++++++++++++------ drivers/gpio/gpio_dw_registers.h | 1 - 2 files changed, 270 insertions(+), 61 deletions(-) diff --git a/drivers/gpio/gpio_dw.c b/drivers/gpio/gpio_dw.c index 1eec2b2398b..3e31e9eda5c 100644 --- a/drivers/gpio/gpio_dw.c +++ b/drivers/gpio/gpio_dw.c @@ -30,6 +30,9 @@ #include #endif +static int gpio_dw_port_set_bits_raw(struct device *port, u32_t mask); +static int gpio_dw_port_clear_bits_raw(struct device *port, u32_t mask); + /* * ARC architecture configure IP through IO auxiliary registers. * Other architectures as ARM and x86 configure IP through MMIO registers @@ -113,61 +116,157 @@ static inline void gpio_dw_clock_off(struct device *port) #define gpio_dw_clock_off(...) #endif -static inline void dw_set_both_edges(u32_t base_addr, u32_t pin) -{ - dw_set_bit(base_addr, INT_BOTHEDGE, pin, 1); -} static inline int dw_base_to_block_base(u32_t base_addr) { return (base_addr & 0xFFFFFFC0); } + static inline int dw_derive_port_from_base(u32_t base_addr) { u32_t port = (base_addr & 0x3f) / 12U; return port; } + static inline int dw_interrupt_support(const struct gpio_dw_config *config) { - return ((int)(config->irq_num) > 0); + return ((int)(config->irq_num) > 0U); } -static inline void dw_interrupt_config(struct device *port, int access_op, - u32_t pin, int flags) +static inline u32_t dw_get_ext_port(u32_t base_addr) +{ + u32_t ext_port; + + /* 4-port GPIO implementation translates from base address to port */ + switch (dw_derive_port_from_base(base_addr)) { + case 1: + ext_port = EXT_PORTB; + break; + case 2: + ext_port = EXT_PORTC; + break; + case 3: + ext_port = EXT_PORTD; + break; + case 0: + default: + ext_port = EXT_PORTA; + break; + } + + return ext_port; +} + +static inline u32_t dw_get_data_port(u32_t base_addr) +{ + u32_t dr_port; + + /* 4-port GPIO implementation translates from base address to port */ + switch (dw_derive_port_from_base(base_addr)) { + case 1: + dr_port = SWPORTB_DR; + break; + case 2: + dr_port = SWPORTC_DR; + break; + case 3: + dr_port = SWPORTD_DR; + break; + case 0: + default: + dr_port = SWPORTA_DR; + break; + } + + return dr_port; +} + +static inline u32_t dw_get_dir_port(u32_t base_addr) +{ + u32_t ddr_port; + + /* 4-port GPIO implementation translates from base address to port */ + switch (dw_derive_port_from_base(base_addr)) { + case 1: + ddr_port = SWPORTB_DDR; + break; + case 2: + ddr_port = SWPORTC_DDR; + break; + case 3: + ddr_port = SWPORTD_DDR; + break; + case 0: + default: + ddr_port = SWPORTA_DDR; + break; + } + + return ddr_port; +} + +static int gpio_dw_pin_interrupt_configure(struct device *port, + unsigned int pin, enum gpio_int_mode mode, + enum gpio_int_trig trig) { struct gpio_dw_runtime *context = port->driver_data; const struct gpio_dw_config *config = port->config->config_info; u32_t base_addr = dw_base_to_block_base(context->base_addr); - bool flag_is_set; + u32_t port_base_addr = context->base_addr; + u32_t dir_port = dw_get_dir_port(port_base_addr); + u32_t data_port = dw_get_data_port(port_base_addr); + u32_t dir_reg; - ARG_UNUSED(access_op); + /* Check for invalid pin number */ + if (pin >= config->bits) { + return -EINVAL; + } - /* set as an input pin */ - dw_set_bit(context->base_addr, SWPORTA_DDR, pin, 0); + /* Only PORT-A supports interrupts */ + if (data_port != SWPORTA_DR) { + return -ENOTSUP; + } - if (dw_interrupt_support(config)) { - - /* level or edge */ - flag_is_set = (flags & GPIO_INT_EDGE); - dw_set_bit(base_addr, INTTYPE_LEVEL, pin, flag_is_set); - - /* Active low/high */ - flag_is_set = (flags & GPIO_INT_ACTIVE_HIGH); - dw_set_bit(base_addr, INT_POLARITY, pin, flag_is_set); - - /* both edges */ - flag_is_set = (flags & GPIO_INT_DOUBLE_EDGE); - if (flag_is_set) { - dw_set_both_edges(base_addr, pin); - dw_set_bit(base_addr, INTTYPE_LEVEL, pin, flag_is_set); + if (mode != GPIO_INT_MODE_DISABLED) { + /* Check if GPIO port supports interrupts */ + if (!dw_interrupt_support(config)) { + return -ENOTSUP; } - /* use built-in debounce */ - flag_is_set = (flags & GPIO_INT_DEBOUNCE); - dw_set_bit(base_addr, PORTA_DEBOUNCE, pin, flag_is_set); + /* Interrupt to be enabled but pin is not set to input */ + dir_reg = dw_read(port_base_addr, dir_port) & BIT(pin); + if (dir_reg != 0U) { + return -EINVAL; + } + } + + /* Does not support both edges */ + if ((mode == GPIO_INT_MODE_EDGE) && + (trig == GPIO_INT_TRIG_BOTH)) { + return -ENOTSUP; + } + + /* Clear interrupt enable */ + dw_set_bit(base_addr, INTEN, pin, false); + + /* Mask and clear interrupt */ + dw_set_bit(base_addr, INTMASK, pin, true); + dw_write(base_addr, PORTA_EOI, BIT(pin)); + + if (mode != GPIO_INT_MODE_DISABLED) { + /* level (0) or edge (1) */ + dw_set_bit(base_addr, INTTYPE_LEVEL, pin, + (mode == GPIO_INT_MODE_EDGE)); + + /* Active low/high */ + dw_set_bit(base_addr, INT_POLARITY, pin, + (trig == GPIO_INT_TRIG_HIGH)); /* Finally enabling interrupt */ - dw_set_bit(base_addr, INTEN, pin, 1); + dw_set_bit(base_addr, INTEN, pin, true); + dw_set_bit(base_addr, INTMASK, pin, false); } + + return 0; } static inline void dw_pin_config(struct device *port, @@ -177,18 +276,28 @@ static inline void dw_pin_config(struct device *port, const struct gpio_dw_config *config = port->config->config_info; u32_t base_addr = dw_base_to_block_base(context->base_addr); u32_t port_base_addr = context->base_addr; - int interrupt_support = dw_interrupt_support(config); + u32_t dir_port = dw_get_dir_port(port_base_addr); + bool pin_is_output, need_debounce; - if (interrupt_support) { - /* clear interrupt enable */ - dw_set_bit(base_addr, INTEN, pin, 0); + /* Set init value then direction */ + pin_is_output = (flags & GPIO_OUTPUT) != 0U; + if (pin_is_output) { + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) { + gpio_dw_port_set_bits_raw(port, BIT(pin)); + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) { + gpio_dw_port_clear_bits_raw(port, BIT(pin)); + } } - /* set direction */ - dw_set_bit(port_base_addr, SWPORTA_DDR, pin, (flags & GPIO_DIR_MASK)); + dw_set_bit(port_base_addr, dir_port, pin, pin_is_output); - if (interrupt_support && (flags & GPIO_INT)) { - dw_interrupt_config(port, GPIO_ACCESS_BY_PIN, pin, flags); + /* Use built-in debounce. + * Note debounce circuit is only available if also supporting + * interrupts according to datasheet. + */ + if (dw_interrupt_support(config) && (dir_port == SWPORTA_DDR)) { + need_debounce = (flags & GPIO_INT_DEBOUNCE); + dw_set_bit(base_addr, PORTA_DEBOUNCE, pin, need_debounce); } } @@ -205,15 +314,39 @@ static inline void dw_port_config(struct device *port, int flags) static inline int gpio_dw_config(struct device *port, int access_op, u32_t pin, int flags) { - if ((flags & GPIO_INT) && (flags & GPIO_DIR_OUT)) { + const struct gpio_dw_config *config = port->config->config_info; + u32_t io_flags; + + /* Check for invalid pin number */ + if (pin >= config->bits) { return -EINVAL; } + /* Does not support disconnected pin, and + * not supporting both input/output at same time. + */ + io_flags = flags & (GPIO_INPUT | GPIO_OUTPUT); + if ((io_flags == GPIO_DISCONNECTED) + || (io_flags == (GPIO_INPUT | GPIO_OUTPUT))) { + return -ENOTSUP; + } + + /* No open-drain support */ + if ((flags & GPIO_SINGLE_ENDED) != 0U) { + return -ENOTSUP; + } + + /* Does not support pull-up/pull-down */ + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0U) { + return -ENOTSUP; + } + if (GPIO_ACCESS_BY_PIN == access_op) { dw_pin_config(port, pin, flags); } else { dw_port_config(port, flags); } + return 0; } @@ -221,12 +354,14 @@ static inline int gpio_dw_write(struct device *port, int access_op, u32_t pin, u32_t value) { struct gpio_dw_runtime *context = port->driver_data; - u32_t base_addr = context->base_addr; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t data_port = dw_get_data_port(port_base_addr); if (GPIO_ACCESS_BY_PIN == access_op) { - dw_set_bit(base_addr, SWPORTA_DR, pin, value); + dw_set_bit(base_addr, data_port, pin, value); } else { - dw_write(base_addr, SWPORTA_DR, value); + dw_write(base_addr, data_port, value); } return 0; @@ -236,25 +371,11 @@ static inline int gpio_dw_read(struct device *port, int access_op, u32_t pin, u32_t *value) { struct gpio_dw_runtime *context = port->driver_data; - u32_t base_addr = context->base_addr; - u32_t ext_port = EXT_PORTA; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t ext_port = dw_get_ext_port(port_base_addr); - /* 4-port GPIO implementation translates from base address to port */ - switch (dw_derive_port_from_base(base_addr)) { - case 1: - ext_port = EXT_PORTB; - break; - case 2: - ext_port = EXT_PORTC; - break; - case 3: - ext_port = EXT_PORTD; - break; - case 0: - default: - break; - } - *value = dw_read(dw_base_to_block_base(base_addr), ext_port); + *value = dw_read(base_addr, ext_port); if (GPIO_ACCESS_BY_PIN == access_op) { *value = !!(*value & BIT(pin)); @@ -262,6 +383,79 @@ static inline int gpio_dw_read(struct device *port, int access_op, return 0; } +static int gpio_dw_port_get_raw(struct device *port, u32_t *value) +{ + struct gpio_dw_runtime *context = port->driver_data; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t ext_port = dw_get_ext_port(port_base_addr); + + *value = dw_read(base_addr, ext_port); + + return 0; +} + +static int gpio_dw_port_set_masked_raw(struct device *port, + u32_t mask, u32_t value) +{ + struct gpio_dw_runtime *context = port->driver_data; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t data_port = dw_get_data_port(port_base_addr); + u32_t pins; + + pins = dw_read(base_addr, data_port); + pins = (pins & ~mask) | (mask & value); + dw_write(base_addr, data_port, pins); + + return 0; +} + +static int gpio_dw_port_set_bits_raw(struct device *port, u32_t mask) +{ + struct gpio_dw_runtime *context = port->driver_data; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t data_port = dw_get_data_port(port_base_addr); + u32_t pins; + + pins = dw_read(base_addr, data_port); + pins |= mask; + dw_write(base_addr, data_port, pins); + + return 0; +} + +static int gpio_dw_port_clear_bits_raw(struct device *port, u32_t mask) +{ + struct gpio_dw_runtime *context = port->driver_data; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t data_port = dw_get_data_port(port_base_addr); + u32_t pins; + + pins = dw_read(base_addr, data_port); + pins &= ~mask; + dw_write(base_addr, data_port, pins); + + return 0; +} + +static int gpio_dw_port_toggle_bits(struct device *port, u32_t mask) +{ + struct gpio_dw_runtime *context = port->driver_data; + u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t port_base_addr = context->base_addr; + u32_t data_port = dw_get_data_port(port_base_addr); + u32_t pins; + + pins = dw_read(base_addr, data_port); + pins ^= mask; + dw_write(base_addr, data_port, pins); + + return 0; +} + static inline int gpio_dw_manage_callback(struct device *port, struct gpio_callback *callback, bool set) @@ -277,6 +471,11 @@ static inline int gpio_dw_enable_callback(struct device *port, int access_op, const struct gpio_dw_config *config = port->config->config_info; struct gpio_dw_runtime *context = port->driver_data; u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t data_port = dw_get_data_port(context->base_addr); + + if (data_port != SWPORTA_DR) { + return -ENOTSUP; + } if (GPIO_ACCESS_BY_PIN == access_op) { dw_write(base_addr, PORTA_EOI, BIT(pin)); @@ -295,6 +494,11 @@ static inline int gpio_dw_disable_callback(struct device *port, int access_op, const struct gpio_dw_config *config = port->config->config_info; struct gpio_dw_runtime *context = port->driver_data; u32_t base_addr = dw_base_to_block_base(context->base_addr); + u32_t data_port = dw_get_data_port(context->base_addr); + + if (data_port != SWPORTA_DR) { + return -ENOTSUP; + } if (GPIO_ACCESS_BY_PIN == access_op) { dw_set_bit(base_addr, INTMASK, pin, 1); @@ -394,6 +598,12 @@ static const struct gpio_driver_api api_funcs = { .config = gpio_dw_config, .write = gpio_dw_write, .read = gpio_dw_read, + .port_get_raw = gpio_dw_port_get_raw, + .port_set_masked_raw = gpio_dw_port_set_masked_raw, + .port_set_bits_raw = gpio_dw_port_set_bits_raw, + .port_clear_bits_raw = gpio_dw_port_clear_bits_raw, + .port_toggle_bits = gpio_dw_port_toggle_bits, + .pin_interrupt_configure = gpio_dw_pin_interrupt_configure, .manage_callback = gpio_dw_manage_callback, .enable_callback = gpio_dw_enable_callback, .disable_callback = gpio_dw_disable_callback, diff --git a/drivers/gpio/gpio_dw_registers.h b/drivers/gpio/gpio_dw_registers.h index e47b40d08f7..a3d255e213d 100644 --- a/drivers/gpio/gpio_dw_registers.h +++ b/drivers/gpio/gpio_dw_registers.h @@ -37,7 +37,6 @@ #define EXT_PORTC 0x58 #define EXT_PORTD 0x5c #define INT_CLOCK_SYNC 0x60 /* alias LS_SYNC */ -#define INT_BOTHEDGE 0x68 #define VER_ID_CODE 0x6c #define CONFIG_REG2 0x70 #define CONFIG_REG1 0x74