From 0662793ca0050e823cd1207cc4689a1cba5068bd Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Thu, 28 Jun 2018 06:24:26 -0400 Subject: Add cleaned-up xDuoo X3 support Cleaned up, rebased, and forward-ported from the xvortex fork. (original credit to vsoftster@gmail.com) Change-Id: Ibcc023a0271ea81e901450a88317708c2683236d Signed-off-by: Solomon Peachy --- firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c | 355 +++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c (limited to 'firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c') diff --git a/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c b/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c new file mode 100644 index 0000000000..e35fe7a091 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c @@ -0,0 +1,355 @@ +/*************************************************************************** + * __________ __ ___. + * 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; + 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); + 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); + 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); +} -- cgit v1.2.3