From 385c9ef5a7215b2b0c22836fee6c692dfc8559d7 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Mon, 16 Jan 2012 21:12:23 +0000 Subject: i2c: add i2c_core and prepare for new multibus support 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 Cc: Mike Frysinger Cc: Stephen Warren --- drivers/i2c/Makefile | 1 + drivers/i2c/i2c_core.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 drivers/i2c/i2c_core.c (limited to 'drivers/i2c') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 72e85a349a7..06b211d4a27 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o +COBJS-$(CONFIG_SYS_I2C) += i2c_core.o COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o COBJS := $(COBJS-y) diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c new file mode 100644 index 00000000000..3c01893c9c4 --- /dev/null +++ b/drivers/i2c/i2c_core.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2009 Sergey Kubushyn + * + * (C) Copyright 2012 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Multibus/multiadapter I2C core functions (wrappers) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include + +struct i2c_adapter *i2c_get_adapter(int index) +{ + struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter, + i2c); + int max = ll_entry_count(struct i2c_adapter, i2c); + int i; + + if (index >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + index, max); + return i2c_adap_p; + } + if (index == 0) + return i2c_adap_p; + + for (i = 0; i < index; i++) + i2c_adap_p++; + + return i2c_adap_p; +} + +#if !defined(CONFIG_SYS_I2C_DIRECT_BUS) +struct i2c_bus_hose i2c_bus[CONFIG_SYS_NUM_I2C_BUSES] = + CONFIG_SYS_I2C_BUSES; +#endif + +DECLARE_GLOBAL_DATA_PTR; + +void i2c_reloc_fixup(void) +{ +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter, + i2c); + struct i2c_adapter *tmp = i2c_adap_p; + int max = ll_entry_count(struct i2c_adapter, i2c); + int i; + unsigned long addr; + + if (gd->reloc_off == 0) + return; + + for (i = 0; i < max; i++) { + /* adapter itself */ + addr = (unsigned long)i2c_adap_p; + addr += gd->reloc_off; + i2c_adap_p = (struct i2c_adapter *)addr; + /* i2c_init() */ + addr = (unsigned long)i2c_adap_p->init; + addr += gd->reloc_off; + i2c_adap_p->init = (void (*)(int, int))addr; + /* i2c_probe() */ + addr = (unsigned long)i2c_adap_p->probe; + addr += gd->reloc_off; + i2c_adap_p->probe = (int (*)(uint8_t))addr; + /* i2c_read() */ + addr = (unsigned long)i2c_adap_p->read; + addr += gd->reloc_off; + i2c_adap_p->read = (int (*)(uint8_t, uint, int, uint8_t *, + int))addr; + /* i2c_write() */ + addr = (unsigned long)i2c_adap_p->write; + addr += gd->reloc_off; + i2c_adap_p->write = (int (*)(uint8_t, uint, int, uint8_t *, + int))addr; + /* i2c_set_bus_speed() */ + addr = (unsigned long)i2c_adap_p->set_bus_speed; + addr += gd->reloc_off; + i2c_adap_p->set_bus_speed = (uint (*)(uint))addr; + /* name */ + addr = (unsigned long)i2c_adap_p->name; + addr += gd->reloc_off; + i2c_adap_p->name = (char *)addr; + tmp++; + i2c_adap_p = tmp; + } +#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(struct i2c_adapter *adap, int mux_id, int chip, + int channel) +{ + uint8_t buf; + int ret; + + /* channel < 0 - turn off the mux */ + if (channel < 0) { + buf = 0; + ret = adap->write(adap, chip, 0, 0, &buf, 1); + if (ret) + printf("%s: Could not turn off the mux.\n", __func__); + return ret; + } + + switch (mux_id) { + case I2C_MUX_PCA9540_ID: + case I2C_MUX_PCA9542_ID: + if (channel > 1) + return -1; + buf = (uint8_t)((channel & 0x01) | (1 << 2)); + break; + case I2C_MUX_PCA9544_ID: + if (channel > 3) + return -1; + buf = (uint8_t)((channel & 0x03) | (1 << 2)); + break; + case I2C_MUX_PCA9547_ID: + if (channel > 7) + return -1; + buf = (uint8_t)((channel & 0x07) | (1 << 3)); + break; + default: + printf("%s: wrong mux id: %d\n", __func__, mux_id); + return -1; + } + + ret = adap->write(adap, chip, 0, 0, &buf, 1); + if (ret) + printf("%s: could not set mux: id: %d chip: %x channel: %d\n", + __func__, mux_id, chip, channel); + return ret; +} + +static int i2c_mux_set_all(void) +{ + struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; + int i; + + /* Connect requested bus if behind muxes */ + if (i2c_bus_tmp->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_tmp->next_hop[i].chip == 0) + break; + + ret = i2c_mux_set(I2C_ADAP, + i2c_bus_tmp->next_hop[i].mux.id, + i2c_bus_tmp->next_hop[i].chip, + i2c_bus_tmp->next_hop[i].channel); + if (ret != 0) + return ret; + } + } + return 0; +} + +static int i2c_mux_disconnet_all(void) +{ + struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; + int i; + uint8_t buf; + + if (I2C_ADAP->init_done == 0) + return 0; + + /* Disconnect current bus (turn off muxes if any) */ + if ((i2c_bus_tmp->next_hop[0].chip != 0) && + (I2C_ADAP->init_done != 0)) { + i = CONFIG_SYS_I2C_MAX_HOPS; + do { + uint8_t chip; + int ret; + + chip = i2c_bus_tmp->next_hop[--i].chip; + if (chip == 0) + continue; + + ret = I2C_ADAP->write(I2C_ADAP, chip, 0, 0, &buf, 1); + if (ret != 0) { + printf("i2c: mux diconnect error\n"); + return ret; + } + } while (i > 0); + } + + return 0; +} +#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_BUSES) + return; + + I2C_ADAP->init(I2C_ADAP, speed, slaveaddr); + + if (gd->flags & GD_FLG_RELOC) { + I2C_ADAP->init_done = 1; + I2C_ADAP->speed = speed; + I2C_ADAP->slaveaddr = slaveaddr; + } +} + +/* implement possible board specific board init */ +static void __def_i2c_init_board(void) +{ +} +void i2c_init_board(void) + __attribute__((weak, alias("__def_i2c_init_board"))); + +/* + * 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_init_board(); + 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 gd->cur_i2c_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) +{ + int max = ll_entry_count(struct i2c_adapter, i2c); + + if (I2C_ADAPTER(bus) >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + I2C_ADAPTER(bus), max); + return -2; + } +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + if (bus >= CONFIG_SYS_NUM_I2C_BUSES) + return -1; +#endif + + if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) + return 0; + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + i2c_mux_disconnet_all(); +#endif + + gd->cur_i2c_bus = bus; + if (I2C_ADAP->init_done == 0) + i2c_init_bus(bus, I2C_ADAP->speed, I2C_ADAP->slaveaddr); + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + i2c_mux_set_all(); +#endif + return 0; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uint8_t chip) +{ + return I2C_ADAP->probe(I2C_ADAP, 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(uint8_t chip, unsigned int addr, int alen, + uint8_t *buffer, int len) +{ + return I2C_ADAP->read(I2C_ADAP, chip, addr, alen, buffer, len); +} + +int i2c_write(uint8_t chip, unsigned int addr, int alen, + uint8_t *buffer, int len) +{ + return I2C_ADAP->write(I2C_ADAP, 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(I2C_ADAP, 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; +} + +uint8_t i2c_reg_read(uint8_t addr, uint8_t reg) +{ + uint8_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_get_bus_num()); +#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_get_bus_num(), addr, reg, buf); +#endif + + return buf; +} + +void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_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_get_bus_num()); +#endif + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_get_bus_num(), addr, reg, val); +#endif + + i2c_write(addr, reg, 1, &val, 1); +} + +void __i2c_init(int speed, int slaveaddr) +{ + i2c_init_bus(i2c_get_bus_num(), speed, slaveaddr); +} +void i2c_init(int speed, int slaveaddr) + __attribute__((weak, alias("__i2c_init"))); -- cgit v1.2.3 From ea818dbbcd59300b56014ac2d67798a54994eb9b Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Tue, 29 Jan 2013 08:53:15 +0100 Subject: i2c, soft-i2c: switch to new multibus/multiadapter support - added to soft_i2c driver new multibus/multiadpater support - adapted all config files, which uses this driver Signed-off-by: Heiko Schocher Cc: Simon Glass Cc: Stephen Warren --- drivers/i2c/Makefile | 2 +- drivers/i2c/soft_i2c.c | 104 +++++++++++++++++++++++++++---------------------- 2 files changed, 58 insertions(+), 48 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 06b211d4a27..a885e26ce9f 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -40,13 +40,13 @@ COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o -COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o COBJS-$(CONFIG_TEGRA_I2C) += tegra_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_SH_SH7734_I2C) += sh_sh7734_i2c.o COBJS-$(CONFIG_SYS_I2C) += i2c_core.o +COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o COBJS := $(COBJS-y) diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index ae3c57392b8..12693041d7f 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,8 @@ /* + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * * (C) Copyright 2001, 2002 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * @@ -103,14 +107,30 @@ /* #define DEBUG_I2C */ -#ifdef DEBUG_I2C DECLARE_GLOBAL_DATA_PTR; + +#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); +# elif defined(CONFIG_8xx) +# define I2C_SOFT_DECLARATIONS volatile immap_t *immr = \ + (immap_t *)CONFIG_SYS_IMMR; +# else +# define I2C_SOFT_DECLARATIONS +# endif +#endif + +#if !defined(CONFIG_SYS_SOFT_I2C_SPEED) +#define CONFIG_SYS_SOFT_I2C_SPEED CONFIG_SYS_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_SOFT_I2C_SLAVE) +#define CONFIG_SYS_SOFT_I2C_SLAVE CONFIG_SYS_I2C_SLAVE #endif /*----------------------------------------------------------------------- * Definitions */ - #define RETRIES 0 #define I2C_ACK 0 /* PD_SDA level to ack a byte */ @@ -125,10 +145,6 @@ DECLARE_GLOBAL_DATA_PTR; #define PRINTD(fmt,args...) #endif -#if defined(CONFIG_I2C_MULTI_BUS) -static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; -#endif /* CONFIG_I2C_MULTI_BUS */ - /*----------------------------------------------------------------------- * Local functions */ @@ -267,39 +283,6 @@ static int write_byte(uchar data) return(nack); /* not a nack is an ack */ } -#if defined(CONFIG_I2C_MULTI_BUS) -/* - * Functions for multiple I2C bus handling - */ -unsigned int i2c_get_bus_num(void) -{ - return i2c_bus_num; -} - -int i2c_set_bus_num(unsigned int bus) -{ -#if defined(CONFIG_I2C_MUX) - if (bus < CONFIG_SYS_MAX_I2C_BUS) { - i2c_bus_num = bus; - } else { - int ret; - - ret = i2x_mux_select_mux(bus); - i2c_init_board(); - if (ret == 0) - i2c_bus_num = bus; - else - return ret; - } -#else - if (bus >= CONFIG_SYS_MAX_I2C_BUS) - return -1; - i2c_bus_num = bus; -#endif - return 0; -} -#endif - /*----------------------------------------------------------------------- * if ack == I2C_ACK, ACK the byte so can continue reading, else * send I2C_NOACK to end the read. @@ -330,14 +313,10 @@ static uchar read_byte(int ack) return(data); } -/*=====================================================================*/ -/* Public Functions */ -/*=====================================================================*/ - /*----------------------------------------------------------------------- * Initialization */ -void i2c_init (int speed, int slaveaddr) +static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { #if defined(CONFIG_SYS_I2C_INIT_BOARD) /* call board specific i2c bus reset routine before accessing the */ @@ -360,7 +339,7 @@ void i2c_init (int speed, int slaveaddr) * completion of EEPROM writes since the chip stops responding until * the write completes (typically 10mSec). */ -int i2c_probe(uchar addr) +static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) { int rc; @@ -378,7 +357,8 @@ int i2c_probe(uchar addr) /*----------------------------------------------------------------------- * Read bytes */ -int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) { int shift; PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n", @@ -452,7 +432,8 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) /*----------------------------------------------------------------------- * Write bytes */ -int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) { int shift, failures = 0; @@ -482,3 +463,32 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) send_stop(); return(failures); } + +/* + * Register soft i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, + 0) +#if defined(I2C_SOFT_DECLARATIONS2) +U_BOOT_I2C_ADAP_COMPLETE(soft1, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_2, + CONFIG_SYS_I2C_SOFT_SLAVE_2, + 1) +#endif +#if defined(I2C_SOFT_DECLARATIONS3) +U_BOOT_I2C_ADAP_COMPLETE(soft2, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_3, + CONFIG_SYS_I2C_SOFT_SLAVE_3, + 2) +#endif +#if defined(I2C_SOFT_DECLARATIONS4) +U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_4, + CONFIG_SYS_I2C_SOFT_SLAVE_4, + 3) +#endif -- cgit v1.2.3 From 00f792e0df9ae942427e44595a0f4379582accee Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Wed, 24 Oct 2012 13:48:22 +0200 Subject: i2c, fsl_i2c: switch to new multibus/multiadapter support - added to fsl_i2c driver new multibus/multiadpater support - adapted all config files, which uses this driver Signed-off-by: Heiko Schocher Cc: Simon Glass Cc: Stephen Warren --- drivers/i2c/Makefile | 2 +- drivers/i2c/fsl_i2c.c | 218 ++++++++++++++++++++------------------------------ 2 files changed, 86 insertions(+), 134 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index a885e26ce9f..1202d2454e2 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -28,7 +28,6 @@ LIB := $(obj)libi2c.o COBJS-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o COBJS-$(CONFIG_DW_I2C) += designware_i2c.o -COBJS-$(CONFIG_FSL_I2C) += fsl_i2c.o COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o COBJS-$(CONFIG_I2C_MV) += mv_i2c.o COBJS-$(CONFIG_I2C_MXC) += mxc_i2c.o @@ -46,6 +45,7 @@ COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o COBJS-$(CONFIG_SYS_I2C) += i2c_core.o +COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 5d7e0103197..38455e1c664 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -1,6 +1,9 @@ /* * Copyright 2006,2009 Freescale Semiconductor, Inc. * + * 2012, Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * Version 2 as published by the Free Software Foundation. @@ -17,12 +20,8 @@ */ #include - -#ifdef CONFIG_HARD_I2C - #include #include /* Functional interface */ - #include #include /* HW definitions */ @@ -47,25 +46,10 @@ DECLARE_GLOBAL_DATA_PTR; -/* Initialize the bus pointer to whatever one the SPD EEPROM is on. - * Default is bus 0. This is necessary because the DDR initialization - * runs from ROM, and we can't switch buses because we can't modify - * the global variables. - */ -#ifndef CONFIG_SYS_SPD_BUS_NUM -#define CONFIG_SYS_SPD_BUS_NUM 0 -#endif -static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = CONFIG_SYS_SPD_BUS_NUM; -#if defined(CONFIG_I2C_MUX) -static unsigned int i2c_bus_num_mux __attribute__ ((section ("data"))) = 0; -#endif - -static unsigned int i2c_bus_speed[2] = {CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED}; - static const struct fsl_i2c *i2c_dev[2] = { - (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET), -#ifdef CONFIG_SYS_I2C2_OFFSET - (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C2_OFFSET) + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET), +#ifdef CONFIG_SYS_FSL_I2C2_OFFSET + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET) #endif }; @@ -222,12 +206,9 @@ static unsigned int get_i2c_clock(int bus) return gd->arch.i2c1_clk; /* I2C1 clock */ } -void -i2c_init(int speed, int slaveadd) +static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) { const struct fsl_i2c *dev; - unsigned int temp; - int bus_num, i; #ifdef CONFIG_SYS_I2C_INIT_BOARD /* Call board specific i2c bus reset routine before accessing the @@ -236,23 +217,14 @@ i2c_init(int speed, int slaveadd) */ i2c_init_board(); #endif -#ifdef CONFIG_SYS_I2C2_OFFSET - bus_num = 2; -#else - bus_num = 1; -#endif - for (i = 0; i < bus_num; i++) { - dev = i2c_dev[i]; - - writeb(0, &dev->cr); /* stop I2C controller */ - udelay(5); /* let it shutdown in peace */ - temp = set_i2c_bus_speed(dev, get_i2c_clock(i), speed); - if (gd->flags & GD_FLG_RELOC) - i2c_bus_speed[i] = temp; - writeb(slaveadd << 1, &dev->adr);/* write slave address */ - writeb(0x0, &dev->sr); /* clear status register */ - writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ - } + dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed); + writeb(slaveadd << 1, &dev->adr);/* write slave address */ + writeb(0x0, &dev->sr); /* clear status register */ + writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ #ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT /* Call board specific i2c bus reset routine AFTER the bus has been @@ -265,12 +237,13 @@ i2c_init(int speed, int slaveadd) } static int -i2c_wait4bus(void) +i2c_wait4bus(struct i2c_adapter *adap) { + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; unsigned long long timeval = get_ticks(); const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); - while (readb(&i2c_dev[i2c_bus_num]->sr) & I2C_SR_MBB) { + while (readb(&dev->sr) & I2C_SR_MBB) { if ((get_ticks() - timeval) > timeout) return -1; } @@ -279,20 +252,21 @@ i2c_wait4bus(void) } static __inline__ int -i2c_wait(int write) +i2c_wait(struct i2c_adapter *adap, int write) { u32 csr; unsigned long long timeval = get_ticks(); const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT); + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; do { - csr = readb(&i2c_dev[i2c_bus_num]->sr); + csr = readb(&dev->sr); if (!(csr & I2C_SR_MIF)) continue; /* Read again to allow register to stabilise */ - csr = readb(&i2c_dev[i2c_bus_num]->sr); + csr = readb(&dev->sr); - writeb(0x0, &i2c_dev[i2c_bus_num]->sr); + writeb(0x0, &dev->sr); if (csr & I2C_SR_MAL) { debug("i2c_wait: MAL\n"); @@ -317,29 +291,32 @@ i2c_wait(int write) } static __inline__ int -i2c_write_addr (u8 dev, u8 dir, int rsta) +i2c_write_addr(struct i2c_adapter *adap, u8 dev, u8 dir, int rsta) { + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX | (rsta ? I2C_CR_RSTA : 0), - &i2c_dev[i2c_bus_num]->cr); + &device->cr); - writeb((dev << 1) | dir, &i2c_dev[i2c_bus_num]->dr); + writeb((dev << 1) | dir, &device->dr); - if (i2c_wait(I2C_WRITE_BIT) < 0) + if (i2c_wait(adap, I2C_WRITE_BIT) < 0) return 0; return 1; } static __inline__ int -__i2c_write(u8 *data, int length) +__i2c_write(struct i2c_adapter *adap, u8 *data, int length) { + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; int i; for (i = 0; i < length; i++) { - writeb(data[i], &i2c_dev[i2c_bus_num]->dr); + writeb(data[i], &dev->dr); - if (i2c_wait(I2C_WRITE_BIT) < 0) + if (i2c_wait(adap, I2C_WRITE_BIT) < 0) break; } @@ -347,57 +324,60 @@ __i2c_write(u8 *data, int length) } static __inline__ int -__i2c_read(u8 *data, int length) +__i2c_read(struct i2c_adapter *adap, u8 *data, int length) { + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; int i; writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0), - &i2c_dev[i2c_bus_num]->cr); + &dev->cr); /* dummy read */ - readb(&i2c_dev[i2c_bus_num]->dr); + readb(&dev->dr); for (i = 0; i < length; i++) { - if (i2c_wait(I2C_READ_BIT) < 0) + if (i2c_wait(adap, I2C_READ_BIT) < 0) break; /* Generate ack on last next to last byte */ if (i == length - 2) writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK, - &i2c_dev[i2c_bus_num]->cr); + &dev->cr); /* Do not generate stop on last byte */ if (i == length - 1) writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX, - &i2c_dev[i2c_bus_num]->cr); + &dev->cr); - data[i] = readb(&i2c_dev[i2c_bus_num]->dr); + data[i] = readb(&dev->dr); } return i; } -int -i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +static int +fsl_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr, int alen, u8 *data, + int length) { + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; int i = -1; /* signal error */ u8 *a = (u8*)&addr; - if (i2c_wait4bus() < 0) + if (i2c_wait4bus(adap) < 0) return -1; if ((!length || alen > 0) - && i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0 - && __i2c_write(&a[4 - alen], alen) == alen) + && i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 + && __i2c_write(adap, &a[4 - alen], alen) == alen) i = 0; /* No error so far */ if (length && - i2c_write_addr(dev, I2C_READ_BIT, alen ? 1 : 0) != 0) - i = __i2c_read(data, length); + i2c_write_addr(adap, dev, I2C_READ_BIT, alen ? 1 : 0) != 0) + i = __i2c_read(adap, data, length); - writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); + writeb(I2C_CR_MEN, &device->cr); - if (i2c_wait4bus()) /* Wait until STOP */ + if (i2c_wait4bus(adap)) /* Wait until STOP */ debug("i2c_read: wait4bus timed out\n"); if (i == length) @@ -406,20 +386,22 @@ i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) return -1; } -int -i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +static int +fsl_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, int alen, + u8 *data, int length) { + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; int i = -1; /* signal error */ u8 *a = (u8*)&addr; - if (i2c_wait4bus() >= 0 - && i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0 - && __i2c_write(&a[4 - alen], alen) == alen) { - i = __i2c_write(data, length); + if (i2c_wait4bus(adap) >= 0 && + i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + __i2c_write(adap, &a[4 - alen], alen) == alen) { + i = __i2c_write(adap, data, length); } - writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); - if (i2c_wait4bus()) /* Wait until STOP */ + writeb(I2C_CR_MEN, &device->cr); + if (i2c_wait4bus(adap)) /* Wait until STOP */ debug("i2c_write: wait4bus timed out\n"); if (i == length) @@ -428,72 +410,42 @@ i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) return -1; } -int -i2c_probe(uchar chip) +static int +fsl_i2c_probe(struct i2c_adapter *adap, uchar chip) { + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; /* For unknow reason the controller will ACK when * probing for a slave with the same address, so skip * it. */ - if (chip == (readb(&i2c_dev[i2c_bus_num]->adr) >> 1)) - return -1; - - return i2c_read(chip, 0, 0, NULL, 0); -} - -int i2c_set_bus_num(unsigned int bus) -{ -#if defined(CONFIG_I2C_MUX) - if (bus < CONFIG_SYS_MAX_I2C_BUS) { - i2c_bus_num = bus; - } else { - int ret; - - ret = i2x_mux_select_mux(bus); - if (ret) - return ret; - i2c_bus_num = 0; - } - i2c_bus_num_mux = bus; -#else -#ifdef CONFIG_SYS_I2C2_OFFSET - if (bus > 1) { -#else - if (bus > 0) { -#endif + if (chip == (readb(&dev->adr) >> 1)) return -1; - } - i2c_bus_num = bus; -#endif - return 0; + return fsl_i2c_read(adap, chip, 0, 0, NULL, 0); } -int i2c_set_bus_speed(unsigned int speed) +static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) { - unsigned int i2c_clk = (i2c_bus_num == 1) - ? gd->arch.i2c2_clk : gd->arch.i2c1_clk; + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; - writeb(0, &i2c_dev[i2c_bus_num]->cr); /* stop controller */ - i2c_bus_speed[i2c_bus_num] = - set_i2c_bus_speed(i2c_dev[i2c_bus_num], i2c_clk, speed); - writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); /* start controller */ + writeb(0, &dev->cr); /* stop controller */ + set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed); + writeb(I2C_CR_MEN, &dev->cr); /* start controller */ return 0; } -unsigned int i2c_get_bus_num(void) -{ -#if defined(CONFIG_I2C_MUX) - return i2c_bus_num_mux; -#else - return i2c_bus_num; +/* + * Register fsl i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C_SPEED, CONFIG_SYS_FSL_I2C_SLAVE, + 0) +#ifdef CONFIG_SYS_FSL_I2C2_OFFSET +U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE, + 1) #endif -} - -unsigned int i2c_get_bus_speed(void) -{ - return i2c_bus_speed[i2c_bus_num]; -} - -#endif /* CONFIG_HARD_I2C */ -- cgit v1.2.3 From d84eb856c4385c90f4f68594819744b638f77a02 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 30 Oct 2012 07:28:52 +0000 Subject: tegra: i2c: Add function to know about current bus Rather than using a variable in various places, add a single function, tegra_i2c_get_bus(), which returns a pointer to information about a bus. This will make it easier to move to the new i2c framework. Signed-off-by: Simon Glass --- drivers/i2c/tegra_i2c.c | 78 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 17 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index ca71cd3ee4c..8fa1cdac025 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -284,7 +284,8 @@ exit: return error; } -static int tegra_i2c_write_data(u32 addr, u8 *data, u32 len) +static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, + u32 len) { int error; struct i2c_trans_info trans_info; @@ -295,14 +296,15 @@ static int tegra_i2c_write_data(u32 addr, u8 *data, u32 len) trans_info.num_bytes = len; trans_info.is_10bit_address = 0; - error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info); + error = send_recv_packets(bus, &trans_info); if (error) debug("tegra_i2c_write_data: Error (%d) !!!\n", error); return error; } -static int tegra_i2c_read_data(u32 addr, u8 *data, u32 len) +static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, + u32 len) { int error; struct i2c_trans_info trans_info; @@ -313,7 +315,7 @@ static int tegra_i2c_read_data(u32 addr, u8 *data, u32 len) trans_info.num_bytes = len; trans_info.is_10bit_address = 0; - error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info); + error = send_recv_packets(bus, &trans_info); if (error) debug("tegra_i2c_read_data: Error (%d) !!!\n", error); @@ -324,18 +326,48 @@ static int tegra_i2c_read_data(u32 addr, u8 *data, u32 len) #error "Please enable device tree support to use this driver" #endif +/** + * Check that a bus number is valid and return a pointer to it + * + * @param bus_num Bus number to check / return + * @return pointer to bus, if valid, else NULL + */ +static struct i2c_bus *tegra_i2c_get_bus(unsigned int bus_num) +{ + struct i2c_bus *bus; + + if (bus_num >= TEGRA_I2C_NUM_CONTROLLERS) { + debug("%s: Invalid bus number %u\n", __func__, bus_num); + return NULL; + } + bus = &i2c_controllers[bus_num]; + if (!bus->inited) { + debug("%s: Bus %u not available\n", __func__, bus_num); + return NULL; + } + + return bus; +} + unsigned int i2c_get_bus_speed(void) { - return i2c_controllers[i2c_bus_num].speed; + struct i2c_bus *bus; + + bus = tegra_i2c_get_bus(i2c_bus_num); + if (!bus) + return 0; + return bus->speed; } int i2c_set_bus_speed(unsigned int speed) { - struct i2c_bus *i2c_bus; + struct i2c_bus *bus; - i2c_bus = &i2c_controllers[i2c_bus_num]; - i2c_bus->speed = speed; - i2c_init_controller(i2c_bus); + bus = tegra_i2c_get_bus(i2c_bus_num); + if (!bus) + return 0; + bus->speed = speed; + i2c_init_controller(bus); return 0; } @@ -458,7 +490,7 @@ void i2c_init(int speed, int slaveaddr) } /* i2c write version without the register address */ -int i2c_write_data(uchar chip, uchar *buffer, int len) +int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len) { int rc; @@ -470,7 +502,7 @@ int i2c_write_data(uchar chip, uchar *buffer, int len) debug("\n"); /* Shift 7-bit address over for lower-level i2c functions */ - rc = tegra_i2c_write_data(chip << 1, buffer, len); + rc = tegra_i2c_write_data(bus, chip << 1, buffer, len); if (rc) debug("i2c_write_data(): rc=%d\n", rc); @@ -478,13 +510,13 @@ int i2c_write_data(uchar chip, uchar *buffer, int len) } /* i2c read version without the register address */ -int i2c_read_data(uchar chip, uchar *buffer, int len) +int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len) { int rc; debug("inside i2c_read_data():\n"); /* Shift 7-bit address over for lower-level i2c functions */ - rc = tegra_i2c_read_data(chip << 1, buffer, len); + rc = tegra_i2c_read_data(bus, chip << 1, buffer, len); if (rc) { debug("i2c_read_data(): rc=%d\n", rc); return rc; @@ -502,12 +534,16 @@ int i2c_read_data(uchar chip, uchar *buffer, int len) /* Probe to see if a chip is present. */ int i2c_probe(uchar chip) { + struct i2c_bus *bus; int rc; uchar reg; debug("i2c_probe: addr=0x%x\n", chip); + bus = tegra_i2c_get_bus(i2c_get_bus_num()); + if (!bus) + return 1; reg = 0; - rc = i2c_write_data(chip, ®, 1); + rc = i2c_write_data(bus, chip, ®, 1); if (rc) { debug("Error probing 0x%x.\n", chip); return 1; @@ -524,11 +560,15 @@ static int i2c_addr_ok(const uint addr, const int alen) /* Read bytes */ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) { + struct i2c_bus *bus; uint offset; int i; debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n", chip, addr, len); + bus = tegra_i2c_get_bus(i2c_bus_num); + if (!bus) + return 1; if (!i2c_addr_ok(addr, alen)) { debug("i2c_read: Bad address %x.%d.\n", addr, alen); return 1; @@ -540,13 +580,13 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) data[alen - i - 1] = (addr + offset) >> (8 * i); } - if (i2c_write_data(chip, data, alen)) { + if (i2c_write_data(bus, chip, data, alen)) { debug("i2c_read: error sending (0x%x)\n", addr); return 1; } } - if (i2c_read_data(chip, buffer + offset, 1)) { + if (i2c_read_data(bus, chip, buffer + offset, 1)) { debug("i2c_read: error reading (0x%x)\n", addr); return 1; } @@ -558,11 +598,15 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) /* Write bytes */ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) { + struct i2c_bus *bus; uint offset; int i; debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n", chip, addr, len); + bus = tegra_i2c_get_bus(i2c_bus_num); + if (!bus) + return 1; if (!i2c_addr_ok(addr, alen)) { debug("i2c_write: Bad address %x.%d.\n", addr, alen); return 1; @@ -572,7 +616,7 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) for (i = 0; i < alen; i++) data[alen - i - 1] = (addr + offset) >> (8 * i); data[alen] = buffer[offset]; - if (i2c_write_data(chip, data, alen + 1)) { + if (i2c_write_data(bus, chip, data, alen + 1)) { debug("i2c_write: error sending (0x%x)\n", addr); return 1; } -- cgit v1.2.3 From 1f2ba722ac06393d6abe6d4734824d3b98ea9108 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 30 Oct 2012 07:28:53 +0000 Subject: tegra: i2c: Enable new CONFIG_SYS_I2C framework This enables CONFIG_SYS_I2C on Tegra, updating existing boards and the Tegra i2c driver to support this. Signed-off-by: Simon Glass Signed-off-by: Heiko Schocher --- drivers/i2c/Makefile | 2 +- drivers/i2c/tegra_i2c.c | 80 ++++++++++++++++++++----------------------------- 2 files changed, 33 insertions(+), 49 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 1202d2454e2..e1da58d7bc1 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -39,7 +39,6 @@ COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o -COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o @@ -47,6 +46,7 @@ COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o COBJS-$(CONFIG_SYS_I2C) += i2c_core.o COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o +COBJS-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o COBJS := $(COBJS-y) diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 8fa1cdac025..9bf71a689f1 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -35,8 +35,6 @@ DECLARE_GLOBAL_DATA_PTR; -static unsigned int i2c_bus_num; - /* Information about i2c controller */ struct i2c_bus { int id; @@ -332,38 +330,25 @@ static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, * @param bus_num Bus number to check / return * @return pointer to bus, if valid, else NULL */ -static struct i2c_bus *tegra_i2c_get_bus(unsigned int bus_num) +static struct i2c_bus *tegra_i2c_get_bus(struct i2c_adapter *adap) { struct i2c_bus *bus; - if (bus_num >= TEGRA_I2C_NUM_CONTROLLERS) { - debug("%s: Invalid bus number %u\n", __func__, bus_num); - return NULL; - } - bus = &i2c_controllers[bus_num]; + bus = &i2c_controllers[adap->hwadapnr]; if (!bus->inited) { - debug("%s: Bus %u not available\n", __func__, bus_num); + debug("%s: Bus %u not available\n", __func__, adap->hwadapnr); return NULL; } return bus; } -unsigned int i2c_get_bus_speed(void) -{ - struct i2c_bus *bus; - - bus = tegra_i2c_get_bus(i2c_bus_num); - if (!bus) - return 0; - return bus->speed; -} - -int i2c_set_bus_speed(unsigned int speed) +static unsigned int tegra_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) { struct i2c_bus *bus; - bus = tegra_i2c_get_bus(i2c_bus_num); + bus = tegra_i2c_get_bus(adap); if (!bus) return 0; bus->speed = speed; @@ -482,7 +467,7 @@ void i2c_init_board(void) return; } -void i2c_init(int speed, int slaveaddr) +static void tegra_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { /* This will override the speed selected in the fdt for that port */ debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); @@ -532,14 +517,14 @@ int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len) } /* Probe to see if a chip is present. */ -int i2c_probe(uchar chip) +static int tegra_i2c_probe(struct i2c_adapter *adap, uchar chip) { struct i2c_bus *bus; int rc; uchar reg; debug("i2c_probe: addr=0x%x\n", chip); - bus = tegra_i2c_get_bus(i2c_get_bus_num()); + bus = tegra_i2c_get_bus(adap); if (!bus) return 1; reg = 0; @@ -558,7 +543,8 @@ static int i2c_addr_ok(const uint addr, const int alen) } /* Read bytes */ -int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +static int tegra_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) { struct i2c_bus *bus; uint offset; @@ -566,7 +552,7 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n", chip, addr, len); - bus = tegra_i2c_get_bus(i2c_bus_num); + bus = tegra_i2c_get_bus(adap); if (!bus) return 1; if (!i2c_addr_ok(addr, alen)) { @@ -596,7 +582,8 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) } /* Write bytes */ -int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +static int tegra_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) { struct i2c_bus *bus; uint offset; @@ -604,7 +591,7 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n", chip, addr, len); - bus = tegra_i2c_get_bus(i2c_bus_num); + bus = tegra_i2c_get_bus(adap); if (!bus) return 1; if (!i2c_addr_ok(addr, alen)) { @@ -625,30 +612,11 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) return 0; } -#if defined(CONFIG_I2C_MULTI_BUS) -/* - * Functions for multiple I2C bus handling - */ -unsigned int i2c_get_bus_num(void) -{ - return i2c_bus_num; -} - -int i2c_set_bus_num(unsigned int bus) -{ - if (bus >= TEGRA_I2C_NUM_CONTROLLERS || !i2c_controllers[bus].inited) - return -1; - i2c_bus_num = bus; - - return 0; -} -#endif - int tegra_i2c_get_dvc_bus_num(void) { int i; - for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; i++) { + for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; i++) { struct i2c_bus *bus = &i2c_controllers[i]; if (bus->inited && bus->is_dvc) @@ -657,3 +625,19 @@ int tegra_i2c_get_dvc_bus_num(void) return -1; } + +/* + * Register soft i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(tegra0, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 0) +U_BOOT_I2C_ADAP_COMPLETE(tegra1, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 1) +U_BOOT_I2C_ADAP_COMPLETE(tegra2, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 2) +U_BOOT_I2C_ADAP_COMPLETE(tegra3, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 3) -- cgit v1.2.3 From 880540decfb855e96bc14ac84ac7784669e4b382 Mon Sep 17 00:00:00 2001 From: Dirk Eibach Date: Thu, 25 Apr 2013 02:40:01 +0000 Subject: i2c, ppc4xx_i2c: switch to new multibus/multiadapter support Signed-off-by: Dirk Eibach Cc: Heiko Schocher Cc: Stefan Roese Tested-by: Stefan Roese --- drivers/i2c/Makefile | 2 +- drivers/i2c/ppc4xx_i2c.c | 193 +++++++++++++++++++++++------------------------ 2 files changed, 97 insertions(+), 98 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index e1da58d7bc1..360f93edf7d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -36,7 +36,6 @@ COBJS-$(CONFIG_DRIVER_OMAP1510_I2C) += omap1510_i2c.o COBJS-$(CONFIG_DRIVER_OMAP24XX_I2C) += omap24xx_i2c.o COBJS-$(CONFIG_DRIVER_OMAP34XX_I2C) += omap24xx_i2c.o COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o -COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o @@ -45,6 +44,7 @@ COBJS-$(CONFIG_SH_I2C) += sh_i2c.o COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o COBJS-$(CONFIG_SYS_I2C) += i2c_core.o COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o +COBJS-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o COBJS-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o diff --git a/drivers/i2c/ppc4xx_i2c.c b/drivers/i2c/ppc4xx_i2c.c index 53fedd594cb..c924874b599 100644 --- a/drivers/i2c/ppc4xx_i2c.c +++ b/drivers/i2c/ppc4xx_i2c.c @@ -32,27 +32,29 @@ #include #include -#ifdef CONFIG_HARD_I2C - DECLARE_GLOBAL_DATA_PTR; -#if defined(CONFIG_I2C_MULTI_BUS) -/* - * Initialize the bus pointer to whatever one the SPD EEPROM is on. - * Default is bus 0. This is necessary because the DDR initialization - * runs from ROM, and we can't switch buses because we can't modify - * the global variables. - */ -#ifndef CONFIG_SYS_SPD_BUS_NUM -#define CONFIG_SYS_SPD_BUS_NUM 0 +static inline struct ppc4xx_i2c *ppc4xx_get_i2c(int hwadapnr) +{ + unsigned long base; + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000700 + (hwadapnr * 0x100); +#elif defined(CONFIG_440) || defined(CONFIG_405EX) +/* all remaining 440 variants */ + base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000400 + (hwadapnr * 0x100); +#else +/* all 405 variants */ + base = 0xEF600500 + (hwadapnr * 0x100); #endif -static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = - CONFIG_SYS_SPD_BUS_NUM; -#endif /* CONFIG_I2C_MULTI_BUS */ + return (struct ppc4xx_i2c *)base; +} -static void _i2c_bus_reset(void) +static void _i2c_bus_reset(struct i2c_adapter *adap) { - struct ppc4xx_i2c *i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR; + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); int i; u8 dc; @@ -91,11 +93,10 @@ static void _i2c_bus_reset(void) out_8(&i2c->xtcntlss, 0); } -void i2c_init(int speed, int slaveaddr) +static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { - struct ppc4xx_i2c *i2c; + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); int val, divisor; - int bus; #ifdef CONFIG_SYS_I2C_INIT_BOARD /* @@ -106,67 +107,57 @@ void i2c_init(int speed, int slaveaddr) i2c_init_board(); #endif - for (bus = 0; bus < CONFIG_SYS_MAX_I2C_BUS; bus++) { - I2C_SET_BUS(bus); - - /* Set i2c pointer after calling I2C_SET_BUS() */ - i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR; - - /* Handle possible failed I2C state */ - /* FIXME: put this into i2c_init_board()? */ - _i2c_bus_reset(); + /* Handle possible failed I2C state */ + /* FIXME: put this into i2c_init_board()? */ + _i2c_bus_reset(adap); - /* clear lo master address */ - out_8(&i2c->lmadr, 0); + /* clear lo master address */ + out_8(&i2c->lmadr, 0); - /* clear hi master address */ - out_8(&i2c->hmadr, 0); - - /* clear lo slave address */ - out_8(&i2c->lsadr, 0); + /* clear hi master address */ + out_8(&i2c->hmadr, 0); - /* clear hi slave address */ - out_8(&i2c->hsadr, 0); + /* clear lo slave address */ + out_8(&i2c->lsadr, 0); - /* Clock divide Register */ - /* set divisor according to freq_opb */ - divisor = (get_OPB_freq() - 1) / 10000000; - if (divisor == 0) - divisor = 1; - out_8(&i2c->clkdiv, divisor); + /* clear hi slave address */ + out_8(&i2c->hsadr, 0); - /* no interrupts */ - out_8(&i2c->intrmsk, 0); + /* Clock divide Register */ + /* set divisor according to freq_opb */ + divisor = (get_OPB_freq() - 1) / 10000000; + if (divisor == 0) + divisor = 1; + out_8(&i2c->clkdiv, divisor); - /* clear transfer count */ - out_8(&i2c->xfrcnt, 0); + /* no interrupts */ + out_8(&i2c->intrmsk, 0); - /* clear extended control & stat */ - /* write 1 in SRC SRS SWC SWS to clear these fields */ - out_8(&i2c->xtcntlss, 0xF0); + /* clear transfer count */ + out_8(&i2c->xfrcnt, 0); - /* Mode Control Register - Flush Slave/Master data buffer */ - out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); + /* clear extended control & stat */ + /* write 1 in SRC SRS SWC SWS to clear these fields */ + out_8(&i2c->xtcntlss, 0xF0); - val = in_8(&i2c->mdcntl); + /* Mode Control Register + Flush Slave/Master data buffer */ + out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); - /* Ignore General Call, slave transfers are ignored, - * disable interrupts, exit unknown bus state, enable hold - * SCL 100kHz normaly or FastMode for 400kHz and above - */ + val = in_8(&i2c->mdcntl); - val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL; - if (speed >= 400000) - val |= IIC_MDCNTL_FSM; - out_8(&i2c->mdcntl, val); + /* Ignore General Call, slave transfers are ignored, + * disable interrupts, exit unknown bus state, enable hold + * SCL 100kHz normaly or FastMode for 400kHz and above + */ - /* clear control reg */ - out_8(&i2c->cntl, 0x00); - } + val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL; + if (speed >= 400000) + val |= IIC_MDCNTL_FSM; + out_8(&i2c->mdcntl, val); - /* set to SPD bus as default bus upon powerup */ - I2C_SET_BUS(CONFIG_SYS_SPD_BUS_NUM); + /* clear control reg */ + out_8(&i2c->cntl, 0x00); } /* @@ -194,14 +185,15 @@ void i2c_init(int speed, int slaveaddr) * * It does not check XFRCNT. */ -static int i2c_transfer(unsigned char cmd_type, +static int _i2c_transfer(struct i2c_adapter *adap, + unsigned char cmd_type, unsigned char chip, unsigned char addr[], unsigned char addr_len, unsigned char data[], unsigned short data_len) { - struct ppc4xx_i2c *i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR; + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); u8 *ptr; int reading; int tran, cnt; @@ -345,7 +337,7 @@ static int i2c_transfer(unsigned char cmd_type, return result; } -int i2c_probe(uchar chip) +static int ppc4xx_i2c_probe(struct i2c_adapter *adap, uchar chip) { uchar buf[1]; @@ -356,11 +348,11 @@ int i2c_probe(uchar chip) * address was ed (i.e. there was a chip at that address which * drove the data line low). */ - return (i2c_transfer(1, chip << 1, 0, 0, buf, 1) != 0); + return (_i2c_transfer(adap, 1, chip << 1, 0, 0, buf, 1) != 0); } -static int ppc4xx_i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, - int len, int read) +static int ppc4xx_i2c_transfer(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len, int read) { uchar xaddr[4]; int ret; @@ -394,43 +386,50 @@ static int ppc4xx_i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - if ((ret = i2c_transfer(read, chip << 1, &xaddr[4 - alen], alen, - buffer, len)) != 0) { + ret = _i2c_transfer(adap, read, chip << 1, &xaddr[4 - alen], alen, + buffer, len); + if (ret) { printf("I2C %s: failed %d\n", read ? "read" : "write", ret); - return 1; } return 0; } -int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len) +static int ppc4xx_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) { - return ppc4xx_i2c_transfer(chip, addr, alen, buffer, len, 1); + return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 1); } -int i2c_write(uchar chip, uint addr, int alen, uchar * buffer, int len) +static int ppc4xx_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) { - return ppc4xx_i2c_transfer(chip, addr, alen, buffer, len, 0); + return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 0); } -#if defined(CONFIG_I2C_MULTI_BUS) -/* - * Functions for multiple I2C bus handling - */ -unsigned int i2c_get_bus_num(void) +static unsigned int ppc4xx_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) { - return i2c_bus_num; -} - -int i2c_set_bus_num(unsigned int bus) -{ - if (bus >= CONFIG_SYS_MAX_I2C_BUS) + if (speed != adap->speed) return -1; - - i2c_bus_num = bus; - - return 0; + return speed; } -#endif /* CONFIG_I2C_MULTI_BUS */ -#endif /* CONFIG_HARD_I2C */ + +/* + * Register ppc4xx i2c adapters + */ +#ifdef CONFIG_SYS_I2C_PPC4XX_CH0 +U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_0, ppc4xx_i2c_init, ppc4xx_i2c_probe, + ppc4xx_i2c_read, ppc4xx_i2c_write, + ppc4xx_i2c_set_bus_speed, + CONFIG_SYS_I2C_PPC4XX_SPEED_0, + CONFIG_SYS_I2C_PPC4XX_SLAVE_0, 0) +#endif +#ifdef CONFIG_SYS_I2C_PPC4XX_CH1 +U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_1, ppc4xx_i2c_init, ppc4xx_i2c_probe, + ppc4xx_i2c_read, ppc4xx_i2c_write, + ppc4xx_i2c_set_bus_speed, + CONFIG_SYS_I2C_PPC4XX_SPEED_1, + CONFIG_SYS_I2C_PPC4XX_SLAVE_1, 1) +#endif -- cgit v1.2.3 From 30ea41a489cbfed311f904bd08cb3319f0e73b72 Mon Sep 17 00:00:00 2001 From: Alison Wang Date: Mon, 17 Jun 2013 15:30:39 +0800 Subject: I2C: mxc_i2c: Add support for Vybrid VF610 platform This patch adds support for Vybrid VF610 platform. There are some differences between i.MX6 and Vybrid for I2C controller. (1) The registers' offset are different. (2) The I2C clock divider values are different. (3) In I2C control register, the enable/disable/reset bit is inverted for Vybrid comparing to i.MX6. (4) In I2C status register, the interrupt flag bit is cleared by writing "1" for Vybrid. For i.MX6, this bit is cleared by writing "0". (5) In I2C status register, the arbitration lost flag bit is cleared by writing "1" for Vybrid. For i.MX6, this bit is cleared by writing "0". Signed-off-by: Alison Wang --- drivers/i2c/mxc_i2c.c | 62 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index a73b10b9c49..85e3e8b4eb8 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -38,6 +38,15 @@ #include #include +#ifdef I2C_QUIRK_REG +struct mxc_i2c_regs { + uint8_t iadr; + uint8_t ifdr; + uint8_t i2cr; + uint8_t i2sr; + uint8_t i2dr; +}; +#else struct mxc_i2c_regs { uint32_t iadr; uint32_t ifdr; @@ -45,8 +54,8 @@ struct mxc_i2c_regs { uint32_t i2sr; uint32_t i2dr; }; +#endif -#define I2CR_IEN (1 << 7) #define I2CR_IIEN (1 << 6) #define I2CR_MSTA (1 << 5) #define I2CR_MTX (1 << 4) @@ -59,10 +68,39 @@ struct mxc_i2c_regs { #define I2SR_IIF (1 << 1) #define I2SR_RX_NO_AK (1 << 0) +#ifdef I2C_QUIRK_REG +#define I2CR_IEN (0 << 7) +#define I2CR_IDIS (1 << 7) +#define I2SR_IIF_CLEAR (1 << 1) +#else +#define I2CR_IEN (1 << 7) +#define I2CR_IDIS (0 << 7) +#define I2SR_IIF_CLEAR (0 << 1) +#endif + #if defined(CONFIG_HARD_I2C) && !defined(CONFIG_SYS_I2C_BASE) #error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver" #endif +#ifdef I2C_QUIRK_REG +static u16 i2c_clk_div[60][2] = { + { 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 }, + { 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 }, + { 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D }, + { 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 }, + { 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 }, + { 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 }, + { 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 }, + { 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 }, + { 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 }, + { 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B }, + { 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 }, + { 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 }, + { 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B }, + { 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A }, + { 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E }, +}; +#else static u16 i2c_clk_div[50][2] = { { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, @@ -78,6 +116,7 @@ static u16 i2c_clk_div[50][2] = { { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, { 3072, 0x1E }, { 3840, 0x1F } }; +#endif /* * Calculate and set proper clock divider @@ -125,7 +164,7 @@ static int bus_i2c_set_bus_speed(void *base, int speed) writeb(idx, &i2c_regs->ifdr); /* Reset module */ - writeb(0, &i2c_regs->i2cr); + writeb(I2CR_IDIS, &i2c_regs->i2cr); writeb(0, &i2c_regs->i2sr); return 0; } @@ -157,7 +196,11 @@ static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state) for (;;) { sr = readb(&i2c_regs->i2sr); if (sr & I2SR_IAL) { +#ifdef I2C_QUIRK_REG + writeb(sr | I2SR_IAL, &i2c_regs->i2sr); +#else writeb(sr & ~I2SR_IAL, &i2c_regs->i2sr); +#endif printf("%s: Arbitration lost sr=%x cr=%x state=%x\n", __func__, sr, readb(&i2c_regs->i2cr), state); return -ERESTART; @@ -178,7 +221,7 @@ static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte) { int ret; - writeb(0, &i2c_regs->i2sr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); writeb(byte, &i2c_regs->i2dr); ret = wait_for_sr_state(i2c_regs, ST_IIF); if (ret < 0) @@ -214,14 +257,18 @@ static int i2c_init_transfer_(struct mxc_i2c_regs *i2c_regs, int ret; /* Enable I2C controller */ +#ifdef I2C_QUIRK_REG + if (readb(&i2c_regs->i2cr) & I2CR_IDIS) { +#else if (!(readb(&i2c_regs->i2cr) & I2CR_IEN)) { +#endif writeb(I2CR_IEN, &i2c_regs->i2cr); /* Wait for controller to be stable */ udelay(50); } if (readb(&i2c_regs->iadr) == (chip << 1)) writeb((chip << 1) ^ 2, &i2c_regs->iadr); - writeb(0, &i2c_regs->i2sr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE); if (ret < 0) return ret; @@ -269,7 +316,8 @@ static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs, printf("%s: failed for chip 0x%x retry=%d\n", __func__, chip, retry); if (ret != -ERESTART) - writeb(0, &i2c_regs->i2cr); /* Disable controller */ + /* Disable controller */ + writeb(I2CR_IDIS, &i2c_regs->i2cr); udelay(100); if (i2c_idle_bus(i2c_regs) < 0) break; @@ -309,7 +357,7 @@ int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf, if (len == 1) temp |= I2CR_TX_NO_AK; writeb(temp, &i2c_regs->i2cr); - writeb(0, &i2c_regs->i2sr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); readb(&i2c_regs->i2dr); /* dummy read to clear ICF */ /* read data */ @@ -331,7 +379,7 @@ int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf, temp |= I2CR_TX_NO_AK; writeb(temp, &i2c_regs->i2cr); } - writeb(0, &i2c_regs->i2sr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); buf[i] = readb(&i2c_regs->i2dr); } i2c_imx_stop(i2c_regs); -- cgit v1.2.3 From 3cff842bca8aa78fc49436711873466db9be21f8 Mon Sep 17 00:00:00 2001 From: Kuo-Jung Su Date: Wed, 8 May 2013 15:36:26 +0800 Subject: i2c: add Faraday FTI2C010 I2C controller support Faraday FTI2C010 is a multi-function I2C controller which supports both master and slave mode. This patch simplily implements the master mode only. Signed-off-by: Kuo-Jung Su CC: Heiko Schocher --- drivers/i2c/Makefile | 1 + drivers/i2c/fti2c010.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/fti2c010.h | 81 +++++++++++ 3 files changed, 451 insertions(+) create mode 100644 drivers/i2c/fti2c010.c create mode 100644 drivers/i2c/fti2c010.h (limited to 'drivers/i2c') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 360f93edf7d..8f484f76725 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -44,6 +44,7 @@ COBJS-$(CONFIG_SH_I2C) += sh_i2c.o COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o COBJS-$(CONFIG_SYS_I2C) += i2c_core.o COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o +COBJS-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o COBJS-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o COBJS-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o diff --git a/drivers/i2c/fti2c010.c b/drivers/i2c/fti2c010.c new file mode 100644 index 00000000000..24c4bb53fa1 --- /dev/null +++ b/drivers/i2c/fti2c010.c @@ -0,0 +1,369 @@ +/* + * Faraday I2C Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include +#include +#include + +#include "fti2c010.h" + +#ifndef CONFIG_HARD_I2C +#error "fti2c010: CONFIG_HARD_I2C is not defined" +#endif + +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +#ifndef CONFIG_FTI2C010_FREQ +#define CONFIG_FTI2C010_FREQ clk_get_rate("I2C") +#endif + +/* command timeout */ +#define CFG_CMD_TIMEOUT 10 /* ms */ + +/* 7-bit chip address + 1-bit read/write */ +#define I2C_RD(chip) ((((chip) << 1) & 0xff) | 1) +#define I2C_WR(chip) (((chip) << 1) & 0xff) + +struct fti2c010_chip { + void __iomem *regs; + uint bus; + uint speed; +}; + +static struct fti2c010_chip chip_list[] = { + { + .bus = 0, + .regs = (void __iomem *)CONFIG_FTI2C010_BASE, + }, +#ifdef CONFIG_I2C_MULTI_BUS +# ifdef CONFIG_FTI2C010_BASE1 + { + .bus = 1, + .regs = (void __iomem *)CONFIG_FTI2C010_BASE1, + }, +# endif +# ifdef CONFIG_FTI2C010_BASE2 + { + .bus = 2, + .regs = (void __iomem *)CONFIG_FTI2C010_BASE2, + }, +# endif +# ifdef CONFIG_FTI2C010_BASE3 + { + .bus = 3, + .regs = (void __iomem *)CONFIG_FTI2C010_BASE3, + }, +# endif +#endif /* #ifdef CONFIG_I2C_MULTI_BUS */ +}; + +static struct fti2c010_chip *curr = chip_list; + +static int fti2c010_wait(uint32_t mask) +{ + int ret = -1; + uint32_t stat, ts; + struct fti2c010_regs *regs = curr->regs; + + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + stat = readl(®s->sr); + if ((stat & mask) == mask) { + ret = 0; + break; + } + } + + return ret; +} + +/* + * u-boot I2C API + */ + +/* + * Initialization, must be called once on start up, may be called + * repeatedly to change the speed and slave addresses. + */ +void i2c_init(int speed, int slaveaddr) +{ + if (speed || !curr->speed) + i2c_set_bus_speed(speed); + + /* if slave mode disabled */ + if (!slaveaddr) + return; + + /* + * TODO: + * Implement slave mode, but is it really necessary? + */ +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uchar chip) +{ + int ret; + struct fti2c010_regs *regs = curr->regs; + + i2c_init(0, 0); + + /* 1. Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_WR(chip), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + return ret; + + /* 2. Select device register */ + writel(0, ®s->dr); + writel(CR_ENABLE | CR_TBEN, ®s->cr); + ret = fti2c010_wait(SR_DT); + + return ret; +} + +/* + * 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(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + int ret, pos; + uchar paddr[4]; + struct fti2c010_regs *regs = curr->regs; + + i2c_init(0, 0); + + paddr[0] = (addr >> 0) & 0xFF; + paddr[1] = (addr >> 8) & 0xFF; + paddr[2] = (addr >> 16) & 0xFF; + paddr[3] = (addr >> 24) & 0xFF; + + /* + * Phase A. Set register address + */ + + /* A.1 Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_WR(chip), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + return ret; + + /* A.2 Select device register */ + for (pos = 0; pos < alen; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + writel(paddr[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + return ret; + } + + /* + * Phase B. Get register data + */ + + /* B.1 Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_RD(chip), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + return ret; + + /* B.2 Get register data */ + for (pos = 0; pos < len; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + uint32_t stat = SR_DR; + + if (pos == len - 1) { + ctrl |= CR_NAK | CR_STOP; + stat |= SR_ACK; + } + writel(ctrl, ®s->cr); + ret = fti2c010_wait(stat); + if (ret) + break; + buf[pos] = (uchar)(readl(®s->dr) & 0xFF); + } + + return ret; +} + +/* + * 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_write(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + int ret, pos; + uchar paddr[4]; + struct fti2c010_regs *regs = curr->regs; + + i2c_init(0, 0); + + paddr[0] = (addr >> 0) & 0xFF; + paddr[1] = (addr >> 8) & 0xFF; + paddr[2] = (addr >> 16) & 0xFF; + paddr[3] = (addr >> 24) & 0xFF; + + /* + * Phase A. Set register address + * + * A.1 Select slave device (7bits Address + 1bit R/W) + */ + writel(I2C_WR(chip), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + return ret; + + /* A.2 Select device register */ + for (pos = 0; pos < alen; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + writel(paddr[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + return ret; + } + + /* + * Phase B. Set register data + */ + for (pos = 0; pos < len; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + if (pos == len - 1) + ctrl |= CR_STOP; + writel(buf[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(SR_DT); + if (ret) + break; + } + + return ret; +} + +/* + * Functions for setting the current I2C bus and its speed + */ +#ifdef CONFIG_I2C_MULTI_BUS + +/* + * 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(uint bus) +{ + if (bus >= ARRAY_SIZE(chip_list)) + return -1; + curr = chip_list + bus; + i2c_init(0, 0); + return 0; +} + +/* + * i2c_get_bus_num: + * + * Returns index of currently active I2C bus. Zero-based. + */ + +uint i2c_get_bus_num(void) +{ + return curr->bus; +} + +#endif /* #ifdef CONFIG_I2C_MULTI_BUS */ + +/* + * i2c_set_bus_speed: + * + * Change the speed of the active I2C bus + * + * speed - bus speed in Hz + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_set_bus_speed(uint speed) +{ + struct fti2c010_regs *regs = curr->regs; + uint clk = CONFIG_FTI2C010_FREQ; + uint gsr = 0, tsr = 32; + uint spd, div; + + if (!speed) + speed = CONFIG_SYS_I2C_SPEED; + + for (div = 0; div < 0x3ffff; ++div) { + /* SCLout = PCLK/(2*(COUNT + 2) + GSR) */ + spd = clk / (2 * (div + 2) + gsr); + if (spd <= speed) + break; + } + + if (curr->speed == spd) + return 0; + + writel(CR_I2CRST, ®s->cr); + mdelay(100); + if (readl(®s->cr) & CR_I2CRST) { + printf("fti2c010: reset timeout\n"); + return -1; + } + + curr->speed = spd; + + writel(TGSR_GSR(gsr) | TGSR_TSR(tsr), ®s->tgsr); + writel(CDR_DIV(div), ®s->cdr); + + return 0; +} + +/* + * i2c_get_bus_speed: + * + * Returns speed of currently active I2C bus in Hz + */ + +uint i2c_get_bus_speed(void) +{ + return curr->speed; +} diff --git a/drivers/i2c/fti2c010.h b/drivers/i2c/fti2c010.h new file mode 100644 index 00000000000..18aec2c976f --- /dev/null +++ b/drivers/i2c/fti2c010.h @@ -0,0 +1,81 @@ +/* + * Faraday I2C Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#ifndef __FTI2C010_H +#define __FTI2C010_H + +/* + * FTI2C010 registers + */ +struct fti2c010_regs { + uint32_t cr; /* 0x00: control register */ + uint32_t sr; /* 0x04: status register */ + uint32_t cdr; /* 0x08: clock division register */ + uint32_t dr; /* 0x0c: data register */ + uint32_t sar; /* 0x10: slave address register */ + uint32_t tgsr;/* 0x14: time & glitch suppression register */ + uint32_t bmr; /* 0x18: bus monitor register */ + uint32_t rsvd[5]; + uint32_t revr;/* 0x30: revision register */ +}; + +/* + * control register + */ +#define CR_ALIRQ 0x2000 /* arbitration lost interrupt (master) */ +#define CR_SAMIRQ 0x1000 /* slave address match interrupt (slave) */ +#define CR_STOPIRQ 0x800 /* stop condition interrupt (slave) */ +#define CR_NAKRIRQ 0x400 /* NACK response interrupt (master) */ +#define CR_DRIRQ 0x200 /* rx interrupt (both) */ +#define CR_DTIRQ 0x100 /* tx interrupt (both) */ +#define CR_TBEN 0x80 /* tx enable (both) */ +#define CR_NAK 0x40 /* NACK (both) */ +#define CR_STOP 0x20 /* stop (master) */ +#define CR_START 0x10 /* start (master) */ +#define CR_GCEN 0x8 /* general call support (slave) */ +#define CR_SCLEN 0x4 /* enable clock out (master) */ +#define CR_I2CEN 0x2 /* enable I2C (both) */ +#define CR_I2CRST 0x1 /* reset I2C (both) */ +#define CR_ENABLE \ + (CR_ALIRQ | CR_NAKRIRQ | CR_DRIRQ | CR_DTIRQ | CR_SCLEN | CR_I2CEN) + +/* + * status register + */ +#define SR_CLRAL 0x400 /* clear arbitration lost */ +#define SR_CLRGC 0x200 /* clear general call */ +#define SR_CLRSAM 0x100 /* clear slave address match */ +#define SR_CLRSTOP 0x80 /* clear stop */ +#define SR_CLRNAKR 0x40 /* clear NACK respond */ +#define SR_DR 0x20 /* rx ready */ +#define SR_DT 0x10 /* tx done */ +#define SR_BB 0x8 /* bus busy */ +#define SR_BUSY 0x4 /* chip busy */ +#define SR_ACK 0x2 /* ACK/NACK received */ +#define SR_RW 0x1 /* set when master-rx or slave-tx mode */ + +/* + * clock division register + */ +#define CDR_DIV(n) ((n) & 0x3ffff) + +/* + * time & glitch suppression register + */ +#define TGSR_GSR(n) (((n) & 0x7) << 10) +#define TGSR_TSR(n) ((n) & 0x3ff) + +/* + * bus monitor register + */ +#define BMR_SCL 0x2 /* SCL is pull-up */ +#define BMR_SDA 0x1 /* SDA is pull-up */ + +#endif /* __FTI2C010_H */ -- cgit v1.2.3