333 lines
6.9 KiB
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,
|
|
};
|
|
|