| /*************************************************************************** |
| * __________ __ ___. |
| * 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; |
| } |