rk27xx: Turn off i2c clock when not in use

Change-Id: Ifc6c25a53ace1a5f4d716a33d4979ea0a37fac98
This commit is contained in:
Marcin Bukat 2012-08-27 23:18:31 +02:00
parent 16335da1cf
commit ae27c331e1

View file

@ -40,7 +40,7 @@ static struct mutex i2c_mtx;
static bool i2c_write_byte(uint8_t data, bool start) static bool i2c_write_byte(uint8_t data, bool start)
{ {
long timeout = current_tick + 50; long timeout = current_tick + HZ/50;
/* START */ /* START */
I2C_CONR |= (1<<3) | (1<<2); /* master port enable, transmit bit */ I2C_CONR |= (1<<3) | (1<<2); /* master port enable, transmit bit */
@ -66,34 +66,34 @@ static bool i2c_write_byte(uint8_t data, bool start)
static bool i2c_read_byte(unsigned char *data) static bool i2c_read_byte(unsigned char *data)
{ {
long timeout = current_tick + HZ / 50; long timeout = current_tick + HZ/50;
I2C_LCMR = (1<<2); /* resume op */ I2C_LCMR = (1<<2); /* resume op */
while (!(I2C_ISR & (1<<1))) while (!(I2C_ISR & (1<<1)))
if (TIME_AFTER(current_tick, timeout)) if (TIME_AFTER(current_tick, timeout))
return false; return false;
*data = I2C_MRXR; *data = I2C_MRXR;
/* clear status bit */ /* clear status bit */
I2C_ISR &= ~(1<<1); I2C_ISR &= ~(1<<1);
return true; return true;
} }
static bool i2c_stop(void) static bool i2c_stop(void)
{ {
long timeout = current_tick + HZ / 50; long timeout = current_tick + HZ/50;
I2C_CONR &= ~(1<<4); I2C_CONR &= ~(1<<4);
I2C_LCMR |= (1<<2) | (1<<1); /* resume op, stop */ I2C_LCMR |= (1<<2) | (1<<1); /* resume op, stop */
while (I2C_LCMR & (1<<1)) while (I2C_LCMR & (1<<1))
if (TIME_AFTER(current_tick, timeout)) if (TIME_AFTER(current_tick, timeout))
return false; return false;
return true; return true;
} }
/* route i2c bus to internal codec or external bus /* route i2c bus to internal codec or external bus
@ -103,154 +103,173 @@ static bool i2c_stop(void)
*/ */
static void i2c_iomux(unsigned char slave) static void i2c_iomux(unsigned char slave)
{ {
unsigned long muxa = SCU_IOMUXA_CON & ~(0x1f<<14); unsigned long muxa = SCU_IOMUXA_CON & ~(0x1f<<14);
if ((slave & 0xfe) == (0x27<<1)) if ((slave & 0xfe) == (0x27<<1))
{ {
/* internal codec */ /* internal codec */
SCU_IOMUXA_CON = (muxa | (1<<16) | (1<<14)); SCU_IOMUXA_CON = (muxa | (1<<16) | (1<<14));
} }
else else
{ {
/* external I2C bus */ /* external I2C bus */
SCU_IOMUXA_CON = (muxa | (1<<18)); SCU_IOMUXA_CON = (muxa | (1<<18));
} }
} }
void i2c_init(void) void i2c_init(void)
{ {
mutex_init(&i2c_mtx); mutex_init(&i2c_mtx);
SCU_CLKCFG &= ~(1<< 20);
I2C_OPR |= (1<<7); /* reset state machine */ /* ungate i2c module clock */
sleep(HZ/100); SCU_CLKCFG &= ~(1<< 20);
I2C_OPR &= ~((1<<7) | (1<<6)); /* clear ENABLE bit, deasert reset */
/* set I2C divider to stay within allowed SCL freq limit I2C_OPR |= (1<<7); /* reset state machine */
* APBfreq = 50Mhz sleep(HZ/100);
* SCLfreq = (APBfreq/5*(I2CCDVR[5:3] + 1) * 2^((I2CCDVR[2:0] + 1)) I2C_OPR &= ~((1<<7) | (1<<6)); /* clear ENABLE bit, deasert reset */
*/
/* we are driving this slightly above specs /* set I2C divider to stay within allowed SCL freq limit
* (6<<3) | (1<<0) 416kHz * APBfreq = 50Mhz
* (7<<3) | (1<<0) 357kHz * SCLfreq = (APBfreq/5*(I2CCDVR[5:3] + 1) * 2^((I2CCDVR[2:0] + 1))
* (6<<3) | (2<<0) 208kHz */
*/
I2C_OPR = (I2C_OPR & ~(0x3F)) | (6<<3) | (1<<0);
I2C_IER = 0x00; /* we are driving this slightly above specs
* (6<<3) | (1<<0) 416kHz
* (7<<3) | (1<<0) 357kHz
* (6<<3) | (2<<0) 208kHz
*/
I2C_OPR = (I2C_OPR & ~(0x3F)) | (6<<3) | (1<<0);
I2C_OPR |= (1<<6); /* enable i2c core */ I2C_IER = 0x00;
I2C_OPR |= (1<<6); /* enable i2c core */
/* turn off i2c module clock until we need to comunicate */
SCU_CLKCFG |= (1<< 20);
} }
int i2c_write(unsigned char slave, int address, int len, int i2c_write(unsigned char slave, int address, int len,
const unsigned char *data) const unsigned char *data)
{ {
mutex_lock(&i2c_mtx); int ret = 0;
i2c_iomux(slave); mutex_lock(&i2c_mtx);
/* clear all flags */ i2c_iomux(slave);
I2C_ISR = 0x00;
I2C_IER = 0x00;
/* START */ /* ungate i2c clock */
if (! i2c_write_byte(slave & ~1, true)) SCU_CLKCFG &= ~(1<<20);
{
mutex_unlock(&i2c_mtx);
return 1;
}
if (address >= 0) /* clear all flags */
{ I2C_ISR = 0x00;
if (! i2c_write_byte(address, false)) I2C_IER = 0x00;
{
mutex_unlock(&i2c_mtx);
return 2;
}
}
/* write data */ /* START */
while (len--) if (! i2c_write_byte(slave & ~1, true))
{ {
if (! i2c_write_byte(*data++, false)) ret = 1;
{ goto end;
mutex_unlock(&i2c_mtx); }
return 4;
}
}
/* STOP */ if (address >= 0)
if (! i2c_stop()) {
{ if (! i2c_write_byte(address, false))
mutex_unlock(&i2c_mtx); {
return 5; ret = 2;
} goto end;
}
}
mutex_unlock(&i2c_mtx); /* write data */
return 0; while (len--)
{
if (! i2c_write_byte(*data++, false))
{
ret = 4;
goto end;
}
}
/* STOP */
if (! i2c_stop())
{
ret = 5;
goto end;
}
end:
mutex_unlock(&i2c_mtx);
SCU_CLKCFG |= (1<<20);
return ret;
} }
int i2c_read(unsigned char slave, int address, int len, unsigned char *data) int i2c_read(unsigned char slave, int address, int len, unsigned char *data)
{ {
mutex_lock(&i2c_mtx); int ret = 0;
i2c_iomux(slave); mutex_lock(&i2c_mtx);
/* clear all flags */ i2c_iomux(slave);
I2C_ISR = 0x00;
I2C_IER = 0x00;
if (address >= 0) /* ungate i2c module clock */
{ SCU_CLKCFG &= ~(1<<20);
/* START */
if (! i2c_write_byte(slave & ~1, true))
{
mutex_unlock(&i2c_mtx);
return 1;
}
/* write address */ /* clear all flags */
if (! i2c_write_byte(address, false)) I2C_ISR = 0x00;
{ I2C_IER = 0x00;
mutex_unlock(&i2c_mtx);
return 2;
}
}
/* (repeated) START */ if (address >= 0)
if (! i2c_write_byte(slave | 1, true)) {
{ /* START */
mutex_unlock(&i2c_mtx); if (! i2c_write_byte(slave & ~1, true))
return 3; {
} ret = 1;
goto end;
}
I2C_CONR &= ~(1<<3); /* clear transmit bit (switch to receive mode) */ /* write address */
if (! i2c_write_byte(address, false))
{
ret = 2;
goto end;
}
}
while (len) /* (repeated) START */
{ if (! i2c_write_byte(slave | 1, true))
if (! i2c_read_byte(data++)) {
{ ret = 3;
mutex_unlock(&i2c_mtx); goto end;
return 4; }
}
if (len == 1) I2C_CONR &= ~(1<<3); /* clear transmit bit (switch to receive mode) */
I2C_CONR |= (1<<4); /* NACK */
else
I2C_CONR &= ~(1<<4); /* ACK */
len--; while (len)
} {
if (! i2c_read_byte(data++))
{
ret = 4;
goto end;
}
if (len == 1)
I2C_CONR |= (1<<4); /* NACK */
else
I2C_CONR &= ~(1<<4); /* ACK */
len--;
}
/* STOP */ /* STOP */
if (! i2c_stop()) if (! i2c_stop())
{ {
mutex_unlock(&i2c_mtx); ret = 5;
return 5; goto end;
} }
mutex_unlock(&i2c_mtx); end:
return 0; mutex_unlock(&i2c_mtx);
SCU_CLKCFG |= (1<<20);
return ret;
} }