72820d8b2d
Change-Id: I1746d67c818ad099edea83e6242ffd5c79be0000
358 lines
8.9 KiB
C
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);
|
|
}
|