From 6eb0cb0d855848273fc5cc3652399ad69c1c07aa Mon Sep 17 00:00:00 2001 From: Brett Witherspoon Date: Tue, 23 Apr 2019 15:33:18 -0500 Subject: [PATCH] drivers: add CC13xx / CC26xx I2C driver Add I2C driver for the TI CC13xx / CC26xx series SoCs. Signed-off-by: Brett Witherspoon --- drivers/i2c/CMakeLists.txt | 1 + drivers/i2c/Kconfig | 1 + drivers/i2c/Kconfig.cc13xx_cc26xx | 12 + drivers/i2c/i2c_cc13xx_cc26xx.c | 337 ++++++++++++++++++ dts/arm/ti/cc13x2_cc26x2.dtsi | 12 + dts/bindings/i2c/ti,cc13xx-cc26xx-i2c.yaml | 46 +++ .../ti/devices/cc13x2_cc26x2/CMakeLists.txt | 2 + .../cc13x2_cc26x2/Kconfig.defconfig.series | 7 + 8 files changed, 418 insertions(+) create mode 100644 drivers/i2c/Kconfig.cc13xx_cc26xx create mode 100644 drivers/i2c/i2c_cc13xx_cc26xx.c create mode 100644 dts/bindings/i2c/ti,cc13xx-cc26xx-i2c.yaml diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 8f9b3805f9..06df11a15c 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_I2C_BITBANG i2c_bitbang.c) +zephyr_library_sources_ifdef(CONFIG_I2C_CC13XX_CC26XX i2c_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_I2C_CC32XX i2c_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_I2C_ESP32 i2c_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2C_GPIO i2c_gpio.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 39dbb5fef8..83c5013f0c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -18,6 +18,7 @@ if I2C # Include these first so that any properties (e.g. defaults) below can be # overriden (by defining symbols in multiple locations) +source "drivers/i2c/Kconfig.cc13xx_cc26xx" source "drivers/i2c/Kconfig.dw" source "drivers/i2c/Kconfig.esp32" source "drivers/i2c/slave/Kconfig" diff --git a/drivers/i2c/Kconfig.cc13xx_cc26xx b/drivers/i2c/Kconfig.cc13xx_cc26xx new file mode 100644 index 0000000000..352a8cb450 --- /dev/null +++ b/drivers/i2c/Kconfig.cc13xx_cc26xx @@ -0,0 +1,12 @@ +# +# Copyright (c) 2019 Brett Witherspoon +# +# SPDX-License-Identifier: Apache-2.0 +# + +config I2C_CC13XX_CC26XX + bool "TI SimpleLink CC13xx / CC26xx I2C driver" + depends on SOC_SERIES_CC13X2_CC26X2 + select HAS_DTS_I2C + help + Enable support for I2C on the TI SimpleLink CC13xx / CC26xx series. diff --git a/drivers/i2c/i2c_cc13xx_cc26xx.c b/drivers/i2c/i2c_cc13xx_cc26xx.c new file mode 100644 index 0000000000..79762fa89a --- /dev/null +++ b/drivers/i2c/i2c_cc13xx_cc26xx.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2019 Brett Witherspoon + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL +#include +LOG_MODULE_REGISTER(i2c_cc13xx_cc26xx); + +#include +#include +#include + +#include "i2c-priv.h" + +DEVICE_DECLARE(i2c_cc13xx_cc26xx); + +struct i2c_cc13xx_cc26xx_data { + struct k_sem lock; + struct k_sem complete; + volatile u32_t error; +}; + +struct i2c_cc13xx_cc26xx_config { + u32_t base; + u32_t scl_pin; + u32_t sda_pin; +}; + +static inline struct i2c_cc13xx_cc26xx_data *get_dev_data(struct device *dev) +{ + return dev->driver_data; +} + +static inline const struct i2c_cc13xx_cc26xx_config * +get_dev_config(struct device *dev) +{ + return dev->config->config_info; +} + +static int i2c_cc13xx_cc26xx_transmit(struct device *dev, const u8_t *buf, + u32_t len, u16_t addr) +{ + const u32_t base = get_dev_config(dev)->base; + struct i2c_cc13xx_cc26xx_data *data = get_dev_data(dev); + + /* Sending address without data is not supported */ + if (len == 0) { + return -EIO; + } + + I2CMasterSlaveAddrSet(base, addr, false); + + /* The following assumes a single master. Use I2CMasterBusBusy() if + * wanting to implement multiple master support. + */ + + /* Single transmission */ + if (len == 1) { + I2CMasterDataPut(base, *buf); + + I2CMasterControl(base, I2C_MASTER_CMD_SINGLE_SEND); + + k_sem_take(&data->complete, K_FOREVER); + + return data->error == I2C_MASTER_ERR_NONE ? 0 : -EIO; + } + + /* Burst transmission */ + I2CMasterDataPut(base, buf[0]); + + I2CMasterControl(base, I2C_MASTER_CMD_BURST_SEND_START); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + goto send_error_stop; + } + + for (int i = 1; i < len - 1; i++) { + I2CMasterDataPut(base, buf[i]); + + I2CMasterControl(base, I2C_MASTER_CMD_BURST_SEND_CONT); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + goto send_error_stop; + } + } + + I2CMasterDataPut(base, buf[len - 1]); + + I2CMasterControl(base, I2C_MASTER_CMD_BURST_SEND_FINISH); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + return -EIO; + } + + return 0; + +send_error_stop: + I2CMasterControl(base, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + return -EIO; +} + +static int i2c_cc13xx_cc26xx_receive(struct device *dev, u8_t *buf, u32_t len, + u16_t addr) +{ + const u32_t base = get_dev_config(dev)->base; + struct i2c_cc13xx_cc26xx_data *data = get_dev_data(dev); + + /* Sending address without data is not supported */ + if (len == 0) { + return -EIO; + } + + I2CMasterSlaveAddrSet(base, addr, true); + + /* The following assumes a single master. Use I2CMasterBusBusy() if + * wanting to implement multiple master support. + */ + + /* Single receive */ + if (len == 1) { + I2CMasterControl(base, I2C_MASTER_CMD_SINGLE_RECEIVE); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + return -EIO; + } + + *buf = I2CMasterDataGet(base); + + return 0; + } + + /* Burst receive */ + I2CMasterControl(base, I2C_MASTER_CMD_BURST_RECEIVE_START); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + goto recv_error_stop; + } + + buf[0] = I2CMasterDataGet(base); + + for (int i = 1; i < len - 1; i++) { + I2CMasterControl(base, I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + goto recv_error_stop; + } + + buf[i] = I2CMasterDataGet(base); + } + + I2CMasterControl(base, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + k_sem_take(&data->complete, K_FOREVER); + + if (data->error != I2C_MASTER_ERR_NONE) { + return -EIO; + } + + buf[len - 1] = I2CMasterDataGet(base); + + return 0; + +recv_error_stop: + I2CMasterControl(base, I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP); + return -EIO; +} + +static int i2c_cc13xx_cc26xx_transfer(struct device *dev, struct i2c_msg *msgs, + u8_t num_msgs, u16_t addr) +{ + int ret = 0; + + if (num_msgs == 0) { + return 0; + } + + k_sem_take(&get_dev_data(dev)->lock, K_FOREVER); + + for (int i = 0; i < num_msgs; i++) { + /* Not supported by hardware */ + if (msgs[i].flags & I2C_MSG_ADDR_10_BITS) { + ret = -EIO; + break; + } + + if ((msgs[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { + ret = i2c_cc13xx_cc26xx_transmit(dev, msgs[i].buf, + msgs[i].len, addr); + } else { + ret = i2c_cc13xx_cc26xx_receive(dev, msgs[i].buf, + msgs[i].len, addr); + } + + if (ret) { + break; + } + } + + k_sem_give(&get_dev_data(dev)->lock); + + return ret; +} + +static int i2c_cc13xx_cc26xx_configure(struct device *dev, u32_t dev_config) +{ + bool fast; + + switch (I2C_SPEED_GET(dev_config)) { + case I2C_SPEED_STANDARD: + fast = false; + break; + case I2C_SPEED_FAST: + fast = true; + break; + default: + LOG_ERR("Unsupported speed"); + return -EIO; + } + + /* Support for slave mode has not been implemented */ + if (!(dev_config & I2C_MODE_MASTER)) { + LOG_ERR("Slave mode is not supported"); + return -EIO; + } + + /* This is deprecated and could be ignored in the future */ + if (dev_config & I2C_ADDR_10_BITS) { + LOG_ERR("10-bit addressing mode is not supported"); + return -EIO; + } + + /* Enables and configures I2C master */ + I2CMasterInitExpClk(get_dev_config(dev)->base, + sys_clock_hw_cycles_per_sec(), fast); + + return 0; +} + +static void i2c_cc13xx_cc26xx_isr(void *arg) +{ + const u32_t base = get_dev_config(arg)->base; + struct i2c_cc13xx_cc26xx_data *data = get_dev_data(arg); + + if (I2CMasterIntStatus(base, true)) { + I2CMasterIntClear(base); + + data->error = I2CMasterErr(base); + + k_sem_give(&data->complete); + } +} + +static int i2c_cc13xx_cc26xx_init(struct device *dev) +{ + u32_t cfg; + int err; + + /* Enable I2C power domain */ + PRCMPowerDomainOn(PRCM_DOMAIN_SERIAL); + + /* Enable I2C peripheral clock */ + PRCMPeripheralRunEnable(PRCM_PERIPH_I2C0); + /* Enable in sleep mode until proper power managment is added */ + PRCMPeripheralSleepEnable(PRCM_PERIPH_I2C0); + PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_I2C0); + + /* Load PRCM settings */ + PRCMLoadSet(); + while (!PRCMLoadGet()) { + continue; + } + + /* I2C should not be accessed until power domain is on. */ + while (PRCMPowerDomainStatus(PRCM_DOMAIN_SERIAL) != + PRCM_DOMAIN_POWER_ON) { + continue; + } + + IRQ_CONNECT(DT_TI_CC13XX_CC26XX_I2C_0_IRQ_0, + DT_TI_CC13XX_CC26XX_I2C_0_IRQ_0_PRIORITY, + i2c_cc13xx_cc26xx_isr, DEVICE_GET(i2c_cc13xx_cc26xx), 0); + irq_enable(DT_TI_CC13XX_CC26XX_I2C_0_IRQ_0); + + /* Configure IOC module to route SDA and SCL signals */ + IOCPinTypeI2c(get_dev_config(dev)->base, get_dev_config(dev)->sda_pin, + get_dev_config(dev)->scl_pin); + + cfg = i2c_map_dt_bitrate(DT_TI_CC13XX_CC26XX_I2C_0_CLOCK_FREQUENCY); + err = i2c_cc13xx_cc26xx_configure(dev, cfg | I2C_MODE_MASTER); + if (err) { + LOG_ERR("Failed to configure"); + return err; + } + + I2CMasterIntEnable(get_dev_config(dev)->base); + + return 0; +} + +static const struct i2c_driver_api i2c_cc13xx_cc26xx_driver_api = { + .configure = i2c_cc13xx_cc26xx_configure, + .transfer = i2c_cc13xx_cc26xx_transfer +}; + +static const struct i2c_cc13xx_cc26xx_config i2c_cc13xx_cc26xx_config = { + .base = DT_TI_CC13XX_CC26XX_I2C_0_BASE_ADDRESS, + .sda_pin = DT_TI_CC13XX_CC26XX_I2C_0_SDA_PIN, + .scl_pin = DT_TI_CC13XX_CC26XX_I2C_0_SCL_PIN +}; + +static struct i2c_cc13xx_cc26xx_data i2c_cc13xx_cc26xx_data = { + .lock = Z_SEM_INITIALIZER(i2c_cc13xx_cc26xx_data.lock, 1, 1), + .complete = Z_SEM_INITIALIZER(i2c_cc13xx_cc26xx_data.complete, 0, 1), + .error = I2C_MASTER_ERR_NONE +}; + +DEVICE_AND_API_INIT(i2c_cc13xx_cc26xx, DT_TI_CC13XX_CC26XX_I2C_0_LABEL, + i2c_cc13xx_cc26xx_init, &i2c_cc13xx_cc26xx_data, + &i2c_cc13xx_cc26xx_config, POST_KERNEL, + CONFIG_I2C_INIT_PRIORITY, &i2c_cc13xx_cc26xx_driver_api); diff --git a/dts/arm/ti/cc13x2_cc26x2.dtsi b/dts/arm/ti/cc13x2_cc26x2.dtsi index e601bbd664..6b8af889ba 100644 --- a/dts/arm/ti/cc13x2_cc26x2.dtsi +++ b/dts/arm/ti/cc13x2_cc26x2.dtsi @@ -5,6 +5,7 @@ */ #include +#include #include / { @@ -75,6 +76,17 @@ status = "disabled"; label = "UART_1"; }; + + i2c0: i2c@40002000 { + compatible = "ti,cc13xx-cc26xx-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40002000 0x1000>; + interrupts = <1 0>; + clock-frequency = ; + status = "disabled"; + label = "I2C_0"; + }; }; }; diff --git a/dts/bindings/i2c/ti,cc13xx-cc26xx-i2c.yaml b/dts/bindings/i2c/ti,cc13xx-cc26xx-i2c.yaml new file mode 100644 index 0000000000..a6d6ee6f0b --- /dev/null +++ b/dts/bindings/i2c/ti,cc13xx-cc26xx-i2c.yaml @@ -0,0 +1,46 @@ +# +# Copyright (c) 2019 Brett Witherspoon +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: TI CC13xx / CC26xx I2C +version: 0.1 + +description: > + This is a representation of the TI CC13xx / CC26xx I2C node + +inherits: + !include i2c.yaml + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "ti,cc13xx-cc26xx-i2c" + + reg: + type: array + category: required + description: mmio register space + generation: define + + interrupts: + type: array + category: required + description: required interrupts + generation: define + + sda-pin: + type: int + category: required + description: SDA pin + generation: define + + scl-pin: + type: int + category: required + description: SCL pin + generation: define +... diff --git a/ext/hal/ti/simplelink/source/ti/devices/cc13x2_cc26x2/CMakeLists.txt b/ext/hal/ti/simplelink/source/ti/devices/cc13x2_cc26x2/CMakeLists.txt index 70c0bf249e..7e684a4b7a 100644 --- a/ext/hal/ti/simplelink/source/ti/devices/cc13x2_cc26x2/CMakeLists.txt +++ b/ext/hal/ti/simplelink/source/ti/devices/cc13x2_cc26x2/CMakeLists.txt @@ -11,6 +11,8 @@ zephyr_library_sources( driverlib/setup.c driverlib/chipinfo.c driverlib/aux_sysif.c + # Required for CPUdelay which is not in ROM + driverlib/cpu.c # Required for default CCFG confguration startup_files/ccfg.c ) diff --git a/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series b/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series index ca84cfa8e4..ee546bdd05 100644 --- a/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series +++ b/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series @@ -53,4 +53,11 @@ config UART_CC13XX_CC26XX endif # SERIAL +if I2C + +config I2C_CC13XX_CC26XX + default y + +endif # I2C + endif # SOC_SERIES_CC13X2_CC26X2