blob: 7e4e608e154f586332137a61e95eec55f5b04860 [file] [log] [blame]
/***************************************************************************
* __________ __ ___.
* 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;
}