diff --git a/boards/arm/atsamd20_xpro/atsamd20_xpro.dts b/boards/arm/atsamd20_xpro/atsamd20_xpro.dts index 4464106d1b212..bad6551b02cc2 100644 --- a/boards/arm/atsamd20_xpro/atsamd20_xpro.dts +++ b/boards/arm/atsamd20_xpro/atsamd20_xpro.dts @@ -26,7 +26,7 @@ leds { compatible = "gpio-leds"; yellow_led: led_0 { - gpios = <&porta 14 0>; + gpios = <&porta 14 GPIO_ACTIVE_LOW>; label = "LED0"; }; }; @@ -34,7 +34,7 @@ buttons { compatible = "gpio-keys"; user_button: button_0 { - gpios = <&porta 15 (GPIO_PUD_PULL_UP | GPIO_INT_ACTIVE_LOW)>; + gpios = <&porta 15 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "SW0"; }; }; diff --git a/boards/arm/atsamd21_xpro/atsamd21_xpro.dts b/boards/arm/atsamd21_xpro/atsamd21_xpro.dts index 9fb0cafd3324f..5bc4d0c1f91fe 100644 --- a/boards/arm/atsamd21_xpro/atsamd21_xpro.dts +++ b/boards/arm/atsamd21_xpro/atsamd21_xpro.dts @@ -27,7 +27,7 @@ leds { compatible = "gpio-leds"; led0: led_0 { - gpios = <&portb 30 0>; + gpios = <&portb 30 GPIO_ACTIVE_LOW>; label = "Yellow LED"; }; }; @@ -35,7 +35,7 @@ buttons { compatible = "gpio-keys"; user_button: button_0 { - gpios = <&porta 15 (GPIO_PUD_PULL_UP | GPIO_INT_ACTIVE_LOW)>; + gpios = <&porta 15 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "SW0"; }; }; diff --git a/boards/arm/atsamr21_xpro/atsamr21_xpro.dts b/boards/arm/atsamr21_xpro/atsamr21_xpro.dts index c22037b9546d6..caca52f5d1397 100644 --- a/boards/arm/atsamr21_xpro/atsamr21_xpro.dts +++ b/boards/arm/atsamr21_xpro/atsamr21_xpro.dts @@ -27,7 +27,7 @@ leds { compatible = "gpio-leds"; led0: led_0 { - gpios = <&porta 19 0>; + gpios = <&porta 19 GPIO_ACTIVE_LOW>; label = "Yellow LED"; }; }; @@ -35,7 +35,7 @@ buttons { compatible = "gpio-keys"; user_button: button_0 { - gpios = <&porta 28 (GPIO_PUD_PULL_UP | GPIO_INT_ACTIVE_LOW)>; + gpios = <&porta 28 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "SW0"; }; }; diff --git a/drivers/gpio/gpio_sam0.c b/drivers/gpio/gpio_sam0.c index c15a826f57c97..e8b7361eb3b0f 100644 --- a/drivers/gpio/gpio_sam0.c +++ b/drivers/gpio/gpio_sam0.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2017 Google LLC. + * Copyright (c) 2019 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,6 +13,10 @@ #include "gpio_utils.h" +#ifndef PORT_PMUX_PMUXE_A_Val +#define PORT_PMUX_PMUXE_A_Val (0) +#endif + struct gpio_sam0_config { PortGroup *regs; #ifdef CONFIG_SAM0_EIC @@ -22,6 +27,7 @@ struct gpio_sam0_config { struct gpio_sam0_data { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; + gpio_port_pins_t debounce; #ifdef CONFIG_SAM0_EIC sys_slist_t callbacks; #endif @@ -47,104 +53,53 @@ static int gpio_sam0_config(struct device *dev, int access_op, u32_t pin, { const struct gpio_sam0_config *config = DEV_CFG(dev); PortGroup *regs = config->regs; - u32_t mask = 1 << pin; - bool is_out = (flags & GPIO_DIR_MASK) == GPIO_DIR_OUT; - int pud = flags & GPIO_PUD_MASK; - PORT_PINCFG_Type pincfg; + PORT_PINCFG_Type pincfg = { + .reg = 0, + }; - if (access_op != GPIO_ACCESS_BY_PIN) { + if ((flags & GPIO_SINGLE_ENDED) != 0) { return -ENOTSUP; } - /* Builds the configuration and writes it in one go */ - pincfg.reg = 0; - pincfg.bit.INEN = 1; - - /* Direction */ - if (is_out) { - regs->DIRSET.bit.DIRSET = mask; - } else { - regs->DIRCLR.bit.DIRCLR = mask; - } - - /* Pull up / pull down */ - if (is_out && pud != GPIO_PUD_NORMAL) { - return -ENOTSUP; + /* Supports disconnected, input, output, or bidirectional */ + if ((flags & GPIO_INPUT) != 0) { + pincfg.bit.INEN = 1; } - - switch (pud) { - case GPIO_PUD_NORMAL: - break; - case GPIO_PUD_PULL_UP: - pincfg.bit.PULLEN = 1; - regs->OUTSET.reg = mask; - break; - case GPIO_PUD_PULL_DOWN: - pincfg.bit.PULLEN = 1; - regs->OUTCLR.reg = mask; - break; - default: - return -ENOTSUP; - } - -#ifdef CONFIG_SAM0_EIC - if ((flags & GPIO_INT) != 0) { - /* - * The EIC requires setting the pin to function A, so do - * that as part of the interrupt enable, so that the API - * is consistent. - */ - pincfg.bit.PMUXEN = 1; - if (pin & 1) { - regs->PMUX[pin / 2].bit.PMUXO = PORT_PMUX_PMUXE_A_Val; - } else { - regs->PMUX[pin / 2].bit.PMUXE = PORT_PMUX_PMUXE_A_Val; + if ((flags & GPIO_OUTPUT) != 0) { + /* Output is incompatible with pull */ + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { + return -ENOTSUP; } - - enum sam0_eic_trigger trigger; - - if ((flags & GPIO_INT_DOUBLE_EDGE) != 0) { - trigger = SAM0_EIC_BOTH; - } else if (flags & GPIO_INT_EDGE) { - if (flags & GPIO_INT_ACTIVE_HIGH) { - trigger = SAM0_EIC_RISING; - } else { - trigger = SAM0_EIC_FALLING; - } - } else { - if (flags & GPIO_INT_ACTIVE_HIGH) { - trigger = SAM0_EIC_HIGH; + /* Bidirectional is supported */ + if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { + regs->OUTCLR.reg = BIT(pin); + } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { + regs->OUTSET.reg = BIT(pin); + } + regs->DIRSET.reg = BIT(pin); + } else { + /* Not output, may be input */ + regs->DIRCLR.reg = BIT(pin); + + /* Pull configuration is supported if not output */ + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { + pincfg.bit.PULLEN = 1; + if ((flags & GPIO_PULL_UP) != 0) { + regs->OUTSET.reg = BIT(pin); } else { - trigger = SAM0_EIC_LOW; + regs->OUTCLR.reg = BIT(pin); } } - - int retval = sam0_eic_acquire(config->id, pin, trigger, - (flags & GPIO_INT_DEBOUNCE) != 0, - gpio_sam0_isr, dev); - if (retval != 0) { - return retval; - } - } else { - sam0_eic_release(config->id, pin); - /* - * Pinmux disabled by the config set, so this - * correctly inverts the EIC enable part above. - */ } -#else - if ((flags & GPIO_INT) != 0) { - return -ENOTSUP; - } -#endif + + /* Preserve debounce flag for interrupt configuration. */ + WRITE_BIT(DEV_DATA(dev)->debounce, pin, + ((flags & GPIO_INT_DEBOUNCE) != 0) + && (pincfg.bit.INEN != 0)); /* Write the now-built pin configuration */ regs->PINCFG[pin] = pincfg; - if ((flags & GPIO_POL_MASK) != GPIO_POL_NORMAL) { - return -ENOTSUP; - } - return 0; } @@ -152,17 +107,11 @@ static int gpio_sam0_write(struct device *dev, int access_op, u32_t pin, u32_t value) { const struct gpio_sam0_config *config = DEV_CFG(dev); - u32_t mask = 1 << pin; - - if (access_op != GPIO_ACCESS_BY_PIN) { - /* TODO(mlhx): support GPIO_ACCESS_BY_PORT */ - return -ENOTSUP; - } if (value != 0U) { - config->regs->OUTSET.bit.OUTSET = mask; + config->regs->OUTSET.reg = BIT(pin); } else { - config->regs->OUTCLR.bit.OUTCLR = mask; + config->regs->OUTCLR.reg = BIT(pin); } return 0; @@ -172,22 +121,158 @@ static int gpio_sam0_read(struct device *dev, int access_op, u32_t pin, u32_t *value) { const struct gpio_sam0_config *config = DEV_CFG(dev); - u32_t bits; - if (access_op != GPIO_ACCESS_BY_PIN) { - /* TODO(mlhx): support GPIO_ACCESS_BY_PORT */ - return -ENOTSUP; - } + *value = (config->regs->IN.reg & BIT(pin)) != 0; + + return 0; +} + +static int gpio_sam0_port_get_raw(struct device *dev, + gpio_port_value_t *value) +{ + const struct gpio_sam0_config *config = DEV_CFG(dev); + + *value = config->regs->IN.reg; + + return 0; +} + +static int gpio_sam0_port_set_masked_raw(struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_sam0_config *config = DEV_CFG(dev); + u32_t out = config->regs->OUT.reg; - bits = config->regs->IN.bit.IN; - *value = (bits >> pin) & 1; + config->regs->OUT.reg = (out & ~mask) | (value & mask); + + return 0; +} + +static int gpio_sam0_port_set_bits_raw(struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_sam0_config *config = DEV_CFG(dev); + + config->regs->OUTSET.reg = pins; + + return 0; +} + +static int gpio_sam0_port_clear_bits_raw(struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_sam0_config *config = DEV_CFG(dev); + + config->regs->OUTCLR.reg = pins; + + return 0; +} + +static int gpio_sam0_port_toggle_bits(struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_sam0_config *config = DEV_CFG(dev); + + config->regs->OUTTGL.reg = pins; return 0; } #ifdef CONFIG_SAM0_EIC -int gpio_sam0_manage_callback(struct device *dev, +static int gpio_sam0_pin_interrupt_configure(struct device *dev, + unsigned int pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + const struct gpio_sam0_config *config = DEV_CFG(dev); + PortGroup *regs = config->regs; + PORT_PINCFG_Type pincfg = { + .reg = regs->PINCFG[pin].reg, + }; + enum sam0_eic_trigger trigger; + int rc = 0; + + switch (mode) { + case GPIO_INT_MODE_DISABLED: + pincfg.bit.PMUXEN = 0; + rc = sam0_eic_disable_interrupt(config->id, pin); + if (rc == -EBUSY) { + /* Ignore diagnostic disabling disabled */ + rc = 0; + } + if (rc == 0) { + rc = sam0_eic_release(config->id, pin); + } + break; + case GPIO_INT_MODE_LEVEL: + case GPIO_INT_MODE_EDGE: + /* Enabling interrupts on a pin requires disconnecting + * the pin from the I/O pin controller (PORT) module + * and connecting it to the External Interrupt + * Controller (EIC). This would prevent using the pin + * as an output, so interrupts are only supported if + * the pin is configured as input-only. + */ + if ((pincfg.bit.INEN == 0) + || ((regs->DIR.reg & BIT(pin)) != 0)) { + rc = -ENOTSUP; + break; + } + + /* Transfer control to EIC */ + pincfg.bit.PMUXEN = 1; + if ((pin & 1U) != 0) { + regs->PMUX[pin / 2U].bit.PMUXO = PORT_PMUX_PMUXE_A_Val; + } else { + regs->PMUX[pin / 2U].bit.PMUXE = PORT_PMUX_PMUXE_A_Val; + } + + switch (trig) { + case GPIO_INT_TRIG_LOW: + trigger = (mode == GPIO_INT_MODE_LEVEL) + ? SAM0_EIC_LOW + : SAM0_EIC_FALLING; + break; + case GPIO_INT_TRIG_HIGH: + trigger = (mode == GPIO_INT_MODE_LEVEL) + ? SAM0_EIC_HIGH + : SAM0_EIC_RISING; + break; + case GPIO_INT_TRIG_BOTH: + trigger = SAM0_EIC_BOTH; + break; + default: + rc = -EINVAL; + break; + } + + if (rc == 0) { + rc = sam0_eic_acquire(config->id, pin, trigger, + (DEV_DATA(dev)->debounce & BIT(pin)) != 0, + gpio_sam0_isr, dev); + } + if (rc == 0) { + rc = sam0_eic_enable_interrupt(config->id, pin); + } + + break; + default: + rc = -EINVAL; + break; + } + + if (rc == 0) { + /* Update the pin configuration */ + regs->PINCFG[pin] = pincfg; + } + + return rc; +} + + +static int gpio_sam0_manage_callback(struct device *dev, struct gpio_callback *callback, bool set) { struct gpio_sam0_data *const data = DEV_DATA(dev); @@ -199,10 +284,6 @@ int gpio_sam0_enable_callback(struct device *dev, int access_op, u32_t pin) { const struct gpio_sam0_config *config = DEV_CFG(dev); - if (access_op != GPIO_ACCESS_BY_PIN) { - return -ENOTSUP; - } - return sam0_eic_enable_interrupt(config->id, pin); } @@ -210,14 +291,10 @@ int gpio_sam0_disable_callback(struct device *dev, int access_op, u32_t pin) { const struct gpio_sam0_config *config = DEV_CFG(dev); - if (access_op != GPIO_ACCESS_BY_PIN) { - return -ENOTSUP; - } - return sam0_eic_disable_interrupt(config->id, pin); } -u32_t gpio_sam0_get_pending_int(struct device *dev) +static u32_t gpio_sam0_get_pending_int(struct device *dev) { const struct gpio_sam0_config *config = DEV_CFG(dev); @@ -230,7 +307,13 @@ static const struct gpio_driver_api gpio_sam0_api = { .config = gpio_sam0_config, .write = gpio_sam0_write, .read = gpio_sam0_read, + .port_get_raw = gpio_sam0_port_get_raw, + .port_set_masked_raw = gpio_sam0_port_set_masked_raw, + .port_set_bits_raw = gpio_sam0_port_set_bits_raw, + .port_clear_bits_raw = gpio_sam0_port_clear_bits_raw, + .port_toggle_bits = gpio_sam0_port_toggle_bits, #ifdef CONFIG_SAM0_EIC + .pin_interrupt_configure = gpio_sam0_pin_interrupt_configure, .manage_callback = gpio_sam0_manage_callback, .enable_callback = gpio_sam0_enable_callback, .disable_callback = gpio_sam0_disable_callback, diff --git a/tests/drivers/gpio/gpio_basic_api/boards/atsamd21_xpro.overlay b/tests/drivers/gpio/gpio_basic_api/boards/atsamd21_xpro.overlay new file mode 100644 index 0000000000000..047170d361291 --- /dev/null +++ b/tests/drivers/gpio/gpio_basic_api/boards/atsamd21_xpro.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + resources { + compatible = "test,gpio_basic_api"; + out-gpios = <&portb 6 0>; /* EXT1.5 */ + in-gpios = <&portb 7 0>; /* EXT1.6 */ + }; +};