From: Christopher H. <ch...@us...> - 2002-04-25 22:46:17
|
Update of /cvsroot/blob/blob/src/lib In directory usw-pr-cvs1:/tmp/cvs-serv23553/src/lib Added Files: i2c.c i2c-gpio.c Log Message: simple i2c implementation -- useful for reading DRAM size and timing info out of SPD roms on SIMMs --- NEW FILE: i2c.c --- /* * i2c.c: generic i2c bus support * * Copyright (C) 2001 Hewlett-Packard Comapny * Written by Christopher Hoover <ch...@hp...> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ident "$Id: i2c.c,v 1.1 2002/04/25 22:46:11 choover Exp $" #ifdef HAVE_CONFIG_H # include <blob/config.h> #endif #include <blob/i2c.h> #include <blob/util.h> #include <blob/errno.h> #ifdef DEBUG #define DPRINTF(...) do { printf(__VA_ARGS__); } while (0) #else #define DPRINTF(...) do { } while (0) #endif //////////////////////////////////////////////////////////////////////// static inline int set_scl(struct i2c_bus *bus, unsigned sense) { return (bus->set_scl)(bus, sense); } static inline int set_sda(struct i2c_bus *bus, unsigned sense) { return (bus->set_sda)(bus, sense); } static inline int get_scl(struct i2c_bus *bus) { return (bus->get_scl)(bus); } static inline int get_sda(struct i2c_bus *bus) { return (bus->get_sda)(bus); } //////////////////////////////////////////////////////////////////////// int i2c_start(struct i2c_bus *bus) { /* high to low transition on SDA while SCL is high */ set_scl(bus, 1); set_sda(bus, 1); set_sda(bus, 0); set_scl(bus, 0); set_sda(bus, 1); return 0; } int i2c_stop(struct i2c_bus *bus) { /* low to high transition on SDA while SCL is high */ set_sda(bus, 0); set_scl(bus, 1); set_sda(bus, 1); return 0; } int i2c_init(struct i2c_bus *bus) { int rc, i; DPRINTF("initializing i2c driver\n"); rc = (bus->init_bus)(bus); if (rc < 0) return rc; DPRINTF("resetting i2c bus"); for (i = 0; i < 9; i++) (void) i2c_stop(bus); DPRINTF("testing i2c bus"); DPRINTF("[0] scl: %d, sda: %d\n", get_scl(bus), get_sda(bus)); if ((get_scl(bus) != 1) || (get_sda(bus) != 1)) { printf("bus seems to be busy\n"); goto fail; } set_sda(bus, 0); DPRINTF("[1] scl: %d, sda: %d\n", get_scl(bus), get_sda(bus)); if (get_sda(bus) != 0) { printf("sda stuck high\n"); goto fail; } if (get_scl(bus) == 0) { printf("scl unexpectedly low while pulling SDA low\n"); goto fail; } set_sda(bus, 1); DPRINTF("[2] scl: %d, sda: %d\n", get_scl(bus), get_sda(bus)); if (get_sda(bus) == 0) { printf("sda stuck low\n"); goto fail; } if (get_scl(bus) == 0) { printf("scl unexpectedly low while SDA high\n"); goto fail; } set_scl(bus, 0); DPRINTF("[3] scl: %d, sda: %d\n", get_scl(bus), get_sda(bus)); if (get_scl(bus) != 0) { printf("scl stuck high\n"); goto fail; } if (get_sda(bus) == 0) { printf("sda unexpected low while pulling scl low\n"); goto fail; } set_scl(bus, 1); DPRINTF("[4] scl: %d, sda: %d\n", get_scl(bus), get_sda(bus)); if (get_scl(bus) == 0) { printf("scl stuck low\n"); goto fail; } if (get_sda(bus) == 0) { printf("sda unexpected low while scl high\n"); goto fail; } DPRINTF("test passed\n"); return 0; fail: /* float the lines */ set_scl(bus, 1); set_sda(bus, 1); return -EINVAL; } int i2c_send_ack(struct i2c_bus *bus) { set_sda(bus, 0); set_scl(bus, 1); set_scl(bus, 0); set_sda(bus, 1); return 0; } int i2c_get_ack(struct i2c_bus *bus) { unsigned bit; set_sda(bus, 1); set_scl(bus, 1); bit = get_sda(bus); if (bit) printf(__FUNCTION__ ": did not get ack\n"); set_scl(bus, 0); /* zero is winning */ return bit; } int i2c_get_ack_quietly(struct i2c_bus *bus) { unsigned bit; set_sda(bus, 1); set_scl(bus, 1); bit = get_sda(bus); set_scl(bus, 0); /* zero is winning */ return bit; } int i2c_write_byte(struct i2c_bus *bus, u8 byte) { int i; for (i = 7; i >= 0; i--) { unsigned bit = byte & (1<<i); set_sda(bus, bit); set_scl(bus, 1); set_scl(bus, 0); } set_sda(bus, 1); return 0; } int i2c_read_byte(struct i2c_bus *bus) { int i; u8 value = 0; set_sda(bus, 1); for (i = 0; i < 8; i++) { unsigned bit; set_scl(bus, 1); bit = get_sda(bus); value = (value << 1) | bit; set_scl(bus, 0); } return value; } #define READ_ADDR(x) (((x) << 1) | 1) #define WRITE_ADDR(x) ((x) << 1) #define i2c_get_ack_or_bail(bus) \ do { \ int rc = i2c_get_ack(bus); \ if (rc != 0) { i2c_stop(bus); i2c_stop(bus); return rc; } \ } while (0) int i2c_read_device_byte(struct i2c_bus *bus, u8 devAddr) { int rc; u8 val; i2c_start(bus); i2c_write_byte(bus, READ_ADDR(devAddr)); i2c_get_ack_or_bail(bus); val = i2c_read_byte(bus); i2c_stop(bus); return val; } int i2c_read_device_byte_at(struct i2c_bus *bus, u8 devAddr, u8 startAddr) { int rc; i2c_start(bus); i2c_write_byte(bus, WRITE_ADDR(devAddr)); i2c_get_ack_or_bail(bus); i2c_write_byte(bus, startAddr); i2c_get_ack_or_bail(bus); return i2c_read_device_byte(bus, devAddr); } int i2c_read_device_bytes(struct i2c_bus *bus, u8 devAddr, u8 startAddr, u8 *buf, unsigned long n) { int rc; i2c_start(bus); i2c_write_byte(bus, WRITE_ADDR(devAddr)); i2c_get_ack_or_bail(bus); i2c_write_byte(bus, startAddr); i2c_get_ack_or_bail(bus); i2c_start(bus); i2c_write_byte(bus, READ_ADDR(devAddr)); i2c_get_ack_or_bail(bus); for (; n > 0; n--) { u8 val = i2c_read_byte(bus); *buf++ = val; if (n) i2c_send_ack(bus); } i2c_stop(bus); return 0; } int i2c_write_device_byte_at(struct i2c_bus *bus, u8 devAddr, u8 val, u8 startAddr) { int rc; i2c_start(bus); i2c_write_byte(bus, WRITE_ADDR(devAddr)); i2c_get_ack_or_bail(bus); i2c_write_byte(bus, startAddr); i2c_get_ack_or_bail(bus); i2c_write_byte(bus, val); i2c_get_ack_or_bail(bus); i2c_stop(bus); return 0; } int i2c_write_device_bytes_at(struct i2c_bus *bus, u8 devAddr, u8 startAddr, const u8 *buf, unsigned long n) { int rc; i2c_start(bus); i2c_write_byte(bus, WRITE_ADDR(devAddr)); i2c_get_ack_or_bail(bus); i2c_write_byte(bus, startAddr); i2c_get_ack_or_bail(bus); for (; n > 0; n--) { u8 val = *buf++; i2c_write_byte(bus, val); i2c_get_ack_or_bail(bus); } i2c_stop(bus); return 0; } int i2c_scan(struct i2c_bus *bus, u8 map[256]) { int i; DPRINTF("scanning bus "); for (i = 0; i < 0xff; i += 1) { unsigned ack; i2c_start(bus); i2c_write_byte(bus, i); ack = i2c_get_ack_quietly(bus); i2c_stop(bus); if (ack == 0) { DPRINTF("(%02x)", i); map[i] = 1; } else { DPRINTF("."); map[i] = 0; } } DPRINTF("\ndone\n"); return 0; } --- NEW FILE: i2c-gpio.c --- /* * i2c-gpio.c: i2c on a pair of GPIO pins * * Copyright (C) 2001 Hewlett-Packard Comapny * Written by Christopher Hoover <ch...@hp...> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ident "$Id: i2c-gpio.c,v 1.1 2002/04/25 22:46:11 choover Exp $" #ifdef HAVE_CONFIG_H # include <blob/config.h> #endif #include <blob/i2c.h> #include <blob/i2c-gpio.h> #include <blob/sa1100.h> #include <blob/errno.h> #include <blob/util.h> #ifdef DEBUG #define DPRINTF(...) do { printf(__VA_ARGS__); } while (0) #else #define DPRINTF(...) do { } while (0) #endif //////////////////////////////////////////////////////////////////////// static inline int set_scl(struct i2c_bus *bus, unsigned sense) { return (bus->set_scl)(bus, sense); } static inline int set_sda(struct i2c_bus *bus, unsigned sense) { return (bus->set_sda)(bus, sense); } static inline int get_scl(struct i2c_bus *bus) { return (bus->get_scl)(bus); } static inline int get_sda(struct i2c_bus *bus) { return (bus->get_sda)(bus); } //////////////////////////////////////////////////////////////////////// static inline void delay(const struct i2c_bus *bus) { struct i2c_bus_gpio_private *private = (struct i2c_bus_gpio_private *) bus->private; /* ### use msleep? no, it may not be around early enough */ volatile int i; for (i = 0; i < private->delay; i++) ; } int init_bus_gpio(struct i2c_bus *bus) { struct i2c_bus_gpio_private *private = (struct i2c_bus_gpio_private *) bus->private; GPDR &= ~(private->sda_gpio | private->scl_gpio); GPCR = (private->sda_gpio | private->scl_gpio); set_sda_gpio(bus, 1); set_scl_gpio(bus, 1); delay(bus); delay(bus); return 0; } int set_sda_gpio(struct i2c_bus *bus, unsigned sense) { struct i2c_bus_gpio_private *private = (struct i2c_bus_gpio_private *) bus->private; if (sense) /* float high */ GPDR &= ~(private->sda_gpio); else /* pull low */ GPDR |= (private->sda_gpio); delay(bus); return 0; } int get_sda_gpio(struct i2c_bus *bus) { struct i2c_bus_gpio_private *private = (struct i2c_bus_gpio_private *) bus->private; return (GPLR & (private->sda_gpio)) ? 1 : 0; } int set_scl_gpio(struct i2c_bus *bus, unsigned sense) { struct i2c_bus_gpio_private *private = (struct i2c_bus_gpio_private *) bus->private; if (sense) { int i = 0; /* float high */ GPDR &= ~(private->scl_gpio); /* ### this should timeout and return -ETIMEOUT */ while (!get_scl(bus)) { if (i++ > 10) { DPRINTF(__FUNCTION__ ": scl did not go high\n"); return -ETIMEOUT; } delay(bus); } } else /* pull low */ GPDR |= (private->scl_gpio); delay(bus); return 0; } int get_scl_gpio(struct i2c_bus *bus) { struct i2c_bus_gpio_private *private = (struct i2c_bus_gpio_private *) bus->private; return (GPLR & (private->scl_gpio)) ? 1 : 0; } |