View: 8437|Reply: 2

How to change I2C clock speed on Orange Pi One?

[Copy link]

4

threads

11

posts

142

credits

Registered member

Rank: 2

credits
142
Published in 2016-7-26 23:22:26 | Show all floors |Read mode
I'm trying to control "touch key" pad, which was used by Photo frame, with I2C.
Original clock speed was 20kHz, very slow. "Touch key controller" may not work with high speed clock, over 100kHz.
I want to change I2C clock speed more slow.
Anyone know how to change I2c clock speed? (I'm using Armbian 5.14 now.)

62

threads

653

posts

5498

credits

Administrator

Rank: 9Rank: 9Rank: 9

credits
5498
Published in 2016-8-30 14:24:01 | Show all floors
Pls refer I2C source code, It work on Uboot and If you want to work on kernel , pls set specify register as correct value.
#include <common.h>
#include <asm/io.h>
#include <asm/arch/twi.h>
#include <asm/arch/ccmu.h>
#include <asm/arch/gpio.h>
#define I2C_WRITE  0
#define I2C_READ  1
#define I2C_OK   0
#define I2C_NOK   1
#define I2C_NACK  2
#define I2C_NOK_LA  3 /* Lost arbitration */
#define I2C_NOK_TOUT 4 /* time out */
#define I2C_START_TRANSMIT     0x08
#define I2C_RESTART_TRANSMIT   0x10
#define I2C_ADDRWRITE_ACK    0x18
#define I2C_ADDRREAD_ACK    0x40
#define I2C_DATAWRITE_ACK      0x28
#define I2C_READY      0xf8
#define I2C_DATAREAD_NACK    0x58
#define I2C_DATAREAD_ACK    0x50
/* status or interrupt source */
/*------------------------------------------------------------------------------
* Code   Status
* 00h    Bus error
* 08h    START condition transmitted
* 10h    Repeated START condition transmitted
* 18h    Address + Write bit transmitted, ACK received
* 20h    Address + Write bit transmitted, ACK not received
* 28h    Data byte transmitted in master mode, ACK received
* 30h    Data byte transmitted in master mode, ACK not received
* 38h    Arbitration lost in address or data byte
* 40h    Address + Read bit transmitted, ACK received
* 48h    Address + Read bit transmitted, ACK not received
* 50h    Data byte received in master mode, ACK transmitted
* 58h    Data byte received in master mode, not ACK transmitted
* 60h    Slave address + Write bit received, ACK transmitted
* 68h    Arbitration lost in address as master, slave address + Write bit received, ACK transmitted
* 70h    General Call address received, ACK transmitted
* 78h    Arbitration lost in address as master, General Call address received, ACK transmitted
* 80h    Data byte received after slave address received, ACK transmitted
* 88h    Data byte received after slave address received, not ACK transmitted
* 90h    Data byte received after General Call received, ACK transmitted
* 98h    Data byte received after General Call received, not ACK transmitted
* A0h    STOP or repeated START condition received in slave mode
* A8h    Slave address + Read bit received, ACK transmitted
* B0h    Arbitration lost in address as master, slave address + Read bit received, ACK transmitted
* B8h    Data byte transmitted in slave mode, ACK received
* C0h    Data byte transmitted in slave mode, ACK not received
* C8h    Last byte transmitted in slave mode, ACK received
* D0h    Second Address byte + Write bit transmitted, ACK received
* D8h    Second Address byte + Write bit transmitted, ACK not received
* F8h    No relevant status information or no interrupt
*-----------------------------------------------------------------------------*/

/* OrangePi 8106 */
#define   BOOT_8106_VERSION                (0x01)
#define   BOOT_8106_DCDOUT_VOL             (0x15)

static  struct sunxi_twi_reg *i2c  = NULL;
static __s32 i2c_sendbyteaddr(__u32 byteaddr)
{
__s32  time = 0xffff;
__u32  tmp_val;
i2c->data = byteaddr & 0xff;
        i2c->ctl |= (0x01<<3);//write 1 to clean int flag
        while( (time--) && (!(i2c->ctl & 0x08)) );
if(time <= 0)
{
  return -I2C_NOK_TOUT;
}
tmp_val = i2c->status;
if(tmp_val != I2C_DATAWRITE_ACK)
{
  return -I2C_DATAWRITE_ACK;
}
return I2C_OK;
}
static __s32 i2c_sendstart(void)
{
    __s32  time = 0xfffff;
    __u32  tmp_val;
    i2c->eft  = 0;
    i2c->srst = 1;
    i2c->ctl |= 0x20;
    while((time--)&&(!(i2c->ctl & 0x08)));
if(time <= 0)
{
  return -I2C_NOK_TOUT;
}
tmp_val = i2c->status;
    if(tmp_val != I2C_START_TRANSMIT)
    {
  return -I2C_START_TRANSMIT;
    }
    return I2C_OK;
}
static __s32 i2c_sendslaveaddr(__u32 saddr,  __u32 rw)
{
__s32  time = 0xffff;
__u32  tmp_val;
rw &= 1;
i2c->data = ((saddr & 0xff) << 1)| rw;
        i2c->ctl  |= (0x01<<3);//write 1 to clean int flag
while(( time-- ) && (!( i2c->ctl & 0x08 )));
if(time <= 0)
{
  printf("I2C_NOK_TOUT\n");
  return -I2C_NOK_TOUT;
}
tmp_val = i2c->status;
if(rw == I2C_WRITE)//+write
{
  if(tmp_val != I2C_ADDRWRITE_ACK)
  {
   printf("I2C_ADDRWRITE_ACK\n");
   return -I2C_ADDRWRITE_ACK;
  }
}
else//+read
{
  if(tmp_val != I2C_ADDRREAD_ACK)
  {
   return -I2C_ADDRREAD_ACK;
  }
}
return I2C_OK;
}
static __s32 i2c_sendRestart(void)
{
__s32  time = 0xffff;
__u32  tmp_val;
tmp_val = i2c->ctl;
tmp_val |= 0x20;
i2c->ctl = tmp_val;
while( (time--) && (!(i2c->ctl & 0x08)) );
if(time <= 0)
{
  return -I2C_NOK_TOUT;
}
tmp_val = i2c->status;
if(tmp_val != I2C_RESTART_TRANSMIT)
{
  return -I2C_RESTART_TRANSMIT;
}
return I2C_OK;
}
static __s32 i2c_stop(void)
{
__s32  time = 0xffff;
__u32  tmp_val;
i2c->ctl |= (0x01 << 4);
        i2c->ctl |= (0x01 << 3);
        while( (time--) && (i2c->ctl & 0x10) );
if(time <= 0)
{
  return -I2C_NOK_TOUT;
}
time = 0xffff;
while( (time--) && (i2c->status != I2C_READY) );
tmp_val = i2c->status;
if(tmp_val != I2C_READY)
{
  return -I2C_NOK_TOUT;
}
return I2C_OK;
}
static __s32 i2c_getdata(__u8 *data_addr, __u32 data_count)
{
__s32  time = 0xffff;
__u32  tmp_val;
__u32  i;
if(data_count == 1)
{
         i2c->ctl |= (0x01<<3);
         while( (time--) && (!(i2c->ctl & 0x08)) );
  if(time <= 0)
  {
             return -I2C_NOK_TOUT;
  }
  for(time=0;time<100;time++);
  *data_addr = i2c->data;
  tmp_val = i2c->status;
  if(tmp_val != I2C_DATAREAD_NACK)
  {
             return -I2C_DATAREAD_NACK;
  }
}
else
{
  for(i=0; i< data_count - 1; i++)
  {
   time = 0xffff;
   //host should send ack every time when a data packet finished
   tmp_val = i2c->ctl | (0x01<<2);
                        tmp_val = i2c->ctl | (0x01<<3);
          tmp_val |= 0x04;
                        i2c->ctl = tmp_val;
    //i2c->ctl |=(0x01<<3);
   while( (time--) && (!(i2c->ctl & 0x08)) );
   if(time <= 0)
   {
    return -I2C_NOK_TOUT;
   }
   for(time=0;time<100;time++);
   time = 0xffff;
   data_addr[i] = i2c->data;
   while( (time--) && (i2c->status != I2C_DATAREAD_ACK) );
   if(time <= 0)
   {
                            return -I2C_NOK_TOUT;
   }
  }
  time = 0xffff;
                i2c->ctl &= 0xFb;  //the last data packet,not send ack
                i2c->ctl |= (0x01<<3);
  while( (time--) && (!(i2c->ctl & 0x08)) );
  if(time <= 0)
  {
                    return -I2C_NOK_TOUT;
  }
  for(time=0;time<100;time++);
  data_addr[data_count - 1] = i2c->data;
  while( (time--) && (i2c->status != I2C_DATAREAD_NACK) );
  if(time <= 0)
  {
                    return -I2C_NOK_TOUT;
  }
}
return I2C_OK;
}
static int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
int   i, ret, ret0, addrlen;
char  *slave_reg;
ret0 = -1;
ret = i2c_sendstart();
if(ret)
{
  goto i2c_read_err_occur;
}
ret = i2c_sendslaveaddr(chip, I2C_WRITE);
if(ret)
{
  goto i2c_read_err_occur;
}
//send byte address
if(alen >= 3)
{
  addrlen = 2;
}
else if(alen <= 1)
{
  addrlen = 0;
}
else
{
  addrlen = 1;
}
slave_reg = (char *)&addr;
for (i = addrlen; i>=0; i--)
{
  ret = i2c_sendbyteaddr(slave_reg[i] & 0xff);
  if(ret)
  {
   goto i2c_read_err_occur;
  }
}
ret = i2c_sendRestart();
if(ret)
{
  goto i2c_read_err_occur;
}
ret = i2c_sendslaveaddr(chip, I2C_READ);
if(ret)
{
  goto i2c_read_err_occur;
}
//get data
ret = i2c_getdata(buffer, len);
if(ret)
{
  goto i2c_read_err_occur;
}
ret0 = 0;
i2c_read_err_occur:
i2c_stop();
return ret0;
}


static __s32 i2c_senddata(__u8  *data_addr, __u32 data_count)
{
    __s32  time = 0xffff;
    __u32  i;
for(i=0; i< data_count; i++)
{
  time = 0xffff;
         i2c->data = data_addr[i];
#if defined(CONFIG_ARCH_SUN5I)|defined(CONFIG_ARCH_SUN7I)
  i2c->ctl &= 0xF7;
#else
                i2c->ctl |= (0x01<<3);
#endif
                while( (time--) && (!(i2c->ctl & 0x08)) );
  if(time <= 0)
  {
                    return -I2C_NOK_TOUT;
  }
  time = 0xffff;
  while( (time--) && (i2c->status != I2C_DATAWRITE_ACK) );
                if(time <= 0)
  {
                    return -I2C_NOK_TOUT;
  }
}
return I2C_OK;
}

static int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
int   i, ret, ret0, addrlen;
char  *slave_reg;
ret0 = -1;
ret = i2c_sendstart();
if(ret)
{
  printf("BAD Send start\n");
  goto i2c_write_err_occur;
}
retry:
ret = i2c_sendslaveaddr(chip, I2C_WRITE);
if(ret)
{
  printf("BAD Send slavaddr\n");
  goto retry;
//  goto i2c_write_err_occur;
}
//send byte address
if(alen >= 3)
{
  addrlen = 2;
}
else if(alen <= 1)
{
  addrlen = 0;
}
else
{
  addrlen = 1;
}
slave_reg = (char *)&addr;
for (i = addrlen; i>=0; i--)
{
  ret = i2c_sendbyteaddr(slave_reg[i] & 0xff);
  if(ret)
  {
   goto i2c_write_err_occur;
  }
}
ret = i2c_senddata(buffer, len);
if(ret)
{
  goto i2c_write_err_occur;
}
ret0 = 0;
i2c_write_err_occur:
i2c_stop();
return ret0;
}
int orangepi_i2c_write(unsigned char chip, unsigned char addr, unsigned char data)
{
int ret = i2c_write(chip, addr, 1, &data, 1);
if (ret != 0)
  printf("BAD I2C WRITE\n");
else
  printf("SUCCEED I2C WRITE\n");
return ret;
}
int orangepi_i2c_read(unsigned char chip, unsigned char addr, unsigned char *buffer)
{
int ret = i2c_read(chip, addr, 1, buffer, 1);

if (ret != 0)
  printf("BAD I2C READ\n");
else
  printf("SUCCEED I2C READ\n");
return ret;
}

static int set_cpus_i2c_clock(unsigned int onoff)
{
int reg_value = 0;
//opne CPUS PIO
volatile unsigned int reg_val;
// R_GPIO reset deassert
reg_val = readl(SUNXI_RPRCM_BASE+0xb0);
reg_val |= 1;
writel(reg_val, SUNXI_RPRCM_BASE+0xb0);
// R_GPIO GATING open
reg_val = readl(SUNXI_RPRCM_BASE+0x28);
reg_val |= 1;
writel(reg_val, SUNXI_RPRCM_BASE+0x28);
//R_GPIO: PL0,PL1 cfg 2
writel(readl(0x01F02C00)& ~0xff,0x01F02C00);
writel(readl(0x01F02C00)|0x22,0x01F02C00);
//PL0,PL1 pull up 1
writel(readl(0x01F02C00+0x1C)& ~0xf,0x01F02C00+0x1C);
writel(readl(0x01F02C00+0x1C)|0x5,0x01F02C00+0x1C);
//PL0,PL1 drv 2
writel(readl(0x01F02C00+0x14)& ~0xf,0x01F02C00+0x14);
writel(readl(0x01F02C00+0x14)|0xa,0x01F02C00+0x14);
//deassert twi reset
reg_value = readl(SUNXI_RPRCM_BASE + 0xb0);
reg_value &= ~(0x01 << 6);
writel(reg_value,SUNXI_RPRCM_BASE + 0xb0);
reg_value = readl(SUNXI_RPRCM_BASE + 0xb0);
reg_value |= 0x01 << 6;
writel(reg_value,SUNXI_RPRCM_BASE + 0xb0);
__usdelay(10);
//open twi gating
reg_value = readl(SUNXI_RPRCM_BASE + 0x28);
if(onoff)
  reg_value |= 0x01 << 6;
else
  reg_value &= ~(0x01 << 6);
writel(reg_value,SUNXI_RPRCM_BASE + 0x28);
__usdelay(10);
return 0;
}

static void i2c_set_clock(int speed, int slaveaddr)
{
int i, clk_n, clk_m;
     /* reset i2c control  */
i = 0xffff;
i2c->srst = 1;
while((i2c->srst) && (i))
{
     i --;
}
if((i2c->lcr & 0x30) != 0x30 )
{
     /* toggle I2CSCL until bus idle */
     i2c->lcr = 0x05;
     __usdelay(500);
     i = 10;
      while ((i > 0) && ((i2c->lcr & 0x02) != 2))
      {
       i2c->lcr |= 0x08;
       __usdelay(1000);
       i2c->lcr &= ~0x08;
       __usdelay(1000);
       i--;
      }
      i2c->lcr = 0x0;
      __usdelay(500);
}
if(speed < 100)
{
     speed = 100;
}
else if(speed > 400)
{
     speed = 400;
}
clk_n = 1;
clk_m = (24000/10)/((2^clk_n) * speed) - 1;
i2c->clk = (clk_m<<3) | clk_n;
i2c->ctl = 0x40;
i2c->eft = 0;
}
void orangepi_i2c_init_cpus(int speed, int slaveaddr)
{
i2c = (struct sunxi_twi_reg *)SUNXI_RTWI_BASE;
set_cpus_i2c_clock(1);
i2c_set_clock(speed,slaveaddr);
return ;
}
This snippet was truncated for display: see it in full.
wrap
Today at 2:18 PM • 11KB C snippet • Shared in common Unshare from #common
bug Today at 2:18 PM  
static int set_cpus_i2c_clock(unsigned int onoff)
{
   int reg_value = 0;
   //opne CPUS PIO
   volatile unsigned int reg_val;
   // R_GPIO reset deassert
   reg_val = readl(SUNXI_RPRCM_BASE+0xb0);
   reg_val |= 1;
   writel(reg_val, SUNXI_RPRCM_BASE+0xb0);
   // R_GPIO GATING open
   reg_val = readl(SUNXI_RPRCM_BASE+0x28);
   reg_val |= 1;
   writel(reg_val, SUNXI_RPRCM_BASE+0x28);
   //R_GPIO: PL0,PL1 cfg 2
   writel(readl(0x01F02C00)& ~0xff,0x01F02C00);
   writel(readl(0x01F02C00)|0x22,0x01F02C00);
   //PL0,PL1 pull up 1
   writel(readl(0x01F02C00+0x1C)& ~0xf,0x01F02C00+0x1C);
   writel(readl(0x01F02C00+0x1C)|0x5,0x01F02C00+0x1C);
   //PL0,PL1 drv 2
   writel(readl(0x01F02C00+0x14)& ~0xf,0x01F02C00+0x14);
   writel(readl(0x01F02C00+0x14)|0xa,0x01F02C00+0x14);
   //deassert twi reset
   reg_value = readl(SUNXI_RPRCM_BASE + 0xb0);
   reg_value &= ~(0x01 << 6);
   writel(reg_value,SUNXI_RPRCM_BASE + 0xb0);
   reg_value = readl(SUNXI_RPRCM_BASE + 0xb0);
   reg_value |= 0x01 << 6;
   writel(reg_value,SUNXI_RPRCM_BASE + 0xb0);
   __usdelay(10);
   //open twi gating
   reg_value = readl(SUNXI_RPRCM_BASE + 0x28);
   if(onoff)
       reg_value |= 0x01 << 6;
   else
       reg_value &= ~(0x01 << 6);
   writel(reg_value,SUNXI_RPRCM_BASE + 0x28);
   __usdelay(10);
   return 0;
}

2

threads

6

posts

46

credits

Novice

Rank: 1

credits
46
Published in 2016-10-12 02:58:19 | Show all floors
Hello admin,
though I'm a novice to C Programming, I tried to figure out what your code would do. It seems to talk directly to the SOC chip registers ?! When I compile this code, how can I run it - I guess I need to call "orangepi_i2c_init_cpus" ?
Can you explain a little bit more in detail, how to use it ? I investigated the kernel source for orangepi PC and found the bus speed is hard coded to 400 Khz in i2c-sunxi.h
To be honest I take it as unhandy to change the bus speed by re-compiling the kernel every time. Does your code overcome this need ?

Any further hint would be appreciated.

Regards Rolf
You need to log in before you can reply login | Register

Points Rule

Quick reply Top Return list