1097 lines
27 KiB
C
1097 lines
27 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* drivers/i2c/aml_i2c.c
|
|
*
|
|
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#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;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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 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;
|
|
}
|
|
#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);
|
|
scl_pinmux |= i2c->master_pinmux.scl_bit;
|
|
writel(scl_pinmux, i2c->master_pinmux.scl_reg);
|
|
|
|
sda_pinmux = readl(i2c->master_pinmux.sda_reg);
|
|
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 = 4; 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 = 4; 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 <= 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();
|
|
}
|