doubao_chat/apps/common/iic/software_iic.c

333 lines
6.9 KiB
C

#include "system/init.h"
#include "device/device.h"
#include "device/iic.h"
#include "device/gpio.h"
#include "asm/cpu.h"
#include "asm/gpio.h"
#define IIC_DELAY_COUNT iic->sw_iic_delay
#define INIT_IIC(iic) \
do { \
__gpio_direction_output(iic->dat_pin, 1); \
__gpio_set_pull_down(iic->dat_pin, 0); \
__gpio_set_pull_up(iic->dat_pin, 1); \
__gpio_set_die(iic->dat_pin, 1); \
__gpio_direction_output(iic->clk_pin, 1); \
__gpio_set_pull_down(iic->clk_pin, 0); \
__gpio_set_pull_up(iic->clk_pin, 1); \
__gpio_set_die(iic->clk_pin, 1); \
}while(0)
#define SCCB_SID_H(iic) \
do { \
__gpio_set_pull_up(iic->dat_pin, 1); \
__gpio_direction_output(iic->dat_pin, 1); \
}while(0)
#define SCCB_SID_L(iic) \
do { \
__gpio_set_pull_up(iic->dat_pin, 1); \
__gpio_direction_output(iic->dat_pin, 0); \
}while(0)
#define SCCB_SIC_H(iic) \
do { \
__gpio_set_pull_up(iic->clk_pin, 1); \
__gpio_direction_output(iic->clk_pin, 1); \
}while(0)
#define SCCB_SIC_L(iic) \
do { \
__gpio_set_pull_up(iic->clk_pin, 1); \
__gpio_direction_output(iic->clk_pin, 0); \
}while(0)
#define SCCB_SID_IN(iic) \
do { \
__gpio_direction_input(iic->dat_pin); \
}while(0)
#define SCCB_SID_OUT(iic) \
do { \
__gpio_direction_output(iic->dat_pin, 0); \
}while(0)
#define SCCB_SID_STATE(iic) \
__gpio_read(iic->dat_pin)
static void iic_start(const struct software_iic *iic)
{
INIT_IIC(iic);
SCCB_SID_H(iic);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT * 2);
SCCB_SID_L(iic);
delay(IIC_DELAY_COUNT);
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
}
static void iic_stop(const struct software_iic *iic)
{
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
SCCB_SID_L(iic);
delay(IIC_DELAY_COUNT);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT * 2);
SCCB_SID_H(iic);
delay(IIC_DELAY_COUNT);
}
static u8 iic_wait_ack(const struct software_iic *iic)
{
int ucErrTime = 0;
SCCB_SID_IN(iic);
delay(IIC_DELAY_COUNT);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT);
while (SCCB_SID_STATE(iic)) {
if (++ucErrTime > 250) {
iic_stop(iic);
return 1;
}
}
delay(IIC_DELAY_COUNT);
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
return 0;
}
static void iic_ack(const struct software_iic *iic)
{
/* SCCB_SIC_L(iic); */
/* delay(IIC_DELAY_COUNT); */
SCCB_SID_L(iic);
delay(IIC_DELAY_COUNT);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT * 2);
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
}
static void iic_noack(const struct software_iic *iic)
{
/* SCCB_SIC_L(iic); */
/* delay(IIC_DELAY_COUNT); */
SCCB_SID_H(iic);
delay(IIC_DELAY_COUNT);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT * 2);
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
}
static u8 iic_tx(const struct software_iic *iic, u8 m_iic)
{
unsigned char j, tem;
SCCB_SIC_L(iic);
for (j = 0; j < 8; j++) {
if ((m_iic << j) & 0x80) {
SCCB_SID_H(iic);
} else {
SCCB_SID_L(iic);
}
delay(IIC_DELAY_COUNT);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT * 2);
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
}
tem = iic_wait_ack(iic);
SCCB_SID_OUT(iic);
return tem;
}
static u8 iic_rx(const struct software_iic *iic)
{
u8 read = 0, j;
SCCB_SID_IN(iic);
for (j = 8; j > 0; j--) {
delay(IIC_DELAY_COUNT);
SCCB_SIC_H(iic);
delay(IIC_DELAY_COUNT);
read = read << 1;
if (SCCB_SID_STATE(iic)) {
read = read + 1;
}
delay(IIC_DELAY_COUNT);
SCCB_SIC_L(iic);
delay(IIC_DELAY_COUNT);
}
return read;
}
static int iic_read(struct iic_device *iic, void *buf, int len)
{
return 0;
}
static int iic_write(struct iic_device *iic, void *buf, int len)
{
int i;
u8 *p = (u8 *)buf;
iic_start(iic->hw);
for (i = 0; i < len; i++) {
iic_tx(iic->hw, p[i]);
if (iic->nrate > 1 && ((i + 1) % iic->nrate) == 0) {
iic_stop(iic->hw);
if (i + 1 == len) {
return len;
}
delay(100);
iic_start(iic->hw);
}
}
iic_stop(iic->hw);
return len;
}
static int iic_ioctl(struct iic_device *iic, int cmd, int arg)
{
int ret = 0;
struct software_iic *hw = (struct software_iic *)iic->hw;
u8 tmp = 0;
switch (cmd) {
case IOCTL_SET_SPEED:
break;
case IIC_IOCTL_TX_START_BIT:
iic_start(iic->hw);
break;
case IIC_IOCTL_TX_STOP_BIT:
iic_stop(iic->hw);
break;
case IIC_IOCTL_TX_WITH_START_BIT:
iic_start(iic->hw);
ret = iic_tx(iic->hw, arg);
break;
case IIC_IOCTL_TX:
ret = iic_tx(iic->hw, arg);
break;
case IIC_IOCTL_TX_WITH_STOP_BIT:
ret = iic_tx(iic->hw, arg);
iic_stop(iic->hw);
break;
case IIC_IOCTL_RX:
tmp = iic_rx(iic->hw);
if (arg) {
*((u8 *)arg) = tmp;
}
iic_ack(iic->hw);
break;
case IIC_IOCTL_RX_WITH_STOP_BIT:
tmp = iic_rx(iic->hw);
if (arg) {
*((u8 *)arg) = tmp;
}
iic_noack(iic->hw);
iic_stop(iic->hw);
break;
case IIC_IOCTL_RX_WITH_NOACK:
tmp = iic_rx(iic->hw);
if (arg) {
*((u8 *)arg) = tmp;
}
iic_noack(iic->hw);
break;
case IIC_IOCTL_RX_WITH_ACK:
tmp = iic_rx(iic->hw);
if (arg) {
*((u8 *)arg) = tmp;
}
iic_ack(iic->hw);
break;
case IIC_IOCTL_SET_NORMAT_RATE:
iic->nrate = arg + 1;
break;
case IIC_IOCTL_START:
gpio_port_lock(GPIO2PORT(hw->dat_pin));
if (GPIO2PORT(hw->dat_pin) != GPIO2PORT(hw->clk_pin)) {
gpio_port_lock(GPIO2PORT(hw->clk_pin));
}
break;
case IIC_IOCTL_STOP:
gpio_port_unlock(GPIO2PORT(hw->dat_pin));
if (GPIO2PORT(hw->dat_pin) != GPIO2PORT(hw->clk_pin)) {
gpio_port_unlock(GPIO2PORT(hw->clk_pin));
}
break;
default:
return -EINVAL;
}
if (ret) {
iic_stop(iic->hw);
}
return ret;
}
static int iic_close(struct iic_device *iic)
{
struct software_iic *hw = (struct software_iic *)iic->hw;
gpio_direction_input(hw->clk_pin);
gpio_set_pull_down(hw->clk_pin, 0);
gpio_set_pull_up(hw->clk_pin, 0);
gpio_set_die(hw->clk_pin, 0);
gpio_direction_input(hw->dat_pin);
gpio_set_pull_down(hw->dat_pin, 0);
gpio_set_pull_up(hw->dat_pin, 0);
gpio_set_die(hw->dat_pin, 0);
return 0;
}
REGISTER_IIC_DEVICE(sw_iic_ops) = {
.type = IIC_TYPE_SW,
.read = iic_read,
.write = iic_write,
.ioctl = iic_ioctl,
.close = iic_close,
};