From 5f1590bf4a1da9f6edd11c167e67109e9e06e03a Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sun, 30 Mar 2008 05:38:28 +0000 Subject: 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 --- .../target/arm/s3c2440/gigabeat-fx/i2c-meg-fx.c | 127 ++++++++++++--------- 1 file changed, 72 insertions(+), 55 deletions(-) (limited to 'firmware/target/arm') diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/i2c-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/i2c-meg-fx.c index 45140c0f0c..7e4e608e15 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/i2c-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/i2c-meg-fx.c @@ -19,79 +19,55 @@ #include "system.h" #include "i2c-meg-fx.h" -/* Only implements sending bytes for now. Adding receiving bytes should be - straightforward if needed. No yielding is present since the calls only - involve setting audio codec registers - a very rare event. */ - -/* 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 struct wakeup i2c_wake; /* Transfer completion signal */ +static struct mutex i2c_mtx; /* Mutual exclusion */ +static unsigned char *buf_ptr; /* Next byte to transfer */ +static int buf_count; /* Number of bytes remaining to transfer */ static void i2c_stop(void) { /* Generate STOP */ 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) -{ - /* Write byte to shift register */ - IICDS = byte; - /* Clear pending interrupt to continue */ - IICCON &= ~I2C_TXRX_INTPND; - return i2c_getack(); + /* No more interrupts, clear pending interrupt to continue */ + IICCON &= ~(I2C_TXRX_INTPND | I2C_TXRX_INTENB); } void i2c_write(int addr, const unsigned char *buf, int count) { + if (count <= 0) + return; + + mutex_lock(&i2c_mtx); + /* Turn on I2C clock */ CLKCON |= (1 << 16); /* Set mode to master transmitter and enable lines */ IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB; - /* Wait for bus to be available */ - if (WAIT_COND(!(IICSTAT & I2C_BUSY), COND_RET)) - { - /* Send slave address and then data */ - IICCON &= ~I2C_TXRX_INTPND; - IICCON |= I2C_TXRX_INTENB; + /* Set buffer start and count */ + buf_ptr = (unsigned char *)buf; + buf_count = count; - IICDS = addr & 0xfe; + /* Send slave address and then data */ + SRCPND = IIC_MASK; + INTPND = IIC_MASK; - if (i2c_start() == 0) - while (count-- > 0 && i2c_outb(*buf++) == 0); + IICCON |= I2C_TXRX_INTENB; - i2c_stop(); + /* Load slave address into shift register */ + IICDS = addr & 0xfe; + + /* Generate START */ + IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_START | I2C_RXTX_ENB; - IICCON &= ~I2C_TXRX_INTENB; + if (wakeup_wait(&i2c_wake, HZ) != WAIT_SUCCEEDED) + { + /* Something went wrong - stop transmission */ + int oldlevel = disable_irq_save(); + i2c_stop(); + restore_irq(oldlevel); } /* 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 */ CLKCON &= ~(1 << 16); + + mutex_unlock(&i2c_mtx); } void i2c_init(void) { - /* We poll I2C interrupts */ - INTMSK |= (1 << 27); + /* Init kernel objects */ + 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 */ CLKCON |= (1 << 16); @@ -123,3 +110,33 @@ void i2c_init(void) /* Turn off I2C clock */ 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; +} -- cgit