rockbox/firmware/target/arm/gigabeat/meg-fx/sc606-meg-fx.c
Steve Gotthardt ccfa883d78 The i2c driver did not conform with i2c spec and was missing the restart. Some of the commands worked, but not every time.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11915 a1c6a512-1295-4272-9138-f99709370657
2007-01-05 08:50:09 +00:00

223 lines
3.7 KiB
C

#include "config.h"
#include "cpu.h"
#include <stdbool.h>
#include "kernel.h"
#include "system.h"
#include "hwcompat.h"
#include "logf.h"
#include "debug.h"
#include "string.h"
#define SLAVE_ADDRESS 0xCC
#define SDA_LO (GPHDAT &= ~(1 << 9))
#define SDA_HI (GPHDAT |= (1 << 9))
#define SDA_INPUT (GPHCON &= ~(3 << 18))
#define SDA_OUTPUT (GPHCON |= (1 << 18))
#define SDA (GPHDAT & (1 << 9))
#define SCL_LO (GPHDAT &= ~(1 << 10))
#define SCL_HI (GPHDAT |= (1 << 10))
#define SCL_INPUT (GPHCON &= ~(3 << 20))
#define SCL_OUTPUT (GPHCON |= (1 << 20))
#define SCL (GPHDAT & (1 << 10))
#define SCL_SDA_HI (GPHDAT |= (3 << 9))
/* The SC606 can clock at 400KHz: 2.5uS period -> 1.25uS half period */
/* The high and low times are different enough to need different timings */
/* At 300Mhz - one loop takes about 10 cycles */
#define DELAY_LO do { volatile int _x; for(_x=0;_x<20;_x++);} while (0)
#define DELAY do { volatile int _x; for(_x=0;_x<15;_x++);} while (0)
#define DELAY_HI do { volatile int _x; for(_x=0;_x<10;_x++);} while (0)
static void sc606_i2c_start(void)
{
SCL_SDA_HI;
DELAY;
SDA_LO;
DELAY;
SCL_LO;
}
static void sc606_i2c_restart(void)
{
SCL_SDA_HI;
DELAY;
SDA_LO;
DELAY;
SCL_LO;
}
static void sc606_i2c_stop(void)
{
SDA_LO;
SCL_HI;
DELAY_HI;
SDA_HI;
}
static void sc606_i2c_ack(void)
{
SDA_LO;
SCL_HI;
DELAY_HI;
SCL_LO;
}
static int sc606_i2c_getack(void)
{
int ret;
/* Don't need a delay since follows a data bit with a delay on the end */
SDA_INPUT; /* And set to input */
DELAY;
SCL_HI;
ret = (SDA != 0); /* ack failed if SDA is not low */
DELAY_HI;
SCL_LO;
DELAY_LO;
SDA_HI;
SDA_OUTPUT;
DELAY_LO;
return ret;
}
static void sc606_i2c_outb(unsigned char byte)
{
int i;
/* clock out each bit, MSB first */
for (i = 0x80; i; i >>= 1)
{
if (i & byte)
{
SDA_HI;
}
else
{
SDA_LO;
}
DELAY;
SCL_HI;
DELAY_HI;
SCL_LO;
DELAY_LO;
}
SDA_HI;
}
static unsigned char sc606_i2c_inb(void)
{
int i;
unsigned char byte = 0;
SDA_INPUT; /* And set to input */
/* clock in each bit, MSB first */
for (i = 0x80; i; i >>= 1) {
SCL_HI;
if (SDA)
byte |= i;
SCL_LO;
}
SDA_OUTPUT;
sc606_i2c_ack();
return byte;
}
/* returns number of acks that were bad */
int sc606_write(unsigned char reg, unsigned char data)
{
int x = 0;
sc606_i2c_start();
sc606_i2c_outb(SLAVE_ADDRESS);
x = sc606_i2c_getack();
sc606_i2c_outb(reg);
x += sc606_i2c_getack();
sc606_i2c_restart();
sc606_i2c_outb(SLAVE_ADDRESS);
x += sc606_i2c_getack();
sc606_i2c_outb(data);
x += sc606_i2c_getack();
sc606_i2c_stop();
return x;
}
int sc606_read(unsigned char reg, unsigned char* data)
{
int x = 0;
sc606_i2c_start();
sc606_i2c_outb(SLAVE_ADDRESS);
x = sc606_i2c_getack();
sc606_i2c_outb(reg);
x += sc606_i2c_getack();
sc606_i2c_restart();
sc606_i2c_outb(SLAVE_ADDRESS | 1);
x += sc606_i2c_getack();
*data = sc606_i2c_inb();
sc606_i2c_stop();
return x;
}
void sc606_init(void)
{
volatile int i;
/* Set GPB2 (EN) to 1 */
GPBCON = (GPBCON & ~(3<<4)) | 1<<4;
/* Turn enable line on */
GPBDAT |= 1<<2;
/* OFF GPBDAT &= ~(1 << 2); */
/* About 400us - needs 350us */
for (i = 200; i; i--)
{
DELAY_LO;
}
/* Set GPH9 (SDA) and GPH10 (SCL) to 1 */
GPHUP &= ~(3<<9);
GPHCON = (GPHCON & ~(0xF<<18)) | 5<<18;
}