diff --git a/firmware/SOURCES b/firmware/SOURCES index 05c03ce474..92a41cdead 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -239,7 +239,6 @@ target/arm/wmcodec-pp.c #ifdef IAUDIO_X5 target/coldfire/iaudio/x5/power-x5.c #ifndef SIMULATOR -drivers/generic_i2c.c target/coldfire/iaudio/x5/button-x5.c target/coldfire/iaudio/x5/lcd-as-x5.S target/coldfire/iaudio/x5/lcd-x5.c diff --git a/firmware/target/coldfire/iaudio/x5/pcf50606-x5.c b/firmware/target/coldfire/iaudio/x5/pcf50606-x5.c index eaecc5bef6..59ecdb612c 100644 --- a/firmware/target/coldfire/iaudio/x5/pcf50606-x5.c +++ b/firmware/target/coldfire/iaudio/x5/pcf50606-x5.c @@ -28,92 +28,455 @@ #include "generic_i2c.h" #include "powermgmt.h" -void pcf50606_sda_output(void) +#define USE_ASM + +/* Data */ +#define SDA_BITNUM 12 /* SDA1/RXD1/GPIO44 */ +#define SDA_GPIO_READ GPIO1_READ /* MBAR2 + 0x0b0 */ +#define SDA_GPIO_OUT GPIO1_OUT /* MBAR2 + 0x0b4 */ +#define SDA_GPIO_ENABLE GPIO1_ENABLE /* MBAR2 + 0x0b8 */ +#define SDA_GPIO_FUNCTION GPIO1_FUNCTION /* MBAR2 + 0x0bc */ + +/* Clock */ +#define SCL_BITNUM 10 /* SCL1/TXD1/GPIO10 */ +#define SCL_GPIO_READ GPIO_READ /* MBAR2 + 0x000 */ +#define SCL_GPIO_OUT GPIO_OUT /* MBAR2 + 0x004 */ +#define SCL_GPIO_ENABLE GPIO_ENABLE /* MBAR2 + 0x008 */ +#define SCL_GPIO_FUNCTION GPIO_FUNCTION /* MBAR2 + 0x00c */ + +#define PCF50606_ADDR 0x10 +#define SCL_BIT (1ul << SCL_BITNUM) +#define SDA_BIT (1ul << SDA_BITNUM) + +#define SDA ( SDA_BIT & SDA_GPIO_READ) +#define SDA_LO_OUT or_l( SDA_BIT, &SDA_GPIO_ENABLE) +#define SDA_HI_IN and_l(~SDA_BIT, &SDA_GPIO_ENABLE) + +#define SCL ( SCL_BIT & SCL_GPIO_READ) +#define SCL_LO_OUT or_l( SCL_BIT, &SCL_GPIO_ENABLE) +#define SCL_HI_IN and_l(~SCL_BIT, &SCL_GPIO_ENABLE); while(!SCL); + +#define DELAY \ + asm ( \ + "move.l %[dly],%%d0 \n" \ + "1: \n" \ + "subq.l #1,%%d0 \n" \ + "bhi.s 1b \n" \ + : : [dly]"d"(i2c_delay) : "d0" ); + +static int i2c_delay IDATA_ATTR = 44; + +void pcf50606_i2c_recalc_delay(int cpu_clock) { - or_l( 0x00001000, &GPIO1_ENABLE); + i2c_delay = MAX(cpu_clock / (400000*2*3) - 7, 1); } -void pcf50606_sda_input(void) +inline void pcf50606_i2c_start(void) { - and_l(~0x00001000, &GPIO1_ENABLE); +#ifdef USE_ASM + asm ( + "not.l %[sdab] \n" /* SDA_HI_IN */ + "and.l %[sdab],(8,%[sdard]) \n" + "not.l %[sdab] \n" + + "not.l %[sclb] \n" /* SCL_HI_IN */ + "and.l %[sclb],(8,%[sclrd]) \n" + "not.l %[sclb] \n" + "1: \n" + "move.l (%[sclrd]),%%d0 \n" + "btst.l %[sclbnum], %%d0 \n" + "beq.s 1b \n" + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "or.l %[sdab],(8,%[sdard]) \n" /* SDA_LO_OUT */ + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "or.l %[sclb],(8,%[sclrd]) \n" /* SCL_LO_OUT */ + : /* outputs */ + : /* inputs */ + [sclrd] "a"(&SCL_GPIO_READ), + [sclb] "d"(SCL_BIT), + [sclbnum] "i"(SCL_BITNUM), + [sdard] "a"(&SDA_GPIO_READ), + [sdab] "d"(SDA_BIT), + [dly] "d"(i2c_delay) + : /* clobbers */ + "d0" + ); +#else + SDA_HI_IN; + SCL_HI_IN; + DELAY; + SDA_LO_OUT; + DELAY; + SCL_LO_OUT; +#endif } -void pcf50606_sda_lo(void) +inline void pcf50606_i2c_stop(void) { - and_l(~0x00001000, &GPIO1_OUT); +#ifdef USE_ASM + asm ( + "or.l %[sdab],(8,%[sdard]) \n" /* SDA_LO_OUT */ + + "not.l %[sclb] \n" /* SCL_HI_IN */ + "and.l %[sclb],(8,%[sclrd]) \n" + "not.l %[sclb] \n" + "1: \n" + "move.l (%[sclrd]),%%d0 \n" + "btst.l %[sclbnum],%%d0 \n" + "beq.s 1b \n" + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "not.l %[sdab] \n" /* SDA_HI_IN */ + "and.l %[sdab],(8,%[sdard]) \n" + "not.l %[sdab] \n" + : /* outputs */ + : /* inputs */ + [sclrd] "a"(&SCL_GPIO_READ), + [sclb] "d"(SCL_BIT), + [sclbnum] "i"(SCL_BITNUM), + [sdard] "a"(&SDA_GPIO_READ), + [sdab] "d"(SDA_BIT), + [dly] "d"(i2c_delay) + : /* clobbers */ + "d0" + ); +#else + SDA_LO_OUT; + SCL_HI_IN; + DELAY; + SDA_HI_IN; +#endif } -void pcf50606_sda_hi(void) +inline void pcf50606_i2c_ack(bool ack) { - or_l( 0x00001000, &GPIO1_OUT); +#ifdef USE_ASM + asm ( + "tst.b %[ack] \n" /* if (!ack) */ + "bne.s 1f \n" + + "not.l %[sdab] \n" /* SDA_HI_IN */ + "and.l %[sdab],(8,%[sdard]) \n" + "not.l %[sdab] \n" + ".word 0x51fb \n" /* trapf.l : else */ + "1: \n" + "or.l %[sdab],(8,%[sdard]) \n" /* SDA_LO_OUT */ + + "not.l %[sclb] \n" /* SCL_HI_IN */ + "and.l %[sclb],(8,%[sclrd]) \n" + "not.l %[sclb] \n" + "1: \n" + "move.l (%[sclrd]),%%d0 \n" + "btst.l %[sclbnum],%%d0 \n" + "beq.s 1b \n" + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "or.l %[sclb],(8,%[sclrd]) \n" /* SCL_LO_OUT */ + : /* outputs */ + : /* inputs */ + [sclrd] "a"(&SCL_GPIO_READ), + [sclb] "d"(SCL_BIT), + [sclbnum] "i"(SCL_BITNUM), + [sdard] "a"(&SDA_GPIO_READ), + [sdab] "d"(SDA_BIT), + [dly] "d"(i2c_delay), + [ack] "d"(ack) + : /* clobbers */ + "d0" + ); +#else + if(ack) + SDA_LO_OUT; + else + SDA_HI_IN; + + SCL_HI_IN; + + DELAY; + SCL_LO_OUT; +#endif } -int pcf50606_sda(void) +inline bool pcf50606_i2c_getack(void) { - return 0x00001000 & GPIO1_READ; + bool ret; + +#ifdef USE_ASM + asm ( + "not.l %[sdab] \n" /* SDA_HI_IN */ + "and.l %[sdab],(8,%[sdard]) \n" + "not.l %[sdab] \n" + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "not.l %[sclb] \n" /* SCL_HI_IN */ + "and.l %[sclb],(8,%[sclrd]) \n" + "not.l %[sclb] \n" + "1: \n" + "move.l (%[sclrd]),%%d0 \n" + "btst.l %[sclbnum],%%d0 \n" + "beq.s 1b \n" + + "move.l (%[sdard]),%%d0 \n" /* ret = !SDA */ + "btst.l %[sdabnum],%%d0 \n" + "seq.b %[ret] \n" + + "or.l %[sclb],(8,%[sclrd]) \n" /* SCL_LO_OUT */ + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + : /* outputs */ + [ret] "=&d"(ret) + : /* inputs */ + [sclrd] "a"(&SCL_GPIO_READ), + [sclb] "d"(SCL_BIT), + [sclbnum] "i"(SCL_BITNUM), + [sdard] "a"(&SDA_GPIO_READ), + [sdab] "d"(SDA_BIT), + [sdabnum] "i"(SDA_BITNUM), + [dly] "d"(i2c_delay) + : /* clobbers */ + "d0" + ); +#else + SDA_HI_IN; + DELAY; + SCL_HI_IN; + + ret = !SDA; + + SCL_LO_OUT; + DELAY; +#endif + return ret; } -void pcf50606_scl_output(void) +void pcf50606_i2c_outb(unsigned char byte) { - or_l( 0x00000400, &GPIO_ENABLE); -} +#ifdef USE_ASM + asm volatile ( + "moveq.l #24,%%d0 \n" /* byte <<= 24 */ + "lsl.l %%d0,%[byte] \n" + "moveq.l #8,%%d1 \n" /* i = 8 */ -void pcf50606_scl_input(void) -{ - and_l(~0x00000400, &GPIO_ENABLE); -} + "2: \n" /* do */ + "lsl.l #1,%[byte] \n" /* if ((byte <<= 1) carry) */ + "bcc.s 1f \n" -int pcf50606_scl(void) -{ - return 0x00000400 & GPIO_READ; -} + "not.l %[sdab] \n" /* SDA_HI_IN */ + "and.l %[sdab],(8,%[sdard]) \n" + "not.l %[sdab] \n" + ".word 0x51fb \n" /* trapf.l; else */ + "1: \n" + "or.l %[sdab],(8,%[sdard]) \n" /* SDA_LO_OUT */ -void pcf50606_scl_lo(void) -{ - and_l(~0x00000400, &GPIO_OUT); -} + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" -void pcf50606_scl_hi(void) -{ - pcf50606_scl_input(); - while(!pcf50606_scl()) + "not.l %[sclb] \n" /* SCL_HI_IN */ + "and.l %[sclb],(8,%[sclrd]) \n" + "not.l %[sclb] \n" + "1: \n" + "move.l (%[sclrd]),%%d0 \n" + "btst.l %[sclbnum],%%d0 \n" + "beq.s 1b \n" + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "or.l %[sclb],(8,%[sclrd]) \n" /* SCL_LO_OUT */ + + "subq.l #1,%%d1 \n" /* i-- */ + "bne.s 2b \n" /* while (i != 0) */ + : /* outputs */ + [byte] "+d"(byte) + : /* inputs */ + [sclrd] "a"(&SCL_GPIO_READ), + [sclb] "d"(SCL_BIT), + [sclbnum] "i"(SCL_BITNUM), + [sdard] "a"(&SDA_GPIO_READ), + [sdab] "d"(SDA_BIT), + [dly] "d"(i2c_delay) + : /* clobbers */ + "d0", "d1" + ); +#else + int i; + + /* clock out each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + if ( i & byte ) + SDA_HI_IN; + else + SDA_LO_OUT; + DELAY; + SCL_HI_IN; + DELAY; + SCL_LO_OUT; } - or_l(0x0400, &GPIO_OUT); - pcf50606_scl_output(); +#endif } -void pcf50606_delay(void) +unsigned char pcf50606_i2c_inb(bool ack) { - do { int _x; for(_x=0;_x<32;_x++);} while(0); + unsigned char byte = 0; + +#ifdef USE_ASM + asm ( + "not.l %[sdab] \n" /* SDA_HI_IN */ + "and.l %[sdab],(8,%[sdard]) \n" + "not.l %[sdab] \n" + + "moveq.l #8,%%d1 \n" /* i = 8 */ + "clr.l %[byte] \n" /* byte = 0 */ + + "2: \n" /* do */ + "not.l %[sclb] \n" /* SCL_HI_IN */ + "and.l %[sclb],(8,%[sclrd]) \n" + "not.l %[sclb] \n" + "1: \n" + "move.l (%[sclrd]),%%d0 \n" + "btst.l %[sclbnum],%%d0 \n" + "beq.s 1b \n" + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "lsl.l #1,%[byte] \n" /* byte <<= 1 */ + "move.l (%[sdard]),%%d0 \n" /* if (SDA) */ + "btst.l %[sdabnum],%%d0 \n" + "beq.s 1f \n" + "addq.l #1,%[byte] \n" /* byte++ */ + "1: \n" + + "or.l %[sclb],(8,%[sclrd]) \n" /* SCL_LO_OUT */ + + "move.l %[dly],%%d0 \n" /* DELAY */ + "1: \n" + "subq.l #1,%%d0 \n" + "bhi.s 1b \n" + + "subq.l #1,%%d1 \n" /* i-- */ + "bne.s 2b \n" /* while (i != 0) */ + : /* outputs */ + [byte] "=&d"(byte) + : /* inputs */ + [sclrd] "a"(&SCL_GPIO_READ), + [sclb] "d"(SCL_BIT), + [sclbnum] "i"(SCL_BITNUM), + [sdard] "a"(&SDA_GPIO_READ), + [sdab] "d"(SDA_BIT), + [sdabnum] "i"(SDA_BITNUM), + [dly] "d"(i2c_delay) + : /* clobbers */ + "d0", "d1" + ); +#else + int i; + + /* clock in each bit, MSB first */ + SDA_HI_IN; + for ( i=0x80; i; i>>=1 ) + { + SCL_HI_IN; + DELAY; + if ( SDA ) + byte |= i; + SCL_LO_OUT; + DELAY; + } +#endif + + pcf50606_i2c_ack(ack); + + return byte; } -struct i2c_interface pcf50606_i2c = { - 0x10, /* Address */ +int pcf50606_i2c_write(int address, const unsigned char* buf, int count) +{ + int i,x=0; - /* Bit-banged interface definitions */ - pcf50606_scl_hi, /* Drive SCL high, might sleep on clk stretch */ - pcf50606_scl_lo, /* Drive SCL low */ - pcf50606_sda_hi, /* Drive SDA high */ - pcf50606_sda_lo, /* Drive SDA low */ - pcf50606_sda_input, /* Set SDA as input */ - pcf50606_sda_output, /* Set SDA as output */ - pcf50606_scl_input, /* Set SCL as input */ - pcf50606_scl_output, /* Set SCL as output */ - pcf50606_scl, /* Read SCL, returns 0 or nonzero */ - pcf50606_sda, /* Read SDA, returns 0 or nonzero */ - - pcf50606_delay, /* START SDA hold time (tHD:SDA) */ - pcf50606_delay, /* SDA hold time (tHD:DAT) */ - pcf50606_delay, /* SDA setup time (tSU:DAT) */ - pcf50606_delay, /* STOP setup time (tSU:STO) */ - pcf50606_delay, /* Rep. START setup time (tSU:STA) */ - pcf50606_delay, /* SCL high period (tHIGH) */ -}; + pcf50606_i2c_start(); + pcf50606_i2c_outb(address & 0xfe); + if (pcf50606_i2c_getack()) + { + for (i=0; i= 0) + { + pcf50606_i2c_start(); + pcf50606_i2c_outb(0x11); + if (pcf50606_i2c_getack()) + { + for(i = 0;i < count-1;i++) + buf[i] = pcf50606_i2c_inb(true); + + buf[i] = pcf50606_i2c_inb(false); + } + else + { + ret = -1; + } + } + + pcf50606_i2c_stop(); + + return ret; } int pcf50606_read(int address) @@ -130,7 +493,32 @@ int pcf50606_read(int address) int pcf50606_write_multiple(int address, const unsigned char* buf, int count) { - return i2c_write_data(0x10, address, buf, count); + unsigned char obuf[1]; + int i; + int ret = 0; + + obuf[0] = address; + + /* send write command */ + if (pcf50606_i2c_write(PCF50606_ADDR, obuf, 1) >= 0) + { + for (i=0; i