378 lines
8.6 KiB
C
378 lines
8.6 KiB
C
/*
|
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2020-08-21 heyuanjie87 first version
|
|
* 2023-03-31 Vandoul formatting code.
|
|
*/
|
|
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include "board.h"
|
|
#include "i2c.h"
|
|
#include "gpiohs.h"
|
|
#include "utils.h"
|
|
#include "sleep.h"
|
|
#include "fpioa.h"
|
|
#ifdef RT_USING_I2C
|
|
|
|
#ifndef BSP_I2C0_SCL_PIN
|
|
#define BSP_I2C0_SCL_PIN 0
|
|
#endif
|
|
#ifndef BSP_I2C0_SDA_PIN
|
|
#define BSP_I2C0_SDA_PIN 1
|
|
#endif
|
|
#ifndef BSP_I2C1_SCL_PIN
|
|
#define BSP_I2C1_SCL_PIN 30
|
|
#endif
|
|
#ifndef BSP_I2C1_SDA_PIN
|
|
#define BSP_I2C1_SDA_PIN 31
|
|
#endif
|
|
#ifndef BSP_I2C2_SCL_PIN
|
|
#define BSP_I2C2_SCL_PIN 4
|
|
#endif
|
|
#ifndef BSP_I2C2_SDA_PIN
|
|
#define BSP_I2C2_SDA_PIN 5
|
|
#endif
|
|
|
|
static rt_err_t ki2c_send(
|
|
volatile i2c_t *i2c_adapter,
|
|
rt_uint8_t *send_buf,
|
|
rt_uint32_t send_buf_len)
|
|
{
|
|
rt_uint32_t fifo_len, index;
|
|
|
|
while (send_buf_len)
|
|
{
|
|
fifo_len = 8 - i2c_adapter->txflr;
|
|
fifo_len = send_buf_len < fifo_len ? send_buf_len : fifo_len;
|
|
for (index = 0; index < fifo_len; index++)
|
|
i2c_adapter->data_cmd = I2C_DATA_CMD_DATA(*send_buf++);
|
|
if (i2c_adapter->tx_abrt_source != 0)
|
|
{
|
|
while (i2c_adapter->status & I2C_STATUS_ACTIVITY); //
|
|
i2c_adapter->clr_intr = i2c_adapter->clr_intr; //
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
send_buf_len -= fifo_len;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t ki2c_recv(
|
|
volatile i2c_t *i2c_adapter,
|
|
rt_uint8_t *receive_buf,
|
|
rt_uint32_t receive_buf_len)
|
|
{
|
|
rt_uint32_t fifo_len, index;
|
|
rt_uint32_t rx_len = receive_buf_len;
|
|
|
|
while (receive_buf_len || rx_len)
|
|
{
|
|
fifo_len = i2c_adapter->rxflr;
|
|
fifo_len = rx_len < fifo_len ? rx_len : fifo_len;
|
|
for (index = 0; index < fifo_len; index++)
|
|
*receive_buf++ = (rt_uint8_t)i2c_adapter->data_cmd;
|
|
rx_len -= fifo_len;
|
|
fifo_len = 8 - i2c_adapter->txflr;
|
|
fifo_len = receive_buf_len < fifo_len ? receive_buf_len : fifo_len;
|
|
for (index = 0; index < fifo_len; index++)
|
|
i2c_adapter->data_cmd = I2C_DATA_CMD_CMD;
|
|
if (i2c_adapter->tx_abrt_source != 0)
|
|
return -RT_ERROR;
|
|
receive_buf_len -= fifo_len;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static void ki2c_setaddr(
|
|
volatile i2c_t *i2c_adapter,
|
|
rt_uint16_t addr,
|
|
int width)
|
|
{
|
|
i2c_adapter->tar = I2C_TAR_ADDRESS(addr) & I2C_TAR_ADDRESS_MASK;
|
|
|
|
if(width == 10)
|
|
{
|
|
i2c_adapter->tar |= I2C_TAR_10BITADDR_MASTER;
|
|
}
|
|
else
|
|
{
|
|
i2c_adapter->tar &= ~I2C_TAR_10BITADDR_MASTER;
|
|
}
|
|
|
|
}
|
|
|
|
static int ki2c_waittx(volatile i2c_t *i2c_adapter, int timeout_ms)
|
|
{
|
|
rt_tick_t start;
|
|
|
|
start = rt_tick_get();
|
|
while ((i2c_adapter->status & I2C_STATUS_ACTIVITY) || !(i2c_adapter->status & I2C_STATUS_TFE))
|
|
{
|
|
if (rt_tick_from_millisecond(rt_tick_get() - start) > timeout_ms)
|
|
break;
|
|
}
|
|
|
|
if (i2c_adapter->tx_abrt_source != 0)
|
|
return -RT_ERROR;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static void ki2c_clearerr(volatile i2c_t *i2c_adapter)
|
|
{
|
|
i2c_adapter->clr_tx_abrt = i2c_adapter->clr_tx_abrt;
|
|
}
|
|
|
|
static rt_ssize_t _i2c_mst_xfer(struct rt_i2c_bus_device *bus,
|
|
struct rt_i2c_msg msgs[],
|
|
rt_uint32_t num)
|
|
{
|
|
rt_ssize_t i;
|
|
i2c_t *kbus = (i2c_t *)bus->priv;
|
|
rt_err_t status;
|
|
int waittx = 0;
|
|
|
|
RT_ASSERT(bus != RT_NULL);
|
|
if(msgs[0].flags & RT_I2C_ADDR_10BIT)
|
|
{
|
|
ki2c_setaddr(kbus, msgs[0].addr, 10);
|
|
}
|
|
else
|
|
{
|
|
ki2c_setaddr(kbus, msgs[0].addr, 7);
|
|
}
|
|
|
|
|
|
ki2c_clearerr(kbus);
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
waittx = 0;
|
|
|
|
if (msgs[i].flags & RT_I2C_RD)
|
|
{
|
|
status = ki2c_recv(kbus, msgs[i].buf, msgs[i].len);
|
|
}
|
|
else
|
|
{
|
|
status = ki2c_send(kbus, msgs[i].buf, msgs[i].len);
|
|
waittx = 1;
|
|
}
|
|
|
|
if (status != RT_EOK)
|
|
{
|
|
goto _out;
|
|
}
|
|
}
|
|
|
|
if (waittx)
|
|
{
|
|
status = ki2c_waittx(kbus, 2000);
|
|
if (status != RT_EOK)
|
|
{
|
|
goto _out;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
_out:
|
|
return status;
|
|
}
|
|
|
|
static const struct rt_i2c_bus_device_ops i2c_ops =
|
|
{
|
|
.master_xfer = _i2c_mst_xfer,
|
|
.slave_xfer = RT_NULL,
|
|
.i2c_bus_control = RT_NULL,
|
|
};
|
|
|
|
#ifdef RT_USING_I2C_BITOPS
|
|
|
|
typedef struct pin_info_s {
|
|
uint32_t scl;
|
|
uint32_t sda;
|
|
} pin_info_t;
|
|
|
|
static void set_sda(void *data, rt_int32_t state)
|
|
{
|
|
pin_info_t *pin = (pin_info_t *)data;
|
|
/* state = 1: disable output. state = 0: enable output.*/
|
|
set_gpio_bit(gpiohs->output_en.u32, pin->sda, !state);
|
|
}
|
|
|
|
static void set_scl(void *data, rt_int32_t state)
|
|
{
|
|
pin_info_t *pin = (pin_info_t *)data;
|
|
/* state = 1: disable output. state = 0: enable output.*/
|
|
set_gpio_bit(gpiohs->output_en.u32, pin->scl, !state);
|
|
}
|
|
|
|
static rt_int32_t get_sda(void *data)
|
|
{
|
|
pin_info_t *pin = (pin_info_t *)data;
|
|
/* disable output.*/
|
|
set_gpio_bit(gpiohs->output_en.u32, pin->sda, 0);
|
|
|
|
return get_gpio_bit(gpiohs->input_val.u32, pin->sda);
|
|
}
|
|
|
|
static rt_int32_t get_scl(void *data)
|
|
{
|
|
pin_info_t *pin = (pin_info_t *)data;
|
|
/* disable output.*/
|
|
set_gpio_bit(gpiohs->output_en.u32, pin->scl, 0);
|
|
|
|
return get_gpio_bit(gpiohs->input_val.u32, pin->scl);
|
|
}
|
|
|
|
static void udelay(rt_uint32_t us)
|
|
{
|
|
usleep((uint64_t)us);
|
|
}
|
|
|
|
static struct rt_i2c_bit_ops bit_ops_0 =
|
|
{
|
|
RT_NULL,
|
|
set_sda,
|
|
set_scl,
|
|
get_sda,
|
|
get_scl,
|
|
udelay,
|
|
5,
|
|
5
|
|
};
|
|
|
|
static struct rt_i2c_bit_ops bit_ops_1 =
|
|
{
|
|
RT_NULL,
|
|
set_sda,
|
|
set_scl,
|
|
get_sda,
|
|
get_scl,
|
|
udelay,
|
|
5,
|
|
5
|
|
};
|
|
|
|
static struct rt_i2c_bit_ops bit_ops_2 =
|
|
{
|
|
RT_NULL,
|
|
set_sda,
|
|
set_scl,
|
|
get_sda,
|
|
get_scl,
|
|
udelay,
|
|
5,
|
|
5
|
|
};
|
|
|
|
extern int get_pin_channel(rt_base_t pin_index);
|
|
#endif
|
|
|
|
int rt_hw_i2c_init(void)
|
|
{
|
|
struct rt_i2c_bus_device *busdev;
|
|
|
|
#ifdef BSP_USING_I2C0
|
|
static struct rt_i2c_bus_device i2c_dev0;
|
|
busdev = &i2c_dev0;
|
|
|
|
#ifdef RT_USING_I2C_BITOPS
|
|
fpioa_set_function(BSP_I2C0_SCL_PIN, FUNC_RESV0);
|
|
fpioa_set_function(BSP_I2C0_SDA_PIN, FUNC_RESV0);
|
|
|
|
rt_pin_write(BSP_I2C0_SCL_PIN, PIN_LOW);
|
|
rt_pin_write(BSP_I2C0_SDA_PIN, PIN_LOW);
|
|
rt_pin_mode(BSP_I2C0_SCL_PIN, PIN_MODE_INPUT_PULLUP);
|
|
rt_pin_mode(BSP_I2C0_SDA_PIN, PIN_MODE_INPUT_PULLUP);
|
|
|
|
static pin_info_t pin0;
|
|
pin0.scl = get_pin_channel(BSP_I2C0_SCL_PIN);
|
|
pin0.sda = get_pin_channel(BSP_I2C0_SDA_PIN);
|
|
bit_ops_0.data = (void *)&pin0;
|
|
|
|
busdev->priv = (void *)&bit_ops_0;
|
|
rt_i2c_bit_add_bus(busdev, "i2c0");
|
|
#else
|
|
|
|
busdev->ops = &i2c_ops;
|
|
busdev->priv = (void *)I2C0_BASE_ADDR;
|
|
|
|
i2c_init(I2C_DEVICE_0, 0, 7, 100000);
|
|
rt_i2c_bus_device_register(busdev, "i2c0");
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef BSP_USING_I2C1
|
|
static struct rt_i2c_bus_device i2c_dev1;
|
|
busdev = &i2c_dev1;
|
|
|
|
#ifdef RT_USING_I2C_BITOPS
|
|
fpioa_set_function(BSP_I2C1_SCL_PIN, FUNC_RESV0);
|
|
fpioa_set_function(BSP_I2C1_SDA_PIN, FUNC_RESV0);
|
|
|
|
rt_pin_write(BSP_I2C1_SCL_PIN, PIN_LOW);
|
|
rt_pin_write(BSP_I2C1_SDA_PIN, PIN_LOW);
|
|
rt_pin_mode(BSP_I2C1_SCL_PIN, PIN_MODE_INPUT_PULLUP);
|
|
rt_pin_mode(BSP_I2C1_SDA_PIN, PIN_MODE_INPUT_PULLUP);
|
|
|
|
static pin_info_t pin1;
|
|
pin1.scl = get_pin_channel(BSP_I2C1_SCL_PIN);
|
|
pin1.sda = get_pin_channel(BSP_I2C1_SDA_PIN);
|
|
bit_ops_1.data = (void *)&pin1;
|
|
|
|
busdev->priv = (void *)&bit_ops_1;
|
|
rt_i2c_bit_add_bus(busdev, "i2c1");
|
|
#else
|
|
|
|
busdev->ops = &i2c_ops;
|
|
busdev->priv = (void *)I2C1_BASE_ADDR;
|
|
|
|
i2c_init(I2C_DEVICE_1, 0, 7, 100000);
|
|
rt_i2c_bus_device_register(busdev, "i2c1");
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef BSP_USING_I2C2
|
|
static struct rt_i2c_bus_device i2c_dev2;
|
|
busdev = &i2c_dev2;
|
|
|
|
#ifdef RT_USING_I2C_BITOPS
|
|
fpioa_set_function(BSP_I2C2_SCL_PIN, FUNC_RESV0);
|
|
fpioa_set_function(BSP_I2C2_SDA_PIN, FUNC_RESV0);
|
|
|
|
rt_pin_write(BSP_I2C2_SCL_PIN, PIN_LOW);
|
|
rt_pin_write(BSP_I2C2_SDA_PIN, PIN_LOW);
|
|
rt_pin_mode(BSP_I2C2_SCL_PIN, PIN_MODE_INPUT_PULLUP);
|
|
rt_pin_mode(BSP_I2C2_SDA_PIN, PIN_MODE_INPUT_PULLUP);
|
|
|
|
static pin_info_t pin2;
|
|
pin2.scl = get_pin_channel(BSP_I2C2_SCL_PIN);
|
|
pin2.sda = get_pin_channel(BSP_I2C2_SDA_PIN);
|
|
bit_ops_2.data = (void *)&pin2;
|
|
|
|
busdev->priv = (void *)&bit_ops_2;
|
|
rt_i2c_bit_add_bus(busdev, "i2c2");
|
|
#else
|
|
|
|
busdev->ops = &i2c_ops;
|
|
busdev->priv = (void *)I2C2_BASE_ADDR;
|
|
|
|
i2c_init(I2C_DEVICE_2, 0, 7, 100000);
|
|
rt_i2c_bus_device_register(busdev, "i2c2");
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
INIT_BOARD_EXPORT(rt_hw_i2c_init);
|
|
#endif
|