[U-Boot] [PATCH 6/6] i2c: designware_i2c: Add support for PCI(e) based I2C cores (x86)
Stefan Roese
sr at denx.de
Mon Mar 21 10:03:14 CET 2016
Hi Bin,
On 21.03.2016 09:54, Bin Meng wrote:
> 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;
> }
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
More information about the U-Boot
mailing list