diff options
-rw-r--r-- | Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt | 25 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/i2c.h | 4 | ||||
-rw-r--r-- | arch/mips/include/asm/mach-au1x00/au1xxx_psc.h | 13 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 16 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 5 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-au1550.c | 280 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware-core.c (renamed from drivers/i2c/busses/i2c-designware.c) | 382 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware-core.h | 105 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware-pcidrv.c | 392 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware-platdrv.c | 215 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-eg20t.c | 270 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 46 |
12 files changed, 1206 insertions, 547 deletions
diff --git a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt new file mode 100644 index 000000000000..f3cf43b66f7e --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt @@ -0,0 +1,25 @@ +* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX + +Required properties: +- compatible : Should be "fsl,<chip>-i2c" +- reg : Should contain I2C/HS-I2C registers location and length +- interrupts : Should contain I2C/HS-I2C interrupt + +Optional properties: +- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. + The absence of the propoerty indicates the default frequency 100 kHz. + +Examples: + +i2c@83fc4000 { /* I2C2 on i.MX51 */ + compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + reg = <0x83fc4000 0x4000>; + interrupts = <63>; +}; + +i2c@70038000 { /* HS-I2C on i.MX51 */ + compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + reg = <0x70038000 0x4000>; + interrupts = <64>; + clock-frequency = <400000>; +}; diff --git a/arch/arm/plat-mxc/include/mach/i2c.h b/arch/arm/plat-mxc/include/mach/i2c.h index 4a5dc5c6d8e8..375cdd0cf876 100644 --- a/arch/arm/plat-mxc/include/mach/i2c.h +++ b/arch/arm/plat-mxc/include/mach/i2c.h @@ -11,14 +11,10 @@ /** * struct imxi2c_platform_data - structure of platform data for MXC I2C driver - * @init: Initialise gpio's and other board specific things - * @exit: Free everything initialised by @init * @bitrate: Bus speed measured in Hz * **/ struct imxi2c_platform_data { - int (*init)(struct device *dev); - void (*exit)(struct device *dev); int bitrate; }; diff --git a/arch/mips/include/asm/mach-au1x00/au1xxx_psc.h b/arch/mips/include/asm/mach-au1x00/au1xxx_psc.h index 892b7f168eb4..5a5cb7386427 100644 --- a/arch/mips/include/asm/mach-au1x00/au1xxx_psc.h +++ b/arch/mips/include/asm/mach-au1x00/au1xxx_psc.h @@ -394,19 +394,6 @@ typedef struct psc_spi { #define PSC_SPITXRX_LC (1 << 29) #define PSC_SPITXRX_SR (1 << 28) -/* PSC in SMBus (I2C) Mode. */ -typedef struct psc_smb { - u32 psc_sel; - u32 psc_ctrl; - u32 psc_smbcfg; - u32 psc_smbmsk; - u32 psc_smbpcr; - u32 psc_smbstat; - u32 psc_smbevnt; - u32 psc_smbtxrx; - u32 psc_smbtmr; -} psc_smb_t; - /* SMBus Config Register. */ #define PSC_SMBCFG_RT_MASK (3 << 30) #define PSC_SMBCFG_RT_FIFO1 (0 << 30) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 646068e5100b..1d7ce9c18bff 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -350,15 +350,25 @@ config I2C_DAVINCI devices such as DaVinci NIC. For details please see http://www.ti.com/davinci -config I2C_DESIGNWARE - tristate "Synopsys DesignWare" +config I2C_DESIGNWARE_PLATFORM + tristate "Synopsys DesignWare Platfrom" depends on HAVE_CLK help If you say yes to this option, support will be included for the Synopsys DesignWare I2C adapter. Only master mode is supported. This driver can also be built as a module. If so, the module - will be called i2c-designware. + will be called i2c-designware-platform. + +config I2C_DESIGNWARE_PCI + tristate "Synopsys DesignWare PCI" + depends on PCI + help + If you say yes to this option, support will be included for the + Synopsys DesignWare I2C adapter. Only master mode is supported. + + This driver can also be built as a module. If so, the module + will be called i2c-designware-pci. config I2C_GPIO tristate "GPIO-based bitbanging I2C" diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e6cf294d3729..fba6da60aa0e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,7 +33,10 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o -obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o +obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o +i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o +obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o +i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c index 532828bc50e6..4f757a2da8cc 100644 --- a/drivers/i2c/busses/i2c-au1550.c +++ b/drivers/i2c/busses/i2c-au1550.c @@ -39,29 +39,41 @@ #include <asm/mach-au1x00/au1xxx.h> #include <asm/mach-au1x00/au1xxx_psc.h> +#define PSC_SEL 0x00 +#define PSC_CTRL 0x04 +#define PSC_SMBCFG 0x08 +#define PSC_SMBMSK 0x0C +#define PSC_SMBPCR 0x10 +#define PSC_SMBSTAT 0x14 +#define PSC_SMBEVNT 0x18 +#define PSC_SMBTXRX 0x1C +#define PSC_SMBTMR 0x20 + struct i2c_au1550_data { - u32 psc_base; + void __iomem *psc_base; int xfer_timeout; - int ack_timeout; struct i2c_adapter adap; struct resource *ioarea; }; -static int -wait_xfer_done(struct i2c_au1550_data *adap) +static inline void WR(struct i2c_au1550_data *a, int r, unsigned long v) { - u32 stat; - int i; - volatile psc_smb_t *sp; + __raw_writel(v, a->psc_base + r); + wmb(); +} - sp = (volatile psc_smb_t *)(adap->psc_base); +static inline unsigned long RD(struct i2c_au1550_data *a, int r) +{ + return __raw_readl(a->psc_base + r); +} - /* Wait for Tx Buffer Empty - */ +static int wait_xfer_done(struct i2c_au1550_data *adap) +{ + int i; + + /* Wait for Tx Buffer Empty */ for (i = 0; i < adap->xfer_timeout; i++) { - stat = sp->psc_smbstat; - au_sync(); - if ((stat & PSC_SMBSTAT_TE) != 0) + if (RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_TE) return 0; udelay(1); @@ -70,41 +82,27 @@ wait_xfer_done(struct i2c_au1550_data *adap) return -ETIMEDOUT; } -static int -wait_ack(struct i2c_au1550_data *adap) +static int wait_ack(struct i2c_au1550_data *adap) { - u32 stat; - volatile psc_smb_t *sp; + unsigned long stat; if (wait_xfer_done(adap)) return -ETIMEDOUT; - sp = (volatile psc_smb_t *)(adap->psc_base); - - stat = sp->psc_smbevnt; - au_sync(); - + stat = RD(adap, PSC_SMBEVNT); if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0) return -ETIMEDOUT; return 0; } -static int -wait_master_done(struct i2c_au1550_data *adap) +static int wait_master_done(struct i2c_au1550_data *adap) { - u32 stat; - int i; - volatile psc_smb_t *sp; + int i; - sp = (volatile psc_smb_t *)(adap->psc_base); - - /* Wait for Master Done. - */ - for (i = 0; i < adap->xfer_timeout; i++) { - stat = sp->psc_smbevnt; - au_sync(); - if ((stat & PSC_SMBEVNT_MD) != 0) + /* Wait for Master Done. */ + for (i = 0; i < 2 * adap->xfer_timeout; i++) { + if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0) return 0; udelay(1); } @@ -115,29 +113,20 @@ wait_master_done(struct i2c_au1550_data *adap) static int do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q) { - volatile psc_smb_t *sp; - u32 stat; + unsigned long stat; - sp = (volatile psc_smb_t *)(adap->psc_base); - - /* Reset the FIFOs, clear events. - */ - stat = sp->psc_smbstat; - sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR; - au_sync(); + /* Reset the FIFOs, clear events. */ + stat = RD(adap, PSC_SMBSTAT); + WR(adap, PSC_SMBEVNT, PSC_SMBEVNT_ALLCLR); if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) { - sp->psc_smbpcr = PSC_SMBPCR_DC; - au_sync(); - do { - stat = sp->psc_smbpcr; - au_sync(); - } while ((stat & PSC_SMBPCR_DC) != 0); + WR(adap, PSC_SMBPCR, PSC_SMBPCR_DC); + while ((RD(adap, PSC_SMBPCR) & PSC_SMBPCR_DC) != 0) + cpu_relax(); udelay(50); } - /* Write out the i2c chip address and specify operation - */ + /* Write out the i2c chip address and specify operation */ addr <<= 1; if (rd) addr |= 1; @@ -146,56 +135,42 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q) if (q) addr |= PSC_SMBTXRX_STP; - /* Put byte into fifo, start up master. - */ - sp->psc_smbtxrx = addr; - au_sync(); - sp->psc_smbpcr = PSC_SMBPCR_MS; - au_sync(); + /* Put byte into fifo, start up master. */ + WR(adap, PSC_SMBTXRX, addr); + WR(adap, PSC_SMBPCR, PSC_SMBPCR_MS); if (wait_ack(adap)) return -EIO; return (q) ? wait_master_done(adap) : 0; } -static u32 -wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data) +static int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out) { - int j; - u32 data, stat; - volatile psc_smb_t *sp; + int j; if (wait_xfer_done(adap)) return -EIO; - sp = (volatile psc_smb_t *)(adap->psc_base); - j = adap->xfer_timeout * 100; do { j--; if (j <= 0) return -EIO; - stat = sp->psc_smbstat; - au_sync(); - if ((stat & PSC_SMBSTAT_RE) == 0) + if ((RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_RE) == 0) j = 0; else udelay(1); } while (j > 0); - data = sp->psc_smbtxrx; - au_sync(); - *ret_data = data; + + *out = RD(adap, PSC_SMBTXRX); return 0; } -static int -i2c_read(struct i2c_au1550_data *adap, unsigned char *buf, +static int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf, unsigned int len) { - int i; - u32 data; - volatile psc_smb_t *sp; + int i; if (len == 0) return 0; @@ -204,62 +179,46 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf, * zero bytes for timing, waiting for bytes to appear in the * receive fifo, then reading the bytes. */ - - sp = (volatile psc_smb_t *)(adap->psc_base); - i = 0; - while (i < (len-1)) { - sp->psc_smbtxrx = 0; - au_sync(); - if (wait_for_rx_byte(adap, &data)) + while (i < (len - 1)) { + WR(adap, PSC_SMBTXRX, 0); + if (wait_for_rx_byte(adap, &buf[i])) return -EIO; - buf[i] = data; i++; } - /* The last byte has to indicate transfer done. - */ - sp->psc_smbtxrx = PSC_SMBTXRX_STP; - au_sync(); + /* The last byte has to indicate transfer done. */ + WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP); if (wait_master_done(adap)) return -EIO; - data = sp->psc_smbtxrx; - au_sync(); - buf[i] = data; + buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff); return 0; } -static int -i2c_write(struct i2c_au1550_data *adap, unsigned char *buf, +static int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf, unsigned int len) { - int i; - u32 data; - volatile psc_smb_t *sp; + int i; + unsigned long data; if (len == 0) return 0; - sp = (volatile psc_smb_t *)(adap->psc_base); - i = 0; while (i < (len-1)) { data = buf[i]; - sp->psc_smbtxrx = data; - au_sync(); + WR(adap, PSC_SMBTXRX, data); if (wait_ack(adap)) return -EIO; i++; } - /* The last byte has to indicate transfer done. - */ + /* The last byte has to indicate transfer done. */ data = buf[i]; data |= PSC_SMBTXRX_STP; - sp->psc_smbtxrx = data; - au_sync(); + WR(adap, PSC_SMBTXRX, data); if (wait_master_done(adap)) return -EIO; return 0; @@ -269,12 +228,10 @@ static int au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { struct i2c_au1550_data *adap = i2c_adap->algo_data; - volatile psc_smb_t *sp = (volatile psc_smb_t *)adap->psc_base; struct i2c_msg *p; int i, err = 0; - sp->psc_ctrl = PSC_CTRL_ENABLE; - au_sync(); + WR(adap, PSC_CTRL, PSC_CTRL_ENABLE); for (i = 0; !err && i < num; i++) { p = &msgs[i]; @@ -293,14 +250,12 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) if (err == 0) err = num; - sp->psc_ctrl = PSC_CTRL_SUSPEND; - au_sync(); + WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND); return err; } -static u32 -au1550_func(struct i2c_adapter *adap) +static u32 au1550_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } @@ -312,57 +267,45 @@ static const struct i2c_algorithm au1550_algo = { static void i2c_au1550_setup(struct i2c_au1550_data *priv) { - volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; - u32 stat; - - sp->psc_ctrl = PSC_CTRL_DISABLE; - au_sync(); - sp->psc_sel = PSC_SEL_PS_SMBUSMODE; - sp->psc_smbcfg = 0; - au_sync(); - sp->psc_ctrl = PSC_CTRL_ENABLE; - au_sync(); - do { - stat = sp->psc_smbstat; - au_sync(); - } while ((stat & PSC_SMBSTAT_SR) == 0); + unsigned long cfg; - sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | - PSC_SMBCFG_DD_DISABLE); + WR(priv, PSC_CTRL, PSC_CTRL_DISABLE); + WR(priv, PSC_SEL, PSC_SEL_PS_SMBUSMODE); + WR(priv, PSC_SMBCFG, 0); + WR(priv, PSC_CTRL, PSC_CTRL_ENABLE); + while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0) + cpu_relax(); + + cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE; + WR(priv, PSC_SMBCFG, cfg); /* Divide by 8 to get a 6.25 MHz clock. The later protocol * timings are based on this clock. */ - sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8); - sp->psc_smbmsk = PSC_SMBMSK_ALLMASK; - au_sync(); + cfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8); + WR(priv, PSC_SMBCFG, cfg); + WR(priv, PSC_SMBMSK, PSC_SMBMSK_ALLMASK); /* Set the protocol timer values. See Table 71 in the * Au1550 Data Book for standard timing values. */ - sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \ + WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \ PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \ PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \ - PSC_SMBTMR_SET_CH(15); - au_sync(); + PSC_SMBTMR_SET_CH(15)); - sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE; - do { - stat = sp->psc_smbstat; - au_sync(); - } while ((stat & PSC_SMBSTAT_SR) == 0); + cfg |= PSC_SMBCFG_DE_ENABLE; + WR(priv, PSC_SMBCFG, cfg); + while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0) + cpu_relax(); - sp->psc_ctrl = PSC_CTRL_SUSPEND; - au_sync(); + WR(priv, PSC_CTRL, PSC_CTRL_SUSPEND); } static void i2c_au1550_disable(struct i2c_au1550_data *priv) { - volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; - - sp->psc_smbcfg = 0; - sp->psc_ctrl = PSC_CTRL_DISABLE; - au_sync(); + WR(priv, PSC_SMBCFG, 0); + WR(priv, PSC_CTRL, PSC_CTRL_DISABLE); } /* @@ -396,9 +339,12 @@ i2c_au1550_probe(struct platform_device *pdev) goto out_mem; } - priv->psc_base = CKSEG1ADDR(r->start); + priv->psc_base = ioremap(r->start, resource_size(r)); + if (!priv->psc_base) { + ret = -EIO; + goto out_map; + } priv->xfer_timeout = 200; - priv->ack_timeout = 200; priv->adap.nr = pdev->id; priv->adap.algo = &au1550_algo; @@ -406,8 +352,7 @@ i2c_au1550_probe(struct platform_device *pdev) priv->adap.dev.parent = &pdev->dev; strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); - /* Now, set up the PSC for SMBus PIO mode. - */ + /* Now, set up the PSC for SMBus PIO mode. */ i2c_au1550_setup(priv); ret = i2c_add_numbered_adapter(&priv->adap); @@ -417,7 +362,8 @@ i2c_au1550_probe(struct platform_device *pdev) } i2c_au1550_disable(priv); - + iounmap(priv->psc_base); +out_map: release_resource(priv->ioarea); kfree(priv->ioarea); out_mem: @@ -426,14 +372,14 @@ out: return ret; } -static int __devexit -i2c_au1550_remove(struct platform_device *pdev) +static int __devexit i2c_au1550_remove(struct platform_device *pdev) { struct i2c_au1550_data *priv = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); i2c_del_adapter(&priv->adap); i2c_au1550_disable(priv); + iounmap(priv->psc_base); release_resource(priv->ioarea); kfree(priv->ioarea); kfree(priv); @@ -441,49 +387,51 @@ i2c_au1550_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int -i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state) +static int i2c_au1550_suspend(struct device *dev) { - struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + struct i2c_au1550_data *priv = dev_get_drvdata(dev); i2c_au1550_disable(priv); return 0; } -static int -i2c_au1550_resume(struct platform_device *pdev) +static int i2c_au1550_resume(struct device *dev) { - struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + struct i2c_au1550_data *priv = dev_get_drvdata(dev); i2c_au1550_setup(priv); return 0; } + +static const struct dev_pm_ops i2c_au1550_pmops = { + .suspend = i2c_au1550_suspend, + .resume = i2c_au1550_resume, +}; + +#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops) + #else -#define i2c_au1550_suspend NULL -#define i2c_au1550_resume NULL +#define AU1XPSC_SMBUS_PMOPS NULL #endif static struct platform_driver au1xpsc_smbus_driver = { .driver = { .name = "au1xpsc_smbus", .owner = THIS_MODULE, + .pm = AU1XPSC_SMBUS_PMOPS, }, .probe = i2c_au1550_probe, .remove = __devexit_p(i2c_au1550_remove), - .suspend = i2c_au1550_suspend, - .resume = i2c_au1550_resume, }; -static int __init -i2c_au1550_init(void) +static int __init i2c_au1550_init(void) { return platform_driver_register(&au1xpsc_smbus_driver); } -static void __exit -i2c_au1550_exit(void) +static void __exit i2c_au1550_exit(void) { platform_driver_unregister(&au1xpsc_smbus_driver); } diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware-core.c index 1b42b50b5992..df8799241009 100644 --- a/drivers/i2c/busses/i2c-designware.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -25,18 +25,15 @@ * ---------------------------------------------------------------------------- * */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/i2c.h> #include <linux/clk.h> #include <linux/errno.h> -#include <linux/sched.h> #include <linux/err.h> +#include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/platform_device.h> #include <linux/io.h> -#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> +#include "i2c-designware-core.h" /* * Registers offset @@ -68,15 +65,10 @@ #define DW_IC_STATUS 0x70 #define DW_IC_TXFLR 0x74 #define DW_IC_RXFLR 0x78 -#define DW_IC_COMP_PARAM_1 0xf4 #define DW_IC_TX_ABRT_SOURCE 0x80 - -#define DW_IC_CON_MASTER 0x1 -#define DW_IC_CON_SPEED_STD 0x2 -#define DW_IC_CON_SPEED_FAST 0x4 -#define DW_IC_CON_10BITADDR_MASTER 0x10 -#define DW_IC_CON_RESTART_EN 0x20 -#define DW_IC_CON_SLAVE_DISABLE 0x40 +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_COMP_TYPE 0xfc +#define DW_IC_COMP_TYPE_VALUE 0x44570140 #define DW_IC_INTR_RX_UNDER 0x001 #define DW_IC_INTR_RX_OVER 0x002 @@ -170,55 +162,23 @@ static char *abort_sources[] = { "lost arbitration", }; -/** - * struct dw_i2c_dev - private i2c-designware data - * @dev: driver model device node - * @base: IO registers pointer - * @cmd_complete: tx completion indicator - * @lock: protect this struct and IO registers - * @clk: input reference clock - * @cmd_err: run time hadware error code - * @msgs: points to an array of messages currently being transferred - * @msgs_num: the number of elements in msgs - * @msg_write_idx: the element index of the current tx message in the msgs - * array - * @tx_buf_len: the length of the current tx buffer - * @tx_buf: the current tx buffer - * @msg_read_idx: the element index of the current rx message in the msgs - * array - * @rx_buf_len: the length of the current rx buffer - * @rx_buf: the current rx buffer - * @msg_err: error status of the current transfer - * @status: i2c master status, one of STATUS_* - * @abort_source: copy of the TX_ABRT_SOURCE register - * @irq: interrupt number for the i2c master - * @adapter: i2c subsystem adapter node - * @tx_fifo_depth: depth of the hardware tx fifo - * @rx_fifo_depth: depth of the hardware rx fifo - */ -struct dw_i2c_dev { - struct device *dev; - void __iomem *base; - struct completion cmd_complete; - struct mutex lock; - struct clk *clk; - int cmd_err; - struct i2c_msg *msgs; - int msgs_num; - int msg_write_idx; - u32 tx_buf_len; - u8 *tx_buf; - int msg_read_idx; - u32 rx_buf_len; - u8 *rx_buf; - int msg_err; - unsigned int status; - u32 abort_source; - int irq; - struct i2c_adapter adapter; - unsigned int tx_fifo_depth; - unsigned int rx_fifo_depth; -}; +u32 dw_readl(struct dw_i2c_dev *dev, int offset) +{ + u32 value = readl(dev->base + offset); + + if (dev->swab) + return swab32(value); + else + return value; +} + +void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset) +{ + if (dev->swab) + b = swab32(b); + + writel(b, dev->base + offset); +} static u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) @@ -283,13 +243,29 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) * This function is called during I2C init function, and in case of timeout at * run time. */ -static void i2c_dw_init(struct dw_i2c_dev *dev) +int i2c_dw_init(struct dw_i2c_dev *dev) { - u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; - u32 ic_con, hcnt, lcnt; + u32 input_clock_khz; + u32 hcnt, lcnt; + u32 reg; + + input_clock_khz = dev->get_clk_rate_khz(dev); + + /* Configure register endianess access */ + reg = dw_readl(dev, DW_IC_COMP_TYPE); + if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) { + dev->swab = 1; + reg = DW_IC_COMP_TYPE_VALUE; + } + + if (reg != DW_IC_COMP_TYPE_VALUE) { + dev_err(dev->dev, "Unknown Synopsys component type: " + "0x%08x\n", reg); + return -ENODEV; + } /* Disable the adapter */ - writel(0, dev->base + DW_IC_ENABLE); + dw_writel(dev, 0, DW_IC_ENABLE); /* set standard and fast speed deviders for high/low periods */ @@ -303,8 +279,8 @@ static void i2c_dw_init(struct dw_i2c_dev *dev) 47, /* tLOW = 4.7 us */ 3, /* tf = 0.3 us */ 0); /* No offset */ - writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT); - writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT); + dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT); + dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT); dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); /* Fast-mode */ @@ -317,18 +293,17 @@ static void i2c_dw_init(struct dw_i2c_dev *dev) 13, /* tLOW = 1.3 us */ 3, /* tf = 0.3 us */ 0); /* No offset */ - writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT); - writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT); + dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT); + dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT); dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); /* Configure Tx/Rx FIFO threshold levels */ - writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL); - writel(0, dev->base + DW_IC_RX_TL); + dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL); + dw_writel(dev, 0, DW_IC_RX_TL); /* configure the i2c master */ - ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | - DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; - writel(ic_con, dev->base + DW_IC_CON); + dw_writel(dev, dev->master_cfg , DW_IC_CON); + return 0; } /* @@ -338,7 +313,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) { int timeout = TIMEOUT; - while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { + while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { if (timeout <= 0) { dev_warn(dev->dev, "timeout waiting for bus ready\n"); return -ETIMEDOUT; @@ -356,24 +331,24 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) u32 ic_con; /* Disable the adapter */ - writel(0, dev->base + DW_IC_ENABLE); + dw_writel(dev, 0, DW_IC_ENABLE); /* set the slave (target) address */ - writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR); + dw_writel(dev, msgs[dev->msg_write_idx].addr, DW_IC_TAR); /* if the slave address is ten bit address, enable 10BITADDR */ - ic_con = readl(dev->base + DW_IC_CON); + ic_con = dw_readl(dev, DW_IC_CON); if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) ic_con |= DW_IC_CON_10BITADDR_MASTER; else ic_con &= ~DW_IC_CON_10BITADDR_MASTER; - writel(ic_con, dev->base + DW_IC_CON); + dw_writel(dev, ic_con, DW_IC_CON); /* Enable the adapter */ - writel(1, dev->base + DW_IC_ENABLE); + dw_writel(dev, 1, DW_IC_ENABLE); /* Enable interrupts */ - writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK); + dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK); } /* @@ -382,7 +357,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) * messages into the tx buffer. Even if the size of i2c_msg data is * longer than the size of the tx buffer, it handles everything. */ -static void +void i2c_dw_xfer_msg(struct dw_i2c_dev *dev) { struct i2c_msg *msgs = dev->msgs; @@ -420,15 +395,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) buf_len = msgs[dev->msg_write_idx].len; } - tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR); - rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR); + tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR); + rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR); while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { - writel(0x100, dev->base + DW_IC_DATA_CMD); + dw_writel(dev, 0x100, DW_IC_DATA_CMD); rx_limit--; } else - writel(*buf++, dev->base + DW_IC_DATA_CMD); + dw_writel(dev, *buf++, DW_IC_DATA_CMD); tx_limit--; buf_len--; } @@ -453,7 +428,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) if (dev->msg_err) intr_mask = 0; - writel(intr_mask, dev->base + DW_IC_INTR_MASK); + dw_writel(dev, intr_mask, DW_IC_INTR_MASK); } static void @@ -477,10 +452,10 @@ i2c_dw_read(struct dw_i2c_dev *dev) buf = dev->rx_buf; } - rx_valid = readl(dev->base + DW_IC_RXFLR); + rx_valid = dw_readl(dev, DW_IC_RXFLR); for (; len > 0 && rx_valid > 0; len--, rx_valid--) - *buf++ = readl(dev->base + DW_IC_DATA_CMD); + *buf++ = dw_readl(dev, DW_IC_DATA_CMD); if (len > 0) { dev->status |= STATUS_READ_IN_PROGRESS; @@ -518,7 +493,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) /* * Prepare controller for a transaction and call i2c_dw_xfer_msg */ -static int +int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct dw_i2c_dev *dev = i2c_get_adapdata(adap); @@ -527,6 +502,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); mutex_lock(&dev->lock); + pm_runtime_get_sync(dev->dev); INIT_COMPLETION(dev->cmd_complete); dev->msgs = msgs; @@ -563,7 +539,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) /* no error */ if (likely(!dev->cmd_err)) { /* Disable the adapter */ - writel(0, dev->base + DW_IC_ENABLE); + dw_writel(dev, 0, DW_IC_ENABLE); ret = num; goto done; } @@ -576,19 +552,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ret = -EIO; done: + pm_runtime_put(dev->dev); mutex_unlock(&dev->lock); return ret; } -static u32 i2c_dw_func(struct i2c_adapter *adap) +u32 i2c_dw_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | - I2C_FUNC_10BIT_ADDR | - I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_I2C_BLOCK; + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + return dev->functionality; } static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) @@ -601,47 +574,47 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) * in the IC_RAW_INTR_STAT register. * * That is, - * stat = readl(IC_INTR_STAT); + * stat = dw_readl(IC_INTR_STAT); * equals to, - * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); + * stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK); * * The raw version might be useful for debugging purposes. */ - stat = readl(dev->base + DW_IC_INTR_STAT); + stat = dw_readl(dev, DW_IC_INTR_STAT); /* * Do not use the IC_CLR_INTR register to clear interrupts, or * you'll miss some interrupts, triggered during the period from - * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + * dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR). * * Instead, use the separately-prepared IC_CLR_* registers. */ if (stat & DW_IC_INTR_RX_UNDER) - readl(dev->base + DW_IC_CLR_RX_UNDER); + dw_readl(dev, DW_IC_CLR_RX_UNDER); if (stat & DW_IC_INTR_RX_OVER) - readl(dev->base + DW_IC_CLR_RX_OVER); + dw_readl(dev, DW_IC_CLR_RX_OVER); if (stat & DW_IC_INTR_TX_OVER) - readl(dev->base + DW_IC_CLR_TX_OVER); + dw_readl(dev, DW_IC_CLR_TX_OVER); if (stat & DW_IC_INTR_RD_REQ) - readl(dev->base + DW_IC_CLR_RD_REQ); + dw_readl(dev, DW_IC_CLR_RD_REQ); if (stat & DW_IC_INTR_TX_ABRT) { /* * The IC_TX_ABRT_SOURCE register is cleared whenever * the IC_CLR_TX_ABRT is read. Preserve it beforehand. */ - dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE); - readl(dev->base + DW_IC_CLR_TX_ABRT); + dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE); + dw_readl(dev, DW_IC_CLR_TX_ABRT); } if (stat & DW_IC_INTR_RX_DONE) - readl(dev->base + DW_IC_CLR_RX_DONE); + dw_readl(dev, DW_IC_CLR_RX_DONE); if (stat & DW_IC_INTR_ACTIVITY) - readl(dev->base + DW_IC_CLR_ACTIVITY); + dw_readl(dev, DW_IC_CLR_ACTIVITY); if (stat & DW_IC_INTR_STOP_DET) - readl(dev->base + DW_IC_CLR_STOP_DET); + dw_readl(dev, DW_IC_CLR_STOP_DET); if (stat & DW_IC_INTR_START_DET) - readl(dev->base + DW_IC_CLR_START_DET); + dw_readl(dev, DW_IC_CLR_START_DET); if (stat & DW_IC_INTR_GEN_CALL) - readl(dev->base + DW_IC_CLR_GEN_CALL); + dw_readl(dev, DW_IC_CLR_GEN_CALL); return stat; } @@ -650,13 +623,19 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) * Interrupt service routine. This gets called whenever an I2C interrupt * occurs. */ -static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) { struct dw_i2c_dev *dev = dev_id; - u32 stat; + u32 stat, enabled; + + enabled = dw_readl(dev, DW_IC_ENABLE); + stat = dw_readl(dev, DW_IC_RAW_INTR_STAT); + dev_dbg(dev->dev, "%s: %s enabled= 0x%x stat=0x%x\n", __func__, + dev->adapter.name, enabled, stat); + if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY)) + return IRQ_NONE; stat = i2c_dw_read_clear_intrbits(dev); - dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); if (stat & DW_IC_INTR_TX_ABRT) { dev->cmd_err |= DW_IC_ERR_TX_ABRT; @@ -666,7 +645,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) * Anytime TX_ABRT is set, the contents of the tx/rx * buffers are flushed. Make sure to skip them. */ - writel(0, dev->base + DW_IC_INTR_MASK); + dw_writel(dev, 0, DW_IC_INTR_MASK); goto tx_aborted; } @@ -689,159 +668,38 @@ tx_aborted: return IRQ_HANDLED; } -static struct i2c_algorithm i2c_dw_algo = { - .master_xfer = i2c_dw_xfer, - .functionality = i2c_dw_func, -}; - -static int __devinit dw_i2c_probe(struct platform_device *pdev) +void i2c_dw_enable(struct dw_i2c_dev *dev) { - struct dw_i2c_dev *dev; - struct i2c_adapter *adap; - struct resource *mem, *ioarea; - int irq, r; - - /* NOTE: driver uses the static register mapping */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq resource?\n"); - return irq; /* -ENXIO */ - } - - ioarea = request_mem_region(mem->start, resource_size(mem), - pdev->name); - if (!ioarea) { - dev_err(&pdev->dev, "I2C region already claimed\n"); - return -EBUSY; - } - - dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); - if (!dev) { - r = -ENOMEM; - goto err_release_region; - } - - init_completion(&dev->cmd_complete); - mutex_init(&dev->lock); - dev->dev = get_device(&pdev->dev); - dev->irq = irq; - platform_set_drvdata(pdev, dev); - - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) { - r = -ENODEV; - goto err_free_mem; - } - clk_enable(dev->clk); - - dev->base = ioremap(mem->start, resource_size(mem)); - if (dev->base == NULL) { - dev_err(&pdev->dev, "failure mapping io resources\n"); - r = -EBUSY; - goto err_unuse_clocks; - } - { - u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1); - - dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1; - dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1; - } - i2c_dw_init(dev); - - writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */ - r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev); - if (r) { - dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); - goto err_iounmap; - } - - adap = &dev->adapter; - i2c_set_adapdata(adap, dev); - adap->owner = THIS_MODULE; - adap->class = I2C_CLASS_HWMON; - strlcpy(adap->name, "Synopsys DesignWare I2C adapter", - sizeof(adap->name)); - adap->algo = &i2c_dw_algo; - adap->dev.parent = &pdev->dev; - - adap->nr = pdev->id; - r = i2c_add_numbered_adapter(adap); - if (r) { - dev_err(&pdev->dev, "failure adding adapter\n"); - goto err_free_irq; - } - - return 0; - -err_free_irq: - free_irq(dev->irq, dev); -err_iounmap: - iounmap(dev->base); -err_unuse_clocks: - clk_disable(dev->clk); - clk_put(dev->clk); - dev->clk = NULL; -err_free_mem: - platform_set_drvdata(pdev, NULL); - put_device(&pdev->dev); - kfree(dev); -err_release_region: - release_mem_region(mem->start, resource_size(mem)); - - return r; + /* Enable the adapter */ + dw_writel(dev, 1, DW_IC_ENABLE); } -static int __devexit dw_i2c_remove(struct platform_device *pdev) +u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev) { - struct dw_i2c_dev *dev = platform_get_drvdata(pdev); - struct resource *mem; - - platform_set_drvdata(pdev, NULL); - i2c_del_adapter(&dev->adapter); - put_device(&pdev->dev); - - clk_disable(dev->clk); - clk_put(dev->clk); - dev->clk = NULL; - - writel(0, dev->base + DW_IC_ENABLE); - free_irq(dev->irq, dev); - kfree(dev); - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); - return 0; + return dw_readl(dev, DW_IC_ENABLE); } -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:i2c_designware"); +void i2c_dw_disable(struct dw_i2c_dev *dev) +{ + /* Disable controller */ + dw_writel(dev, 0, DW_IC_ENABLE); -static struct platform_driver dw_i2c_driver = { - .remove = __devexit_p(dw_i2c_remove), - .driver = { - .name = "i2c_designware", - .owner = THIS_MODULE, - }, -}; + /* Disable all interupts */ + dw_writel(dev, 0, DW_IC_INTR_MASK); + dw_readl(dev, DW_IC_CLR_INTR); +} -static int __init dw_i2c_init_driver(void) +void i2c_dw_clear_int(struct dw_i2c_dev *dev) { - return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); + dw_readl(dev, DW_IC_CLR_INTR); } -module_init(dw_i2c_init_driver); -static void __exit dw_i2c_exit_driver(void) +void i2c_dw_disable_int(struct dw_i2c_dev *dev) { - platform_driver_unregister(&dw_i2c_driver); + dw_writel(dev, 0, DW_IC_INTR_MASK); } -module_exit(dw_i2c_exit_driver); -MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); -MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); -MODULE_LICENSE("GPL"); +u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev) +{ + return dw_readl(dev, DW_IC_COMP_PARAM_1); +} diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h new file mode 100644 index 000000000000..02d1a2ddd853 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -0,0 +1,105 @@ +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + * + */ + + +#define DW_IC_CON_MASTER 0x1 +#define DW_IC_CON_SPEED_STD 0x2 +#define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_10BITADDR_MASTER 0x10 +#define DW_IC_CON_RESTART_EN 0x20 +#define DW_IC_CON_SLAVE_DISABLE 0x40 + + +/** + * struct dw_i2c_dev - private i2c-designware data + * @dev: driver model device node + * @base: IO registers pointer + * @cmd_complete: tx completion indicator + * @lock: protect this struct and IO registers + * @clk: input reference clock + * @cmd_err: run time hadware error code + * @msgs: points to an array of messages currently being transfered + * @msgs_num: the number of elements in msgs + * @msg_write_idx: the element index of the current tx message in the msgs + * array + * @tx_buf_len: the length of the current tx buffer + * @tx_buf: the current tx buffer + * @msg_read_idx: the element index of the current rx message in the msgs + * array + * @rx_buf_len: the length of the current rx buffer + * @rx_buf: the current rx buffer + * @msg_err: error status of the current transfer + * @status: i2c master status, one of STATUS_* + * @abort_source: copy of the TX_ABRT_SOURCE register + * @irq: interrupt number for the i2c master + * @adapter: i2c subsystem adapter node + * @tx_fifo_depth: depth of the hardware tx fifo + * @rx_fifo_depth: depth of the hardware rx fifo + */ +struct dw_i2c_dev { + struct device *dev; + void __iomem *base; + struct completion cmd_complete; + struct mutex lock; + struct clk *clk; + u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); + struct dw_pci_controller *controller; + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + u32 tx_buf_len; + u8 *tx_buf; + int msg_read_idx; + u32 rx_buf_len; + u8 *rx_buf; + int msg_err; + unsigned int status; + u32 abort_source; + int irq; + int swab; + struct i2c_adapter adapter; + u32 functionality; + u32 master_cfg; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; +}; + +extern u32 dw_readl(struct dw_i2c_dev *dev, int offset); +extern void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset); +extern int i2c_dw_init(struct dw_i2c_dev *dev); +extern int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num); +extern u32 i2c_dw_func(struct i2c_adapter *adap); +extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id); +extern void i2c_dw_enable(struct dw_i2c_dev *dev); +extern u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev); +extern void i2c_dw_disable(struct dw_i2c_dev *dev); +extern void i2c_dw_clear_int(struct dw_i2c_dev *dev); +extern void i2c_dw_disable_int(struct dw_i2c_dev *dev); +extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c new file mode 100644 index 000000000000..9e89e7313d62 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -0,0 +1,392 @@ +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * Copyright (C) 2011 Intel corporation. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include "i2c-designware-core.h" + +#define DRIVER_NAME "i2c-designware-pci" + +enum dw_pci_ctl_id_t { + moorestown_0, + moorestown_1, + moorestown_2, + + medfield_0, + medfield_1, + medfield_2, + medfield_3, + medfield_4, + medfield_5, +}; + +struct dw_pci_controller { + u32 bus_num; + u32 bus_cfg; + u32 tx_fifo_depth; + u32 rx_fifo_depth; + u32 clk_khz; +}; + +#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \ + DW_IC_CON_SLAVE_DISABLE | \ + DW_IC_CON_RESTART_EN) + +static struct dw_pci_controller dw_pci_controllers[] = { + [moorestown_0] = { + .bus_num = 0, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [moorestown_1] = { + .bus_num = 1, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [moorestown_2] = { + .bus_num = 2, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [medfield_0] = { + .bus_num = 0, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [medfield_1] = { + .bus_num = 1, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [medfield_2] = { + .bus_num = 2, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [medfield_3] = { + .bus_num = 3, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [medfield_4] = { + .bus_num = 4, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, + [medfield_5] = { + .bus_num = 5, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, + }, +}; +static struct i2c_algorithm i2c_dw_algo = { + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +}; + +static int i2c_dw_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); + int err; + + + i2c_dw_disable(i2c); + + err = pci_save_state(pdev); + if (err) { + dev_err(&pdev->dev, "pci_save_state failed\n"); + return err; + } + + err = pci_set_power_state(pdev, PCI_D3hot); + if (err) { + dev_err(&pdev->dev, "pci_set_power_state failed\n"); + return err; + } + + return 0; +} + +static int i2c_dw_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); + int err; + u32 enabled; + + enabled = i2c_dw_is_enabled(i2c); + if (enabled) + return 0; + + err = pci_set_power_state(pdev, PCI_D0); + if (err) { + dev_err(&pdev->dev, "pci_set_power_state() failed\n"); + return err; + } + + pci_restore_state(pdev); + + i2c_dw_init(i2c); + i2c_dw_enable(i2c); + return 0; +} + +static int i2c_dw_pci_runtime_idle(struct device *dev) +{ + int err = pm_schedule_suspend(dev, 500); + dev_dbg(dev, "runtime_idle called\n"); + + if (err != 0) + return 0; + return -EBUSY; +} + +static const struct dev_pm_ops i2c_dw_pm_ops = { + .resume = i2c_dw_pci_resume, + .suspend = i2c_dw_pci_suspend, + SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume, + i2c_dw_pci_runtime_idle) +}; + +static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) +{ + return dev->controller->clk_khz; +} + +static int __devinit i2c_dw_pci_probe(struct pci_dev *pdev, +const struct pci_device_id *id) +{ + struct dw_i2c_dev *dev; + struct i2c_adapter *adap; + unsigned long start, len; + void __iomem *base; + int r; + struct dw_pci_controller *controller; + + if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) { + printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n", + id->driver_data); + return -EINVAL; + } + + controller = &dw_pci_controllers[id->driver_data]; + + r = pci_enable_device(pdev); + if (r) { + dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n", + r); + goto exit; + } + + /* Determine the address of the I2C area */ + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!start || len == 0) { + dev_err(&pdev->dev, "base address not set\n"); + r = -ENODEV; + goto exit; + } + + r = pci_request_region(pdev, 0, DRIVER_NAME); + if (r) { + dev_err(&pdev->dev, "failed to request I2C region " + "0x%lx-0x%lx\n", start, + (unsigned long)pci_resource_end(pdev, 0)); + goto exit; + } + + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "I/O memory remapping failed\n"); + r = -ENOMEM; + goto err_release_region; + } + + + dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); + mutex_init(&dev->lock); + dev->clk = NULL; + dev->controller = controller; + dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; + dev->base = base; + dev->dev = get_device(&pdev->dev); + dev->functionality = + I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; + dev->master_cfg = controller->bus_cfg; + + pci_set_drvdata(pdev, dev); + + dev->tx_fifo_depth = controller->tx_fifo_depth; + dev->rx_fifo_depth = controller->rx_fifo_depth; + r = i2c_dw_init(dev); + if (r) + goto err_iounmap; + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = 0; + adap->algo = &i2c_dw_algo; + adap->dev.parent = &pdev->dev; + adap->nr = controller->bus_num; + snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d", + adap->nr); + + r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_iounmap; + } + + i2c_dw_disable_int(dev); + i2c_dw_clear_int(dev); + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + return 0; + +err_free_irq: + free_irq(pdev->irq, dev); +err_iounmap: + iounmap(dev->base); + pci_set_drvdata(pdev, NULL); + put_device(&pdev->dev); + kfree(dev); +err_release_region: + pci_release_region(pdev, 0); +exit: + return r; +} + +static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev) +{ + struct dw_i2c_dev *dev = pci_get_drvdata(pdev); + + i2c_dw_disable(dev); + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + pci_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + put_device(&pdev->dev); + + free_irq(dev->irq, dev); + kfree(dev); + pci_release_region(pdev, 0); +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("i2c_designware-pci"); + +DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = { + /* Moorestown */ + { PCI_VDEVICE(INTEL, 0x0802), moorestown_0 }, + { PCI_VDEVICE(INTEL, 0x0803), moorestown_1 }, + { PCI_VDEVICE(INTEL, 0x0804), moorestown_2 }, + /* Medfield */ + { PCI_VDEVICE(INTEL, 0x0817), medfield_3,}, + { PCI_VDEVICE(INTEL, 0x0818), medfield_4 }, + { PCI_VDEVICE(INTEL, 0x0819), medfield_5 }, + { PCI_VDEVICE(INTEL, 0x082C), medfield_0 }, + { PCI_VDEVICE(INTEL, 0x082D), medfield_1 }, + { PCI_VDEVICE(INTEL, 0x082E), medfield_2 }, + { 0,} +}; +MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids); + +static struct pci_driver dw_i2c_driver = { + .name = DRIVER_NAME, + .id_table = i2_designware_pci_ids, + .probe = i2c_dw_pci_probe, + .remove = __devexit_p(i2c_dw_pci_remove), + .driver = { + .pm = &i2c_dw_pm_ops, + }, +}; + +static int __init dw_i2c_init_driver(void) +{ + return pci_register_driver(&dw_i2c_driver); +} +module_init(dw_i2c_init_driver); + +static void __exit dw_i2c_exit_driver(void) +{ + pci_unregister_driver(&dw_i2c_driver); +} +module_exit(dw_i2c_exit_driver); + +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c new file mode 100644 index 000000000000..2d3657ab1258 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -0,0 +1,215 @@ +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "i2c-designware-core.h" + +static struct i2c_algorithm i2c_dw_algo = { + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +}; +static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) +{ + return clk_get_rate(dev->clk)/1000; +} + +static int __devinit dw_i2c_probe(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *ioarea; + int irq, r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return irq; /* -ENXIO */ + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); + mutex_init(&dev->lock); + dev->dev = get_device(&pdev->dev); + dev->irq = irq; + platform_set_drvdata(pdev, dev); + + dev->clk = clk_get(&pdev->dev, NULL); + dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; + + if (IS_ERR(dev->clk)) { + r = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->functionality = + I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; + dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + + dev->base = ioremap(mem->start, resource_size(mem)); + if (dev->base == NULL) { + dev_err(&pdev->dev, "failure mapping io resources\n"); + r = -EBUSY; + goto err_unuse_clocks; + } + { + u32 param1 = i2c_dw_read_comp_param(dev); + + dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1; + dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1; + } + r = i2c_dw_init(dev); + if (r) + goto err_iounmap; + + i2c_dw_disable_int(dev); + r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_iounmap; + } + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "Synopsys DesignWare I2C adapter", + sizeof(adap->name)); + adap->algo = &i2c_dw_algo; + adap->dev.parent = &pdev->dev; + + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_iounmap: + iounmap(dev->base); +err_unuse_clocks: + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; +err_free_mem: + platform_set_drvdata(pdev, NULL); + put_device(&pdev->dev); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int __devexit dw_i2c_remove(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + put_device(&pdev->dev); + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + i2c_dw_disable(dev); + free_irq(dev->irq, dev); + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:i2c_designware"); + +static struct platform_driver dw_i2c_driver = { + .remove = __devexit_p(dw_i2c_remove), + .driver = { + .name = "i2c_designware", + .owner = THIS_MODULE, + }, +}; + +static int __init dw_i2c_init_driver(void) +{ + return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); +} +module_init(dw_i2c_init_driver); + +static void __exit dw_i2c_exit_driver(void) +{ + platform_driver_unregister(&dw_i2c_driver); +} +module_exit(dw_i2c_exit_driver); + +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index ce1a32b71e47..8cebef49aeac 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -64,6 +64,7 @@ #define TEN_BIT_ADDR_DEFAULT 0xF000 #define TEN_BIT_ADDR_MASK 0xF0 #define PCH_START 0x0020 +#define PCH_RESTART 0x0004 #define PCH_ESR_START 0x0001 #define PCH_BUFF_START 0x1 #define PCH_REPSTART 0x0004 @@ -273,23 +274,24 @@ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, s32 timeout) { void __iomem *p = adap->pch_base_address; + ktime_t ns_val; + + if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) + return 0; /* MAX timeout value is timeout*1000*1000nsec */ - ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000); + ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000); do { - if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) - break; msleep(20); + if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) + return 0; } while (ktime_lt(ktime_get(), ns_val)); pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME); + pch_i2c_init(adap); - if (timeout == 0) { - pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME); - return -ETIME; - } - - return 0; + return -ETIME; } /** @@ -311,21 +313,19 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap) */ static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap) { - s32 ret; + long ret; ret = wait_event_timeout(pch_event, (adap->pch_event_flag != 0), msecs_to_jiffies(50)); - if (ret < 0) { - pch_err(adap, "timeout: %x\n", adap->pch_event_flag); - return ret; - } if (ret == 0) { pch_err(adap, "timeout: %x\n", adap->pch_event_flag); + adap->pch_event_flag = 0; return -ETIMEDOUT; } if (adap->pch_event_flag & I2C_ERROR_MASK) { pch_err(adap, "error bits set: %x\n", adap->pch_event_flag); + adap->pch_event_flag = 0; return -EIO; } @@ -394,6 +394,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, u32 addr_2_msb; u32 addr_8_lsb; s32 wrcount; + s32 rtn; void __iomem *p = adap->pch_base_address; length = msgs->len; @@ -412,15 +413,29 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, } if (msgs->flags & I2C_M_TEN) { - addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); + addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06; iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); if (first) pch_i2c_start(adap); - if (pch_i2c_wait_for_xfer_complete(adap) == 0 && - pch_i2c_getack(adap) == 0) { + + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave address" + "setting\n"); + return -EIO; + } addr_8_lsb = (addr & I2C_ADDR_MSK); iowrite32(addr_8_lsb, p + PCH_I2CDR); - } else { + } else if (rtn == -EIO) { /* Arbitration Lost */ + pch_err(adap, "Lost Arbitration\n"); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; + } else { /* wait-event timeout */ pch_i2c_stop(adap); return -ETIME; } @@ -431,30 +446,51 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, pch_i2c_start(adap); } - if ((pch_i2c_wait_for_xfer_complete(adap) == 0) && - (pch_i2c_getack(adap) == 0)) { - for (wrcount = 0; wrcount < length; ++wrcount) { - /* write buffer value to I2C data register */ - iowrite32(buf[wrcount], p + PCH_I2CDR); - pch_dbg(adap, "writing %x to Data register\n", - buf[wrcount]); + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave address" + "setting\n"); + return -EIO; + } + } else if (rtn == -EIO) { /* Arbitration Lost */ + pch_err(adap, "Lost Arbitration\n"); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; + } - if (pch_i2c_wait_for_xfer_complete(adap) != 0) - return -ETIME; + for (wrcount = 0; wrcount < length; ++wrcount) { + /* write buffer value to I2C data register */ + iowrite32(buf[wrcount], p + PCH_I2CDR); + pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]); - if (pch_i2c_getack(adap)) + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave address" + "setting\n"); return -EIO; + } + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMCF_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMIF_BIT); + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; } + } - /* check if this is the last message */ - if (last) - pch_i2c_stop(adap); - else - pch_i2c_repstart(adap); - } else { + /* check if this is the last message */ + if (last) pch_i2c_stop(adap); - return -EIO; - } + else + pch_i2c_repstart(adap); pch_dbg(adap, "return=%d\n", wrcount); @@ -484,6 +520,19 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) } /** + * pch_i2c_restart() - Generate I2C restart condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + * + * Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA. + */ +static void pch_i2c_restart(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART); +} + +/** * pch_i2c_readbytes() - read data from I2C bus in normal mode. * @i2c_adap: Pointer to the struct i2c_adapter. * @msgs: Pointer to i2c_msg structure. @@ -500,7 +549,9 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, u32 length; u32 addr; u32 addr_2_msb; + u32 addr_8_lsb; void __iomem *p = adap->pch_base_address; + s32 rtn; length = msgs->len; buf = msgs->buf; @@ -515,9 +566,55 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, } if (msgs->flags & I2C_M_TEN) { - addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD)); + addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + if (first) + pch_i2c_start(adap); + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave address" + "setting\n"); + return -EIO; + } + addr_8_lsb = (addr & I2C_ADDR_MSK); + iowrite32(addr_8_lsb, p + PCH_I2CDR); + } else if (rtn == -EIO) { /* Arbitration Lost */ + pch_err(adap, "Lost Arbitration\n"); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; + } + pch_i2c_restart(adap); + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave address" + "setting\n"); + return -EIO; + } + addr_2_msb |= I2C_RD; + iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, + p + PCH_I2CDR); + } else if (rtn == -EIO) { /* Arbitration Lost */ + pch_err(adap, "Lost Arbitration\n"); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, + I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; + } } else { /* 7 address bits + R/W bit */ addr = (((addr) << 1) | (I2C_RD)); @@ -528,56 +625,81 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, if (first) pch_i2c_start(adap); - if ((pch_i2c_wait_for_xfer_complete(adap) == 0) && - (pch_i2c_getack(adap) == 0)) { - pch_dbg(adap, "return %d\n", 0); + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave address" + "setting\n"); + return -EIO; + } + } else if (rtn == -EIO) { /* Arbitration Lost */ + pch_err(adap, "Lost Arbitration\n"); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; + } - if (length == 0) { - pch_i2c_stop(adap); - ioread32(p + PCH_I2CDR); /* Dummy read needs */ + if (length == 0) { + pch_i2c_stop(adap); + ioread32(p + PCH_I2CDR); /* Dummy read needs */ - count = length; - } else { - int read_index; - int loop; - pch_i2c_sendack(adap); + count = length; + } else { + int read_index; + int loop; + pch_i2c_sendack(adap); - /* Dummy read */ - for (loop = 1, read_index = 0; loop < length; loop++) { - buf[read_index] = ioread32(p + PCH_I2CDR); + /* Dummy read */ + for (loop = 1, read_index = 0; loop < length; loop++) { + buf[read_index] = ioread32(p + PCH_I2CDR); - if (loop != 1) - read_index++; + if (loop != 1) + read_index++; - if (pch_i2c_wait_for_xfer_complete(adap) != 0) { - pch_i2c_stop(adap); - return -ETIME; + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave" + "address setting\n"); + return -EIO; } - } /* end for */ + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; + } - pch_i2c_sendnack(adap); + } /* end for */ - buf[read_index] = ioread32(p + PCH_I2CDR); + pch_i2c_sendnack(adap); - if (length != 1) - read_index++; + buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */ - if (pch_i2c_wait_for_xfer_complete(adap) == 0) { - if (last) - pch_i2c_stop(adap); - else - pch_i2c_repstart(adap); + if (length != 1) + read_index++; - buf[read_index++] = ioread32(p + PCH_I2CDR); - count = read_index; - } else { - count = -ETIME; + rtn = pch_i2c_wait_for_xfer_complete(adap); + if (rtn == 0) { + if (pch_i2c_getack(adap)) { + pch_dbg(adap, "Receive NACK for slave" + "address setting\n"); + return -EIO; } - + } else { /* wait-event timeout */ + pch_i2c_stop(adap); + return -ETIME; } - } else { - count = -ETIME; - pch_i2c_stop(adap); + + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); + + buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */ + count = read_index; } return count; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 4c2a62b75b5c..58832e578fff 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -48,6 +48,9 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_i2c.h> #include <mach/irqs.h> #include <mach/hardware.h> @@ -125,6 +128,11 @@ struct imx_i2c_struct { unsigned int ifdr; /* IMX_I2C_IFDR */ }; +static const struct of_device_id i2c_imx_dt_ids[] = { + { .compatible = "fsl,imx1-i2c", }, + { /* sentinel */ } +}; + /** Functions for IMX I2C adapter driver *************************************** *******************************************************************************/ @@ -466,10 +474,10 @@ static int __init i2c_imx_probe(struct platform_device *pdev) { struct imx_i2c_struct *i2c_imx; struct resource *res; - struct imxi2c_platform_data *pdata; + struct imxi2c_platform_data *pdata = pdev->dev.platform_data; void __iomem *base; resource_size_t res_size; - int irq; + int irq, bitrate; int ret; dev_dbg(&pdev->dev, "<%s>\n", __func__); @@ -485,19 +493,11 @@ static int __init i2c_imx_probe(struct platform_device *pdev) return -ENOENT; } - pdata = pdev->dev.platform_data; - - if (pdata && pdata->init) { - ret = pdata->init(&pdev->dev); - if (ret) - return ret; - } - res_size = resource_size(res); if (!request_mem_region(res->start, res_size, DRIVER_NAME)) { - ret = -EBUSY; - goto fail0; + dev_err(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; } base = ioremap(res->start, res_size); @@ -520,6 +520,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.algo = &i2c_imx_algo; i2c_imx->adapter.dev.parent = &pdev->dev; i2c_imx->adapter.nr = pdev->id; + i2c_imx->adapter.dev.of_node = pdev->dev.of_node; i2c_imx->irq = irq; i2c_imx->base = base; i2c_imx->res = res; @@ -546,10 +547,12 @@ static int __init i2c_imx_probe(struct platform_device *pdev) i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); /* Set up clock divider */ - if (pdata && pdata->bitrate) - i2c_imx_set_clk(i2c_imx, pdata->bitrate); - else - i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE); + bitrate = IMX_I2C_BIT_RATE; + ret = of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &bitrate); + if (ret < 0 && pdata && pdata->bitrate) + bitrate = pdata->bitrate; + i2c_imx_set_clk(i2c_imx, bitrate); /* Set up chip registers to defaults */ writeb(0, i2c_imx->base + IMX_I2C_I2CR); @@ -562,6 +565,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev) goto fail5; } + of_i2c_register_devices(&i2c_imx->adapter); + /* Set up platform driver data */ platform_set_drvdata(pdev, i2c_imx); @@ -586,16 +591,12 @@ fail2: iounmap(base); fail1: release_mem_region(res->start, resource_size(res)); -fail0: - if (pdata && pdata->exit) - pdata->exit(&pdev->dev); return ret; /* Return error number */ } static int __exit i2c_imx_remove(struct platform_device *pdev) { struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev); - struct imxi2c_platform_data *pdata = pdev->dev.platform_data; /* remove adapter */ dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); @@ -611,10 +612,6 @@ static int __exit i2c_imx_remove(struct platform_device *pdev) writeb(0, i2c_imx->base + IMX_I2C_I2CR); writeb(0, i2c_imx->base + IMX_I2C_I2SR); - /* Shut down hardware */ - if (pdata && pdata->exit) - pdata->exit(&pdev->dev); - clk_put(i2c_imx->clk); iounmap(i2c_imx->base); @@ -628,6 +625,7 @@ static struct platform_driver i2c_imx_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = i2c_imx_dt_ids, } }; |