diff --git a/CODEOWNERS b/CODEOWNERS index c39c9e19b4f..4c56bf028a3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -140,6 +140,7 @@ /drivers/usb/ @jfischer-phytec-iot @finikorg /drivers/usb/device/usb_dc_stm32.c @ydamigos @loicpoulain /drivers/i2c/i2c_ll_stm32* @ldts @ydamigos +/drivers/i2c/i2c_rv32m1_lpi2c* @henrikbrixandersen /drivers/wifi/ @jukkar @tbursztyka @pfalcon /drivers/wifi/eswifi/ @loicpoulain /dts/arm/st/ @erwango diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 7ff22981c58..00ac164a67e 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -20,6 +20,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SBCON i2c_sbcon.c) zephyr_library_sources_ifdef(CONFIG_I2C_SIFIVE i2c_sifive.c) zephyr_library_sources_ifdef(CONFIG_I2C_NIOS2 i2c_nios2.c) zephyr_library_sources_ifdef(CONFIG_I2C_GECKO i2c_gecko.c) +zephyr_library_sources_ifdef(CONFIG_I2C_RV32M1_LPI2C i2c_rv32m1_lpi2c.c) zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32_v1.c diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 145ab301c6a..c15bb453d52 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -300,4 +300,11 @@ config I2C_NIOS2 help Enable the Nios-II I2C driver. +config I2C_RV32M1_LPI2C + bool "RV32M1 LPI2C driver" + depends on HAS_RV32M1_LPI2C && CLOCK_CONTROL + select HAS_DTS_I2C + help + Enable the RV32M1 LPI2C driver. + endif # I2C diff --git a/drivers/i2c/i2c_rv32m1_lpi2c.c b/drivers/i2c/i2c_rv32m1_lpi2c.c new file mode 100644 index 00000000000..18111b90eec --- /dev/null +++ b/drivers/i2c/i2c_rv32m1_lpi2c.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2019, Henrik Brix Andersen + * + * Based on the i2c_mcux_lpi2c.c driver, which is: + * Copyright (c) 2016 Freescale Semiconductor, Inc. + * Copyright (c) 2019, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +LOG_MODULE_REGISTER(rv32m1_lpi2c); + +#include "i2c-priv.h" + +struct rv32m1_lpi2c_config { + LPI2C_Type *base; + char *clock_controller; + clock_control_subsys_t clock_subsys; + clock_ip_name_t clock_ip_name; + u32_t clock_ip_src; + u32_t bitrate; + void (*irq_config_func)(struct device *dev); +}; + +struct rv32m1_lpi2c_data { + lpi2c_master_handle_t handle; + struct k_sem transfer_sync; + struct k_sem completion_sync; + status_t completion_status; +}; + +static int rv32m1_lpi2c_configure(struct device *dev, u32_t dev_config) +{ + const struct rv32m1_lpi2c_config *config = dev->config->config_info; + struct device *clk; + u32_t baudrate; + u32_t clk_freq; + int err; + + if (!(I2C_MODE_MASTER & dev_config)) { + /* Slave mode not supported - yet */ + LOG_ERR("Slave mode not supported"); + return -ENOTSUP; + } + + if (I2C_ADDR_10_BITS & dev_config) { + /* FSL LPI2C driver only supports 7-bit addressing */ + LOG_ERR("10 bit addressing not supported"); + return -ENOTSUP; + } + + switch (I2C_SPEED_GET(dev_config)) { + case I2C_SPEED_STANDARD: + baudrate = KHZ(100); + break; + case I2C_SPEED_FAST: + baudrate = KHZ(400); + break; + case I2C_SPEED_FAST_PLUS: + baudrate = MHZ(1); + break; + /* TODO: only if SCL pin implements current source pull-up */ + /* case I2C_SPEED_HIGH: */ + /* baudrate = KHZ(3400); */ + /* break; */ + /* TODO: ultra-fast requires pin_config setting */ + /* case I2C_SPEED_ULTRA: */ + /* baudrate = MHZ(5); */ + /* break; */ + default: + LOG_ERR("Unsupported speed"); + return -ENOTSUP; + } + + clk = device_get_binding(config->clock_controller); + if (!clk) { + LOG_ERR("Could not get clock controller '%s'", + config->clock_controller); + return -EINVAL; + } + + err = clock_control_get_rate(clk, config->clock_subsys, &clk_freq); + if (err) { + LOG_ERR("Could not get clock frequency (err %d)", err); + return -EINVAL; + } + + LPI2C_MasterSetBaudRate(config->base, clk_freq, baudrate); + + return 0; +} + +static void rv32m1_lpi2c_master_transfer_callback(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + status_t completionStatus, + void *userData) +{ + struct device *dev = userData; + struct rv32m1_lpi2c_data *data = dev->driver_data; + + ARG_UNUSED(base); + ARG_UNUSED(handle); + + data->completion_status = completionStatus; + k_sem_give(&data->completion_sync); +} + +static u32_t rv32m1_lpi2c_convert_flags(int msg_flags) +{ + u32_t flags = 0U; + + if (!(msg_flags & I2C_MSG_STOP)) { + flags |= kLPI2C_TransferNoStopFlag; + } + + if (msg_flags & I2C_MSG_RESTART) { + flags |= kLPI2C_TransferRepeatedStartFlag; + } + + return flags; +} + +static int rv32m1_lpi2c_transfer(struct device *dev, struct i2c_msg *msgs, + u8_t num_msgs, u16_t addr) +{ + const struct rv32m1_lpi2c_config *config = dev->config->config_info; + struct rv32m1_lpi2c_data *data = dev->driver_data; + lpi2c_master_transfer_t transfer; + status_t status; + int ret = 0; + + k_sem_take(&data->transfer_sync, K_FOREVER); + + /* Iterate over all the messages */ + for (int i = 0; i < num_msgs; i++) { + if (I2C_MSG_ADDR_10_BITS & msgs->flags) { + ret = -ENOTSUP; + goto out; + } + + /* Initialize the transfer descriptor */ + transfer.flags = rv32m1_lpi2c_convert_flags(msgs->flags); + + /* Prevent the controller to send a start condition between + * messages, except if explicitly requested. + */ + if (i != 0 && !(msgs->flags & I2C_MSG_RESTART)) { + transfer.flags |= kLPI2C_TransferNoStartFlag; + } + + transfer.slaveAddress = addr; + transfer.direction = (msgs->flags & I2C_MSG_READ) + ? kLPI2C_Read : kLPI2C_Write; + transfer.subaddress = 0; + transfer.subaddressSize = 0; + transfer.data = msgs->buf; + transfer.dataSize = msgs->len; + + /* Start the transfer */ + status = LPI2C_MasterTransferNonBlocking(config->base, + &data->handle, + &transfer); + + /* Return an error if the transfer didn't start successfully + * e.g., if the bus was busy + */ + if (status != kStatus_Success) { + LOG_DBG("Could not start transfer (status %d)", status); + ret = -EIO; + goto out; + } + + /* Wait for the transfer to complete */ + k_sem_take(&data->completion_sync, K_FOREVER); + + /* Return an error if the transfer didn't complete + * successfully. e.g., nak, timeout, lost arbitration + */ + if (data->completion_status != kStatus_Success) { + LOG_DBG("Transfer failed (status %d)", + data->completion_status); + LPI2C_MasterTransferAbort(config->base, &data->handle); + ret = -EIO; + goto out; + } + + /* Move to the next message */ + msgs++; + } + +out: + k_sem_give(&data->transfer_sync); + return ret; +} + +static void rv32m1_lpi2c_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + const struct rv32m1_lpi2c_config *config = dev->config->config_info; + struct rv32m1_lpi2c_data *data = dev->driver_data; + + LPI2C_MasterTransferHandleIRQ(config->base, &data->handle); +} + +static int rv32m1_lpi2c_init(struct device *dev) +{ + const struct rv32m1_lpi2c_config *config = dev->config->config_info; + struct rv32m1_lpi2c_data *data = dev->driver_data; + lpi2c_master_config_t master_config; + struct device *clk; + u32_t clk_freq, dev_cfg; + int err; + + CLOCK_SetIpSrc(config->clock_ip_name, config->clock_ip_src); + + clk = device_get_binding(config->clock_controller); + if (!clk) { + LOG_ERR("Could not get clock controller '%s'", + config->clock_controller); + return -EINVAL; + } + + err = clock_control_on(clk, config->clock_subsys); + if (err) { + LOG_ERR("Could not turn on clock (err %d)", err); + return -EINVAL; + } + + err = clock_control_get_rate(clk, config->clock_subsys, &clk_freq); + if (err) { + LOG_ERR("Could not get clock frequency (err %d)", err); + return -EINVAL; + } + + LPI2C_MasterGetDefaultConfig(&master_config); + LPI2C_MasterInit(config->base, &master_config, clk_freq); + LPI2C_MasterTransferCreateHandle(config->base, &data->handle, + rv32m1_lpi2c_master_transfer_callback, + dev); + + dev_cfg = i2c_map_dt_bitrate(config->bitrate); + err = rv32m1_lpi2c_configure(dev, dev_cfg | I2C_MODE_MASTER); + if (err) { + LOG_ERR("Could not configure controller (err %d)", err); + return err; + } + + config->irq_config_func(dev); + + return 0; +} + +static const struct i2c_driver_api rv32m1_lpi2c_driver_api = { + .configure = rv32m1_lpi2c_configure, + .transfer = rv32m1_lpi2c_transfer, +}; + +#define RV32M1_LPI2C_DEVICE(id) \ + static void rv32m1_lpi2c_irq_config_func_##id(struct device *dev); \ + static const struct rv32m1_lpi2c_config rv32m1_lpi2c_##id##_config = { \ + .base = \ + (LPI2C_Type *)DT_OPENISA_RV32M1_LPI2C_I2C_##id##_BASE_ADDRESS, \ + .clock_controller = \ + DT_OPENISA_RV32M1_LPI2C_I2C_##id##_CLOCK_CONTROLLER, \ + .clock_subsys = \ + (clock_control_subsys_t) \ + DT_OPENISA_RV32M1_LPI2C_I2C_##id##_CLOCK_NAME, \ + .clock_ip_name = kCLOCK_Lpi2c##id, \ + .clock_ip_src = kCLOCK_IpSrcFircAsync, \ + .bitrate = DT_OPENISA_RV32M1_LPI2C_I2C_##id##_CLOCK_FREQUENCY, \ + .irq_config_func = rv32m1_lpi2c_irq_config_func_##id, \ + }; \ + static struct rv32m1_lpi2c_data rv32m1_lpi2c_##id##_data = { \ + .transfer_sync = Z_SEM_INITIALIZER( \ + rv32m1_lpi2c_##id##_data.transfer_sync, 1, 1), \ + .completion_sync = Z_SEM_INITIALIZER( \ + rv32m1_lpi2c_##id##_data.completion_sync, 0, 1), \ + }; \ + DEVICE_AND_API_INIT(rv32m1_lpi2c_##id, \ + DT_OPENISA_RV32M1_LPI2C_I2C_##id##_LABEL, \ + &rv32m1_lpi2c_init, \ + &rv32m1_lpi2c_##id##_data, \ + &rv32m1_lpi2c_##id##_config, \ + POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ + &rv32m1_lpi2c_driver_api); \ + static void rv32m1_lpi2c_irq_config_func_##id(struct device *dev) \ + { \ + IRQ_CONNECT(DT_OPENISA_RV32M1_LPI2C_I2C_##id##_IRQ, \ + DT_OPENISA_RV32M1_LPI2C_I2C_##id##_IRQ_PRI, \ + rv32m1_lpi2c_isr, DEVICE_GET(rv32m1_lpi2c_##id), \ + 0); \ + irq_enable(DT_OPENISA_RV32M1_LPI2C_I2C_##id##_IRQ); \ + } \ + +#ifdef CONFIG_I2C_0 +RV32M1_LPI2C_DEVICE(0) +#endif + +#ifdef CONFIG_I2C_1 +RV32M1_LPI2C_DEVICE(1) +#endif + +#ifdef CONFIG_I2C_2 +RV32M1_LPI2C_DEVICE(2) +#endif + +#ifdef CONFIG_I2C_3 +RV32M1_LPI2C_DEVICE(3) +#endif diff --git a/dts/bindings/i2c/openisa,rv32m1-lpi2c.yaml b/dts/bindings/i2c/openisa,rv32m1-lpi2c.yaml new file mode 100644 index 00000000000..842c12084d2 --- /dev/null +++ b/dts/bindings/i2c/openisa,rv32m1-lpi2c.yaml @@ -0,0 +1,30 @@ +# +# Copyright (c) 2019, Henrik Brix Andersen +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: OpenISA LPI2C +version: 0.1 + +description: > + This binding gives a base representation of the OpenISA LPI2C controller + +inherits: + !include i2c.yaml + +properties: + compatible: + constraint: "openisa,rv32m1-lpi2c" + + reg: + type: int + description: mmio register space + generation: define + category: required + + interrupts: + type: compound + category: required + description: required interrupts + generation: define diff --git a/dts/riscv32/rv32m1_ri5cy.dtsi b/dts/riscv32/rv32m1_ri5cy.dtsi index f1e146345d6..47d8268cbd6 100644 --- a/dts/riscv32/rv32m1_ri5cy.dtsi +++ b/dts/riscv32/rv32m1_ri5cy.dtsi @@ -5,6 +5,7 @@ #include #include +#include / { #address-cells = <1>; @@ -29,6 +30,10 @@ uart-1 = &uart1; uart-2 = &uart2; uart-3 = &uart3; + i2c-0 = &i2c0; + i2c-1 = &i2c1; + i2c-2 = &i2c2; + i2c-3 = &i2c3; }; cpus { @@ -244,5 +249,57 @@ label = "UART_3"; status = "disabled"; }; + + i2c0: lpi2c@4003a000 { + compatible = "openisa,rv32m1-lpi2c"; + reg = <0x4003a000 0x170>; + interrupt-parent = <&event>; + interrupts = <15>; + clocks = <&pcc0 0xe8>; + label = "I2C_0"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: lpi2c@4003b000 { + compatible = "openisa,rv32m1-lpi2c"; + reg = <0x4003b000 0x170>; + interrupt-parent = <&event>; + interrupts = <16>; + clocks = <&pcc0 0xec>; + label = "I2C_1"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2: lpi2c@4003c000 { + compatible = "openisa,rv32m1-lpi2c"; + reg = <0x4003c000 0x170>; + interrupt-parent = <&intmux>; + interrupts = ; + clocks = <&pcc0 0xf0>; + label = "I2C_2"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c3: lpi2c@4102e000 { + compatible = "openisa,rv32m1-lpi2c"; + reg = <0x4102e000 0x170>; + interrupt-parent = <&intmux>; + interrupts = ; + clocks = <&pcc1 0xb8>; + label = "I2C_3"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; }; diff --git a/ext/hal/openisa/vega_sdk_riscv/Kconfig b/ext/hal/openisa/vega_sdk_riscv/Kconfig index 81fbbe2bd66..02519b11eb2 100644 --- a/ext/hal/openisa/vega_sdk_riscv/Kconfig +++ b/ext/hal/openisa/vega_sdk_riscv/Kconfig @@ -10,3 +10,8 @@ config HAS_RV32M1_LPUART bool help Set if the low power uart (LPUART) module is present in the SoC. + +config HAS_RV32M1_LPI2C + bool + help + Set if the low power i2c (LPI2C) module is present in the SoC. diff --git a/ext/hal/openisa/vega_sdk_riscv/devices/RV32M1/drivers/CMakeLists.txt b/ext/hal/openisa/vega_sdk_riscv/devices/RV32M1/drivers/CMakeLists.txt index 8d601a47c2b..a50be0c20af 100644 --- a/ext/hal/openisa/vega_sdk_riscv/devices/RV32M1/drivers/CMakeLists.txt +++ b/ext/hal/openisa/vega_sdk_riscv/devices/RV32M1/drivers/CMakeLists.txt @@ -2,3 +2,4 @@ zephyr_include_directories(.) zephyr_sources(fsl_clock.c) zephyr_sources_ifdef(CONFIG_UART_RV32M1_LPUART fsl_lpuart.c) +zephyr_sources_ifdef(CONFIG_I2C_RV32M1_LPI2C fsl_lpi2c.c) diff --git a/soc/riscv32/openisa_rv32m1/Kconfig.defconfig b/soc/riscv32/openisa_rv32m1/Kconfig.defconfig index 4984f7de386..3b35faa638c 100644 --- a/soc/riscv32/openisa_rv32m1/Kconfig.defconfig +++ b/soc/riscv32/openisa_rv32m1/Kconfig.defconfig @@ -185,4 +185,11 @@ config UART_RV32M1_LPUART endif # SERIAL +if I2C + +config I2C_RV32M1_LPI2C + def_bool y + +endif # I2C + endif # SOC_OPENISA_RV32M1_RISCV32 diff --git a/soc/riscv32/openisa_rv32m1/Kconfig.soc b/soc/riscv32/openisa_rv32m1/Kconfig.soc index 44efb6c3980..4909cac8229 100644 --- a/soc/riscv32/openisa_rv32m1/Kconfig.soc +++ b/soc/riscv32/openisa_rv32m1/Kconfig.soc @@ -12,6 +12,7 @@ config SOC_OPENISA_RV32M1_RISCV32 # (We can't make it a 'depends on' without causing a dependency loop). select XIP select HAS_RV32M1_LPUART + select HAS_RV32M1_LPI2C select ATOMIC_OPERATIONS_C select VEGA_SDK_HAL select RISCV_SOC_INTERRUPT_INIT