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

Bin Meng bmeng.cn at gmail.com
Mon Mar 21 09:54:53 CET 2016


Hi Stefan,

On Fri, Mar 18, 2016 at 3:54 PM, Stefan Roese <sr at 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 at denx.de>
> Cc: Simon Glass <sjg at chromium.org>
> Cc: Bin Meng <bmeng.cn at gmail.com>
> Cc: Marek Vasut <marex at denx.de>
> Cc: Heiko Schocher <hs at 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


More information about the U-Boot mailing list