diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2005-11-14 19:50:16 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2005-11-14 19:50:16 +0000 |
commit | 3b0e8f89ab443c8d07b94165e6a301066b8e7ea1 (patch) | |
tree | 3ff4ffd2a0af1f8ea04852b930fe805dec48e4e0 /firmware/drivers/pcf50606.c | |
parent | 362259ad3e1d969d2b515248ea6bb050808a222c (diff) |
H300: First lame attempt to a pcf50606 driver
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7873 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/pcf50606.c')
-rw-r--r-- | firmware/drivers/pcf50606.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/firmware/drivers/pcf50606.c b/firmware/drivers/pcf50606.c new file mode 100644 index 0000000000..993e56800d --- /dev/null +++ b/firmware/drivers/pcf50606.c @@ -0,0 +1,298 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "cpu.h" +#include <stdbool.h> +#include "kernel.h" +#include "system.h" +#include "hwcompat.h" +#include "logf.h" +#include "string.h" + +/* TODO: merge all bit-banged I2C into a generic I2C driver */ + + +#define SDA_LO and_l(~0x00002000, &GPIO1_OUT) +#define SDA_HI or_l( 0x00002000, &GPIO1_OUT) +#define SDA_INPUT and_l(~0x00002000, &GPIO1_ENABLE) +#define SDA_OUTPUT or_l( 0x00002000, &GPIO1_ENABLE) +#define SDA ( 0x00002000 & GPIO1_READ) + +/* SCL is GPIO, 3 */ +#define SCL_INPUT and_l(~0x00001000, &GPIO_ENABLE) +#define SCL_OUTPUT or_l( 0x00001000, &GPIO_ENABLE) +#define SCL_LO and_l(~0x00001000, &GPIO_OUT) +#define SCL_HI or_l( 0x00001000, &GPIO_OUT) +#define SCL ( 0x00001000 & GPIO_READ) + +/* delay loop to achieve 400kHz at 120MHz CPU frequency */ +#define DELAY do { int _x; for(_x=0;_x<22;_x++);} while(0) + + +static void pcf50606_i2c_start(void) +{ + SDA_OUTPUT; + SCL_OUTPUT; + SDA_HI; + SCL_HI; + DELAY; + SDA_LO; + DELAY; + SCL_LO; +} + +static void pcf50606_i2c_stop(void) +{ + SDA_LO; + SCL_HI; + DELAY; + SDA_HI; +} + + +static void pcf50606_i2c_ack(bool ack) +{ + /* Here's the deal. The slave is slow, and sometimes needs to wait + before it can receive the acknowledge. Therefore it forces the clock + low until it is ready. We need to poll the clock line until it goes + high before we release the ack. + + In their infinite wisdom, iriver didn't pull up the SCL line, so + we have to drive the SCL high repeatedly to simulate a pullup. */ + + SCL_LO; /* Set the clock low */ + if(ack) + SDA_LO; + else + SDA_HI; + + SCL_INPUT; /* Set the clock to input */ + while(!SCL) /* and wait for the slave to release it */ + { + SCL_OUTPUT; /* Set the clock to output */ + SCL_HI; + SCL_INPUT; /* Set the clock to input */ + DELAY; + } + + DELAY; + SCL_OUTPUT; + SCL_LO; +} + +static int pcf50606_i2c_getack(void) +{ + int ret = 1; + + /* Here's the deal. The slave is slow, and sometimes needs to wait + before it can send the acknowledge. Therefore it forces the clock + low until it is ready. We need to poll the clock line until it goes + high before we read the ack. + + In their infinite wisdom, iriver didn't pull up the SCL line, so + we have to drive the SCL high repeatedly to simulate a pullup. */ + + SDA_INPUT; /* And set to input */ + SCL_INPUT; /* Set the clock to input */ + while(!SCL) /* and wait for the slave to release it */ + { + SCL_OUTPUT; /* Set the clock to output */ + SCL_HI; + SCL_INPUT; /* Set the clock to input */ + DELAY; + } + + if (SDA) + /* ack failed */ + ret = 0; + + SCL_OUTPUT; + SCL_LO; + SDA_HI; + SDA_OUTPUT; + + return ret; +} + +static void pcf50606_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; + SCL_LO; + } + + SDA_HI; +} + +static unsigned char pcf50606_i2c_inb(bool ack) +{ + int i; + unsigned char byte = 0; + + /* clock in each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + SDA_INPUT; /* And set to input */ + SCL_HI; + DELAY; + if ( SDA ) + byte |= i; + SCL_LO; + DELAY; + SDA_OUTPUT; + } + + pcf50606_i2c_ack(ack); + + return byte; +} + +int pcf50606_i2c_write(int address, const unsigned char* buf, int count) +{ + int i,x=0; + + pcf50606_i2c_start(); + pcf50606_i2c_outb(address & 0xfe); + if (pcf50606_i2c_getack()) + { + for (i=0; i<count; i++) + { + pcf50606_i2c_outb(buf[i]); + if (!pcf50606_i2c_getack()) + { + x=-2; + break; + } + } + } + else + { + logf("pcf50606_i2c_write() - no ack\n"); + x=-1; + } + return x; +} + +int pcf50606_read(int address, unsigned char* buf, int count) +{ + int i=0; + int ret = 0; + unsigned char obuf[1]; + + obuf[0] = address; + + /* send read command */ + if (pcf50606_i2c_write(0x10, obuf, 1) >= 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_write(int address, const unsigned char* buf, int count) +{ + unsigned char obuf[1]; + int i; + int ret = 0; + + obuf[0] = address; + + /* send write command */ + if (pcf50606_i2c_write(0x10, obuf, 1) >= 0) + { + for (i=0; i<count; i++) + { + pcf50606_i2c_outb(buf[i]); + if (!pcf50606_i2c_getack()) + { + ret = -2; + break; + } + } + } + else + { + ret = -1; + } + + pcf50606_i2c_stop(); + return ret; +} + +/* These voltages were determined by measuring the output of the PCF50606 + on a running H300, and verified by disassembling the original firmware */ +static void set_voltages(void) +{ + static const unsigned char buf[5] = + { + 0xf4, /* IOREGC = 3.3V, ON in all states */ + 0xef, /* D1REGC = 2.4V, ON in all states */ + 0x18, /* D2REGC = 3.3V, OFF in all states */ + 0xf0, /* D3REGC = 2.5V, ON in all states */ + 0xef, /* LPREGC1 = 2.4V, ON in all states */ + }; + + pcf50606_write(0x23, buf, 5); +} + +void pcf50606_init(void) +{ + unsigned char c; + + /* Bit banged I2C */ + or_l(0x00002000, &GPIO1_OUT); + or_l(0x00001000, &GPIO_OUT); + or_l(0x00002000, &GPIO1_ENABLE); + or_l(0x00001000, &GPIO_ENABLE); + or_l(0x00002000, &GPIO1_FUNCTION); + or_l(0x00001000, &GPIO_FUNCTION); + + set_voltages(); + + /* Backlight PWM = 512Hz 50/50 */ + c = 0x13; + pcf50606_write(0x35, &c, 1); +} |