You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
4.3 KiB
188 lines
4.3 KiB
/* |
|
* 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 <logging/log.h> |
|
LOG_MODULE_REGISTER(i2c_telink); |
|
|
|
#include <drivers/i2c.h> |
|
#include "i2c-priv.h" |
|
#include <drivers/pinmux.h> |
|
#include <dt-bindings/pinctrl/b91-pinctrl.h> |
|
|
|
/* I2C configuration structure */ |
|
struct i2c_b91_cfg { |
|
uint32_t bitrate; |
|
const uint32_t *pinctrl_list; |
|
size_t pinctrl_list_size; |
|
}; |
|
|
|
/* 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_MASTER)) { |
|
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 device *pinmux; |
|
const struct i2c_b91_cfg *cfg = dev->config; |
|
struct i2c_b91_data *data = dev->data; |
|
uint32_t dev_config = (I2C_MODE_MASTER | 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; |
|
} |
|
|
|
/* get pinmux driver */ |
|
pinmux = DEVICE_DT_GET(DT_NODELABEL(pinmux)); |
|
if (!device_is_ready(pinmux)) { |
|
return -ENODEV; |
|
} |
|
|
|
/* config pins */ |
|
for (int i = 0; i < cfg->pinctrl_list_size; i++) { |
|
pinmux_pin_set(pinmux, B91_PINMUX_GET_PIN(cfg->pinctrl_list[i]), |
|
B91_PINMUX_GET_FUNC(cfg->pinctrl_list[i])); |
|
} |
|
|
|
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) \ |
|
\ |
|
static const uint32_t i2c_pins_##inst[] = \ |
|
B91_PINMUX_DT_INST_GET_ARRAY(inst, 0); \ |
|
\ |
|
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), \ |
|
.pinctrl_list_size = ARRAY_SIZE(i2c_pins_##inst), \ |
|
.pinctrl_list = i2c_pins_##inst \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(inst, i2c_b91_init, \ |
|
NULL, \ |
|
&i2c_b91_data_##inst, \ |
|
&i2c_b91_cfg_##inst, \ |
|
POST_KERNEL, \ |
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ |
|
&i2c_b91_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(I2C_B91_INIT)
|
|
|