diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2006-02-22 14:50:57 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2006-02-22 14:50:57 +0000 |
commit | 2a2d346b08002fd4c83229960728e7a5dad3144f (patch) | |
tree | 0b5d57157028ec430c5cac64a51ace66e21af836 /firmware/drivers/generic_i2c.c | |
parent | 37b15d3833a8663458a541a2ad1e20fc66b233cf (diff) |
Generic bitbang I2C driver
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8782 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/generic_i2c.c')
-rwxr-xr-x | firmware/drivers/generic_i2c.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/firmware/drivers/generic_i2c.c b/firmware/drivers/generic_i2c.c new file mode 100755 index 0000000000..8475f00ee6 --- /dev/null +++ b/firmware/drivers/generic_i2c.c @@ -0,0 +1,204 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 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 <stdlib.h> +#include "debug.h" +#include "logf.h" +#include "generic_i2c.h" + +int i2c_num_ifs = 0; +struct i2c_interface *i2c_if[5]; + +static void i2c_start(struct i2c_interface *iface) +{ + iface->sda_hi(); + iface->scl_hi(); + iface->sda_lo(); + iface->delay_hd_sta(); + iface->scl_lo(); +} + +static void i2c_stop(struct i2c_interface *iface) +{ + iface->sda_lo(); + iface->scl_hi(); + iface->delay_su_sto(); + iface->sda_hi(); +} + +static void i2c_ack(struct i2c_interface *iface, bool ack) +{ + iface->scl_lo(); + if ( ack ) + iface->sda_lo(); + else + iface->sda_hi(); + + iface->delay_su_dat(); + iface->scl_hi(); + iface->delay_thigh(); + iface->scl_lo(); +} + +static int i2c_getack(struct i2c_interface *iface) +{ + int ret = 1; + + iface->sda_input(); + iface->delay_su_dat(); + iface->scl_hi(); + + if (iface->sda()) + ret = 0; /* ack failed */ + + iface->delay_thigh(); + iface->scl_lo(); + iface->sda_hi(); + iface->sda_output(); + iface->delay_hd_dat(); + return ret; +} + +static unsigned char i2c_inb(struct i2c_interface *iface, bool ack) +{ + int i; + unsigned char byte = 0; + + /* clock in each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + iface->sda_input(); + iface->scl_hi(); + iface->delay_thigh(); + if (iface->sda()) + byte |= i; + iface->scl_lo(); + iface->delay_hd_dat(); + iface->sda_output(); + } + + i2c_ack(iface, ack); + + return byte; +} + +static void i2c_outb(struct i2c_interface *iface, unsigned char byte) +{ + int i; + + /* clock out each bit, MSB first */ + for (i=0x80; i; i>>=1) { + if (i & byte) + iface->sda_hi(); + else + iface->sda_lo(); + iface->delay_su_dat(); + iface->scl_hi(); + iface->delay_thigh(); + iface->scl_lo(); + iface->delay_hd_dat(); + } + + iface->sda_hi(); +} + +static struct i2c_interface *find_if(int address) +{ + int i; + + for(i = 0;i < i2c_num_ifs;i++) { + if(i2c_if[i]->address == address) + return i2c_if[i]; + } + return NULL; +} + +int i2c_write_data(int bus_address, int address, + const unsigned char* buf, int count) +{ + int i; + int ret = 0; + struct i2c_interface *iface = find_if(bus_address); + if(!iface) + return -1; + + i2c_start(iface); + i2c_outb(iface, iface->address & 0xfe); + if (i2c_getack(iface)) { + i2c_outb(iface, address); + if (i2c_getack(iface)) { + for(i = 0;i < count;i++) { + i2c_outb(iface, buf[i]); + if (!i2c_getack(iface)) { + ret = -3; + break; + } + } + } else { + ret = -2; + } + } else { + logf("i2c_write_data() - no ack\n"); + ret = -1; + } + + i2c_stop(iface); + return ret; +} + +int i2c_read_data(int bus_address, int address, + unsigned char* buf, int count) +{ + int i; + int ret = 0; + struct i2c_interface *iface = find_if(bus_address); + if(!iface) + return -1; + + i2c_start(iface); + i2c_outb(iface, iface->address & 0xfe); + if (i2c_getack(iface)) { + i2c_outb(iface, address); + if (i2c_getack(iface)) { + i2c_start(iface); + i2c_outb(iface, iface->address | 1); + if (i2c_getack(iface)) { + for(i = 0;i < count-1;i++) + buf[i] = i2c_inb(iface, true); + + buf[i] = i2c_inb(iface, false); + } else { + ret = -3; + } + } else { + ret = -2; + } + } else { + ret = -1; + } + + i2c_stop(iface); + return ret; +} + +void i2c_add_node(struct i2c_interface *iface) +{ + i2c_if[i2c_num_ifs++] = iface; +} |