diff mbox

[U-Boot,1/3] i2c: add i2c_core and prepare for new multibus support

Message ID 1326784345-19953-2-git-send-email-sjg@chromium.org
State Superseded, archived
Delegated to: Tom Warren
Headers show

Commit Message

Simon Glass Jan. 17, 2012, 7:12 a.m. UTC
From: Heiko Schocher <hs@denx.de>

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 <hs@denx.de>
Signed-off-by: Simon Glass <sjg@chromium.org>
---
 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

Comments

Mike Frysinger Jan. 17, 2012, 7:23 p.m. UTC | #1
On Tuesday 17 January 2012 02:12:23 Simon Glass wrote:
> +#if defined(CONFIG_SYS_I2C)
> +	void		*cur_adap;	/* current used i2c adapter */
> +#endif

let's have "i2c" in the variable name somewhere.  "curr_i2c" ?

> --- /dev/null
> +++ b/drivers/i2c/i2c_core.c
>
> +#ifdef CONFIG_TEGRA2_I2C
> +extern struct i2c_adapter tegra_i2c_adap[];
> +#endif

i feel like if your initial run includes i2c bus specific defines, the end 
result is a failure

> +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

the nand layer is moving away from this style ...

> + * 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?)

does part-specific stuff belong in the core ?  seems like it should be in a 
part-specific driver ...

> +int i2c_probe(u_int8_t chip)

use uintx_t types.  the u_intx_t types are deprecated.
-mike
Tabi Timur-B04825 Jan. 18, 2012, 8:11 p.m. UTC | #2
On Tue, Jan 17, 2012 at 1:12 AM, Simon Glass <sjg@chromium.org> wrote:
> From: Heiko Schocher <hs@denx.de>
>
> 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 <hs@denx.de>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>  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

I was really hoping we could get rid of the concept of a "current" i2c
adapter, and just force all drivers to specify the I2C adapter they
want to use for a given I2C operation.  That's how Linux operates, and
it will prevent stuff like this:

void *old;
void *new;


old = get_current_i2c_adapter();
set_i2c_adapter(new);
// do I2C stuff
set_i2c_adapter(old);
Mike Frysinger Jan. 18, 2012, 8:41 p.m. UTC | #3
On Wednesday 18 January 2012 15:11:56 Tabi Timur-B04825 wrote:
> On Tue, Jan 17, 2012 at 1:12 AM, Simon Glass wrote:
> > --- 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
> 
> I was really hoping we could get rid of the concept of a "current" i2c
> adapter, and just force all drivers to specify the I2C adapter they
> want to use for a given I2C operation.  That's how Linux operates, and
> it will prevent stuff like this:
> 
> void *old;
> void *new;
> 
> old = get_current_i2c_adapter();
> set_i2c_adapter(new);
> // do I2C stuff
> set_i2c_adapter(old);

that's only needed if you expect the pointer to stay valid across calls.  i 
don't think it does for most (all?) drivers.
-mike
Timur Tabi Jan. 18, 2012, 8:43 p.m. UTC | #4
Mike Frysinger wrote:
> that's only needed if you expect the pointer to stay valid across calls.  i 
> don't think it does for most (all?) drivers.

True, but it's hard to know sometimes when it's needed.  I do it in my
code just to be sure.

Regardless, I still think the idea of a "current" i2c bus is flawed.
Simon Glass Jan. 18, 2012, 9:37 p.m. UTC | #5
Hi Tabi,

On Wed, Jan 18, 2012 at 12:11 PM, Tabi Timur-B04825
<B04825@freescale.com> wrote:
> On Tue, Jan 17, 2012 at 1:12 AM, Simon Glass <sjg@chromium.org> wrote:
>> From: Heiko Schocher <hs@denx.de>
>>
>> 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 <hs@denx.de>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>  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
>
> I was really hoping we could get rid of the concept of a "current" i2c
> adapter, and just force all drivers to specify the I2C adapter they
> want to use for a given I2C operation.  That's how Linux operates, and
> it will prevent stuff like this:

I agree completely, it was one of the things I was going to ask for.
We should add a new parameter to calls instead IMO.

>
> void *old;
> void *new;
>
>
> old = get_current_i2c_adapter();
> set_i2c_adapter(new);
> // do I2C stuff
> set_i2c_adapter(old);
>
> --
> Timur Tabi
> Linux kernel developer at Freescale

Regards,
Simon
Timur Tabi Jan. 18, 2012, 9:39 p.m. UTC | #6
Simon Glass wrote:
> I agree completely, it was one of the things I was going to ask for.
> We should add a new parameter to calls instead IMO.

I would be in favor of a patch that replaces all of the I2C calls.  It
would be a massive patch, but it solve a lot of problems in one shot.
Simon Glass Jan. 18, 2012, 10:21 p.m. UTC | #7
Hi Timur,

On Wed, Jan 18, 2012 at 1:39 PM, Timur Tabi <timur@freescale.com> wrote:
> Simon Glass wrote:
>> I agree completely, it was one of the things I was going to ask for.
>> We should add a new parameter to calls instead IMO.
>
> I would be in favor of a patch that replaces all of the I2C calls.  It
> would be a massive patch, but it solve a lot of problems in one shot.

I agree. Do you know of such a patch? :-)

Regards,
Simon

>
> --
> Timur Tabi
> Linux kernel developer at Freescale
>
Timur Tabi Jan. 18, 2012, 10:24 p.m. UTC | #8
Simon Glass wrote:
> I agree. Do you know of such a patch? :-)

No, but it wouldn't be heard to create -- mostly a global
search-and-replace.  I wouldn't even attempt it without getting
pre-approved by Wolfgang first, though.
Wolfgang Denk Jan. 19, 2012, 5:36 a.m. UTC | #9
Dear Simon Glass,

In message <CAPnjgZ2kLPfMzzwgHkDJ4eL+wixqLv89+CvdVP7PCcy+XFaqNQ@mail.gmail.com> you wrote:
> 
> > I was really hoping we could get rid of the concept of a "current" i2c
> > adapter, and just force all drivers to specify the I2C adapter they
> > want to use for a given I2C operation. =A0That's how Linux operates, and
> > it will prevent stuff like this:
> 
> I agree completely, it was one of the things I was going to ask for.
> We should add a new parameter to calls instead IMO.

Let's do one step at a time.  Now we go for multi-bus support.
Implementing a new, better device interface is one of the next steps,
then.

Best regards,

Wolfgang Denk
Heiko Schocher Jan. 19, 2012, 6:35 a.m. UTC | #10
Hello Wolfgang, Timur, Simon,

Wolfgang Denk wrote:
> Dear Simon Glass,
> 
> In message <CAPnjgZ2kLPfMzzwgHkDJ4eL+wixqLv89+CvdVP7PCcy+XFaqNQ@mail.gmail.com> you wrote:
>>> I was really hoping we could get rid of the concept of a "current" i2c
>>> adapter, and just force all drivers to specify the I2C adapter they
>>> want to use for a given I2C operation. =A0That's how Linux operates, and
>>> it will prevent stuff like this:
>> I agree completely, it was one of the things I was going to ask for.
>> We should add a new parameter to calls instead IMO.
> 
> Let's do one step at a time.  Now we go for multi-bus support.

Ok.

> Implementing a new, better device interface is one of the next steps,
> then.

Some thoughts to the subject "get rid of concept of a current i2c"

- First, it would be great to get rid of that ;-)

- 2 reasons why we currently need the info, what is the current
  i2c adapter/i2c bus:

- U-Boot principle says, don't init a device, if you don't use it.
  So, if we switch to another i2c adapter or i2c bus (A i2c bus is a
  combination if one i2c adpater and n i2c muxes), we must deinit
  the current adapter/bus. Ok, current code didn't this too, but
  this should a goal to keep in mind ... or we define, that this
  is not needed.

- If we have i2c muxes, and you don't know your current i2c bus,
  you must on every i2c access also setup the i2c muxes on this
  i2c bus ... would we do this really?
  if we have the current i2c bus info, we just have to check, if
  we are on this bus, and do the i2c access ... if we are not on
  this i2c bus, we can deinit it complete (the new i2c_core disconnects
  for example all i2c muxes on the i2c bus) and init the new bus.
  All this work is done in a central function i2c_set_bus_numer()

- Looking in the new multibus i2c_core.c file, we should get rid of

  static unsigned int i2c_cur_bus __attribute__((section(".data"))) =
                                CONFIG_SYS_SPD_BUS_NUM;

  and "cur_adap" renamed to "cur_i2c_bus" and should be a
  "struct i2c_bus_hose" pointer.

bye,
Heiko
Simon Glass Jan. 19, 2012, 6:53 a.m. UTC | #11
Hi Heiko,

On Wed, Jan 18, 2012 at 10:35 PM, Heiko Schocher <hs@denx.de> wrote:
> Hello Wolfgang, Timur, Simon,
>
> Wolfgang Denk wrote:
>> Dear Simon Glass,
>>
>> In message <CAPnjgZ2kLPfMzzwgHkDJ4eL+wixqLv89+CvdVP7PCcy+XFaqNQ@mail.gmail.com> you wrote:
>>>> I was really hoping we could get rid of the concept of a "current" i2c
>>>> adapter, and just force all drivers to specify the I2C adapter they
>>>> want to use for a given I2C operation. =A0That's how Linux operates, and
>>>> it will prevent stuff like this:
>>> I agree completely, it was one of the things I was going to ask for.
>>> We should add a new parameter to calls instead IMO.
>>
>> Let's do one step at a time.  Now we go for multi-bus support.
>
> Ok.
>
>> Implementing a new, better device interface is one of the next steps,
>> then.
>
> Some thoughts to the subject "get rid of concept of a current i2c"
>
> - First, it would be great to get rid of that ;-)
>
> - 2 reasons why we currently need the info, what is the current
>  i2c adapter/i2c bus:
>
> - U-Boot principle says, don't init a device, if you don't use it.
>  So, if we switch to another i2c adapter or i2c bus (A i2c bus is a
>  combination if one i2c adpater and n i2c muxes), we must deinit
>  the current adapter/bus. Ok, current code didn't this too, but
>  this should a goal to keep in mind ... or we define, that this
>  is not needed.
>
> - If we have i2c muxes, and you don't know your current i2c bus,
>  you must on every i2c access also setup the i2c muxes on this
>  i2c bus ... would we do this really?

Ignoring muxes, we can have more than one i2c adaptor inited at a time
(i.e. de-initing is optional).

Perhaps reword this slightly. U-Boot can have knowledge of a current
adaptor, mux settings and so on, and use this internally within the
i2c layer to optimise performance and redundant i2c traffic. But the
pain is when the concept of a 'current adaptor' is exposed outside the
i2c layer.

>  if we have the current i2c bus info, we just have to check, if
>  we are on this bus, and do the i2c access ... if we are not on
>  this i2c bus, we can deinit it complete (the new i2c_core disconnects
>  for example all i2c muxes on the i2c bus) and init the new bus.
>  All this work is done in a central function i2c_set_bus_numer()

Yes but this function could become internal perhaps.

>
> - Looking in the new multibus i2c_core.c file, we should get rid of
>
>  static unsigned int i2c_cur_bus __attribute__((section(".data"))) =
>                                CONFIG_SYS_SPD_BUS_NUM;
>
>  and "cur_adap" renamed to "cur_i2c_bus" and should be a
>  "struct i2c_bus_hose" pointer.

Sounds good. Heiko do you have time to take over your two patches I
sent and tidy them up, or should I continue?

Regards,
Simon

>
> bye,
> Heiko
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Heiko Schocher Jan. 19, 2012, 7:53 a.m. UTC | #12
Hello Simon,

Simon Glass wrote:
> Hi Heiko,
> 
> On Wed, Jan 18, 2012 at 10:35 PM, Heiko Schocher <hs@denx.de> wrote:
>> Hello Wolfgang, Timur, Simon,
>>
>> Wolfgang Denk wrote:
>>> Dear Simon Glass,
>>>
>>> In message <CAPnjgZ2kLPfMzzwgHkDJ4eL+wixqLv89+CvdVP7PCcy+XFaqNQ@mail.gmail.com> you wrote:
>>>>> I was really hoping we could get rid of the concept of a "current" i2c
>>>>> adapter, and just force all drivers to specify the I2C adapter they
>>>>> want to use for a given I2C operation. =A0That's how Linux operates, and
>>>>> it will prevent stuff like this:
>>>> I agree completely, it was one of the things I was going to ask for.
>>>> We should add a new parameter to calls instead IMO.
>>> Let's do one step at a time.  Now we go for multi-bus support.
>> Ok.
>>
>>> Implementing a new, better device interface is one of the next steps,
>>> then.
>> Some thoughts to the subject "get rid of concept of a current i2c"
>>
>> - First, it would be great to get rid of that ;-)
>>
>> - 2 reasons why we currently need the info, what is the current
>>  i2c adapter/i2c bus:
>>
>> - U-Boot principle says, don't init a device, if you don't use it.
>>  So, if we switch to another i2c adapter or i2c bus (A i2c bus is a
>>  combination if one i2c adpater and n i2c muxes), we must deinit
>>  the current adapter/bus. Ok, current code didn't this too, but
>>  this should a goal to keep in mind ... or we define, that this
>>  is not needed.
>>
>> - If we have i2c muxes, and you don't know your current i2c bus,
>>  you must on every i2c access also setup the i2c muxes on this
>>  i2c bus ... would we do this really?
> 
> Ignoring muxes, we can have more than one i2c adaptor inited at a time
> (i.e. de-initing is optional).

Ok for the point "more adapters inited at one time".

But we have to setup the muxes every time! We cannot ignore this.

> Perhaps reword this slightly. U-Boot can have knowledge of a current
> adaptor, mux settings and so on, and use this internally within the
> i2c layer to optimise performance and redundant i2c traffic. But the

Ok, thats what I mean. The "cur_i2c_bus" pointer should only be used
in the i2c subsystem! This pointer is in gd_t because it must be
writeable, also when running from flash ...

> pain is when the concept of a 'current adaptor' is exposed outside the
> i2c layer.

The "cur_i2c_bus" pointer is only used inside the i2c subsystem.

>>  if we have the current i2c bus info, we just have to check, if
>>  we are on this bus, and do the i2c access ... if we are not on
>>  this i2c bus, we can deinit it complete (the new i2c_core disconnects
>>  for example all i2c muxes on the i2c bus) and init the new bus.
>>  All this work is done in a central function i2c_set_bus_numer()
> 
> Yes but this function could become internal perhaps.

Yes, if we make the change, Timur suggested ... but that can be done
easy in a second step, as Wolfgang mentioned.

>> - Looking in the new multibus i2c_core.c file, we should get rid of
>>
>>  static unsigned int i2c_cur_bus __attribute__((section(".data"))) =
>>                                CONFIG_SYS_SPD_BUS_NUM;
>>
>>  and "cur_adap" renamed to "cur_i2c_bus" and should be a
>>  "struct i2c_bus_hose" pointer.
> 
> Sounds good. Heiko do you have time to take over your two patches I
> sent and tidy them up, or should I continue?

I did some work on this 2 patches, to get it working with soft_i2c.c
driver ... I try to rework this suggestions in, and send it to the
ML as a v2 ... if I do not find time, I speak up ;-)

bye,
Heiko
Wolfgang Denk Jan. 19, 2012, 11:20 a.m. UTC | #13
Dear Simon Glass,

In message <CAPnjgZ1RoyguaAScOQiw8PoQkzVp5rim7KJ6zzHLNPwuBYK48Q@mail.gmail.com> you wrote:
> 
> Perhaps reword this slightly. U-Boot can have knowledge of a current
> adaptor, mux settings and so on, and use this internally within the
> i2c layer to optimise performance and redundant i2c traffic. But the
> pain is when the concept of a 'current adaptor' is exposed outside the
> i2c layer.

As mentioned before, this is what we currently have as "device model"
in U-Boot - not only I2C: we have the same "current device" concept
with IDE, USB, ...

It makes no sense trying to change it here; it would only cause even
more incompatibilities.  If you want to fix that, then help working on
the new driver model.  Marek is planning for this.

Best regards,

Wolfgang Denk
Simon Glass Jan. 19, 2012, 6:07 p.m. UTC | #14
Hi Heiko,

On Wed, Jan 18, 2012 at 11:53 PM, Heiko Schocher <hs@denx.de> wrote:
> Hello Simon,
>
> Simon Glass wrote:
>> Hi Heiko,
>>
>> On Wed, Jan 18, 2012 at 10:35 PM, Heiko Schocher <hs@denx.de> wrote:
>>> Hello Wolfgang, Timur, Simon,
>>>
>>> Wolfgang Denk wrote:
>>>> Dear Simon Glass,
>>>>
>>>> In message <CAPnjgZ2kLPfMzzwgHkDJ4eL+wixqLv89+CvdVP7PCcy+XFaqNQ@mail.gmail.com> you wrote:
>>>>>> I was really hoping we could get rid of the concept of a "current" i2c
>>>>>> adapter, and just force all drivers to specify the I2C adapter they
>>>>>> want to use for a given I2C operation. =A0That's how Linux operates, and
>>>>>> it will prevent stuff like this:
>>>>> I agree completely, it was one of the things I was going to ask for.
>>>>> We should add a new parameter to calls instead IMO.
>>>> Let's do one step at a time.  Now we go for multi-bus support.
>>> Ok.
>>>
>>>> Implementing a new, better device interface is one of the next steps,
>>>> then.
>>> Some thoughts to the subject "get rid of concept of a current i2c"
>>>
>>> - First, it would be great to get rid of that ;-)
>>>
>>> - 2 reasons why we currently need the info, what is the current
>>>  i2c adapter/i2c bus:
>>>
>>> - U-Boot principle says, don't init a device, if you don't use it.
>>>  So, if we switch to another i2c adapter or i2c bus (A i2c bus is a
>>>  combination if one i2c adpater and n i2c muxes), we must deinit
>>>  the current adapter/bus. Ok, current code didn't this too, but
>>>  this should a goal to keep in mind ... or we define, that this
>>>  is not needed.
>>>
>>> - If we have i2c muxes, and you don't know your current i2c bus,
>>>  you must on every i2c access also setup the i2c muxes on this
>>>  i2c bus ... would we do this really?
>>
>> Ignoring muxes, we can have more than one i2c adaptor inited at a time
>> (i.e. de-initing is optional).
>
> Ok for the point "more adapters inited at one time".
>
> But we have to setup the muxes every time! We cannot ignore this.
>
>> Perhaps reword this slightly. U-Boot can have knowledge of a current
>> adaptor, mux settings and so on, and use this internally within the
>> i2c layer to optimise performance and redundant i2c traffic. But the
>
> Ok, thats what I mean. The "cur_i2c_bus" pointer should only be used
> in the i2c subsystem! This pointer is in gd_t because it must be
> writeable, also when running from flash ...

Yes that's fine.

>
>> pain is when the concept of a 'current adaptor' is exposed outside the
>> i2c layer.
>
> The "cur_i2c_bus" pointer is only used inside the i2c subsystem.

OK good.

>
>>>  if we have the current i2c bus info, we just have to check, if
>>>  we are on this bus, and do the i2c access ... if we are not on
>>>  this i2c bus, we can deinit it complete (the new i2c_core disconnects
>>>  for example all i2c muxes on the i2c bus) and init the new bus.
>>>  All this work is done in a central function i2c_set_bus_numer()
>>
>> Yes but this function could become internal perhaps.
>
> Yes, if we make the change, Timur suggested ... but that can be done
> easy in a second step, as Wolfgang mentioned.

Yes indeed, and quite a major one by the sound of it.

>
>>> - Looking in the new multibus i2c_core.c file, we should get rid of
>>>
>>>  static unsigned int i2c_cur_bus __attribute__((section(".data"))) =
>>>                                CONFIG_SYS_SPD_BUS_NUM;
>>>
>>>  and "cur_adap" renamed to "cur_i2c_bus" and should be a
>>>  "struct i2c_bus_hose" pointer.
>>
>> Sounds good. Heiko do you have time to take over your two patches I
>> sent and tidy them up, or should I continue?
>
> I did some work on this 2 patches, to get it working with soft_i2c.c
> driver ... I try to rework this suggestions in, and send it to the
> ML as a v2 ... if I do not find time, I speak up ;-)

Sounds good.

Regards,
Simon

>
> bye,
> Heiko
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Simon Glass Jan. 19, 2012, 6:10 p.m. UTC | #15
Hi Wolfgang,

On Thu, Jan 19, 2012 at 3:20 AM, Wolfgang Denk <wd@denx.de> wrote:
> Dear Simon Glass,
>
> In message <CAPnjgZ1RoyguaAScOQiw8PoQkzVp5rim7KJ6zzHLNPwuBYK48Q@mail.gmail.com> you wrote:
>>
>> Perhaps reword this slightly. U-Boot can have knowledge of a current
>> adaptor, mux settings and so on, and use this internally within the
>> i2c layer to optimise performance and redundant i2c traffic. But the
>> pain is when the concept of a 'current adaptor' is exposed outside the
>> i2c layer.
>
> As mentioned before, this is what we currently have as "device model"
> in U-Boot - not only I2C: we have the same "current device" concept
> with IDE, USB, ...
>
> It makes no sense trying to change it here; it would only cause even
> more incompatibilities.  If you want to fix that, then help working on
> the new driver model.  Marek is planning for this.

Yes of course, sorry for not replying earlier, but I completely agree.
Heiko's patch here fits with the "current device" model. I was talking
about the way I hope it will be in the future. I can help with new
driver model also but I would like to get the rationalisation of code
duplication (relocation, board init) to bed first.

Regards,
Simon

>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> Good morning. This is the telephone company. Due  to  repairs,  we're
> giving  you  advance notice that your service will be cut off indefi-
> nitely at ten o'clock. That's two minutes from now.
Timur Tabi Jan. 19, 2012, 6:47 p.m. UTC | #16
Wolfgang Denk wrote:
> As mentioned before, this is what we currently have as "device model"
> in U-Boot - not only I2C: we have the same "current device" concept
> with IDE, USB, ...

The difference is that I2C operations are typically done internally by
other code, whereas IDE, USB, etc are done by the user on the command
line.  It's not unusual for boot code to access multiple I2C devices on
different buses, so we're switching I2C buses a lot.  People generally
don't try to access two networks or two USB devices back-to-back, but
that's exactly what we do with I2C.

The other problem is that I2C operations are necessary prior to
relocation, but IDE, USB, etc generally are not.  That's why we have this:

static unsigned int i2c_bus_num __attribute__ ((section (".data"))) =
CONFIG_SYS_SPD_BUS_NUM;

We need to initialize i2c_bus_num to the I2C bus that SPD is on, because
i2c_bus_num is not writable until after relocation, and DDR initialization
requires I2C.

A board that has SPD on two different I2C buses could not be supported by
U-Boot today.
Heiko Schocher Jan. 20, 2012, 6:50 a.m. UTC | #17
Hello Timur,

Timur Tabi wrote:
> Wolfgang Denk wrote:
>> As mentioned before, this is what we currently have as "device model"
>> in U-Boot - not only I2C: we have the same "current device" concept
>> with IDE, USB, ...
> 
> The difference is that I2C operations are typically done internally by
> other code, whereas IDE, USB, etc are done by the user on the command
> line.  It's not unusual for boot code to access multiple I2C devices on
> different buses, so we're switching I2C buses a lot.  People generally
> don't try to access two networks or two USB devices back-to-back, but
> that's exactly what we do with I2C.
>
> The other problem is that I2C operations are necessary prior to
> relocation, but IDE, USB, etc generally are not.  That's why we have this:
> 
> static unsigned int i2c_bus_num __attribute__ ((section (".data"))) =
> CONFIG_SYS_SPD_BUS_NUM;

Yep, correct. For that case, I need the "trickery" with the "cur_i2c_adap"
pointer in gd_t in the "new" framework, as gd_t is writeable before
relocation ...

> We need to initialize i2c_bus_num to the I2C bus that SPD is on, because
> i2c_bus_num is not writable until after relocation, and DDR initialization
> requires I2C.
> 
> A board that has SPD on two different I2C buses could not be supported by
> U-Boot today.

... and so with the "new" framework, it should be possible to switch to
other i2c busses (incl. i2c muxes on that), before relocation.

bye,
Heiko
diff mbox

Patch

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 <ksi@koi8.net>
+ *
+ * Multibus/multiadapter I2C core functions (wrappers)
+ */
+#include <common.h>
+#include <i2c.h>
+
+#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 <ksi@koi8.net>
+ * Copyright (C) 2009 Heiko Schocher <hs@denx.de>
+ * 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));