From patchwork Tue Jan 17 07:12:23 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 136413 X-Patchwork-Delegate: twarren@nvidia.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 483D71007D2 for ; Tue, 17 Jan 2012 18:14:15 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1AA5F282E0; Tue, 17 Jan 2012 08:14:04 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yGL3woxLzbOh; Tue, 17 Jan 2012 08:14:03 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2327E282D1; Tue, 17 Jan 2012 08:13:40 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4C9EB28124 for ; Tue, 17 Jan 2012 08:13:19 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4FFUiREcRbqh for ; Tue, 17 Jan 2012 08:13:18 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-yw0-f74.google.com (mail-yw0-f74.google.com [209.85.213.74]) by theia.denx.de (Postfix) with ESMTPS id E3D6C282DB for ; Tue, 17 Jan 2012 08:13:07 +0100 (CET) Received: by yhjj52 with SMTP id j52so417887yhj.3 for ; Mon, 16 Jan 2012 23:13:05 -0800 (PST) Received: by 10.236.175.42 with SMTP id y30mr21306460yhl.3.1326784385962; Mon, 16 Jan 2012 23:13:05 -0800 (PST) Received: by 10.236.175.42 with SMTP id y30mr21306448yhl.3.1326784385898; Mon, 16 Jan 2012 23:13:05 -0800 (PST) Received: from wpzn4.hot.corp.google.com (216-239-44-65.google.com [216.239.44.65]) by gmr-mx.google.com with ESMTPS id d9si3875993ane.3.2012.01.16.23.13.05 (version=TLSv1/SSLv3 cipher=AES128-SHA); Mon, 16 Jan 2012 23:13:05 -0800 (PST) Received: from wpaz9.hot.corp.google.com (wpaz9.hot.corp.google.com [172.24.198.73]) by wpzn4.hot.corp.google.com (Postfix) with ESMTPS id D397C1E004D; Mon, 16 Jan 2012 23:13:05 -0800 (PST) Received: from glucid-laptop-imager.hot.corp.google.com ([172.29.162.89]) by wpaz9.hot.corp.google.com with ESMTP id q0H7D4rn025670; Mon, 16 Jan 2012 23:13:04 -0800 Received: by glucid-laptop-imager.hot.corp.google.com (Postfix, from userid 121222) id 3457F34E5B7; Mon, 16 Jan 2012 23:13:04 -0800 (PST) From: Simon Glass To: U-Boot Mailing List Date: Mon, 16 Jan 2012 23:12:23 -0800 Message-Id: <1326784345-19953-2-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 1.7.7.3 In-Reply-To: <1326784345-19953-1-git-send-email-sjg@chromium.org> References: <1326784345-19953-1-git-send-email-sjg@chromium.org> X-System-Of-Record: true Cc: Heiko Schocher Subject: [U-Boot] [PATCH 1/3] i2c: add i2c_core and prepare for new multibus support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Heiko Schocher This Patch introduce the new i2c_core file, which holds the I2C core functions, for the rework of the multibus/ multiadapter support. Also adds changes in i2c.h for the new I2C multibus/multiadapter support. This new support can be activated with the CONFIG_SYS_I2C define. Signed-off-by: Heiko Schocher Signed-off-by: Simon Glass --- arch/arm/include/asm/global_data.h | 3 + arch/avr32/include/asm/global_data.h | 3 + arch/blackfin/include/asm/global_data.h | 4 +- arch/m68k/include/asm/global_data.h | 3 + arch/microblaze/include/asm/global_data.h | 3 + arch/mips/include/asm/global_data.h | 3 + arch/nios2/include/asm/global_data.h | 3 + arch/powerpc/include/asm/global_data.h | 3 + arch/sh/include/asm/global_data.h | 3 + arch/sparc/include/asm/global_data.h | 3 + arch/x86/include/asm/global_data.h | 3 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c_core.c | 360 +++++++++++++++++++++++++++++ include/i2c.h | 199 +++++++++++++++- 14 files changed, 584 insertions(+), 10 deletions(-) create mode 100644 drivers/i2c/i2c_core.c diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h index 23a6077..924cea2 100644 --- a/arch/arm/include/asm/global_data.h +++ b/arch/arm/include/asm/global_data.h @@ -87,6 +87,9 @@ typedef struct global_data { unsigned long post_log_res; /* success of POST test */ unsigned long post_init_f_time; /* When post_init_f started */ #endif +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/avr32/include/asm/global_data.h b/arch/avr32/include/asm/global_data.h index 5c654bd..605b1a7 100644 --- a/arch/avr32/include/asm/global_data.h +++ b/arch/avr32/include/asm/global_data.h @@ -50,6 +50,9 @@ typedef struct global_data { #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/blackfin/include/asm/global_data.h b/arch/blackfin/include/asm/global_data.h index 67aa30f..eacfd17 100644 --- a/arch/blackfin/include/asm/global_data.h +++ b/arch/blackfin/include/asm/global_data.h @@ -56,9 +56,11 @@ typedef struct global_data { unsigned long post_log_res; /* success of POST test */ unsigned long post_init_f_time; /* When post_init_f started */ #endif - void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/m68k/include/asm/global_data.h b/arch/m68k/include/asm/global_data.h index 0ba2b43..fb171f8 100644 --- a/arch/m68k/include/asm/global_data.h +++ b/arch/m68k/include/asm/global_data.h @@ -68,6 +68,9 @@ typedef struct global_data { #endif void **jt; /* Standalone app jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/microblaze/include/asm/global_data.h b/arch/microblaze/include/asm/global_data.h index 6e8537c..a43ca0d 100644 --- a/arch/microblaze/include/asm/global_data.h +++ b/arch/microblaze/include/asm/global_data.h @@ -47,6 +47,9 @@ typedef struct global_data { unsigned long fb_base; /* base address of frame buffer */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h index f6cf9fe..436c0c7 100644 --- a/arch/mips/include/asm/global_data.h +++ b/arch/mips/include/asm/global_data.h @@ -61,6 +61,9 @@ typedef struct global_data { unsigned long env_valid; /* Checksum of Environment valid? */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/nios2/include/asm/global_data.h b/arch/nios2/include/asm/global_data.h index 4b86fbd..714e2f8 100644 --- a/arch/nios2/include/asm/global_data.h +++ b/arch/nios2/include/asm/global_data.h @@ -42,6 +42,9 @@ typedef struct global_data { #endif void **jt; /* Standalone app jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* flags */ diff --git a/arch/powerpc/include/asm/global_data.h b/arch/powerpc/include/asm/global_data.h index 01f1d4a..96f2ad8 100644 --- a/arch/powerpc/include/asm/global_data.h +++ b/arch/powerpc/include/asm/global_data.h @@ -184,6 +184,9 @@ typedef struct global_data { #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/sh/include/asm/global_data.h b/arch/sh/include/asm/global_data.h index 1b782fc..7b25d8f 100644 --- a/arch/sh/include/asm/global_data.h +++ b/arch/sh/include/asm/global_data.h @@ -42,6 +42,9 @@ typedef struct global_data unsigned long env_valid; /* Checksum of Environment valid */ void **jt; /* Standalone app jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; #define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */ diff --git a/arch/sparc/include/asm/global_data.h b/arch/sparc/include/asm/global_data.h index 613e2d8..6a86b69 100644 --- a/arch/sparc/include/asm/global_data.h +++ b/arch/sparc/include/asm/global_data.h @@ -76,6 +76,9 @@ typedef struct global_data { #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; /* diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 05a2139..4713b41 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -55,6 +55,9 @@ typedef struct global_data { unsigned long reset_status; /* reset status register at boot */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_SYS_I2C) + void *cur_adap; /* current used i2c adapter */ +#endif } gd_t; extern gd_t *gd; diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index c123c72..ebedef2 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -45,6 +45,7 @@ COBJS-$(CONFIG_TEGRA2_I2C) += tegra2_i2c.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o +COBJS-$(CONFIG_SYS_I2C) += i2c_core.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c new file mode 100644 index 0000000..139f4c9 --- /dev/null +++ b/drivers/i2c/i2c_core.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2009 Sergey Kubushyn + * + * Multibus/multiadapter I2C core functions (wrappers) + */ +#include +#include + +#ifdef CONFIG_TEGRA2_I2C +extern struct i2c_adapter tegra_i2c_adap[]; +#endif + +struct i2c_adapter *i2c_adap[CONFIG_SYS_NUM_I2C_ADAPTERS] = + CONFIG_SYS_I2C_ADAPTERS; +#ifndef CONFIG_SYS_I2C_DIRECT_BUS +struct i2c_bus i2c_bus[CONFIG_SYS_NUM_I2C_BUSSES] = CONFIG_SYS_I2C_BUSSES; +#endif + +static unsigned int i2c_cur_bus __attribute__((section(".data"))) = + CONFIG_SYS_SPD_BUS_NUM; + +DECLARE_GLOBAL_DATA_PTR; + +void i2c_reloc_fixup(void) +{ +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + int i; + unsigned long addr; + + for (i = 0; i < CONFIG_SYS_NUM_I2C_ADAPTERS; i++) { + /* Adapter itself */ + addr = (unsigned long)i2c_adap[i]; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i] = (struct i2c_adapter *)addr; + /* i2c_init() */ + addr = (unsigned long)i2c_adap[i]->init; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i]->init = (void (*)(int, int))addr; + /* i2c_probe() */ + addr = (unsigned long)i2c_adap[i]->probe; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i]->probe = (int (*)(u_int8_t))addr; + /* i2c_read() */ + addr = (unsigned long)i2c_adap[i]->read; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i]->read = (int (*)(u_int8_t, uint, int, u_int8_t *, + int))addr; + /* i2c_write() */ + addr = (unsigned long)i2c_adap[i]->write; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i]->write = (int (*)(u_int8_t, uint, int, u_int8_t *, + int))addr; + /* i2c_set_bus_speed() */ + addr = (unsigned long)i2c_adap[i]->set_bus_speed; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i]->set_bus_speed = (uint (*)(uint))addr; + /* name */ + addr = (unsigned long)i2c_adap[i]->name; + if (addr != 0) + addr += gd->reloc_off; + + i2c_adap[i]->name = (char *)addr; + } + gd->cur_adap += gd->reloc_off; +#else + /* + * we need this here to set after relocation gd->cur_adap + * to the new relocated value. + */ + gd->cur_adap = NULL; + i2c_set_bus_num(i2c_cur_bus); +#endif +} + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS +/* + * i2c_mux_set() + * ------------- + * + * This turns on the given channel on I2C multiplexer chip connected to + * a given I2C adapter directly or via other multiplexers. In the latter + * case the entire multiplexer chain must be initialized first starting + * with the one connected directly to the adapter. When disabling a chain + * muxes must be programmed in reverse order, starting with the one + * farthest from the adapter. + * + * mux_id is the multiplexer chip type from defined in i2c.h. So far only + * NXP (Philips) PCA954x multiplexers are supported. Switches are NOT + * supported (anybody uses them?) + */ + +static int i2c_mux_set(int adapter, int mux_id, int chip, int channel) +{ + u_int8_t buf; + + /* channel < 0 - turn off the mux */ + if (channel < 0) { + buf = 0; + return i2c_adap[adapter]->write(chip, 0, 0, &buf, 1); + } + + switch (mux_id) { + case I2C_MUX_PCA9540_ID: + case I2C_MUX_PCA9542_ID: + if (channel > 1) + return -1; + buf = (u_int8_t)((channel & 0x01) | (1 << 2)); + break; + case I2C_MUX_PCA9544_ID: + if (channel > 3) + return -1; + buf = (u_int8_t)((channel & 0x03) | (1 << 2)); + break; + case I2C_MUX_PCA9547_ID: + if (channel > 7) + return -1; + buf = (u_int8_t)((channel & 0x07) | (1 << 3)); + break; + default: + return -1; + } + + return i2c_adap[adapter]->write(chip, 0, 0, &buf, 1); +} +#endif + +/* + * i2c_init_bus(): + * --------------- + * + * Initializes one bus. Will initialize the parent adapter. No current bus + * changes, no mux (if any) setup. + */ +static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +{ + if (bus_no >= CONFIG_SYS_NUM_I2C_BUSSES) + return; + + I2C_ADAP->init(speed, slaveaddr); + + if (gd->flags & GD_FLG_RELOC) { + I2C_ADAP->init_done = 1; + I2C_ADAP->speed = speed; + I2C_ADAP->slaveaddr = slaveaddr; + } +} + +/* + * i2c_init_all(): + * + * not longer needed, will deleted. Actual init the SPD_BUS + * for compatibility. + * i2c_adap[] must be initialized beforehead with function pointers and + * data, including speed and slaveaddr. + */ +void i2c_init_all(void) +{ + i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + return; +} + +/* + * i2c_get_bus_num(): + * ------------------ + * + * Returns index of currently active I2C bus. Zero-based. + */ +unsigned int i2c_get_bus_num(void) +{ + return i2c_cur_bus; +} + +/* + * i2c_set_bus_num(): + * ------------------ + * + * Change the active I2C bus. Subsequent read/write calls will + * go to this one. Sets all of the muxes in a proper condition + * if that bus is behind muxes. + * If previously selected bus is behind the muxes turns off all the + * muxes along the path to that bus. + * + * bus - bus index, zero based + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_set_bus_num(unsigned int bus) +{ +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + int i; + u_int8_t buf; +#endif + + if (bus >= CONFIG_SYS_NUM_I2C_BUSSES) + return -1; + + if (gd->cur_adap == NULL) + gd->cur_adap = I2C_ADAP_NR(bus); + else if ((bus == i2c_cur_bus) && + (I2C_ADAP->init_done > 0)) + return 0; +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + else { + /* Disconnect current bus (turn off muxes if any) */ + if ((i2c_bus[i2c_cur_bus].next_hop[0].chip != 0) && + (I2C_ADAP->init_done != 0)) { + i = CONFIG_SYS_I2C_MAX_HOPS; + do { + u_int8_t chip; + + chip = i2c_bus[i2c_cur_bus].next_hop[--i].chip; + if (chip == 0) + continue; + + I2C_ADAP->write(chip, 0, 0, &buf, 1); + } while (i > 0); + } + } +#endif + + gd->cur_adap = I2C_ADAP_NR(bus); + if (I2C_ADAP->init_done == 0) + i2c_init_bus(bus, I2C_ADAP->speed, I2C_ADAP->slaveaddr); + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + /* Connect requested bus if behind muxes */ + if (i2c_bus[bus].next_hop[0].chip != 0) { + + /* Set all muxes along the path to that bus */ + for (i = 0; i < CONFIG_SYS_I2C_MAX_HOPS; i++) { + int ret; + + if (i2c_bus[bus].next_hop[i].chip == 0) + break; + + ret = i2c_mux_set(i2c_bus[bus].adapter, + i2c_bus[bus].next_hop[i].mux.id, + i2c_bus[bus].next_hop[i].chip, + i2c_bus[bus].next_hop[i].channel); + if (ret != 0) { + printf("%s: could not set mux: id: %d "\ + "chip: %x channel: %d\n", __func__, + i2c_bus[bus].next_hop[i].mux.id, + i2c_bus[bus].next_hop[i].chip, + i2c_bus[bus].next_hop[i].channel); + + } + } + } +#endif + + i2c_cur_bus = bus; + return 0; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(u_int8_t chip) +{ + return I2C_ADAP->probe(chip); +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(u_int8_t chip, unsigned int addr, int alen, + u_int8_t *buffer, int len) +{ + return I2C_ADAP->read(chip, addr, alen, buffer, len); +} + +int i2c_write(u_int8_t chip, unsigned int addr, int alen, + u_int8_t *buffer, int len) +{ + return I2C_ADAP->write(chip, addr, alen, buffer, len); +} + +unsigned int i2c_set_bus_speed(unsigned int speed) +{ + unsigned int ret; + + if (I2C_ADAP->set_bus_speed == NULL) + return 0; + ret = I2C_ADAP->set_bus_speed(speed); + if (gd->flags & GD_FLG_RELOC) + I2C_ADAP->speed = ret; + + return ret; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct i2c_adapter *cur = I2C_ADAP; + return cur->speed; +} + +u_int8_t i2c_reg_read(u_int8_t addr, u_int8_t reg) +{ + u_int8_t buf; + +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + /* maybe it is now the time for it ... */ + i2c_set_bus_num(i2c_cur_bus); +#endif + i2c_read(addr, reg, 1, &buf, 1); + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_cur_bus, addr, reg, buf); +#endif + + return buf; +} + +void i2c_reg_write(u_int8_t addr, u_int8_t reg, u_int8_t val) +{ +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + /* maybe it is now the time for it ... */ + i2c_set_bus_num(i2c_cur_bus); +#endif + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_cur_bus, addr, reg, val); +#endif + + i2c_write(addr, reg, 1, &val, 1); +} + +void __i2c_init(int speed, int slaveaddr) +{ + i2c_init_bus(i2c_cur_bus, speed, slaveaddr); +} +void i2c_init(int speed, int slaveaddr) + __attribute__((weak, alias("__i2c_init"))); diff --git a/include/i2c.h b/include/i2c.h index ee31034..910552d 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -1,4 +1,8 @@ /* + * Copyright (C) 2009 Sergey Kubushyn + * Copyright (C) 2009 Heiko Schocher + * Changes for multibus/multiadapter I2C support. + * * (C) Copyright 2001 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. * @@ -46,16 +50,20 @@ */ #define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ -#ifdef CONFIG_I2C_MULTI_BUS -#define MAX_I2C_BUS 2 -#define I2C_MULTI_BUS 1 -#else -#define MAX_I2C_BUS 1 -#define I2C_MULTI_BUS 0 +#ifndef CONFIG_SYS_NUM_I2C_ADAPTERS +#define CONFIG_SYS_NUM_I2C_ADAPTERS 1 #endif -#if !defined(CONFIG_SYS_MAX_I2C_BUS) -#define CONFIG_SYS_MAX_I2C_BUS MAX_I2C_BUS +#if !defined(CONFIG_SYS_I2C_MAX_HOPS) || (CONFIG_SYS_I2C_MAX_HOPS == 0) +#define CONFIG_SYS_I2C_DIRECT_BUS 1 +#if !defined(CONFIG_SYS_NUM_I2C_BUSSES) +#define CONFIG_SYS_NUM_I2C_BUSSES CONFIG_SYS_NUM_I2C_ADAPTERS +#endif +#else +#undef CONFIG_SYS_I2C_DIRECT_BUS +#ifndef CONFIG_SYS_NUM_I2C_BUSSES +#define CONFIG_SYS_NUM_I2C_BUSSES 1 +#endif #endif /* define the I2C bus number for RTC and DTT if not already done */ @@ -69,6 +77,65 @@ #define CONFIG_SYS_SPD_BUS_NUM 0 #endif +struct i2c_adapter { + void (*init)(int speed, int slaveaddr); + int (*probe)(u_int8_t chip); + int (*read)(u_int8_t chip, uint addr, int alen, + u_int8_t *buffer, int len); + int (*write)(u_int8_t chip, uint addr, int alen, + u_int8_t *buffer, int len); + uint (*set_bus_speed)(uint speed); + + int speed; + int slaveaddr; + int init_done; + int hwadapnr; + char *name; +}; + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS +#define I2C_ADAP_NR(bus) i2c_adap[i2c_bus[(bus)].adapter] +#else +#define I2C_ADAP_NR(bus) i2c_adap[bus] +#endif +#define I2C_ADAP ((struct i2c_adapter *)gd->cur_adap) + +#define I2C_ADAP_HWNR (I2C_ADAP->hwadapnr) + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS +#define I2C_MUX_PCA9540_ID 1 +#define I2C_MUX_PCA9540 {I2C_MUX_PCA9540_ID, "PCA9540B"} +#define I2C_MUX_PCA9542_ID 2 +#define I2C_MUX_PCA9542 {I2C_MUX_PCA9542_ID, "PCA9542A"} +#define I2C_MUX_PCA9544_ID 3 +#define I2C_MUX_PCA9544 {I2C_MUX_PCA9544_ID, "PCA9544A"} +#define I2C_MUX_PCA9547_ID 4 +#define I2C_MUX_PCA9547 {I2C_MUX_PCA9547_ID, "PCA9547A"} + +struct i2c_mux { + int id; + char name[16]; +}; + +struct i2c_next_hop { + i2c_mux_t mux; + u_int8_t chip; + u_int8_t channel; +}; + +struct i2c_bus_hose { + int adapter; + i2c_next_hop_t next_hop[CONFIG_SYS_I2C_MAX_HOPS]; +}; + +#define I2C_NULL_HOP {{-1, ""}, 0, 0} + +extern i2c_bus_t i2c_bus[]; +#endif + +extern struct i2c_adapter *i2c_adap[]; +extern struct i2c_adapter *cur_adap_nr; + #ifndef I2C_SOFT_DECLARATIONS # if defined(CONFIG_MPC8260) # define I2C_SOFT_DECLARATIONS volatile ioport_t *iop = ioport_addr((immap_t *)CONFIG_SYS_IMMR, I2C_PORT); @@ -108,7 +175,7 @@ * repeatedly to change the speed and slave addresses. */ void i2c_init(int speed, int slaveaddr); -void i2c_init_board(void); +int i2c_init_board(void); #ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT void i2c_board_late_init(void); #endif @@ -134,6 +201,104 @@ int i2x_mux_select_mux(int bus); int i2c_mux_ident_muxstring_f (uchar *buf); #endif +#ifdef CONFIG_SYS_I2C +/* + * Initialization, must be called once on start up, may be called + * repeatedly to change the speed and slave addresses. + */ +void i2c_init(unsigned int speed, int slaveaddr); +#ifdef CONFIG_SYS_I2C_INIT_BOARD +void i2c_init_board(void); +#endif + +/* + * i2c_get_bus_num: + * + * Returns index of currently active I2C bus. Zero-based. + */ +unsigned int i2c_get_bus_num(void); + +/* + * i2c_set_bus_num: + * + * Change the active I2C bus. Subsequent read/write calls will + * go to this one. + * + * bus - bus index, zero based + * + * Returns: 0 on success, not 0 on failure + * + */ +int i2c_set_bus_num(unsigned int bus); + +/* + * i2c_init_all(): + * + * Initializes all I2C adapters in the system. All i2c_adap structures must + * be initialized beforehead with function pointers and data, including + * speed and slaveaddr. Returns 0 on success, non-0 on failure. + */ +void i2c_init_all(void); + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(u_int8_t chip); + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(u_int8_t chip, unsigned int addr, int alen, + u_int8_t *buffer, int len); + +int i2c_write(u_int8_t chip, unsigned int addr, int alen, + u_int8_t *buffer, int len); + +/* + * Utility routines to read/write registers. + */ +u_int8_t i2c_reg_read(u_int8_t addr, u_int8_t reg); + +void i2c_reg_write(u_int8_t addr, u_int8_t reg, u_int8_t val); + +/* + * i2c_set_bus_speed: + * + * Change the speed of the active I2C bus + * + * speed - bus speed in Hz + * + * Returns: new bus speed + * + */ +unsigned int i2c_set_bus_speed(unsigned int speed); + +/* + * i2c_get_bus_speed: + * + * Returns speed of currently active I2C bus in Hz + */ + +unsigned int i2c_get_bus_speed(void); + +/* + * i2c_reloc_fixup: + * + * Adjusts I2C pointers after U-Boot is relocated to DRAM + */ +void i2c_reloc_fixup(void); +#else + /* * Probe the given I2C chip address. Returns 0 if a chip responded, * not 0 on failure. @@ -235,6 +400,22 @@ int i2c_set_bus_speed(unsigned int); */ unsigned int i2c_get_bus_speed(void); +#endif /* CONFIG_SYS_I2C */ + +/* + * only for backwardcompatibility, should go away if we switched + * completely to new multibus support. + */ +#if (CONFIG_SYS_NUM_I2C_BUSSES > 1) || defined(CONFIG_I2C_MULTI_BUS) +# if !defined(CONFIG_SYS_MAX_I2C_BUS) +# define CONFIG_SYS_MAX_I2C_BUS 2 +# endif +# define I2C_MULTI_BUS 0 +#else +# define CONFIG_SYS_MAX_I2C_BUS 1 +# define I2C_MULTI_BUS 0 +#endif + /* NOTE: These two functions MUST be always_inline to avoid code growth! */ static inline unsigned int I2C_GET_BUS(void) __attribute__((always_inline));