/*************************************************************************** * __________ __ ___. * 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" 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; /* 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; /* Set buffer start and count */ 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) { /* Something went wrong - stop transmission */ int oldlevel = disable_irq_save(); i2c_stop(); restore_irq(oldlevel); } /* Go back to slave receive mode and disable lines */ IICSTAT = 0; /* Turn off I2C clock */ CLKCON &= ~(1 << 16); mutex_unlock(&i2c_mtx); } void i2c_init(void) { /* 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); /* 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); } 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; }