summaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2012-04-07 03:48:59 +0200
committerLen Brown <len.brown@intel.com>2012-04-07 03:48:59 +0200
commiteeaab2d8af2cf1d36d7086f22e9de42d6dd2995c (patch)
tree369b9c91a6d808944f07d2290fec6f9fe2731904 /drivers/i2c/busses
parentcpuidle: Fix panic in CPU off-lining with no idle driver (diff)
parentMAINTAINERS: Update git url for ACPI (diff)
downloadlinux-eeaab2d8af2cf1d36d7086f22e9de42d6dd2995c.tar.xz
linux-eeaab2d8af2cf1d36d7086f22e9de42d6dd2995c.zip
Merge branches 'idle-fix' and 'misc' into release
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r--drivers/i2c/busses/Kconfig45
-rw-r--r--drivers/i2c/busses/Makefile4
-rw-r--r--drivers/i2c/busses/i2c-acorn.c1
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c2
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c44
-rw-r--r--drivers/i2c/busses/i2c-gpio.c98
-rw-r--r--drivers/i2c/busses/i2c-i801.c27
-rw-r--r--drivers/i2c/busses/i2c-imx.c9
-rw-r--r--drivers/i2c/busses/i2c-isch.c10
-rw-r--r--drivers/i2c/busses/i2c-mpc.c63
-rw-r--r--drivers/i2c/busses/i2c-pxa.c95
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c14
-rw-r--r--drivers/i2c/busses/i2c-sirf.c459
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1
-rw-r--r--drivers/i2c/busses/i2c-versatile.c10
-rw-r--r--drivers/i2c/busses/i2c-xlr.c278
16 files changed, 1037 insertions, 123 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 3101dd59e379..d2c5095deeac 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -103,6 +103,7 @@ config I2C_I801
Patsburg (PCH)
DH89xxCC (PCH)
Panther Point (PCH)
+ Lynx Point (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -369,6 +370,21 @@ config I2C_DESIGNWARE_PCI
This driver can also be built as a module. If so, the module
will be called i2c-designware-pci.
+config I2C_EG20T
+ tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
+ depends on PCI
+ help
+ This driver is for PCH(Platform controller Hub) I2C of EG20T which
+ is an IOH(Input/Output Hub) for x86 embedded processor.
+ This driver can access PCH I2C bus device.
+
+ This driver also can be used for LAPIS Semiconductor IOH(Input/
+ Output Hub), ML7213, ML7223 and ML7831.
+ ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
+ for MP(Media Phone) use and ML7831 IOH is for general purpose use.
+ ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
+
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
depends on GENERIC_GPIO
@@ -630,6 +646,16 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module
will be called i2c-simtec.
+config I2C_SIRF
+ tristate "CSR SiRFprimaII I2C interface"
+ depends on ARCH_PRIMA2
+ help
+ If you say yes to this option, support will be included for the
+ CSR SiRFprimaII I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-sirf.
+
config I2C_STU300
tristate "ST Microelectronics DDC I2C interface"
depends on MACH_U300
@@ -681,20 +707,15 @@ config I2C_XILINX
This driver can also be built as a module. If so, the module
will be called xilinx_i2c.
-config I2C_EG20T
- tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
- depends on PCI
+config I2C_XLR
+ tristate "XLR I2C support"
+ depends on CPU_XLR
help
- This driver is for PCH(Platform controller Hub) I2C of EG20T which
- is an IOH(Input/Output Hub) for x86 embedded processor.
- This driver can access PCH I2C bus device.
+ This driver enables support for the on-chip I2C interface of
+ the Netlogic XLR/XLS MIPS processors.
- This driver also can be used for LAPIS Semiconductor IOH(Input/
- Output Hub), ML7213, ML7223 and ML7831.
- ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
- for MP(Media Phone) use and ML7831 IOH is for general purpose use.
- ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
- ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
+ This driver can also be built as a module. If so, the module
+ will be called i2c-xlr.
comment "External I2C/SMBus adapter drivers"
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index fba6da60aa0e..569567b0d027 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -37,6 +37,7 @@ 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_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
@@ -63,12 +64,13 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
+obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
-obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
+obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c
index 86796488ef4f..ed9f48d566db 100644
--- a/drivers/i2c/busses/i2c-acorn.c
+++ b/drivers/i2c/busses/i2c-acorn.c
@@ -19,7 +19,6 @@
#include <mach/hardware.h>
#include <asm/hardware/ioc.h>
-#include <asm/system.h>
#define FORCE_ONES 0xdc
#define SCL 0x02
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 5244c4724df7..4ba589ab8614 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -214,7 +214,7 @@ static int __init dw_i2c_init_driver(void)
{
return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
}
-module_init(dw_i2c_init_driver);
+subsys_initcall(dw_i2c_init_driver);
static void __exit dw_i2c_exit_driver(void)
{
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index ca8877641040..f086131cb1c7 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -271,30 +271,36 @@ static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
/**
* pch_i2c_wait_for_bus_idle() - check the status of bus.
* @adap: Pointer to struct i2c_algo_pch_data.
- * @timeout: waiting time counter (us).
+ * @timeout: waiting time counter (ms).
*/
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;
+ int schedule = 0;
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+
+ while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) {
+ if (time_after(jiffies, end)) {
+ 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 ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
- return 0;
+ return -ETIME;
+ }
- /* MAX timeout value is timeout*1000*1000nsec */
- ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
- do {
- msleep(20);
- if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
- return 0;
- } while (ktime_lt(ktime_get(), ns_val));
+ if (!schedule)
+ /* Retry after some usecs */
+ udelay(5);
+ else
+ /* Wait a bit more without consuming CPU */
+ usleep_range(20, 1000);
- 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);
+ schedule = 1;
+ }
- return -ETIME;
+ return 0;
}
/**
@@ -778,8 +784,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *pmsg;
u32 i = 0;
u32 status;
- u32 msglen;
- u32 subaddrlen;
s32 ret;
struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
@@ -804,12 +808,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
status = pmsg->flags;
pch_dbg(adap,
"After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
- /* calculate sub address length and message length */
- /* these are applicable only for buffer mode */
- subaddrlen = pmsg->buf[0];
- /* calculate actual message length excluding
- * the sub address fields */
- msglen = (pmsg->len) - (subaddrlen + 1);
if ((status & (I2C_M_RD)) != false) {
ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index a651779d9ff7..c0330a41db03 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -14,8 +14,15 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
-
-#include <asm/gpio.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_i2c.h>
+
+struct i2c_gpio_private_data {
+ struct i2c_adapter adap;
+ struct i2c_algo_bit_data bit_data;
+ struct i2c_gpio_platform_data pdata;
+};
/* Toggle SDA by changing the direction of the pin */
static void i2c_gpio_setsda_dir(void *data, int state)
@@ -78,24 +85,62 @@ static int i2c_gpio_getscl(void *data)
return gpio_get_value(pdata->scl_pin);
}
+static int __devinit of_i2c_gpio_probe(struct device_node *np,
+ struct i2c_gpio_platform_data *pdata)
+{
+ u32 reg;
+
+ if (of_gpio_count(np) < 2)
+ return -ENODEV;
+
+ pdata->sda_pin = of_get_gpio(np, 0);
+ pdata->scl_pin = of_get_gpio(np, 1);
+
+ if (!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) {
+ pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n",
+ np->full_name, pdata->sda_pin, pdata->scl_pin);
+ return -ENODEV;
+ }
+
+ of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay);
+
+ if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", &reg))
+ pdata->timeout = msecs_to_jiffies(reg);
+
+ pdata->sda_is_open_drain =
+ of_property_read_bool(np, "i2c-gpio,sda-open-drain");
+ pdata->scl_is_open_drain =
+ of_property_read_bool(np, "i2c-gpio,scl-open-drain");
+ pdata->scl_is_output_only =
+ of_property_read_bool(np, "i2c-gpio,scl-output-only");
+
+ return 0;
+}
+
static int __devinit i2c_gpio_probe(struct platform_device *pdev)
{
+ struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata)
- return -ENXIO;
-
- ret = -ENOMEM;
- adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
- if (!adap)
- goto err_alloc_adap;
- bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
- if (!bit_data)
- goto err_alloc_bit_data;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ adap = &priv->adap;
+ bit_data = &priv->bit_data;
+ pdata = &priv->pdata;
+
+ if (pdev->dev.of_node) {
+ ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata);
+ if (ret)
+ return ret;
+ } else {
+ if (!pdev->dev.platform_data)
+ return -ENXIO;
+ memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
+ }
ret = gpio_request(pdata->sda_pin, "sda");
if (ret)
@@ -143,6 +188,7 @@ static int __devinit i2c_gpio_probe(struct platform_device *pdev)
adap->algo_data = bit_data;
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
/*
* If "dev->id" is negative we consider it as zero.
@@ -154,7 +200,9 @@ static int __devinit i2c_gpio_probe(struct platform_device *pdev)
if (ret)
goto err_add_bus;
- platform_set_drvdata(pdev, adap);
+ of_i2c_register_devices(adap);
+
+ platform_set_drvdata(pdev, priv);
dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n",
pdata->sda_pin, pdata->scl_pin,
@@ -168,34 +216,40 @@ err_add_bus:
err_request_scl:
gpio_free(pdata->sda_pin);
err_request_sda:
- kfree(bit_data);
-err_alloc_bit_data:
- kfree(adap);
-err_alloc_adap:
return ret;
}
static int __devexit i2c_gpio_remove(struct platform_device *pdev)
{
+ struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
struct i2c_adapter *adap;
- adap = platform_get_drvdata(pdev);
- pdata = pdev->dev.platform_data;
+ priv = platform_get_drvdata(pdev);
+ adap = &priv->adap;
+ pdata = &priv->pdata;
i2c_del_adapter(adap);
gpio_free(pdata->scl_pin);
gpio_free(pdata->sda_pin);
- kfree(adap->algo_data);
- kfree(adap);
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id i2c_gpio_dt_ids[] = {
+ { .compatible = "i2c-gpio", },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
+#endif
+
static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(i2c_gpio_dt_ids),
},
.probe = i2c_gpio_probe,
.remove = __devexit_p(i2c_gpio_remove),
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 5d2e2816831f..ae2945a5e007 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -2,7 +2,7 @@
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
<mdsxyz123@yahoo.com>
- Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2007 - 2012 Jean Delvare <khali@linux-fr.org>
Copyright (C) 2010 Intel Corporation,
David Woodhouse <dwmw2@infradead.org>
@@ -51,6 +51,7 @@
Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes
DH89xxCC (PCH) 0x2330 32 hard yes yes yes
Panther Point (PCH) 0x1e22 32 hard yes yes yes
+ Lynx Point (PCH) 0x8c22 32 hard yes yes yes
Features supported by this driver:
Software PEC no
@@ -105,7 +106,7 @@
#define SMBHSTCNT_KILL 2
/* Other settings */
-#define MAX_TIMEOUT 100
+#define MAX_RETRIES 400
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
@@ -145,6 +146,7 @@
#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
struct i801_priv {
struct i2c_adapter adapter;
@@ -215,7 +217,7 @@ static int i801_check_post(struct i801_priv *priv, int status, int timeout)
dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n");
outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL,
SMBHSTCNT(priv));
- msleep(1);
+ usleep_range(1000, 2000);
outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL),
SMBHSTCNT(priv));
@@ -272,11 +274,11 @@ static int i801_transaction(struct i801_priv *priv, int xact)
/* We will always wait for a fraction of a second! */
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
- } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
+ } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES));
- result = i801_check_post(priv, status, timeout > MAX_TIMEOUT);
+ result = i801_check_post(priv, status, timeout > MAX_RETRIES);
if (result < 0)
return result;
@@ -291,12 +293,12 @@ static void i801_wait_hwpec(struct i801_priv *priv)
int status;
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
} while ((!(status & SMBHSTSTS_INTR))
- && (timeout++ < MAX_TIMEOUT));
+ && (timeout++ < MAX_RETRIES));
- if (timeout > MAX_TIMEOUT)
+ if (timeout > MAX_RETRIES)
dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n");
outb_p(status, SMBHSTSTS(priv));
@@ -380,12 +382,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
} while ((!(status & SMBHSTSTS_BYTE_DONE))
- && (timeout++ < MAX_TIMEOUT));
+ && (timeout++ < MAX_RETRIES));
- result = i801_check_post(priv, status, timeout > MAX_TIMEOUT);
+ result = i801_check_post(priv, status, timeout > MAX_RETRIES);
if (result < 0)
return result;
@@ -633,6 +635,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 58832e578fff..dfb84b7ee550 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -149,11 +149,6 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
break;
if (!for_busy && !(temp & I2SR_IBB))
break;
- if (signal_pending(current)) {
- dev_dbg(&i2c_imx->adapter.dev,
- "<%s> I2C Interrupted\n", __func__);
- return -EINTR;
- }
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&i2c_imx->adapter.dev,
"<%s> I2C bus is busy\n", __func__);
@@ -196,7 +191,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
- clk_enable(i2c_imx->clk);
+ clk_prepare_enable(i2c_imx->clk);
writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR);
/* Enable I2C controller */
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
@@ -245,7 +240,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
/* Disable I2C controller */
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
- clk_disable(i2c_imx->clk);
+ clk_disable_unprepare(i2c_imx->clk);
}
static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index 6561d275b8cf..f90a6057508d 100644
--- a/drivers/i2c/busses/i2c-isch.c
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -47,7 +47,7 @@
#define SMBBLKDAT (0x20 + sch_smba)
/* Other settings */
-#define MAX_TIMEOUT 500
+#define MAX_RETRIES 5000
/* I2C constants */
#define SCH_QUICK 0x00
@@ -68,7 +68,7 @@ static int sch_transaction(void)
{
int temp;
int result = 0;
- int timeout = 0;
+ int retries = 0;
dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
@@ -100,12 +100,12 @@ static int sch_transaction(void)
outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT);
do {
- msleep(1);
+ usleep_range(100, 200);
temp = inb(SMBHSTSTS) & 0x0f;
- } while ((temp & 0x08) && (timeout++ < MAX_TIMEOUT));
+ } while ((temp & 0x08) && (retries++ < MAX_RETRIES));
/* If the SMBus is still busy, we give up */
- if (timeout > MAX_TIMEOUT) {
+ if (retries > MAX_RETRIES) {
dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
}
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index a8ebb84e23f9..206caacd30d7 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -454,7 +454,7 @@ static int mpc_write(struct mpc_i2c *i2c, int target,
}
static int mpc_read(struct mpc_i2c *i2c, int target,
- u8 *data, int length, int restart)
+ u8 *data, int length, int restart, bool recv_len)
{
unsigned timeout = i2c->adap.timeout;
int i, result;
@@ -470,7 +470,7 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
return result;
if (length) {
- if (length == 1)
+ if (length == 1 && !recv_len)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
else
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
@@ -479,17 +479,46 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
}
for (i = 0; i < length; i++) {
+ u8 byte;
+
result = i2c_wait(i2c, timeout, 0);
if (result < 0)
return result;
- /* Generate txack on next to last byte */
- if (i == length - 2)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
- /* Do not generate stop on last byte */
- if (i == length - 1)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX);
- data[i] = readb(i2c->base + MPC_I2C_DR);
+ /*
+ * For block reads, we have to know the total length (1st byte)
+ * before we can determine if we are done.
+ */
+ if (i || !recv_len) {
+ /* Generate txack on next to last byte */
+ if (i == length - 2)
+ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
+ | CCR_TXAK);
+ /* Do not generate stop on last byte */
+ if (i == length - 1)
+ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
+ | CCR_MTX);
+ }
+
+ byte = readb(i2c->base + MPC_I2C_DR);
+
+ /*
+ * Adjust length if first received byte is length.
+ * The length is 1 length byte plus actually data length
+ */
+ if (i == 0 && recv_len) {
+ if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+ length += byte;
+ /*
+ * For block reads, generate txack here if data length
+ * is 1 byte (total length is 2 bytes).
+ */
+ if (length == 2)
+ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
+ | CCR_TXAK);
+ }
+ data[i] = byte;
}
return length;
@@ -532,12 +561,17 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
"Doing %s %d bytes to 0x%02x - %d of %d messages\n",
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, pmsg->addr, i + 1, num);
- if (pmsg->flags & I2C_M_RD)
- ret =
- mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
- else
+ if (pmsg->flags & I2C_M_RD) {
+ bool recv_len = pmsg->flags & I2C_M_RECV_LEN;
+
+ ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i,
+ recv_len);
+ if (recv_len && ret > 0)
+ pmsg->len = ret;
+ } else {
ret =
mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
+ }
}
mpc_i2c_stop(i2c);
return (ret < 0) ? ret : num;
@@ -545,7 +579,8 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
static u32 mpc_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
}
static const struct i2c_algorithm mpc_algo = {
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index d60364650990..f6733267fa9c 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -29,6 +29,8 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/i2c-pxa.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_i2c.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@@ -1044,23 +1046,60 @@ static const struct i2c_algorithm i2c_pxa_pio_algorithm = {
.functionality = i2c_pxa_functionality,
};
-static int i2c_pxa_probe(struct platform_device *dev)
+static struct of_device_id i2c_pxa_dt_ids[] = {
+ { .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
+ { .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
+ { .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA2XX },
+ {}
+};
+MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
+
+static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c,
+ enum pxa_i2c_types *i2c_types)
{
- struct pxa_i2c *i2c;
- struct resource *res;
- struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
- const struct platform_device_id *id = platform_get_device_id(dev);
- enum pxa_i2c_types i2c_type = id->driver_data;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(i2c_pxa_dt_ids, &pdev->dev);
int ret;
- int irq;
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(dev, 0);
- if (res == NULL || irq < 0)
- return -ENODEV;
+ if (!of_id)
+ return 1;
+ ret = of_alias_get_id(np, "i2c");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ pdev->id = ret;
+ if (of_get_property(np, "mrvl,i2c-polling", NULL))
+ i2c->use_pio = 1;
+ if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
+ i2c->fast_mode = 1;
+ *i2c_types = (u32)(of_id->data);
+ return 0;
+}
- if (!request_mem_region(res->start, resource_size(res), res->name))
- return -ENOMEM;
+static int i2c_pxa_probe_pdata(struct platform_device *pdev,
+ struct pxa_i2c *i2c,
+ enum pxa_i2c_types *i2c_types)
+{
+ struct i2c_pxa_platform_data *plat = pdev->dev.platform_data;
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+
+ *i2c_types = id->driver_data;
+ if (plat) {
+ i2c->use_pio = plat->use_pio;
+ i2c->fast_mode = plat->fast_mode;
+ }
+ return 0;
+}
+
+static int i2c_pxa_probe(struct platform_device *dev)
+{
+ struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
+ enum pxa_i2c_types i2c_type;
+ struct pxa_i2c *i2c;
+ struct resource *res = NULL;
+ int ret, irq;
i2c = kzalloc(sizeof(struct pxa_i2c), GFP_KERNEL);
if (!i2c) {
@@ -1068,6 +1107,24 @@ static int i2c_pxa_probe(struct platform_device *dev)
goto emalloc;
}
+ ret = i2c_pxa_probe_dt(dev, i2c, &i2c_type);
+ if (ret > 0)
+ ret = i2c_pxa_probe_pdata(dev, i2c, &i2c_type);
+ if (ret < 0)
+ goto eclk;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(dev, 0);
+ if (res == NULL || irq < 0) {
+ ret = -ENODEV;
+ goto eclk;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), res->name)) {
+ ret = -ENOMEM;
+ goto eclk;
+ }
+
i2c->adap.owner = THIS_MODULE;
i2c->adap.retries = 5;
@@ -1109,21 +1166,16 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->slave_addr = I2C_PXA_SLAVE_ADDR;
-#ifdef CONFIG_I2C_PXA_SLAVE
if (plat) {
+#ifdef CONFIG_I2C_PXA_SLAVE
i2c->slave_addr = plat->slave_addr;
i2c->slave = plat->slave;
- }
#endif
-
- clk_enable(i2c->clk);
-
- if (plat) {
i2c->adap.class = plat->class;
- i2c->use_pio = plat->use_pio;
- i2c->fast_mode = plat->fast_mode;
}
+ clk_enable(i2c->clk);
+
if (i2c->use_pio) {
i2c->adap.algo = &i2c_pxa_pio_algorithm;
} else {
@@ -1234,6 +1286,7 @@ static struct platform_driver i2c_pxa_driver = {
.name = "pxa2xx-i2c",
.owner = THIS_MODULE,
.pm = I2C_PXA_DEV_PM_OPS,
+ .of_match_table = i2c_pxa_dt_ids,
},
.id_table = i2c_pxa_id_table,
};
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 4c1718081685..737f7218a32c 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -31,6 +31,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
@@ -564,6 +565,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
int retry;
int ret;
+ pm_runtime_get_sync(&adap->dev);
clk_enable(i2c->clk);
for (retry = 0; retry < adap->retries; retry++) {
@@ -572,6 +574,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
if (ret != -EAGAIN) {
clk_disable(i2c->clk);
+ pm_runtime_put_sync(&adap->dev);
return ret;
}
@@ -581,6 +584,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
}
clk_disable(i2c->clk);
+ pm_runtime_put_sync(&adap->dev);
return -EREMOTEIO;
}
@@ -890,7 +894,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
}
}
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
@@ -1013,6 +1017,9 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(pdev, i2c);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_enable(&i2c->adap.dev);
+
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
clk_disable(i2c->clk);
return 0;
@@ -1035,7 +1042,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
clk_put(i2c->clk);
err_noclk:
- kfree(i2c);
return ret;
}
@@ -1048,6 +1054,9 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
+ pm_runtime_disable(&i2c->adap.dev);
+ pm_runtime_disable(&pdev->dev);
+
s3c24xx_i2c_deregister_cpufreq(i2c);
i2c_del_adapter(&i2c->adap);
@@ -1061,7 +1070,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
release_resource(i2c->ioarea);
s3c24xx_i2c_dt_gpio_free(i2c);
kfree(i2c->ioarea);
- kfree(i2c);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c
new file mode 100644
index 000000000000..5574a47792fb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-sirf.c
@@ -0,0 +1,459 @@
+/*
+ * I2C bus driver for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#define SIRFSOC_I2C_CLK_CTRL 0x00
+#define SIRFSOC_I2C_STATUS 0x0C
+#define SIRFSOC_I2C_CTRL 0x10
+#define SIRFSOC_I2C_IO_CTRL 0x14
+#define SIRFSOC_I2C_SDA_DELAY 0x18
+#define SIRFSOC_I2C_CMD_START 0x1C
+#define SIRFSOC_I2C_CMD_BUF 0x30
+#define SIRFSOC_I2C_DATA_BUF 0x80
+
+#define SIRFSOC_I2C_CMD_BUF_MAX 16
+#define SIRFSOC_I2C_DATA_BUF_MAX 16
+
+#define SIRFSOC_I2C_CMD(x) (SIRFSOC_I2C_CMD_BUF + (x)*0x04)
+#define SIRFSOC_I2C_DATA_MASK(x) (0xFF<<(((x)&3)*8))
+#define SIRFSOC_I2C_DATA_SHIFT(x) (((x)&3)*8)
+
+#define SIRFSOC_I2C_DIV_MASK (0xFFFF)
+
+/* I2C status flags */
+#define SIRFSOC_I2C_STAT_BUSY BIT(0)
+#define SIRFSOC_I2C_STAT_TIP BIT(1)
+#define SIRFSOC_I2C_STAT_NACK BIT(2)
+#define SIRFSOC_I2C_STAT_TR_INT BIT(4)
+#define SIRFSOC_I2C_STAT_STOP BIT(6)
+#define SIRFSOC_I2C_STAT_CMD_DONE BIT(8)
+#define SIRFSOC_I2C_STAT_ERR BIT(9)
+#define SIRFSOC_I2C_CMD_INDEX (0x1F<<16)
+
+/* I2C control flags */
+#define SIRFSOC_I2C_RESET BIT(0)
+#define SIRFSOC_I2C_CORE_EN BIT(1)
+#define SIRFSOC_I2C_MASTER_MODE BIT(2)
+#define SIRFSOC_I2C_CMD_DONE_EN BIT(11)
+#define SIRFSOC_I2C_ERR_INT_EN BIT(12)
+
+#define SIRFSOC_I2C_SDA_DELAY_MASK (0xFF)
+#define SIRFSOC_I2C_SCLF_FILTER (3<<8)
+
+#define SIRFSOC_I2C_START_CMD BIT(0)
+
+#define SIRFSOC_I2C_CMD_RP(x) ((x)&0x7)
+#define SIRFSOC_I2C_NACK BIT(3)
+#define SIRFSOC_I2C_WRITE BIT(4)
+#define SIRFSOC_I2C_READ BIT(5)
+#define SIRFSOC_I2C_STOP BIT(6)
+#define SIRFSOC_I2C_START BIT(7)
+
+#define SIRFSOC_I2C_DEFAULT_SPEED 100000
+
+struct sirfsoc_i2c {
+ void __iomem *base;
+ struct clk *clk;
+ u32 cmd_ptr; /* Current position in CMD buffer */
+ u8 *buf; /* Buffer passed by user */
+ u32 msg_len; /* Message length */
+ u32 finished_len; /* number of bytes read/written */
+ u32 read_cmd_len; /* number of read cmd sent */
+ int msg_read; /* 1 indicates a read message */
+ int err_status; /* 1 indicates an error on bus */
+
+ u32 sda_delay; /* For suspend/resume */
+ u32 clk_div;
+ int last; /* Last message in transfer, STOP cmd can be sent */
+
+ struct completion done; /* indicates completion of message transfer */
+ struct i2c_adapter adapter;
+};
+
+static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic)
+{
+ u32 data = 0;
+ int i;
+
+ for (i = 0; i < siic->read_cmd_len; i++) {
+ if (!(i & 0x3))
+ data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i);
+ siic->buf[siic->finished_len++] =
+ (u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >>
+ SIRFSOC_I2C_DATA_SHIFT(i));
+ }
+}
+
+static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic)
+{
+ u32 regval;
+ int i = 0;
+
+ if (siic->msg_read) {
+ while (((siic->finished_len + i) < siic->msg_len)
+ && (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) {
+ regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0);
+ if (((siic->finished_len + i) ==
+ (siic->msg_len - 1)) && siic->last)
+ regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK;
+ writel(regval,
+ siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+ i++;
+ }
+
+ siic->read_cmd_len = i;
+ } else {
+ while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1)
+ && (siic->finished_len < siic->msg_len)) {
+ regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0);
+ if ((siic->finished_len == (siic->msg_len - 1))
+ && siic->last)
+ regval |= SIRFSOC_I2C_STOP;
+ writel(regval,
+ siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+ writel(siic->buf[siic->finished_len++],
+ siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+ }
+ }
+ siic->cmd_ptr = 0;
+
+ /* Trigger the transfer */
+ writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START);
+}
+
+static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id)
+{
+ struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id;
+ u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS);
+
+ if (i2c_stat & SIRFSOC_I2C_STAT_ERR) {
+ /* Error conditions */
+ siic->err_status = 1;
+ writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
+
+ if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
+ dev_err(&siic->adapter.dev, "ACK not received\n");
+ else
+ dev_err(&siic->adapter.dev, "I2C error\n");
+
+ complete(&siic->done);
+ } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) {
+ /* CMD buffer execution complete */
+ if (siic->msg_read)
+ i2c_sirfsoc_read_data(siic);
+ if (siic->finished_len == siic->msg_len)
+ complete(&siic->done);
+ else /* Fill a new CMD buffer for left data */
+ i2c_sirfsoc_queue_cmd(siic);
+
+ writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic,
+ struct i2c_msg *msg)
+{
+ unsigned char addr;
+ u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE;
+
+ /* no data and last message -> add STOP */
+ if (siic->last && (msg->len == 0))
+ regval |= SIRFSOC_I2C_STOP;
+
+ writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+
+ addr = msg->addr << 1; /* Generate address */
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+
+ writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+}
+
+static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
+{
+ u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL);
+ /* timeout waiting for the xfer to finish or fail */
+ int timeout = msecs_to_jiffies((msg->len + 1) * 50);
+ int ret = 0;
+
+ i2c_sirfsoc_set_address(siic, msg);
+
+ writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN,
+ siic->base + SIRFSOC_I2C_CTRL);
+ i2c_sirfsoc_queue_cmd(siic);
+
+ if (wait_for_completion_timeout(&siic->done, timeout) == 0) {
+ siic->err_status = 1;
+ dev_err(&siic->adapter.dev, "Transfer timeout\n");
+ }
+
+ writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN),
+ siic->base + SIRFSOC_I2C_CTRL);
+ writel(0, siic->base + SIRFSOC_I2C_CMD_START);
+
+ if (siic->err_status) {
+ writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
+ siic->base + SIRFSOC_I2C_CTRL);
+ while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
+ cpu_relax();
+
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static u32 i2c_sirfsoc_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct sirfsoc_i2c *siic = adap->algo_data;
+ int i, ret;
+
+ clk_enable(siic->clk);
+
+ for (i = 0; i < num; i++) {
+ siic->buf = msgs[i].buf;
+ siic->msg_len = msgs[i].len;
+ siic->msg_read = !!(msgs[i].flags & I2C_M_RD);
+ siic->err_status = 0;
+ siic->cmd_ptr = 0;
+ siic->finished_len = 0;
+ siic->last = (i == (num - 1));
+
+ ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]);
+ if (ret) {
+ clk_disable(siic->clk);
+ return ret;
+ }
+ }
+
+ clk_disable(siic->clk);
+ return num;
+}
+
+/* I2C algorithms associated with this master controller driver */
+static const struct i2c_algorithm i2c_sirfsoc_algo = {
+ .master_xfer = i2c_sirfsoc_xfer,
+ .functionality = i2c_sirfsoc_func,
+};
+
+static int __devinit i2c_sirfsoc_probe(struct platform_device *pdev)
+{
+ struct sirfsoc_i2c *siic;
+ struct i2c_adapter *adap;
+ struct resource *mem_res;
+ struct clk *clk;
+ int bitrate;
+ int ctrl_speed;
+ int irq;
+
+ int err;
+ u32 regval;
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ dev_err(&pdev->dev, "Clock get failed\n");
+ goto err_get_clk;
+ }
+
+ err = clk_prepare(clk);
+ if (err) {
+ dev_err(&pdev->dev, "Clock prepare failed\n");
+ goto err_clk_prep;
+ }
+
+ err = clk_enable(clk);
+ if (err) {
+ dev_err(&pdev->dev, "Clock enable failed\n");
+ goto err_clk_en;
+ }
+
+ ctrl_speed = clk_get_rate(clk);
+
+ siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL);
+ if (!siic) {
+ dev_err(&pdev->dev, "Can't allocate driver data\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ adap = &siic->adapter;
+ adap->class = I2C_CLASS_HWMON;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem_res == NULL) {
+ dev_err(&pdev->dev, "Unable to get MEM resource\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ siic->base = devm_request_and_ioremap(&pdev->dev, mem_res);
+ if (siic->base == NULL) {
+ dev_err(&pdev->dev, "IO remap failed!\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = irq;
+ goto out;
+ }
+ err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0,
+ dev_name(&pdev->dev), siic);
+ if (err)
+ goto out;
+
+ adap->algo = &i2c_sirfsoc_algo;
+ adap->algo_data = siic;
+
+ adap->dev.parent = &pdev->dev;
+ adap->nr = pdev->id;
+
+ strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name));
+
+ platform_set_drvdata(pdev, adap);
+ init_completion(&siic->done);
+
+ /* Controller Initalisation */
+
+ writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
+ while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
+ cpu_relax();
+ writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
+ siic->base + SIRFSOC_I2C_CTRL);
+
+ siic->clk = clk;
+
+ err = of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &bitrate);
+ if (err < 0)
+ bitrate = SIRFSOC_I2C_DEFAULT_SPEED;
+
+ if (bitrate < 100000)
+ regval =
+ (2 * ctrl_speed) / (2 * bitrate * 11);
+ else
+ regval = ctrl_speed / (bitrate * 5);
+
+ writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL);
+ if (regval > 0xFF)
+ writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY);
+ else
+ writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY);
+
+ err = i2c_add_numbered_adapter(adap);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't add new i2c adapter\n");
+ goto out;
+ }
+
+ clk_disable(clk);
+
+ dev_info(&pdev->dev, " I2C adapter ready to operate\n");
+
+ return 0;
+
+out:
+ clk_disable(clk);
+err_clk_en:
+ clk_unprepare(clk);
+err_clk_prep:
+ clk_put(clk);
+err_get_clk:
+ return err;
+}
+
+static int __devexit i2c_sirfsoc_remove(struct platform_device *pdev)
+{
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct sirfsoc_i2c *siic = adapter->algo_data;
+
+ writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
+ i2c_del_adapter(adapter);
+ clk_unprepare(siic->clk);
+ clk_put(siic->clk);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2c_sirfsoc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct sirfsoc_i2c *siic = adapter->algo_data;
+
+ clk_enable(siic->clk);
+ siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY);
+ siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL);
+ clk_disable(siic->clk);
+ return 0;
+}
+
+static int i2c_sirfsoc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct sirfsoc_i2c *siic = adapter->algo_data;
+
+ clk_enable(siic->clk);
+ writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
+ writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
+ siic->base + SIRFSOC_I2C_CTRL);
+ writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL);
+ writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY);
+ clk_disable(siic->clk);
+ return 0;
+}
+
+static const struct dev_pm_ops i2c_sirfsoc_pm_ops = {
+ .suspend = i2c_sirfsoc_suspend,
+ .resume = i2c_sirfsoc_resume,
+};
+#endif
+
+static const struct of_device_id sirfsoc_i2c_of_match[] __devinitconst = {
+ { .compatible = "sirf,prima2-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match);
+
+static struct platform_driver i2c_sirfsoc_driver = {
+ .driver = {
+ .name = "sirfsoc_i2c",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &i2c_sirfsoc_pm_ops,
+#endif
+ .of_match_table = sirfsoc_i2c_of_match,
+ },
+ .probe = i2c_sirfsoc_probe,
+ .remove = __devexit_p(i2c_sirfsoc_remove),
+};
+module_platform_driver(i2c_sirfsoc_driver);
+
+MODULE_DESCRIPTION("SiRF SoC I2C master controller driver");
+MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, "
+ "Xiangzhen Ye <Xiangzhen.Ye@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 0ab4a9548745..e978635e60f0 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -457,7 +457,6 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
int ret;
tegra_i2c_flush_fifos(i2c_dev);
- i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS);
if (msg->len == 0)
return -EINVAL;
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
index 60556012312f..f585aead50cc 100644
--- a/drivers/i2c/busses/i2c-versatile.c
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of_i2c.h>
#define I2C_CONTROL 0x00
#define I2C_CONTROLS 0x00
@@ -99,6 +100,7 @@ static int i2c_versatile_probe(struct platform_device *dev)
strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name));
i2c->adap.algo_data = &i2c->algo;
i2c->adap.dev.parent = &dev->dev;
+ i2c->adap.dev.of_node = dev->dev.of_node;
i2c->algo = i2c_versatile_algo;
i2c->algo.data = i2c;
@@ -111,6 +113,7 @@ static int i2c_versatile_probe(struct platform_device *dev)
ret = i2c_bit_add_bus(&i2c->adap);
if (ret >= 0) {
platform_set_drvdata(dev, i2c);
+ of_i2c_register_devices(&i2c->adap);
return 0;
}
@@ -133,12 +136,19 @@ static int i2c_versatile_remove(struct platform_device *dev)
return 0;
}
+static const struct of_device_id i2c_versatile_match[] = {
+ { .compatible = "arm,versatile-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_versatile_match);
+
static struct platform_driver i2c_versatile_driver = {
.probe = i2c_versatile_probe,
.remove = i2c_versatile_remove,
.driver = {
.name = "versatile-i2c",
.owner = THIS_MODULE,
+ .of_match_table = i2c_versatile_match,
},
};
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
new file mode 100644
index 000000000000..96d3fabd8883
--- /dev/null
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2011, Netlogic Microsystems Inc.
+ * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+/* XLR I2C REGISTERS */
+#define XLR_I2C_CFG 0x00
+#define XLR_I2C_CLKDIV 0x01
+#define XLR_I2C_DEVADDR 0x02
+#define XLR_I2C_ADDR 0x03
+#define XLR_I2C_DATAOUT 0x04
+#define XLR_I2C_DATAIN 0x05
+#define XLR_I2C_STATUS 0x06
+#define XLR_I2C_STARTXFR 0x07
+#define XLR_I2C_BYTECNT 0x08
+#define XLR_I2C_HDSTATIM 0x09
+
+/* XLR I2C REGISTERS FLAGS */
+#define XLR_I2C_BUS_BUSY 0x01
+#define XLR_I2C_SDOEMPTY 0x02
+#define XLR_I2C_RXRDY 0x04
+#define XLR_I2C_ACK_ERR 0x08
+#define XLR_I2C_ARB_STARTERR 0x30
+
+/* Register Values */
+#define XLR_I2C_CFG_ADDR 0xF8
+#define XLR_I2C_CFG_NOADDR 0xFA
+#define XLR_I2C_STARTXFR_ND 0x02 /* No Data */
+#define XLR_I2C_STARTXFR_RD 0x01 /* Read */
+#define XLR_I2C_STARTXFR_WR 0x00 /* Write */
+
+#define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */
+
+/*
+ * On XLR/XLS, we need to use __raw_ IO to read the I2C registers
+ * because they are in the big-endian MMIO area on the SoC.
+ *
+ * The readl/writel implementation on XLR/XLS byteswaps, because
+ * those are for its little-endian PCI space (see arch/mips/Kconfig).
+ */
+static inline void xlr_i2c_wreg(u32 __iomem *base, unsigned int reg, u32 val)
+{
+ __raw_writel(val, base + reg);
+}
+
+static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
+{
+ return __raw_readl(base + reg);
+}
+
+struct xlr_i2c_private {
+ struct i2c_adapter adap;
+ u32 __iomem *iobase;
+};
+
+static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
+ u8 *buf, u16 addr)
+{
+ struct i2c_adapter *adap = &priv->adap;
+ unsigned long timeout, stoptime, checktime;
+ u32 i2c_status;
+ int pos, timedout;
+ u8 offset, byte;
+
+ offset = buf[0];
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
+
+ timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
+ stoptime = jiffies + timeout;
+ timedout = 0;
+ pos = 1;
+retry:
+ if (len == 1) {
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
+ XLR_I2C_STARTXFR_ND);
+ } else {
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
+ XLR_I2C_STARTXFR_WR);
+ }
+
+ while (!timedout) {
+ checktime = jiffies;
+ i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+ if (i2c_status & XLR_I2C_SDOEMPTY) {
+ pos++;
+ /* need to do a empty dataout after the last byte */
+ byte = (pos < len) ? buf[pos] : 0;
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
+
+ /* reset timeout on successful xmit */
+ stoptime = jiffies + timeout;
+ }
+ timedout = time_after(checktime, stoptime);
+
+ if (i2c_status & XLR_I2C_ARB_STARTERR) {
+ if (timedout)
+ break;
+ goto retry;
+ }
+
+ if (i2c_status & XLR_I2C_ACK_ERR)
+ return -EIO;
+
+ if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len)
+ return 0;
+ }
+ dev_err(&adap->dev, "I2C transmit timeout\n");
+ return -ETIMEDOUT;
+}
+
+static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
+{
+ struct i2c_adapter *adap = &priv->adap;
+ u32 i2c_status;
+ unsigned long timeout, stoptime, checktime;
+ int nbytes, timedout;
+ u8 byte;
+
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
+
+ timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
+ stoptime = jiffies + timeout;
+ timedout = 0;
+ nbytes = 0;
+retry:
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
+
+ while (!timedout) {
+ checktime = jiffies;
+ i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+ if (i2c_status & XLR_I2C_RXRDY) {
+ if (nbytes > len)
+ return -EIO; /* should not happen */
+
+ /* we need to do a dummy datain when nbytes == len */
+ byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
+ if (nbytes < len)
+ buf[nbytes] = byte;
+ nbytes++;
+
+ /* reset timeout on successful read */
+ stoptime = jiffies + timeout;
+ }
+
+ timedout = time_after(checktime, stoptime);
+ if (i2c_status & XLR_I2C_ARB_STARTERR) {
+ if (timedout)
+ break;
+ goto retry;
+ }
+
+ if (i2c_status & XLR_I2C_ACK_ERR)
+ return -EIO;
+
+ if ((i2c_status & XLR_I2C_BUS_BUSY) == 0)
+ return 0;
+ }
+
+ dev_err(&adap->dev, "I2C receive timeout\n");
+ return -ETIMEDOUT;
+}
+
+static int xlr_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct i2c_msg *msg;
+ int i;
+ int ret = 0;
+ struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
+
+ for (i = 0; ret == 0 && i < num; i++) {
+ msg = &msgs[i];
+ if (msg->flags & I2C_M_RD)
+ ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
+ msg->addr);
+ else
+ ret = xlr_i2c_tx(priv, msg->len, &msg->buf[0],
+ msg->addr);
+ }
+
+ return (ret != 0) ? ret : num;
+}
+
+static u32 xlr_func(struct i2c_adapter *adap)
+{
+ /* Emulate SMBUS over I2C */
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm xlr_i2c_algo = {
+ .master_xfer = xlr_i2c_xfer,
+ .functionality = xlr_func,
+};
+
+static int __devinit xlr_i2c_probe(struct platform_device *pdev)
+{
+ struct xlr_i2c_private *priv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->iobase = devm_request_and_ioremap(&pdev->dev, res);
+ if (!priv->iobase) {
+ dev_err(&pdev->dev, "devm_request_and_ioremap failed\n");
+ return -EBUSY;
+ }
+
+ priv->adap.dev.parent = &pdev->dev;
+ priv->adap.owner = THIS_MODULE;
+ priv->adap.algo_data = priv;
+ priv->adap.algo = &xlr_i2c_algo;
+ priv->adap.nr = pdev->id;
+ priv->adap.class = I2C_CLASS_HWMON;
+ snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
+
+ i2c_set_adapdata(&priv->adap, priv);
+ ret = i2c_add_numbered_adapter(&priv->adap);
+ if (ret < 0) {
+ dev_err(&priv->adap.dev, "Failed to add i2c bus.\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ dev_info(&priv->adap.dev, "Added I2C Bus.\n");
+ return 0;
+}
+
+static int __devexit xlr_i2c_remove(struct platform_device *pdev)
+{
+ struct xlr_i2c_private *priv;
+
+ priv = platform_get_drvdata(pdev);
+ i2c_del_adapter(&priv->adap);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver xlr_i2c_driver = {
+ .probe = xlr_i2c_probe,
+ .remove = __devexit_p(xlr_i2c_remove),
+ .driver = {
+ .name = "xlr-i2cbus",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(xlr_i2c_driver);
+
+MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@netlogicmicro.com>");
+MODULE_DESCRIPTION("XLR/XLS SoC I2C Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:xlr-i2cbus");