/* * Copyright (c) 2019 Brett Witherspoon * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ti_cc13xx_cc26xx_i2c #include #include #include #include #include #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL #include LOG_MODULE_REGISTER(i2c_cc13xx_cc26xx); #include #include #include #include #include #include "i2c-priv.h" struct i2c_cc13xx_cc26xx_data { struct k_sem lock; struct k_sem complete; volatile uint32_t error; #ifdef CONFIG_PM Power_NotifyObj postNotify; uint32_t dev_config; #endif }; struct i2c_cc13xx_cc26xx_config { uint32_t base; const struct pinctrl_dev_config *pcfg; }; static int i2c_cc13xx_cc26xx_transmit(const struct device *dev, const struct i2c_msg *msg, const uint8_t addr) { const struct i2c_cc13xx_cc26xx_config *config = dev->config; const uint32_t base = config->base; struct i2c_cc13xx_cc26xx_data *data = dev->data; I2CMasterSlaveAddrSet(base, addr, false); for (int i = 0; i < msg->len; i++) { uint32_t command = I2C_MCTRL_RUN; if (i == 0 && msg->flags & I2C_MSG_RESTART) { command |= I2C_MCTRL_START; } if (i == msg->len - 1 && msg->flags & I2C_MSG_STOP) { command |= I2C_MCTRL_STOP; } I2CMasterDataPut(base, msg->buf[i]); I2CMasterControl(base, command); k_sem_take(&data->complete, K_FOREVER); if (data->error != I2C_MASTER_ERR_NONE) { if ((command & I2C_MCTRL_STOP) == 0) { I2CMasterControl(base, I2C_MCTRL_STOP); } return -EIO; } } return 0; } static int i2c_cc13xx_cc26xx_receive(const struct device *dev, const struct i2c_msg *msg, const uint8_t addr) { struct i2c_cc13xx_cc26xx_data *data = dev->data; const struct i2c_cc13xx_cc26xx_config *config = dev->config; const uint32_t base = config->base; I2CMasterSlaveAddrSet(base, addr, true); for (int i = 0; i < msg->len; i++) { uint32_t command = I2C_MCTRL_RUN; if (i == 0 && msg->flags & I2C_MSG_RESTART) { command |= I2C_MCTRL_START; } if (i == msg->len - 1 && msg->flags & I2C_MSG_STOP) { command |= I2C_MCTRL_STOP; } else if (i < msg->len - 1) { command |= I2C_MCTRL_ACK; } I2CMasterControl(base, command); k_sem_take(&data->complete, K_FOREVER); if (data->error != I2C_MASTER_ERR_NONE) { if ((command & I2C_MCTRL_STOP) == 0) { I2CMasterControl(base, I2C_MCTRL_STOP); } return -EIO; } msg->buf[i] = I2CMasterDataGet(base); } return 0; } static int i2c_cc13xx_cc26xx_transfer(const struct device *dev, struct i2c_msg *msgs, const uint8_t num_msgs, const uint16_t addr) { struct i2c_cc13xx_cc26xx_data *data = dev->data; int ret = 0; if (num_msgs == 0) { return 0; } /* Always a start condition in the first message */ msgs[0].flags |= I2C_MSG_RESTART; k_sem_take(&data->lock, K_FOREVER); pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); for (int i = 0; i < num_msgs; i++) { /* Not supported by hardware */ if (msgs[i].flags & I2C_MSG_ADDR_10_BITS) { ret = -EIO; break; } /* Sending address without data is not supported */ if (msgs[i].len == 0) { ret = -EIO; break; } if ((msgs[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { ret = i2c_cc13xx_cc26xx_transmit(dev, &msgs[i], addr); } else { ret = i2c_cc13xx_cc26xx_receive(dev, &msgs[i], addr); } if (ret) { break; } } pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); k_sem_give(&data->lock); return ret; } #define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) static int i2c_cc13xx_cc26xx_configure(const struct device *dev, uint32_t dev_config) { const struct i2c_cc13xx_cc26xx_config *config = 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_CONTROLLER)) { 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(config->base, CPU_FREQ, fast); #ifdef CONFIG_PM struct i2c_cc13xx_cc26xx_data *data = dev->data; data->dev_config = dev_config; #endif return 0; } static void i2c_cc13xx_cc26xx_isr(const struct device *dev) { const struct i2c_cc13xx_cc26xx_config *config = dev->config; struct i2c_cc13xx_cc26xx_data *data = dev->data; const uint32_t base = config->base; if (I2CMasterIntStatus(base, true)) { I2CMasterIntClear(base); data->error = I2CMasterErr(base); k_sem_give(&data->complete); } } #ifdef CONFIG_PM /* * ======== postNotifyFxn ======== * Called by Power module when waking up the CPU from Standby. The i2c needs * to be reconfigured afterwards, unless Zephyr's device PM turned it off, in * which case it'd be responsible for turning it back on and reconfigure it. */ static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg) { const struct device *dev = (const struct device *)clientArg; const struct i2c_cc13xx_cc26xx_config *config = dev->config; struct i2c_cc13xx_cc26xx_data *data = dev->data; int ret = Power_NOTIFYDONE; int16_t res_id; /* Reconfigure the hardware if returning from sleep */ if (eventType == PowerCC26XX_AWAKE_STANDBY) { res_id = PowerCC26XX_PERIPH_I2C0; if (Power_getDependencyCount(res_id) != 0) { /* Reconfigure and enable I2C only if powered */ if (i2c_cc13xx_cc26xx_configure(dev, data->dev_config) != 0) { ret = Power_NOTIFYERROR; } I2CMasterIntEnable(config->base); } } return (ret); } #endif #ifdef CONFIG_PM_DEVICE static int i2c_cc13xx_cc26xx_pm_action(const struct device *dev, enum pm_device_action action) { const struct i2c_cc13xx_cc26xx_config *config = dev->config; struct i2c_cc13xx_cc26xx_data *data = dev->data; int ret = 0; switch (action) { case PM_DEVICE_ACTION_RESUME: Power_setDependency(PowerCC26XX_PERIPH_I2C0); ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (ret < 0) { return ret; } ret = i2c_cc13xx_cc26xx_configure(dev, data->dev_config); if (ret == 0) { I2CMasterIntEnable(config->base); } break; case PM_DEVICE_ACTION_SUSPEND: I2CMasterIntDisable(config->base); I2CMasterDisable(config->base); /* Reset pin type to default GPIO configuration */ ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); if (ret < 0) { return ret; } Power_releaseDependency(PowerCC26XX_PERIPH_I2C0); break; default: return -ENOTSUP; } return ret; } #endif /* CONFIG_PM_DEVICE */ static int i2c_cc13xx_cc26xx_init(const struct device *dev) { const struct i2c_cc13xx_cc26xx_config *config = dev->config; uint32_t cfg; int err; #ifdef CONFIG_PM struct i2c_cc13xx_cc26xx_data *data = dev->data; /* Set Power dependencies & constraints */ Power_setDependency(PowerCC26XX_PERIPH_I2C0); /* Register notification function */ Power_registerNotify(&data->postNotify, PowerCC26XX_AWAKE_STANDBY, postNotifyFxn, (uintptr_t)dev); #else /* Enable I2C power domain */ PRCMPowerDomainOn(PRCM_DOMAIN_SERIAL); /* Enable I2C peripheral clock */ PRCMPeripheralRunEnable(PRCM_PERIPH_I2C0); /* Enable in sleep mode until proper power management 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 (PRCMPowerDomainsAllOn(PRCM_DOMAIN_SERIAL) != PRCM_DOMAIN_POWER_ON) { continue; } #endif IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), i2c_cc13xx_cc26xx_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQN(0)); err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (err < 0) { LOG_ERR("Failed to configure pinctrl state"); return err; } cfg = i2c_map_dt_bitrate(DT_INST_PROP(0, clock_frequency)); err = i2c_cc13xx_cc26xx_configure(dev, cfg | I2C_MODE_CONTROLLER); if (err) { LOG_ERR("Failed to configure"); return err; } I2CMasterIntEnable(config->base); return 0; } static DEVICE_API(i2c, i2c_cc13xx_cc26xx_driver_api) = { .configure = i2c_cc13xx_cc26xx_configure, .transfer = i2c_cc13xx_cc26xx_transfer, #ifdef CONFIG_I2C_RTIO .iodev_submit = i2c_iodev_submit_fallback, #endif }; PINCTRL_DT_INST_DEFINE(0); static const struct i2c_cc13xx_cc26xx_config i2c_cc13xx_cc26xx_config = { .base = DT_INST_REG_ADDR(0), .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), }; 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 }; PM_DEVICE_DT_INST_DEFINE(0, i2c_cc13xx_cc26xx_pm_action); I2C_DEVICE_DT_INST_DEFINE(0, i2c_cc13xx_cc26xx_init, PM_DEVICE_DT_INST_GET(0), &i2c_cc13xx_cc26xx_data, &i2c_cc13xx_cc26xx_config, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, &i2c_cc13xx_cc26xx_driver_api);