352 lines
7.8 KiB
C
352 lines
7.8 KiB
C
#include "asm/includes.h"
|
|
#include "device/gpio.h"
|
|
#include "device/iic.h"
|
|
#include "system/includes.h"
|
|
|
|
|
|
static u8 hw_iic_busy;
|
|
|
|
|
|
static const struct iic_outport *get_hw_iic_outport(const struct hardware_iic *iic)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IIC_OUTPORT_NUM; i++) {
|
|
if (iic->clk_pin == iic->outport_map[i].clk_pin &&
|
|
iic->dat_pin == iic->outport_map[i].dat_pin) {
|
|
return &iic->outport_map[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void iic_set_baudrate(const struct hardware_iic *iic, int baudrate)
|
|
{
|
|
iic->reg->BAUD = baudrate;
|
|
}
|
|
|
|
static void iic_start(const struct hardware_iic *iic)
|
|
{
|
|
iic_set_output(iic);
|
|
iic_add_start_bit(iic);
|
|
}
|
|
|
|
static void iic_stop(const struct hardware_iic *iic)
|
|
{
|
|
iic_add_end_bit(iic);
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
#if 0
|
|
u32 time = jiffies + msecs_to_jiffies(200);
|
|
while (time_before(jiffies, time)) {
|
|
if (iic_epnd(iic)) {
|
|
iic_clr_epnd(iic);
|
|
return;
|
|
}
|
|
}
|
|
log_e("iic_wait_stop_timeout\r\n");
|
|
#else
|
|
while (!iic_epnd(iic));
|
|
#endif
|
|
iic_clr_epnd(iic);
|
|
}
|
|
|
|
static int iic_wait_end(const struct hardware_iic *iic)
|
|
{
|
|
u32 time = jiffies + msecs_to_jiffies(200);
|
|
|
|
while (time_before(jiffies, time)) {
|
|
/* while (1) { */
|
|
if (iic_pnd(iic)) {
|
|
iic_clr_pnd(iic);
|
|
return 0;
|
|
}
|
|
}
|
|
log_e("iic_wait_end_timeout\r\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
static int iic_tx(const struct hardware_iic *iic, u8 dat)
|
|
{
|
|
iic_set_output(iic);
|
|
iic->reg->BUF = dat;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
return iic_wait_end(iic);
|
|
}
|
|
|
|
static int iic_tx_with_start_bit(const struct hardware_iic *iic, u8 dat)
|
|
{
|
|
iic_set_output(iic);
|
|
iic_add_start_bit(iic);
|
|
iic->reg->BUF = dat;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
return iic_wait_end(iic);
|
|
}
|
|
|
|
static int iic_tx_with_stop_bit(const struct hardware_iic *iic, u8 dat)
|
|
{
|
|
int err;
|
|
|
|
iic_set_output(iic);
|
|
iic->reg->BUF = dat;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
err = iic_wait_end(iic);
|
|
iic_stop(iic);
|
|
|
|
return err;
|
|
}
|
|
|
|
static u8 iic_rx(const struct hardware_iic *iic, int *err)
|
|
{
|
|
u8 res;
|
|
iic_set_input(iic);
|
|
iic_set_ack(iic);
|
|
iic->reg->BUF = 0xff;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
*err = iic_wait_end(iic);
|
|
res = iic->reg->BUF;
|
|
return res;
|
|
}
|
|
|
|
static u8 iic_rx_with_start_bit(const struct hardware_iic *iic, int *err)
|
|
{
|
|
u8 res;
|
|
iic_set_input(iic);
|
|
iic_add_start_bit(iic);
|
|
iic_set_ack(iic);
|
|
iic->reg->BUF = 0xff;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
*err = iic_wait_end(iic);
|
|
return iic->reg->BUF;
|
|
}
|
|
|
|
static u8 iic_rx_with_stop_bit(const struct hardware_iic *iic, int *err)
|
|
{
|
|
u8 res;
|
|
iic_set_input(iic);
|
|
iic_set_unack(iic);
|
|
iic->reg->BUF = 0xff;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
*err = iic_wait_end(iic);
|
|
res = iic->reg->BUF;
|
|
iic_stop(iic);
|
|
return res;
|
|
}
|
|
|
|
static u8 iic_rx_with_no_ack(const struct hardware_iic *iic, int *err)
|
|
{
|
|
iic_set_input(iic);
|
|
iic_set_unack(iic);
|
|
iic->reg->BUF = 0xff;
|
|
__asm_csync();
|
|
asm("nop");
|
|
iic_cfg_done(iic);
|
|
*err = iic_wait_end(iic);
|
|
return iic->reg->BUF;
|
|
}
|
|
|
|
static int iic_open(struct iic_device *iic)
|
|
{
|
|
int value;
|
|
const struct iic_outport *port;
|
|
const struct hardware_iic *hw = (const struct hardware_iic *)iic->hw;
|
|
|
|
port = get_hw_iic_outport(hw);
|
|
if (!port) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
value = readl(hw->occupy_reg);
|
|
value &= hw->occupy_io_mask;
|
|
value |= port->value;
|
|
writel(hw->occupy_reg, value);
|
|
|
|
if (port->clk_pin == IO_PORT_USB_DPA && port->dat_pin == IO_PORT_USB_DMA) {
|
|
usb_iomode(0, 1);
|
|
}
|
|
|
|
gpio_direction_output(port->clk_pin, 1);
|
|
gpio_direction_output(port->dat_pin, 1);
|
|
gpio_set_pull_up(port->clk_pin, 1);
|
|
gpio_set_pull_up(port->dat_pin, 1);
|
|
gpio_set_pull_down(port->clk_pin, 0);
|
|
gpio_set_pull_down(port->dat_pin, 0);
|
|
gpio_set_hd(port->clk_pin, 0);
|
|
gpio_set_hd(port->dat_pin, 0);
|
|
gpio_set_die(port->clk_pin, 1);
|
|
gpio_set_die(port->dat_pin, 1);
|
|
|
|
hw->reg->BAUD = hw->baudrate;
|
|
hw->reg->CON0 = BIT(0) | BIT(9);
|
|
hw->reg->CON0 |= BIT(14) | BIT(12);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iic_read(struct iic_device *iic, void *buf, int len)
|
|
{
|
|
int err = 0;
|
|
u8 *p = (u8 *)buf;
|
|
u32 rlen = 0;
|
|
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
p[rlen++] = iic_rx_with_start_bit((const struct hardware_iic *)iic->hw, &err);
|
|
|
|
while (rlen < (len - 1)) {
|
|
p[rlen++] = iic_rx((const struct hardware_iic *)iic->hw, &err);
|
|
}
|
|
if (len - rlen == 1) {
|
|
p[rlen++] = iic_rx_with_stop_bit((const struct hardware_iic *)iic->hw, &err);
|
|
} else {
|
|
iic_stop((const struct hardware_iic *)iic->hw);
|
|
}
|
|
|
|
return rlen;
|
|
}
|
|
|
|
static int iic_write(struct iic_device *iic, void *buf, int len)
|
|
{
|
|
u8 *p = (u8 *)buf;
|
|
u32 wlen = 0;
|
|
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
iic_tx_with_start_bit((const struct hardware_iic *)iic->hw, p[wlen++]);
|
|
|
|
while (wlen < (len - 1)) {
|
|
iic_tx((const struct hardware_iic *)iic->hw, p[wlen++]);
|
|
}
|
|
if (len - wlen == 1) {
|
|
iic_tx_with_stop_bit((const struct hardware_iic *)iic->hw, p[wlen++]);
|
|
} else {
|
|
iic_stop((const struct hardware_iic *)iic->hw);
|
|
}
|
|
|
|
return wlen;
|
|
}
|
|
|
|
static int iic_ioctl(struct iic_device *iic, int cmd, int arg)
|
|
{
|
|
int err = 0;
|
|
struct hardware_iic *hw_iic;
|
|
u8 tmp = 0;
|
|
|
|
switch (cmd) {
|
|
case IOCTL_SET_SPEED:
|
|
iic_set_baudrate(iic->hw, arg);
|
|
break;
|
|
case IIC_IOCTL_TX:
|
|
err = iic_tx(iic->hw, arg);
|
|
break;
|
|
case IIC_IOCTL_TX_WITH_START_BIT:
|
|
err = iic_tx_with_start_bit(iic->hw, arg);
|
|
break;
|
|
case IIC_IOCTL_TX_WITH_STOP_BIT:
|
|
err = iic_tx_with_stop_bit(iic->hw, arg);
|
|
break;
|
|
case IIC_IOCTL_TX_STOP_BIT:
|
|
iic_stop(iic->hw);
|
|
break;
|
|
case IIC_IOCTL_TX_START_BIT:
|
|
iic_start(iic->hw);
|
|
break;
|
|
case IIC_IOCTL_RX:
|
|
tmp = iic_rx(iic->hw, &err);
|
|
if (arg) {
|
|
*((u8 *)arg) = tmp;
|
|
}
|
|
break;
|
|
case IIC_IOCTL_RX_WITH_START_BIT:
|
|
tmp = iic_rx_with_start_bit(iic->hw, &err);
|
|
if (arg) {
|
|
*((u8 *)arg) = tmp;
|
|
}
|
|
break;
|
|
case IIC_IOCTL_RX_WITH_STOP_BIT:
|
|
tmp = iic_rx_with_stop_bit(iic->hw, &err);
|
|
if (arg) {
|
|
*((u8 *)arg) = tmp;
|
|
}
|
|
break;
|
|
case IIC_IOCTL_RX_WITH_NOACK:
|
|
tmp = iic_rx_with_no_ack(iic->hw, &err);
|
|
if (arg) {
|
|
*((u8 *)arg) = tmp;
|
|
}
|
|
break;
|
|
case IIC_IOCTL_RX_WITH_ACK:
|
|
tmp = iic_rx(iic->hw, &err);
|
|
if (arg) {
|
|
*((u8 *)arg) = tmp;
|
|
}
|
|
break;
|
|
case IIC_IOCTL_START:
|
|
hw_iic_busy = 1;
|
|
break;
|
|
case IIC_IOCTL_STOP:
|
|
hw_iic_busy = 0;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int iic_close(struct iic_device *iic)
|
|
{
|
|
const struct hardware_iic *hw = (const struct hardware_iic *)iic->hw;
|
|
const struct iic_outport *port = get_hw_iic_outport(hw);
|
|
|
|
hw->reg->CON0 = 0;
|
|
|
|
gpio_direction_input(port->clk_pin);
|
|
gpio_set_pull_down(port->clk_pin, 0);
|
|
gpio_set_pull_up(port->clk_pin, 0);
|
|
gpio_set_die(port->clk_pin, 0);
|
|
|
|
gpio_direction_input(port->dat_pin);
|
|
gpio_set_pull_down(port->dat_pin, 0);
|
|
gpio_set_pull_up(port->dat_pin, 0);
|
|
gpio_set_die(port->dat_pin, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
REGISTER_IIC_DEVICE(hw_iic_ops) = {
|
|
.type = IIC_TYPE_HW,
|
|
.open = iic_open,
|
|
.read = iic_read,
|
|
.write = iic_write,
|
|
.ioctl = iic_ioctl,
|
|
.close = iic_close,
|
|
};
|
|
|
|
static u8 iic_idle_query(void)
|
|
{
|
|
return !hw_iic_busy;
|
|
}
|
|
|
|
REGISTER_LP_TARGET(iic_lp_target) = {
|
|
.name = "iic",
|
|
.is_idle = iic_idle_query,
|
|
};
|
|
|