diff mbox

[U-Boot,6/6] i2c: designware_i2c: Add support for PCI(e) based I2C cores (x86)

Message ID 1458287661-21745-6-git-send-email-sr@denx.de
State Superseded
Delegated to: Heiko Schocher
Headers show

Commit Message

Stefan Roese March 18, 2016, 7:54 a.m. UTC
This patch adds support for the PCI(e) based I2C cores. Which can be
found for example on the Intel Bay Trail SoC. It has 7 I2C controllers
implemented as PCI devices.

This patch also adds the fixed values for the timing registers for
BayTrail which are taken from the Linux designware I2C driver.

Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Simon Glass <sjg@chromium.org>
Cc: Bin Meng <bmeng.cn@gmail.com>
Cc: Marek Vasut <marex@denx.de>
Cc: Heiko Schocher <hs@denx.de>
---
 drivers/i2c/designware_i2c.c | 111 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 101 insertions(+), 10 deletions(-)

Comments

Bin Meng March 21, 2016, 8:54 a.m. UTC | #1
Hi Stefan,

On Fri, Mar 18, 2016 at 3:54 PM, Stefan Roese <sr@denx.de> wrote:
> This patch adds support for the PCI(e) based I2C cores. Which can be
> found for example on the Intel Bay Trail SoC. It has 7 I2C controllers
> implemented as PCI devices.
>
> This patch also adds the fixed values for the timing registers for
> BayTrail which are taken from the Linux designware I2C driver.
>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Bin Meng <bmeng.cn@gmail.com>
> Cc: Marek Vasut <marex@denx.de>
> Cc: Heiko Schocher <hs@denx.de>
> ---
>  drivers/i2c/designware_i2c.c | 111 +++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 101 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
> index 4e5340d..f7f2eba 100644
> --- a/drivers/i2c/designware_i2c.c
> +++ b/drivers/i2c/designware_i2c.c
> @@ -8,11 +8,32 @@
>  #include <common.h>
>  #include <dm.h>
>  #include <i2c.h>
> +#include <pci.h>
>  #include <asm/io.h>
>  #include "designware_i2c.h"
>
> +struct dw_scl_sda_cfg {
> +       u32 ss_hcnt;
> +       u32 fs_hcnt;
> +       u32 ss_lcnt;
> +       u32 fs_lcnt;
> +       u32 sda_hold;
> +};
> +
> +#ifdef CONFIG_X86
> +/* BayTrail HCNT/LCNT/SDA hold time */
> +static struct dw_scl_sda_cfg byt_config = {
> +       .ss_hcnt = 0x200,
> +       .fs_hcnt = 0x55,
> +       .ss_lcnt = 0x200,
> +       .fs_lcnt = 0x99,
> +       .sda_hold = 0x6,
> +};
> +#endif
> +
>  struct dw_i2c {
>         struct i2c_regs *regs;
> +       struct dw_scl_sda_cfg *scl_sda_cfg;
>  };
>
>  static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
> @@ -42,6 +63,7 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
>   * Set the i2c speed.
>   */
>  static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
> +                                          struct dw_scl_sda_cfg *scl_sda_cfg,
>                                            unsigned int speed)
>  {
>         unsigned int cntl;
> @@ -61,34 +83,55 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
>         cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK));
>
>         switch (i2c_spd) {
> +#ifndef CONFIG_X86 /* No High-speed for BayTrail yet */
>         case IC_SPEED_MODE_MAX:
> -               cntl |= IC_CON_SPD_HS;
> -               hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
> +               cntl |= IC_CON_SPD_SS;
> +               if (scl_sda_cfg) {
> +                       hcnt = scl_sda_cfg->fs_hcnt;
> +                       lcnt = scl_sda_cfg->fs_lcnt;
> +               } else {
> +                       hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
> +                       lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
> +               }
>                 writel(hcnt, &i2c_base->ic_hs_scl_hcnt);
> -               lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
>                 writel(lcnt, &i2c_base->ic_hs_scl_lcnt);
>                 break;
> +#endif
>
>         case IC_SPEED_MODE_STANDARD:
>                 cntl |= IC_CON_SPD_SS;
> -               hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
> +               if (scl_sda_cfg) {
> +                       hcnt = scl_sda_cfg->ss_hcnt;
> +                       lcnt = scl_sda_cfg->ss_lcnt;
> +               } else {
> +                       hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
> +                       lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
> +               }
>                 writel(hcnt, &i2c_base->ic_ss_scl_hcnt);
> -               lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
>                 writel(lcnt, &i2c_base->ic_ss_scl_lcnt);
>                 break;
>
>         case IC_SPEED_MODE_FAST:
>         default:
>                 cntl |= IC_CON_SPD_FS;
> -               hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
> +               if (scl_sda_cfg) {
> +                       hcnt = scl_sda_cfg->fs_hcnt;
> +                       lcnt = scl_sda_cfg->fs_lcnt;
> +               } else {
> +                       hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
> +                       lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
> +               }
>                 writel(hcnt, &i2c_base->ic_fs_scl_hcnt);
> -               lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
>                 writel(lcnt, &i2c_base->ic_fs_scl_lcnt);
>                 break;
>         }
>
>         writel(cntl, &i2c_base->ic_con);
>
> +       /* Configure SDA Hold Time if required */
> +       if (scl_sda_cfg)
> +               writel(scl_sda_cfg->sda_hold, &i2c_base->ic_sda_hold);
> +
>         /* Enable back i2c now speed set */
>         dw_i2c_enable(i2c_base, 1);
>
> @@ -315,7 +358,7 @@ static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
>         writel(IC_TX_TL, &i2c_base->ic_tx_tl);
>         writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
>  #ifndef CONFIG_DM_I2C
> -       __dw_i2c_set_bus_speed(i2c_base, speed);
> +       __dw_i2c_set_bus_speed(i2c_base, NULL, speed);
>         writel(slaveaddr, &i2c_base->ic_sar);
>  #endif
>
> @@ -356,7 +399,7 @@ static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap,
>                                          unsigned int speed)
>  {
>         adap->speed = speed;
> -       return __dw_i2c_set_bus_speed(i2c_get_base(adap), speed);
> +       return __dw_i2c_set_bus_speed(i2c_get_base(adap), NULL, speed);
>  }
>
>  static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
> @@ -451,7 +494,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
>  {
>         struct dw_i2c *i2c = dev_get_priv(bus);
>
> -       return __dw_i2c_set_bus_speed(i2c->regs, speed);
> +       return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed);
>  }
>
>  static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>  {
>         struct dw_i2c *priv = dev_get_priv(bus);
>
> +#ifdef CONFIG_X86
> +       /* Save base address from PCI BAR */
> +       priv->regs = (struct i2c_regs *)
> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
> +       /* Use BayTrail specific timing values */
> +       priv->scl_sda_cfg = &byt_config;
> +#else

How about:

    if (device_is_on_pci_bus(dev)) {
    do the PCI I2C stuff here;
    }

See driver/net/designware.c for example.

>         /* Save base address from device-tree */
>         priv->regs = (struct i2c_regs *)dev_get_addr(bus);
> +#endif
>
>         __dw_i2c_init(priv->regs, 0, 0);
>
>         return 0;
>  }
>
> +static int designware_i2c_bind(struct udevice *dev)
> +{
> +       static int num_cards;
> +       char name[20];
> +
> +       /* Create a unique device name for PCI type devices */
> +       if (device_is_on_pci_bus(dev)) {
> +               /*
> +                * ToDo:
> +                * Setting req_seq in the driver is probably not recommended.
> +                * But without a DT alias the number is not configured. And
> +                * using this driver is impossible for PCIe I2C devices.
> +                * This can be removed, once a better (correct) way for this
> +                * is found and implemented.
> +                */
> +               dev->req_seq = num_cards;
> +               sprintf(name, "i2c_designware#%u", num_cards++);
> +               device_set_name(dev, name);
> +       }

I believe the above should be wrapped by #ifdef CONFIG_DM_PCI #endif,
otherwise it won't build when DM_PCI is not enabled.

> +
> +       return 0;
> +}
> +
>  static const struct dm_i2c_ops designware_i2c_ops = {
>         .xfer           = designware_i2c_xfer,
>         .probe_chip     = designware_i2c_probe_chip,
> @@ -499,9 +573,26 @@ U_BOOT_DRIVER(i2c_designware) = {
>         .name   = "i2c_designware",
>         .id     = UCLASS_I2C,
>         .of_match = designware_i2c_ids,
> +       .bind   = designware_i2c_bind,
>         .probe  = designware_i2c_probe,
>         .priv_auto_alloc_size = sizeof(struct dw_i2c),
>         .ops    = &designware_i2c_ops,
>  };
>
> +#ifdef CONFIG_X86
> +static struct pci_device_id designware_pci_supported[] = {
> +       /* Intel BayTrail has 7 I2C controller located on the PCI bus */
> +       { PCI_VDEVICE(INTEL, 0x0f41) },
> +       { PCI_VDEVICE(INTEL, 0x0f42) },
> +       { PCI_VDEVICE(INTEL, 0x0f43) },
> +       { PCI_VDEVICE(INTEL, 0x0f44) },
> +       { PCI_VDEVICE(INTEL, 0x0f45) },
> +       { PCI_VDEVICE(INTEL, 0x0f46) },
> +       { PCI_VDEVICE(INTEL, 0x0f47) },
> +       {},
> +};
> +
> +U_BOOT_PCI_DEVICE(i2c_designware, designware_pci_supported);
> +#endif
> +
>  #endif /* CONFIG_DM_I2C */
> --

Regards,
Bin
Stefan Roese March 21, 2016, 9:03 a.m. UTC | #2
Hi Bin,

On 21.03.2016 09:54, Bin Meng wrote:
> On Fri, Mar 18, 2016 at 3:54 PM, Stefan Roese <sr@denx.de> wrote:
>> This patch adds support for the PCI(e) based I2C cores. Which can be
>> found for example on the Intel Bay Trail SoC. It has 7 I2C controllers
>> implemented as PCI devices.
>>
>> This patch also adds the fixed values for the timing registers for
>> BayTrail which are taken from the Linux designware I2C driver.
>>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Bin Meng <bmeng.cn@gmail.com>
>> Cc: Marek Vasut <marex@denx.de>
>> Cc: Heiko Schocher <hs@denx.de>
>> ---
>>   drivers/i2c/designware_i2c.c | 111 +++++++++++++++++++++++++++++++++++++++----
>>   1 file changed, 101 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
>> index 4e5340d..f7f2eba 100644
>> --- a/drivers/i2c/designware_i2c.c
>> +++ b/drivers/i2c/designware_i2c.c
>> @@ -8,11 +8,32 @@
>>   #include <common.h>
>>   #include <dm.h>
>>   #include <i2c.h>
>> +#include <pci.h>
>>   #include <asm/io.h>
>>   #include "designware_i2c.h"
>>
>> +struct dw_scl_sda_cfg {
>> +       u32 ss_hcnt;
>> +       u32 fs_hcnt;
>> +       u32 ss_lcnt;
>> +       u32 fs_lcnt;
>> +       u32 sda_hold;
>> +};
>> +
>> +#ifdef CONFIG_X86
>> +/* BayTrail HCNT/LCNT/SDA hold time */
>> +static struct dw_scl_sda_cfg byt_config = {
>> +       .ss_hcnt = 0x200,
>> +       .fs_hcnt = 0x55,
>> +       .ss_lcnt = 0x200,
>> +       .fs_lcnt = 0x99,
>> +       .sda_hold = 0x6,
>> +};
>> +#endif
>> +
>>   struct dw_i2c {
>>          struct i2c_regs *regs;
>> +       struct dw_scl_sda_cfg *scl_sda_cfg;
>>   };
>>
>>   static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
>> @@ -42,6 +63,7 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
>>    * Set the i2c speed.
>>    */
>>   static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
>> +                                          struct dw_scl_sda_cfg *scl_sda_cfg,
>>                                             unsigned int speed)
>>   {
>>          unsigned int cntl;
>> @@ -61,34 +83,55 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
>>          cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK));
>>
>>          switch (i2c_spd) {
>> +#ifndef CONFIG_X86 /* No High-speed for BayTrail yet */
>>          case IC_SPEED_MODE_MAX:
>> -               cntl |= IC_CON_SPD_HS;
>> -               hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
>> +               cntl |= IC_CON_SPD_SS;
>> +               if (scl_sda_cfg) {
>> +                       hcnt = scl_sda_cfg->fs_hcnt;
>> +                       lcnt = scl_sda_cfg->fs_lcnt;
>> +               } else {
>> +                       hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
>> +                       lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
>> +               }
>>                  writel(hcnt, &i2c_base->ic_hs_scl_hcnt);
>> -               lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
>>                  writel(lcnt, &i2c_base->ic_hs_scl_lcnt);
>>                  break;
>> +#endif
>>
>>          case IC_SPEED_MODE_STANDARD:
>>                  cntl |= IC_CON_SPD_SS;
>> -               hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
>> +               if (scl_sda_cfg) {
>> +                       hcnt = scl_sda_cfg->ss_hcnt;
>> +                       lcnt = scl_sda_cfg->ss_lcnt;
>> +               } else {
>> +                       hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
>> +                       lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
>> +               }
>>                  writel(hcnt, &i2c_base->ic_ss_scl_hcnt);
>> -               lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
>>                  writel(lcnt, &i2c_base->ic_ss_scl_lcnt);
>>                  break;
>>
>>          case IC_SPEED_MODE_FAST:
>>          default:
>>                  cntl |= IC_CON_SPD_FS;
>> -               hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
>> +               if (scl_sda_cfg) {
>> +                       hcnt = scl_sda_cfg->fs_hcnt;
>> +                       lcnt = scl_sda_cfg->fs_lcnt;
>> +               } else {
>> +                       hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
>> +                       lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
>> +               }
>>                  writel(hcnt, &i2c_base->ic_fs_scl_hcnt);
>> -               lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
>>                  writel(lcnt, &i2c_base->ic_fs_scl_lcnt);
>>                  break;
>>          }
>>
>>          writel(cntl, &i2c_base->ic_con);
>>
>> +       /* Configure SDA Hold Time if required */
>> +       if (scl_sda_cfg)
>> +               writel(scl_sda_cfg->sda_hold, &i2c_base->ic_sda_hold);
>> +
>>          /* Enable back i2c now speed set */
>>          dw_i2c_enable(i2c_base, 1);
>>
>> @@ -315,7 +358,7 @@ static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
>>          writel(IC_TX_TL, &i2c_base->ic_tx_tl);
>>          writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
>>   #ifndef CONFIG_DM_I2C
>> -       __dw_i2c_set_bus_speed(i2c_base, speed);
>> +       __dw_i2c_set_bus_speed(i2c_base, NULL, speed);
>>          writel(slaveaddr, &i2c_base->ic_sar);
>>   #endif
>>
>> @@ -356,7 +399,7 @@ static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap,
>>                                           unsigned int speed)
>>   {
>>          adap->speed = speed;
>> -       return __dw_i2c_set_bus_speed(i2c_get_base(adap), speed);
>> +       return __dw_i2c_set_bus_speed(i2c_get_base(adap), NULL, speed);
>>   }
>>
>>   static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
>> @@ -451,7 +494,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
>>   {
>>          struct dw_i2c *i2c = dev_get_priv(bus);
>>
>> -       return __dw_i2c_set_bus_speed(i2c->regs, speed);
>> +       return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed);
>>   }
>>
>>   static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>   {
>>          struct dw_i2c *priv = dev_get_priv(bus);
>>
>> +#ifdef CONFIG_X86
>> +       /* Save base address from PCI BAR */
>> +       priv->regs = (struct i2c_regs *)
>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>> +       /* Use BayTrail specific timing values */
>> +       priv->scl_sda_cfg = &byt_config;
>> +#else
> 
> How about:
> 
>      if (device_is_on_pci_bus(dev)) {
>      do the PCI I2C stuff here;
>      }

I've tried this but it generated compilation errors on socfpga, as the
dm_pci_xxx functions are not available there. So it definitely needs
some #ifdef here. I could go with your suggestion and use
#if CONFIG_DM_PCI as well.
 
> See driver/net/designware.c for example.
> 
>>          /* Save base address from device-tree */
>>          priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>> +#endif
>>
>>          __dw_i2c_init(priv->regs, 0, 0);
>>
>>          return 0;
>>   }
>>
>> +static int designware_i2c_bind(struct udevice *dev)
>> +{
>> +       static int num_cards;
>> +       char name[20];
>> +
>> +       /* Create a unique device name for PCI type devices */
>> +       if (device_is_on_pci_bus(dev)) {
>> +               /*
>> +                * ToDo:
>> +                * Setting req_seq in the driver is probably not recommended.
>> +                * But without a DT alias the number is not configured. And
>> +                * using this driver is impossible for PCIe I2C devices.
>> +                * This can be removed, once a better (correct) way for this
>> +                * is found and implemented.
>> +                */
>> +               dev->req_seq = num_cards;
>> +               sprintf(name, "i2c_designware#%u", num_cards++);
>> +               device_set_name(dev, name);
>> +       }
> 
> I believe the above should be wrapped by #ifdef CONFIG_DM_PCI #endif,
> otherwise it won't build when DM_PCI is not enabled.

It does build and work on socfpga without CONFIG_DM_PCI enabled. I've
double-checked this. The only problem is the dm_pci_xxx() in
designware_i2c_probe() as I mentioned above.

Thanks,
Stefan
Bin Meng March 21, 2016, 9:05 a.m. UTC | #3
Hi Stefan,

On Mon, Mar 21, 2016 at 5:03 PM, Stefan Roese <sr@denx.de> wrote:
> Hi Bin,
>
> On 21.03.2016 09:54, Bin Meng wrote:
>> On Fri, Mar 18, 2016 at 3:54 PM, Stefan Roese <sr@denx.de> wrote:
>>> This patch adds support for the PCI(e) based I2C cores. Which can be
>>> found for example on the Intel Bay Trail SoC. It has 7 I2C controllers
>>> implemented as PCI devices.
>>>
>>> This patch also adds the fixed values for the timing registers for
>>> BayTrail which are taken from the Linux designware I2C driver.
>>>
>>> Signed-off-by: Stefan Roese <sr@denx.de>
>>> Cc: Simon Glass <sjg@chromium.org>
>>> Cc: Bin Meng <bmeng.cn@gmail.com>
>>> Cc: Marek Vasut <marex@denx.de>
>>> Cc: Heiko Schocher <hs@denx.de>
>>> ---
>>>   drivers/i2c/designware_i2c.c | 111 +++++++++++++++++++++++++++++++++++++++----
>>>   1 file changed, 101 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
>>> index 4e5340d..f7f2eba 100644
>>> --- a/drivers/i2c/designware_i2c.c
>>> +++ b/drivers/i2c/designware_i2c.c
>>> @@ -8,11 +8,32 @@
>>>   #include <common.h>
>>>   #include <dm.h>
>>>   #include <i2c.h>
>>> +#include <pci.h>
>>>   #include <asm/io.h>
>>>   #include "designware_i2c.h"
>>>
>>> +struct dw_scl_sda_cfg {
>>> +       u32 ss_hcnt;
>>> +       u32 fs_hcnt;
>>> +       u32 ss_lcnt;
>>> +       u32 fs_lcnt;
>>> +       u32 sda_hold;
>>> +};
>>> +
>>> +#ifdef CONFIG_X86
>>> +/* BayTrail HCNT/LCNT/SDA hold time */
>>> +static struct dw_scl_sda_cfg byt_config = {
>>> +       .ss_hcnt = 0x200,
>>> +       .fs_hcnt = 0x55,
>>> +       .ss_lcnt = 0x200,
>>> +       .fs_lcnt = 0x99,
>>> +       .sda_hold = 0x6,
>>> +};
>>> +#endif
>>> +
>>>   struct dw_i2c {
>>>          struct i2c_regs *regs;
>>> +       struct dw_scl_sda_cfg *scl_sda_cfg;
>>>   };
>>>
>>>   static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
>>> @@ -42,6 +63,7 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
>>>    * Set the i2c speed.
>>>    */
>>>   static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
>>> +                                          struct dw_scl_sda_cfg *scl_sda_cfg,
>>>                                             unsigned int speed)
>>>   {
>>>          unsigned int cntl;
>>> @@ -61,34 +83,55 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
>>>          cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK));
>>>
>>>          switch (i2c_spd) {
>>> +#ifndef CONFIG_X86 /* No High-speed for BayTrail yet */
>>>          case IC_SPEED_MODE_MAX:
>>> -               cntl |= IC_CON_SPD_HS;
>>> -               hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
>>> +               cntl |= IC_CON_SPD_SS;
>>> +               if (scl_sda_cfg) {
>>> +                       hcnt = scl_sda_cfg->fs_hcnt;
>>> +                       lcnt = scl_sda_cfg->fs_lcnt;
>>> +               } else {
>>> +                       hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
>>> +                       lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
>>> +               }
>>>                  writel(hcnt, &i2c_base->ic_hs_scl_hcnt);
>>> -               lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
>>>                  writel(lcnt, &i2c_base->ic_hs_scl_lcnt);
>>>                  break;
>>> +#endif
>>>
>>>          case IC_SPEED_MODE_STANDARD:
>>>                  cntl |= IC_CON_SPD_SS;
>>> -               hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
>>> +               if (scl_sda_cfg) {
>>> +                       hcnt = scl_sda_cfg->ss_hcnt;
>>> +                       lcnt = scl_sda_cfg->ss_lcnt;
>>> +               } else {
>>> +                       hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
>>> +                       lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
>>> +               }
>>>                  writel(hcnt, &i2c_base->ic_ss_scl_hcnt);
>>> -               lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
>>>                  writel(lcnt, &i2c_base->ic_ss_scl_lcnt);
>>>                  break;
>>>
>>>          case IC_SPEED_MODE_FAST:
>>>          default:
>>>                  cntl |= IC_CON_SPD_FS;
>>> -               hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
>>> +               if (scl_sda_cfg) {
>>> +                       hcnt = scl_sda_cfg->fs_hcnt;
>>> +                       lcnt = scl_sda_cfg->fs_lcnt;
>>> +               } else {
>>> +                       hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
>>> +                       lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
>>> +               }
>>>                  writel(hcnt, &i2c_base->ic_fs_scl_hcnt);
>>> -               lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
>>>                  writel(lcnt, &i2c_base->ic_fs_scl_lcnt);
>>>                  break;
>>>          }
>>>
>>>          writel(cntl, &i2c_base->ic_con);
>>>
>>> +       /* Configure SDA Hold Time if required */
>>> +       if (scl_sda_cfg)
>>> +               writel(scl_sda_cfg->sda_hold, &i2c_base->ic_sda_hold);
>>> +
>>>          /* Enable back i2c now speed set */
>>>          dw_i2c_enable(i2c_base, 1);
>>>
>>> @@ -315,7 +358,7 @@ static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
>>>          writel(IC_TX_TL, &i2c_base->ic_tx_tl);
>>>          writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
>>>   #ifndef CONFIG_DM_I2C
>>> -       __dw_i2c_set_bus_speed(i2c_base, speed);
>>> +       __dw_i2c_set_bus_speed(i2c_base, NULL, speed);
>>>          writel(slaveaddr, &i2c_base->ic_sar);
>>>   #endif
>>>
>>> @@ -356,7 +399,7 @@ static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap,
>>>                                           unsigned int speed)
>>>   {
>>>          adap->speed = speed;
>>> -       return __dw_i2c_set_bus_speed(i2c_get_base(adap), speed);
>>> +       return __dw_i2c_set_bus_speed(i2c_get_base(adap), NULL, speed);
>>>   }
>>>
>>>   static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
>>> @@ -451,7 +494,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
>>>   {
>>>          struct dw_i2c *i2c = dev_get_priv(bus);
>>>
>>> -       return __dw_i2c_set_bus_speed(i2c->regs, speed);
>>> +       return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed);
>>>   }
>>>
>>>   static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>   {
>>>          struct dw_i2c *priv = dev_get_priv(bus);
>>>
>>> +#ifdef CONFIG_X86
>>> +       /* Save base address from PCI BAR */
>>> +       priv->regs = (struct i2c_regs *)
>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>> +       /* Use BayTrail specific timing values */
>>> +       priv->scl_sda_cfg = &byt_config;
>>> +#else
>>
>> How about:
>>
>>      if (device_is_on_pci_bus(dev)) {
>>      do the PCI I2C stuff here;
>>      }
>
> I've tried this but it generated compilation errors on socfpga, as the
> dm_pci_xxx functions are not available there. So it definitely needs
> some #ifdef here. I could go with your suggestion and use
> #if CONFIG_DM_PCI as well.
>
>> See driver/net/designware.c for example.
>>
>>>          /* Save base address from device-tree */
>>>          priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>> +#endif
>>>
>>>          __dw_i2c_init(priv->regs, 0, 0);
>>>
>>>          return 0;
>>>   }
>>>
>>> +static int designware_i2c_bind(struct udevice *dev)
>>> +{
>>> +       static int num_cards;
>>> +       char name[20];
>>> +
>>> +       /* Create a unique device name for PCI type devices */
>>> +       if (device_is_on_pci_bus(dev)) {
>>> +               /*
>>> +                * ToDo:
>>> +                * Setting req_seq in the driver is probably not recommended.
>>> +                * But without a DT alias the number is not configured. And
>>> +                * using this driver is impossible for PCIe I2C devices.
>>> +                * This can be removed, once a better (correct) way for this
>>> +                * is found and implemented.
>>> +                */
>>> +               dev->req_seq = num_cards;
>>> +               sprintf(name, "i2c_designware#%u", num_cards++);
>>> +               device_set_name(dev, name);
>>> +       }
>>
>> I believe the above should be wrapped by #ifdef CONFIG_DM_PCI #endif,
>> otherwise it won't build when DM_PCI is not enabled.
>
> It does build and work on socfpga without CONFIG_DM_PCI enabled. I've
> double-checked this. The only problem is the dm_pci_xxx() in
> designware_i2c_probe() as I mentioned above.
>

Great. So looks we may consolidate one usage for such both PCI and SoC
devices, like the one used in drivers/net/designware.c?

Regards,
Bin
Stefan Roese March 21, 2016, 12:04 p.m. UTC | #4
Hi Bin,

On 21.03.2016 10:03, Stefan Roese wrote:

<snip>

>>>    static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>    {
>>>           struct dw_i2c *priv = dev_get_priv(bus);
>>>
>>> +#ifdef CONFIG_X86
>>> +       /* Save base address from PCI BAR */
>>> +       priv->regs = (struct i2c_regs *)
>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>> +       /* Use BayTrail specific timing values */
>>> +       priv->scl_sda_cfg = &byt_config;
>>> +#else
>>
>> How about:
>>
>>       if (device_is_on_pci_bus(dev)) {
>>       do the PCI I2C stuff here;
>>       }
> 
> I've tried this but it generated compilation errors on socfpga, as the
> dm_pci_xxx functions are not available there. So it definitely needs
> some #ifdef here. I could go with your suggestion and use
> #if CONFIG_DM_PCI as well.
>   
>> See driver/net/designware.c for example.
>>
>>>           /* Save base address from device-tree */
>>>           priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>> +#endif

Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
in this ugly compilation warning:

drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
   priv->regs = (struct i2c_regs *)dev_get_addr(bus);
                ^

This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
I'm wondering, how dev_get_addr() should get used on x86. Has it
been used anywhere here at all? Should we perhaps go back to
a 32bit phy_addr representation again? So that dev_get_addr()
matches the (void *) size again?

The other option would to just leave the code as in v1 so that
dev_get_addr() is not referenced on x86 here.

Thanks,
Stefan
Bin Meng March 21, 2016, 12:43 p.m. UTC | #5
Hi Stefan,

On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
> Hi Bin,
>
> On 21.03.2016 10:03, Stefan Roese wrote:
>
> <snip>
>
>>>>    static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>>    {
>>>>           struct dw_i2c *priv = dev_get_priv(bus);
>>>>
>>>> +#ifdef CONFIG_X86
>>>> +       /* Save base address from PCI BAR */
>>>> +       priv->regs = (struct i2c_regs *)
>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>>> +       /* Use BayTrail specific timing values */
>>>> +       priv->scl_sda_cfg = &byt_config;
>>>> +#else
>>>
>>> How about:
>>>
>>>       if (device_is_on_pci_bus(dev)) {
>>>       do the PCI I2C stuff here;
>>>       }
>>
>> I've tried this but it generated compilation errors on socfpga, as the
>> dm_pci_xxx functions are not available there. So it definitely needs
>> some #ifdef here. I could go with your suggestion and use
>> #if CONFIG_DM_PCI as well.
>>
>>> See driver/net/designware.c for example.
>>>
>>>>           /* Save base address from device-tree */
>>>>           priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>> +#endif
>
> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
> in this ugly compilation warning:
>
> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
>    priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>                 ^
>
> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
> I'm wondering, how dev_get_addr() should get used on x86. Has it
> been used anywhere here at all? Should we perhaps go back to
> a 32bit phy_addr representation again? So that dev_get_addr()
> matches the (void *) size again?
>

dev_get_addr() is being used on x86 drivers. See
ns16550_serial_ofdata_to_platdata() for example. There is no build
warning for the ns16550 driver.

> The other option would to just leave the code as in v1 so that
> dev_get_addr() is not referenced on x86 here.
>

Regards,
Bin
Stefan Roese March 21, 2016, 12:52 p.m. UTC | #6
Hi Bin,

On 21.03.2016 13:43, Bin Meng wrote:
> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>> Hi Bin,
>>
>> On 21.03.2016 10:03, Stefan Roese wrote:
>>
>> <snip>
>>
>>>>>     static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>>>     {
>>>>>            struct dw_i2c *priv = dev_get_priv(bus);
>>>>>
>>>>> +#ifdef CONFIG_X86
>>>>> +       /* Save base address from PCI BAR */
>>>>> +       priv->regs = (struct i2c_regs *)
>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>>>> +       /* Use BayTrail specific timing values */
>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>> +#else
>>>>
>>>> How about:
>>>>
>>>>        if (device_is_on_pci_bus(dev)) {
>>>>        do the PCI I2C stuff here;
>>>>        }
>>>
>>> I've tried this but it generated compilation errors on socfpga, as the
>>> dm_pci_xxx functions are not available there. So it definitely needs
>>> some #ifdef here. I could go with your suggestion and use
>>> #if CONFIG_DM_PCI as well.
>>>
>>>> See driver/net/designware.c for example.
>>>>
>>>>>            /* Save base address from device-tree */
>>>>>            priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>> +#endif
>>
>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>> in this ugly compilation warning:
>>
>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
>>     priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>                  ^
>>
>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>> been used anywhere here at all? Should we perhaps go back to
>> a 32bit phy_addr representation again? So that dev_get_addr()
>> matches the (void *) size again?
>>
>
> dev_get_addr() is being used on x86 drivers. See
> ns16550_serial_ofdata_to_platdata() for example. There is no build
> warning for the ns16550 driver.

I see, thanks. Its used differently there though. Let me see, if I
cook something up...

Thanks,
Stefan
Stefan Roese March 21, 2016, 2:04 p.m. UTC | #7
Hi Bin,

On 21.03.2016 13:43, Bin Meng wrote:
> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>> Hi Bin,
>>
>> On 21.03.2016 10:03, Stefan Roese wrote:
>>
>> <snip>
>>
>>>>>     static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>>>     {
>>>>>            struct dw_i2c *priv = dev_get_priv(bus);
>>>>>
>>>>> +#ifdef CONFIG_X86
>>>>> +       /* Save base address from PCI BAR */
>>>>> +       priv->regs = (struct i2c_regs *)
>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>>>> +       /* Use BayTrail specific timing values */
>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>> +#else
>>>>
>>>> How about:
>>>>
>>>>        if (device_is_on_pci_bus(dev)) {
>>>>        do the PCI I2C stuff here;
>>>>        }
>>>
>>> I've tried this but it generated compilation errors on socfpga, as the
>>> dm_pci_xxx functions are not available there. So it definitely needs
>>> some #ifdef here. I could go with your suggestion and use
>>> #if CONFIG_DM_PCI as well.
>>>
>>>> See driver/net/designware.c for example.
>>>>
>>>>>            /* Save base address from device-tree */
>>>>>            priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>> +#endif
>>
>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>> in this ugly compilation warning:
>>
>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
>>     priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>                  ^
>>
>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>> been used anywhere here at all? Should we perhaps go back to
>> a 32bit phy_addr representation again? So that dev_get_addr()
>> matches the (void *) size again?
>>
> 
> dev_get_addr() is being used on x86 drivers. See
> ns16550_serial_ofdata_to_platdata() for example. There is no build
> warning for the ns16550 driver.

Looking closer, the warning does not occur here, since the registers
are stored in a u32 variable "base". And assigning a 64bit value to a
32bit variable as in "plat->base = addr" in ns16550.c does not cause any
warnings.

Here in the I2C driver though, the base address is stored as a pointer
(pointer size is 32 bit for x86). And this triggers this warning, even
though its effectively the same assignment. I could cast to u32 but this
would cause problems on 64 bit architectures using this driver (in the
future). So I came up with this approach:

/*
 * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning the
 * register base directly in dev_get_addr() results in this compilation warning:
 *     warning: cast to pointer from integer of different size
 *
 * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
 * dev_get_addr() into a 32bit value before casting it to the pointer
 * (struct i2c_regs *).
 */
#ifdef CONFIG_X86
#define POINTER_SIZE_CAST	u32
#endif

...

static int designware_i2c_probe(struct udevice *bus)
{
	struct dw_i2c *priv = dev_get_priv(bus);

	if (device_is_on_pci_bus(bus)) {
#ifdef CONFIG_DM_PCI
		/* Save base address from PCI BAR */
		priv->regs = (struct i2c_regs *)
			dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
#ifdef CONFIG_X86
		/* Use BayTrail specific timing values */
		priv->scl_sda_cfg = &byt_config;
#endif
#endif
	} else {
		/* Save base address from device-tree */
		priv->regs = (struct i2c_regs *)(POINTER_SIZE_CAST)dev_get_addr(bus);
	}

But I'm not 100% happy with this approach.

So what are the alternatives:

a) Don't compile the  dev_get_addr() part for x86 similar to what I've
   done in v1

b) This approach with POINTER_SIZE_CAST

Any preferences of other ideas?

Side note: My general feeling is, that dev_get_addr() should be able to
get cast into a pointer on all platforms. This is how it is used in many
drivers, btw. Since this is not possible on x86, we might have a problem
here. Simon might have some ideas on this as well...

Thanks,
Stefan
Bin Meng March 28, 2016, 6:01 a.m. UTC | #8
Hi Stefan,

On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
> Hi Bin,
>
> On 21.03.2016 13:43, Bin Meng wrote:
>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>>> Hi Bin,
>>>
>>> On 21.03.2016 10:03, Stefan Roese wrote:
>>>
>>> <snip>
>>>
>>>>>>     static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>>>>     {
>>>>>>            struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>
>>>>>> +#ifdef CONFIG_X86
>>>>>> +       /* Save base address from PCI BAR */
>>>>>> +       priv->regs = (struct i2c_regs *)
>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>>>>> +       /* Use BayTrail specific timing values */
>>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>>> +#else
>>>>>
>>>>> How about:
>>>>>
>>>>>        if (device_is_on_pci_bus(dev)) {
>>>>>        do the PCI I2C stuff here;
>>>>>        }
>>>>
>>>> I've tried this but it generated compilation errors on socfpga, as the
>>>> dm_pci_xxx functions are not available there. So it definitely needs
>>>> some #ifdef here. I could go with your suggestion and use
>>>> #if CONFIG_DM_PCI as well.
>>>>
>>>>> See driver/net/designware.c for example.
>>>>>
>>>>>>            /* Save base address from device-tree */
>>>>>>            priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>> +#endif
>>>
>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>>> in this ugly compilation warning:
>>>
>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
>>>     priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>                  ^
>>>
>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>>> been used anywhere here at all? Should we perhaps go back to
>>> a 32bit phy_addr representation again? So that dev_get_addr()
>>> matches the (void *) size again?
>>>
>>
>> dev_get_addr() is being used on x86 drivers. See
>> ns16550_serial_ofdata_to_platdata() for example. There is no build
>> warning for the ns16550 driver.
>
> Looking closer, the warning does not occur here, since the registers
> are stored in a u32 variable "base". And assigning a 64bit value to a
> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
> warnings.
>
> Here in the I2C driver though, the base address is stored as a pointer
> (pointer size is 32 bit for x86). And this triggers this warning, even
> though its effectively the same assignment. I could cast to u32 but this
> would cause problems on 64 bit architectures using this driver (in the
> future). So I came up with this approach:

Thanks for digging out these.

>
> /*
>  * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning the
>  * register base directly in dev_get_addr() results in this compilation warning:
>  *     warning: cast to pointer from integer of different size
>  *
>  * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
>  * dev_get_addr() into a 32bit value before casting it to the pointer
>  * (struct i2c_regs *).
>  */
> #ifdef CONFIG_X86
> #define POINTER_SIZE_CAST       u32
> #endif
>
> ...
>
> static int designware_i2c_probe(struct udevice *bus)
> {
>         struct dw_i2c *priv = dev_get_priv(bus);
>
>         if (device_is_on_pci_bus(bus)) {
> #ifdef CONFIG_DM_PCI
>                 /* Save base address from PCI BAR */
>                 priv->regs = (struct i2c_regs *)
>                         dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
> #ifdef CONFIG_X86
>                 /* Use BayTrail specific timing values */
>                 priv->scl_sda_cfg = &byt_config;
> #endif
> #endif
>         } else {
>                 /* Save base address from device-tree */
>                 priv->regs = (struct i2c_regs *)(POINTER_SIZE_CAST)dev_get_addr(bus);
>         }
>
> But I'm not 100% happy with this approach.
>

Yes, it's annoying.

> So what are the alternatives:
>
> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
>    done in v1
>
> b) This approach with POINTER_SIZE_CAST
>
> Any preferences of other ideas?
>
> Side note: My general feeling is, that dev_get_addr() should be able to
> get cast into a pointer on all platforms. This is how it is used in many
> drivers, btw. Since this is not possible on x86, we might have a problem
> here. Simon might have some ideas on this as well...
>

I would like to hear Simon's input. Simon?

Regards,
Bin
Stefan Roese April 4, 2016, 2:53 p.m. UTC | #9
Hi Simon,

as you seem to be back from vacation (?), we (Bin and myself) would
like to hear your expert comment on a x86 issue I've discovered
while porting the Designware I2C driver to x86. Please see below:

On 28.03.2016 08:01, Bin Meng wrote:
> Hi Stefan,
> 
> On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
>> Hi Bin,
>>
>> On 21.03.2016 13:43, Bin Meng wrote:
>>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>> Hi Bin,
>>>>
>>>> On 21.03.2016 10:03, Stefan Roese wrote:
>>>>
>>>> <snip>
>>>>
>>>>>>>      static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>>>>>      {
>>>>>>>             struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>>
>>>>>>> +#ifdef CONFIG_X86
>>>>>>> +       /* Save base address from PCI BAR */
>>>>>>> +       priv->regs = (struct i2c_regs *)
>>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>>>>>> +       /* Use BayTrail specific timing values */
>>>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>>>> +#else
>>>>>>
>>>>>> How about:
>>>>>>
>>>>>>         if (device_is_on_pci_bus(dev)) {
>>>>>>         do the PCI I2C stuff here;
>>>>>>         }
>>>>>
>>>>> I've tried this but it generated compilation errors on socfpga, as the
>>>>> dm_pci_xxx functions are not available there. So it definitely needs
>>>>> some #ifdef here. I could go with your suggestion and use
>>>>> #if CONFIG_DM_PCI as well.
>>>>>
>>>>>> See driver/net/designware.c for example.
>>>>>>
>>>>>>>             /* Save base address from device-tree */
>>>>>>>             priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>> +#endif
>>>>
>>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>>>> in this ugly compilation warning:
>>>>
>>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
>>>>      priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>                   ^
>>>>
>>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>>>> been used anywhere here at all? Should we perhaps go back to
>>>> a 32bit phy_addr representation again? So that dev_get_addr()
>>>> matches the (void *) size again?
>>>>
>>>
>>> dev_get_addr() is being used on x86 drivers. See
>>> ns16550_serial_ofdata_to_platdata() for example. There is no build
>>> warning for the ns16550 driver.
>>
>> Looking closer, the warning does not occur here, since the registers
>> are stored in a u32 variable "base". And assigning a 64bit value to a
>> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
>> warnings.
>>
>> Here in the I2C driver though, the base address is stored as a pointer
>> (pointer size is 32 bit for x86). And this triggers this warning, even
>> though its effectively the same assignment. I could cast to u32 but this
>> would cause problems on 64 bit architectures using this driver (in the
>> future). So I came up with this approach:
> 
> Thanks for digging out these.
> 
>>
>> /*
>>   * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning the
>>   * register base directly in dev_get_addr() results in this compilation warning:
>>   *     warning: cast to pointer from integer of different size
>>   *
>>   * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
>>   * dev_get_addr() into a 32bit value before casting it to the pointer
>>   * (struct i2c_regs *).
>>   */
>> #ifdef CONFIG_X86
>> #define POINTER_SIZE_CAST       u32
>> #endif
>>
>> ...
>>
>> static int designware_i2c_probe(struct udevice *bus)
>> {
>>          struct dw_i2c *priv = dev_get_priv(bus);
>>
>>          if (device_is_on_pci_bus(bus)) {
>> #ifdef CONFIG_DM_PCI
>>                  /* Save base address from PCI BAR */
>>                  priv->regs = (struct i2c_regs *)
>>                          dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>> #ifdef CONFIG_X86
>>                  /* Use BayTrail specific timing values */
>>                  priv->scl_sda_cfg = &byt_config;
>> #endif
>> #endif
>>          } else {
>>                  /* Save base address from device-tree */
>>                  priv->regs = (struct i2c_regs *)(POINTER_SIZE_CAST)dev_get_addr(bus);
>>          }
>>
>> But I'm not 100% happy with this approach.
>>
> 
> Yes, it's annoying.
> 
>> So what are the alternatives:
>>
>> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
>>     done in v1
>>
>> b) This approach with POINTER_SIZE_CAST
>>
>> Any preferences of other ideas?
>>
>> Side note: My general feeling is, that dev_get_addr() should be able to
>> get cast into a pointer on all platforms. This is how it is used in many
>> drivers, btw. Since this is not possible on x86, we might have a problem
>> here. Simon might have some ideas on this as well...
>>
> 
> I would like to hear Simon's input. Simon?

Yes, Simon, what do you think?

Please also see my v2 of this patch which uses (__UINTPTR_TYPE__)
for the cast:

https://patchwork.ozlabs.org/patch/601113/

Thanks,
Stefan
Stefan Roese April 11, 2016, 3:03 p.m. UTC | #10
Hi Simon,

On 04.04.2016 16:53, Stefan Roese wrote:
> Hi Simon,
>
> as you seem to be back from vacation (?), we (Bin and myself) would
> like to hear your expert comment on a x86 issue I've discovered
> while porting the Designware I2C driver to x86. Please see below:
>
> On 28.03.2016 08:01, Bin Meng wrote:
>> Hi Stefan,
>>
>> On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
>>> Hi Bin,
>>>
>>> On 21.03.2016 13:43, Bin Meng wrote:
>>>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>> Hi Bin,
>>>>>
>>>>> On 21.03.2016 10:03, Stefan Roese wrote:
>>>>>
>>>>> <snip>
>>>>>
>>>>>>>>       static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct udevice *bus)
>>>>>>>>       {
>>>>>>>>              struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>>>
>>>>>>>> +#ifdef CONFIG_X86
>>>>>>>> +       /* Save base address from PCI BAR */
>>>>>>>> +       priv->regs = (struct i2c_regs *)
>>>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>>>>>>> +       /* Use BayTrail specific timing values */
>>>>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>>>>> +#else
>>>>>>>
>>>>>>> How about:
>>>>>>>
>>>>>>>          if (device_is_on_pci_bus(dev)) {
>>>>>>>          do the PCI I2C stuff here;
>>>>>>>          }
>>>>>>
>>>>>> I've tried this but it generated compilation errors on socfpga, as the
>>>>>> dm_pci_xxx functions are not available there. So it definitely needs
>>>>>> some #ifdef here. I could go with your suggestion and use
>>>>>> #if CONFIG_DM_PCI as well.
>>>>>>
>>>>>>> See driver/net/designware.c for example.
>>>>>>>
>>>>>>>>              /* Save base address from device-tree */
>>>>>>>>              priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>>> +#endif
>>>>>
>>>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>>>>> in this ugly compilation warning:
>>>>>
>>>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>>>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
>>>>>       priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>                    ^
>>>>>
>>>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>>>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>>>>> been used anywhere here at all? Should we perhaps go back to
>>>>> a 32bit phy_addr representation again? So that dev_get_addr()
>>>>> matches the (void *) size again?
>>>>>
>>>>
>>>> dev_get_addr() is being used on x86 drivers. See
>>>> ns16550_serial_ofdata_to_platdata() for example. There is no build
>>>> warning for the ns16550 driver.
>>>
>>> Looking closer, the warning does not occur here, since the registers
>>> are stored in a u32 variable "base". And assigning a 64bit value to a
>>> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
>>> warnings.
>>>
>>> Here in the I2C driver though, the base address is stored as a pointer
>>> (pointer size is 32 bit for x86). And this triggers this warning, even
>>> though its effectively the same assignment. I could cast to u32 but this
>>> would cause problems on 64 bit architectures using this driver (in the
>>> future). So I came up with this approach:
>>
>> Thanks for digging out these.
>>
>>>
>>> /*
>>>    * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning the
>>>    * register base directly in dev_get_addr() results in this compilation warning:
>>>    *     warning: cast to pointer from integer of different size
>>>    *
>>>    * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
>>>    * dev_get_addr() into a 32bit value before casting it to the pointer
>>>    * (struct i2c_regs *).
>>>    */
>>> #ifdef CONFIG_X86
>>> #define POINTER_SIZE_CAST       u32
>>> #endif
>>>
>>> ...
>>>
>>> static int designware_i2c_probe(struct udevice *bus)
>>> {
>>>           struct dw_i2c *priv = dev_get_priv(bus);
>>>
>>>           if (device_is_on_pci_bus(bus)) {
>>> #ifdef CONFIG_DM_PCI
>>>                   /* Save base address from PCI BAR */
>>>                   priv->regs = (struct i2c_regs *)
>>>                           dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
>>> #ifdef CONFIG_X86
>>>                   /* Use BayTrail specific timing values */
>>>                   priv->scl_sda_cfg = &byt_config;
>>> #endif
>>> #endif
>>>           } else {
>>>                   /* Save base address from device-tree */
>>>                   priv->regs = (struct i2c_regs *)(POINTER_SIZE_CAST)dev_get_addr(bus);
>>>           }
>>>
>>> But I'm not 100% happy with this approach.
>>>
>>
>> Yes, it's annoying.
>>
>>> So what are the alternatives:
>>>
>>> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
>>>      done in v1
>>>
>>> b) This approach with POINTER_SIZE_CAST
>>>
>>> Any preferences of other ideas?
>>>
>>> Side note: My general feeling is, that dev_get_addr() should be able to
>>> get cast into a pointer on all platforms. This is how it is used in many
>>> drivers, btw. Since this is not possible on x86, we might have a problem
>>> here. Simon might have some ideas on this as well...
>>>
>>
>> I would like to hear Simon's input. Simon?
>
> Yes, Simon, what do you think?
>
> Please also see my v2 of this patch which uses (__UINTPTR_TYPE__)
> for the cast:
>
> https://patchwork.ozlabs.org/patch/601113/

Simon, could you please take a quick look at this patch? With the
general problem of dev_get_addr() on x86 (as described above). Do you
have some other suggestions to solve this? Or is the solution in
v2 which uses (__UINTPTR_TYPE__) acceptable?

https://patchwork.ozlabs.org/patch/601113/

Thanks,
Stefan
Simon Glass April 20, 2016, 2:40 p.m. UTC | #11
Hi Stefan,

On 11 April 2016 at 09:03, Stefan Roese <sr@denx.de> wrote:
> Hi Simon,
>
>
> On 04.04.2016 16:53, Stefan Roese wrote:
>>
>> Hi Simon,
>>
>> as you seem to be back from vacation (?), we (Bin and myself) would
>> like to hear your expert comment on a x86 issue I've discovered
>> while porting the Designware I2C driver to x86. Please see below:
>>
>> On 28.03.2016 08:01, Bin Meng wrote:
>>>
>>> Hi Stefan,
>>>
>>> On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>
>>>> Hi Bin,
>>>>
>>>> On 21.03.2016 13:43, Bin Meng wrote:
>>>>>
>>>>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>>>
>>>>>> Hi Bin,
>>>>>>
>>>>>> On 21.03.2016 10:03, Stefan Roese wrote:
>>>>>>
>>>>>> <snip>
>>>>>>
>>>>>>>>>       static int designware_i2c_probe_chip(struct udevice *bus,
>>>>>>>>> uint chip_addr,
>>>>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct
>>>>>>>>> udevice *bus)
>>>>>>>>>       {
>>>>>>>>>              struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>>>>
>>>>>>>>> +#ifdef CONFIG_X86
>>>>>>>>> +       /* Save base address from PCI BAR */
>>>>>>>>> +       priv->regs = (struct i2c_regs *)
>>>>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
>>>>>>>>> PCI_REGION_MEM);
>>>>>>>>> +       /* Use BayTrail specific timing values */
>>>>>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>>>>>> +#else
>>>>>>>>
>>>>>>>>
>>>>>>>> How about:
>>>>>>>>
>>>>>>>>          if (device_is_on_pci_bus(dev)) {
>>>>>>>>          do the PCI I2C stuff here;
>>>>>>>>          }
>>>>>>>
>>>>>>>
>>>>>>> I've tried this but it generated compilation errors on socfpga, as
>>>>>>> the
>>>>>>> dm_pci_xxx functions are not available there. So it definitely needs
>>>>>>> some #ifdef here. I could go with your suggestion and use
>>>>>>> #if CONFIG_DM_PCI as well.
>>>>>>>
>>>>>>>> See driver/net/designware.c for example.
>>>>>>>>
>>>>>>>>>              /* Save base address from device-tree */
>>>>>>>>>              priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>>>> +#endif
>>>>>>
>>>>>>
>>>>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>>>>>> in this ugly compilation warning:
>>>>>>
>>>>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>>>>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from
>>>>>> integer of different size [-Wint-to-pointer-cast]
>>>>>>       priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>                    ^
>>>>>>
>>>>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>>>>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>>>>>> been used anywhere here at all? Should we perhaps go back to
>>>>>> a 32bit phy_addr representation again? So that dev_get_addr()
>>>>>> matches the (void *) size again?
>>>>>>
>>>>>
>>>>> dev_get_addr() is being used on x86 drivers. See
>>>>> ns16550_serial_ofdata_to_platdata() for example. There is no build
>>>>> warning for the ns16550 driver.
>>>>
>>>>
>>>> Looking closer, the warning does not occur here, since the registers
>>>> are stored in a u32 variable "base". And assigning a 64bit value to a
>>>> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
>>>> warnings.
>>>>
>>>> Here in the I2C driver though, the base address is stored as a pointer
>>>> (pointer size is 32 bit for x86). And this triggers this warning, even
>>>> though its effectively the same assignment. I could cast to u32 but this
>>>> would cause problems on 64 bit architectures using this driver (in the
>>>> future). So I came up with this approach:
>>>
>>>
>>> Thanks for digging out these.
>>>
>>>>
>>>> /*
>>>>    * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning
>>>> the
>>>>    * register base directly in dev_get_addr() results in this
>>>> compilation warning:
>>>>    *     warning: cast to pointer from integer of different size
>>>>    *
>>>>    * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
>>>>    * dev_get_addr() into a 32bit value before casting it to the pointer
>>>>    * (struct i2c_regs *).
>>>>    */
>>>> #ifdef CONFIG_X86
>>>> #define POINTER_SIZE_CAST       u32
>>>> #endif
>>>>
>>>> ...
>>>>
>>>> static int designware_i2c_probe(struct udevice *bus)
>>>> {
>>>>           struct dw_i2c *priv = dev_get_priv(bus);
>>>>
>>>>           if (device_is_on_pci_bus(bus)) {
>>>> #ifdef CONFIG_DM_PCI
>>>>                   /* Save base address from PCI BAR */
>>>>                   priv->regs = (struct i2c_regs *)
>>>>                           dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
>>>> PCI_REGION_MEM);
>>>> #ifdef CONFIG_X86
>>>>                   /* Use BayTrail specific timing values */
>>>>                   priv->scl_sda_cfg = &byt_config;
>>>> #endif
>>>> #endif
>>>>           } else {
>>>>                   /* Save base address from device-tree */
>>>>                   priv->regs = (struct i2c_regs
>>>> *)(POINTER_SIZE_CAST)dev_get_addr(bus);
>>>>           }
>>>>
>>>> But I'm not 100% happy with this approach.
>>>>
>>>
>>> Yes, it's annoying.
>>>
>>>> So what are the alternatives:
>>>>
>>>> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
>>>>      done in v1
>>>>
>>>> b) This approach with POINTER_SIZE_CAST
>>>>
>>>> Any preferences of other ideas?
>>>>
>>>> Side note: My general feeling is, that dev_get_addr() should be able to
>>>> get cast into a pointer on all platforms. This is how it is used in many
>>>> drivers, btw. Since this is not possible on x86, we might have a problem
>>>> here. Simon might have some ideas on this as well...
>>>>
>>>
>>> I would like to hear Simon's input. Simon?
>>
>>
>> Yes, Simon, what do you think?
>>
>> Please also see my v2 of this patch which uses (__UINTPTR_TYPE__)
>> for the cast:
>>
>> https://patchwork.ozlabs.org/patch/601113/
>
>
> Simon, could you please take a quick look at this patch? With the
> general problem of dev_get_addr() on x86 (as described above). Do you
> have some other suggestions to solve this? Or is the solution in
> v2 which uses (__UINTPTR_TYPE__) acceptable?
>
> https://patchwork.ozlabs.org/patch/601113/

I feel that you should store the return value from dev_get_addr() in
an fdt_addr_t or a ulong. Then it can be cast to a pointer as you
wish. Platform data should hold the ulong, and private data
(dev_get_priv()) should hold the pointer.

I'm not keen on the POINTER_SIZE_CAST idea.

Does that fix the problem?

Regards,
Simon
Stefan Roese April 20, 2016, 2:58 p.m. UTC | #12
Hi Simon.

On 20.04.2016 16:40, Simon Glass wrote:

> On 11 April 2016 at 09:03, Stefan Roese <sr@denx.de> wrote:
>> Hi Simon,
>>
>>
>> On 04.04.2016 16:53, Stefan Roese wrote:
>>>
>>> Hi Simon,
>>>
>>> as you seem to be back from vacation (?), we (Bin and myself) would
>>> like to hear your expert comment on a x86 issue I've discovered
>>> while porting the Designware I2C driver to x86. Please see below:
>>>
>>> On 28.03.2016 08:01, Bin Meng wrote:
>>>>
>>>> Hi Stefan,
>>>>
>>>> On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>>
>>>>> Hi Bin,
>>>>>
>>>>> On 21.03.2016 13:43, Bin Meng wrote:
>>>>>>
>>>>>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>>>>
>>>>>>> Hi Bin,
>>>>>>>
>>>>>>> On 21.03.2016 10:03, Stefan Roese wrote:
>>>>>>>
>>>>>>> <snip>
>>>>>>>
>>>>>>>>>>        static int designware_i2c_probe_chip(struct udevice *bus,
>>>>>>>>>> uint chip_addr,
>>>>>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct
>>>>>>>>>> udevice *bus)
>>>>>>>>>>        {
>>>>>>>>>>               struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>>>>>
>>>>>>>>>> +#ifdef CONFIG_X86
>>>>>>>>>> +       /* Save base address from PCI BAR */
>>>>>>>>>> +       priv->regs = (struct i2c_regs *)
>>>>>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
>>>>>>>>>> PCI_REGION_MEM);
>>>>>>>>>> +       /* Use BayTrail specific timing values */
>>>>>>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>>>>>>> +#else
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> How about:
>>>>>>>>>
>>>>>>>>>           if (device_is_on_pci_bus(dev)) {
>>>>>>>>>           do the PCI I2C stuff here;
>>>>>>>>>           }
>>>>>>>>
>>>>>>>>
>>>>>>>> I've tried this but it generated compilation errors on socfpga, as
>>>>>>>> the
>>>>>>>> dm_pci_xxx functions are not available there. So it definitely needs
>>>>>>>> some #ifdef here. I could go with your suggestion and use
>>>>>>>> #if CONFIG_DM_PCI as well.
>>>>>>>>
>>>>>>>>> See driver/net/designware.c for example.
>>>>>>>>>
>>>>>>>>>>               /* Save base address from device-tree */
>>>>>>>>>>               priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>>>>> +#endif
>>>>>>>
>>>>>>>
>>>>>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>>>>>>> in this ugly compilation warning:
>>>>>>>
>>>>>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>>>>>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from
>>>>>>> integer of different size [-Wint-to-pointer-cast]
>>>>>>>        priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>>                     ^
>>>>>>>
>>>>>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>>>>>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>>>>>>> been used anywhere here at all? Should we perhaps go back to
>>>>>>> a 32bit phy_addr representation again? So that dev_get_addr()
>>>>>>> matches the (void *) size again?
>>>>>>>
>>>>>>
>>>>>> dev_get_addr() is being used on x86 drivers. See
>>>>>> ns16550_serial_ofdata_to_platdata() for example. There is no build
>>>>>> warning for the ns16550 driver.
>>>>>
>>>>>
>>>>> Looking closer, the warning does not occur here, since the registers
>>>>> are stored in a u32 variable "base". And assigning a 64bit value to a
>>>>> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
>>>>> warnings.
>>>>>
>>>>> Here in the I2C driver though, the base address is stored as a pointer
>>>>> (pointer size is 32 bit for x86). And this triggers this warning, even
>>>>> though its effectively the same assignment. I could cast to u32 but this
>>>>> would cause problems on 64 bit architectures using this driver (in the
>>>>> future). So I came up with this approach:
>>>>
>>>>
>>>> Thanks for digging out these.
>>>>
>>>>>
>>>>> /*
>>>>>     * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning
>>>>> the
>>>>>     * register base directly in dev_get_addr() results in this
>>>>> compilation warning:
>>>>>     *     warning: cast to pointer from integer of different size
>>>>>     *
>>>>>     * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
>>>>>     * dev_get_addr() into a 32bit value before casting it to the pointer
>>>>>     * (struct i2c_regs *).
>>>>>     */
>>>>> #ifdef CONFIG_X86
>>>>> #define POINTER_SIZE_CAST       u32
>>>>> #endif
>>>>>
>>>>> ...
>>>>>
>>>>> static int designware_i2c_probe(struct udevice *bus)
>>>>> {
>>>>>            struct dw_i2c *priv = dev_get_priv(bus);
>>>>>
>>>>>            if (device_is_on_pci_bus(bus)) {
>>>>> #ifdef CONFIG_DM_PCI
>>>>>                    /* Save base address from PCI BAR */
>>>>>                    priv->regs = (struct i2c_regs *)
>>>>>                            dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
>>>>> PCI_REGION_MEM);
>>>>> #ifdef CONFIG_X86
>>>>>                    /* Use BayTrail specific timing values */
>>>>>                    priv->scl_sda_cfg = &byt_config;
>>>>> #endif
>>>>> #endif
>>>>>            } else {
>>>>>                    /* Save base address from device-tree */
>>>>>                    priv->regs = (struct i2c_regs
>>>>> *)(POINTER_SIZE_CAST)dev_get_addr(bus);
>>>>>            }
>>>>>
>>>>> But I'm not 100% happy with this approach.
>>>>>
>>>>
>>>> Yes, it's annoying.
>>>>
>>>>> So what are the alternatives:
>>>>>
>>>>> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
>>>>>       done in v1
>>>>>
>>>>> b) This approach with POINTER_SIZE_CAST
>>>>>
>>>>> Any preferences of other ideas?
>>>>>
>>>>> Side note: My general feeling is, that dev_get_addr() should be able to
>>>>> get cast into a pointer on all platforms. This is how it is used in many
>>>>> drivers, btw. Since this is not possible on x86, we might have a problem
>>>>> here. Simon might have some ideas on this as well...
>>>>>
>>>>
>>>> I would like to hear Simon's input. Simon?
>>>
>>>
>>> Yes, Simon, what do you think?
>>>
>>> Please also see my v2 of this patch which uses (__UINTPTR_TYPE__)
>>> for the cast:
>>>
>>> https://patchwork.ozlabs.org/patch/601113/
>>
>>
>> Simon, could you please take a quick look at this patch? With the
>> general problem of dev_get_addr() on x86 (as described above). Do you
>> have some other suggestions to solve this? Or is the solution in
>> v2 which uses (__UINTPTR_TYPE__) acceptable?
>>
>> https://patchwork.ozlabs.org/patch/601113/
> 
> I feel that you should store the return value from dev_get_addr() in
> an fdt_addr_t or a ulong. Then it can be cast to a pointer as you
> wish. Platform data should hold the ulong, and private data
> (dev_get_priv()) should hold the pointer.
> 
> I'm not keen on the POINTER_SIZE_CAST idea.
> 
> Does that fix the problem?

Yes, it does. In a somewhat less ugly way. This is my current result:

	} else {
		ulong base;

		/* Save base address from device-tree */

		/*
		 * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit.
		 * So assigning the register base directly in dev_get_addr()
		 * results in this compilation warning:
		 *   warning: cast to pointer from integer of different size
		 *
		 * Using an intermediate "ulong" variable before assigning
		 * this pointer to the "regs" variable solves this issue.
		 */
		base = dev_get_addr(bus);
		priv->regs = (struct i2c_regs *)base;
	}

If you think this is acceptable, I'll send a new patch version to
the list.

Thanks,
Stefan
Simon Glass April 20, 2016, 3:09 p.m. UTC | #13
Hi Stefan,

On 20 April 2016 at 08:58, Stefan Roese <sr@denx.de> wrote:
>
> Hi Simon.
>
> On 20.04.2016 16:40, Simon Glass wrote:
>
> > On 11 April 2016 at 09:03, Stefan Roese <sr@denx.de> wrote:
> >> Hi Simon,
> >>
> >>
> >> On 04.04.2016 16:53, Stefan Roese wrote:
> >>>
> >>> Hi Simon,
> >>>
> >>> as you seem to be back from vacation (?), we (Bin and myself) would
> >>> like to hear your expert comment on a x86 issue I've discovered
> >>> while porting the Designware I2C driver to x86. Please see below:
> >>>
> >>> On 28.03.2016 08:01, Bin Meng wrote:
> >>>>
> >>>> Hi Stefan,
> >>>>
> >>>> On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
> >>>>>
> >>>>> Hi Bin,
> >>>>>
> >>>>> On 21.03.2016 13:43, Bin Meng wrote:
> >>>>>>
> >>>>>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
> >>>>>>>
> >>>>>>> Hi Bin,
> >>>>>>>
> >>>>>>> On 21.03.2016 10:03, Stefan Roese wrote:
> >>>>>>>
> >>>>>>> <snip>
> >>>>>>>
> >>>>>>>>>>        static int designware_i2c_probe_chip(struct udevice *bus,
> >>>>>>>>>> uint chip_addr,
> >>>>>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct
> >>>>>>>>>> udevice *bus)
> >>>>>>>>>>        {
> >>>>>>>>>>               struct dw_i2c *priv = dev_get_priv(bus);
> >>>>>>>>>>
> >>>>>>>>>> +#ifdef CONFIG_X86
> >>>>>>>>>> +       /* Save base address from PCI BAR */
> >>>>>>>>>> +       priv->regs = (struct i2c_regs *)
> >>>>>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
> >>>>>>>>>> PCI_REGION_MEM);
> >>>>>>>>>> +       /* Use BayTrail specific timing values */
> >>>>>>>>>> +       priv->scl_sda_cfg = &byt_config;
> >>>>>>>>>> +#else
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> How about:
> >>>>>>>>>
> >>>>>>>>>           if (device_is_on_pci_bus(dev)) {
> >>>>>>>>>           do the PCI I2C stuff here;
> >>>>>>>>>           }
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> I've tried this but it generated compilation errors on socfpga, as
> >>>>>>>> the
> >>>>>>>> dm_pci_xxx functions are not available there. So it definitely needs
> >>>>>>>> some #ifdef here. I could go with your suggestion and use
> >>>>>>>> #if CONFIG_DM_PCI as well.
> >>>>>>>>
> >>>>>>>>> See driver/net/designware.c for example.
> >>>>>>>>>
> >>>>>>>>>>               /* Save base address from device-tree */
> >>>>>>>>>>               priv->regs = (struct i2c_regs *)dev_get_addr(bus);
> >>>>>>>>>> +#endif
> >>>>>>>
> >>>>>>>
> >>>>>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
> >>>>>>> in this ugly compilation warning:
> >>>>>>>
> >>>>>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
> >>>>>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from
> >>>>>>> integer of different size [-Wint-to-pointer-cast]
> >>>>>>>        priv->regs = (struct i2c_regs *)dev_get_addr(bus);
> >>>>>>>                     ^
> >>>>>>>
> >>>>>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
> >>>>>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
> >>>>>>> been used anywhere here at all? Should we perhaps go back to
> >>>>>>> a 32bit phy_addr representation again? So that dev_get_addr()
> >>>>>>> matches the (void *) size again?
> >>>>>>>
> >>>>>>
> >>>>>> dev_get_addr() is being used on x86 drivers. See
> >>>>>> ns16550_serial_ofdata_to_platdata() for example. There is no build
> >>>>>> warning for the ns16550 driver.
> >>>>>
> >>>>>
> >>>>> Looking closer, the warning does not occur here, since the registers
> >>>>> are stored in a u32 variable "base". And assigning a 64bit value to a
> >>>>> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
> >>>>> warnings.
> >>>>>
> >>>>> Here in the I2C driver though, the base address is stored as a pointer
> >>>>> (pointer size is 32 bit for x86). And this triggers this warning, even
> >>>>> though its effectively the same assignment. I could cast to u32 but this
> >>>>> would cause problems on 64 bit architectures using this driver (in the
> >>>>> future). So I came up with this approach:
> >>>>
> >>>>
> >>>> Thanks for digging out these.
> >>>>
> >>>>>
> >>>>> /*
> >>>>>     * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning
> >>>>> the
> >>>>>     * register base directly in dev_get_addr() results in this
> >>>>> compilation warning:
> >>>>>     *     warning: cast to pointer from integer of different size
> >>>>>     *
> >>>>>     * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
> >>>>>     * dev_get_addr() into a 32bit value before casting it to the pointer
> >>>>>     * (struct i2c_regs *).
> >>>>>     */
> >>>>> #ifdef CONFIG_X86
> >>>>> #define POINTER_SIZE_CAST       u32
> >>>>> #endif
> >>>>>
> >>>>> ...
> >>>>>
> >>>>> static int designware_i2c_probe(struct udevice *bus)
> >>>>> {
> >>>>>            struct dw_i2c *priv = dev_get_priv(bus);
> >>>>>
> >>>>>            if (device_is_on_pci_bus(bus)) {
> >>>>> #ifdef CONFIG_DM_PCI
> >>>>>                    /* Save base address from PCI BAR */
> >>>>>                    priv->regs = (struct i2c_regs *)
> >>>>>                            dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
> >>>>> PCI_REGION_MEM);
> >>>>> #ifdef CONFIG_X86
> >>>>>                    /* Use BayTrail specific timing values */
> >>>>>                    priv->scl_sda_cfg = &byt_config;
> >>>>> #endif
> >>>>> #endif
> >>>>>            } else {
> >>>>>                    /* Save base address from device-tree */
> >>>>>                    priv->regs = (struct i2c_regs
> >>>>> *)(POINTER_SIZE_CAST)dev_get_addr(bus);
> >>>>>            }
> >>>>>
> >>>>> But I'm not 100% happy with this approach.
> >>>>>
> >>>>
> >>>> Yes, it's annoying.
> >>>>
> >>>>> So what are the alternatives:
> >>>>>
> >>>>> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
> >>>>>       done in v1
> >>>>>
> >>>>> b) This approach with POINTER_SIZE_CAST
> >>>>>
> >>>>> Any preferences of other ideas?
> >>>>>
> >>>>> Side note: My general feeling is, that dev_get_addr() should be able to
> >>>>> get cast into a pointer on all platforms. This is how it is used in many
> >>>>> drivers, btw. Since this is not possible on x86, we might have a problem
> >>>>> here. Simon might have some ideas on this as well...
> >>>>>
> >>>>
> >>>> I would like to hear Simon's input. Simon?
> >>>
> >>>
> >>> Yes, Simon, what do you think?
> >>>
> >>> Please also see my v2 of this patch which uses (__UINTPTR_TYPE__)
> >>> for the cast:
> >>>
> >>> https://patchwork.ozlabs.org/patch/601113/
> >>
> >>
> >> Simon, could you please take a quick look at this patch? With the
> >> general problem of dev_get_addr() on x86 (as described above). Do you
> >> have some other suggestions to solve this? Or is the solution in
> >> v2 which uses (__UINTPTR_TYPE__) acceptable?
> >>
> >> https://patchwork.ozlabs.org/patch/601113/
> >
> > I feel that you should store the return value from dev_get_addr() in
> > an fdt_addr_t or a ulong. Then it can be cast to a pointer as you
> > wish. Platform data should hold the ulong, and private data
> > (dev_get_priv()) should hold the pointer.
> >
> > I'm not keen on the POINTER_SIZE_CAST idea.
> >
> > Does that fix the problem?
>
> Yes, it does. In a somewhat less ugly way. This is my current result:
>
>         } else {
>                 ulong base;
>
>                 /* Save base address from device-tree */
>
>                 /*
>                  * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit.
>                  * So assigning the register base directly in dev_get_addr()
>                  * results in this compilation warning:
>                  *   warning: cast to pointer from integer of different size
>                  *
>                  * Using an intermediate "ulong" variable before assigning
>                  * this pointer to the "regs" variable solves this issue.
>                  */
>                 base = dev_get_addr(bus);
>                 priv->regs = (struct i2c_regs *)base;
>         }
>
> If you think this is acceptable, I'll send a new patch version to
> the list.

Seems fine to me. Perhaps we should have dev_get_addr_ptr() to do this for us?

Regards,
Simon
Stefan Roese April 20, 2016, 3:17 p.m. UTC | #14
Hi Simon,

On 20.04.2016 17:09, Simon Glass wrote:
> Hi Stefan,
>
> On 20 April 2016 at 08:58, Stefan Roese <sr@denx.de> wrote:
>>
>> Hi Simon.
>>
>> On 20.04.2016 16:40, Simon Glass wrote:
>>
>>> On 11 April 2016 at 09:03, Stefan Roese <sr@denx.de> wrote:
>>>> Hi Simon,
>>>>
>>>>
>>>> On 04.04.2016 16:53, Stefan Roese wrote:
>>>>>
>>>>> Hi Simon,
>>>>>
>>>>> as you seem to be back from vacation (?), we (Bin and myself) would
>>>>> like to hear your expert comment on a x86 issue I've discovered
>>>>> while porting the Designware I2C driver to x86. Please see below:
>>>>>
>>>>> On 28.03.2016 08:01, Bin Meng wrote:
>>>>>>
>>>>>> Hi Stefan,
>>>>>>
>>>>>> On Mon, Mar 21, 2016 at 10:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>>>>
>>>>>>> Hi Bin,
>>>>>>>
>>>>>>> On 21.03.2016 13:43, Bin Meng wrote:
>>>>>>>>
>>>>>>>> On Mon, Mar 21, 2016 at 8:04 PM, Stefan Roese <sr@denx.de> wrote:
>>>>>>>>>
>>>>>>>>> Hi Bin,
>>>>>>>>>
>>>>>>>>> On 21.03.2016 10:03, Stefan Roese wrote:
>>>>>>>>>
>>>>>>>>> <snip>
>>>>>>>>>
>>>>>>>>>>>>         static int designware_i2c_probe_chip(struct udevice *bus,
>>>>>>>>>>>> uint chip_addr,
>>>>>>>>>>>> @@ -476,14 +519,45 @@ static int designware_i2c_probe(struct
>>>>>>>>>>>> udevice *bus)
>>>>>>>>>>>>         {
>>>>>>>>>>>>                struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>>>>>>>
>>>>>>>>>>>> +#ifdef CONFIG_X86
>>>>>>>>>>>> +       /* Save base address from PCI BAR */
>>>>>>>>>>>> +       priv->regs = (struct i2c_regs *)
>>>>>>>>>>>> +               dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
>>>>>>>>>>>> PCI_REGION_MEM);
>>>>>>>>>>>> +       /* Use BayTrail specific timing values */
>>>>>>>>>>>> +       priv->scl_sda_cfg = &byt_config;
>>>>>>>>>>>> +#else
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> How about:
>>>>>>>>>>>
>>>>>>>>>>>            if (device_is_on_pci_bus(dev)) {
>>>>>>>>>>>            do the PCI I2C stuff here;
>>>>>>>>>>>            }
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> I've tried this but it generated compilation errors on socfpga, as
>>>>>>>>>> the
>>>>>>>>>> dm_pci_xxx functions are not available there. So it definitely needs
>>>>>>>>>> some #ifdef here. I could go with your suggestion and use
>>>>>>>>>> #if CONFIG_DM_PCI as well.
>>>>>>>>>>
>>>>>>>>>>> See driver/net/designware.c for example.
>>>>>>>>>>>
>>>>>>>>>>>>                /* Save base address from device-tree */
>>>>>>>>>>>>                priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>>>>>>> +#endif
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Enabling this code for x86 via if (device_is_on_pci_bus(dev)) results
>>>>>>>>> in this ugly compilation warning:
>>>>>>>>>
>>>>>>>>> drivers/i2c/designware_i2c.c: In function ‘designware_i2c_probe’:
>>>>>>>>> drivers/i2c/designware_i2c.c:530:16: warning: cast to pointer from
>>>>>>>>> integer of different size [-Wint-to-pointer-cast]
>>>>>>>>>         priv->regs = (struct i2c_regs *)dev_get_addr(bus);
>>>>>>>>>                      ^
>>>>>>>>>
>>>>>>>>> This is because x86 defines fdt_addr_t / phys_addr_t as 64bit. So
>>>>>>>>> I'm wondering, how dev_get_addr() should get used on x86. Has it
>>>>>>>>> been used anywhere here at all? Should we perhaps go back to
>>>>>>>>> a 32bit phy_addr representation again? So that dev_get_addr()
>>>>>>>>> matches the (void *) size again?
>>>>>>>>>
>>>>>>>>
>>>>>>>> dev_get_addr() is being used on x86 drivers. See
>>>>>>>> ns16550_serial_ofdata_to_platdata() for example. There is no build
>>>>>>>> warning for the ns16550 driver.
>>>>>>>
>>>>>>>
>>>>>>> Looking closer, the warning does not occur here, since the registers
>>>>>>> are stored in a u32 variable "base". And assigning a 64bit value to a
>>>>>>> 32bit variable as in "plat->base = addr" in ns16550.c does not cause any
>>>>>>> warnings.
>>>>>>>
>>>>>>> Here in the I2C driver though, the base address is stored as a pointer
>>>>>>> (pointer size is 32 bit for x86). And this triggers this warning, even
>>>>>>> though its effectively the same assignment. I could cast to u32 but this
>>>>>>> would cause problems on 64 bit architectures using this driver (in the
>>>>>>> future). So I came up with this approach:
>>>>>>
>>>>>>
>>>>>> Thanks for digging out these.
>>>>>>
>>>>>>>
>>>>>>> /*
>>>>>>>      * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit. So assigning
>>>>>>> the
>>>>>>>      * register base directly in dev_get_addr() results in this
>>>>>>> compilation warning:
>>>>>>>      *     warning: cast to pointer from integer of different size
>>>>>>>      *
>>>>>>>      * Using this macro POINTER_SIZE_CAST, allows us to cast the result of
>>>>>>>      * dev_get_addr() into a 32bit value before casting it to the pointer
>>>>>>>      * (struct i2c_regs *).
>>>>>>>      */
>>>>>>> #ifdef CONFIG_X86
>>>>>>> #define POINTER_SIZE_CAST       u32
>>>>>>> #endif
>>>>>>>
>>>>>>> ...
>>>>>>>
>>>>>>> static int designware_i2c_probe(struct udevice *bus)
>>>>>>> {
>>>>>>>             struct dw_i2c *priv = dev_get_priv(bus);
>>>>>>>
>>>>>>>             if (device_is_on_pci_bus(bus)) {
>>>>>>> #ifdef CONFIG_DM_PCI
>>>>>>>                     /* Save base address from PCI BAR */
>>>>>>>                     priv->regs = (struct i2c_regs *)
>>>>>>>                             dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0,
>>>>>>> PCI_REGION_MEM);
>>>>>>> #ifdef CONFIG_X86
>>>>>>>                     /* Use BayTrail specific timing values */
>>>>>>>                     priv->scl_sda_cfg = &byt_config;
>>>>>>> #endif
>>>>>>> #endif
>>>>>>>             } else {
>>>>>>>                     /* Save base address from device-tree */
>>>>>>>                     priv->regs = (struct i2c_regs
>>>>>>> *)(POINTER_SIZE_CAST)dev_get_addr(bus);
>>>>>>>             }
>>>>>>>
>>>>>>> But I'm not 100% happy with this approach.
>>>>>>>
>>>>>>
>>>>>> Yes, it's annoying.
>>>>>>
>>>>>>> So what are the alternatives:
>>>>>>>
>>>>>>> a) Don't compile the  dev_get_addr() part for x86 similar to what I've
>>>>>>>        done in v1
>>>>>>>
>>>>>>> b) This approach with POINTER_SIZE_CAST
>>>>>>>
>>>>>>> Any preferences of other ideas?
>>>>>>>
>>>>>>> Side note: My general feeling is, that dev_get_addr() should be able to
>>>>>>> get cast into a pointer on all platforms. This is how it is used in many
>>>>>>> drivers, btw. Since this is not possible on x86, we might have a problem
>>>>>>> here. Simon might have some ideas on this as well...
>>>>>>>
>>>>>>
>>>>>> I would like to hear Simon's input. Simon?
>>>>>
>>>>>
>>>>> Yes, Simon, what do you think?
>>>>>
>>>>> Please also see my v2 of this patch which uses (__UINTPTR_TYPE__)
>>>>> for the cast:
>>>>>
>>>>> https://patchwork.ozlabs.org/patch/601113/
>>>>
>>>>
>>>> Simon, could you please take a quick look at this patch? With the
>>>> general problem of dev_get_addr() on x86 (as described above). Do you
>>>> have some other suggestions to solve this? Or is the solution in
>>>> v2 which uses (__UINTPTR_TYPE__) acceptable?
>>>>
>>>> https://patchwork.ozlabs.org/patch/601113/
>>>
>>> I feel that you should store the return value from dev_get_addr() in
>>> an fdt_addr_t or a ulong. Then it can be cast to a pointer as you
>>> wish. Platform data should hold the ulong, and private data
>>> (dev_get_priv()) should hold the pointer.
>>>
>>> I'm not keen on the POINTER_SIZE_CAST idea.
>>>
>>> Does that fix the problem?
>>
>> Yes, it does. In a somewhat less ugly way. This is my current result:
>>
>>          } else {
>>                  ulong base;
>>
>>                  /* Save base address from device-tree */
>>
>>                  /*
>>                   * On x86, "fdt_addr_t" is 64bit but "void *" only 32bit.
>>                   * So assigning the register base directly in dev_get_addr()
>>                   * results in this compilation warning:
>>                   *   warning: cast to pointer from integer of different size
>>                   *
>>                   * Using an intermediate "ulong" variable before assigning
>>                   * this pointer to the "regs" variable solves this issue.
>>                   */
>>                  base = dev_get_addr(bus);
>>                  priv->regs = (struct i2c_regs *)base;
>>          }
>>
>> If you think this is acceptable, I'll send a new patch version to
>> the list.
>
> Seems fine to me. Perhaps we should have dev_get_addr_ptr() to do
> this for us?

Might make sense. I can generate a small patch for this.

Perhaps we should better use "uintptr_t" as type for the intermediate
variable instead. But then we can effectively drop the intermediate
variable and do the casting directly.

What do you think?

Thanks,
Stefan
diff mbox

Patch

diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index 4e5340d..f7f2eba 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -8,11 +8,32 @@ 
 #include <common.h>
 #include <dm.h>
 #include <i2c.h>
+#include <pci.h>
 #include <asm/io.h>
 #include "designware_i2c.h"
 
+struct dw_scl_sda_cfg {
+	u32 ss_hcnt;
+	u32 fs_hcnt;
+	u32 ss_lcnt;
+	u32 fs_lcnt;
+	u32 sda_hold;
+};
+
+#ifdef CONFIG_X86
+/* BayTrail HCNT/LCNT/SDA hold time */
+static struct dw_scl_sda_cfg byt_config = {
+	.ss_hcnt = 0x200,
+	.fs_hcnt = 0x55,
+	.ss_lcnt = 0x200,
+	.fs_lcnt = 0x99,
+	.sda_hold = 0x6,
+};
+#endif
+
 struct dw_i2c {
 	struct i2c_regs *regs;
+	struct dw_scl_sda_cfg *scl_sda_cfg;
 };
 
 static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
@@ -42,6 +63,7 @@  static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
  * Set the i2c speed.
  */
 static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
+					   struct dw_scl_sda_cfg *scl_sda_cfg,
 					   unsigned int speed)
 {
 	unsigned int cntl;
@@ -61,34 +83,55 @@  static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
 	cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK));
 
 	switch (i2c_spd) {
+#ifndef CONFIG_X86 /* No High-speed for BayTrail yet */
 	case IC_SPEED_MODE_MAX:
-		cntl |= IC_CON_SPD_HS;
-		hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
+		cntl |= IC_CON_SPD_SS;
+		if (scl_sda_cfg) {
+			hcnt = scl_sda_cfg->fs_hcnt;
+			lcnt = scl_sda_cfg->fs_lcnt;
+		} else {
+			hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
+			lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
+		}
 		writel(hcnt, &i2c_base->ic_hs_scl_hcnt);
-		lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
 		writel(lcnt, &i2c_base->ic_hs_scl_lcnt);
 		break;
+#endif
 
 	case IC_SPEED_MODE_STANDARD:
 		cntl |= IC_CON_SPD_SS;
-		hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
+		if (scl_sda_cfg) {
+			hcnt = scl_sda_cfg->ss_hcnt;
+			lcnt = scl_sda_cfg->ss_lcnt;
+		} else {
+			hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
+			lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
+		}
 		writel(hcnt, &i2c_base->ic_ss_scl_hcnt);
-		lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
 		writel(lcnt, &i2c_base->ic_ss_scl_lcnt);
 		break;
 
 	case IC_SPEED_MODE_FAST:
 	default:
 		cntl |= IC_CON_SPD_FS;
-		hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
+		if (scl_sda_cfg) {
+			hcnt = scl_sda_cfg->fs_hcnt;
+			lcnt = scl_sda_cfg->fs_lcnt;
+		} else {
+			hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
+			lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
+		}
 		writel(hcnt, &i2c_base->ic_fs_scl_hcnt);
-		lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
 		writel(lcnt, &i2c_base->ic_fs_scl_lcnt);
 		break;
 	}
 
 	writel(cntl, &i2c_base->ic_con);
 
+	/* Configure SDA Hold Time if required */
+	if (scl_sda_cfg)
+		writel(scl_sda_cfg->sda_hold, &i2c_base->ic_sda_hold);
+
 	/* Enable back i2c now speed set */
 	dw_i2c_enable(i2c_base, 1);
 
@@ -315,7 +358,7 @@  static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
 	writel(IC_TX_TL, &i2c_base->ic_tx_tl);
 	writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
 #ifndef CONFIG_DM_I2C
-	__dw_i2c_set_bus_speed(i2c_base, speed);
+	__dw_i2c_set_bus_speed(i2c_base, NULL, speed);
 	writel(slaveaddr, &i2c_base->ic_sar);
 #endif
 
@@ -356,7 +399,7 @@  static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap,
 					 unsigned int speed)
 {
 	adap->speed = speed;
-	return __dw_i2c_set_bus_speed(i2c_get_base(adap), speed);
+	return __dw_i2c_set_bus_speed(i2c_get_base(adap), NULL, speed);
 }
 
 static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
@@ -451,7 +494,7 @@  static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 {
 	struct dw_i2c *i2c = dev_get_priv(bus);
 
-	return __dw_i2c_set_bus_speed(i2c->regs, speed);
+	return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed);
 }
 
 static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
@@ -476,14 +519,45 @@  static int designware_i2c_probe(struct udevice *bus)
 {
 	struct dw_i2c *priv = dev_get_priv(bus);
 
+#ifdef CONFIG_X86
+	/* Save base address from PCI BAR */
+	priv->regs = (struct i2c_regs *)
+		dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
+	/* Use BayTrail specific timing values */
+	priv->scl_sda_cfg = &byt_config;
+#else
 	/* Save base address from device-tree */
 	priv->regs = (struct i2c_regs *)dev_get_addr(bus);
+#endif
 
 	__dw_i2c_init(priv->regs, 0, 0);
 
 	return 0;
 }
 
+static int designware_i2c_bind(struct udevice *dev)
+{
+	static int num_cards;
+	char name[20];
+
+	/* Create a unique device name for PCI type devices */
+	if (device_is_on_pci_bus(dev)) {
+		/*
+		 * ToDo:
+		 * Setting req_seq in the driver is probably not recommended.
+		 * But without a DT alias the number is not configured. And
+		 * using this driver is impossible for PCIe I2C devices.
+		 * This can be removed, once a better (correct) way for this
+		 * is found and implemented.
+		 */
+		dev->req_seq = num_cards;
+		sprintf(name, "i2c_designware#%u", num_cards++);
+		device_set_name(dev, name);
+	}
+
+	return 0;
+}
+
 static const struct dm_i2c_ops designware_i2c_ops = {
 	.xfer		= designware_i2c_xfer,
 	.probe_chip	= designware_i2c_probe_chip,
@@ -499,9 +573,26 @@  U_BOOT_DRIVER(i2c_designware) = {
 	.name	= "i2c_designware",
 	.id	= UCLASS_I2C,
 	.of_match = designware_i2c_ids,
+	.bind	= designware_i2c_bind,
 	.probe	= designware_i2c_probe,
 	.priv_auto_alloc_size = sizeof(struct dw_i2c),
 	.ops	= &designware_i2c_ops,
 };
 
+#ifdef CONFIG_X86
+static struct pci_device_id designware_pci_supported[] = {
+	/* Intel BayTrail has 7 I2C controller located on the PCI bus */
+	{ PCI_VDEVICE(INTEL, 0x0f41) },
+	{ PCI_VDEVICE(INTEL, 0x0f42) },
+	{ PCI_VDEVICE(INTEL, 0x0f43) },
+	{ PCI_VDEVICE(INTEL, 0x0f44) },
+	{ PCI_VDEVICE(INTEL, 0x0f45) },
+	{ PCI_VDEVICE(INTEL, 0x0f46) },
+	{ PCI_VDEVICE(INTEL, 0x0f47) },
+	{},
+};
+
+U_BOOT_PCI_DEVICE(i2c_designware, designware_pci_supported);
+#endif
+
 #endif /* CONFIG_DM_I2C */