rockbox/firmware/target/arm/tms320dm320/i2c-dm320.c

161 lines
4.8 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2008 by Maurus Cuelenaere
*
* DM320 I²C driver
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "thread.h"
#include "i2c-dm320.h"
#define I2C_SCS_COND_START 0x0001
#define I2C_SCS_COND_STOP 0x0002
#define I2C_SCS_XMIT 0x0004
#define I2C_TX_ACK (1 << 8)
static struct mutex i2c_mtx;
static inline void i2c_begin(void)
{
mutex_lock(&i2c_mtx);
}
static inline void i2c_end(void)
{
mutex_unlock(&i2c_mtx);
}
static inline bool i2c_getack(void)
{
return (IO_I2C_RXDATA >> 8) & 1;
}
static inline void i2c_ack(void)
{
IO_I2C_TXDATA |= I2C_TX_ACK;
}
#define WAIT_FOR_I2C if(IO_I2C_SCS & 0x4){ \
while(IO_I2C_SCS & 0x4) { \
asm volatile("nop"); \
} \
} \
static inline void i2c_start(void)
{
IO_I2C_SCS |= I2C_SCS_XMIT;
return;
}
int i2c_write(unsigned short address, const unsigned char *buf, int count)
{
int i;
int ret=0;
i2c_begin();
IO_I2C_TXDATA = ( (address << 1) & 0xFF ) | (address>0x7F ? 0 : 1 ) | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //clear conditions
IO_I2C_SCS |= I2C_SCS_COND_START; // write 'start condition'
i2c_start();
WAIT_FOR_I2C;
/* experimental */
if(address>0x7F){ // check if it is 10-bit instead of 7-bit
IO_I2C_TXDATA = ( (address >> 7) & 0xFF) | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //normal transfer
i2c_start();
WAIT_FOR_I2C;
IO_I2C_TXDATA = ( (address << 1) & 0xFF) | 1 | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //clear conditions
IO_I2C_SCS |= I2C_SCS_COND_START; //write 'start condition'
i2c_start();
WAIT_FOR_I2C;
}
for(i=0; i<count; i++){
IO_I2C_TXDATA = buf[i] | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //normal transfer
i2c_start();
WAIT_FOR_I2C;
if(!i2c_getack())
ret = -1;
}
IO_I2C_SCS &= ~0x3; //clear conditions
IO_I2C_SCS |= I2C_SCS_COND_STOP; //write 'stop condition'
i2c_start();
WAIT_FOR_I2C;
i2c_end();
return ret;
}
int i2c_read(unsigned short address, unsigned char* buf, int count)
{
int i;
int ack=0;
i2c_begin();
IO_I2C_TXDATA = ( (address << 1) & 0xFF ) | (address>0x7F ? 0 : 1 ) | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //clear conditions
IO_I2C_SCS |= I2C_SCS_COND_START; // write 'start condition'
i2c_start();
WAIT_FOR_I2C;
/* experimental */
if(address>0x7F){ // check if it is 10-bit instead of 7-bit
IO_I2C_TXDATA = ( (address >> 7) & 0xFF ) | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //normal transfer
i2c_start();
WAIT_FOR_I2C;
IO_I2C_TXDATA = ( (address << 1) & 0xFF ) | 1 | I2C_TX_ACK;
IO_I2C_SCS &= ~0x3; //clear conditions
IO_I2C_SCS |= I2C_SCS_COND_START; //write 'start condition'
i2c_start();
WAIT_FOR_I2C;
}
for(i=0; i<count; i++){
unsigned short temp;
IO_I2C_TXDATA = 0xFF | ( (count-1)==i ? I2C_TX_ACK : 0);
IO_I2C_SCS &= ~0x3; //normal transfer
i2c_start();
WAIT_FOR_I2C;
temp = IO_I2C_RXDATA;
buf[i] = temp & 0xFF;
ack = (temp & 0x100) >> 8;
}
IO_I2C_SCS &= ~0x3; //clear conditions
IO_I2C_SCS |= I2C_SCS_COND_STOP; //write 'stop condition'
i2c_start();
WAIT_FOR_I2C;
i2c_end();
return ack;
}
void i2c_init(void)
{
mutex_init(&i2c_mtx);
#ifdef CREATIVE_ZVx //TODO: mimic OF I2C clock settings; currently this is done by the bootloader
IO_CLK_MOD2 &= ~CLK_MOD2_I2C; // turn I²C clock off (just to be sure)
IO_CLK_LPCTL1 &= ~1; // set Powerdown mode to off
IO_CLK_SEL0 &= ~0x800; // set I²C clock to PLLA
IO_CLK_DIV4 &= ~0x1F; // I²C clock division = 1
IO_CLK_MOD2 |= CLK_MOD2_I2C; // enable I²C clock
#endif
IO_I2C_SCS &= ~0x8; //set clock to 100 kHz
IO_INTC_EINT2 &= ~INTR_EINT2_I2C; // disable I²C interrupt
}