cc2650: Add GPIO driver.
Signed-off-by: Geoffrey Le Gourriérec <geoffrey.legourrierec@smile.fr>
This commit is contained in:
parent
0abeba8b1a
commit
0a89e00080
5 changed files with 404 additions and 0 deletions
|
@ -61,4 +61,6 @@ source "drivers/gpio/Kconfig.pulpino"
|
|||
|
||||
source "drivers/gpio/Kconfig.fe310"
|
||||
|
||||
source "drivers/gpio/Kconfig.cc2650"
|
||||
|
||||
endif # GPIO
|
||||
|
|
26
drivers/gpio/Kconfig.cc2650
Normal file
26
drivers/gpio/Kconfig.cc2650
Normal file
|
@ -0,0 +1,26 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# GPIO configuration options for the CC2650 SoC (Texas Instruments).
|
||||
|
||||
menuconfig GPIO_CC2650
|
||||
bool "TI CC2650 GPIO driver"
|
||||
depends on GPIO && SOC_SERIES_CC2650
|
||||
default n
|
||||
help
|
||||
Enable the GPIO driver on boards equipped with TI CC2650.
|
||||
|
||||
if GPIO_CC2650
|
||||
|
||||
# A single block of GPIO exist.
|
||||
|
||||
config GPIO_CC2650_NAME
|
||||
string
|
||||
default "GPIO_0"
|
||||
prompt "GPIO driver name."
|
||||
|
||||
config GPIO_CC2650_INIT_PRIO
|
||||
int
|
||||
default 40
|
||||
prompt "GPIO driver initialization priority."
|
||||
|
||||
endif # GPIO_CC2650
|
|
@ -14,3 +14,4 @@ obj-$(CONFIG_GPIO_CMSDK_AHB) += gpio_cmsdk_ahb.o
|
|||
obj-$(CONFIG_GPIO_CC32XX) += gpio_cc32xx.o
|
||||
obj-$(CONFIG_GPIO_PULPINO) += gpio_pulpino.o
|
||||
obj-$(CONFIG_GPIO_FE310) += gpio_fe310.o
|
||||
obj-$(CONFIG_GPIO_CC2650) += gpio_cc2650.o
|
||||
|
|
348
drivers/gpio/gpio_cc2650.c
Normal file
348
drivers/gpio/gpio_cc2650.c
Normal file
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* GPIO driver for the CC2650 SOC from Texas Instruments.
|
||||
*/
|
||||
|
||||
#include <toolchain/gcc.h>
|
||||
#include <device.h>
|
||||
#include <gpio.h>
|
||||
#include <init.h>
|
||||
#include <soc.h>
|
||||
#include <sys_io.h>
|
||||
|
||||
#include "gpio_utils.h"
|
||||
|
||||
|
||||
struct gpio_cc2650_data {
|
||||
u32_t pin_callback_enables;
|
||||
sys_slist_t callbacks;
|
||||
};
|
||||
|
||||
/* Pre-declarations */
|
||||
static int gpio_cc2650_init(struct device *dev);
|
||||
static int gpio_cc2650_config(struct device *port, int access_op,
|
||||
u32_t pin, int flags);
|
||||
static int gpio_cc2650_write(struct device *port, int access_op,
|
||||
u32_t pin, u32_t value);
|
||||
|
||||
static int gpio_cc2650_read(struct device *port, int access_op,
|
||||
u32_t pin, u32_t *value);
|
||||
static int gpio_cc2650_manage_callback(struct device *port,
|
||||
struct gpio_callback *callback,
|
||||
bool set);
|
||||
static int gpio_cc2650_enable_callback(struct device *port,
|
||||
int access_op,
|
||||
u32_t pin);
|
||||
static int gpio_cc2650_disable_callback(struct device *port,
|
||||
int access_op,
|
||||
u32_t pin);
|
||||
static u32_t gpio_cc2650_get_pending_int(struct device *dev);
|
||||
|
||||
/* GPIO registers */
|
||||
static const u32_t doutset31_0 =
|
||||
REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS,
|
||||
CC2650_GPIO_DOUTSET31_0);
|
||||
static const u32_t doutclr31_0 =
|
||||
REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS,
|
||||
CC2650_GPIO_DOUTCLR31_0);
|
||||
static const u32_t din31_0 =
|
||||
REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS,
|
||||
CC2650_GPIO_DIN31_0);
|
||||
static const u32_t doe31_0 =
|
||||
REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS,
|
||||
CC2650_GPIO_DOE31_0);
|
||||
static const u32_t evflags31_0 =
|
||||
REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS,
|
||||
CC2650_GPIO_EVFLAGS31_0);
|
||||
|
||||
static struct gpio_cc2650_data gpio_cc2650_data = {
|
||||
.pin_callback_enables = 0
|
||||
};
|
||||
|
||||
static const struct gpio_driver_api gpio_cc2650_funcs = {
|
||||
.config = gpio_cc2650_config,
|
||||
.write = gpio_cc2650_write,
|
||||
.read = gpio_cc2650_read,
|
||||
.manage_callback = gpio_cc2650_manage_callback,
|
||||
.enable_callback = gpio_cc2650_enable_callback,
|
||||
.disable_callback = gpio_cc2650_disable_callback,
|
||||
.get_pending_int = gpio_cc2650_get_pending_int
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(gpio_cc2650_0, CONFIG_GPIO_CC2650_NAME,
|
||||
gpio_cc2650_init, &gpio_cc2650_data, NULL,
|
||||
PRE_KERNEL_1, CONFIG_GPIO_CC2650_INIT_PRIO,
|
||||
&gpio_cc2650_funcs);
|
||||
static void disconnect(const int pin, u32_t *gpiodoe31_0,
|
||||
u32_t *iocfg)
|
||||
{
|
||||
*gpiodoe31_0 &= ~BIT(pin);
|
||||
|
||||
*iocfg &= ~(CC2650_IOC_IOCFGX_PULL_CTL_MASK |
|
||||
CC2650_IOC_IOCFGX_IE_MASK);
|
||||
*iocfg |= CC2650_IOC_INPUT_DISABLED |
|
||||
CC2650_IOC_NO_PULL;
|
||||
}
|
||||
|
||||
/* Configure a single pin.
|
||||
* If any asked option is not implementable, rollback entirely to
|
||||
* previous configuration.
|
||||
*
|
||||
* Note: For pin drive strength, the CC2650 devices only support
|
||||
* symmetric sink/source capabilities.
|
||||
* Thus, you may ONLY determine the common drive strength with
|
||||
* GPIO *low output state* flags. Flags for *high output state*
|
||||
* will be ignored.
|
||||
*/
|
||||
static int gpio_cc2650_config_pin(int pin, int flags)
|
||||
{
|
||||
const u32_t iocfg = REG_ADDR(TI_CC2650_PINMUX_40081000_BASE_ADDRESS,
|
||||
CC2650_IOC_IOCFG0 + 0x4 * pin);
|
||||
u32_t iocfg_config = sys_read32(iocfg);
|
||||
u32_t gpio_doe31_0_config = sys_read32(doe31_0);
|
||||
|
||||
/* Reset all configurable fields to 0 */
|
||||
iocfg_config &= ~(CC2650_IOC_IOCFGX_IOSTR_MASK |
|
||||
CC2650_IOC_IOCFGX_PULL_CTL_MASK |
|
||||
CC2650_IOC_IOCFGX_EDGE_DET_MASK |
|
||||
CC2650_IOC_IOCFGX_EDGE_IRQ_EN_MASK |
|
||||
CC2650_IOC_IOCFGX_IOMODE_MASK |
|
||||
CC2650_IOC_IOCFGX_IE_MASK |
|
||||
CC2650_IOC_IOCFGX_HYST_EN_MASK);
|
||||
|
||||
if (flags & GPIO_PIN_DISABLE) {
|
||||
disconnect(pin, &gpio_doe31_0_config, &iocfg_config);
|
||||
goto commit_config;
|
||||
}
|
||||
|
||||
if (flags & GPIO_DIR_OUT) {
|
||||
gpio_doe31_0_config |= BIT(pin);
|
||||
iocfg_config |= CC2650_IOC_INPUT_DISABLED;
|
||||
} else {
|
||||
gpio_doe31_0_config &= ~BIT(pin);
|
||||
iocfg_config |= CC2650_IOC_INPUT_ENABLED;
|
||||
}
|
||||
|
||||
if (flags & GPIO_INT) {
|
||||
if (!(flags & GPIO_INT_EDGE) &&
|
||||
!(flags & GPIO_INT_DOUBLE_EDGE)) {
|
||||
/* Can't do level-based interrupt */
|
||||
/* Don't commit changes */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
iocfg_config |= BIT(CC2650_IOC_IOCFGX_EDGE_IRQ_EN_POS);
|
||||
|
||||
if (flags & GPIO_INT_EDGE) {
|
||||
if (flags & GPIO_INT_ACTIVE_HIGH) {
|
||||
iocfg_config |= CC2650_IOC_POS_EDGE_DET;
|
||||
} else {
|
||||
iocfg_config |= CC2650_IOC_NEG_EDGE_DET;
|
||||
}
|
||||
} else if (flags & GPIO_INT_DOUBLE_EDGE) {
|
||||
iocfg_config |= CC2650_IOC_NEG_AND_POS_EDGE_DET;
|
||||
}
|
||||
|
||||
if (flags & GPIO_INT_CLOCK_SYNC) {
|
||||
/* Don't commit changes */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (flags & GPIO_INT_DEBOUNCE) {
|
||||
iocfg_config |= CC2650_IOC_HYSTERESIS_ENABLED;
|
||||
} else {
|
||||
iocfg_config |= CC2650_IOC_HYSTERESIS_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & GPIO_POL_INV) {
|
||||
iocfg_config |= CC2650_IOC_INVERTED_IO;
|
||||
} else {
|
||||
iocfg_config |= CC2650_IOC_NORMAL_IO;
|
||||
}
|
||||
|
||||
if (flags & GPIO_PUD_NORMAL) {
|
||||
iocfg_config |= CC2650_IOC_NO_PULL;
|
||||
} else if (flags & GPIO_PUD_PULL_UP) {
|
||||
iocfg_config |= CC2650_IOC_PULL_UP;
|
||||
} else if (flags & GPIO_PUD_PULL_DOWN) {
|
||||
iocfg_config |= CC2650_IOC_PULL_DOWN;
|
||||
}
|
||||
|
||||
/* Remember, we only look at GPIO_DS_*_LOW ! */
|
||||
if (flags & GPIO_DS_DISCONNECT_LOW) {
|
||||
disconnect(pin, &gpio_doe31_0_config, &iocfg_config);
|
||||
}
|
||||
if (flags & GPIO_DS_DFLT_LOW) {
|
||||
iocfg_config |= CC2650_IOC_MIN_DRIVE_STRENGTH;
|
||||
} else {
|
||||
iocfg_config |= CC2650_IOC_MAX_DRIVE_STRENGTH;
|
||||
}
|
||||
|
||||
/* Commit changes */
|
||||
commit_config:
|
||||
sys_write32(iocfg_config, iocfg);
|
||||
sys_write32(gpio_doe31_0_config, doe31_0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gpio_cc2650_write_pin(int pin, u32_t value)
|
||||
{
|
||||
value ? sys_write32(BIT(pin), doutset31_0) :
|
||||
sys_write32(BIT(pin), doutclr31_0);
|
||||
}
|
||||
|
||||
static inline void gpio_cc2650_read_pin(int pin, u32_t *value)
|
||||
{
|
||||
*value = sys_read32(din31_0) & BIT(pin);
|
||||
}
|
||||
|
||||
static void gpio_cc2650_isr(void *arg)
|
||||
{
|
||||
struct device *dev = (struct device *)arg;
|
||||
struct gpio_cc2650_data *data = dev->driver_data;
|
||||
|
||||
const u32_t events = sys_read32(evflags31_0);
|
||||
const u32_t call_mask = events & data->pin_callback_enables;
|
||||
|
||||
/* Clear GPIO trigger events */
|
||||
u32_t evflags = sys_read32(evflags31_0);
|
||||
|
||||
sys_write32(evflags | call_mask, evflags31_0);
|
||||
|
||||
_gpio_fire_callbacks(&data->callbacks, dev, call_mask);
|
||||
}
|
||||
|
||||
|
||||
static int gpio_cc2650_init(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/* ISR setup */
|
||||
IRQ_CONNECT(TI_CC2650_GPIO_40022000_IRQ_0,
|
||||
TI_CC2650_GPIO_40022000_IRQ_0_PRIORITY,
|
||||
gpio_cc2650_isr, DEVICE_GET(gpio_cc2650_0),
|
||||
0);
|
||||
irq_enable(TI_CC2650_GPIO_40022000_IRQ_0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cc2650_config(struct device *port, int access_op,
|
||||
u32_t pin, int flags)
|
||||
{
|
||||
ARG_UNUSED(port);
|
||||
|
||||
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||
return gpio_cc2650_config_pin(pin, flags);
|
||||
}
|
||||
|
||||
const u32_t nb_pins = 32;
|
||||
|
||||
for (u8_t i = 0; i < nb_pins; ++i) {
|
||||
if (pin & 0x1 &&
|
||||
gpio_cc2650_config_pin(i, flags) == -ENOTSUP) {
|
||||
/* The flags being treated the same for
|
||||
* every pin, if we get here then it's
|
||||
* necessarily the first pin on which we act.
|
||||
*
|
||||
* We expect gpio_cc2650_config_pin() to
|
||||
* NOT commit its changes if any problem
|
||||
* arises, thus we do nothing special here
|
||||
* to implement rollback to previous
|
||||
* configuration.
|
||||
*/
|
||||
return -ENOTSUP;
|
||||
}
|
||||
pin >>= 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cc2650_write(struct device *port, int access_op,
|
||||
u32_t pin, u32_t value)
|
||||
{
|
||||
ARG_UNUSED(port);
|
||||
|
||||
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||
gpio_cc2650_write_pin(pin, value);
|
||||
} else {
|
||||
const u32_t nb_pins = 32;
|
||||
|
||||
for (u32_t i = 0; i < nb_pins; ++i) {
|
||||
if (pin & 0x1) {
|
||||
gpio_cc2650_write_pin(i, value);
|
||||
}
|
||||
pin >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cc2650_read(struct device *port, int access_op,
|
||||
u32_t pin, u32_t *value)
|
||||
{
|
||||
ARG_UNUSED(port);
|
||||
|
||||
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||
gpio_cc2650_read_pin(pin, value);
|
||||
*value >>= pin;
|
||||
} else {
|
||||
const u32_t nb_pins = 32;
|
||||
|
||||
for (u32_t i = 0; i < nb_pins; ++i) {
|
||||
if (pin & 0x1) {
|
||||
gpio_cc2650_read_pin(i, value);
|
||||
}
|
||||
pin >>= 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cc2650_manage_callback(struct device *port,
|
||||
struct gpio_callback *callback,
|
||||
bool set)
|
||||
{
|
||||
struct gpio_cc2650_data *data = port->driver_data;
|
||||
|
||||
_gpio_manage_callback(&data->callbacks, callback, set);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cc2650_enable_callback(struct device *port,
|
||||
int access_op,
|
||||
u32_t pin)
|
||||
{
|
||||
struct gpio_cc2650_data *data = port->driver_data;
|
||||
|
||||
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||
data->pin_callback_enables |= BIT(pin);
|
||||
} else {
|
||||
data->pin_callback_enables |= pin;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cc2650_disable_callback(struct device *port,
|
||||
int access_op,
|
||||
u32_t pin)
|
||||
{
|
||||
struct gpio_cc2650_data *data = port->driver_data;
|
||||
|
||||
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||
data->pin_callback_enables &= ~BIT(pin);
|
||||
} else {
|
||||
data->pin_callback_enables &= ~pin;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32_t gpio_cc2650_get_pending_int(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return sys_read32(evflags31_0);
|
||||
}
|
27
dts/arm/yaml/ti,cc2650-gpio.yaml
Normal file
27
dts/arm/yaml/ti,cc2650-gpio.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
title: TI CC2650 GPIO
|
||||
version: 0.1
|
||||
|
||||
description: >
|
||||
This is a representation of the TI CC2650 GPIO node
|
||||
|
||||
properties:
|
||||
- compatible:
|
||||
type: string
|
||||
category: required
|
||||
description: compatible strings
|
||||
constraint: "ti,cc2650-gpio"
|
||||
|
||||
- reg:
|
||||
type: int
|
||||
description: mmio register space
|
||||
generation: define
|
||||
category: required
|
||||
|
||||
- interrupts:
|
||||
type: compound
|
||||
category: required
|
||||
description: required interrupts
|
||||
generation: define
|
||||
...
|
Loading…
Add table
Add a link
Reference in a new issue