| /* |
| * Copyright (c) 2018 Diego Sueiro |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT silabs_gecko_i2c |
| |
| #include <errno.h> |
| #include <drivers/i2c.h> |
| #include <sys/util.h> |
| #include <em_cmu.h> |
| #include <em_i2c.h> |
| #include <em_gpio.h> |
| #include <soc.h> |
| |
| #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(i2c_gecko); |
| |
| #include "i2c-priv.h" |
| |
| #define DEV_BASE(dev) \ |
| ((I2C_TypeDef *)((const struct i2c_gecko_config * const)(dev)->config)->base) |
| |
| struct i2c_gecko_config { |
| I2C_TypeDef *base; |
| CMU_Clock_TypeDef clock; |
| uint32_t bitrate; |
| struct soc_gpio_pin pin_sda; |
| struct soc_gpio_pin pin_scl; |
| #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION |
| uint8_t loc_sda; |
| uint8_t loc_scl; |
| #else |
| uint8_t loc; |
| #endif |
| }; |
| |
| struct i2c_gecko_data { |
| struct k_sem device_sync_sem; |
| uint32_t dev_config; |
| }; |
| |
| void i2c_gecko_config_pins(const struct device *dev, |
| const struct soc_gpio_pin *pin_sda, |
| const struct soc_gpio_pin *pin_scl) |
| { |
| I2C_TypeDef *base = DEV_BASE(dev); |
| const struct i2c_gecko_config *config = dev->config; |
| |
| soc_gpio_configure(pin_scl); |
| soc_gpio_configure(pin_sda); |
| |
| #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION |
| base->ROUTEPEN = I2C_ROUTEPEN_SDAPEN | I2C_ROUTEPEN_SCLPEN; |
| base->ROUTELOC0 = (config->loc_sda << _I2C_ROUTELOC0_SDALOC_SHIFT) | |
| (config->loc_scl << _I2C_ROUTELOC0_SCLLOC_SHIFT); |
| #elif defined(GPIO_I2C_ROUTEEN_SCLPEN) && defined(GPIO_I2C_ROUTEEN_SDAPEN) |
| GPIO->I2CROUTE[I2C_NUM(base)].ROUTEEN = GPIO_I2C_ROUTEEN_SCLPEN | |
| GPIO_I2C_ROUTEEN_SDAPEN; |
| GPIO->I2CROUTE[I2C_NUM(base)].SCLROUTE = |
| (config->pin_scl.pin << _GPIO_I2C_SCLROUTE_PIN_SHIFT) | |
| (config->pin_scl.port << _GPIO_I2C_SCLROUTE_PORT_SHIFT); |
| GPIO->I2CROUTE[I2C_NUM(base)].SDAROUTE = |
| (config->pin_sda.pin << _GPIO_I2C_SDAROUTE_PIN_SHIFT) | |
| (config->pin_sda.port << _GPIO_I2C_SDAROUTE_PORT_SHIFT); |
| #else |
| base->ROUTE = I2C_ROUTE_SDAPEN | I2C_ROUTE_SCLPEN | (config->loc << 8); |
| #endif |
| } |
| |
| static int i2c_gecko_configure(const struct device *dev, |
| uint32_t dev_config_raw) |
| { |
| I2C_TypeDef *base = DEV_BASE(dev); |
| struct i2c_gecko_data *data = dev->data; |
| I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT; |
| uint32_t baudrate; |
| |
| if (!(I2C_MODE_MASTER & dev_config_raw)) { |
| return -EINVAL; |
| } |
| |
| switch (I2C_SPEED_GET(dev_config_raw)) { |
| 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; |
| default: |
| return -EINVAL; |
| } |
| |
| data->dev_config = dev_config_raw; |
| i2cInit.freq = baudrate; |
| |
| I2C_Init(base, &i2cInit); |
| |
| return 0; |
| } |
| |
| static int i2c_gecko_transfer(const struct device *dev, struct i2c_msg *msgs, |
| uint8_t num_msgs, uint16_t addr) |
| { |
| I2C_TypeDef *base = DEV_BASE(dev); |
| struct i2c_gecko_data *data = dev->data; |
| I2C_TransferSeq_TypeDef seq; |
| I2C_TransferReturn_TypeDef ret = -EIO; |
| uint32_t timeout = 300000U; |
| |
| if (!num_msgs) { |
| return 0; |
| } |
| |
| seq.addr = addr << 1; |
| |
| do { |
| seq.buf[0].data = msgs->buf; |
| seq.buf[0].len = msgs->len; |
| |
| if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) { |
| seq.flags = I2C_FLAG_READ; |
| } else { |
| seq.flags = I2C_FLAG_WRITE; |
| if (num_msgs > 1) { |
| /* Next message */ |
| msgs++; |
| num_msgs--; |
| if ((msgs->flags & I2C_MSG_RW_MASK) |
| == I2C_MSG_READ) { |
| seq.flags = I2C_FLAG_WRITE_READ; |
| } else { |
| seq.flags = I2C_FLAG_WRITE_WRITE; |
| } |
| seq.buf[1].data = msgs->buf; |
| seq.buf[1].len = msgs->len; |
| } |
| } |
| |
| if (data->dev_config & I2C_ADDR_10_BITS) { |
| seq.flags |= I2C_FLAG_10BIT_ADDR; |
| } |
| |
| /* Do a polled transfer */ |
| ret = I2C_TransferInit(base, &seq); |
| while (ret == i2cTransferInProgress && timeout--) { |
| ret = I2C_Transfer(base); |
| } |
| |
| if (ret != i2cTransferDone) { |
| goto finish; |
| } |
| |
| /* Next message */ |
| msgs++; |
| num_msgs--; |
| } while (num_msgs); |
| |
| finish: |
| if (ret != i2cTransferDone) { |
| ret = -EIO; |
| } |
| return ret; |
| } |
| |
| static int i2c_gecko_init(const struct device *dev) |
| { |
| const struct i2c_gecko_config *config = dev->config; |
| uint32_t bitrate_cfg; |
| int error; |
| |
| CMU_ClockEnable(config->clock, true); |
| |
| i2c_gecko_config_pins(dev, &config->pin_sda, &config->pin_scl); |
| |
| bitrate_cfg = i2c_map_dt_bitrate(config->bitrate); |
| |
| error = i2c_gecko_configure(dev, I2C_MODE_MASTER | bitrate_cfg); |
| if (error) { |
| return error; |
| } |
| |
| return 0; |
| } |
| |
| static const struct i2c_driver_api i2c_gecko_driver_api = { |
| .configure = i2c_gecko_configure, |
| .transfer = i2c_gecko_transfer, |
| }; |
| |
| #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION |
| #define I2C_LOC_DATA(idx) \ |
| .loc_sda = DT_INST_PROP_BY_IDX(idx, location_sda, 0), \ |
| .loc_scl = DT_INST_PROP_BY_IDX(idx, location_scl, 0) |
| #define I2C_VALIDATE_LOC(idx) BUILD_ASSERT(true, "") |
| #else |
| #define I2C_VALIDATE_LOC(idx) \ |
| BUILD_ASSERT(DT_INST_PROP_BY_IDX(idx, location_sda, 0) \ |
| == DT_INST_PROP_BY_IDX(idx, location_scl, 0), \ |
| "DTS location-* properties must be equal") |
| #define I2C_LOC_DATA(idx) \ |
| .loc = DT_INST_PROP_BY_IDX(idx, location_scl, 0) |
| #endif |
| |
| #define I2C_INIT(idx) \ |
| I2C_VALIDATE_LOC(idx); \ |
| static const struct i2c_gecko_config i2c_gecko_config_##idx = { \ |
| .base = (I2C_TypeDef *)DT_INST_REG_ADDR(idx), \ |
| .clock = cmuClock_I2C##idx, \ |
| .pin_sda = {DT_INST_PROP_BY_IDX(idx, location_sda, 1), \ |
| DT_INST_PROP_BY_IDX(idx, location_sda, 2), gpioModeWiredAnd, 1}, \ |
| .pin_scl = {DT_INST_PROP_BY_IDX(idx, location_scl, 1), \ |
| DT_INST_PROP_BY_IDX(idx, location_scl, 2), gpioModeWiredAnd, 1}, \ |
| I2C_LOC_DATA(idx), \ |
| .bitrate = DT_INST_PROP(idx, clock_frequency), \ |
| }; \ |
| \ |
| static struct i2c_gecko_data i2c_gecko_data_##idx; \ |
| \ |
| I2C_DEVICE_DT_INST_DEFINE(idx, i2c_gecko_init, \ |
| NULL, \ |
| &i2c_gecko_data_##idx, &i2c_gecko_config_##idx, \ |
| POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ |
| &i2c_gecko_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(I2C_INIT) |