From ccfa883d78546de493115faaff8a46d3a5ff087a Mon Sep 17 00:00:00 2001 From: Steve Gotthardt Date: Fri, 5 Jan 2007 08:50:09 +0000 Subject: 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 --- firmware/target/arm/gigabeat/meg-fx/sc606-meg-fx.c | 123 ++++++++++++++------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/firmware/target/arm/gigabeat/meg-fx/sc606-meg-fx.c b/firmware/target/arm/gigabeat/meg-fx/sc606-meg-fx.c index 9e9fcb3547..654095c2f5 100644 --- a/firmware/target/arm/gigabeat/meg-fx/sc606-meg-fx.c +++ b/firmware/target/arm/gigabeat/meg-fx/sc606-meg-fx.c @@ -10,23 +10,28 @@ #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 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_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) +#define SCL_SDA_HI (GPHDAT |= (3 << 9)) /* The SC606 can clock at 400KHz: 2.5uS period -> 1.25uS half period */ -/* At 300Mhz - if loop takes 10 cycles @ 3.3nS each -> 1.25uS / 30nS -> 40 */ -#define DELAY do { volatile int _x; for(_x=0;_x<40;_x++);} while (0) +/* 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) { @@ -49,11 +54,9 @@ static void sc606_i2c_restart(void) static void sc606_i2c_stop(void) { SDA_LO; - DELAY; SCL_HI; - DELAY; + DELAY_HI; SDA_HI; - DELAY; } static void sc606_i2c_ack(void) @@ -61,52 +64,66 @@ static void sc606_i2c_ack(void) SDA_LO; SCL_HI; - DELAY; + DELAY_HI; SCL_LO; } + + static int sc606_i2c_getack(void) { - int ret = 0; + int ret; /* Don't need a delay since follows a data bit with a delay on the end */ SDA_INPUT; /* And set to input */ - SCL_HI; DELAY; + SCL_HI; - if (SDA) /* ack failed */ - ret = 1; + ret = (SDA != 0); /* ack failed if SDA is not low */ + DELAY_HI; - DELAY; SCL_LO; - DELAY; + DELAY_LO; + + SDA_HI; SDA_OUTPUT; + DELAY_LO; + return ret; } -static int sc606_i2c_outb(unsigned char byte) + + +static void sc606_i2c_outb(unsigned char byte) { int i; /* clock out each bit, MSB first */ - for (i = 0x80; i; i >>= 1) { + for (i = 0x80; i; i >>= 1) + { + if (i & byte) + { + SDA_HI; + } + else + { + SDA_LO; + } + DELAY; - if (i & byte) { - SDA_HI; - } else { - SDA_LO; - } + SCL_HI; + DELAY_HI; - DELAY; - SCL_HI; - DELAY; - SCL_LO; - DELAY; + SCL_LO; + DELAY_LO; } - return sc606_i2c_getack(); + SDA_HI; + } + + static unsigned char sc606_i2c_inb(void) { int i; @@ -131,37 +148,57 @@ static unsigned char sc606_i2c_inb(void) +/* returns number of acks that were bad */ int sc606_write(unsigned char reg, unsigned char data) { int x = 0; sc606_i2c_start(); - x += sc606_i2c_outb(SLAVE_ADDRESS); - x += 0x10 * sc606_i2c_outb(reg); - sc606_i2c_start(); -/* sc606_i2c_restart(); */ - x += 0x100 * sc606_i2c_outb(SLAVE_ADDRESS); - x += 0x1000 *sc606_i2c_outb(data); + + 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; @@ -176,7 +213,7 @@ void sc606_init(void) /* About 400us - needs 350us */ for (i = 200; i; i--) { - DELAY; + DELAY_LO; } /* Set GPH9 (SDA) and GPH10 (SCL) to 1 */ -- cgit