| |
| /* |
| * drivers/i2c/aml_i2c.c |
| * |
| * Copyright (C) 2015 Amlogic, Inc. All rights reserved. |
| * |
| * 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 program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <errno.h> |
| #include <fdtdec.h> |
| #include <i2c.h> |
| #include <linux/types.h> |
| //#include <linux/mtd/compat.h> |
| #include <asm-generic/errno.h> |
| |
| #include <asm/arch/clock.h> |
| #include <asm/arch/i2c.h> |
| #include <asm/arch/io.h> |
| |
| #include <aml_i2c.h> |
| |
| #define HAS_AO_MODULE |
| #define AML_I2C_CTRL_CLK_DELAY_MASK (0x3FF) |
| #define AML_I2C_SLAVE_ADDR_MASK (0xFF) |
| #define AML_I2C_SLAVE_ADDR_MASK_7BIT (0x7F) |
| |
| #define AML_I2C_ASSERT(X) \ |
| do { \ |
| if (unlikely(!(X))) { \ |
| printf("\n"); \ |
| printf("CacheFiles: Assertion failed\n"); \ |
| BUG(); \ |
| } \ |
| } while (0) |
| |
| |
| #define AML_I2C_DBG(level,fmt,args... ) do { \ |
| if (g_aml_i2c_data.i2c_debug > level) \ |
| printf(fmt,##args); \ |
| }while(0) |
| |
| extern struct aml_i2c_platform g_aml_i2c_plat; |
| struct aml_i2c_platform *g_i2c_ports; |
| static unsigned char g_bAmlogicI2CInitialized = 0; //I2C initialized flag |
| static unsigned char g_cur_bus_num = 0; |
| |
| static struct aml_i2c g_aml_i2c_data = { |
| .i2c_debug = 0, |
| .cur_slave_addr= 0, |
| .wait_count = 0, |
| .wait_ack_interval = 0, |
| .wait_read_interval= 0, |
| .wait_xfer_interval= 0, |
| .master_no = 0, |
| .msg_flags = 0, |
| .ops = 0, |
| .master_regs = 0, |
| .reg_base = 0, |
| .use_pio = 0, |
| .master_i2c_speed= 0, |
| }; |
| |
| static void aml_i2c_set_clk(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| unsigned int i2c_clock_set; |
| unsigned int sys_clk; |
| volatile struct aml_i2c_reg_ctrl* ctrl; |
| //have not thought about sleep mode, sleep mode is low system clock |
| sys_clk = get_clk81(); |
| AML_I2C_DBG(1, "clk81 is 0x%x\n", sys_clk); |
| |
| i2c_clock_set = sys_clk / i2c->master_i2c_speed; |
| |
| AML_I2C_DBG(1, "i2c->master_i2c_speed is 0x%x\n", i2c->master_i2c_speed); |
| #if 1 |
| i2c_clock_set >>= 1; |
| ctrl = (struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl); |
| if (i2c_clock_set > 0xfff) i2c_clock_set = 0xfff; |
| ctrl->clk_delay = i2c_clock_set & 0x3ff; |
| ctrl->clk_delay_ext = i2c_clock_set >> 10; |
| i2c->master_regs->i2c_slave_addr &= ~(0xfff<<16); |
| i2c->master_regs->i2c_slave_addr |= (i2c_clock_set>>1)<<16; |
| i2c->master_regs->i2c_slave_addr |= 1<<28; |
| i2c->master_regs->i2c_slave_addr &= ~(0x3f<<8); //no filter on scl&sda |
| #else |
| i2c_clock_set >>= 2; |
| |
| AML_I2C_DBG(1, "i2c_clock_set is 0x%x\n", i2c_clock_set); |
| |
| ctrl = (struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl); |
| ctrl->clk_delay = i2c_clock_set & AML_I2C_CTRL_CLK_DELAY_MASK; |
| #endif |
| } |
| |
| static void aml_i2c_set_platform_data(struct aml_i2c *i2c, |
| struct aml_i2c_platform *plat) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| i2c->master_i2c_speed = plat->master_i2c_speed; |
| i2c->wait_count = plat->wait_count; |
| i2c->wait_ack_interval = plat->wait_ack_interval; |
| i2c->wait_read_interval = plat->wait_read_interval; |
| i2c->wait_xfer_interval = plat->wait_xfer_interval; |
| |
| if (I2C_MASTER_A == i2c->master_no) { |
| i2c->master_pinmux.scl_reg = plat->master_a_pinmux.scl_reg; |
| i2c->master_pinmux.scl_bit = plat->master_a_pinmux.scl_bit; |
| i2c->master_pinmux.sda_reg = plat->master_a_pinmux.sda_reg; |
| i2c->master_pinmux.sda_bit = plat->master_a_pinmux.sda_bit; |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| i2c->master_pinmux.scl_mask = plat->master_a_pinmux.scl_mask; |
| i2c->master_pinmux.sda_mask = plat->master_a_pinmux.sda_mask; |
| #endif |
| } |
| else if(I2C_MASTER_B == i2c->master_no){ |
| i2c->master_pinmux.scl_reg = plat->master_b_pinmux.scl_reg; |
| i2c->master_pinmux.scl_bit = plat->master_b_pinmux.scl_bit; |
| i2c->master_pinmux.sda_reg = plat->master_b_pinmux.sda_reg; |
| i2c->master_pinmux.sda_bit = plat->master_b_pinmux.sda_bit; |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| i2c->master_pinmux.scl_mask = plat->master_b_pinmux.scl_mask; |
| i2c->master_pinmux.sda_mask = plat->master_b_pinmux.sda_mask; |
| #endif |
| } |
| else if(I2C_MASTER_C == i2c->master_no){ |
| i2c->master_pinmux.scl_reg = plat->master_c_pinmux.scl_reg; |
| i2c->master_pinmux.scl_bit = plat->master_c_pinmux.scl_bit; |
| i2c->master_pinmux.sda_reg = plat->master_c_pinmux.sda_reg; |
| i2c->master_pinmux.sda_bit = plat->master_c_pinmux.sda_bit; |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| i2c->master_pinmux.scl_mask = plat->master_c_pinmux.scl_mask; |
| i2c->master_pinmux.sda_mask = plat->master_c_pinmux.sda_mask; |
| #endif |
| } |
| else if(I2C_MASTER_D == i2c->master_no){ |
| i2c->master_pinmux.scl_reg = plat->master_d_pinmux.scl_reg; |
| i2c->master_pinmux.scl_bit = plat->master_d_pinmux.scl_bit; |
| i2c->master_pinmux.sda_reg = plat->master_d_pinmux.sda_reg; |
| i2c->master_pinmux.sda_bit = plat->master_d_pinmux.sda_bit; |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| i2c->master_pinmux.scl_mask = plat->master_d_pinmux.scl_mask; |
| i2c->master_pinmux.sda_mask = plat->master_d_pinmux.sda_mask; |
| #endif |
| } |
| #ifdef HAS_AO_MODULE |
| else if(I2C_MASTER_AO == i2c->master_no){ |
| i2c->master_pinmux.scl_reg = plat->master_ao_pinmux.scl_reg; |
| i2c->master_pinmux.scl_bit = plat->master_ao_pinmux.scl_bit; |
| i2c->master_pinmux.sda_reg = plat->master_ao_pinmux.sda_reg; |
| i2c->master_pinmux.sda_bit = plat->master_ao_pinmux.sda_bit; |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| i2c->master_pinmux.scl_mask = plat->master_ao_pinmux.scl_mask; |
| i2c->master_pinmux.sda_mask = plat->master_ao_pinmux.sda_mask; |
| #endif |
| } |
| #endif |
| } |
| |
| static void aml_i2c_pinmux_master(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| unsigned int scl_pinmux; |
| unsigned int sda_pinmux; |
| |
| scl_pinmux = readl(i2c->master_pinmux.scl_reg); |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| scl_pinmux &= ~i2c->master_pinmux.scl_mask; |
| #endif |
| scl_pinmux |= i2c->master_pinmux.scl_bit; |
| writel(scl_pinmux, i2c->master_pinmux.scl_reg); |
| |
| sda_pinmux = readl(i2c->master_pinmux.sda_reg); |
| #ifdef CONFIG_SYS_I2C_AML_HAS_MASK |
| sda_pinmux &= ~i2c->master_pinmux.sda_mask; |
| #endif |
| sda_pinmux |= i2c->master_pinmux.sda_bit; |
| writel(sda_pinmux, i2c->master_pinmux.sda_reg); |
| } |
| |
| static void aml_i2c_dbg(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| struct aml_i2c_reg_ctrl* ctrl; |
| |
| if (i2c->i2c_debug == 0) |
| return ; |
| |
| printf( "i2c_slave_addr: 0x%x\n", |
| i2c->master_regs->i2c_slave_addr); |
| printf( "i2c_token_list_0: 0x%x\n", |
| i2c->master_regs->i2c_token_list_0); |
| printf( "i2c_token_list_1: 0x%x\n", |
| i2c->master_regs->i2c_token_list_1); |
| printf( "i2c_token_wdata_0: 0x%x\n", |
| i2c->master_regs->i2c_token_wdata_0); |
| printf( "i2c_token_wdata_1: 0x%x\n", |
| i2c->master_regs->i2c_token_wdata_1); |
| printf( "i2c_token_rdata_0: 0x%x\n", |
| i2c->master_regs->i2c_token_rdata_0); |
| printf( "i2c_token_rdata_1: 0x%x\n", |
| i2c->master_regs->i2c_token_rdata_1); |
| for (i=0; i<AML_I2C_MAX_TOKENS; i++) |
| printf("token_tag[%d] %d\n", i, i2c->token_tag[i]); |
| |
| ctrl = ((struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl)); |
| printf( "i2c_ctrl: 0x%x\n", i2c->master_regs->i2c_ctrl); |
| printf( "ctrl.rdsda 0x%x\n", ctrl->rdsda); |
| printf( "ctrl.rdscl 0x%x\n", ctrl->rdscl); |
| printf( "ctrl.wrsda 0x%x\n", ctrl->wrsda); |
| printf( "ctrl.wrscl 0x%x\n", ctrl->wrscl); |
| printf( "ctrl.manual_en 0x%x\n", ctrl->manual_en); |
| printf( "ctrl.clk_delay 0x%x\n", ctrl->clk_delay); |
| printf( "ctrl.rd_data_cnt 0x%x\n", ctrl->rd_data_cnt); |
| printf( "ctrl.cur_token 0x%x\n", ctrl->cur_token); |
| printf( "ctrl.error 0x%x\n", ctrl->error); |
| printf( "ctrl.status 0x%x\n", ctrl->status); |
| printf( "ctrl.ack_ignore 0x%x\n", ctrl->ack_ignore); |
| printf( "ctrl.start 0x%x\n", ctrl->start); |
| |
| } |
| |
| static void aml_i2c_clear_token_list(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| i2c->master_regs->i2c_token_list_0 = 0; |
| i2c->master_regs->i2c_token_list_1 = 0; |
| memset(i2c->token_tag, TOKEN_END, AML_I2C_MAX_TOKENS); |
| } |
| |
| static void aml_i2c_set_token_list(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| unsigned int token_reg=0; |
| |
| for (i=0; i<AML_I2C_MAX_TOKENS; i++) |
| token_reg |= i2c->token_tag[i]<<(i*4); |
| |
| i2c->master_regs->i2c_token_list_0=token_reg; |
| } |
| |
| static void aml_i2c_hw_init(struct aml_i2c *i2c, unsigned int use_pio) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| struct aml_i2c_reg_ctrl* ctrl; |
| |
| aml_i2c_set_clk(i2c); |
| |
| /*manual mode*/ |
| if (use_pio) { |
| ctrl = (struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl); |
| ctrl->manual_en = 1; |
| } |
| } |
| |
| static int aml_i2c_check_error(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| struct aml_i2c_reg_ctrl* ctrl; |
| ctrl = (struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl); |
| |
| if (ctrl->error) |
| { |
| //printf( "ctrl.cur_token 0x%x\n", ctrl->cur_token); |
| return -EIO; |
| } |
| else |
| return 0; |
| } |
| |
| /*poll status*/ |
| static int aml_i2c_wait_ack(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| struct aml_i2c_reg_ctrl* ctrl; |
| |
| for (i=0; i<i2c->wait_count; i++) { |
| udelay(i2c->wait_ack_interval); |
| ctrl = (struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl); |
| if (I2C_IDLE == ctrl->status) |
| return aml_i2c_check_error(i2c); |
| } |
| |
| return -ETIMEDOUT; |
| } |
| |
| static void aml_i2c_get_read_data(struct aml_i2c *i2c, unsigned char *buf, |
| size_t len) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| unsigned long rdata0 = i2c->master_regs->i2c_token_rdata_0; |
| unsigned long rdata1 = i2c->master_regs->i2c_token_rdata_1; |
| |
| for (i=0; i< min_t(size_t, len, AML_I2C_MAX_TOKENS>>1); i++) |
| *buf++ = (rdata0 >> (i*8)) & 0xff; |
| |
| for (; i< min_t(size_t, len, AML_I2C_MAX_TOKENS); i++) |
| *buf++ = (rdata1 >> ((i - (AML_I2C_MAX_TOKENS>>1))*8)) & 0xff; |
| } |
| |
| static void aml_i2c_fill_data(struct aml_i2c *i2c, unsigned char *buf, |
| size_t len) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| unsigned int wdata0 = 0; |
| unsigned int wdata1 = 0; |
| |
| for (i=0; i< min_t(size_t, len, AML_I2C_MAX_TOKENS>>1); i++) |
| wdata0 |= (*buf++) << (i*8); |
| |
| for (; i< min_t(size_t, len, AML_I2C_MAX_TOKENS); i++) |
| wdata1 |= (*buf++) << ((i - (AML_I2C_MAX_TOKENS>>1))*8); |
| |
| i2c->master_regs->i2c_token_wdata_0 = wdata0; |
| i2c->master_regs->i2c_token_wdata_1 = wdata1; |
| } |
| |
| static void aml_i2c_xfer_prepare(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| aml_i2c_pinmux_master(i2c); |
| aml_i2c_set_clk(i2c); |
| } |
| |
| static void aml_i2c_start_token_xfer(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| |
| i2c->master_regs->i2c_ctrl &= ~1; /*clear*/ |
| i2c->master_regs->i2c_ctrl |= 1; /*set*/ |
| |
| udelay(i2c->wait_xfer_interval); |
| } |
| |
| /*Amlogic I2C controller will send write data with slave addr in the token list, |
| and set addr into addr reg is enough*/ |
| static int aml_i2c_do_address(struct aml_i2c *i2c, unsigned int addr) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| |
| i2c->cur_slave_addr = addr & AML_I2C_SLAVE_ADDR_MASK_7BIT; |
| #if 1 |
| i2c->master_regs->i2c_slave_addr &=(~AML_I2C_SLAVE_ADDR_MASK); |
| |
| i2c->master_regs->i2c_slave_addr |=(i2c->cur_slave_addr<<1); |
| #else |
| i2c->master_regs->i2c_slave_addr = i2c->cur_slave_addr<<1; |
| #endif |
| return 0; |
| } |
| |
| static void aml_i2c_stop(struct aml_i2c *i2c) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| // aml_i2c_clear_token_list(i2c); |
| // i2c->token_tag[0]=TOKEN_STOP; |
| // aml_i2c_set_token_list(i2c); |
| // aml_i2c_start_token_xfer(i2c); |
| // aml_i2c_wait_ack(i2c); |
| struct aml_i2c_reg_ctrl* ctrl; |
| ctrl = (struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl); |
| if (!ctrl->error) { |
| AML_I2C_DBG(1, "FILE1:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| aml_i2c_clear_token_list(i2c); |
| i2c->token_tag[0]=TOKEN_STOP; |
| aml_i2c_set_token_list(i2c); |
| aml_i2c_start_token_xfer(i2c); |
| aml_i2c_wait_ack(i2c); |
| } |
| } |
| |
| static long aml_i2c_read(struct aml_i2c *i2c, unsigned char *buf, |
| size_t len) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| int ret; |
| size_t rd_len; |
| int tagnum=0; |
| |
| aml_i2c_clear_token_list(i2c); |
| |
| if (!(i2c->msg_flags & I2C_M_NOSTART)) { |
| i2c->token_tag[tagnum++]=TOKEN_START; |
| i2c->token_tag[tagnum++]=TOKEN_SLAVE_ADDR_READ; |
| |
| aml_i2c_set_token_list(i2c); |
| aml_i2c_dbg(i2c); |
| aml_i2c_start_token_xfer(i2c); |
| |
| udelay(i2c->wait_ack_interval); |
| |
| ret = aml_i2c_wait_ack(i2c); |
| if (ret<0) |
| return ret; |
| aml_i2c_clear_token_list(i2c); |
| } |
| |
| while (len) { |
| tagnum = 0; |
| rd_len = min_t(size_t, len, AML_I2C_MAX_TOKENS); |
| if (rd_len == 1) |
| i2c->token_tag[tagnum++]=TOKEN_DATA_LAST; |
| else{ |
| for (i=0; i<rd_len-1; i++) |
| i2c->token_tag[tagnum++]=TOKEN_DATA; |
| if (len > rd_len) |
| i2c->token_tag[tagnum++]=TOKEN_DATA; |
| else |
| i2c->token_tag[tagnum++]=TOKEN_DATA_LAST; |
| } |
| aml_i2c_set_token_list(i2c); |
| aml_i2c_dbg(i2c); |
| aml_i2c_start_token_xfer(i2c); |
| |
| udelay(i2c->wait_ack_interval); |
| |
| ret = aml_i2c_wait_ack(i2c); |
| if (ret<0) |
| return ret; |
| |
| aml_i2c_get_read_data(i2c, buf, rd_len); |
| len -= rd_len; |
| buf += rd_len; |
| |
| aml_i2c_dbg(i2c); |
| udelay(i2c->wait_read_interval); |
| aml_i2c_clear_token_list(i2c); |
| } |
| return 0; |
| } |
| |
| static long aml_i2c_write(struct aml_i2c *i2c, unsigned char *buf, |
| size_t len) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| int i; |
| int ret; |
| size_t wr_len; |
| int tagnum=0; |
| |
| aml_i2c_clear_token_list(i2c); |
| |
| if (!(i2c->msg_flags & I2C_M_NOSTART)) { |
| i2c->token_tag[tagnum++]=TOKEN_START; |
| i2c->token_tag[tagnum++]=TOKEN_SLAVE_ADDR_WRITE; |
| } |
| while (len) { |
| wr_len = min_t(size_t, len, AML_I2C_MAX_TOKENS-tagnum); |
| for (i=0; i<wr_len; i++) |
| i2c->token_tag[tagnum++]=TOKEN_DATA; |
| |
| aml_i2c_set_token_list(i2c); |
| |
| aml_i2c_fill_data(i2c, buf, wr_len); |
| |
| aml_i2c_dbg(i2c); |
| aml_i2c_start_token_xfer(i2c); |
| |
| len -= wr_len; |
| buf += wr_len; |
| tagnum = 0; |
| |
| ret = aml_i2c_wait_ack(i2c); |
| if (ret<0) |
| return ret; |
| |
| aml_i2c_clear_token_list(i2c); |
| } |
| return 0; |
| } |
| |
| static struct aml_i2c_ops g_aml_i2c_m1_ops = { |
| .xfer_prepare = aml_i2c_xfer_prepare, |
| .read = aml_i2c_read, |
| .write = aml_i2c_write, |
| .do_address = aml_i2c_do_address, |
| .stop = aml_i2c_stop, |
| }; |
| |
| /*General i2c master transfer*/ |
| int aml_i2c_xfer(struct i2c_msg *msgs, |
| int num) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| |
| if (0 == g_bAmlogicI2CInitialized) { |
| return -ENXIO; //device not initialized |
| } |
| |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| struct i2c_msg * p=0; |
| unsigned int i; |
| unsigned int ret=0; |
| |
| i2c->ops->xfer_prepare(i2c); |
| |
| for (i = 0; !ret && i < num; i++) { |
| p = &msgs[i]; |
| i2c->msg_flags = p->flags; |
| ret = i2c->ops->do_address(i2c, p->addr); |
| if (ret || !p->len) |
| { |
| continue; |
| } |
| if (p->flags & I2C_M_RD) |
| ret = i2c->ops->read(i2c, p->buf, p->len); |
| else |
| ret = i2c->ops->write(i2c, p->buf, p->len); |
| } |
| |
| i2c->ops->stop(i2c); |
| |
| |
| if (p->flags & I2C_M_RD) { |
| AML_I2C_DBG(0, "read "); |
| } |
| else { |
| AML_I2C_DBG(0, "write "); |
| } |
| for (i=0;i<p->len;i++) |
| AML_I2C_DBG(0, "%x-",*(p->buf)++); |
| AML_I2C_DBG(0, "\n"); |
| |
| /* Return the number of messages processed, or the error code*/ |
| if (ret == 0) |
| return num; |
| else { |
| printf("[aml_i2c_xfer] error ret = %d \t", ret); |
| printf("i2c master %s current slave addr is 0x%x\n", |
| i2c->master_no?"a":"b", i2c->cur_slave_addr); |
| return ret; |
| } |
| } |
| /*General i2c master transfer 100k*/ |
| int aml_i2c_xfer_slow(struct i2c_msg *msgs, |
| int num) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| struct i2c_msg * p=0; |
| unsigned int i; |
| unsigned int ret=0; |
| unsigned int last_speed = i2c->master_i2c_speed; |
| |
| |
| i2c->master_i2c_speed = AML_I2C_SPPED_100K;/* change speed in i2c->lock*/ |
| i2c->ops->xfer_prepare(i2c); |
| |
| for (i = 0; !ret && i < num; i++) { |
| p = &msgs[i]; |
| i2c->msg_flags = p->flags; |
| //ret = i2c->ops->do_address(i2c, p->addr, p->buf, p->flags & I2C_M_RD, p->len); |
| ret = i2c->ops->do_address(i2c, p->addr); |
| if (ret || !p->len) |
| continue; |
| if (p->flags & I2C_M_RD) |
| ret = i2c->ops->read(i2c, p->buf, p->len); |
| else |
| ret = i2c->ops->write(i2c, p->buf, p->len); |
| } |
| |
| i2c->ops->stop(i2c); |
| |
| |
| AML_I2C_DBG(0, "aml_i2c_xfer_slow"); |
| if (p->flags & I2C_M_RD) { |
| AML_I2C_DBG(0, "read "); |
| } |
| else { |
| AML_I2C_DBG(0, "write "); |
| } |
| for (i=0;i<p->len;i++) |
| AML_I2C_DBG(0, "%x-",*(p->buf)++); |
| AML_I2C_DBG(0, "\n"); |
| |
| i2c->master_i2c_speed = last_speed; |
| /* Return the number of messages processed, or the error code*/ |
| if (ret == 0) |
| return num; |
| else |
| return ret; |
| } |
| |
| |
| /***************i2c class****************/ |
| |
| __attribute__((unused)) static ssize_t show_i2c_debug(void) |
| { |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| printf("i2c debug is 0x%x\n", i2c->i2c_debug); |
| return 0; |
| } |
| |
| __attribute__((unused)) static ssize_t show_i2c_info(void) |
| { |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| struct aml_i2c_reg_ctrl* ctrl; |
| |
| printf( "i2c master %s current slave addr is 0x%x\n", |
| i2c->master_no?"b":"a", i2c->cur_slave_addr); |
| printf( "wait ack timeout is 0x%x\n", |
| i2c->wait_count * i2c->wait_ack_interval); |
| printf( "master regs base is 0x%lx \n", |
| (unsigned long)(i2c->master_regs)); |
| |
| ctrl = ((struct aml_i2c_reg_ctrl*)&(i2c->master_regs->i2c_ctrl)); |
| printf( "i2c_ctrl: 0x%x\n", i2c->master_regs->i2c_ctrl); |
| printf( "ctrl.rdsda 0x%x\n", ctrl->rdsda); |
| printf( "ctrl.rdscl 0x%x\n", ctrl->rdscl); |
| printf( "ctrl.wrsda 0x%x\n", ctrl->wrsda); |
| printf( "ctrl.wrscl 0x%x\n", ctrl->wrscl); |
| printf( "ctrl.manual_en 0x%x\n", ctrl->manual_en); |
| printf( "ctrl.clk_delay 0x%x\n", ctrl->clk_delay); |
| printf( "ctrl.rd_data_cnt 0x%x\n", ctrl->rd_data_cnt); |
| printf( "ctrl.cur_token 0x%x\n", ctrl->cur_token); |
| printf( "ctrl.error 0x%x\n", ctrl->error); |
| printf( "ctrl.status 0x%x\n", ctrl->status); |
| printf( "ctrl.ack_ignore 0x%x\n", ctrl->ack_ignore); |
| printf( "ctrl.start 0x%x\n", ctrl->start); |
| |
| printf( "i2c_slave_addr: 0x%x\n", |
| i2c->master_regs->i2c_slave_addr); |
| printf( "i2c_token_list_0: 0x%x\n", |
| i2c->master_regs->i2c_token_list_0); |
| printf( "i2c_token_list_1: 0x%x\n", |
| i2c->master_regs->i2c_token_list_1); |
| printf( "i2c_token_wdata_0: 0x%x\n", |
| i2c->master_regs->i2c_token_wdata_0); |
| printf( "i2c_token_wdata_1: 0x%x\n", |
| i2c->master_regs->i2c_token_wdata_1); |
| printf( "i2c_token_rdata_0: 0x%x\n", |
| i2c->master_regs->i2c_token_rdata_0); |
| printf( "i2c_token_rdata_1: 0x%x\n", |
| i2c->master_regs->i2c_token_rdata_1); |
| |
| printf( "master pinmux\n"); |
| printf( "scl_reg: 0x%lx\n", i2c->master_pinmux.scl_reg); |
| printf( "scl_bit: 0x%x\n", i2c->master_pinmux.scl_bit); |
| printf( "sda_reg: 0x%lx\n", i2c->master_pinmux.sda_reg); |
| printf( "sda_bit: 0x%x\n", i2c->master_pinmux.sda_bit); |
| |
| return 0; |
| } |
| |
| static const unsigned long g_aml_i2c_reg_start[] = { |
| [0] = MESON_I2C_MASTER_AO_START,/*master a*/ |
| [1] = MESON_I2C_MASTER_A_START,/*master a*/ |
| [2] = MESON_I2C_MASTER_B_START,/*master b*/ |
| [3] = MESON_I2C_MASTER_C_START,/*master b*/ |
| [4] = MESON_I2C_MASTER_D_START,/*master b*/ |
| // [2] = MESON_I2C_SLAVE_START,/*slave*/ |
| |
| #ifdef HAS_AO_MODULE |
| [3] = MESON_I2C_MASTER_AO_START,/*master ao*/ |
| #endif |
| }; |
| |
| int aml_i2c_init_port(struct aml_i2c_platform *plat) |
| { |
| |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| |
| if (plat == NULL) |
| { |
| printf("\nERROR! struct aml_i2c_platform *plat is a NULL pointer!\n"); |
| return -1; |
| } |
| |
| i2c->ops = &g_aml_i2c_m1_ops; |
| i2c->master_no = plat->master_no; |
| |
| i2c->use_pio = plat->use_pio; |
| AML_I2C_ASSERT((i2c->master_no >= 0) && (i2c->master_no <= 4)); |
| |
| /*master a or master b*/ |
| if (i2c->master_no >= ARRAY_SIZE(g_aml_i2c_reg_start)) |
| { |
| printf("\nERROR! overflow: i2c->master_no = %d\n", i2c->master_no); |
| return -1; |
| } |
| |
| printf("%s init regs for %d\n", __func__, i2c->master_no); |
| i2c->master_regs = (struct aml_i2c_reg_master __iomem*)(g_aml_i2c_reg_start[i2c->master_no]); |
| |
| AML_I2C_ASSERT(i2c->master_regs); |
| AML_I2C_ASSERT(plat); |
| |
| aml_i2c_set_platform_data(i2c, plat); |
| aml_i2c_xfer_prepare(i2c); |
| |
| aml_i2c_hw_init(i2c , i2c->use_pio); |
| |
| g_bAmlogicI2CInitialized = 1; |
| |
| return 0; |
| } |
| |
| int aml_i2c_init(void) |
| { |
| return aml_i2c_init_port(g_i2c_ports + g_cur_bus_num); |
| } |
| |
| /***************************** |
| ** add by wch for cmd_i2c ** |
| ****************************/ |
| /* |
| * i2c read cmd |
| */ |
| int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) |
| { |
| int ret = 0; |
| |
| /* |
| * I2C data address within the chip. This can be 1 or |
| * 2 bytes long. Some day it might be 3 bytes long :-). |
| * here,if it has 2 bytes long at most. |
| */ |
| uint8_t devaddr[2]; |
| |
| switch (alen) |
| { |
| case 0: //NO I2C data address within the chip. |
| puts ("'*.0' shows no i2c data or register address within the chip. \n"); |
| return -1; |
| break; |
| |
| case 1: //I2C data address:1 byte long within the chip. |
| if (addr>0xff) |
| { |
| puts ("'*.1' shows i2c data or register address 1 byte long within the chip. \n"); |
| return -1; |
| } |
| else |
| devaddr[0] = addr & 0xff; |
| break; |
| |
| case 2: |
| #if 0 |
| //I2C data address:2 bytes long within the chip. |
| if (addr>0xffff || addr<0x1ff) |
| { |
| puts ("'*.2' shows i2c data or register address 2 bytes long within the chip. \n"); |
| return -1; |
| } |
| else |
| #endif |
| { |
| devaddr[0] = addr & 0xff; |
| devaddr[1] = (addr >> 8) & 0xff; |
| } |
| break; |
| |
| case 3: //I2C data address:3 bytes long within the chip. |
| //Here,if it has 2 bytes long at most. |
| puts ("Here,we have set i2c data address or register address 2 bytes long at most.\n"); |
| return -1; |
| break; |
| } |
| |
| |
| struct i2c_msg msg[] = { |
| { |
| .addr = chip, |
| .flags = 0, |
| .len = alen, //I2C data address length. |
| .buf = devaddr, |
| }, |
| { |
| .addr = chip, |
| .flags = 1, |
| .len = len, //read len bytes from I2C data address. |
| .buf = buffer, |
| } |
| }; |
| |
| ret = aml_i2c_xfer((struct i2c_msg *)msg, 2); |
| if (ret < 0) { |
| printf("%s: i2c transfer failed\n", __FUNCTION__); |
| return ret; |
| } |
| |
| /* |
| printf("chip=0x%x,addr=0x%x,alen=%d,len=%d",chip,addr,alen,len); |
| for (ret=0;ret<len;ret++) |
| printf(",buffer[%d]=0x%x",ret,*buffer++); |
| printf("\n"); |
| */ |
| |
| return 0; |
| } |
| |
| |
| |
| /* |
| * i2c write cmd |
| */ |
| int i2c_write(unsigned char chip, unsigned int addr, int alen,unsigned char *buffer, int len) |
| { |
| int ret; |
| int length = 0; |
| uint8_t buff[3]; |
| |
| /* |
| * I2C data address within the chip. This can be 1 or |
| * 2 bytes long. Some day it might be 3 bytes long :-). |
| * here,if it has 2 bytes long at most. |
| */ |
| uint8_t devaddr[2] = {0}; |
| |
| switch (alen) |
| { |
| case 0: //NO I2C data address within the chip. |
| puts ("'*.0' shows no i2c data or register address within the chip. \n"); |
| return -1; |
| break; |
| |
| case 1: //I2C data address:1 byte long within the chip. |
| if (addr>0xff) |
| { |
| puts ("'*.1' shows i2c data or register address 1 byte long within the chip. \n"); |
| return -1; |
| } |
| else |
| devaddr[0] = addr & 0xff; |
| break; |
| |
| case 2: |
| #if 0 |
| //I2C data address:2 bytes long within the chip. |
| if (addr>0xffff || addr<0x1ff) |
| { |
| puts ("'*.2' shows i2c data or register address 2 bytes long within the chip. \n"); |
| return -1; |
| } |
| else |
| #endif |
| { |
| devaddr[0] = addr & 0xff; |
| devaddr[1] = (addr >> 8) & 0xff; |
| } |
| break; |
| |
| case 3: //I2C data address:3 bytes long within the chip. |
| //Here,if it has 2 bytes long at most. |
| puts ("Here,we have set i2c data address or register address 2 bytes long at most.\n"); |
| return -1; |
| break; |
| } |
| |
| if (len == 1) |
| { |
| switch (alen) |
| { |
| |
| case 1: |
| buff[0] = devaddr[0]; |
| buff[1] = *buffer; |
| length = 2; |
| break; |
| |
| case 2: |
| buff[0] = devaddr[0]; |
| buff[1] = devaddr[1]; |
| buff[2] = *buffer; |
| length = 3; |
| break; |
| /* |
| case 3: |
| //when i2c data address or register address 3 bytes long ,here should be completed. |
| break; |
| */ |
| |
| } |
| |
| struct i2c_msg msg[] = { |
| { |
| .addr = chip, |
| .flags = 0, |
| .len = length, |
| .buf = buff, |
| } |
| }; |
| |
| ret = aml_i2c_xfer((struct i2c_msg *)msg, 1); |
| if (ret < 0) { |
| printf("%s: i2c transfer failed\n", __FUNCTION__); |
| return ret; |
| } |
| |
| } |
| else |
| { |
| /* |
| * This section may be modified when len > 1. |
| */ |
| printf("I2C write data length is %d. \n",len); |
| return -1; |
| } |
| |
| |
| |
| return 0; |
| } |
| |
| |
| |
| /* |
| * i2c probe cmd |
| * return 0:i2c probe ok, non 0:i2c probe failed. |
| */ |
| int i2c_probe(uchar chip) |
| { |
| int ret; |
| unsigned int addr=0x00; //i2c data or register address |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| struct i2c_msg * p=0; |
| |
| struct i2c_msg msg[] = { |
| { |
| .addr = chip, |
| .flags = 0, //write |
| .len = 1, |
| .buf = (unsigned char *)&addr, |
| } |
| }; |
| |
| |
| i2c->ops->xfer_prepare(i2c); |
| |
| |
| p = &msg[0]; |
| i2c->msg_flags = p->flags; |
| ret = i2c->ops->do_address(i2c, p->addr); |
| |
| if (p->flags & I2C_M_RD) |
| ret = i2c->ops->read(i2c, p->buf, p->len); |
| else |
| ret = i2c->ops->write(i2c, p->buf, p->len); |
| |
| i2c->ops->stop(i2c); |
| |
| if (ret == 0) |
| return 0; //This chip valid. |
| else |
| return ret; //This chip invalid. |
| } |
| |
| |
| |
| /* |
| * i2c reset cmd |
| */ |
| void i2c_init(int speed, int slaveaddr) |
| { |
| #define AML_I2C_SPPED_400K 400000 //The initial value of amlogic i2c speed |
| |
| if (g_i2c_ports == NULL) { |
| g_i2c_ports = &g_aml_i2c_plat; |
| g_cur_bus_num = 0; |
| } |
| aml_i2c_init(); |
| } |
| |
| |
| |
| /* |
| * i2c speed cmd |
| * get i2c speed |
| */ |
| unsigned int i2c_get_bus_speed(void) |
| { |
| |
| return g_i2c_ports[g_cur_bus_num].master_i2c_speed; |
| } |
| |
| |
| |
| /* |
| * i2c speed xxx cmd |
| * set i2c speed |
| */ |
| int i2c_set_bus_speed(unsigned int speed) |
| { |
| /* |
| #define AML_I2C_SPPED_50K 50000 |
| #define AML_I2C_SPPED_100K 100000 |
| #define AML_I2C_SPPED_200K 200000 |
| #define AML_I2C_SPPED_300K 300000 |
| #define AML_I2C_SPPED_400K 400000 |
| */ |
| if ((speed == 50000) || (speed == 100000) || (speed == 200000) || (speed == 300000) || (speed == 400000)) |
| { |
| g_i2c_ports[g_cur_bus_num].master_i2c_speed = speed; |
| aml_i2c_init(); |
| return 0; |
| } |
| else |
| { |
| printf("The I2C speed setting don't match,should choose:50000,100000,200000,300000 or 400000.\n"); |
| return -1; |
| } |
| |
| } |
| #if defined(CONFIG_CMD_EEPROM) |
| int i2c_eeprom_write(unsigned char chip, unsigned int addr, int alen,unsigned char *buffer, int len) |
| { |
| int i, err; |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| unsigned char buf[4]; |
| |
| if (alen > 4) return -1; |
| for (i=0; i<alen; i++) { |
| buf[i] = addr&0xff; |
| addr >>= 8; |
| } |
| |
| i2c->msg_flags = 0; |
| i2c->ops->xfer_prepare(i2c); |
| i2c->ops->do_address(i2c, chip); |
| err = i2c->ops->write(i2c, buf, alen); |
| if (!err) { |
| i2c->msg_flags |= I2C_M_NOSTART; |
| err = i2c->ops->write(i2c, buffer, len); |
| } |
| i2c->ops->stop(i2c); |
| return err; |
| } |
| #endif |
| |
| /*General i2c master transfer,it can Send to a specific master control*/ |
| int aml_i2c_transfer(int adap_num,struct i2c_msg *msgs, |
| int num) |
| { |
| AML_I2C_DBG(1, "FILE:%s:%d, FUNC:%s\n", __FILE__,__LINE__,__func__); |
| |
| if (0 == g_bAmlogicI2CInitialized) { |
| return -ENXIO; //device not initialized |
| } |
| |
| g_aml_i2c_data.master_no = adap_num; |
| struct aml_i2c *i2c = &g_aml_i2c_data; |
| i2c->master_regs = (struct aml_i2c_reg_master __iomem*)(g_aml_i2c_reg_start[i2c->master_no]); |
| struct i2c_msg * p=0; |
| unsigned int i; |
| unsigned int ret=0; |
| |
| i2c->ops->xfer_prepare(i2c); |
| |
| for (i = 0; !ret && i < num; i++) { |
| p = &msgs[i]; |
| i2c->msg_flags = p->flags; |
| ret = i2c->ops->do_address(i2c, p->addr); |
| if (ret || !p->len) |
| { |
| continue; |
| } |
| if (p->flags & I2C_M_RD) |
| ret = i2c->ops->read(i2c, p->buf, p->len); |
| else |
| ret = i2c->ops->write(i2c, p->buf, p->len); |
| } |
| |
| i2c->ops->stop(i2c); |
| |
| |
| if (p->flags & I2C_M_RD) { |
| AML_I2C_DBG(0, "read "); |
| } |
| else { |
| AML_I2C_DBG(0, "write "); |
| } |
| for (i=0;i<p->len;i++) |
| AML_I2C_DBG(0, "%x-",*(p->buf)++); |
| AML_I2C_DBG(0, "\n"); |
| |
| /* Return the number of messages processed, or the error code*/ |
| if (ret == 0) |
| return num; |
| else { |
| printf("[aml_i2c_xfer] error ret = %d \t", ret); |
| printf("i2c master %s current slave addr is 0x%x\n", |
| i2c->master_no?"a":"b", i2c->cur_slave_addr); |
| return ret; |
| } |
| } |
| unsigned int i2c_get_bus_num() |
| { |
| return g_i2c_ports[g_cur_bus_num].master_no; |
| } |
| /* |
| * cmd: i2c dev |
| */ |
| int i2c_set_bus_num(unsigned int busnum) |
| { |
| int i = 0; |
| for (;g_i2c_ports[i].master_i2c_speed > 0;i++) { |
| if (g_i2c_ports[i].master_no == busnum) { |
| g_cur_bus_num = i; |
| break; |
| } |
| } |
| if (g_i2c_ports[i].master_i2c_speed == 0 ) { |
| printf("%s no %d busnum enable\n", __func__, busnum); |
| return 0; |
| } |
| printf("%s i2c sel %d bus\n", __func__, busnum); |
| aml_i2c_init_port(&g_i2c_ports[i]); |
| return 0; |
| } |
| void aml_i2c_set_ports(struct aml_i2c_platform *i2c_plat) |
| { |
| g_i2c_ports = i2c_plat; |
| aml_i2c_init(); |
| } |