| /* |
| * Copyright (c) 2021 Telink Semiconductor |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT telink_b91_i2c |
| |
| #include "i2c.h" |
| #include "clock.h" |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(i2c_telink); |
| |
| #include <zephyr/drivers/i2c.h> |
| #include "i2c-priv.h" |
| #include <zephyr/drivers/pinctrl.h> |
| |
| /* I2C configuration structure */ |
| struct i2c_b91_cfg { |
| uint32_t bitrate; |
| const struct pinctrl_dev_config *pcfg; |
| }; |
| |
| /* I2C data structure */ |
| struct i2c_b91_data { |
| struct k_sem mutex; |
| }; |
| |
| /* API implementation: configure */ |
| static int i2c_b91_configure(const struct device *dev, uint32_t dev_config) |
| { |
| ARG_UNUSED(dev); |
| |
| uint32_t i2c_speed = 0u; |
| |
| /* check address size */ |
| if (dev_config & I2C_ADDR_10_BITS) { |
| LOG_ERR("10-bits address is not supported"); |
| return -ENOTSUP; |
| } |
| |
| /* check I2C Master/Slave configuration */ |
| if (!(dev_config & I2C_MODE_CONTROLLER)) { |
| LOG_ERR("I2C slave is not implemented"); |
| return -ENOTSUP; |
| } |
| |
| /* check i2c speed */ |
| switch (I2C_SPEED_GET(dev_config)) { |
| case I2C_SPEED_STANDARD: |
| i2c_speed = 100000u; |
| break; |
| |
| case I2C_SPEED_FAST: |
| i2c_speed = 400000U; |
| break; |
| |
| case I2C_SPEED_FAST_PLUS: |
| case I2C_SPEED_HIGH: |
| case I2C_SPEED_ULTRA: |
| default: |
| LOG_ERR("Unsupported I2C speed requested"); |
| return -ENOTSUP; |
| } |
| |
| /* init i2c */ |
| i2c_master_init(); |
| i2c_set_master_clk((unsigned char)(sys_clk.pclk * 1000 * 1000 / (4 * i2c_speed))); |
| |
| return 0; |
| } |
| |
| /* API implementation: transfer */ |
| static int i2c_b91_transfer(const struct device *dev, |
| struct i2c_msg *msgs, |
| uint8_t num_msgs, |
| uint16_t addr) |
| { |
| int status = 0; |
| uint8_t send_stop = 0; |
| struct i2c_b91_data *data = dev->data; |
| |
| /* get the mutex */ |
| k_sem_take(&data->mutex, K_FOREVER); |
| |
| /* loop through all messages */ |
| for (int i = 0; i < num_msgs; i++) { |
| /* check addr size */ |
| if (msgs[i].flags & I2C_MSG_ADDR_10_BITS) { |
| LOG_ERR("10-bits address is not supported"); |
| k_sem_give(&data->mutex); |
| return -ENOTSUP; |
| } |
| |
| /* config stop bit */ |
| send_stop = msgs[i].flags & I2C_MSG_STOP ? 1 : 0; |
| i2c_master_send_stop(send_stop); |
| |
| /* transfer data */ |
| if (msgs[i].flags & I2C_MSG_READ) { |
| status = i2c_master_read(addr, msgs[i].buf, msgs[i].len); |
| } else { |
| status = i2c_master_write(addr, msgs[i].buf, msgs[i].len); |
| } |
| |
| /* check status */ |
| if (!status) { |
| LOG_ERR("Failed to transfer I2C messages\n"); |
| k_sem_give(&data->mutex); |
| return -EIO; |
| } |
| } |
| |
| /* release the mutex */ |
| k_sem_give(&data->mutex); |
| |
| return 0; |
| }; |
| |
| /* API implementation: init */ |
| static int i2c_b91_init(const struct device *dev) |
| { |
| int status = 0; |
| const struct i2c_b91_cfg *cfg = dev->config; |
| struct i2c_b91_data *data = dev->data; |
| uint32_t dev_config = (I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(cfg->bitrate)); |
| |
| /* init mutex */ |
| k_sem_init(&data->mutex, 1, 1); |
| |
| /* config i2c on startup */ |
| status = i2c_b91_configure(dev, dev_config); |
| if (status != 0) { |
| LOG_ERR("Failed to configure I2C on init"); |
| return status; |
| } |
| |
| /* configure pins */ |
| status = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); |
| if (status < 0) { |
| LOG_ERR("Failed to configure I2C pins"); |
| return status; |
| } |
| |
| return 0; |
| } |
| |
| /* I2C driver APIs structure */ |
| static const struct i2c_driver_api i2c_b91_api = { |
| .configure = i2c_b91_configure, |
| .transfer = i2c_b91_transfer, |
| }; |
| |
| BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1, |
| "unsupported I2C instance"); |
| |
| /* I2C driver registration */ |
| #define I2C_B91_INIT(inst) \ |
| \ |
| PINCTRL_DT_INST_DEFINE(inst); \ |
| \ |
| static struct i2c_b91_data i2c_b91_data_##inst; \ |
| \ |
| static struct i2c_b91_cfg i2c_b91_cfg_##inst = { \ |
| .bitrate = DT_INST_PROP(inst, clock_frequency), \ |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ |
| }; \ |
| \ |
| I2C_DEVICE_DT_INST_DEFINE(inst, i2c_b91_init, \ |
| NULL, \ |
| &i2c_b91_data_##inst, \ |
| &i2c_b91_cfg_##inst, \ |
| POST_KERNEL, \ |
| CONFIG_I2C_INIT_PRIORITY, \ |
| &i2c_b91_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(I2C_B91_INIT) |