From 2a2d346b08002fd4c83229960728e7a5dad3144f Mon Sep 17 00:00:00 2001 From: Linus Nielsen Feltzing Date: Wed, 22 Feb 2006 14:50:57 +0000 Subject: Generic bitbang I2C driver git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8782 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/generic_i2c.c | 204 +++++++++++++++++++++++++++++++++++++++++ firmware/export/generic_i2c.h | 53 +++++++++++ 2 files changed, 257 insertions(+) create mode 100755 firmware/drivers/generic_i2c.c create mode 100755 firmware/export/generic_i2c.h (limited to 'firmware') 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 +#include +#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; +} diff --git a/firmware/export/generic_i2c.h b/firmware/export/generic_i2c.h new file mode 100755 index 0000000000..5570d940fb --- /dev/null +++ b/firmware/export/generic_i2c.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ +#ifndef _GEN_I2C_ +#define _GEN_I2C_ + +struct i2c_interface +{ + unsigned char address; /* Address of the chip that this interface + describes */ + + void (*scl_hi)(void); /* Drive SCL high, might sleep on clk stretch */ + void (*scl_lo)(void); /* Drive SCL low */ + void (*sda_hi)(void); /* Drive SDA high */ + void (*sda_lo)(void); /* Drive SDA low */ + void (*sda_input)(void); /* Set SDA as input */ + void (*sda_output)(void); /* Set SDA as output */ + void (*scl_input)(void); /* Set SCL as input */ + void (*scl_output)(void); /* Set SCL as output */ + int (*scl)(void); /* Read SCL, returns 0 or non-zero */ + int (*sda)(void); /* Read SDA, returns 0 or non-zero */ + + /* These are the delays specified in the I2C specification, the + time pairs to the right are the minimum 100kHz and 400kHz specs */ + void (*delay_hd_sta)(void); /* START SDA hold (tHD:SDA) 4.0us/0.6us */ + void (*delay_hd_dat)(void); /* SDA hold (tHD:DAT) 5.0us/0us */ + void (*delay_su_dat)(void); /* SDA setup (tSU:DAT) 250ns/100ns */ + void (*delay_su_sto)(void); /* STOP setup (tSU:STO) 4.0us/0.6us */ + void (*delay_su_sta)(void); /* Rep. START setup (tSU:STA) 4.7us/0.6us */ + void (*delay_thigh)(void); /* SCL high period (tHIGH) 4.0us/0.6us */ +}; + +extern void i2c_add_node(struct i2c_interface *iface); +extern int i2c_write_data(int bus_address, int address, + const unsigned char* buf, int count); +extern int i2c_read_data(int bus_address, int address, + unsigned char* buf, int count); +#endif -- cgit v1.2.3