summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt20
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt2
-rw-r--r--Documentation/i2c/busses/i2c-i8012
-rw-r--r--Documentation/i2c/busses/i2c-ismt36
-rw-r--r--Documentation/i2c/busses/i2c-sis6309
-rw-r--r--Documentation/i2c/smbus-protocol4
-rw-r--r--Documentation/i2c/writing-clients4
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/i2c/busses/Kconfig28
-rw-r--r--drivers/i2c/busses/Makefile2
-rw-r--r--drivers/i2c/busses/i2c-at91.c17
-rw-r--r--drivers/i2c/busses/i2c-au1550.c1
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c342
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c2
-rw-r--r--drivers/i2c/busses/i2c-cpm.c2
-rw-r--r--drivers/i2c/busses/i2c-davinci.c2
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c16
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c2
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c61
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c2
-rw-r--r--drivers/i2c/busses/i2c-highlander.c4
-rw-r--r--drivers/i2c/busses/i2c-i801.c19
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c3
-rw-r--r--drivers/i2c/busses/i2c-imx.c1
-rw-r--r--drivers/i2c/busses/i2c-intel-mid.c2
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c2
-rw-r--r--drivers/i2c/busses/i2c-isch.c17
-rw-r--r--drivers/i2c/busses/i2c-ismt.c963
-rw-r--r--drivers/i2c/busses/i2c-mpc.c2
-rw-r--r--drivers/i2c/busses/i2c-mxs.c272
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c152
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c100
-rw-r--r--drivers/i2c/busses/i2c-ocores.c3
-rw-r--r--drivers/i2c/busses/i2c-octeon.c5
-rw-r--r--drivers/i2c/busses/i2c-omap.c3
-rw-r--r--drivers/i2c/busses/i2c-pca-platform.c1
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c2
-rw-r--r--drivers/i2c/busses/i2c-pnx.c2
-rw-r--r--drivers/i2c/busses/i2c-powermac.c1
-rw-r--r--drivers/i2c/busses/i2c-puv3.c2
-rw-r--r--drivers/i2c/busses/i2c-pxa-pci.c2
-rw-r--r--drivers/i2c/busses/i2c-pxa.c6
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c62
-rw-r--r--drivers/i2c/busses/i2c-s6000.c1
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c1
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c152
-rw-r--r--drivers/i2c/busses/i2c-sis630.c360
-rw-r--r--drivers/i2c/busses/i2c-stu300.c1
-rw-r--r--drivers/i2c/busses/i2c-taos-evm.c2
-rw-r--r--drivers/i2c/busses/i2c-tegra.c79
-rw-r--r--drivers/i2c/busses/i2c-versatile.c2
-rw-r--r--drivers/i2c/busses/i2c-xiic.c2
-rw-r--r--drivers/i2c/busses/i2c-xlr.c1
-rw-r--r--drivers/i2c/busses/scx200_acb.c1
-rw-r--r--drivers/i2c/i2c-core.c23
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpio.c1
-rw-r--r--drivers/misc/eeprom/Kconfig11
57 files changed, 2310 insertions, 514 deletions
diff --git a/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
new file mode 100644
index 000000000000..e9de3756752b
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
@@ -0,0 +1,20 @@
+Broadcom BCM2835 I2C controller
+
+Required properties:
+- compatible : Should be "brcm,bcm2835-i2c".
+- reg: Should contain register location and length.
+- interrupts: Should contain interrupt.
+- clocks : The clock feeding the I2C controller.
+
+Recommended properties:
+- clock-frequency : desired I2C bus clock frequency in Hz.
+
+Example:
+
+i2c@20205000 {
+ compatible = "brcm,bcm2835-i2c";
+ reg = <0x7e205000 0x1000>;
+ interrupts = <2 21>;
+ clocks = <&clk_i2c>;
+ clock-frequency = <100000>;
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt
index e9611ace8792..f98d4c5b5cca 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt
@@ -8,6 +8,8 @@ Required properties:
(b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c.
(c) "samsung, s3c2440-hdmiphy-i2c", for s3c2440-like i2c used
inside HDMIPHY block found on several samsung SoCs
+ (d) "samsung, exynos5440-i2c", for s3c2440-like i2c used
+ on EXYNOS5440 which does not need GPIO configuration.
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801
index 157416e78cc4..d55b8ab2d10f 100644
--- a/Documentation/i2c/busses/i2c-i801
+++ b/Documentation/i2c/busses/i2c-i801
@@ -22,6 +22,8 @@ Supported adapters:
* Intel Panther Point (PCH)
* Intel Lynx Point (PCH)
* Intel Lynx Point-LP (PCH)
+ * Intel Avoton (SOC)
+ * Intel Wellsburg (PCH)
Datasheets: Publicly available at the Intel website
On Intel Patsburg and later chipsets, both the normal host SMBus controller
diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt
new file mode 100644
index 000000000000..737355822c0b
--- /dev/null
+++ b/Documentation/i2c/busses/i2c-ismt
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+ * Intel S12xx series SOCs
+
+Authors:
+ Bill Brown <bill.e.brown@intel.com>
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed. Normally, the bus speed is set by the BIOS
+and never needs to be changed. However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Specify the bus speed in kHz.
+Available bus frequency settings:
+ 0 no change
+ 80 kHz
+ 100 kHz
+ 400 kHz
+ 1000 kHz
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions. An output of lspci will show
+something similar to the following:
+
+ 00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0
+ 00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1
diff --git a/Documentation/i2c/busses/i2c-sis630 b/Documentation/i2c/busses/i2c-sis630
index 0b9697366930..ee7943631074 100644
--- a/Documentation/i2c/busses/i2c-sis630
+++ b/Documentation/i2c/busses/i2c-sis630
@@ -4,9 +4,11 @@ Supported adapters:
* Silicon Integrated Systems Corp (SiS)
630 chipset (Datasheet: available at http://www.sfr-fresh.com/linux)
730 chipset
+ 964 chipset
* Possible other SiS chipsets ?
Author: Alexander Malysh <amalysh@web.de>
+ Amaury Decrême <amaury.decreme@gmail.com> - SiS964 support
Module Parameters
-----------------
@@ -18,6 +20,7 @@ Module Parameters
* high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default,
what your BIOS use). DANGEROUS! This should be a bit
faster, but freeze some systems (i.e. my Laptop).
+ SIS630/730 chip only.
Description
@@ -36,6 +39,12 @@ or like this:
00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02)
00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513
+or like this:
+
+00:00.0 Host bridge: Silicon Integrated Systems [SiS] 760/M760 Host (rev 02)
+00:02.0 ISA bridge: Silicon Integrated Systems [SiS] SiS964 [MuTIOL Media IO]
+ LPC Controller (rev 36)
+
in your 'lspci' output , then this driver is for your chipset.
Thank You
diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol
index d1f22618e14b..6012b12b3510 100644
--- a/Documentation/i2c/smbus-protocol
+++ b/Documentation/i2c/smbus-protocol
@@ -137,8 +137,8 @@ available for writes where the two data bytes are the other way
around (not SMBus compliant, but very popular.)
-SMBus Process Call: i2c_smbus_process_call()
-=============================================
+SMBus Process Call:
+===================
This command selects a device register (through the Comm byte), sends
16 bits of data to it, and reads 16 bits of data in return.
diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients
index 3a94b0e6f601..6b344b516bff 100644
--- a/Documentation/i2c/writing-clients
+++ b/Documentation/i2c/writing-clients
@@ -365,8 +365,6 @@ in terms of it. Never use this function directly!
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(struct i2c_client *client,
u8 command, u16 value);
- s32 i2c_smbus_process_call(struct i2c_client *client,
- u8 command, u16 value);
s32 i2c_smbus_read_block_data(struct i2c_client *client,
u8 command, u8 *values);
s32 i2c_smbus_write_block_data(struct i2c_client *client,
@@ -381,6 +379,8 @@ These ones were removed from i2c-core because they had no users, but could
be added back later if needed:
s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value);
+ s32 i2c_smbus_process_call(struct i2c_client *client,
+ u8 command, u16 value);
s32 i2c_smbus_block_process_call(struct i2c_client *client,
u8 command, u8 length, u8 *values);
diff --git a/MAINTAINERS b/MAINTAINERS
index ec9b7d2aa6ab..1e5c3a4ece7b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3852,6 +3852,13 @@ F: drivers/i2c/busses/i2c-sis96x.c
F: drivers/i2c/busses/i2c-via.c
F: drivers/i2c/busses/i2c-viapro.c
+I2C/SMBUS ISMT DRIVER
+M: Seth Heasley <seth.heasley@intel.com>
+M: Neil Horman <nhorman@tuxdriver.com>
+L: linux-i2c@vger.kernel.org
+F: drivers/i2c/busses/i2c-ismt.c
+F: Documentation/i2c/busses/i2c-ismt
+
I2C/SMBUS STUB DRIVER
M: "Mark M. Hoffman" <mhoffman@lightlink.com>
L: linux-i2c@vger.kernel.org
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 8bb810e1900b..a3725de92384 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -106,6 +106,8 @@ config I2C_I801
Panther Point (PCH)
Lynx Point (PCH)
Lynx Point-LP (PCH)
+ Avoton (SOC)
+ Wellsburg (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -121,6 +123,16 @@ config I2C_ISCH
This driver can also be built as a module. If so, the module
will be called i2c-isch.
+config I2C_ISMT
+ tristate "Intel iSMT SMBus Controller"
+ depends on PCI && X86
+ help
+ If you say yes to this option, support will be included for the Intel
+ iSMT SMBus host controller interface.
+
+ This driver can also be built as a module. If so, the module will be
+ called i2c-ismt.
+
config I2C_PIIX4
tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
depends on PCI
@@ -186,11 +198,11 @@ config I2C_SIS5595
will be called i2c-sis5595.
config I2C_SIS630
- tristate "SiS 630/730"
+ tristate "SiS 630/730/964"
depends on PCI
help
If you say yes to this option, support will be included for the
- SiS630 and SiS730 SMBus (a subset of I2C) interface.
+ SiS630, SiS730 and SiS964 SMBus (a subset of I2C) interface.
This driver can also be built as a module. If so, the module
will be called i2c-sis630.
@@ -319,6 +331,18 @@ config I2C_AU1550
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
+config I2C_BCM2835
+ tristate "Broadcom BCM2835 I2C controller"
+ depends on ARCH_BCM2835
+ help
+ If you say yes to this option, support will be included for the
+ BCM2835 I2C controller.
+
+ If you don't know what to do here, say N.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-bcm2835.
+
config I2C_BLACKFIN_TWI
tristate "Blackfin TWI I2C support"
depends on BLACKFIN
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3ff263f..8f4fc23b85b1 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
+obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
# Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
+obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index ebc224154695..75195e3f5ddb 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -553,13 +553,6 @@ static struct at91_twi_pdata at91sam9g10_config = {
.has_dma_support = false,
};
-static struct at91_twi_pdata at91sam9x5_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_dma_support = true,
-};
-
static const struct platform_device_id at91_twi_devtypes[] = {
{
.name = "i2c-at91rm9200",
@@ -582,8 +575,18 @@ static const struct platform_device_id at91_twi_devtypes[] = {
};
#if defined(CONFIG_OF)
+static struct at91_twi_pdata at91sam9x5_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_unre_flag = false,
+ .has_dma_support = true,
+};
+
static const struct of_device_id atmel_twi_dt_ids[] = {
{
+ .compatible = "atmel,at91rm9200-i2c",
+ .data = &at91rm9200_config,
+ } , {
.compatible = "atmel,at91sam9260-i2c",
.data = &at91sam9260_config,
} , {
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index b278298787d7..b5b89239d622 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -376,7 +376,6 @@ static int 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);
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
new file mode 100644
index 000000000000..ea4b08fc3353
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -0,0 +1,342 @@
+/*
+ * BCM2835 master mode driver
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define BCM2835_I2C_C 0x0
+#define BCM2835_I2C_S 0x4
+#define BCM2835_I2C_DLEN 0x8
+#define BCM2835_I2C_A 0xc
+#define BCM2835_I2C_FIFO 0x10
+#define BCM2835_I2C_DIV 0x14
+#define BCM2835_I2C_DEL 0x18
+#define BCM2835_I2C_CLKT 0x1c
+
+#define BCM2835_I2C_C_READ BIT(0)
+#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */
+#define BCM2835_I2C_C_ST BIT(7)
+#define BCM2835_I2C_C_INTD BIT(8)
+#define BCM2835_I2C_C_INTT BIT(9)
+#define BCM2835_I2C_C_INTR BIT(10)
+#define BCM2835_I2C_C_I2CEN BIT(15)
+
+#define BCM2835_I2C_S_TA BIT(0)
+#define BCM2835_I2C_S_DONE BIT(1)
+#define BCM2835_I2C_S_TXW BIT(2)
+#define BCM2835_I2C_S_RXR BIT(3)
+#define BCM2835_I2C_S_TXD BIT(4)
+#define BCM2835_I2C_S_RXD BIT(5)
+#define BCM2835_I2C_S_TXE BIT(6)
+#define BCM2835_I2C_S_RXF BIT(7)
+#define BCM2835_I2C_S_ERR BIT(8)
+#define BCM2835_I2C_S_CLKT BIT(9)
+#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */
+
+#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+struct bcm2835_i2c_dev {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ int irq;
+ struct i2c_adapter adapter;
+ struct completion completion;
+ u32 msg_err;
+ u8 *msg_buf;
+ size_t msg_buf_remaining;
+};
+
+static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
+ u32 reg, u32 val)
+{
+ writel(val, i2c_dev->regs + reg);
+}
+
+static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
+{
+ return readl(i2c_dev->regs + reg);
+}
+
+static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
+{
+ u32 val;
+
+ while (i2c_dev->msg_buf_remaining) {
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ if (!(val & BCM2835_I2C_S_TXD))
+ break;
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO,
+ *i2c_dev->msg_buf);
+ i2c_dev->msg_buf++;
+ i2c_dev->msg_buf_remaining--;
+ }
+}
+
+static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
+{
+ u32 val;
+
+ while (i2c_dev->msg_buf_remaining) {
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ if (!(val & BCM2835_I2C_S_RXD))
+ break;
+ *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev,
+ BCM2835_I2C_FIFO);
+ i2c_dev->msg_buf++;
+ i2c_dev->msg_buf_remaining--;
+ }
+}
+
+static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
+{
+ struct bcm2835_i2c_dev *i2c_dev = data;
+ u32 val, err;
+
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
+
+ err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
+ if (err) {
+ i2c_dev->msg_err = err;
+ complete(&i2c_dev->completion);
+ return IRQ_HANDLED;
+ }
+
+ if (val & BCM2835_I2C_S_RXD) {
+ bcm2835_drain_rxfifo(i2c_dev);
+ if (!(val & BCM2835_I2C_S_DONE))
+ return IRQ_HANDLED;
+ }
+
+ if (val & BCM2835_I2C_S_DONE) {
+ if (i2c_dev->msg_buf_remaining)
+ i2c_dev->msg_err = BCM2835_I2C_S_LEN;
+ else
+ i2c_dev->msg_err = 0;
+ complete(&i2c_dev->completion);
+ return IRQ_HANDLED;
+ }
+
+ if (val & BCM2835_I2C_S_TXD) {
+ bcm2835_fill_txfifo(i2c_dev);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
+ struct i2c_msg *msg)
+{
+ u32 c;
+ int time_left;
+
+ i2c_dev->msg_buf = msg->buf;
+ i2c_dev->msg_buf_remaining = msg->len;
+ INIT_COMPLETION(i2c_dev->completion);
+
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+
+ if (msg->flags & I2C_M_RD) {
+ c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
+ } else {
+ c = BCM2835_I2C_C_INTT;
+ bcm2835_fill_txfifo(i2c_dev);
+ }
+ c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN;
+
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
+
+ time_left = wait_for_completion_timeout(&i2c_dev->completion,
+ BCM2835_I2C_TIMEOUT);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+ if (!time_left) {
+ dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ if (likely(!i2c_dev->msg_err))
+ return 0;
+
+ if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) &&
+ (msg->flags & I2C_M_IGNORE_NAK))
+ return 0;
+
+ dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
+
+ if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
+ return -EREMOTEIO;
+ else
+ return -EIO;
+}
+
+static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num; i++) {
+ ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
+ if (ret)
+ break;
+ }
+
+ return ret ?: i;
+}
+
+static u32 bcm2835_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm bcm2835_i2c_algo = {
+ .master_xfer = bcm2835_i2c_xfer,
+ .functionality = bcm2835_i2c_func,
+};
+
+static int bcm2835_i2c_probe(struct platform_device *pdev)
+{
+ struct bcm2835_i2c_dev *i2c_dev;
+ struct resource *mem, *requested, *irq;
+ u32 bus_clk_rate, divider;
+ int ret;
+ struct i2c_adapter *adap;
+
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev) {
+ dev_err(&pdev->dev, "Cannot allocate i2c_dev\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, i2c_dev);
+ i2c_dev->dev = &pdev->dev;
+ init_completion(&i2c_dev->completion);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No mem resource\n");
+ return -ENODEV;
+ }
+
+ requested = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem),
+ dev_name(&pdev->dev));
+ if (!requested) {
+ dev_err(&pdev->dev, "Could not claim register region\n");
+ return -EBUSY;
+ }
+
+ i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+ if (!i2c_dev->regs) {
+ dev_err(&pdev->dev, "Could not map registers\n");
+ return -ENOMEM;
+ }
+
+ i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_dev->clk)) {
+ dev_err(&pdev->dev, "Could not get clock\n");
+ return PTR_ERR(i2c_dev->clk);
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &bus_clk_rate);
+ if (ret < 0) {
+ dev_warn(&pdev->dev,
+ "Could not read clock-frequency property\n");
+ bus_clk_rate = 100000;
+ }
+
+ divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
+ /*
+ * Per the datasheet, the register is always interpreted as an even
+ * number, by rounding down. In other words, the LSB is ignored. So,
+ * if the LSB is set, increment the divider to avoid any issue.
+ */
+ if (divider & 1)
+ divider++;
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return -ENODEV;
+ }
+ i2c_dev->irq = irq->start;
+
+ ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
+ dev_name(&pdev->dev), i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return -ENODEV;
+ }
+
+ adap = &i2c_dev->adapter;
+ i2c_set_adapdata(adap, i2c_dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
+ adap->algo = &bcm2835_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
+
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ free_irq(i2c_dev->irq, i2c_dev);
+
+ return ret;
+}
+
+static int bcm2835_i2c_remove(struct platform_device *pdev)
+{
+ struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ free_irq(i2c_dev->irq, i2c_dev);
+ i2c_del_adapter(&i2c_dev->adapter);
+
+ return 0;
+}
+
+static const struct of_device_id bcm2835_i2c_of_match[] = {
+ { .compatible = "brcm,bcm2835-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
+
+static struct platform_driver bcm2835_i2c_driver = {
+ .probe = bcm2835_i2c_probe,
+ .remove = bcm2835_i2c_remove,
+ .driver = {
+ .name = "i2c-bcm2835",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_i2c_of_match,
+ },
+};
+module_platform_driver(bcm2835_i2c_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
+MODULE_DESCRIPTION("BCM2835 I2C bus adapter");
+MODULE_LICENSE("