/*
*
* I2C Foundation Driver
*
* DESCRIPTION
* Tis driver handles the foundation layer of the MCF5282 I2C
* serial bus interface. It has an API that should be used by
* device specific I2c drivers, e.g. serial EEPROM, RTC, ADC
* and DAC. The API has generic and register-oriented functions.
*
* MODIFICATION HISTORY
* 12/29/03 LR: initial version
* 01/05/04 LR: add support for masked interrupts
* 02/07/04 LR: tested with RTC and EEPROM on bus, fixed many bugs
* 03/29/04 LR: add new {set,get}_page routines with 8/16 bit offset
* 10/12/04 VL: Use ICR17_I2C macro defined in mcf5282.h
*
* CAVEATS
* Restart does not work, wrecked in last rework attempt
* No timeout implemented when slave gets stuck, need to abort bus
* supports a single boardrate, least common denominator of all
* devices connected.
* Slave operations is neither properly supported nor tested.
*/
#include <targetos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <kernel.h>
#include <sys.h>
#include <errno.h>
#include "syslog.h"
#include <mcf5282.h>
//#include "board.h"
#include "i2c_drv.h"
//#define I2C_USE_RESTART
/* MCF5282 hardware definitions */
/* control & status register bits definitions */
#define I2CR_IEN ((unsigned char )0x80)
#define I2CR_IIEN ((unsigned char )0x40)
#define I2CR_MSTA ((unsigned char )0x20)
#define I2CR_MTX ((unsigned char )0x10)
#define I2CR_TNAK ((unsigned char )0x08)
#define I2CR_RSTA ((unsigned char )0x04)
#define I2SR_ICF ((unsigned char )0x80) // RO
#define I2SR_IAAS ((unsigned char )0x40) // RO
#define I2SR_IBB ((unsigned char )0x20) // RO
#define I2SR_IAL ((unsigned char )0x10) // RW
#define I2SR_SRW ((unsigned char )0x04) // RO
#define I2SR_IIF ((unsigned char )0x02) // RW
#define I2SR_RXAK ((unsigned char )0x01) // RO
#define MCF7282_I2C ((void *)(IPSBAR+0x300)) // base addr
#define I2C_MAGIC 0x12c12c
/* I2C registers */
struct i2c_regs {
volatile unsigned char
i2adr, pad1[3], /* slave address register */
i2fdr, pad2[3], /* frequency divider */
i2cr, pad3[3], /* control register */
i2sr, pad4[3], /* status register */
i2dr, pad5[3]; /* bi-directional data register */
} ;
/* structure per operation request */
struct i2c_req {
unsigned master: 1, /* 1 master, 0 slave */
rx:1, /* 0 Tx, 1 Rx */
wait:1, /* =1 if no data yet transferred */
done:1, /* =1 when all data xfered */
err:1, /* =1 if retriable error */
abrt:1, /* =1 if aborted by peer */
rest:1; /* =1 if req was started by ISR */
ui8 slv_addr: 8; /* slave's address if master */
ui8 *pData, *origData ;
unsigned dataCount, origCount ;
struct i2c_req * next ; /* ptr to next request queued */
TASK task; /* task waiting on request */
};
/* driver control structure */
struct i2c_drv {
struct i2c_req /* separate queues for master/slave */
* req_curr_master,
* req_curr_slave_tx,
* req_curr_slave_rx,
* req_last_master,
* req_last_slave ;
PART partId; /* memory partition for buffers */
unsigned magic; /* set correctly after good init */
// temp. debug fields
struct i2c_req prev, pprev;
};
static struct i2c_drv i2c ; /* only one instance is supported */
/* dummy variable to avoid compiler optimizations */
static volatile unsigned char dummy ;
/* Prototype */
static void i2c_isr( void );
static void i2c_isr_master_done( void );
/* clock divisor table */
struct i2c_div {
unsigned char fdr; /* register value */
unsigned short divisor; /* actual clock divisor */
};
/* there appears to be no clear rule in these numbers, thus table */
static const struct i2c_div i2c_div_tab[] = {
{ 0x00, 28 }, { 0x01, 30 }, { 0x02, 34 }, { 0x03, 40 },
{ 0x04, 44 }, { 0x05, 48 }, { 0x06, 56 }, { 0x07, 68 },
{ 0x08, 80 }, { 0x09, 88 }, { 0x0A, 104 }, { 0x0B, 128 },
{ 0x0C, 144 }, { 0x0D, 160 }, { 0x0E, 192 }, { 0x0F, 240 },
{ 0x10, 288 }, { 0x11, 320 }, { 0x12, 384 }, { 0x13, 480 },
{ 0x14, 576 }, { 0x15, 640 }, { 0x16, 768 }, { 0x17, 960 },
{ 0x18, 1152 }, { 0x19, 1280 }, { 0x1A, 1536 }, { 0x1B, 1920 },
{ 0x1C, 2304 }, { 0x1D, 2560 }, { 0x1E, 3072 }, { 0x1F, 3840 },
{ 0x20, 20 }, { 0x21, 22 }, { 0x22, 24 }, { 0x23, 26 },
{ 0x24, 28 }, { 0x25, 32 }, { 0x26, 36 }, { 0x27, 40 },
{ 0x28, 48 }, { 0x29, 56 }, { 0x2A, 64 }, { 0x2B, 72 },
{ 0x2C, 80 }, { 0x2D, 96 }, { 0x2E, 112 }, { 0x30, 160 },
{ 0x31, 192 }, { 0x32, 224 }, { 0x33, 256 }, { 0x34, 320 },
{ 0x35, 384 }, { 0x36, 448 }, { 0x37, 512 }, { 0x38, 640 },
{ 0x39, 768 }, { 0x3A, 896 }, { 0x3B, 1024 }, { 0x3C, 1280 },
{ 0x3D, 1536 }, { 0x3E, 1792 }, { 0x2F, 128 }, { 0x3F, 2048 },
};
/*
* initialize the frequency divider register for a gien baudrate
* tries to find teh BEST FIT in the constant table
*/
static void i2c_clock_init( unsigned baudrate )
{
unsigned i, d;
unsigned n = sizeof( i2c_div_tab) / sizeof( struct i2c_div ) ;
volatile struct i2c_regs *pReg = MCF7282_I2C;
for(d = i = 0; i < n; i++ )
{
/*
* use the highest divisor which yields clock
* slower than requested, i.e. closest to requested
* yet attempt as many times as needed till best is found
*/
if( (CPU_CLOCK_FREQ / i2c_div_tab[i].divisor) >= baudrate )
if( i2c_div_tab[i].divisor > d )
{
d = i2c_div_tab[i].divisor ;
pReg->i2fdr = i2c_div_tab[i].fdr ;
}
}
}
/*
* Wait for an approximate time in milliseconds
* method depends if interrupts are masked or not,
* so that any I2C API function may be called from an ISR
* or prior to kernel start, or from regular task context.
*/
static void i2c_wait( int msec )
{
volatile struct i2c_regs *pReg = MCF7282_I2C;
if( isrMasked() )
{
/* Interrupts locked, do polling */
msec = msec << 7 ; /* approx. 10 usec per iteration */
while( msec -- )
{
/* if ints enabled for i2c and int should have occured,
* pretend it did.
*/
if( (pReg->i2sr & I2SR_IIF) && (pReg->i2cr & I2CR_IIEN))
{
i2c_isr();
}
/* this will slow I2C down when ints are off */
}
}
else
{
/* Normal tasking mode, yield */
taskSleep( 1+msec/OsTicksPerSec );
}
}
/*
* initialize I2c hardware subsystem
*/
static int i2c_hw_init( unsigned baudrate, unsigned char slave_addr )
{
volatile struct i2c_regs *pReg = MCF7282_I2C;
/* configure the 2 pins for I2C functionality Port AS 0,1 */
MCF5282_PASPAR |= 0x000f ;
/* initialize clock for given baudrate */
i2c_clock_init( baudrate );
//pReg->i2fdr = 0x16;
/* set OUR slave address */
pReg->i2adr = slave_addr & 0xFE ;
/* First attempt to enable I2C, other bits are 0 */
pReg->i2cr = I2CR_IEN | I2CR_IIEN ;
pReg->i2sr = 0;
i2c_wait(1);
/* recover from BUSY bit condition */
if( pReg->i2sr & I2SR_IBB )
{
ui8 cr = I2CR_MSTA | I2CR_TNAK | I2CR_RSTA ;
ui8 retries = 0xff;
pReg->i2cr = 0;
pReg->i2sr = 0;
pReg->i2cr |= cr ; /* turn Master Rx while unit off */
pReg->i2sr =0;
dummy = pReg->i2dr ;
pReg->i2sr = 0 ;
pReg->i2cr |= I2CR_IEN; /* turn it off to slide into Rx */
pReg->i2sr = 0;
while (--retries && !(pReg->i2sr & I2SR_ICF))
i2c_wait(1);
dummy = pReg->i2dr ;
pReg->i2sr = 0;
while (--retries && !(pReg->i2sr & I2SR_ICF))
i2c_wait(1);
pReg->i2sr = 0;
pReg->i2cr &= ~ cr ; /* do STOP */
while (--retries && (pReg->i2sr & I2SR_IBB))
i2c_wait(10);
pReg->i2sr =0;
}
/* Enable again, with interrupts */
pReg->i2cr = I2CR_IEN | I2CR_IIEN ;
/* fail if still busy */
if( pReg->i2sr & I2SR_IBB )
return -1;
i2c.magic = I2C_MAGIC;
return 0;
}
/*
* subroutine to generate a start condition
* to kick-off a new request
* NOTE: at 100kbps, our typical speed, one bit takes
* 10 microsec, byte ~ 100 microsec,
* while OS ticks are 10 millisecond each -
* sometimes we must do tight loop!
*/
static i