Have meg-fx i2c driver use the wakeup functionality by making it interrupt-based (and serves as a simple usage example).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16886 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2008-03-30 05:38:28 +00:00
parent bc192c953e
commit 5f1590bf4a

View file

@ -19,79 +19,55 @@
#include "system.h" #include "system.h"
#include "i2c-meg-fx.h" #include "i2c-meg-fx.h"
/* Only implements sending bytes for now. Adding receiving bytes should be static struct wakeup i2c_wake; /* Transfer completion signal */
straightforward if needed. No yielding is present since the calls only static struct mutex i2c_mtx; /* Mutual exclusion */
involve setting audio codec registers - a very rare event. */ static unsigned char *buf_ptr; /* Next byte to transfer */
static int buf_count; /* Number of bytes remaining to transfer */
/* Wait for a condition on the bus, optionally returning it */
#define COND_RET _c;
#define COND_VOID
#define WAIT_COND(cond, ret) \
({ \
int _t = current_tick + 2; \
bool _c; \
while (1) { \
_c = !!(cond); \
if (_c || TIME_AFTER(current_tick, _t)) \
break; \
} \
ret \
})
static int i2c_getack(void)
{
/* Wait for ACK: 0 = ack received, 1 = ack not received */
WAIT_COND(IICCON & I2C_TXRX_INTPND, COND_VOID);
return IICSTAT & I2C_ACK_L;
}
static int i2c_start(void)
{
/* Generate START */
IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_START | I2C_RXTX_ENB;
return i2c_getack();
}
static void i2c_stop(void) static void i2c_stop(void)
{ {
/* Generate STOP */ /* Generate STOP */
IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB; IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB;
/* Clear pending interrupt to continue */
IICCON &= ~I2C_TXRX_INTPND;
}
static int i2c_outb(unsigned char byte) /* No more interrupts, clear pending interrupt to continue */
{ IICCON &= ~(I2C_TXRX_INTPND | I2C_TXRX_INTENB);
/* Write byte to shift register */
IICDS = byte;
/* Clear pending interrupt to continue */
IICCON &= ~I2C_TXRX_INTPND;
return i2c_getack();
} }
void i2c_write(int addr, const unsigned char *buf, int count) void i2c_write(int addr, const unsigned char *buf, int count)
{ {
if (count <= 0)
return;
mutex_lock(&i2c_mtx);
/* Turn on I2C clock */ /* Turn on I2C clock */
CLKCON |= (1 << 16); CLKCON |= (1 << 16);
/* Set mode to master transmitter and enable lines */ /* Set mode to master transmitter and enable lines */
IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB; IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB;
/* Wait for bus to be available */ /* Set buffer start and count */
if (WAIT_COND(!(IICSTAT & I2C_BUSY), COND_RET)) buf_ptr = (unsigned char *)buf;
buf_count = count;
/* Send slave address and then data */
SRCPND = IIC_MASK;
INTPND = IIC_MASK;
IICCON |= I2C_TXRX_INTENB;
/* Load slave address into shift register */
IICDS = addr & 0xfe;
/* Generate START */
IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_START | I2C_RXTX_ENB;
if (wakeup_wait(&i2c_wake, HZ) != WAIT_SUCCEEDED)
{ {
/* Send slave address and then data */ /* Something went wrong - stop transmission */
IICCON &= ~I2C_TXRX_INTPND; int oldlevel = disable_irq_save();
IICCON |= I2C_TXRX_INTENB;
IICDS = addr & 0xfe;
if (i2c_start() == 0)
while (count-- > 0 && i2c_outb(*buf++) == 0);
i2c_stop(); i2c_stop();
restore_irq(oldlevel);
IICCON &= ~I2C_TXRX_INTENB;
} }
/* Go back to slave receive mode and disable lines */ /* Go back to slave receive mode and disable lines */
@ -99,12 +75,23 @@ void i2c_write(int addr, const unsigned char *buf, int count)
/* Turn off I2C clock */ /* Turn off I2C clock */
CLKCON &= ~(1 << 16); CLKCON &= ~(1 << 16);
mutex_unlock(&i2c_mtx);
} }
void i2c_init(void) void i2c_init(void)
{ {
/* We poll I2C interrupts */ /* Init kernel objects */
INTMSK |= (1 << 27); wakeup_init(&i2c_wake);
mutex_init(&i2c_mtx);
/* Clear pending source */
SRCPND = IIC_MASK;
INTPND = IIC_MASK;
/* Enable i2c interrupt in controller */
INTMOD &= ~IIC_MASK;
INTMSK &= ~IIC_MASK;
/* Turn on I2C clock */ /* Turn on I2C clock */
CLKCON |= (1 << 16); CLKCON |= (1 << 16);
@ -123,3 +110,33 @@ void i2c_init(void)
/* Turn off I2C clock */ /* Turn off I2C clock */
CLKCON &= ~(1 << 16); CLKCON &= ~(1 << 16);
} }
void IIC(void)
{
for (;;)
{
/* If ack was received from last byte and bytes are remaining */
if (--buf_count >= 0 && (IICSTAT & I2C_ACK_L) == 0)
{
/* Write next byte to shift register */
IICDS = *buf_ptr++;
/* Clear pending interrupt to continue */
IICCON &= ~I2C_TXRX_INTPND;
break;
}
/* Finished */
/* Generate STOP */
i2c_stop();
/* Signal thread */
wakeup_signal(&i2c_wake);
break;
}
/* Ack */
SRCPND = IIC_MASK;
INTPND = IIC_MASK;
}