/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 by Michael Sevakis * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #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 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(); } void i2c_write(int addr, const unsigned char *buf, int count) { /* 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; IICDS = addr & 0xfe; if (i2c_start() == 0) while (count-- > 0 && i2c_outb(*buf++) == 0); i2c_stop(); IICCON &= ~I2C_TXRX_INTENB; } /* Go back to slave receive mode and disable lines */ IICSTAT = 0; /* Turn off I2C clock */ CLKCON &= ~(1 << 16); } void i2c_init(void) { /* We poll I2C interrupts */ INTMSK |= (1 << 27); /* Turn on I2C clock */ CLKCON |= (1 << 16); /* Set GPE15 (IICSDA) and GPE14 (IICSCL) to IIC */ GPECON = (GPECON & ~((3 << 30) | (3 << 28))) | ((2 << 30) | (2 << 28)); /* Bus ACK, IICCLK: fPCLK / 16, Rx/Tx Int: Disable, Tx clock: IICCLK/8 */ /* OF PCLK: 49.1568MHz / 16 / 8 = 384.0375 kHz */ IICCON = (7 << 0); /* SDA line delayed 0 PCLKs */ IICLC = (0 << 0); /* Turn off I2C clock */ CLKCON &= ~(1 << 16); }