rockbox/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c
Solomon Peachy 72820d8b2d jz4760: Greatly enhance debug code and silence some compilation warnings.
Change-Id: I1746d67c818ad099edea83e6242ffd5c79be0000
2018-09-20 18:59:19 -04:00

358 lines
8.9 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2016 by Roman Stolyarov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "system.h"
#include "cpu.h"
#include "logf.h"
#include "i2c.h"
#define I2C_CHN 1
#define I2C_CLK 100000
#define I2C_READ 1
#define I2C_WRITE 0
#define I2C_M_RD 1
#define I2C_M_WR 2
#define TIMEOUT 100000
static char i2c_rwflags;
static int i2c_ctrl_rest = 0;
static unsigned char *msg_buf;
static int cmd_cnt;
static volatile int cmd_flag;
static int r_cnt;
static unsigned char i2c_subaddr = 0;
/*
* I2C bus protocol basic routines
*/
/* Interrupt handler */
void I2C1(void)
{
int timeout = TIMEOUT;
if (__i2c_abrt_7b_addr_nack(I2C_CHN)) {
int ret;
cmd_flag = -1;
__i2c_clear_interrupts(ret,I2C_CHN);
REG_I2C_INTM(I2C_CHN) = 0x0;
(void)ret;
return;
}
/* first byte,when length > 1 */
if (cmd_flag == 0 && cmd_cnt > 1) {
cmd_flag = 1;
if (i2c_rwflags == I2C_M_RD) {
REG_I2C_DC(I2C_CHN) = I2C_READ << 8;
} else {
REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | *msg_buf++;
}
cmd_cnt--;
}
if (i2c_rwflags == I2C_M_RD) {
if (REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE) {
*msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff;
r_cnt--;
}
REG_I2C_DC(I2C_CHN) = I2C_READ << 8;
} else {
REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | *msg_buf++;
}
cmd_cnt--;
if (!(cmd_cnt)) {
REG_I2C_INTM(I2C_CHN) = 0x0;
cmd_flag = 2;
if (i2c_rwflags == I2C_M_RD){
while (r_cnt > 2) {
if ((REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE) && timeout) {
*msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff;
r_cnt--;
}
if (!(timeout--)) {
cmd_flag = -1;
return;
}
}
}
}
return;
}
static int i2c_set_clk(int i2c_clk)
{
int dev_clk = __cpm_get_pclk();
int count = 0;
if (i2c_clk < 0 || i2c_clk > 400000)
goto Set_clk_err;
count = dev_clk/i2c_clk - 23;
if (count < 0)
goto Set_clk_err;
if (i2c_clk <= 100000) {
REG_I2C_CTRL(I2C_CHN) = 0x43 | i2c_ctrl_rest; /* standard speed mode*/
if (count%2 == 0) {
REG_I2C_SHCNT(I2C_CHN) = count/2 + 6 - 5;
REG_I2C_SLCNT(I2C_CHN) = count/2 + 8 + 5;
} else {
REG_I2C_SHCNT(I2C_CHN) = count/2 + 6 -5;
REG_I2C_SLCNT(I2C_CHN) = count/2 + 8 +5 + 1;
}
} else {
REG_I2C_CTRL(I2C_CHN) = 0x45 | i2c_ctrl_rest; /* high speed mode*/
if (count%2 == 0) {
REG_I2C_FHCNT(I2C_CHN) = count/2 + 6;
REG_I2C_FLCNT(I2C_CHN) = count/2 + 8;
} else {
REG_I2C_FHCNT(I2C_CHN) = count/2 + 6;
REG_I2C_FLCNT(I2C_CHN) = count/2 + 8 + 1;
}
}
return 0;
Set_clk_err:
logf("i2c set sclk faild,i2c_clk=%d,dev_clk=%d.\n",i2c_clk,dev_clk);
return -1;
}
static int i2c_disable(void)
{
int timeout = TIMEOUT;
__i2c_disable(I2C_CHN);
while(__i2c_is_enable(I2C_CHN) && (timeout > 0)) {
udelay(1);
timeout--;
}
if(timeout)
return 0;
else
return 1;
}
static int i2c_enable(void)
{
int timeout = TIMEOUT;
__i2c_enable(I2C_CHN);
while(__i2c_is_disable(I2C_CHN) && (timeout > 0)) {
udelay(1);
timeout--;
}
if(timeout)
return 0;
else
return 1;
}
static void i2c_init_as_master(unsigned char address)
{
if(i2c_disable())
logf("i2c not disable\n");
i2c_set_clk(I2C_CLK);
REG_I2C_TAR(I2C_CHN) = address; /* slave id needed write only once */
REG_I2C_INTM(I2C_CHN) = 0x0; /* unmask all interrupts */
REG_I2C_TXTL(I2C_CHN) = 0x1;
if(i2c_enable())
logf("i2c not enable\n");
}
int xfer_read_subaddr(unsigned char subaddr, unsigned char device, unsigned char *buf, int length)
{
int timeout,r_i = 0;
cmd_cnt = length;
r_cnt = length;
msg_buf = buf;
i2c_rwflags = I2C_M_RD;
i2c_ctrl_rest = I2C_CTRL_REST;
i2c_init_as_master(device);
REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | subaddr;
cmd_flag = 0;
REG_I2C_INTM(I2C_CHN) = 0x10;
timeout = TIMEOUT;
while (cmd_flag != 2 && --timeout) {
if (cmd_flag == -1) {
r_i = 1;
goto R_dev_err;
}
udelay(10);
}
if (!timeout) {
r_i = 4;
goto R_timeout;
}
while (r_cnt) {
while (!(REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE)) {
if ((cmd_flag == -1) ||
(REG_I2C_INTST(I2C_CHN) & I2C_INTST_TXABT) ||
REG_I2C_TXABRT(I2C_CHN)) {
int ret;
r_i = 2;
__i2c_clear_interrupts(ret,I2C_CHN);
(void)ret;
goto R_dev_err;
}
}
*msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff;
r_cnt--;
}
timeout = TIMEOUT;
while ((REG_I2C_STA(I2C_CHN) & I2C_STA_MSTACT) && --timeout)
udelay(10);
if (!timeout){
r_i = 3;
goto R_timeout;
}
return 0;
R_dev_err:
R_timeout:
i2c_init_as_master(device);
if (r_i == 1) {
logf("Read i2c device 0x%2x failed in r_i = %d :device no ack.\n",device,r_i);
} else if (r_i == 2) {
logf("Read i2c device 0x%2x failed in r_i = %d :i2c abort.\n",device,r_i);
} else if (r_i == 3) {
logf("Read i2c device 0x%2x failed in r_i = %d :waite master inactive timeout.\n",device,r_i);
} else if (r_i == 4) {
logf("Read i2c device 0x%2x failed in r_i = %d.\n",device,r_i);
} else {
logf("Read i2c device 0x%2x failed in r_i = %d.\n",device,r_i);
}
return -1;
}
int xfer_write_subaddr(unsigned char subaddr, unsigned char device, const unsigned char *buf, int length)
{
int timeout,w_i = 0;
cmd_cnt = length;
r_cnt = length;
msg_buf = (unsigned char *)buf;
i2c_rwflags = I2C_M_WR;
i2c_ctrl_rest = I2C_CTRL_REST;
i2c_init_as_master(device);
REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | subaddr;
cmd_flag = 0;
REG_I2C_INTM(I2C_CHN) = 0x10;
timeout = TIMEOUT;
while ((cmd_flag != 2) && (--timeout))
{
if (cmd_flag == -1){
w_i = 1;
goto W_dev_err;
}
udelay(10);
}
timeout = TIMEOUT;
while((!(REG_I2C_STA(I2C_CHN) & I2C_STA_TFE)) && --timeout){
udelay(10);
}
if (!timeout){
w_i = 2;
goto W_timeout;
}
timeout = TIMEOUT;
while (__i2c_master_active(I2C_CHN) && --timeout);
if (!timeout){
w_i = 3;
goto W_timeout;
}
if ((length == 1)&&
((cmd_flag == -1) ||
(REG_I2C_INTST(I2C_CHN) & I2C_INTST_TXABT) ||
REG_I2C_TXABRT(I2C_CHN))) {
int ret;
w_i = 5;
__i2c_clear_interrupts(ret,I2C_CHN);
(void)ret;
goto W_dev_err;
}
return 0;
W_dev_err:
W_timeout:
i2c_init_as_master(device);
if (w_i == 1) {
logf("Write i2c device 0x%2x failed in w_i=%d:device no ack. I2C_CHN:[%d]sxyzhang\n",device,w_i,I2C_CHN);
} else if (w_i == 2) {
logf("Write i2c device 0x%2x failed in w_i=%d:waite TF buff empty timeout.\n",device,w_i);
} else if (w_i == 3) {
logf("Write i2c device 0x%2x failed in w_i=%d:waite master inactive timeout.\n",device,w_i);
} else if (w_i == 5) {
logf("Write i2c device 0x%2x failed in w_i=%d:device no ack or abort.I2C_CHN:[%d]sxyzhang \n",device,w_i,I2C_CHN);
} else {
logf("Write i2c device 0x%2x failed in w_i=%d.\n",device,w_i);
}
return -1;
}
int i2c_read(int device, unsigned char* buf, int count)
{
return xfer_read_subaddr(i2c_subaddr, device, &buf[0], count);
}
int i2c_write(int device, const unsigned char* buf, int count)
{
if (count < 2)
{
i2c_subaddr = buf[0];
return 0;
}
return xfer_write_subaddr(buf[0], device, &buf[1], count-1);
}
void i2c_init(void)
{
__gpio_as_i2c(I2C_CHN);
__cpm_start_i2c1();
system_enable_irq(IRQ_I2C1);
}