drivers: i2c: initial device driver for ENE KB1200
Add i2c driver for ENE KB1200 Signed-off-by: Steven Chang <steven@ene.com.tw>
This commit is contained in:
parent
b27fac08de
commit
b231905e38
|
@ -63,6 +63,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_XILINX_AXI i2c_xilinx_axi.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_I2C_MCHP_MSS i2c_mchp_mss.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_SEDI i2c_sedi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_AMBIQ i2c_ambiq.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_ENE_KB1200 i2c_ene_kb1200.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_I2C_SWITCH gpio_i2c_switch.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_NUMAKER i2c_numaker.c)
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ source "drivers/i2c/Kconfig.sedi"
|
|||
source "drivers/i2c/Kconfig.ambiq"
|
||||
source "drivers/i2c/Kconfig.numaker"
|
||||
source "drivers/i2c/Kconfig.mcux"
|
||||
source "drivers/i2c/Kconfig.ene"
|
||||
|
||||
config I2C_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
|
|
10
drivers/i2c/Kconfig.ene
Normal file
10
drivers/i2c/Kconfig.ene
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Copyright (c) 2024 ENE Technology Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config I2C_ENE_KB1200
|
||||
bool "ENE KB1200 I2C driver"
|
||||
default y
|
||||
depends on DT_HAS_ENE_KB1200_I2C_ENABLED
|
||||
select PINCTRL
|
||||
help
|
||||
Enable the ENE KB1200 I2C driver.
|
354
drivers/i2c/i2c_ene_kb1200.c
Normal file
354
drivers/i2c/i2c_ene_kb1200.c
Normal file
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Copyright (c) 2024 ENE Technology Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ene_kb1200_i2c
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <errno.h>
|
||||
#include <reg/fsmbm.h>
|
||||
|
||||
struct i2c_kb1200_config {
|
||||
struct fsmbm_regs *fsmbm;
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
};
|
||||
|
||||
struct i2c_kb1200_data {
|
||||
struct k_sem mutex;
|
||||
volatile uint8_t *msg_buf;
|
||||
volatile uint32_t msg_len;
|
||||
volatile uint8_t msg_flags;
|
||||
volatile int state;
|
||||
volatile uint32_t index;
|
||||
volatile int err_code;
|
||||
};
|
||||
|
||||
/* I2C Master local functions */
|
||||
static void i2c_kb1200_isr(const struct device *dev)
|
||||
{
|
||||
const struct i2c_kb1200_config *config = dev->config;
|
||||
struct i2c_kb1200_data *data = dev->data;
|
||||
|
||||
if (data->state == STATE_SENDING) {
|
||||
if (config->fsmbm->FSMBMPF & FSMBM_BLOCK_FINISH_EVENT) {
|
||||
/* continue block */
|
||||
uint32_t remain = data->msg_len - data->index;
|
||||
uint32_t send_bytes =
|
||||
remain > FSMBM_BUFFER_SIZE ? FSMBM_BUFFER_SIZE : remain;
|
||||
memcpy((void *)&config->fsmbm->FSMBMDAT[0],
|
||||
(void *)&data->msg_buf[data->index], send_bytes);
|
||||
data->index += send_bytes;
|
||||
/* Increase CNT setting let hw can't match counter */
|
||||
config->fsmbm->FSMBMPRTC_C += send_bytes;
|
||||
/* If it was the last protocol recover the correct length value*/
|
||||
if (data->msg_len == data->index) {
|
||||
config->fsmbm->FSMBMPRTC_C -= 1;
|
||||
}
|
||||
config->fsmbm->FSMBMPF = FSMBM_BLOCK_FINISH_EVENT;
|
||||
} else if (config->fsmbm->FSMBMPF & FSMBM_COMPLETE_EVENT) {
|
||||
/* complete */
|
||||
if (((config->fsmbm->FSMBMSTS & FSMBM_STS_MASK) == FSMBM_SMBUS_BUSY) &&
|
||||
((config->fsmbm->FSMBMFRT & ___STOP) == ___NONE)) {
|
||||
/* while packet finish without STOP, the error message is
|
||||
* FSMBM_SMBUS_BUSY
|
||||
*/
|
||||
data->err_code = 0;
|
||||
} else {
|
||||
data->err_code = config->fsmbm->FSMBMSTS & FSMBM_STS_MASK;
|
||||
}
|
||||
data->state = STATE_COMPLETE;
|
||||
config->fsmbm->FSMBMPF = FSMBM_COMPLETE_EVENT;
|
||||
} else {
|
||||
data->err_code = config->fsmbm->FSMBMSTS & FSMBM_STS_MASK;
|
||||
data->state = STATE_COMPLETE;
|
||||
}
|
||||
} else if (data->state == STATE_RECEIVING) {
|
||||
uint32_t remain = data->msg_len - data->index;
|
||||
uint32_t receive_bytes = (remain > FSMBM_BUFFER_SIZE) ? FSMBM_BUFFER_SIZE : remain;
|
||||
|
||||
memcpy((void *)&data->msg_buf[data->index], (void *)&config->fsmbm->FSMBMDAT[0],
|
||||
receive_bytes);
|
||||
data->index += receive_bytes;
|
||||
if (config->fsmbm->FSMBMPF & FSMBM_BLOCK_FINISH_EVENT) {
|
||||
/* continue block */
|
||||
/* Check next protocl information */
|
||||
remain = data->msg_len - data->index;
|
||||
uint32_t NextLen =
|
||||
(remain > FSMBM_BUFFER_SIZE) ? FSMBM_BUFFER_SIZE : remain;
|
||||
/* Increase CNT setting let hw can't match counter */
|
||||
config->fsmbm->FSMBMPRTC_C += NextLen;
|
||||
/* If it was the last protocol recover the correct length value */
|
||||
if (data->msg_len == (data->index + NextLen)) {
|
||||
config->fsmbm->FSMBMPRTC_C -= 1;
|
||||
}
|
||||
config->fsmbm->FSMBMPF = FSMBM_BLOCK_FINISH_EVENT;
|
||||
} else if (config->fsmbm->FSMBMPF & FSMBM_COMPLETE_EVENT) {
|
||||
/* complete */
|
||||
if (((config->fsmbm->FSMBMSTS & FSMBM_STS_MASK) == FSMBM_SMBUS_BUSY) &&
|
||||
((config->fsmbm->FSMBMFRT & ___STOP) == ___NONE)) {
|
||||
/* while packet finish without STOP, the error message is
|
||||
* FSMBM_SMBUS_BUSY
|
||||
*/
|
||||
data->err_code = 0;
|
||||
} else {
|
||||
data->err_code = config->fsmbm->FSMBMSTS & FSMBM_STS_MASK;
|
||||
}
|
||||
data->state = STATE_COMPLETE;
|
||||
config->fsmbm->FSMBMPF = FSMBM_COMPLETE_EVENT;
|
||||
} else {
|
||||
data->err_code = config->fsmbm->FSMBMSTS & FSMBM_STS_MASK;
|
||||
data->state = STATE_COMPLETE;
|
||||
}
|
||||
} else if (data->state == STATE_COMPLETE) {
|
||||
config->fsmbm->FSMBMPF = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
}
|
||||
}
|
||||
|
||||
static int i2c_kb1200_poll_write(const struct device *dev, struct i2c_msg msg, uint16_t addr)
|
||||
{
|
||||
const struct i2c_kb1200_config *config = dev->config;
|
||||
struct i2c_kb1200_data *data = dev->data;
|
||||
uint8_t send_bytes;
|
||||
|
||||
if (msg.flags & I2C_MSG_STOP) {
|
||||
/* No CMD, No CNT, No PEC, with STOP*/
|
||||
config->fsmbm->FSMBMFRT = ___STOP;
|
||||
} else {
|
||||
/* No CMD, No CNT, No PEC, no STOP*/
|
||||
config->fsmbm->FSMBMFRT = ___NONE;
|
||||
}
|
||||
data->msg_len = msg.len;
|
||||
data->msg_buf = msg.buf;
|
||||
data->msg_flags = msg.flags;
|
||||
data->state = STATE_IDLE;
|
||||
data->index = 0;
|
||||
data->err_code = 0;
|
||||
|
||||
send_bytes = (msg.len > FSMBM_BUFFER_SIZE) ? FSMBM_BUFFER_SIZE : msg.len;
|
||||
memcpy((void *)&config->fsmbm->FSMBMDAT[0], (void *)&data->msg_buf[data->index],
|
||||
send_bytes);
|
||||
data->index += send_bytes;
|
||||
data->state = STATE_SENDING;
|
||||
|
||||
config->fsmbm->FSMBMCMD = 0;
|
||||
config->fsmbm->FSMBMADR = (addr & ~BIT(0)) | FSMBM_WRITE;
|
||||
config->fsmbm->FSMBMPF = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
/* If data over bufferSize increase 1 to force continue transmit */
|
||||
if (msg.len >= (FSMBM_BUFFER_SIZE + 1)) {
|
||||
config->fsmbm->FSMBMPRTC_C = FSMBM_BUFFER_SIZE + 1;
|
||||
} else {
|
||||
config->fsmbm->FSMBMPRTC_C = send_bytes;
|
||||
}
|
||||
config->fsmbm->FSMBMIE = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
config->fsmbm->FSMBMPRTC_P = FLEXIBLE_PROTOCOL;
|
||||
while (data->state != STATE_COMPLETE)
|
||||
;
|
||||
data->state = STATE_IDLE;
|
||||
if (data->err_code != 0) {
|
||||
/* reset HW */
|
||||
config->fsmbm->FSMBMCFG |= FSMBM_HW_RESET;
|
||||
return data->err_code;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_kb1200_poll_read(const struct device *dev, struct i2c_msg msg, uint16_t addr)
|
||||
{
|
||||
const struct i2c_kb1200_config *config = dev->config;
|
||||
struct i2c_kb1200_data *data = dev->data;
|
||||
|
||||
if (msg.flags & I2C_MSG_STOP) {
|
||||
/* No CMD, No CNT, No PEC, with STOP*/
|
||||
config->fsmbm->FSMBMFRT = ___STOP;
|
||||
} else {
|
||||
/* No CMD, No CNT, No PEC, no STOP*/
|
||||
config->fsmbm->FSMBMFRT = ___NONE;
|
||||
}
|
||||
data->msg_len = msg.len;
|
||||
data->msg_buf = msg.buf;
|
||||
data->msg_flags = msg.flags;
|
||||
data->state = STATE_IDLE;
|
||||
data->index = 0;
|
||||
data->err_code = 0;
|
||||
data->state = STATE_RECEIVING;
|
||||
|
||||
config->fsmbm->FSMBMCMD = 0;
|
||||
config->fsmbm->FSMBMADR = (addr & ~BIT(0)) | FSMBM_READ;
|
||||
config->fsmbm->FSMBMPF = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
/* If data over bufferSize increase 1 to force continue receive */
|
||||
if (msg.len >= (FSMBM_BUFFER_SIZE + 1)) {
|
||||
config->fsmbm->FSMBMPRTC_C = FSMBM_BUFFER_SIZE + 1;
|
||||
} else {
|
||||
config->fsmbm->FSMBMPRTC_C = msg.len;
|
||||
}
|
||||
config->fsmbm->FSMBMIE = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
config->fsmbm->FSMBMPRTC_P = FLEXIBLE_PROTOCOL;
|
||||
while (data->state != STATE_COMPLETE)
|
||||
;
|
||||
data->state = STATE_IDLE;
|
||||
if (data->err_code != 0) {
|
||||
/* reset HW */
|
||||
config->fsmbm->FSMBMCFG |= FSMBM_HW_RESET;
|
||||
return data->err_code;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C Master api functions */
|
||||
static int i2c_kb1200_configure(const struct device *dev, uint32_t dev_config)
|
||||
{
|
||||
const struct i2c_kb1200_config *config = dev->config;
|
||||
|
||||
if (!(dev_config & I2C_MODE_CONTROLLER)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (dev_config & I2C_ADDR_10_BITS) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uint32_t speed = I2C_SPEED_GET(dev_config);
|
||||
|
||||
switch (speed) {
|
||||
case I2C_SPEED_STANDARD:
|
||||
config->fsmbm->FSMBMCFG = (FSMBM_CLK_100K << FSMBM_CLK_POS);
|
||||
break;
|
||||
case I2C_SPEED_FAST:
|
||||
config->fsmbm->FSMBMCFG = (FSMBM_CLK_400K << FSMBM_CLK_POS);
|
||||
break;
|
||||
case I2C_SPEED_FAST_PLUS:
|
||||
config->fsmbm->FSMBMCFG = (FSMBM_CLK_1M << FSMBM_CLK_POS);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config->fsmbm->FSMBMPF = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
config->fsmbm->FSMBMIE = (FSMBM_COMPLETE_EVENT | FSMBM_BLOCK_FINISH_EVENT);
|
||||
/* HW reset, Enable FSMBM function, Timeout function*/
|
||||
config->fsmbm->FSMBMCFG |= FSMBM_HW_RESET | FSMBM_TIMEOUT_ENABLE | FSMBM_FUNCTION_ENABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_kb1200_get_config(const struct device *dev, uint32_t *dev_config)
|
||||
{
|
||||
const struct i2c_kb1200_config *config = dev->config;
|
||||
|
||||
if ((config->fsmbm->FSMBMCFG & FSMBM_FUNCTION_ENABLE) == 0x00) {
|
||||
printk("Cannot find i2c controller on 0x%p!\n", config->fsmbm);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch ((config->fsmbm->FSMBMCFG >> FSMBM_CLK_POS) & FSMBM_CLK_MASK) {
|
||||
case FSMBM_CLK_100K:
|
||||
*dev_config = I2C_MODE_CONTROLLER | I2C_SPEED_SET(I2C_SPEED_STANDARD);
|
||||
break;
|
||||
case FSMBM_CLK_400K:
|
||||
*dev_config = I2C_MODE_CONTROLLER | I2C_SPEED_SET(I2C_SPEED_FAST);
|
||||
break;
|
||||
case FSMBM_CLK_1M:
|
||||
*dev_config = I2C_MODE_CONTROLLER | I2C_SPEED_SET(I2C_SPEED_FAST_PLUS);
|
||||
break;
|
||||
default:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_kb1200_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
|
||||
uint16_t addr)
|
||||
{
|
||||
struct i2c_kb1200_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
/* get the mutex */
|
||||
k_sem_take(&data->mutex, K_FOREVER);
|
||||
for (int i = 0U; i < num_msgs; i++) {
|
||||
if ((msgs[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
|
||||
ret = i2c_kb1200_poll_write(dev, msgs[i], addr);
|
||||
if (ret) {
|
||||
printk("%s Write error: 0x%X\n", dev->name, ret);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = i2c_kb1200_poll_read(dev, msgs[i], addr);
|
||||
if (ret) {
|
||||
printk("%s Read error: 0x%X\n", dev->name, ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* release the mutex */
|
||||
k_sem_give(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* I2C Master driver registration */
|
||||
static const struct i2c_driver_api i2c_kb1200_api = {
|
||||
.configure = i2c_kb1200_configure,
|
||||
.get_config = i2c_kb1200_get_config,
|
||||
.transfer = i2c_kb1200_transfer,
|
||||
};
|
||||
|
||||
#define KB1200_FSMBM_DEV(inst) DEVICE_DT_INST_GET(inst),
|
||||
static const struct device *const fsmbm_devices[] = {DT_INST_FOREACH_STATUS_OKAY(KB1200_FSMBM_DEV)};
|
||||
static void i2c_kb1200_isr_wrap(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(fsmbm_devices); i++) {
|
||||
const struct device *dev_ = fsmbm_devices[i];
|
||||
const struct i2c_kb1200_config *config = dev_->config;
|
||||
|
||||
if (config->fsmbm->FSMBMIE & config->fsmbm->FSMBMPF) {
|
||||
i2c_kb1200_isr(dev_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool init_irq = true;
|
||||
static void kb1200_fsmbm_irq_init(void)
|
||||
{
|
||||
if (init_irq) {
|
||||
init_irq = false;
|
||||
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), i2c_kb1200_isr_wrap, NULL,
|
||||
0);
|
||||
irq_enable(DT_INST_IRQN(0));
|
||||
}
|
||||
}
|
||||
|
||||
static int i2c_kb1200_init(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
const struct i2c_kb1200_config *config = dev->config;
|
||||
struct i2c_kb1200_data *data = dev->data;
|
||||
|
||||
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* init mutex */
|
||||
k_sem_init(&data->mutex, 1, 1);
|
||||
kb1200_fsmbm_irq_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define I2C_KB1200_DEVICE(inst) \
|
||||
PINCTRL_DT_INST_DEFINE(inst); \
|
||||
static struct i2c_kb1200_data i2c_kb1200_data_##inst; \
|
||||
static const struct i2c_kb1200_config i2c_kb1200_config_##inst = { \
|
||||
.fsmbm = (struct fsmbm_regs *)DT_INST_REG_ADDR(inst), \
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, &i2c_kb1200_init, NULL, &i2c_kb1200_data_##inst, \
|
||||
&i2c_kb1200_config_##inst, PRE_KERNEL_1, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &i2c_kb1200_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(I2C_KB1200_DEVICE)
|
Loading…
Reference in a new issue