drivers: gpio_sam0: update to new GPIO API
Drop the port access, rework to separate interrupt and pin configuration, add new API. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
parent
9aa1dc11ce
commit
746f7e37e7
2 changed files with 200 additions and 108 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Google LLC.
|
||||
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -26,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
|
||||
|
@ -51,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;
|
||||
/* Supports disconnected, input, output, or bidirectional */
|
||||
if ((flags & GPIO_INPUT) != 0) {
|
||||
pincfg.bit.INEN = 1;
|
||||
}
|
||||
|
||||
/* Pull up / pull down */
|
||||
if (is_out && pud != GPIO_PUD_NORMAL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
/* 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_FALLING;
|
||||
}
|
||||
} else {
|
||||
if (flags & GPIO_INT_ACTIVE_HIGH) {
|
||||
trigger = SAM0_EIC_HIGH;
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
@ -156,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;
|
||||
|
@ -176,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;
|
||||
|
||||
bits = config->regs->IN.bit.IN;
|
||||
*value = (bits >> pin) & 1;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
@ -203,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);
|
||||
}
|
||||
|
||||
|
@ -214,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);
|
||||
|
||||
|
@ -234,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,
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue