/* * Copyright (c) 2018 Diego Sueiro * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT silabs_gecko_i2c #include #include #include #include #include #include #include #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL #include LOG_MODULE_REGISTER(i2c_gecko); #include "i2c-priv.h" #define DEV_CFG(dev) \ ((const struct i2c_gecko_config * const)(dev)->config) #define DEV_DATA(dev) \ ((struct i2c_gecko_data * const)(dev)->data) #define DEV_BASE(dev) \ ((I2C_TypeDef *)(DEV_CFG(dev))->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_CFG(dev); 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(dev); 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(dev); 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_CFG(dev); 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; \ \ DEVICE_DT_INST_DEFINE(idx, &i2c_gecko_init, \ device_pm_control_nop, \ &i2c_gecko_data_##idx, &i2c_gecko_config_##idx, \ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &i2c_gecko_driver_api); DT_INST_FOREACH_STATUS_OKAY(I2C_INIT)