/*************************************************************************** * __________ __ ___. * 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); }