forked from xuos/xiuos
First commit XiUOS
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
if BSP_USING_I2C
|
||||
config BSP_I2C_SDA
|
||||
int "SDA pin number for I2C"
|
||||
default 15
|
||||
config BSP_I2C_SCL
|
||||
int "SCLK pin number for I2C"
|
||||
default 17
|
||||
config I2C_BUS_NAME_1
|
||||
string "i2c bus 1 name"
|
||||
default "i2c1"
|
||||
config I2C_DRV_NAME_1
|
||||
string "i2c bus 1 driver name"
|
||||
default "i2c1_drv"
|
||||
config I2C_1_DEVICE_NAME_0
|
||||
string "i2c bus 1 device 0 name"
|
||||
default "i2c1_dev0"
|
||||
endif
|
||||
@@ -0,0 +1,6 @@
|
||||
SRC_FILES := hardware_i2c.c connect_i2c.c
|
||||
|
||||
|
||||
|
||||
|
||||
include $(KERNEL_ROOT)/compiler.mk
|
||||
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
* Copyright (c) 2020 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2012-04-25 weety first version
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file connect_i2c.c
|
||||
* @brief support aiit-riscv64-board i2c function and register to bus framework
|
||||
* @version 1.0
|
||||
* @author AIIT XUOS Lab
|
||||
* @date 2021-04-25
|
||||
*/
|
||||
|
||||
/*************************************************
|
||||
File name: connect_i2c.c
|
||||
Description: support aiit-riscv64-board i2c configure and i2c bus register function
|
||||
Others: take RT-Thread v4.0.2/components/drivers/i2c/i2c-bit-ops.c for references
|
||||
https://github.com/RT-Thread/rt-thread/tree/v4.0.2
|
||||
History:
|
||||
1. Date: 2021-04-25
|
||||
Author: AIIT XUOS Lab
|
||||
Modification:
|
||||
1. support aiit-riscv64-board i2c bit configure, write and read
|
||||
2. support aiit-riscv64-board i2c bus device and driver register
|
||||
*************************************************/
|
||||
|
||||
#include <board.h>
|
||||
#include <connect_i2c.h>
|
||||
#include <fpioa.h>
|
||||
#include <gpio_common.h>
|
||||
#include <gpio.h>
|
||||
#include <sleep.h>
|
||||
#include <sysctl.h>
|
||||
|
||||
#ifndef BSP_USING_I2C1
|
||||
#define BSP_USING_I2C1
|
||||
#endif
|
||||
|
||||
#define I2C_SDA_FUNC_GPIO 3
|
||||
#define I2C_SCL_FUNC_GPIO 4
|
||||
|
||||
static I2cBusParam i2c_bus_param =
|
||||
{
|
||||
I2C_SDA_FUNC_GPIO,
|
||||
I2C_SCL_FUNC_GPIO,
|
||||
};
|
||||
|
||||
#define SET_SDA(done, val) done->SetSdaState(done->data, val)
|
||||
#define SET_SCL(done, val) done->SetSclState(done->data, val)
|
||||
#define GET_SDA(done) done->GetSdaState(done->data)
|
||||
#define GET_SCL(done) done->GetSclState(done->data)
|
||||
#define SdaLow(done) SET_SDA(done, 0)
|
||||
#define SdaHigh(done) SET_SDA(done, 1)
|
||||
#define SclLow(done) SET_SCL(done, 0)
|
||||
|
||||
void I2cGpioInit(const I2cBusParam *bus_param)
|
||||
{
|
||||
gpio_init ();
|
||||
FpioaSetFunction(BSP_I2C_SDA , FUNC_GPIO3 );//RISC-V FPIOA CFG
|
||||
FpioaSetFunction(BSP_I2C_SCL , FUNC_GPIO4 );//RISC-V FPIOA CFG
|
||||
gpio_set_drive_mode(bus_param->i2c_sda_pin , GPIO_DM_OUTPUT );
|
||||
gpio_set_drive_mode(bus_param->i2c_scl_pin, GPIO_DM_OUTPUT );
|
||||
gpio_set_pin(bus_param->i2c_sda_pin , GPIO_PV_HIGH );
|
||||
gpio_set_pin(bus_param->i2c_scl_pin , GPIO_PV_HIGH );
|
||||
}
|
||||
|
||||
static void SetSdaState(void *data, uint8 sda_state)
|
||||
{
|
||||
I2cBusParam *bus_param = (I2cBusParam *)data;
|
||||
if (sda_state) {
|
||||
gpio_set_drive_mode(bus_param->i2c_sda_pin, GPIO_DM_OUTPUT );
|
||||
gpio_set_pin(bus_param->i2c_sda_pin , GPIO_PV_HIGH );
|
||||
} else {
|
||||
gpio_set_drive_mode(bus_param->i2c_sda_pin, GPIO_DM_OUTPUT );
|
||||
gpio_set_pin(bus_param->i2c_sda_pin , GPIO_PV_LOW );
|
||||
}
|
||||
}
|
||||
|
||||
static void SetSclState(void *data, uint8 scl_state)
|
||||
{
|
||||
I2cBusParam *bus_param = (I2cBusParam *)data;
|
||||
if (scl_state) {
|
||||
gpio_set_drive_mode(bus_param->i2c_scl_pin, GPIO_DM_OUTPUT );
|
||||
gpio_set_pin(bus_param->i2c_scl_pin , GPIO_PV_HIGH );
|
||||
} else {
|
||||
gpio_set_drive_mode(bus_param->i2c_scl_pin, GPIO_DM_OUTPUT );
|
||||
gpio_set_pin(bus_param->i2c_scl_pin , GPIO_PV_LOW );
|
||||
}
|
||||
}
|
||||
|
||||
static uint8 GetSdaState(void *data)
|
||||
{
|
||||
I2cBusParam *bus_param = (I2cBusParam *)data;
|
||||
gpio_set_drive_mode (bus_param->i2c_sda_pin, GPIO_DM_INPUT_PULL_UP );
|
||||
return gpio_get_pin(bus_param->i2c_sda_pin);
|
||||
}
|
||||
|
||||
static uint8 GetSclState(void *data)
|
||||
{
|
||||
I2cBusParam *bus_param = (I2cBusParam *)data;
|
||||
gpio_set_drive_mode (bus_param->i2c_scl_pin, GPIO_DM_INPUT_PULL_UP );
|
||||
return gpio_get_pin(bus_param->i2c_scl_pin);
|
||||
}
|
||||
|
||||
static const struct I2cHalDrvDone i2c_hal_drv_done =
|
||||
{
|
||||
.data = (&i2c_bus_param),
|
||||
.SetSdaState = SetSdaState,
|
||||
.SetSclState = SetSclState,
|
||||
.GetSdaState = GetSdaState,
|
||||
.GetSclState = GetSclState,
|
||||
.udelay = usleep,
|
||||
.delay_us = 1,
|
||||
.timeout = 100
|
||||
};
|
||||
|
||||
static x_err_t I2cBusReset(const I2cBusParam *bus_param)
|
||||
{
|
||||
int32 i = 0;
|
||||
gpio_set_drive_mode(bus_param->i2c_sda_pin, GPIO_DM_INPUT_PULL_UP );
|
||||
if (GPIO_LOW == gpio_get_pin(bus_param->i2c_sda_pin)) {
|
||||
while (i++ < 9)
|
||||
{
|
||||
gpio_set_drive_mode(bus_param->i2c_scl_pin, GPIO_DM_OUTPUT );
|
||||
gpio_set_pin(bus_param->i2c_scl_pin , GPIO_PV_HIGH );
|
||||
usleep(100);
|
||||
gpio_set_pin(bus_param->i2c_scl_pin , GPIO_PV_LOW );
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
gpio_set_drive_mode(bus_param->i2c_sda_pin, GPIO_DM_INPUT_PULL_UP );
|
||||
if (GPIO_LOW == gpio_get_pin(bus_param->i2c_sda_pin)) {
|
||||
return -ERROR;
|
||||
}
|
||||
return EOK;
|
||||
}
|
||||
|
||||
static __inline void I2cDelay(struct I2cHalDrvDone *done)
|
||||
{
|
||||
done->udelay((done->delay_us + 1) >> 1);
|
||||
}
|
||||
|
||||
static __inline void I2cDelay2(struct I2cHalDrvDone *done)
|
||||
{
|
||||
done->udelay(done->delay_us);
|
||||
}
|
||||
|
||||
static x_err_t SclHigh(struct I2cHalDrvDone *done)
|
||||
{
|
||||
x_ticks_t start;
|
||||
|
||||
SET_SCL(done, 1);
|
||||
|
||||
if (!done->GetSclState)
|
||||
goto done;
|
||||
|
||||
start = CurrentTicksGain();
|
||||
while (!GET_SCL(done))
|
||||
{
|
||||
if ((CurrentTicksGain() - start) > done->timeout)
|
||||
return -ETIMEOUT;
|
||||
DelayKTask((done->timeout + 1) >> 1);
|
||||
}
|
||||
|
||||
done:
|
||||
I2cDelay(done);
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
static void I2cStart(struct I2cHalDrvDone *done)
|
||||
{
|
||||
SdaLow(done);
|
||||
I2cDelay(done);
|
||||
SclLow(done);
|
||||
}
|
||||
|
||||
static void I2cRestart(struct I2cHalDrvDone *done)
|
||||
{
|
||||
SdaHigh(done);
|
||||
SclHigh(done);
|
||||
I2cDelay(done);
|
||||
SdaLow(done);
|
||||
I2cDelay(done);
|
||||
SclLow(done);
|
||||
}
|
||||
|
||||
static void I2cStop(struct I2cHalDrvDone *done)
|
||||
{
|
||||
SdaLow(done);
|
||||
I2cDelay(done);
|
||||
SclHigh(done);
|
||||
I2cDelay(done);
|
||||
SdaHigh(done);
|
||||
I2cDelay2(done);
|
||||
}
|
||||
|
||||
static __inline x_bool I2cWaitack(struct I2cHalDrvDone *done)
|
||||
{
|
||||
x_bool ack;
|
||||
|
||||
SdaHigh(done);
|
||||
GET_SDA(done);
|
||||
I2cDelay(done);
|
||||
|
||||
if (SclHigh(done) < 0) {
|
||||
KPrintf("wait ack timeout");
|
||||
return -ETIMEOUT;
|
||||
}
|
||||
|
||||
ack = !GET_SDA(done);
|
||||
|
||||
SclLow(done);
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
static int32 I2cWriteb(struct I2cBus *bus, uint8 data)
|
||||
{
|
||||
int32 i;
|
||||
uint8 bit;
|
||||
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
|
||||
for (i = 7; i >= 0; i--)
|
||||
{
|
||||
SclLow(done);
|
||||
bit = (data >> i) & 1;
|
||||
SET_SDA(done, bit);
|
||||
I2cDelay(done);
|
||||
if (SclHigh(done) < 0) {
|
||||
KPrintf("I2cWriteb: 0x%02x, "
|
||||
"wait scl pin high timeout at bit %d",
|
||||
data, i);
|
||||
|
||||
return -ETIMEOUT;
|
||||
}
|
||||
}
|
||||
SclLow(done);
|
||||
I2cDelay(done);
|
||||
|
||||
return I2cWaitack(done);
|
||||
}
|
||||
|
||||
static int32 I2cReadb(struct I2cBus *bus)
|
||||
{
|
||||
uint8 i;
|
||||
uint8 data = 0;
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
|
||||
SdaHigh(done);
|
||||
GET_SDA(done);
|
||||
I2cDelay(done);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
data <<= 1;
|
||||
|
||||
if (SclHigh(done) < 0) {
|
||||
KPrintf("I2cReadb: wait scl pin high "
|
||||
"timeout at bit %d", 7 - i);
|
||||
|
||||
return -ETIMEOUT;
|
||||
}
|
||||
|
||||
if (GET_SDA(done))
|
||||
data |= 1;
|
||||
SclLow(done);
|
||||
I2cDelay2(done);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static x_size_t I2cSendBytes(struct I2cBus *bus, struct I2cDataStandard *msg)
|
||||
{
|
||||
int32 ret;
|
||||
x_size_t bytes = 0;
|
||||
const uint8 *ptr = msg->buf;
|
||||
int32 count = msg->len;
|
||||
uint16 ignore_nack = msg->flags & I2C_IGNORE_NACK;
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
ret = I2cWriteb(bus, *ptr);
|
||||
|
||||
if ((ret > 0) || (ignore_nack && (ret == 0))) {
|
||||
count --;
|
||||
ptr ++;
|
||||
bytes ++;
|
||||
} else if (ret == 0) {
|
||||
//KPrintf("send bytes: NACK.");
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
KPrintf("send bytes: error %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static x_err_t I2cSendAckOrNack(struct I2cBus *bus, int ack)
|
||||
{
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
|
||||
if (ack)
|
||||
SET_SDA(done, 0);
|
||||
I2cDelay(done);
|
||||
if (SclHigh(done) < 0) {
|
||||
KPrintf("ACK or NACK timeout.");
|
||||
|
||||
return -ETIMEOUT;
|
||||
}
|
||||
SclLow(done);
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
static x_size_t I2cRecvBytes(struct I2cBus *bus, struct I2cDataStandard *msg)
|
||||
{
|
||||
int32 val;
|
||||
int32 bytes = 0;
|
||||
uint8 *ptr = msg->buf;
|
||||
int32 count = msg->len;
|
||||
const uint32 flags = msg->flags;
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
val = I2cReadb(bus);
|
||||
if (val >= 0) {
|
||||
*ptr = val;
|
||||
bytes ++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
ptr ++;
|
||||
count --;
|
||||
|
||||
if (!(flags & I2C_NO_READ_ACK)) {
|
||||
val = I2cSendAckOrNack(bus, count);
|
||||
if (val < 0)
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int32 I2cSendAddress(struct I2cBus *bus, uint8 addr, int32 retries)
|
||||
{
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
int32 i;
|
||||
x_err_t ret = 0;
|
||||
|
||||
for (i = 0; i <= retries; i++)
|
||||
{
|
||||
ret = I2cWriteb(bus, addr);
|
||||
if (ret == 1 || i == retries)
|
||||
break;
|
||||
I2cStop(done);
|
||||
I2cDelay2(done);
|
||||
I2cStart(done);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static x_err_t I2cBitSendAddress(struct I2cBus *bus, struct I2cDataStandard *msg)
|
||||
{
|
||||
uint16 flags = msg->flags;
|
||||
uint16 ignore_nack = msg->flags & I2C_IGNORE_NACK;
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
|
||||
uint8 addr1, addr2;
|
||||
int32 retries;
|
||||
x_err_t ret;
|
||||
|
||||
retries = ignore_nack ? 0 : msg->retries;
|
||||
|
||||
if (flags & I2C_ADDR_10BIT) {
|
||||
addr1 = 0xf0 | ((msg->addr >> 7) & 0x06);
|
||||
addr2 = msg->addr & 0xff;
|
||||
|
||||
ret = I2cSendAddress(bus, addr1, retries);
|
||||
if ((ret != 1) && !ignore_nack) {
|
||||
KPrintf("NACK: sending first addr");
|
||||
|
||||
return -EPIO;
|
||||
}
|
||||
|
||||
ret = I2cWriteb(bus, addr2);
|
||||
if ((ret != 1) && !ignore_nack) {
|
||||
//KPrintf("NACK: sending second addr");
|
||||
|
||||
return -EPIO;
|
||||
}
|
||||
|
||||
if (flags & I2C_RD) {
|
||||
I2cRestart(done);
|
||||
addr1 |= 0x01;
|
||||
ret = I2cSendAddress(bus, addr1, retries);
|
||||
if ((ret != 1) && !ignore_nack) {
|
||||
|
||||
return -EPIO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addr1 = msg->addr << 1;
|
||||
if (flags & I2C_RD)
|
||||
addr1 |= 1;
|
||||
ret = I2cSendAddress(bus, addr1, retries);
|
||||
if ((ret != 1) && !ignore_nack)
|
||||
return -EPIO;
|
||||
}
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
static uint32 I2cWriteData(struct I2cHardwareDevice *i2c_dev, struct I2cDataStandard *msg)
|
||||
{
|
||||
struct I2cBus *bus = (struct I2cBus *)i2c_dev->haldev.owner_bus;
|
||||
bus->private_data = i2c_dev->haldev.owner_bus->private_data;
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
int32 ret;
|
||||
int32 i = 0;
|
||||
uint16 ignore_nack;
|
||||
|
||||
I2cStart(done);
|
||||
while(NONE != msg)
|
||||
{
|
||||
ignore_nack = msg->flags & I2C_IGNORE_NACK;
|
||||
if (!(msg->flags & I2C_NO_START)) {
|
||||
if (i) {
|
||||
I2cRestart(done);
|
||||
}
|
||||
ret = I2cBitSendAddress(bus, msg);
|
||||
if ((ret != EOK) && !ignore_nack) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->flags & I2C_WR) {
|
||||
ret = I2cSendBytes(bus, msg);
|
||||
if (ret >= 1)
|
||||
//KPrintf("write %d byte%s", ret, ret == 1 ? "" : "s");
|
||||
if (ret < msg->len) {
|
||||
if (ret >= 0)
|
||||
ret = -ERROR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
msg = msg->next;
|
||||
i++;
|
||||
}
|
||||
ret = i;
|
||||
|
||||
out:
|
||||
I2cStop(done);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32 I2cReadData(struct I2cHardwareDevice *i2c_dev, struct I2cDataStandard *msg)
|
||||
{
|
||||
struct I2cBus *bus = (struct I2cBus *)i2c_dev->haldev.owner_bus;
|
||||
bus->private_data = i2c_dev->haldev.owner_bus->private_data;
|
||||
struct I2cHalDrvDone *done = (struct I2cHalDrvDone *)bus->private_data;
|
||||
int32 ret;
|
||||
int32 i = 0;
|
||||
uint16 ignore_nack;
|
||||
|
||||
I2cStart(done);
|
||||
while (NONE != msg)
|
||||
{
|
||||
ignore_nack = msg->flags & I2C_IGNORE_NACK;
|
||||
if (!(msg->flags & I2C_NO_START)) {
|
||||
if (i) {
|
||||
I2cRestart(done);
|
||||
}
|
||||
ret = I2cBitSendAddress(bus, msg);
|
||||
if ((ret != EOK) && !ignore_nack) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->flags & I2C_RD) {
|
||||
ret = I2cRecvBytes(bus, msg);
|
||||
if (ret >= 1)
|
||||
//KPrintf("read %d byte%s", ret, ret == 1 ? "" : "s");
|
||||
if (ret < msg->len) {
|
||||
if (ret >= 0)
|
||||
ret = -EPIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
msg = msg->next;
|
||||
i++;
|
||||
}
|
||||
ret = i;
|
||||
|
||||
out:
|
||||
I2cStop(done);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*manage the i2c device operations*/
|
||||
static const struct I2cDevDone i2c_dev_done =
|
||||
{
|
||||
.open = NONE,
|
||||
.close = NONE,
|
||||
.write = I2cWriteData,
|
||||
.read = I2cReadData,
|
||||
};
|
||||
|
||||
/*Init i2c bus*/
|
||||
static int BoardI2cBusInit(struct I2cBus *i2c_bus, struct I2cDriver *i2c_driver)
|
||||
{
|
||||
x_err_t ret = EOK;
|
||||
|
||||
/*Init the i2c bus */
|
||||
i2c_bus->private_data = (void *)&i2c_hal_drv_done;
|
||||
ret = I2cBusInit(i2c_bus, I2C_BUS_NAME_1);
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_init I2cBusInit error %d\n", ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/*Init the i2c driver*/
|
||||
i2c_driver->private_data = (void *)&i2c_hal_drv_done;
|
||||
ret = I2cDriverInit(i2c_driver, I2C_DRV_NAME_1);
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_init I2cDriverInit error %d\n", ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/*Attach the i2c driver to the i2c bus*/
|
||||
ret = I2cDriverAttachToBus(I2C_DRV_NAME_1, I2C_BUS_NAME_1);
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_init I2cDriverAttachToBus error %d\n", ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*Attach the i2c device to the i2c bus*/
|
||||
static int BoardI2cDevBend(void)
|
||||
{
|
||||
x_err_t ret = EOK;
|
||||
static struct I2cHardwareDevice i2c_device0;
|
||||
memset(&i2c_device0, 0, sizeof(struct I2cHardwareDevice));
|
||||
|
||||
i2c_device0.i2c_dev_done = &i2c_dev_done;
|
||||
|
||||
ret = I2cDeviceRegister(&i2c_device0, NONE, I2C_1_DEVICE_NAME_0);
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_init I2cDeviceInit device %s error %d\n", I2C_1_DEVICE_NAME_0, ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
ret = I2cDeviceAttachToBus(I2C_1_DEVICE_NAME_0, I2C_BUS_NAME_1);
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_init I2cDeviceAttachToBus device %s error %d\n", I2C_1_DEVICE_NAME_0, ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*RISC-V 64 BOARD I2C INIT*/
|
||||
int HwI2cInit(void)
|
||||
{
|
||||
x_err_t ret = EOK;
|
||||
static struct I2cBus i2c_bus;
|
||||
memset(&i2c_bus, 0, sizeof(struct I2cBus));
|
||||
|
||||
static struct I2cDriver i2c_driver;
|
||||
memset(&i2c_driver, 0, sizeof(struct I2cDriver));
|
||||
|
||||
#ifdef BSP_USING_I2C1
|
||||
I2cGpioInit(&i2c_bus_param);
|
||||
|
||||
ret = BoardI2cBusInit(&i2c_bus, &i2c_driver);
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_Init error ret %u\n", ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
ret = BoardI2cDevBend();
|
||||
if (EOK != ret) {
|
||||
KPrintf("board_i2c_Init error ret %u\n", ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
I2cBusReset(&i2c_bus_param);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file hardware_i2c.c
|
||||
* @brief add from Canaan k210 SDK
|
||||
* https://canaan-creative.com/developer
|
||||
* @version 1.0
|
||||
* @author AIIT XUOS Lab
|
||||
* @date 2021-04-25
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <fpioa.h>
|
||||
#include <hardware_i2c.h>
|
||||
#include <platform.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysctl.h>
|
||||
#include <utils.h>
|
||||
|
||||
typedef struct _i2c_slave_instance
|
||||
{
|
||||
uint32_t i2c_num;
|
||||
const i2c_slave_handler_t *slave_handler;
|
||||
} i2c_slave_instance_t;
|
||||
|
||||
static i2c_slave_instance_t slave_instance[I2C_MAX_NUM];
|
||||
|
||||
typedef struct _i2c_instance
|
||||
{
|
||||
i2c_device_number_t i2c_num;
|
||||
i2c_transfer_mode_t TransferMode;
|
||||
dmac_channel_number_t dmac_channel;
|
||||
plic_instance_t i2c_int_instance;
|
||||
spinlock_t lock;
|
||||
} i2c_instance_t;
|
||||
|
||||
static i2c_instance_t g_i2c_instance[3];
|
||||
|
||||
volatile i2c_t* const i2c[3] =
|
||||
{
|
||||
(volatile i2c_t*)I2C0_BASE_ADDR,
|
||||
(volatile i2c_t*)I2C1_BASE_ADDR,
|
||||
(volatile i2c_t*)I2C2_BASE_ADDR
|
||||
};
|
||||
|
||||
static void i2c_clk_init(i2c_device_number_t i2c_num)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
sysctl_clock_enable(SYSCTL_CLOCK_I2C0 + i2c_num);
|
||||
sysctl_clock_set_threshold(SYSCTL_THRESHOLD_I2C0 + i2c_num, 3);
|
||||
}
|
||||
|
||||
void i2c_init(i2c_device_number_t i2c_num, uint32_t slave_address, uint32_t address_width,
|
||||
uint32_t i2c_clk)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
configASSERT(address_width == 7 || address_width == 10);
|
||||
|
||||
volatile i2c_t *i2c_adapter = i2c[i2c_num];
|
||||
|
||||
i2c_clk_init(i2c_num);
|
||||
|
||||
uint32_t v_i2c_freq = SysctlClockGetFreq(SYSCTL_CLOCK_I2C0 + i2c_num);
|
||||
uint16_t v_period_clk_cnt = v_i2c_freq / i2c_clk / 2;
|
||||
|
||||
if(v_period_clk_cnt == 0)
|
||||
v_period_clk_cnt = 1;
|
||||
|
||||
i2c_adapter->enable = 0;
|
||||
i2c_adapter->con = I2C_CON_MASTER_MODE | I2C_CON_SLAVE_DISABLE | I2C_CON_RESTART_EN |
|
||||
(address_width == 10 ? I2C_CON_10BITADDR_SLAVE : 0) | I2C_CON_SPEED(1);
|
||||
i2c_adapter->ss_scl_hcnt = I2C_SS_SCL_HCNT_COUNT(v_period_clk_cnt);
|
||||
i2c_adapter->ss_scl_lcnt = I2C_SS_SCL_LCNT_COUNT(v_period_clk_cnt);
|
||||
|
||||
i2c_adapter->tar = I2C_TAR_ADDRESS(slave_address);
|
||||
i2c_adapter->intr_mask = 0;
|
||||
i2c_adapter->dma_cr = 0x3;
|
||||
i2c_adapter->dma_rdlr = 0;
|
||||
i2c_adapter->dma_tdlr = 4;
|
||||
i2c_adapter->enable = I2C_ENABLE_ENABLE;
|
||||
}
|
||||
|
||||
static int i2c_slave_irq(void *userdata)
|
||||
{
|
||||
i2c_slave_instance_t *instance = (i2c_slave_instance_t *)userdata;
|
||||
volatile i2c_t *i2c_adapter = i2c[instance->i2c_num];
|
||||
uint32_t status = i2c_adapter->intr_stat;
|
||||
if (status & I2C_INTR_STAT_START_DET)
|
||||
{
|
||||
instance->slave_handler->on_event(I2C_EV_START);
|
||||
readl(&i2c_adapter->clr_start_det);
|
||||
}
|
||||
if (status & I2C_INTR_STAT_STOP_DET)
|
||||
{
|
||||
instance->slave_handler->on_event(I2C_EV_STOP);
|
||||
readl(&i2c_adapter->clr_stop_det);
|
||||
}
|
||||
if (status & I2C_INTR_STAT_RX_FULL)
|
||||
{
|
||||
instance->slave_handler->on_receive(i2c_adapter->data_cmd);
|
||||
}
|
||||
if (status & I2C_INTR_STAT_RD_REQ)
|
||||
{
|
||||
i2c_adapter->data_cmd = instance->slave_handler->on_transmit();
|
||||
readl(&i2c_adapter->clr_rd_req);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_init_as_slave(i2c_device_number_t i2c_num, uint32_t slave_address, uint32_t address_width,
|
||||
const i2c_slave_handler_t *handler)
|
||||
{
|
||||
configASSERT(address_width == 7 || address_width == 10);
|
||||
volatile i2c_t *i2c_adapter = i2c[i2c_num];
|
||||
slave_instance[i2c_num].i2c_num = i2c_num;
|
||||
slave_instance[i2c_num].slave_handler = handler;
|
||||
|
||||
i2c_clk_init(i2c_num);
|
||||
i2c_adapter->enable = 0;
|
||||
i2c_adapter->con = (address_width == 10 ? I2C_CON_10BITADDR_SLAVE : 0) | I2C_CON_SPEED(1) | I2C_CON_STOP_DET_IFADDRESSED;
|
||||
i2c_adapter->ss_scl_hcnt = I2C_SS_SCL_HCNT_COUNT(37);
|
||||
i2c_adapter->ss_scl_lcnt = I2C_SS_SCL_LCNT_COUNT(40);
|
||||
i2c_adapter->sar = I2C_SAR_ADDRESS(slave_address);
|
||||
i2c_adapter->rx_tl = I2C_RX_TL_VALUE(0);
|
||||
i2c_adapter->tx_tl = I2C_TX_TL_VALUE(0);
|
||||
i2c_adapter->intr_mask = I2C_INTR_MASK_RX_FULL | I2C_INTR_MASK_START_DET | I2C_INTR_MASK_STOP_DET | I2C_INTR_MASK_RD_REQ;
|
||||
|
||||
plic_set_priority(IRQN_I2C0_INTERRUPT + i2c_num, 1);
|
||||
plic_irq_register(IRQN_I2C0_INTERRUPT + i2c_num, i2c_slave_irq, slave_instance + i2c_num);
|
||||
plic_irq_enable(IRQN_I2C0_INTERRUPT + i2c_num);
|
||||
|
||||
i2c_adapter->enable = I2C_ENABLE_ENABLE;
|
||||
}
|
||||
|
||||
int i2c_send_data(i2c_device_number_t i2c_num, const uint8_t *SendBuf, size_t send_buf_len)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
volatile i2c_t* i2c_adapter = i2c[i2c_num];
|
||||
size_t fifo_len, index;
|
||||
i2c_adapter->clr_tx_abrt = i2c_adapter->clr_tx_abrt;
|
||||
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(*SendBuf++);
|
||||
if (i2c_adapter->tx_abrt_source != 0)
|
||||
return 1;
|
||||
send_buf_len -= fifo_len;
|
||||
}
|
||||
while ((i2c_adapter->status & I2C_STATUS_ACTIVITY) || !(i2c_adapter->status & I2C_STATUS_TFE))
|
||||
;
|
||||
|
||||
if (i2c_adapter->tx_abrt_source != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_send_data_dma(dmac_channel_number_t dma_channel_num, i2c_device_number_t i2c_num, const uint8_t *SendBuf,
|
||||
size_t send_buf_len)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
volatile i2c_t* i2c_adapter = i2c[i2c_num];
|
||||
i2c_adapter->clr_tx_abrt = i2c_adapter->clr_tx_abrt;
|
||||
uint32_t *buf = malloc(send_buf_len * sizeof(uint32_t));
|
||||
int i;
|
||||
for (i = 0; i < send_buf_len; i++)
|
||||
{
|
||||
buf[i] = SendBuf[i];
|
||||
}
|
||||
|
||||
sysctl_dma_select((sysctl_dma_channel_t)dma_channel_num, SYSCTL_DMA_SELECT_I2C0_TX_REQ + i2c_num * 2);
|
||||
dmac_set_single_mode(dma_channel_num, buf, (void *)(&i2c_adapter->data_cmd), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
|
||||
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, send_buf_len);
|
||||
|
||||
dmac_wait_done(dma_channel_num);
|
||||
free((void *)buf);
|
||||
|
||||
while ((i2c_adapter->status & I2C_STATUS_ACTIVITY) || !(i2c_adapter->status & I2C_STATUS_TFE))
|
||||
{
|
||||
if (i2c_adapter->tx_abrt_source != 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int i2c_recv_data(i2c_device_number_t i2c_num, const uint8_t *SendBuf, size_t send_buf_len, uint8_t *receive_buf,
|
||||
size_t receive_buf_len)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
|
||||
size_t fifo_len, index;
|
||||
size_t rx_len = receive_buf_len;
|
||||
volatile i2c_t* i2c_adapter = i2c[i2c_num];
|
||||
|
||||
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(*SendBuf++);
|
||||
if (i2c_adapter->tx_abrt_source != 0)
|
||||
return 1;
|
||||
send_buf_len -= fifo_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++ = (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 1;
|
||||
receive_buf_len -= fifo_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_recv_data_dma(dmac_channel_number_t dma_send_channel_num, dmac_channel_number_t dma_receive_channel_num,
|
||||
i2c_device_number_t i2c_num, const uint8_t *SendBuf, size_t send_buf_len,
|
||||
uint8_t *receive_buf, size_t receive_buf_len)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
|
||||
volatile i2c_t* i2c_adapter = i2c[i2c_num];
|
||||
|
||||
uint32_t *write_cmd = malloc(sizeof(uint32_t) * (send_buf_len + receive_buf_len));
|
||||
size_t i;
|
||||
for(i = 0; i < send_buf_len; i++)
|
||||
write_cmd[i] = *SendBuf++;
|
||||
for (i = 0; i < receive_buf_len; i++)
|
||||
write_cmd[i + send_buf_len] = I2C_DATA_CMD_CMD;
|
||||
|
||||
sysctl_dma_select((sysctl_dma_channel_t)dma_send_channel_num, SYSCTL_DMA_SELECT_I2C0_TX_REQ + i2c_num * 2);
|
||||
sysctl_dma_select((sysctl_dma_channel_t)dma_receive_channel_num, SYSCTL_DMA_SELECT_I2C0_RX_REQ + i2c_num * 2);
|
||||
|
||||
dmac_set_single_mode(dma_receive_channel_num, (void *)(&i2c_adapter->data_cmd), write_cmd, DMAC_ADDR_NOCHANGE,
|
||||
DMAC_ADDR_INCREMENT,DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, receive_buf_len);
|
||||
|
||||
dmac_set_single_mode(dma_send_channel_num, write_cmd, (void *)(&i2c_adapter->data_cmd), DMAC_ADDR_INCREMENT,
|
||||
DMAC_ADDR_NOCHANGE,DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, receive_buf_len + send_buf_len);
|
||||
|
||||
dmac_wait_done(dma_send_channel_num);
|
||||
dmac_wait_done(dma_receive_channel_num);
|
||||
|
||||
for (i = 0; i < receive_buf_len; i++)
|
||||
{
|
||||
receive_buf[i] = (uint8_t)write_cmd[i];
|
||||
}
|
||||
|
||||
free(write_cmd);
|
||||
}
|
||||
|
||||
static int i2c_dma_irq(void *ctx)
|
||||
{
|
||||
i2c_instance_t *v_instance = (i2c_instance_t *)ctx;
|
||||
volatile i2c_t* i2c_adapter = i2c[v_instance->i2c_num];
|
||||
dmac_irq_unregister(v_instance->dmac_channel);
|
||||
if(v_instance->TransferMode == I2C_SEND)
|
||||
{
|
||||
while ((i2c_adapter->status & I2C_STATUS_ACTIVITY) || !(i2c_adapter->status & I2C_STATUS_TFE))
|
||||
{
|
||||
if (i2c_adapter->tx_abrt_source != 0)
|
||||
{
|
||||
spinlock_unlock(&v_instance->lock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
spinlock_unlock(&v_instance->lock);
|
||||
if(v_instance->i2c_int_instance.callback)
|
||||
{
|
||||
v_instance->i2c_int_instance.callback(v_instance->i2c_int_instance.ctx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_handle_data_dma(i2c_device_number_t i2c_num, i2c_data_t data, plic_interrupt_t *cb)
|
||||
{
|
||||
configASSERT(i2c_num < I2C_MAX_NUM);
|
||||
configASSERT(data.tx_channel < DMAC_CHANNEL_MAX && data.rx_channel < DMAC_CHANNEL_MAX);
|
||||
|
||||
spinlock_lock(&g_i2c_instance[i2c_num].lock);
|
||||
if(cb)
|
||||
{
|
||||
g_i2c_instance[i2c_num].i2c_int_instance.callback = cb->callback;
|
||||
g_i2c_instance[i2c_num].i2c_int_instance.ctx = cb->ctx;
|
||||
}
|
||||
|
||||
volatile i2c_t* i2c_adapter = i2c[i2c_num];
|
||||
if(data.TransferMode == I2C_SEND)
|
||||
{
|
||||
configASSERT(data.tx_buf && data.tx_len);
|
||||
|
||||
i2c_adapter->clr_tx_abrt = i2c_adapter->clr_tx_abrt;
|
||||
if(cb)
|
||||
{
|
||||
g_i2c_instance[i2c_num].dmac_channel = data.tx_channel;
|
||||
g_i2c_instance[i2c_num].TransferMode = I2C_SEND;
|
||||
dmac_irq_register(data.tx_channel, i2c_dma_irq, &g_i2c_instance[i2c_num], cb->priority);
|
||||
}
|
||||
sysctl_dma_select((sysctl_dma_channel_t)data.tx_channel, SYSCTL_DMA_SELECT_I2C0_TX_REQ + i2c_num * 2);
|
||||
dmac_set_single_mode(data.tx_channel, data.tx_buf, (void *)(&i2c_adapter->data_cmd), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
|
||||
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, data.tx_len);
|
||||
if(!cb)
|
||||
{
|
||||
dmac_wait_done(data.tx_channel);
|
||||
while ((i2c_adapter->status & I2C_STATUS_ACTIVITY) || !(i2c_adapter->status & I2C_STATUS_TFE))
|
||||
{
|
||||
if (i2c_adapter->tx_abrt_source != 0)
|
||||
configASSERT(!"source abort");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
configASSERT(data.rx_buf && data.rx_len);
|
||||
if(data.tx_len)
|
||||
configASSERT(data.tx_buf);
|
||||
if(cb)
|
||||
{
|
||||
g_i2c_instance[i2c_num].dmac_channel = data.rx_channel;
|
||||
g_i2c_instance[i2c_num].TransferMode = I2C_RECEIVE;
|
||||
dmac_irq_register(data.rx_channel, i2c_dma_irq, &g_i2c_instance[i2c_num], cb->priority);
|
||||
}
|
||||
sysctl_dma_select((sysctl_dma_channel_t)data.rx_channel, SYSCTL_DMA_SELECT_I2C0_RX_REQ + i2c_num * 2);
|
||||
dmac_set_single_mode(data.rx_channel, (void *)(&i2c_adapter->data_cmd), data.rx_buf, DMAC_ADDR_NOCHANGE,
|
||||
DMAC_ADDR_INCREMENT,DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, data.rx_len);
|
||||
|
||||
sysctl_dma_select((sysctl_dma_channel_t)data.tx_channel, SYSCTL_DMA_SELECT_I2C0_TX_REQ + i2c_num * 2);
|
||||
if(data.tx_len)
|
||||
{
|
||||
configASSERT(data.tx_buf);
|
||||
dmac_set_single_mode(data.tx_channel, data.tx_buf, (void *)(&i2c_adapter->data_cmd), DMAC_ADDR_INCREMENT,
|
||||
DMAC_ADDR_NOCHANGE,DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, data.tx_len);
|
||||
dmac_wait_done(data.tx_channel);
|
||||
}
|
||||
static uint32_t s_read_cmd = I2C_DATA_CMD_CMD;
|
||||
dmac_set_single_mode(data.tx_channel, &s_read_cmd, (void *)(&i2c_adapter->data_cmd), DMAC_ADDR_NOCHANGE,
|
||||
DMAC_ADDR_NOCHANGE,DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, data.rx_len);
|
||||
|
||||
if(!cb)
|
||||
{
|
||||
dmac_wait_done(data.tx_channel);
|
||||
dmac_wait_done(data.rx_channel);
|
||||
}
|
||||
}
|
||||
if(!cb)
|
||||
spinlock_unlock(&g_i2c_instance[i2c_num].lock);
|
||||
}
|
||||
Reference in New Issue
Block a user