summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Kconfig10
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c5
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c5
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c5
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.h7
-rw-r--r--drivers/i2c/busses/Kconfig30
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c4
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c4
-rw-r--r--drivers/i2c/busses/i2c-amd756-s4882.c4
-rw-r--r--drivers/i2c/busses/i2c-amd756.c4
-rw-r--r--drivers/i2c/busses/i2c-at91.c130
-rw-r--r--drivers/i2c/busses/i2c-au1550.c5
-rw-r--r--drivers/i2c/busses/i2c-bcm-kona.c1
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c1
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c1
-rw-r--r--drivers/i2c/busses/i2c-cadence.c12
-rw-r--r--drivers/i2c/busses/i2c-cbus-gpio.c1
-rw-r--r--drivers/i2c/busses/i2c-cpm.c5
-rw-r--r--drivers/i2c/busses/i2c-davinci.c37
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c6
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h4
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c4
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c5
-rw-r--r--drivers/i2c/busses/i2c-dln2.c262
-rw-r--r--drivers/i2c/busses/i2c-efm32.c1
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c4
-rw-r--r--drivers/i2c/busses/i2c-elektor.c6
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c5
-rw-r--r--drivers/i2c/busses/i2c-gpio.c1
-rw-r--r--drivers/i2c/busses/i2c-highlander.c1
-rw-r--r--drivers/i2c/busses/i2c-hix5hd2.c2
-rw-r--r--drivers/i2c/busses/i2c-hydra.c4
-rw-r--r--drivers/i2c/busses/i2c-i801.c61
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c1
-rw-r--r--drivers/i2c/busses/i2c-img-scb.c1412
-rw-r--r--drivers/i2c/busses/i2c-imx.c360
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c1
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.h6
-rw-r--r--drivers/i2c/busses/i2c-isch.c5
-rw-r--r--drivers/i2c/busses/i2c-ismt.c4
-rw-r--r--drivers/i2c/busses/i2c-kempld.c1
-rw-r--r--drivers/i2c/busses/i2c-meson.c492
-rw-r--r--drivers/i2c/busses/i2c-mpc.c19
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c1
-rw-r--r--drivers/i2c/busses/i2c-mxs.c3
-rw-r--r--drivers/i2c/busses/i2c-nforce2-s4985.c4
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c4
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c2
-rw-r--r--drivers/i2c/busses/i2c-ocores.c1
-rw-r--r--drivers/i2c/busses/i2c-octeon.c1
-rw-r--r--drivers/i2c/busses/i2c-omap.c150
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c5
-rw-r--r--drivers/i2c/busses/i2c-parport.c4
-rw-r--r--drivers/i2c/busses/i2c-parport.h4
-rw-r--r--drivers/i2c/busses/i2c-pasemi.c4
-rw-r--r--drivers/i2c/busses/i2c-pca-isa.c4
-rw-r--r--drivers/i2c/busses/i2c-pca-platform.c1
-rw-r--r--drivers/i2c/busses/i2c-piix4.c4
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c5
-rw-r--r--drivers/i2c/busses/i2c-pnx.c1
-rw-r--r--drivers/i2c/busses/i2c-powermac.c4
-rw-r--r--drivers/i2c/busses/i2c-puv3.c1
-rw-r--r--drivers/i2c/busses/i2c-pxa.c20
-rw-r--r--drivers/i2c/busses/i2c-qup.c1
-rw-r--r--drivers/i2c/busses/i2c-rcar.c125
-rw-r--r--drivers/i2c/busses/i2c-riic.c1
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c252
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c34
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c1
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c249
-rw-r--r--drivers/i2c/busses/i2c-sibyte.c4
-rw-r--r--drivers/i2c/busses/i2c-simtec.c5
-rw-r--r--drivers/i2c/busses/i2c-sirf.c1
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c4
-rw-r--r--drivers/i2c/busses/i2c-sis630.c4
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c4
-rw-r--r--drivers/i2c/busses/i2c-st.c1
-rw-r--r--drivers/i2c/busses/i2c-stu300.c1
-rw-r--r--drivers/i2c/busses/i2c-sun6i-p2wi.c1
-rw-r--r--drivers/i2c/busses/i2c-taos-evm.c4
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1
-rw-r--r--drivers/i2c/busses/i2c-versatile.c1
-rw-r--r--drivers/i2c/busses/i2c-via.c4
-rw-r--r--drivers/i2c/busses/i2c-viapro.c4
-rw-r--r--drivers/i2c/busses/i2c-wmt.c1
-rw-r--r--drivers/i2c/busses/i2c-xiic.c63
-rw-r--r--drivers/i2c/busses/i2c-xlr.c1
-rw-r--r--drivers/i2c/busses/scx200_acb.c5
-rw-r--r--drivers/i2c/i2c-boardinfo.c5
-rw-r--r--drivers/i2c/i2c-core.c220
-rw-r--r--drivers/i2c/i2c-core.h5
-rw-r--r--drivers/i2c/i2c-dev.c5
-rw-r--r--drivers/i2c/i2c-mux.c12
-rw-r--r--drivers/i2c/i2c-slave-eeprom.c170
-rw-r--r--drivers/i2c/i2c-smbus.c5
-rw-r--r--drivers/i2c/i2c-stub.c4
-rw-r--r--drivers/i2c/muxes/i2c-arb-gpio-challenge.c1
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpio.c1
-rw-r--r--drivers/i2c/muxes/i2c-mux-pinctrl.c1
101 files changed, 3862 insertions, 504 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index b51a402752c..8c9e619f302 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -110,6 +110,16 @@ config I2C_STUB
If you don't know what to do here, definitely say N.
+config I2C_SLAVE
+ bool "I2C slave support"
+
+if I2C_SLAVE
+
+config I2C_SLAVE_EEPROM
+ tristate "I2C eeprom slave driver"
+
+endif
+
config I2C_DEBUG_CORE
bool "I2C Core debugging messages"
help
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 1722f50f247..45095b3d16a 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
obj-y += algos/ busses/ muxes/
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
+obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
CFLAGS_i2c-core.o := -Wno-deprecated-declarations
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 65ef9664d5d..899bede81b3 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -12,11 +12,6 @@
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., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA.
* ------------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 8b10f88b13d..580dbf05c14 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -12,11 +12,6 @@
* 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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 34370090b75..270d84bfc2c 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -14,11 +14,6 @@
* 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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
- *
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
* Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey
* <mbailey@littlefeet-inc.com>
diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h
index 1ec703ee788..262ee801975 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.h
+++ b/drivers/i2c/algos/i2c-algo-pcf.h
@@ -12,12 +12,7 @@
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., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA. */
+ GNU General Public License for more details. */
/* -------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl> */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 917c3585f45..c1351d9fb35 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -123,6 +123,7 @@ config I2C_I801
Wildcat Point-LP (PCH)
BayTrail (SOC)
Sunrise Point-H (PCH)
+ Sunrise Point-LP (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -523,6 +524,16 @@ config I2C_IBM_IIC
This driver can also be built as a module. If so, the module
will be called i2c-ibm_iic.
+config I2C_IMG
+ tristate "Imagination Technologies I2C SCB Controller"
+ depends on MIPS || METAG || COMPILE_TEST
+ help
+ Say Y here if you want to use the IMG I2C SCB controller,
+ available on the TZ1090 and other IMG SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-img-scb.
+
config I2C_IMX
tristate "IMX I2C interface"
depends on ARCH_MXC
@@ -553,6 +564,13 @@ config I2C_KEMPLD
This driver can also be built as a module. If so, the module
will be called i2c-kempld.
+config I2C_MESON
+ tristate "Amlogic Meson I2C controller"
+ depends on ARCH_MESON
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Amlogic Meson family of SoCs.
+
config I2C_MPC
tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx"
depends on PPC
@@ -702,7 +720,7 @@ config I2C_RIIC
config I2C_RK3X
tristate "Rockchip RK3xxx I2C adapter"
- depends on OF
+ depends on OF && COMMON_CLK
help
Say Y here to include support for the I2C adapter in Rockchip RK3xxx
SoCs.
@@ -881,6 +899,16 @@ config I2C_DIOLAN_U2C
This driver can also be built as a module. If so, the module
will be called i2c-diolan-u2c.
+config I2C_DLN2
+ tristate "Diolan DLN-2 USB I2C adapter"
+ depends on MFD_DLN2
+ help
+ If you say yes to this option, support will be included for Diolan
+ DLN2, a USB to I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-dln2.
+
config I2C_PARPORT
tristate "Parallel port adapter"
depends on PARPORT
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 78d56c54ba2..5e6c8223719 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -50,9 +50,11 @@ obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
+obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
+obj-$(CONFIG_I2C_MESON) += i2c-meson.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
@@ -87,6 +89,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
+obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index 451e305f797..4f2d7886828 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -14,10 +14,6 @@
* 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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 2fa21ce9682..45c5c488302 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -12,10 +12,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c
index 41fc6837fb8..65e32405497 100644
--- a/drivers/i2c/busses/i2c-amd756-s4882.c
+++ b/drivers/i2c/busses/i2c-amd756-s4882.c
@@ -12,10 +12,6 @@
* 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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index a16f7289135..6c7113d990f 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -15,10 +15,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 917d54588d9..636fd2efad8 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -31,10 +31,13 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/platform_data/dma-atmel.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
+#define AUTOSUSPEND_TIMEOUT 2000
/* AT91 TWI register definitions */
#define AT91_TWI_CR 0x0000 /* Control Register */
@@ -72,7 +75,6 @@ struct at91_twi_pdata {
unsigned clk_max_div;
unsigned clk_offset;
bool has_unre_flag;
- bool has_dma_support;
struct at_dma_slave dma_slave;
};
@@ -434,7 +436,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
}
}
- ret = wait_for_completion_io_timeout(&dev->cmd_complete,
+ ret = wait_for_completion_timeout(&dev->cmd_complete,
dev->adapter.timeout);
if (ret == 0) {
dev_err(dev->dev, "controller timed out\n");
@@ -481,6 +483,10 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
+ ret = pm_runtime_get_sync(dev->dev);
+ if (ret < 0)
+ goto out;
+
/*
* The hardware can handle at most two messages concatenated by a
* repeated start via it's internal address feature.
@@ -488,18 +494,21 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
if (num > 2) {
dev_err(dev->dev,
"cannot handle more than two concatenated messages.\n");
- return 0;
+ ret = 0;
+ goto out;
} else if (num == 2) {
int internal_address = 0;
int i;
if (msg->flags & I2C_M_RD) {
dev_err(dev->dev, "first transfer must be write.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (msg->len > 3) {
dev_err(dev->dev, "first message size must be <= 3.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
/* 1st msg is put into the internal address, start with 2nd */
@@ -523,7 +532,12 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
ret = at91_do_twi_transfer(dev);
- return (ret < 0) ? ret : num;
+ ret = (ret < 0) ? ret : num;
+out:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+
+ return ret;
}
static u32 at91_twi_func(struct i2c_adapter *adapter)
@@ -541,35 +555,30 @@ static struct at91_twi_pdata at91rm9200_config = {
.clk_max_div = 5,
.clk_offset = 3,
.has_unre_flag = true,
- .has_dma_support = false,
};
static struct at91_twi_pdata at91sam9261_config = {
.clk_max_div = 5,
.clk_offset = 4,
.has_unre_flag = false,
- .has_dma_support = false,
};
static struct at91_twi_pdata at91sam9260_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
- .has_dma_support = false,
};
static struct at91_twi_pdata at91sam9g20_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
- .has_dma_support = false,
};
static struct at91_twi_pdata at91sam9g10_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
- .has_dma_support = false,
};
static const struct platform_device_id at91_twi_devtypes[] = {
@@ -598,7 +607,6 @@ 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[] = {
@@ -627,30 +635,11 @@ static const struct of_device_id atmel_twi_dt_ids[] = {
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
#endif
-static bool filter(struct dma_chan *chan, void *pdata)
-{
- struct at91_twi_pdata *sl_pdata = pdata;
- struct at_dma_slave *sl;
-
- if (!sl_pdata)
- return false;
-
- sl = &sl_pdata->dma_slave;
- if (sl && (sl->dma_dev == chan->device->dev)) {
- chan->private = sl;
- return true;
- } else {
- return false;
- }
-}
-
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
{
int ret = 0;
- struct at91_twi_pdata *pdata = dev->pdata;
struct dma_slave_config slave_config;
struct at91_twi_dma *dma = &dev->dma;
- dma_cap_mask_t mask;
memset(&slave_config, 0, sizeof(slave_config));
slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR;
@@ -661,22 +650,17 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
slave_config.dst_maxburst = 1;
slave_config.device_fc = false;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- dma->chan_tx = dma_request_slave_channel_compat(mask, filter, pdata,
- dev->dev, "tx");
- if (!dma->chan_tx) {
- dev_err(dev->dev, "can't get a DMA channel for tx\n");
- ret = -EBUSY;
+ dma->chan_tx = dma_request_slave_channel_reason(dev->dev, "tx");
+ if (IS_ERR(dma->chan_tx)) {
+ ret = PTR_ERR(dma->chan_tx);
+ dma->chan_tx = NULL;
goto error;
}
- dma->chan_rx = dma_request_slave_channel_compat(mask, filter, pdata,
- dev->dev, "rx");
- if (!dma->chan_rx) {
- dev_err(dev->dev, "can't get a DMA channel for rx\n");
- ret = -EBUSY;
+ dma->chan_rx = dma_request_slave_channel_reason(dev->dev, "rx");
+ if (IS_ERR(dma->chan_rx)) {
+ ret = PTR_ERR(dma->chan_rx);
+ dma->chan_rx = NULL;
goto error;
}
@@ -697,6 +681,7 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
sg_init_table(&dma->sg, 1);
dma->buf_mapped = false;
dma->xfer_in_progress = false;
+ dev->use_dma = true;
dev_info(dev->dev, "using %s (tx) and %s (rx) for DMA transfers\n",
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
@@ -704,7 +689,8 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
return ret;
error:
- dev_info(dev->dev, "can't use DMA\n");
+ if (ret != -EPROBE_DEFER)
+ dev_info(dev->dev, "can't use DMA, error %d\n", ret);
if (dma->chan_rx)
dma_release_channel(dma->chan_rx);
if (dma->chan_tx)
@@ -772,9 +758,10 @@ static int at91_twi_probe(struct platform_device *pdev)
}
clk_prepare_enable(dev->clk);
- if (dev->pdata->has_dma_support) {
- if (at91_twi_configure_dma(dev, phy_addr) == 0)
- dev->use_dma = true;
+ if (dev->dev->of_node) {
+ rc = at91_twi_configure_dma(dev, phy_addr);
+ if (rc == -EPROBE_DEFER)
+ return rc;
}
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
@@ -795,11 +782,20 @@ static int at91_twi_probe(struct platform_device *pdev)
dev->adapter.timeout = AT91_I2C_TIMEOUT;
dev->adapter.dev.of_node = pdev->dev.of_node;
+ pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_active(dev->dev);
+ pm_runtime_enable(dev->dev);
+
rc = i2c_add_numbered_adapter(&dev->adapter);
if (rc) {
dev_err(dev->dev, "Adapter %s registration failed\n",
dev->adapter.name);
clk_disable_unprepare(dev->clk);
+
+ pm_runtime_disable(dev->dev);
+ pm_runtime_set_suspended(dev->dev);
+
return rc;
}
@@ -814,6 +810,9 @@ static int at91_twi_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter);
clk_disable_unprepare(dev->clk);
+ pm_runtime_disable(dev->dev);
+ pm_runtime_set_suspended(dev->dev);
+
return 0;
}
@@ -823,7 +822,9 @@ static int at91_twi_runtime_suspend(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
- clk_disable(twi_dev->clk);
+ clk_disable_unprepare(twi_dev->clk);
+
+ pinctrl_pm_select_sleep_state(dev);
return 0;
}
@@ -832,10 +833,38 @@ static int at91_twi_runtime_resume(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
- return clk_enable(twi_dev->clk);
+ pinctrl_pm_select_default_state(dev);
+
+ return clk_prepare_enable(twi_dev->clk);
+}
+
+static int at91_twi_suspend_noirq(struct device *dev)
+{
+ if (!pm_runtime_status_suspended(dev))
+ at91_twi_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int at91_twi_resume_noirq(struct device *dev)
+{
+ int ret;
+
+ if (!pm_runtime_status_suspended(dev)) {
+ ret = at91_twi_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_request_autosuspend(dev);
+
+ return 0;
}
static const struct dev_pm_ops at91_twi_pm = {
+ .suspend_noirq = at91_twi_suspend_noirq,
+ .resume_noirq = at91_twi_resume_noirq,
.runtime_suspend = at91_twi_runtime_suspend,
.runtime_resume = at91_twi_runtime_resume,
};
@@ -851,7 +880,6 @@ static struct platform_driver at91_twi_driver = {
.id_table = at91_twi_devtypes,
.driver = {
.name = "at91_i2c",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(atmel_twi_dt_ids),
.pm = at91_twi_pm_ops,
},
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index 8762458ca7d..a6aae84e570 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -21,10 +21,6 @@
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/delay.h>
@@ -417,7 +413,6 @@ static const struct dev_pm_ops i2c_au1550_pmops = {
static struct platform_driver au1xpsc_smbus_driver = {
.driver = {
.name = "au1xpsc_smbus",
- .owner = THIS_MODULE,
.pm = AU1XPSC_SMBUS_PMOPS,
},
.probe = i2c_au1550_probe,
diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c
index 18a74a6751a..2c9d9b1c8e6 100644
--- a/drivers/i2c/busses/i2c-bcm-kona.c
+++ b/drivers/i2c/busses/i2c-bcm-kona.c
@@ -895,7 +895,6 @@ MODULE_DEVICE_TABLE(of, bcm_kona_i2c_of_match);
static struct platform_driver bcm_kona_i2c_driver = {
.driver = {
.name = "bcm-kona-i2c",
- .owner = THIS_MODULE,
.of_match_table = bcm_kona_i2c_of_match,
},
.probe = bcm_kona_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index 4b8ecd0b366..5d6feb937b9 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -313,7 +313,6 @@ static struct platform_driver bcm2835_i2c_driver = {
.remove = bcm2835_i2c_remove,
.driver = {
.name = "i2c-bcm2835",
- .owner = THIS_MODULE,
.of_match_table = bcm2835_i2c_of_match,
},
};
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index 067c1615e96..af162b4c7a6 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -717,7 +717,6 @@ static struct platform_driver i2c_bfin_twi_driver = {
.remove = i2c_bfin_twi_remove,
.driver = {
.name = "i2c-bfin-twi",
- .owner = THIS_MODULE,
.pm = I2C_BFIN_TWI_PM_OPS,
},
};
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 63f3f03ecc9..626f74ecd4b 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -111,6 +111,8 @@
#define CDNS_I2C_DIVA_MAX 4
#define CDNS_I2C_DIVB_MAX 64
+#define CDNS_I2C_TIMEOUT_MAX 0xFF
+
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
@@ -852,6 +854,15 @@ static int cdns_i2c_probe(struct platform_device *pdev)
goto err_clk_dis;
}
+ /*
+ * Cadence I2C controller has a bug wherein it generates
+ * invalid read transaction after HW timeout in master receiver mode.
+ * HW timeout is not used by this driver and the interrupt is disabled.
+ * But the feature itself cannot be disabled. Hence maximum value
+ * is written to this register to reduce the chances of error.
+ */
+ cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
+
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
@@ -890,7 +901,6 @@ MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
static struct platform_driver cdns_i2c_drv = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = cdns_i2c_of_match,
.pm = &cdns_i2c_dev_pm_ops,
},
diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c
index bdf040fd867..b4f91e48948 100644
--- a/drivers/i2c/busses/i2c-cbus-gpio.c
+++ b/drivers/i2c/busses/i2c-cbus-gpio.c
@@ -287,7 +287,6 @@ static struct platform_driver cbus_i2c_driver = {
.probe = cbus_i2c_probe,
.remove = cbus_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "i2c-cbus-gpio",
.of_match_table = of_match_ptr(i2c_cbus_dt_ids),
},
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index f3b89a4698b..2d466538b2e 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -23,10 +23,6 @@
* 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>
@@ -720,7 +716,6 @@ static struct platform_driver cpm_i2c_driver = {
.remove = cpm_i2c_remove,
.driver = {
.name = "fsl-i2c-cpm",
- .owner = THIS_MODULE,
.of_match_table = cpm_i2c_match,
},
};
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 4d961471912..6dc7ff5d3d9 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -17,10 +17,6 @@
* 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.
* ----------------------------------------------------------------------------
*
*/
@@ -372,8 +368,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
flag |= DAVINCI_I2C_MDR_STP;
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
- r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
- dev->adapter.timeout);
+ r = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout);
if (r == 0) {
dev_err(dev->dev, "controller timed out\n");
davinci_i2c_recover_bus(dev);
@@ -384,7 +379,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
if (dev->buf_len) {
/* This should be 0 if all bytes were transferred
* or dev->cmd_err denotes an error.
- * A signal may have aborted the transfer.
*/
if (r >= 0) {
dev_err(dev->dev, "abnormal termination buf_len=%i\n",
@@ -411,11 +405,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
if (dev->cmd_err & DAVINCI_I2C_STR_NACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return msg->len;
- if (stop) {
- w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
- w |= DAVINCI_I2C_MDR_STP;
- davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
- }
+ w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ w |= DAVINCI_I2C_MDR_STP;
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
return -EREMOTEIO;
}
return -EIO;
@@ -640,13 +632,17 @@ static int davinci_i2c_probe(struct platform_device *pdev)
{
struct davinci_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *mem, *irq;
- int r;
-
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
- dev_err(&pdev->dev, "no irq resource?\n");
- return -ENODEV;
+ struct resource *mem;
+ int r, irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ if (!irq)
+ irq = -ENXIO;
+ if (irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "can't get irq resource ret=%d\n", irq);
+ return irq;
}
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_i2c_dev),
@@ -661,7 +657,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
init_completion(&dev->xfr_complete);
#endif
dev->dev = &pdev->dev;
- dev->irq = irq->start;
+ dev->irq = irq;
dev->pdata = dev_get_platdata(&pdev->dev);
platform_set_drvdata(pdev, dev);
@@ -793,7 +789,6 @@ static struct platform_driver davinci_i2c_driver = {
.remove = davinci_i2c_remove,
.driver = {
.name = "i2c_davinci",
- .owner = THIS_MODULE,
.pm = davinci_i2c_pm_ops,
.of_match_table = davinci_i2c_of_match,
},
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 3c20e4bd6dd..23628b7bfb8 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -18,10 +18,6 @@
* 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.
* ----------------------------------------------------------------------------
*
*/
@@ -363,7 +359,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
}
/* Configure Tx/Rx FIFO threshold levels */
- dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
+ dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
dw_writel(dev, 0, DW_IC_RX_TL);
/* configure the i2c master */
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index d66b6cbc9ed..5a410ef17ab 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -18,10 +18,6 @@
* 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.
* ----------------------------------------------------------------------------
*
*/
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index d31d313ab4f..acb40f95db7 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -19,10 +19,6 @@
* 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.
* ----------------------------------------------------------------------------
*
*/
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index a7431150acf..2b463c313e4 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -18,10 +18,6 @@
* 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.
* ----------------------------------------------------------------------------
*
*/
@@ -331,7 +327,6 @@ static struct platform_driver dw_i2c_driver = {
.remove = dw_i2c_remove,
.driver = {
.name = "i2c_designware",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(dw_i2c_of_match),
.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),
.pm = &dw_i2c_dev_pm_ops,
diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c
new file mode 100644
index 00000000000..b3fb86af4cb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-dln2.c
@@ -0,0 +1,262 @@
+/*
+ * Driver for the Diolan DLN-2 USB-I2C adapter
+ *
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Derived from:
+ * i2c-diolan-u2c.c
+ * Copyright (c) 2010-2011 Ericsson AB
+ *
+ * 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, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#define DLN2_I2C_MODULE_ID 0x03
+#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID)
+
+/* I2C commands */
+#define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00)
+#define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01)
+#define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02)
+#define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03)
+#define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06)
+#define DLN2_I2C_READ DLN2_I2C_CMD(0x07)
+#define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08)
+#define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09)
+#define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A)
+#define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B)
+#define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C)
+#define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D)
+#define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E)
+
+#define DLN2_I2C_MAX_XFER_SIZE 256
+#define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16)
+
+struct dln2_i2c {
+ struct platform_device *pdev;
+ struct i2c_adapter adapter;
+ u8 port;
+ /*
+ * Buffer to hold the packet for read or write transfers. One is enough
+ * since we can't have multiple transfers in parallel on the i2c bus.
+ */
+ void *buf;
+};
+
+static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable)
+{
+ u16 cmd;
+ struct {
+ u8 port;
+ } tx;
+
+ tx.port = dln2->port;
+
+ if (enable)
+ cmd = DLN2_I2C_ENABLE;
+ else
+ cmd = DLN2_I2C_DISABLE;
+
+ return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx));
+}
+
+static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr,
+ u8 *data, u16 data_len)
+{
+ int ret;
+ struct {
+ u8 port;
+ u8 addr;
+ u8 mem_addr_len;
+ __le32 mem_addr;
+ __le16 buf_len;
+ u8 buf[DLN2_I2C_MAX_XFER_SIZE];
+ } __packed *tx = dln2->buf;
+ unsigned len;
+
+ BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE);
+
+ tx->port = dln2->port;
+ tx->addr = addr;
+ tx->mem_addr_len = 0;
+ tx->mem_addr = 0;
+ tx->buf_len = cpu_to_le16(data_len);
+ memcpy(tx->buf, data, data_len);
+
+ len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE;
+ ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len);
+ if (ret < 0)
+ return ret;
+
+ return data_len;
+}
+
+static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data,
+ u16 data_len)
+{
+ int ret;
+ struct {
+ u8 port;
+ u8 addr;
+ u8 mem_addr_len;
+ __le32 mem_addr;
+ __le16 buf_len;
+ } __packed tx;
+ struct {
+ __le16 buf_len;
+ u8 buf[DLN2_I2C_MAX_XFER_SIZE];
+ } __packed *rx = dln2->buf;
+ unsigned rx_len = sizeof(*rx);
+
+ BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE);
+
+ tx.port = dln2->port;
+ tx.addr = addr;
+ tx.mem_addr_len = 0;
+ tx.mem_addr = 0;
+ tx.buf_len = cpu_to_le16(data_len);
+
+ ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx),
+ rx, &rx_len);
+ if (ret < 0)
+ return ret;
+ if (rx_len < sizeof(rx->buf_len) + data_len)
+ return -EPROTO;
+ if (le16_to_cpu(rx->buf_len) != data_len)
+ return -EPROTO;
+
+ memcpy(data, rx->buf, data_len);
+
+ return data_len;
+}
+
+static int dln2_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct dln2_i2c *dln2 = i2c_get_adapdata(adapter);
+ struct i2c_msg *pmsg;
+ struct device *dev = &dln2->adapter.dev;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ int ret;
+
+ pmsg = &msgs[i];
+
+ if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) {
+ dev_warn(dev, "maximum transfer size exceeded\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (pmsg->flags & I2C_M_RD) {
+ ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf,
+ pmsg->len);
+ if (ret < 0)
+ return ret;
+
+ pmsg->len = ret;
+ } else {
+ ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf,
+ pmsg->len);
+ if (ret != pmsg->len)
+ return -EPROTO;
+ }
+ }
+
+ return num;
+}
+
+static u32 dln2_i2c_func(struct i2c_adapter *a)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+static const struct i2c_algorithm dln2_i2c_usb_algorithm = {
+ .master_xfer = dln2_i2c_xfer,
+ .functionality = dln2_i2c_func,
+};
+
+static int dln2_i2c_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct dln2_i2c *dln2;
+ struct device *dev = &pdev->dev;
+ struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL);
+ if (!dln2)
+ return -ENOMEM;
+
+ dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL);
+ if (!dln2->buf)
+ return -ENOMEM;
+
+ dln2->pdev = pdev;
+ dln2->port = pdata->port;
+
+ /* setup i2c adapter description */
+ dln2->adapter.owner = THIS_MODULE;
+ dln2->adapter.class = I2C_CLASS_HWMON;
+ dln2->adapter.algo = &dln2_i2c_usb_algorithm;
+ dln2->adapter.dev.parent = dev;
+ i2c_set_adapdata(&dln2->adapter, dln2);
+ snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d",
+ "dln2-i2c", dev_name(pdev->dev.parent), dln2->port);
+
+ platform_set_drvdata(pdev, dln2);
+
+ /* initialize the i2c interface */
+ ret = dln2_i2c_enable(dln2, true);
+ if (ret < 0) {
+ dev_err(dev, "failed to initialize adapter: %d\n", ret);
+ return ret;
+ }
+
+ /* and finally attach to i2c layer */
+ ret = i2c_add_adapter(&dln2->adapter);
+ if (ret < 0) {
+ dev_err(dev, "failed to add I2C adapter: %d\n", ret);
+ goto out_disable;
+ }
+
+ return 0;
+
+out_disable:
+ dln2_i2c_enable(dln2, false);
+
+ return ret;
+}
+
+static int dln2_i2c_remove(struct platform_device *pdev)
+{
+ struct dln2_i2c *dln2 = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&dln2->adapter);
+ dln2_i2c_enable(dln2, false);
+
+ return 0;
+}
+
+static struct platform_driver dln2_i2c_driver = {
+ .driver.name = "dln2-i2c",
+ .probe = dln2_i2c_probe,
+ .remove = dln2_i2c_remove,
+};
+
+module_platform_driver(dln2_i2c_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-i2c");
diff --git a/drivers/i2c/busses/i2c-efm32.c b/drivers/i2c/busses/i2c-efm32.c
index 10b8323b08d..8eff6273887 100644
--- a/drivers/i2c/busses/i2c-efm32.c
+++ b/drivers/i2c/busses/i2c-efm32.c
@@ -473,7 +473,6 @@ static struct platform_driver efm32_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = efm32_i2c_dt_ids,
},
};
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index a44ea13d143..76e699f9ed9 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -9,10 +9,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
index 485497066ed..92e8c0ce162 100644
--- a/drivers/i2c/busses/i2c-elektor.c
+++ b/drivers/i2c/busses/i2c-elektor.c
@@ -12,11 +12,7 @@
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. */
+ GNU General Public License for more details. */
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 81e6263cd7d..b29c7500461 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -457,7 +457,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
goto stop;
} else if (int_status & HSI2C_INT_TIMEOUT) {
dev_dbg(i2c->dev, "Accessing device timed out\n");
- i2c->state = -EAGAIN;
+ i2c->state = -ETIMEDOUT;
goto stop;
}
} else if (int_status & HSI2C_INT_I2C) {
@@ -476,7 +476,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
goto stop;
} else if (trans_status & HSI2C_TIMEOUT_AUTO) {
dev_dbg(i2c->dev, "Accessing device timed out\n");
- i2c->state = -EAGAIN;
+ i2c->state = -ETIMEDOUT;
goto stop;
} else if (trans_status & HSI2C_TRANS_DONE) {
i2c->trans_done = 1;
@@ -861,7 +861,6 @@ static struct platform_driver exynos5_i2c_driver = {
.probe = exynos5_i2c_probe,
.remove = exynos5_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "exynos5-hsi2c",
.pm = &exynos5_i2c_dev_pm_ops,
.of_match_table = exynos5_i2c_match,
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 933f1e453e4..34cfc0ebdcb 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -260,7 +260,6 @@ MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
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,
diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c
index 512fcfabc18..56dc69e7349 100644
--- a/drivers/i2c/busses/i2c-highlander.c
+++ b/drivers/i2c/busses/i2c-highlander.c
@@ -456,7 +456,6 @@ static int highlander_i2c_remove(struct platform_device *pdev)
static struct platform_driver highlander_i2c_driver = {
.driver = {
.name = "i2c-highlander",
- .owner = THIS_MODULE,
},
.probe = highlander_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c
index 9490d0f4255..8fe78d08e01 100644
--- a/drivers/i2c/busses/i2c-hix5hd2.c
+++ b/drivers/i2c/busses/i2c-hix5hd2.c
@@ -528,7 +528,7 @@ static int hix5hd2_i2c_runtime_resume(struct device *dev)
#endif
static const struct dev_pm_ops hix5hd2_i2c_pm_ops = {
- SET_PM_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend,
+ SET_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend,
hix5hd2_i2c_runtime_resume,
NULL)
};
diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c
index 14d2b76de25..b7864cf42a7 100644
--- a/drivers/i2c/busses/i2c-hydra.c
+++ b/drivers/i2c/busses/i2c-hydra.c
@@ -15,10 +15,6 @@
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>
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 7cfc183b3d6..8fafb254e42 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 - 2012 Jean Delvare <jdelvare@suse.de>
+ Copyright (C) 2007 - 2014 Jean Delvare <jdelvare@suse.de>
Copyright (C) 2010 Intel Corporation,
David Woodhouse <dwmw2@infradead.org>
@@ -15,10 +15,6 @@
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.
*/
/*
@@ -63,6 +59,7 @@
* Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
* BayTrail (SOC) 0x0f12 32 hard yes yes yes
* Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes
+ * Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
@@ -113,12 +110,16 @@
/* PCI Address Constants */
#define SMBBAR 4
+#define SMBPCICTL 0x004
#define SMBPCISTS 0x006
#define SMBHSTCFG 0x040
/* Host status bits for SMBPCISTS */
#define SMBPCISTS_INTS 0x08
+/* Control bits for SMBPCICTL */
+#define SMBPCICTL_INTDIS 0x0400
+
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN 1
#define SMBHSTCFG_SMB_SMI_EN 2
@@ -186,6 +187,7 @@
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23
struct i801_mux_config {
char *gpio_chip;
@@ -375,6 +377,7 @@ static int i801_transaction(struct i801_priv *priv, int xact)
{
int status;
int result;
+ const struct i2c_adapter *adap = &priv->adapter;
result = i801_check_pre(priv);
if (result < 0)
@@ -383,7 +386,14 @@ static int i801_transaction(struct i801_priv *priv, int xact)
if (priv->features & FEATURE_IRQ) {
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
SMBHSTCNT(priv));
- wait_event(priv->waitq, (status = priv->status));
+ result = wait_event_timeout(priv->waitq,
+ (status = priv->status),
+ adap->timeout);
+ if (!result) {
+ status = -ETIMEDOUT;
+ dev_warn(&priv->pci_dev->dev,
+ "Timeout waiting for interrupt!\n");
+ }
priv->status = 0;
return i801_check_post(priv, status);
}
@@ -497,9 +507,6 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
return IRQ_NONE;
status = inb_p(SMBHSTSTS(priv));
- if (status != 0x42)
- dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status);
-
if (status & SMBHSTSTS_BYTE_DONE)
i801_isr_byte_done(priv);
@@ -531,6 +538,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
int smbcmd;
int status;
int result;
+ const struct i2c_adapter *adap = &priv->adapter;
result = i801_check_pre(priv);
if (result < 0)
@@ -559,7 +567,14 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
priv->data = &data->block[1];
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
- wait_event(priv->waitq, (status = priv->status));
+ result = wait_event_timeout(priv->waitq,
+ (status = priv->status),
+ adap->timeout);
+ if (!result) {
+ status = -ETIMEDOUT;
+ dev_warn(&priv->pci_dev->dev,
+ "Timeout waiting for interrupt!\n");
+ }
priv->status = 0;
return i801_check_post(priv, status);
}
@@ -833,6 +848,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS) },
{ 0, }
};
@@ -1216,6 +1232,25 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
outb_p(inb_p(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
+ /* Default timeout in interrupt mode: 200 ms */
+ priv->adapter.timeout = HZ / 5;
+
+ if (priv->features & FEATURE_IRQ) {
+ u16 pcictl, pcists;
+
+ /* Complain if an interrupt is already pending */
+ pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
+ if (pcists & SMBPCISTS_INTS)
+ dev_warn(&dev->dev, "An interrupt is pending!\n");
+
+ /* Check if interrupts have been disabled */
+ pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl);
+ if (pcictl & SMBPCICTL_INTDIS) {
+ dev_info(&dev->dev, "Interrupts are disabled\n");
+ priv->features &= ~FEATURE_IRQ;
+ }
+ }
+
if (priv->features & FEATURE_IRQ) {
init_waitqueue_head(&priv->waitq);
@@ -1224,10 +1259,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (err) {
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
dev->irq, err);
- goto exit_release;
+ priv->features &= ~FEATURE_IRQ;
}
- dev_info(&dev->dev, "SMBus using PCI Interrupt\n");
}
+ dev_info(&dev->dev, "SMBus using %s\n",
+ priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
/* set up the sysfs linkage to our parent device */
priv->adapter.dev.parent = &dev->dev;
@@ -1254,7 +1290,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
exit_free_irq:
if (priv->features & FEATURE_IRQ)
free_irq(dev->irq, priv);
-exit_release:
pci_release_region(dev, SMBBAR);
exit:
kfree(priv);
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 274312c96b1..722f839cfa3 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -802,7 +802,6 @@ static const struct of_device_id ibm_iic_match[] = {
static struct platform_driver ibm_iic_driver = {
.driver = {
.name = "ibm-iic",
- .owner = THIS_MODULE,
.of_match_table = ibm_iic_match,
},
.probe = iic_probe,
diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c
new file mode 100644
index 00000000000..0fcc1694c60
--- /dev/null
+++ b/drivers/i2c/busses/i2c-img-scb.c
@@ -0,0 +1,1412 @@
+/*
+ * I2C adapter for the IMG Serial Control Bus (SCB) IP block.
+ *
+ * Copyright (C) 2009, 2010, 2012, 2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * There are three ways that this I2C controller can be driven:
+ *
+ * - Raw control of the SDA and SCK signals.
+ *
+ * This corresponds to MODE_RAW, which takes control of the signals
+ * directly for a certain number of clock cycles (the INT_TIMING
+ * interrupt can be used for timing).
+ *
+ * - Atomic commands. A low level I2C symbol (such as generate
+ * start/stop/ack/nack bit, generate byte, receive byte, and receive
+ * ACK) is given to the hardware, with detection of completion by bits
+ * in the LINESTAT register.
+ *
+ * This mode of operation is used by MODE_ATOMIC, which uses an I2C
+ * state machine in the interrupt handler to compose/react to I2C
+ * transactions using atomic mode commands, and also by MODE_SEQUENCE,
+ * which emits a simple fixed sequence of atomic mode commands.
+ *
+ * Due to software control, the use of atomic commands usually results
+ * in suboptimal use of the bus, with gaps between the I2C symbols while
+ * the driver decides what to do next.
+ *
+ * - Automatic mode. A bus address, and whether to read/write is
+ * specified, and the hardware takes care of the I2C state machine,
+ * using a FIFO to send/receive bytes of data to an I2C slave. The
+ * driver just has to keep the FIFO drained or filled in response to the
+ * appropriate FIFO interrupts.
+ *
+ * This corresponds to MODE_AUTOMATIC, which manages the FIFOs and deals
+ * with control of repeated start bits between I2C messages.
+ *
+ * Use of automatic mode and the FIFO can make much more efficient use
+ * of the bus compared to individual atomic commands, with potentially
+ * no wasted time between I2C symbols or I2C messages.
+ *
+ * In most cases MODE_AUTOMATIC is used, however if any of the messages in
+ * a transaction are zero byte writes (e.g. used by i2cdetect for probing
+ * the bus), MODE_ATOMIC must be used since automatic mode is normally
+ * started by the writing of data into the FIFO.
+ *
+ * The other modes are used in specific circumstances where MODE_ATOMIC and
+ * MODE_AUTOMATIC aren't appropriate. MODE_RAW is used to implement a bus
+ * recovery routine. MODE_SEQUENCE is used to reset the bus and make sure
+ * it is in a sane state.
+ *
+ * Notice that the driver implements a timer-based timeout mechanism.
+ * The reason for this mechanism is to reduce the number of interrupts
+ * received in automatic mode.
+ *
+ * The driver would get a slave event and transaction done interrupts for
+ * each atomic mode command that gets completed. However, these events are
+ * not needed in automatic mode, becase those atomic mode commands are
+ * managed automatically by the hardware.
+ *
+ * In practice, normal I2C transactions will be complete well before you
+ * get the timer interrupt, as the timer is re-scheduled during FIFO
+ * maintenance and disabled after the transaction is complete.
+ *
+ * In this way normal automatic mode operation isn't impacted by
+ * unnecessary interrupts, but the exceptional abort condition can still be
+ * detected (with a slight delay).
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+/* Register offsets */
+
+#define SCB_STATUS_REG 0x00
+#define SCB_OVERRIDE_REG 0x04
+#define SCB_READ_ADDR_REG 0x08
+#define SCB_READ_COUNT_REG 0x0c
+#define SCB_WRITE_ADDR_REG 0x10
+#define SCB_READ_DATA_REG 0x14
+#define SCB_WRITE_DATA_REG 0x18
+#define SCB_FIFO_STATUS_REG 0x1c
+#define SCB_CONTROL_SOFT_RESET 0x1f
+#define SCB_CLK_SET_REG 0x3c
+#define SCB_INT_STATUS_REG 0x40
+#define SCB_INT_CLEAR_REG 0x44
+#define SCB_INT_MASK_REG 0x48
+#define SCB_CONTROL_REG 0x4c
+#define SCB_TIME_TPL_REG 0x50
+#define SCB_TIME_TPH_REG 0x54
+#define SCB_TIME_TP2S_REG 0x58
+#define SCB_TIME_TBI_REG 0x60
+#define SCB_TIME_TSL_REG 0x64
+#define SCB_TIME_TDL_REG 0x68
+#define SCB_TIME_TSDL_REG 0x6c
+#define SCB_TIME_TSDH_REG 0x70
+#define SCB_READ_XADDR_REG 0x74
+#define SCB_WRITE_XADDR_REG 0x78
+#define SCB_WRITE_COUNT_REG 0x7c
+#define SCB_CORE_REV_REG 0x80
+#define SCB_TIME_TCKH_REG 0x84
+#define SCB_TIME_TCKL_REG 0x88
+#define SCB_FIFO_FLUSH_REG 0x8c
+#define SCB_READ_FIFO_REG 0x94
+#define SCB_CLEAR_REG 0x98
+
+/* SCB_CONTROL_REG bits */
+
+#define SCB_CONTROL_CLK_ENABLE 0x1e0
+#define SCB_CONTROL_TRANSACTION_HALT 0x200
+
+#define FIFO_READ_FULL BIT(0)
+#define FIFO_READ_EMPTY BIT(1)
+#define FIFO_WRITE_FULL BIT(2)
+#define FIFO_WRITE_EMPTY BIT(3)
+
+/* SCB_CLK_SET_REG bits */
+#define SCB_FILT_DISABLE BIT(31)
+#define SCB_FILT_BYPASS BIT(30)
+#define SCB_FILT_INC_MASK 0x7f
+#define SCB_FILT_INC_SHIFT 16
+#define SCB_INC_MASK 0x7f
+#define SCB_INC_SHIFT 8
+
+/* SCB_INT_*_REG bits */
+
+#define INT_BUS_INACTIVE BIT(0)
+#define INT_UNEXPECTED_START BIT(1)
+#define INT_SCLK_LOW_TIMEOUT BIT(2)
+#define INT_SDAT_LOW_TIMEOUT BIT(3)
+#define INT_WRITE_ACK_ERR BIT(4)
+#define INT_ADDR_ACK_ERR BIT(5)
+#define INT_FIFO_FULL BIT(9)
+#define INT_FIFO_FILLING BIT(10)
+#define INT_FIFO_EMPTY BIT(11)
+#define INT_FIFO_EMPTYING BIT(12)
+#define INT_TRANSACTION_DONE BIT(15)
+#define INT_SLAVE_EVENT BIT(16)
+#define INT_TIMING BIT(18)
+
+#define INT_FIFO_FULL_FILLING (INT_FIFO_FULL | INT_FIFO_FILLING)
+#define INT_FIFO_EMPTY_EMPTYING (INT_FIFO_EMPTY | INT_FIFO_EMPTYING)
+
+/* Level interrupts need clearing after handling instead of before */
+#define INT_LEVEL 0x01e00
+
+/* Don't allow any interrupts while the clock may be off */
+#define INT_ENABLE_MASK_INACTIVE 0x00000
+
+/* Interrupt masks for the different driver modes */
+
+#define INT_ENABLE_MASK_RAW INT_TIMING
+
+#define INT_ENABLE_MASK_ATOMIC (INT_TRANSACTION_DONE | \
+ INT_SLAVE_EVENT | \
+ INT_ADDR_ACK_ERR | \
+ INT_WRITE_ACK_ERR)
+
+#define INT_ENABLE_MASK_AUTOMATIC (INT_SCLK_LOW_TIMEOUT | \
+ INT_ADDR_ACK_ERR | \
+ INT_WRITE_ACK_ERR | \
+ INT_FIFO_FULL | \
+ INT_FIFO_FILLING | \
+ INT_FIFO_EMPTY | \
+ INT_FIFO_EMPTYING)
+
+#define INT_ENABLE_MASK_WAITSTOP (INT_SLAVE_EVENT | \
+ INT_ADDR_ACK_ERR | \
+ INT_WRITE_ACK_ERR)
+
+/* SCB_STATUS_REG fields */
+
+#define LINESTAT_SCLK_LINE_STATUS BIT(0)
+#define LINESTAT_SCLK_EN BIT(1)
+#define LINESTAT_SDAT_LINE_STATUS BIT(2)
+#define LINESTAT_SDAT_EN BIT(3)
+#define LINESTAT_DET_START_STATUS BIT(4)
+#define LINESTAT_DET_STOP_STATUS BIT(5)
+#define LINESTAT_DET_ACK_STATUS BIT(6)
+#define LINESTAT_DET_NACK_STATUS BIT(7)
+#define LINESTAT_BUS_IDLE BIT(8)
+#define LINESTAT_T_DONE_STATUS BIT(9)
+#define LINESTAT_SCLK_OUT_STATUS BIT(10)
+#define LINESTAT_SDAT_OUT_STATUS BIT(11)
+#define LINESTAT_GEN_LINE_MASK_STATUS BIT(12)
+#define LINESTAT_START_BIT_DET BIT(13)
+#define LINESTAT_STOP_BIT_DET BIT(14)
+#define LINESTAT_ACK_DET BIT(15)
+#define LINESTAT_NACK_DET BIT(16)
+#define LINESTAT_INPUT_HELD_V BIT(17)
+#define LINESTAT_ABORT_DET BIT(18)
+#define LINESTAT_ACK_OR_NACK_DET (LINESTAT_ACK_DET | LINESTAT_NACK_DET)
+#define LINESTAT_INPUT_DATA 0xff000000
+#define LINESTAT_INPUT_DATA_SHIFT 24
+
+#define LINESTAT_CLEAR_SHIFT 13
+#define LINESTAT_LATCHED (0x3f << LINESTAT_CLEAR_SHIFT)
+
+/* SCB_OVERRIDE_REG fields */
+
+#define OVERRIDE_SCLK_OVR BIT(0)
+#define OVERRIDE_SCLKEN_OVR BIT(1)
+#define OVERRIDE_SDAT_OVR BIT(2)
+#define OVERRIDE_SDATEN_OVR BIT(3)
+#define OVERRIDE_MASTER BIT(9)
+#define OVERRIDE_LINE_OVR_EN BIT(10)
+#define OVERRIDE_DIRECT BIT(11)
+#define OVERRIDE_CMD_SHIFT 4
+#define OVERRIDE_CMD_MASK 0x1f
+#define OVERRIDE_DATA_SHIFT 24
+
+#define OVERRIDE_SCLK_DOWN (OVERRIDE_LINE_OVR_EN | \
+ OVERRIDE_SCLKEN_OVR)
+#define OVERRIDE_SCLK_UP (OVERRIDE_LINE_OVR_EN | \
+ OVERRIDE_SCLKEN_OVR | \
+ OVERRIDE_SCLK_OVR)
+#define OVERRIDE_SDAT_DOWN (OVERRIDE_LINE_OVR_EN | \
+ OVERRIDE_SDATEN_OVR)
+#define OVERRIDE_SDAT_UP (OVERRIDE_LINE_OVR_EN | \
+ OVERRIDE_SDATEN_OVR | \
+ OVERRIDE_SDAT_OVR)
+
+/* OVERRIDE_CMD values */
+
+#define CMD_PAUSE 0x00
+#define CMD_GEN_DATA 0x01
+#define CMD_GEN_START 0x02
+#define CMD_GEN_STOP 0x03
+#define CMD_GEN_ACK 0x04
+#define CMD_GEN_NACK 0x05
+#define CMD_RET_DATA 0x08
+#define CMD_RET_ACK 0x09
+
+/* Fixed timing values */
+
+#define TIMEOUT_TBI 0x0
+#define TIMEOUT_TSL 0xffff
+#define TIMEOUT_TDL 0x0
+
+/* Transaction timeout */
+
+#define IMG_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+/*
+ * Worst incs are 1 (innacurate) and 16*256 (irregular).
+ * So a sensible inc is the logarithmic mean: 64 (2^6), which is
+ * in the middle of the valid range (0-127).
+ */
+#define SCB_OPT_INC 64
+
+/* Setup the clock enable filtering for 25 ns */
+#define SCB_FILT_GLITCH 25
+
+/*
+ * Bits to return from interrupt handler functions for different modes.
+ * This delays completion until we've finished with the registers, so that the
+ * function waiting for completion can safely disable the clock to save power.
+ */
+#define ISR_COMPLETE_M BIT(31)
+#define ISR_FATAL_M BIT(30)
+#define ISR_WAITSTOP BIT(29)
+#define ISR_STATUS_M 0x0000ffff /* contains +ve errno */
+#define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err)))
+#define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M)
+
+#define REL_SOC_IP_SCB_2_2_1 0x00020201
+
+enum img_i2c_mode {
+ MODE_INACTIVE,
+ MODE_RAW,
+ MODE_ATOMIC,
+ MODE_AUTOMATIC,
+ MODE_SEQUENCE,
+ MODE_FATAL,
+ MODE_WAITSTOP,
+ MODE_SUSPEND,
+};
+
+/* Timing parameters for i2c modes (in ns) */
+struct img_i2c_timings {
+ const char *name;
+ unsigned int max_bitrate;
+ unsigned int tckh, tckl, tsdh, tsdl;
+ unsigned int tp2s, tpl, tph;
+};
+
+/* The timings array must be ordered from slower to faster */
+static struct img_i2c_timings timings[] = {
+ /* Standard mode */
+ {
+ .name = "standard",
+ .max_bitrate = 100000,
+ .tckh = 4000,
+ .tckl = 4700,
+ .tsdh = 4700,
+ .tsdl = 8700,
+ .tp2s = 4700,
+ .tpl = 4700,
+ .tph = 4000,
+ },
+ /* Fast mode */
+ {
+ .name = "fast",
+ .max_bitrate = 400000,
+ .tckh = 600,
+ .tckl = 1300,
+ .tsdh = 600,
+ .tsdl = 1200,
+ .tp2s = 1300,
+ .tpl = 600,
+ .tph = 600,
+ },
+};
+
+/* Reset dance */
+static u8 img_i2c_reset_seq[] = { CMD_GEN_START,
+ CMD_GEN_DATA, 0xff,
+ CMD_RET_ACK,
+ CMD_GEN_START,
+ CMD_GEN_STOP,
+ 0 };
+/* Just issue a stop (after an abort condition) */
+static u8 img_i2c_stop_seq[] = { CMD_GEN_STOP,
+ 0 };
+
+/* We're interested in different interrupts depending on the mode */
+static unsigned int img_i2c_int_enable_by_mode[] = {
+ [MODE_INACTIVE] = INT_ENABLE_MASK_INACTIVE,
+ [MODE_RAW] = INT_ENABLE_MASK_RAW,
+ [MODE_ATOMIC] = INT_ENABLE_MASK_ATOMIC,
+ [MODE_AUTOMATIC] = INT_ENABLE_MASK_AUTOMATIC,
+ [MODE_SEQUENCE] = INT_ENABLE_MASK_ATOMIC,
+ [MODE_FATAL] = 0,
+ [MODE_WAITSTOP] = INT_ENABLE_MASK_WAITSTOP,
+ [MODE_SUSPEND] = 0,
+};
+
+/* Atomic command names */
+static const char * const img_i2c_atomic_cmd_names[] = {
+ [CMD_PAUSE] = "PAUSE",
+ [CMD_GEN_DATA] = "GEN_DATA",
+ [CMD_GEN_START] = "GEN_START",
+ [CMD_GEN_STOP] = "GEN_STOP",
+ [CMD_GEN_ACK] = "GEN_ACK",
+ [CMD_GEN_NACK] = "GEN_NACK",
+ [CMD_RET_DATA] = "RET_DATA",
+ [CMD_RET_ACK] = "RET_ACK",
+};
+
+struct img_i2c {
+ struct i2c_adapter adap;
+
+ void __iomem *base;
+
+ /*
+ * The scb core clock is used to get the input frequency, and to disable
+ * it after every set of transactions to save some power.
+ */
+ struct clk *scb_clk, *sys_clk;
+ unsigned int bitrate;
+ bool need_wr_rd_fence;
+
+ /* state */
+ struct completion msg_complete;
+ spinlock_t lock; /* lock before doing anything with the state */
+ struct i2c_msg msg;
+
+ /* After the last transaction, wait for a stop bit */
+ bool last_msg;
+ int msg_status;
+
+ enum img_i2c_mode mode;
+ u32 int_enable; /* depends on mode */
+ u32 line_status; /* line status over command */
+
+ /*
+ * To avoid slave event interrupts in automatic mode, use a timer to
+ * poll the abort condition if we don't get an interrupt for too long.
+ */
+ struct timer_list check_timer;
+ bool t_halt;
+
+ /* atomic mode state */
+ bool at_t_done;
+ bool at_slave_event;
+ int at_cur_cmd;
+ u8 at_cur_data;
+
+ /* Sequence: either reset or stop. See img_i2c_sequence. */
+ u8 *seq;
+
+ /* raw mode */
+ unsigned int raw_timeout;
+};
+
+static void img_i2c_writel(struct img_i2c *i2c, u32 offset, u32 value)
+{
+ writel(value, i2c->base + offset);
+}
+
+static u32 img_i2c_readl(struct img_i2c *i2c, u32 offset)
+{
+ return readl(i2c->base + offset);
+}
+
+/*
+ * The code to read from the master read fifo, and write to the master
+ * write fifo, checks a bit in an SCB register before every byte to
+ * ensure that the fifo is not full (write fifo) or empty (read fifo).
+ * Due to clock domain crossing inside the SCB block the updated value
+ * of this bit is only visible after 2 cycles.
+ *
+ * The scb_wr_rd_fence() function does 2 dummy writes (to the read-only
+ * revision register), and it's called after reading from or writing to the
+ * fifos to ensure that subsequent reads of the fifo status bits do not read
+ * stale values.
+ */
+static void img_i2c_wr_rd_fence(struct img_i2c *i2c)
+{
+ if (i2c->need_wr_rd_fence) {
+ img_i2c_writel(i2c, SCB_CORE_REV_REG, 0);
+ img_i2c_writel(i2c, SCB_CORE_REV_REG, 0);
+ }
+}
+
+static void img_i2c_switch_mode(struct img_i2c *i2c, enum img_i2c_mode mode)
+{
+ i2c->mode = mode;
+ i2c->int_enable = img_i2c_int_enable_by_mode[mode];
+ i2c->line_status = 0;
+}
+
+static void img_i2c_raw_op(struct img_i2c *i2c)
+{
+ i2c->raw_timeout = 0;
+ img_i2c_writel(i2c, SCB_OVERRIDE_REG,
+ OVERRIDE_SCLKEN_OVR |
+ OVERRIDE_SDATEN_OVR |
+ OVERRIDE_MASTER |
+ OVERRIDE_LINE_OVR_EN |
+ OVERRIDE_DIRECT |
+ ((i2c->at_cur_cmd & OVERRIDE_CMD_MASK) << OVERRIDE_CMD_SHIFT) |
+ (i2c->at_cur_data << OVERRIDE_DATA_SHIFT));
+}
+
+static const char *img_i2c_atomic_op_name(unsigned int cmd)
+{
+ if (unlikely(cmd >= ARRAY_SIZE(img_i2c_atomic_cmd_names)))
+ return "UNKNOWN";
+ return img_i2c_atomic_cmd_names[cmd];
+}
+
+/* Send a single atomic mode command to the hardware */
+static void img_i2c_atomic_op(struct img_i2c *i2c, int cmd, u8 data)
+{
+ i2c->at_cur_cmd = cmd;
+ i2c->at_cur_data = data;
+
+ /* work around lack of data setup time when generating data */
+ if (cmd == CMD_GEN_DATA && i2c->mode == MODE_ATOMIC) {
+ u32 line_status = img_i2c_readl(i2c, SCB_STATUS_REG);
+
+ if (line_status & LINESTAT_SDAT_LINE_STATUS && !(data & 0x80)) {
+ /* hold the data line down for a moment */
+ img_i2c_switch_mode(i2c, MODE_RAW);
+ img_i2c_raw_op(i2c);
+ return;
+ }
+ }
+
+ dev_dbg(i2c->adap.dev.parent,
+ "atomic cmd=%s (%d) data=%#x\n",
+ img_i2c_atomic_op_name(cmd), cmd, data);
+ i2c->at_t_done = (cmd == CMD_RET_DATA || cmd == CMD_RET_ACK);
+ i2c->at_slave_event = false;
+ i2c->line_status = 0;
+
+ img_i2c_writel(i2c, SCB_OVERRIDE_REG,
+ ((cmd & OVERRIDE_CMD_MASK) << OVERRIDE_CMD_SHIFT) |
+ OVERRIDE_MASTER |
+ OVERRIDE_DIRECT |
+ (data << OVERRIDE_DATA_SHIFT));
+}
+
+/* Start a transaction in atomic mode */
+static void img_i2c_atomic_start(struct img_i2c *i2c)
+{
+ img_i2c_switch_mode(i2c, MODE_ATOMIC);
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+ img_i2c_atomic_op(i2c, CMD_GEN_START, 0x00);
+}
+
+static void img_i2c_soft_reset(struct img_i2c *i2c)
+{
+ i2c->t_halt = false;
+ img_i2c_writel(i2c, SCB_CONTROL_REG, 0);
+ img_i2c_writel(i2c, SCB_CONTROL_REG,
+ SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET);
+}
+
+/* enable or release transaction halt for control of repeated starts */
+static void img_i2c_transaction_halt(struct img_i2c *i2c, bool t_halt)
+{
+ u32 val;
+
+ if (i2c->t_halt == t_halt)
+ return;
+ i2c->t_halt = t_halt;
+ val = img_i2c_readl(i2c, SCB_CONTROL_REG);
+ if (t_halt)
+ val |= SCB_CONTROL_TRANSACTION_HALT;
+ else
+ val &= ~SCB_CONTROL_TRANSACTION_HALT;
+ img_i2c_writel(i2c, SCB_CONTROL_REG, val);
+}
+
+/* Drain data from the FIFO into the buffer (automatic mode) */
+static void img_i2c_read_fifo(struct img_i2c *i2c)
+{
+ while (i2c->msg.len) {
+ u32 fifo_status;
+ u8 data;
+
+ fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG);
+ if (fifo_status & FIFO_READ_EMPTY)
+ break;
+
+ data = img_i2c_readl(i2c, SCB_READ_DATA_REG);
+ *i2c->msg.buf = data;
+
+ img_i2c_writel(i2c, SCB_READ_FIFO_REG, 0xff);
+ img_i2c_wr_rd_fence(i2c);
+ i2c->msg.len--;
+ i2c->msg.buf++;
+ }
+}
+
+/* Fill the FIFO with data from the buffer (automatic mode) */
+static void img_i2c_write_fifo(struct img_i2c *i2c)
+{
+ while (i2c->msg.len) {
+ u32 fifo_status;
+
+ fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG);
+ if (fifo_status & FIFO_WRITE_FULL)
+ break;
+
+ img_i2c_writel(i2c, SCB_WRITE_DATA_REG, *i2c->msg.buf);
+ img_i2c_wr_rd_fence(i2c);
+ i2c->msg.len--;
+ i2c->msg.buf++;
+ }
+
+ /* Disable fifo emptying interrupt if nothing more to write */
+ if (!i2c->msg.len)
+ i2c->int_enable &= ~INT_FIFO_EMPTYING;
+}
+
+/* Start a read transaction in automatic mode */
+static void img_i2c_read(struct img_i2c *i2c)
+{
+ img_i2c_switch_mode(i2c, MODE_AUTOMATIC);
+ if (!i2c->last_msg)
+ i2c->int_enable |= INT_SLAVE_EVENT;
+
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+ img_i2c_writel(i2c, SCB_READ_ADDR_REG, i2c->msg.addr);
+ img_i2c_writel(i2c, SCB_READ_COUNT_REG, i2c->msg.len);
+
+ img_i2c_transaction_halt(i2c, false);
+ mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
+}
+
+/* Start a write transaction in automatic mode */
+static void img_i2c_write(struct img_i2c *i2c)
+{
+ img_i2c_switch_mode(i2c, MODE_AUTOMATIC);
+ if (!i2c->last_msg)
+ i2c->int_enable |= INT_SLAVE_EVENT;
+
+ img_i2c_writel(i2c, SCB_WRITE_ADDR_REG, i2c->msg.addr);
+ img_i2c_writel(i2c, SCB_WRITE_COUNT_REG, i2c->msg.len);
+
+ img_i2c_transaction_halt(i2c, false);
+ mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
+ img_i2c_write_fifo(i2c);
+
+ /* img_i2c_write_fifo() may modify int_enable */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+}
+
+/*
+ * Indicate that the transaction is complete. This is called from the
+ * ISR to wake up the waiting thread, after which the ISR must not
+ * access any more SCB registers.
+ */
+static void img_i2c_complete_transaction(struct img_i2c *i2c, int status)
+{
+ img_i2c_switch_mode(i2c, MODE_INACTIVE);
+ if (status) {
+ i2c->msg_status = status;
+ img_i2c_transaction_halt(i2c, false);
+ }
+ complete(&i2c->msg_complete);
+}
+
+static unsigned int img_i2c_raw_atomic_delay_handler(struct img_i2c *i2c,
+ u32 int_status, u32 line_status)
+{
+ /* Stay in raw mode for this, so we don't just loop infinitely */
+ img_i2c_atomic_op(i2c, i2c->at_cur_cmd, i2c->at_cur_data);
+ img_i2c_switch_mode(i2c, MODE_ATOMIC);
+ return 0;
+}
+
+static unsigned int img_i2c_raw(struct img_i2c *i2c, u32 int_status,
+ u32 line_status)
+{
+ if (int_status & INT_TIMING) {
+ if (i2c->raw_timeout == 0)
+ return img_i2c_raw_atomic_delay_handler(i2c,
+ int_status, line_status);
+ --i2c->raw_timeout;
+ }
+ return 0;
+}
+
+static unsigned int img_i2c_sequence(struct img_i2c *i2c, u32 int_status)
+{
+ static const unsigned int continue_bits[] = {
+ [CMD_GEN_START] = LINESTAT_START_BIT_DET,
+ [CMD_GEN_DATA] = LINESTAT_INPUT_HELD_V,
+ [CMD_RET_ACK] = LINESTAT_ACK_DET | LINESTAT_NACK_DET,
+ [CMD_RET_DATA] = LINESTAT_INPUT_HELD_V,
+ [CMD_GEN_STOP] = LINESTAT_STOP_BIT_DET,
+ };
+ int next_cmd = -1;
+ u8 next_data = 0x00;
+
+ if (int_status & INT_SLAVE_EVENT)
+ i2c->at_slave_event = true;
+ if (int_status & INT_TRANSACTION_DONE)
+ i2c->at_t_done = true;
+
+ if (!i2c->at_slave_event || !i2c->at_t_done)
+ return 0;
+
+ /* wait if no continue bits are set */
+ if (i2c->at_cur_cmd >= 0 &&
+ i2c->at_cur_cmd < ARRAY_SIZE(continue_bits)) {
+ unsigned int cont_bits = continue_bits[i2c->at_cur_cmd];
+
+ if (cont_bits) {
+ cont_bits |= LINESTAT_ABORT_DET;
+ if (!(i2c->line_status & cont_bits))
+ return 0;
+ }
+ }
+
+ /* follow the sequence of commands in i2c->seq */
+ next_cmd = *i2c->seq;
+ /* stop on a nil */
+ if (!next_cmd) {
+ img_i2c_writel(i2c, SCB_OVERRIDE_REG, 0);
+ return ISR_COMPLETE(0);
+ }
+ /* when generating data, the next byte is the data */
+ if (next_cmd == CMD_GEN_DATA) {
+ ++i2c->seq;
+ next_data = *i2c->seq;
+ }
+ ++i2c->seq;
+ img_i2c_atomic_op(i2c, next_cmd, next_data);
+
+ return 0;
+}
+
+static void img_i2c_reset_start(struct img_i2c *i2c)
+{
+ /* Initiate the magic dance */
+ img_i2c_switch_mode(i2c, MODE_SEQUENCE);
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+ i2c->seq = img_i2c_reset_seq;
+ i2c->at_slave_event = true;
+ i2c->at_t_done = true;
+ i2c->at_cur_cmd = -1;
+
+ /* img_i2c_reset_seq isn't empty so the following won't fail */
+ img_i2c_sequence(i2c, 0);
+}
+
+static void img_i2c_stop_start(struct img_i2c *i2c)
+{
+ /* Initiate a stop bit sequence */
+ img_i2c_switch_mode(i2c, MODE_SEQUENCE);
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+ i2c->seq = img_i2c_stop_seq;
+ i2c->at_slave_event = true;
+ i2c->at_t_done = true;
+ i2c->at_cur_cmd = -1;
+
+ /* img_i2c_stop_seq isn't empty so the following won't fail */
+ img_i2c_sequence(i2c, 0);
+}
+
+static unsigned int img_i2c_atomic(struct img_i2c *i2c,
+ u32 int_status,
+ u32 line_status)
+{
+ int next_cmd = -1;
+ u8 next_data = 0x00;
+
+ if (int_status & INT_SLAVE_EVENT)
+ i2c->at_slave_event = true;
+ if (int_status & INT_TRANSACTION_DONE)
+ i2c->at_t_done = true;
+
+ if (!i2c->at_slave_event || !i2c->at_t_done)
+ goto next_atomic_cmd;
+ if (i2c->line_status & LINESTAT_ABORT_DET) {
+ dev_dbg(i2c->adap.dev.parent, "abort condition detected\n");
+ next_cmd = CMD_GEN_STOP;
+ i2c->msg_status = -EIO;
+ goto next_atomic_cmd;
+ }
+
+ /* i2c->at_cur_cmd may have completed */
+ switch (i2c->at_cur_cmd) {
+ case CMD_GEN_START:
+ next_cmd = CMD_GEN_DATA;
+ next_data = (i2c->msg.addr << 1);
+ if (i2c->msg.flags & I2C_M_RD)
+ next_data |= 0x1;
+ break;
+ case CMD_GEN_DATA:
+ if (i2c->line_status & LINESTAT_INPUT_HELD_V)
+ next_cmd = CMD_RET_ACK;
+ break;
+ case CMD_RET_ACK:
+ if (i2c->line_status & LINESTAT_ACK_DET) {
+ if (i2c->msg.len == 0) {
+ next_cmd = CMD_GEN_STOP;
+ } else if (i2c->msg.flags & I2C_M_RD) {
+ next_cmd = CMD_RET_DATA;
+ } else {
+ next_cmd = CMD_GEN_DATA;
+ next_data = *i2c->msg.buf;
+ --i2c->msg.len;
+ ++i2c->msg.buf;
+ }
+ } else if (i2c->line_status & LINESTAT_NACK_DET) {
+ i2c->msg_status = -EIO;
+ next_cmd = CMD_GEN_STOP;
+ }
+ break;
+ case CMD_RET_DATA:
+ if (i2c->line_status & LINESTAT_INPUT_HELD_V) {
+ *i2c->msg.buf = (i2c->line_status &
+ LINESTAT_INPUT_DATA)
+ >> LINESTAT_INPUT_DATA_SHIFT;
+ --i2c->msg.len;
+ ++i2c->msg.buf;
+ if (i2c->msg.len)
+ next_cmd = CMD_GEN_ACK;
+ else
+ next_cmd = CMD_GEN_NACK;
+ }
+ break;
+ case CMD_GEN_ACK:
+ if (i2c->line_status & LINESTAT_ACK_DET) {
+ next_cmd = CMD_RET_DATA;
+ } else {
+ i2c->msg_status = -EIO;
+ next_cmd = CMD_GEN_STOP;
+ }
+ break;
+ case CMD_GEN_NACK:
+ next_cmd = CMD_GEN_STOP;
+ break;
+ case CMD_GEN_STOP:
+ img_i2c_writel(i2c, SCB_OVERRIDE_REG, 0);
+ return ISR_COMPLETE(0);
+ default:
+ dev_err(i2c->adap.dev.parent, "bad atomic command %d\n",
+ i2c->at_cur_cmd);
+ i2c->msg_status = -EIO;
+ next_cmd = CMD_GEN_STOP;
+ break;
+ }
+
+next_atomic_cmd:
+ if (next_cmd != -1) {
+ /* don't actually stop unless we're the last transaction */
+ if (next_cmd == CMD_GEN_STOP && !i2c->msg_status &&
+ !i2c->last_msg)
+ return ISR_COMPLETE(0);
+ img_i2c_atomic_op(i2c, next_cmd, next_data);
+ }
+ return 0;
+}
+
+/*
+ * Timer function to check if something has gone wrong in automatic mode (so we
+ * don't have to handle so many interrupts just to catch an exception).
+ */
+static void img_i2c_check_timer(unsigned long arg)
+{
+ struct img_i2c *i2c = (struct img_i2c *)arg;
+ unsigned long flags;
+ unsigned int line_status;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ line_status = img_i2c_readl(i2c, SCB_STATUS_REG);
+
+ /* check for an abort condition */
+ if (line_status & LINESTAT_ABORT_DET) {
+ dev_dbg(i2c->adap.dev.parent,
+ "abort condition detected by check timer\n");
+ /* enable slave event interrupt mask to trigger irq */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG,
+ i2c->int_enable | INT_SLAVE_EVENT);
+ }
+
+ spin_unlock_irqrestore(&i2c->lock, flags);
+}
+
+static unsigned int img_i2c_auto(struct img_i2c *i2c,
+ unsigned int int_status,
+ unsigned int line_status)
+{
+ if (int_status & (INT_WRITE_ACK_ERR | INT_ADDR_ACK_ERR))
+ return ISR_COMPLETE(EIO);
+
+ if (line_status & LINESTAT_ABORT_DET) {
+ dev_dbg(i2c->adap.dev.parent, "abort condition detected\n");
+ /* empty the read fifo */
+ if ((i2c->msg.flags & I2C_M_RD) &&
+ (int_status & INT_FIFO_FULL_FILLING))
+ img_i2c_read_fifo(i2c);
+ /* use atomic mode and try to force a stop bit */
+ i2c->msg_status = -EIO;
+ img_i2c_stop_start(i2c);
+ return 0;
+ }
+
+ /* Enable transaction halt on start bit */
+ if (!i2c->last_msg && i2c->line_status & LINESTAT_START_BIT_DET) {
+ img_i2c_transaction_halt(i2c, true);
+ /* we're no longer interested in the slave event */
+ i2c->int_enable &= ~INT_SLAVE_EVENT;
+ }
+
+ mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
+
+ if (i2c->msg.flags & I2C_M_RD) {
+ if (int_status & INT_FIFO_FULL_FILLING) {
+ img_i2c_read_fifo(i2c);
+ if (i2c->msg.len == 0)
+ return ISR_WAITSTOP;
+ }
+ } else {
+ if (int_status & INT_FIFO_EMPTY_EMPTYING) {
+ /*
+ * The write fifo empty indicates that we're in the
+ * last byte so it's safe to start a new write
+ * transaction without losing any bytes from the
+ * previous one.
+ * see 2.3.7 Repeated Start Transactions.
+ */
+ if ((int_status & INT_FIFO_EMPTY) &&
+ i2c->msg.len == 0)
+ return ISR_WAITSTOP;
+ img_i2c_write_fifo(i2c);
+ }
+ }
+
+ return 0;
+}
+
+static irqreturn_t img_i2c_isr(int irq, void *dev_id)
+{
+ struct img_i2c *i2c = (struct img_i2c *)dev_id;
+ u32 int_status, line_status;
+ /* We handle transaction completion AFTER accessing registers */
+ unsigned int hret;
+
+ /* Read interrupt status register. */
+ int_status = img_i2c_readl(i2c, SCB_INT_STATUS_REG);
+ /* Clear detected interrupts. */
+ img_i2c_writel(i2c, SCB_INT_CLEAR_REG, int_status);
+
+ /*
+ * Read line status and clear it until it actually is clear. We have
+ * to be careful not to lose any line status bits that get latched.
+ */
+ line_status = img_i2c_readl(i2c, SCB_STATUS_REG);
+ if (line_status & LINESTAT_LATCHED) {
+ img_i2c_writel(i2c, SCB_CLEAR_REG,
+ (line_status & LINESTAT_LATCHED)
+ >> LINESTAT_CLEAR_SHIFT);
+ img_i2c_wr_rd_fence(i2c);
+ }
+
+ spin_lock(&i2c->lock);
+
+ /* Keep track of line status bits received */
+ i2c->line_status &= ~LINESTAT_INPUT_DATA;
+ i2c->line_status |= line_status;
+
+ /*
+ * Certain interrupts indicate that sclk low timeout is not
+ * a problem. If any of these are set, just continue.
+ */
+ if ((int_status & INT_SCLK_LOW_TIMEOUT) &&
+ !(int_status & (INT_SLAVE_EVENT |
+ INT_FIFO_EMPTY |
+ INT_FIFO_FULL))) {
+ dev_crit(i2c->adap.dev.parent,
+ "fatal: clock low timeout occurred %s addr 0x%02x\n",
+ (i2c->msg.flags & I2C_M_RD) ? "reading" : "writing",
+ i2c->msg.addr);
+ hret = ISR_FATAL(EIO);
+ goto out;
+ }
+
+ if (i2c->mode == MODE_ATOMIC)
+ hret = img_i2c_atomic(i2c, int_status, line_status);
+ else if (i2c->mode == MODE_AUTOMATIC)
+ hret = img_i2c_auto(i2c, int_status, line_status);
+ else if (i2c->mode == MODE_SEQUENCE)
+ hret = img_i2c_sequence(i2c, int_status);
+ else if (i2c->mode == MODE_WAITSTOP && (int_status & INT_SLAVE_EVENT) &&
+ (line_status & LINESTAT_STOP_BIT_DET))
+ hret = ISR_COMPLETE(0);
+ else if (i2c->mode == MODE_RAW)
+ hret = img_i2c_raw(i2c, int_status, line_status);
+ else
+ hret = 0;
+
+ /* Clear detected level interrupts. */
+ img_i2c_writel(i2c, SCB_INT_CLEAR_REG, int_status & INT_LEVEL);
+
+out:
+ if (hret & ISR_WAITSTOP) {
+ /*
+ * Only wait for stop on last message.
+ * Also we may already have detected the stop bit.
+ */
+ if (!i2c->last_msg || i2c->line_status & LINESTAT_STOP_BIT_DET)
+ hret = ISR_COMPLETE(0);
+ else
+ img_i2c_switch_mode(i2c, MODE_WAITSTOP);
+ }
+
+ /* now we've finished using regs, handle transaction completion */
+ if (hret & ISR_COMPLETE_M) {
+ int status = -(hret & ISR_STATUS_M);
+
+ img_i2c_complete_transaction(i2c, status);
+ if (hret & ISR_FATAL_M)
+ img_i2c_switch_mode(i2c, MODE_FATAL);
+ }
+
+ /* Enable interrupts (int_enable may be altered by changing mode) */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+
+ spin_unlock(&i2c->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* Force a bus reset sequence and wait for it to complete */
+static int img_i2c_reset_bus(struct img_i2c *i2c)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ reinit_completion(&i2c->msg_complete);
+ img_i2c_reset_start(i2c);
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ ret = wait_for_completion_timeout(&i2c->msg_complete, IMG_I2C_TIMEOUT);
+ if (ret == 0)
+ return -ETIMEDOUT;
+ return 0;
+}
+
+static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct img_i2c *i2c = i2c_get_adapdata(adap);
+ bool atomic = false;
+ int i, ret;
+
+ if (i2c->mode == MODE_SUSPEND) {
+ WARN(1, "refusing to service transaction in suspended state\n");
+ return -EIO;
+ }
+
+ if (i2c->mode == MODE_FATAL)
+ return -EIO;
+
+ for (i = 0; i < num; i++) {
+ if (likely(msgs[i].len))
+ continue;
+ /*
+ * 0 byte reads are not possible because the slave could try
+ * and pull the data line low, preventing a stop bit.
+ */
+ if (unlikely(msgs[i].flags & I2C_M_RD))
+ return -EIO;
+ /*
+ * 0 byte writes are possible and used for probing, but we
+ * cannot do them in automatic mode, so use atomic mode
+ * instead.
+ */
+ atomic = true;
+ }
+
+ ret = clk_prepare_enable(i2c->scb_clk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num; i++) {
+ struct i2c_msg *msg = &msgs[i];
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+
+ /*
+ * Make a copy of the message struct. We mustn't modify the
+ * original or we'll confuse drivers and i2c-dev.
+ */
+ i2c->msg = *msg;
+ i2c->msg_status = 0;
+
+ /*
+ * After the last message we must have waited for a stop bit.
+ * Not waiting can cause problems when the clock is disabled
+ * before the stop bit is sent, and the linux I2C interface
+ * requires separate transfers not to joined with repeated
+ * start.
+ */
+ i2c->last_msg = (i == num - 1);
+ reinit_completion(&i2c->msg_complete);
+
+ if (atomic)
+ img_i2c_atomic_start(i2c);
+ else if (msg->flags & I2C_M_RD)
+ img_i2c_read(i2c);
+ else
+ img_i2c_write(i2c);
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ ret = wait_for_completion_timeout(&i2c->msg_complete,
+ IMG_I2C_TIMEOUT);
+ del_timer_sync(&i2c->check_timer);
+
+ if (ret == 0) {
+ dev_err(adap->dev.parent, "i2c transfer timed out\n");
+ i2c->msg_status = -ETIMEDOUT;
+ break;
+ }
+
+ if (i2c->msg_status)
+ break;
+ }
+
+ clk_disable_unprepare(i2c->scb_clk);
+
+ return i2c->msg_status ? i2c->msg_status : num;
+}
+
+static u32 img_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm img_i2c_algo = {
+ .master_xfer = img_i2c_xfer,
+ .functionality = img_i2c_func,
+};
+
+static int img_i2c_init(struct img_i2c *i2c)
+{
+ unsigned int clk_khz, bitrate_khz, clk_period, tckh, tckl, tsdh;
+ unsigned int i, ret, data, prescale, inc, int_bitrate, filt;
+ struct img_i2c_timings timing;
+ u32 rev;
+
+ ret = clk_prepare_enable(i2c->scb_clk);
+ if (ret)
+ return ret;
+
+ rev = img_i2c_readl(i2c, SCB_CORE_REV_REG);
+ if ((rev & 0x00ffffff) < 0x00020200) {
+ dev_info(i2c->adap.dev.parent,
+ "Unknown hardware revision (%d.%d.%d.%d)\n",
+ (rev >> 24) & 0xff, (rev >> 16) & 0xff,
+ (rev >> 8) & 0xff, rev & 0xff);
+ clk_disable_unprepare(i2c->scb_clk);
+ return -EINVAL;
+ }
+
+ if (rev == REL_SOC_IP_SCB_2_2_1) {
+ i2c->need_wr_rd_fence = true;
+ dev_info(i2c->adap.dev.parent, "fence quirk enabled");
+ }
+
+ bitrate_khz = i2c->bitrate / 1000;
+ clk_khz = clk_get_rate(i2c->scb_clk) / 1000;
+
+ /* Determine what mode we're in from the bitrate */
+ timing = timings[0];
+ for (i = 0; i < ARRAY_SIZE(timings); i++) {
+ if (i2c->bitrate <= timings[i].max_bitrate) {
+ timing = timings[i];
+ break;
+ }
+ }
+
+ /* Find the prescale that would give us that inc (approx delay = 0) */
+ prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz);
+ prescale = clamp_t(unsigned int, prescale, 1, 8);
+ clk_khz /= prescale;
+
+ /* Setup the clock increment value */
+ inc = (256 * 16 * bitrate_khz) / clk_khz;
+
+ /*
+ * The clock generation logic allows to filter glitches on the bus.
+ * This filter is able to remove bus glitches shorter than 50ns.
+ * If the clock enable rate is greater than 20 MHz, no filtering
+ * is required, so we need to disable it.
+ * If it's between the 20-40 MHz range, there's no need to divide
+ * the clock to get a filter.
+ */
+ if (clk_khz < 20000) {
+ filt = SCB_FILT_DISABLE;
+ } else if (clk_khz < 40000) {
+ filt = SCB_FILT_BYPASS;
+ } else {
+ /* Calculate filter clock */
+ filt = (64000 / ((clk_khz / 1000) * SCB_FILT_GLITCH));
+
+ /* Scale up if needed */
+ if (64000 % ((clk_khz / 1000) * SCB_FILT_GLITCH))
+ inc++;
+
+ if (filt > SCB_FILT_INC_MASK)
+ filt = SCB_FILT_INC_MASK;
+
+ filt = (filt & SCB_FILT_INC_MASK) << SCB_FILT_INC_SHIFT;
+ }
+ data = filt | ((inc & SCB_INC_MASK) << SCB_INC_SHIFT) | (prescale - 1);
+ img_i2c_writel(i2c, SCB_CLK_SET_REG, data);
+
+ /* Obtain the clock period of the fx16 clock in ns */
+ clk_period = (256 * 1000000) / (clk_khz * inc);
+
+ /* Calculate the bitrate in terms of internal clock pulses */
+ int_bitrate = 1000000 / (bitrate_khz * clk_period);
+ if ((1000000 % (bitrate_khz * clk_period)) >=
+ ((bitrate_khz * clk_period) / 2))
+ int_bitrate++;
+
+ /* Setup TCKH value */
+ tckh = timing.tckh / clk_period;
+ if (timing.tckh % clk_period)
+ tckh++;
+
+ if (tckh > 0)
+ data = tckh - 1;
+ else
+ data = 0;
+
+ img_i2c_writel(i2c, SCB_TIME_TCKH_REG, data);
+
+ /* Setup TCKL value */
+ tckl = int_bitrate - tckh;
+
+ if (tckl > 0)
+ data = tckl - 1;
+ else
+ data = 0;
+
+ img_i2c_writel(i2c, SCB_TIME_TCKL_REG, data);
+
+ /* Setup TSDH value */
+ tsdh = timing.tsdh / clk_period;
+ if (timing.tsdh % clk_period)
+ tsdh++;
+
+ if (tsdh > 1)
+ data = tsdh - 1;
+ else
+ data = 0x01;
+ img_i2c_writel(i2c, SCB_TIME_TSDH_REG, data);
+
+ /* This value is used later */
+ tsdh = data;
+
+ /* Setup TPL value */
+ data = timing.tpl / clk_period;
+ if (data > 0)
+ --data;
+ img_i2c_writel(i2c, SCB_TIME_TPL_REG, data);
+
+ /* Setup TPH value */
+ data = timing.tph / clk_period;
+ if (data > 0)
+ --data;
+ img_i2c_writel(i2c, SCB_TIME_TPH_REG, data);
+
+ /* Setup TSDL value to TPL + TSDH + 2 */
+ img_i2c_writel(i2c, SCB_TIME_TSDL_REG, data + tsdh + 2);
+
+ /* Setup TP2S value */
+ data = timing.tp2s / clk_period;
+ if (data > 0)
+ --data;
+ img_i2c_writel(i2c, SCB_TIME_TP2S_REG, data);
+
+ img_i2c_writel(i2c, SCB_TIME_TBI_REG, TIMEOUT_TBI);
+ img_i2c_writel(i2c, SCB_TIME_TSL_REG, TIMEOUT_TSL);
+ img_i2c_writel(i2c, SCB_TIME_TDL_REG, TIMEOUT_TDL);
+
+ /* Take module out of soft reset and enable clocks */
+ img_i2c_soft_reset(i2c);
+
+ /* Disable all interrupts */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, 0);
+
+ /* Clear all interrupts */
+ img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
+
+ /* Clear the scb_line_status events */
+ img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
+
+ /* Enable interrupts */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+
+ /* Perform a synchronous sequence to reset the bus */
+ ret = img_i2c_reset_bus(i2c);
+
+ clk_disable_unprepare(i2c->scb_clk);
+
+ return ret;
+}
+
+static int img_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct img_i2c *i2c;
+ struct resource *res;
+ int irq, ret;
+ u32 val;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct img_i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "can't get irq number\n");
+ return irq;
+ }
+
+ i2c->sys_clk = devm_clk_get(&pdev->dev, "sys");
+ if (IS_ERR(i2c->sys_clk)) {
+ dev_err(&pdev->dev, "can't get system clock\n");
+ return PTR_ERR(i2c->sys_clk);
+ }
+
+ i2c->scb_clk = devm_clk_get(&pdev->dev, "scb");
+ if (IS_ERR(i2c->scb_clk)) {
+ dev_err(&pdev->dev, "can't get core clock\n");
+ return PTR_ERR(i2c->scb_clk);
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, img_i2c_isr, 0,
+ pdev->name, i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "can't request irq %d\n", irq);
+ return ret;
+ }
+
+ /* Set up the exception check timer */
+ init_timer(&i2c->check_timer);
+ i2c->check_timer.function = img_i2c_check_timer;
+ i2c->check_timer.data = (unsigned long)i2c;
+
+ i2c->bitrate = timings[0].max_bitrate;
+ if (!of_property_read_u32(node, "clock-frequency", &val))
+ i2c->bitrate = val;
+
+ i2c_set_adapdata(&i2c->adap, i2c);
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.dev.of_node = node;
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.algo = &img_i2c_algo;
+ i2c->adap.retries = 5;
+ i2c->adap.nr = pdev->id;
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name), "IMG SCB I2C");
+
+ img_i2c_switch_mode(i2c, MODE_INACTIVE);
+ spin_lock_init(&i2c->lock);
+ init_completion(&i2c->msg_complete);
+
+ platform_set_drvdata(pdev, i2c);
+
+ ret = clk_prepare_enable(i2c->sys_clk);
+ if (ret)
+ return ret;
+
+ ret = img_i2c_init(i2c);
+ if (ret)
+ goto disable_clk;
+
+ ret = i2c_add_numbered_adapter(&i2c->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add adapter\n");
+ goto disable_clk;
+ }
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(i2c->sys_clk);
+ return ret;
+}
+
+static int img_i2c_remove(struct platform_device *dev)
+{
+ struct img_i2c *i2c = platform_get_drvdata(dev);
+
+ i2c_del_adapter(&i2c->adap);
+ clk_disable_unprepare(i2c->sys_clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int img_i2c_suspend(struct device *dev)
+{
+ struct img_i2c *i2c = dev_get_drvdata(dev);
+
+ img_i2c_switch_mode(i2c, MODE_SUSPEND);
+
+ clk_disable_unprepare(i2c->sys_clk);
+
+ return 0;
+}
+
+static int img_i2c_resume(struct device *dev)
+{
+ struct img_i2c *i2c = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2c->sys_clk);
+ if (ret)
+ return ret;
+
+ img_i2c_init(i2c);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(img_i2c_pm, img_i2c_suspend, img_i2c_resume);
+
+static const struct of_device_id img_scb_i2c_match[] = {
+ { .compatible = "img,scb-i2c" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, img_scb_i2c_match);
+
+static struct platform_driver img_scb_i2c_driver = {
+ .driver = {
+ .name = "img-i2c-scb",
+ .of_match_table = img_scb_i2c_match,
+ .pm = &img_i2c_pm,
+ },
+ .probe = img_i2c_probe,
+ .remove = img_i2c_remove,
+};
+module_platform_driver(img_scb_i2c_driver);
+
+MODULE_AUTHOR("James Hogan <james.hogan@imgtec.com>");
+MODULE_DESCRIPTION("IMG host I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index c48e46af670..7f3a9fe9bf4 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -11,11 +11,6 @@
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- *
* Author:
* Darius Augulis, Teltonika Inc.
*
@@ -37,22 +32,27 @@
/** Includes *******************************************************************
*******************************************************************************/
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/errno.h>
#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/sched.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_dma.h>
#include <linux/platform_data/i2c-imx.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
/** Defines ********************************************************************
*******************************************************************************/
@@ -63,6 +63,15 @@
/* Default value */
#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
+/*
+ * Enable DMA if transfer byte size is bigger than this threshold.
+ * As the hardware request, it must bigger than 4 bytes.\
+ * I have set '16' here, maybe it's not the best but I think it's
+ * the appropriate.
+ */
+#define DMA_THRESHOLD 16
+#define DMA_TIMEOUT 1000
+
/* IMX I2C registers:
* the I2C register offset is different between SoCs,
* to provid support for all these chips, split the
@@ -88,6 +97,7 @@
#define I2SR_IBB 0x20
#define I2SR_IAAS 0x40
#define I2SR_ICF 0x80
+#define I2CR_DMAEN 0x02
#define I2CR_RSTA 0x04
#define I2CR_TXAK 0x08
#define I2CR_MTX 0x10
@@ -174,6 +184,17 @@ struct imx_i2c_hwdata {
unsigned i2cr_ien_opcode;
};
+struct imx_i2c_dma {
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+ struct dma_chan *chan_using;
+ struct completion cmd_complete;
+ dma_addr_t dma_buf;
+ unsigned int dma_len;
+ enum dma_transfer_direction dma_transfer_dir;
+ enum dma_data_direction dma_data_dir;
+};
+
struct imx_i2c_struct {
struct i2c_adapter adapter;
struct clk *clk;
@@ -186,6 +207,8 @@ struct imx_i2c_struct {
unsigned int cur_clk;
unsigned int bitrate;
const struct imx_i2c_hwdata *hwdata;
+
+ struct imx_i2c_dma *dma;
};
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
@@ -256,6 +279,138 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
}
+/* Functions for DMA support */
+static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
+ dma_addr_t phy_addr)
+{
+ struct imx_i2c_dma *dma;
+ struct dma_slave_config dma_sconfig;
+ struct device *dev = &i2c_imx->adapter.dev;
+ int ret;
+
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return;
+
+ dma->chan_tx = dma_request_slave_channel(dev, "tx");
+ if (!dma->chan_tx) {
+ dev_dbg(dev, "can't request DMA tx channel\n");
+ ret = -ENODEV;
+ goto fail_al;
+ }
+
+ dma_sconfig.dst_addr = phy_addr +
+ (IMX_I2C_I2DR << i2c_imx->hwdata->regshift);
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_maxburst = 1;
+ dma_sconfig.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
+ if (ret < 0) {
+ dev_dbg(dev, "can't configure tx channel\n");
+ goto fail_tx;
+ }
+
+ dma->chan_rx = dma_request_slave_channel(dev, "rx");
+ if (!dma->chan_rx) {
+ dev_dbg(dev, "can't request DMA rx channel\n");
+ ret = -ENODEV;
+ goto fail_tx;
+ }
+
+ dma_sconfig.src_addr = phy_addr +
+ (IMX_I2C_I2DR << i2c_imx->hwdata->regshift);
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 1;
+ dma_sconfig.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
+ if (ret < 0) {
+ dev_dbg(dev, "can't configure rx channel\n");
+ goto fail_rx;
+ }
+
+ i2c_imx->dma = dma;
+ init_completion(&dma->cmd_complete);
+ dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
+ dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
+
+ return;
+
+fail_rx:
+ dma_release_channel(dma->chan_rx);
+fail_tx:
+ dma_release_channel(dma->chan_tx);
+fail_al:
+ devm_kfree(dev, dma);
+ dev_info(dev, "can't use DMA\n");
+}
+
+static void i2c_imx_dma_callback(void *arg)
+{
+ struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg;
+ struct imx_i2c_dma *dma = i2c_imx->dma;
+
+ dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf,
+ dma->dma_len, dma->dma_data_dir);
+ complete(&dma->cmd_complete);
+}
+
+static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs)
+{
+ struct imx_i2c_dma *dma = i2c_imx->dma;
+ struct dma_async_tx_descriptor *txdesc;
+ struct device *dev = &i2c_imx->adapter.dev;
+ struct device *chan_dev = dma->chan_using->device->dev;
+
+ dma->dma_buf = dma_map_single(chan_dev, msgs->buf,
+ dma->dma_len, dma->dma_data_dir);
+ if (dma_mapping_error(chan_dev, dma->dma_buf)) {
+ dev_err(dev, "DMA mapping failed\n");
+ goto err_map;
+ }
+
+ txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf,
+ dma->dma_len, dma->dma_transfer_dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc) {
+ dev_err(dev, "Not able to get desc for DMA xfer\n");
+ goto err_desc;
+ }
+
+ txdesc->callback = i2c_imx_dma_callback;
+ txdesc->callback_param = i2c_imx;
+ if (dma_submit_error(dmaengine_submit(txdesc))) {
+ dev_err(dev, "DMA submit failed\n");
+ goto err_submit;
+ }
+
+ dma_async_issue_pending(dma->chan_using);
+ return 0;
+
+err_submit:
+err_desc:
+ dma_unmap_single(chan_dev, dma->dma_buf,
+ dma->dma_len, dma->dma_data_dir);
+err_map:
+ return -EINVAL;
+}
+
+static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
+{
+ struct imx_i2c_dma *dma = i2c_imx->dma;
+
+ dma->dma_buf = 0;
+ dma->dma_len = 0;
+
+ dma_release_channel(dma->chan_tx);
+ dma->chan_tx = NULL;
+
+ dma_release_channel(dma->chan_rx);
+ dma->chan_rx = NULL;
+
+ dma->chan_using = NULL;
+}
+
/** Functions for IMX I2C adapter driver ***************************************
*******************************************************************************/
@@ -387,6 +542,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
i2c_imx->stopped = 0;
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
+ temp &= ~I2CR_DMAEN;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
return result;
}
@@ -400,6 +556,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
temp &= ~(I2CR_MSTA | I2CR_MTX);
+ if (i2c_imx->dma)
+ temp &= ~I2CR_DMAEN;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
}
if (is_imx1_i2c(i2c_imx)) {
@@ -440,6 +598,155 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
return IRQ_NONE;
}
+static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs)
+{
+ int result;
+ unsigned int temp = 0;
+ unsigned long orig_jiffies = jiffies;
+ struct imx_i2c_dma *dma = i2c_imx->dma;
+ struct device *dev = &i2c_imx->adapter.dev;
+
+ dma->chan_using = dma->chan_tx;
+ dma->dma_transfer_dir = DMA_MEM_TO_DEV;
+ dma->dma_data_dir = DMA_TO_DEVICE;
+ dma->dma_len = msgs->len - 1;
+ result = i2c_imx_dma_xfer(i2c_imx, msgs);
+ if (result)
+ return result;
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_DMAEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /*
+ * Write slave address.
+ * The first byte must be transmitted by the CPU.
+ */
+ imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR);
+ reinit_completion(&i2c_imx->dma->cmd_complete);
+ result = wait_for_completion_timeout(
+ &i2c_imx->dma->cmd_complete,
+ msecs_to_jiffies(DMA_TIMEOUT));
+ if (result <= 0) {
+ dmaengine_terminate_all(dma->chan_using);
+ return result ?: -ETIMEDOUT;
+ }
+
+ /* Waiting for transfer complete. */
+ while (1) {
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ if (temp & I2SR_ICF)
+ break;
+ if (time_after(jiffies, orig_jiffies +
+ msecs_to_jiffies(DMA_TIMEOUT))) {
+ dev_dbg(dev, "<%s> Timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ schedule();
+ }
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp &= ~I2CR_DMAEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* The last data byte must be transferred by the CPU. */
+ imx_i2c_write_reg(msgs->buf[msgs->len-1],
+ i2c_imx, IMX_I2C_I2DR);
+ result = i2c_imx_trx_complete(i2c_imx);
+ if (result)
+ return result;
+
+ return i2c_imx_acked(i2c_imx);
+}
+
+static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs, bool is_lastmsg)
+{
+ int result;
+ unsigned int temp;
+ unsigned long orig_jiffies = jiffies;
+ struct imx_i2c_dma *dma = i2c_imx->dma;
+ struct device *dev = &i2c_imx->adapter.dev;
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_DMAEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ dma->chan_using = dma->chan_rx;
+ dma->dma_transfer_dir = DMA_DEV_TO_MEM;
+ dma->dma_data_dir = DMA_FROM_DEVICE;
+ /* The last two data bytes must be transferred by the CPU. */
+ dma->dma_len = msgs->len - 2;
+ result = i2c_imx_dma_xfer(i2c_imx, msgs);
+ if (result)
+ return result;
+
+ reinit_completion(&i2c_imx->dma->cmd_complete);
+ result = wait_for_completion_timeout(
+ &i2c_imx->dma->cmd_complete,
+ msecs_to_jiffies(DMA_TIMEOUT));
+ if (result <= 0) {
+ dmaengine_terminate_all(dma->chan_using);
+ return result ?: -ETIMEDOUT;
+ }
+
+ /* waiting for transfer complete. */
+ while (1) {
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ if (temp & I2SR_ICF)
+ break;
+ if (time_after(jiffies, orig_jiffies +
+ msecs_to_jiffies(DMA_TIMEOUT))) {
+ dev_dbg(dev, "<%s> Timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ schedule();
+ }
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp &= ~I2CR_DMAEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* read n-1 byte data */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ /* read n byte data */
+ result = i2c_imx_trx_complete(i2c_imx);
+ if (result)
+ return result;
+
+ if (is_lastmsg) {
+ /*
+ * It must generate STOP before read I2DR to prevent
+ * controller from generating another clock cycle
+ */
+ dev_dbg(dev, "<%s> clear MSTA\n", __func__);
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ i2c_imx_bus_busy(i2c_imx, 0);
+ i2c_imx->stopped = 1;
+ } else {
+ /*
+ * For i2c master receiver repeat restart operation like:
+ * read -> repeat MSTA -> read/write
+ * The controller must set MTX before read the last byte in
+ * the first read operation, otherwise the first read cost
+ * one extra clock cycle.
+ */
+ temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+ temp |= I2CR_MTX;
+ writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+ }
+ msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+
+ return 0;
+}
+
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
{
int i, result;
@@ -509,6 +816,9 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
+ if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data)
+ return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
+
/* read data */
for (i = 0; i < msgs->len; i++) {
u8 len = 0;
@@ -623,8 +933,12 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
#endif
if (msgs[i].flags & I2C_M_RD)
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
- else
- result = i2c_imx_write(i2c_imx, &msgs[i]);
+ else {
+ if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
+ result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
+ else
+ result = i2c_imx_write(i2c_imx, &msgs[i]);
+ }
if (result)
goto fail0;
}
@@ -659,6 +973,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
int irq, ret;
+ dma_addr_t phy_addr;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
@@ -673,8 +988,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct),
- GFP_KERNEL);
+ phy_addr = (dma_addr_t)res->start;
+ i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
if (!i2c_imx)
return -ENOMEM;
@@ -748,6 +1063,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.name);
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
+ /* Init DMA config if support*/
+ i2c_imx_dma_request(i2c_imx, phy_addr);
+
return 0; /* Return OK */
clk_disable:
@@ -763,6 +1081,9 @@ static int i2c_imx_remove(struct platform_device *pdev)
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
i2c_del_adapter(&i2c_imx->adapter);
+ if (i2c_imx->dma)
+ i2c_imx_dma_free(i2c_imx);
+
/* setup chip registers to defaults */
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR);
@@ -777,7 +1098,6 @@ static struct platform_driver i2c_imx_driver = {
.remove = i2c_imx_remove,
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = i2c_imx_dt_ids,
},
.id_table = imx_i2c_devtype,
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index 3d16c2f60a5..72d6161cf77 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -516,7 +516,6 @@ static struct platform_driver iop3xx_i2c_driver = {
.probe = iop3xx_i2c_probe,
.remove = iop3xx_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "IOP3xx-I2C",
},
};
diff --git a/drivers/i2c/busses/i2c-iop3xx.h b/drivers/i2c/busses/i2c-iop3xx.h
index 097e270955d..2d6929c2bd9 100644
--- a/drivers/i2c/busses/i2c-iop3xx.h
+++ b/drivers/i2c/busses/i2c-iop3xx.h
@@ -11,11 +11,7 @@
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. */
+ GNU General Public License for more details. */
/* ------------------------------------------------------------------------- */
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index cf99dbf21fd..c2f25f19d76 100644
--- a/drivers/i2c/busses/i2c-isch.c
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -14,10 +14,6 @@
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.
*/
/*
@@ -313,7 +309,6 @@ static int smbus_sch_remove(struct platform_device *pdev)
static struct platform_driver smbus_sch_driver = {
.driver = {
.name = "isch_smbus",
- .owner = THIS_MODULE,
},
.probe = smbus_sch_probe,
.remove = smbus_sch_remove,
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index 3f6ecbfb9a5..f2b0ff01163 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -14,10 +14,6 @@
* 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c
index af8f65fb1c0..25993d2e64b 100644
--- a/drivers/i2c/busses/i2c-kempld.c
+++ b/drivers/i2c/busses/i2c-kempld.c
@@ -394,7 +394,6 @@ static int kempld_i2c_resume(struct platform_device *pdev)
static struct platform_driver kempld_i2c_driver = {
.driver = {
.name = "kempld-i2c",
- .owner = THIS_MODULE,
},
.probe = kempld_i2c_probe,
.remove = kempld_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c
new file mode 100644
index 00000000000..5e176adca8e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-meson.c
@@ -0,0 +1,492 @@
+/*
+ * I2C bus driver for Amlogic Meson SoCs
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/* Meson I2C register map */
+#define REG_CTRL 0x00
+#define REG_SLAVE_ADDR 0x04
+#define REG_TOK_LIST0 0x08
+#define REG_TOK_LIST1 0x0c
+#define REG_TOK_WDATA0 0x10
+#define REG_TOK_WDATA1 0x14
+#define REG_TOK_RDATA0 0x18
+#define REG_TOK_RDATA1 0x1c
+
+/* Control register fields */
+#define REG_CTRL_START BIT(0)
+#define REG_CTRL_ACK_IGNORE BIT(1)
+#define REG_CTRL_STATUS BIT(2)
+#define REG_CTRL_ERROR BIT(3)
+#define REG_CTRL_CLKDIV_SHIFT 12
+#define REG_CTRL_CLKDIV_MASK ((BIT(10) - 1) << REG_CTRL_CLKDIV_SHIFT)
+
+#define I2C_TIMEOUT_MS 500
+#define DEFAULT_FREQ 100000
+
+enum {
+ TOKEN_END = 0,
+ TOKEN_START,
+ TOKEN_SLAVE_ADDR_WRITE,
+ TOKEN_SLAVE_ADDR_READ,
+ TOKEN_DATA,
+ TOKEN_DATA_LAST,
+ TOKEN_STOP,
+};
+
+enum {
+ STATE_IDLE,
+ STATE_READ,
+ STATE_WRITE,
+ STATE_STOP,
+};
+
+/**
+ * struct meson_i2c - Meson I2C device private data
+ *
+ * @adap: I2C adapter instance
+ * @dev: Pointer to device structure
+ * @regs: Base address of the device memory mapped registers
+ * @clk: Pointer to clock structure
+ * @irq: IRQ number
+ * @msg: Pointer to the current I2C message
+ * @state: Current state in the driver state machine
+ * @last: Flag set for the last message in the transfer
+ * @count: Number of bytes to be sent/received in current transfer
+ * @pos: Current position in the send/receive buffer
+ * @error: Flag set when an error is received
+ * @lock: To avoid race conditions between irq handler and xfer code
+ * @done: Completion used to wait for transfer termination
+ * @frequency: Operating frequency of I2C bus clock
+ * @tokens: Sequence of tokens to be written to the device
+ * @num_tokens: Number of tokens
+ */
+struct meson_i2c {
+ struct i2c_adapter adap;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ int irq;
+
+ struct i2c_msg *msg;
+ int state;
+ bool last;
+ int count;
+ int pos;
+ int error;
+
+ spinlock_t lock;
+ struct completion done;
+ unsigned int frequency;
+ u32 tokens[2];
+ int num_tokens;
+};
+
+static void meson_i2c_set_mask(struct meson_i2c *i2c, int reg, u32 mask,
+ u32 val)
+{
+ u32 data;
+
+ data = readl(i2c->regs + reg);
+ data &= ~mask;
+ data |= val & mask;
+ writel(data, i2c->regs + reg);
+}
+
+static void meson_i2c_reset_tokens(struct meson_i2c *i2c)
+{
+ i2c->tokens[0] = 0;
+ i2c->tokens[1] = 0;
+ i2c->num_tokens = 0;
+}
+
+static void meson_i2c_add_token(struct meson_i2c *i2c, int token)
+{
+ if (i2c->num_tokens < 8)
+ i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4);
+ else
+ i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4);
+
+ i2c->num_tokens++;
+}
+
+static void meson_i2c_write_tokens(struct meson_i2c *i2c)
+{
+ writel(i2c->tokens[0], i2c->regs + REG_TOK_LIST0);
+ writel(i2c->tokens[1], i2c->regs + REG_TOK_LIST1);
+}
+
+static void meson_i2c_set_clk_div(struct meson_i2c *i2c)
+{
+ unsigned long clk_rate = clk_get_rate(i2c->clk);
+ unsigned int div;
+
+ div = DIV_ROUND_UP(clk_rate, i2c->frequency * 4);
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK,
+ div << REG_CTRL_CLKDIV_SHIFT);
+
+ dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__,
+ clk_rate, i2c->frequency, div);
+}
+
+static void meson_i2c_get_data(struct meson_i2c *i2c, char *buf, int len)
+{
+ u32 rdata0, rdata1;
+ int i;
+
+ rdata0 = readl(i2c->regs + REG_TOK_RDATA0);
+ rdata1 = readl(i2c->regs + REG_TOK_RDATA1);
+
+ dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__,
+ rdata0, rdata1, len);
+
+ for (i = 0; i < min_t(int, 4, len); i++)
+ *buf++ = (rdata0 >> i * 8) & 0xff;
+
+ for (i = 4; i < min_t(int, 8, len); i++)
+ *buf++ = (rdata1 >> (i - 4) * 8) & 0xff;
+}
+
+static void meson_i2c_put_data(struct meson_i2c *i2c, char *buf, int len)
+{
+ u32 wdata0 = 0, wdata1 = 0;
+ int i;
+
+ for (i = 0; i < min_t(int, 4, len); i++)
+ wdata0 |= *buf++ << (i * 8);
+
+ for (i = 4; i < min_t(int, 8, len); i++)
+ wdata1 |= *buf++ << ((i - 4) * 8);
+
+ writel(wdata0, i2c->regs + REG_TOK_WDATA0);
+ writel(wdata0, i2c->regs + REG_TOK_WDATA1);
+
+ dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__,
+ wdata0, wdata1, len);
+}
+
+static void meson_i2c_prepare_xfer(struct meson_i2c *i2c)
+{
+ bool write = !(i2c->msg->flags & I2C_M_RD);
+ int i;
+
+ i2c->count = min_t(int, i2c->msg->len - i2c->pos, 8);
+
+ for (i = 0; i < i2c->count - 1; i++)
+ meson_i2c_add_token(i2c, TOKEN_DATA);
+
+ if (i2c->count) {
+ if (write || i2c->pos + i2c->count < i2c->msg->len)
+ meson_i2c_add_token(i2c, TOKEN_DATA);
+ else
+ meson_i2c_add_token(i2c, TOKEN_DATA_LAST);
+ }
+
+ if (write)
+ meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count);
+}
+
+static void meson_i2c_stop(struct meson_i2c *i2c)
+{
+ dev_dbg(i2c->dev, "%s: last %d\n", __func__, i2c->last);
+
+ if (i2c->last) {
+ i2c->state = STATE_STOP;
+ meson_i2c_add_token(i2c, TOKEN_STOP);
+ } else {
+ i2c->state = STATE_IDLE;
+ complete_all(&i2c->done);
+ }
+}
+
+static irqreturn_t meson_i2c_irq(int irqno, void *dev_id)
+{
+ struct meson_i2c *i2c = dev_id;
+ unsigned int ctrl;
+
+ spin_lock(&i2c->lock);
+
+ meson_i2c_reset_tokens(i2c);
+ ctrl = readl(i2c->regs + REG_CTRL);
+
+ dev_dbg(i2c->dev, "irq: state %d, pos %d, count %d, ctrl %08x\n",
+ i2c->state, i2c->pos, i2c->count, ctrl);
+
+ if (ctrl & REG_CTRL_ERROR && i2c->state != STATE_IDLE) {
+ /*
+ * The bit is set when the IGNORE_NAK bit is cleared
+ * and the device didn't respond. In this case, the
+ * I2C controller automatically generates a STOP
+ * condition.
+ */
+ dev_dbg(i2c->dev, "error bit set\n");
+ i2c->error = -ENXIO;
+ i2c->state = STATE_IDLE;
+ complete_all(&i2c->done);
+ goto out;
+ }
+
+ switch (i2c->state) {
+ case STATE_READ:
+ if (i2c->count > 0) {
+ meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos,
+ i2c->count);
+ i2c->pos += i2c->count;
+ }
+
+ if (i2c->pos >= i2c->msg->len) {
+ meson_i2c_stop(i2c);
+ break;
+ }
+
+ meson_i2c_prepare_xfer(i2c);
+ break;
+ case STATE_WRITE:
+ i2c->pos += i2c->count;
+
+ if (i2c->pos >= i2c->msg->len) {
+ meson_i2c_stop(i2c);
+ break;
+ }
+
+ meson_i2c_prepare_xfer(i2c);
+ break;
+ case STATE_STOP:
+ i2c->state = STATE_IDLE;
+ complete_all(&i2c->done);
+ break;
+ case STATE_IDLE:
+ break;
+ }
+
+out:
+ if (i2c->state != STATE_IDLE) {
+ /* Restart the processing */
+ meson_i2c_write_tokens(i2c);
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START,
+ REG_CTRL_START);
+ }
+
+ spin_unlock(&i2c->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg)
+{
+ int token;
+
+ token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ :
+ TOKEN_SLAVE_ADDR_WRITE;
+
+ writel(msg->addr << 1, i2c->regs + REG_SLAVE_ADDR);
+ meson_i2c_add_token(i2c, TOKEN_START);
+ meson_i2c_add_token(i2c, token);
+}
+
+static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
+ int last)
+{
+ unsigned long time_left, flags;
+ int ret = 0;
+
+ i2c->msg = msg;
+ i2c->last = last;
+ i2c->pos = 0;
+ i2c->count = 0;
+ i2c->error = 0;
+
+ meson_i2c_reset_tokens(i2c);
+
+ flags = (msg->flags & I2C_M_IGNORE_NAK) ? REG_CTRL_ACK_IGNORE : 0;
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_ACK_IGNORE, flags);
+
+ if (!(msg->flags & I2C_M_NOSTART))
+ meson_i2c_do_start(i2c, msg);
+
+ i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
+ meson_i2c_prepare_xfer(i2c);
+ meson_i2c_write_tokens(i2c);
+ reinit_completion(&i2c->done);
+
+ /* Start the transfer */
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START);
+
+ time_left = msecs_to_jiffies(I2C_TIMEOUT_MS);
+ time_left = wait_for_completion_timeout(&i2c->done, time_left);
+
+ /*
+ * Protect access to i2c struct and registers from interrupt
+ * handlers triggered by a transfer terminated after the
+ * timeout period
+ */
+ spin_lock_irqsave(&i2c->lock, flags);
+
+ /* Abort any active operation */
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
+
+ if (!time_left) {
+ i2c->state = STATE_IDLE;
+ ret = -ETIMEDOUT;
+ }
+
+ if (i2c->error)
+ ret = i2c->error;
+
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ return ret;
+}
+
+static int meson_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct meson_i2c *i2c = adap->algo_data;
+ int i, ret = 0, count = 0;
+
+ clk_enable(i2c->clk);
+ meson_i2c_set_clk_div(i2c);
+
+ for (i = 0; i < num; i++) {
+ ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1);
+ if (ret)
+ break;
+ count++;
+ }
+
+ clk_disable(i2c->clk);
+
+ return ret ? ret : count;
+}
+
+static u32 meson_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm meson_i2c_algorithm = {
+ .master_xfer = meson_i2c_xfer,
+ .functionality = meson_i2c_func,
+};
+
+static int meson_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct meson_i2c *i2c;
+ struct resource *mem;
+ int ret = 0;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct meson_i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &i2c->frequency))
+ i2c->frequency = DEFAULT_FREQ;
+
+ i2c->dev = &pdev->dev;
+ platform_set_drvdata(pdev, i2c);
+
+ spin_lock_init(&i2c->lock);
+ init_completion(&i2c->done);
+
+ i2c->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c->clk)) {
+ dev_err(&pdev->dev, "can't get device clock\n");
+ return PTR_ERR(i2c->clk);
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(i2c->regs))
+ return PTR_ERR(i2c->regs);
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0) {
+ dev_err(&pdev->dev, "can't find IRQ\n");
+ return i2c->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, i2c->irq, meson_i2c_irq,
+ 0, dev_name(&pdev->dev), i2c);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't request IRQ\n");
+ return ret;
+ }
+
+ ret = clk_prepare(i2c->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't prepare clock\n");
+ return ret;
+ }
+
+ strlcpy(i2c->adap.name, "Meson I2C adapter",
+ sizeof(i2c->adap.name));
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.algo = &meson_i2c_algorithm;
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.dev.of_node = np;
+ i2c->adap.algo_data = i2c;
+
+ /*
+ * A transfer is triggered when START bit changes from 0 to 1.
+ * Ensure that the bit is set to 0 after probe
+ */
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
+
+ ret = i2c_add_adapter(&i2c->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't register adapter\n");
+ clk_unprepare(i2c->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int meson_i2c_remove(struct platform_device *pdev)
+{
+ struct meson_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+ clk_unprepare(i2c->clk);
+
+ return 0;
+}
+
+static const struct of_device_id meson_i2c_match[] = {
+ { .compatible = "amlogic,meson6-i2c" },
+ { },
+};
+
+static struct platform_driver meson_i2c_driver = {
+ .probe = meson_i2c_probe,
+ .remove = meson_i2c_remove,
+ .driver = {
+ .name = "meson-i2c",
+ .of_match_table = meson_i2c_match,
+ },
+};
+
+module_platform_driver(meson_i2c_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson I2C Bus driver");
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 0edf630b099..c74cc2be613 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -124,7 +124,7 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c)
static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
{
unsigned long orig_jiffies = jiffies;
- u32 x;
+ u32 cmd_err;
int result = 0;
if (!i2c->irq) {
@@ -133,11 +133,11 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
if (time_after(jiffies, orig_jiffies + timeout)) {
dev_dbg(i2c->dev, "timeout\n");
writeccr(i2c, 0);
- result = -EIO;
+ result = -ETIMEDOUT;
break;
}
}
- x = readb(i2c->base + MPC_I2C_SR);
+ cmd_err = readb(i2c->base + MPC_I2C_SR);
writeb(0, i2c->base + MPC_I2C_SR);
} else {
/* Interrupt mode */
@@ -150,28 +150,28 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
result = -ETIMEDOUT;
}
- x = i2c->interrupt;
+ cmd_err = i2c->interrupt;
i2c->interrupt = 0;
}
if (result < 0)
return result;
- if (!(x & CSR_MCF)) {
+ if (!(cmd_err & CSR_MCF)) {
dev_dbg(i2c->dev, "unfinished\n");
return -EIO;
}
- if (x & CSR_MAL) {
+ if (cmd_err & CSR_MAL) {
dev_dbg(i2c->dev, "MAL\n");
- return -EIO;
+ return -EAGAIN;
}
- if (writing && (x & CSR_RXAK)) {
+ if (writing && (cmd_err & CSR_RXAK)) {
dev_dbg(i2c->dev, "No RXAK\n");
/* generate stop */
writeccr(i2c, CCR_MEN);
- return -EIO;
+ return -ENXIO;
}
return 0;
}
@@ -813,7 +813,6 @@ static struct platform_driver mpc_i2c_driver = {
.probe = fsl_i2c_probe,
.remove = fsl_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = DRV_NAME,
.of_match_table = mpc_i2c_of_match,
.pm = MPC_I2C_PM_OPS,
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 2f64273d3f2..373f6d4e408 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -925,7 +925,6 @@ static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.remove = mv64xxx_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = MV64XXX_I2C_CTLR_NAME,
.of_match_table = mv64xxx_i2c_of_match_table,
},
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 07e1be6f899..ff8b12c8d25 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -811,7 +811,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
struct resource *res;
int err, irq;
- i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL);
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
@@ -893,7 +893,6 @@ static int mxs_i2c_remove(struct platform_device *pdev)
static struct platform_driver mxs_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = mxs_i2c_dt_ids,
},
.probe = mxs_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-nforce2-s4985.c b/drivers/i2c/busses/i2c-nforce2-s4985.c
index b170bdffb5d..88eda09e73c 100644
--- a/drivers/i2c/busses/i2c-nforce2-s4985.c
+++ b/drivers/i2c/busses/i2c-nforce2-s4985.c
@@ -12,10 +12,6 @@
* 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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index ee3a76c7ae9..70b3c915850 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -17,10 +17,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 9ad038d223c..97998946c4f 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -932,7 +932,7 @@ static int nmk_i2c_runtime_resume(struct device *dev)
static const struct dev_pm_ops nmk_i2c_pm = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early)
- SET_PM_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend,
+ SET_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend,
nmk_i2c_runtime_resume,
NULL)
};
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 2a4fe0b7cfb..7249b5b1e5d 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -480,7 +480,6 @@ static struct platform_driver ocores_i2c_driver = {
.probe = ocores_i2c_probe,
.remove = ocores_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "ocores-i2c",
.of_match_table = ocores_i2c_match,
.pm = OCORES_I2C_PM,
diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c
index 81042b08a94..6e75e016bff 100644
--- a/drivers/i2c/busses/i2c-octeon.c
+++ b/drivers/i2c/busses/i2c-octeon.c
@@ -623,7 +623,6 @@ static struct platform_driver octeon_i2c_driver = {
.probe = octeon_i2c_probe,
.remove = octeon_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = DRV_NAME,
.of_match_table = octeon_i2c_match,
},
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 0dffb0e62c3..0e894193acc 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -22,10 +22,6 @@
* 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/module.h>
@@ -58,6 +54,9 @@
/* timeout for pm runtime autosuspend */
#define OMAP_I2C_PM_TIMEOUT 1000 /* ms */
+/* timeout for making decision on bus free status */
+#define OMAP_I2C_BUS_FREE_TIMEOUT (msecs_to_jiffies(10))
+
/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
enum {
OMAP_I2C_REV_REG = 0,
@@ -102,7 +101,7 @@ enum {
#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */
#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */
-#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */
+#define OMAP_I2C_STAT_BF (1 << 8) /* Bus Free */
#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */
#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */
@@ -150,16 +149,20 @@ enum {
#define OMAP_I2C_SCLH_HSSCLH 8
/* I2C System Test Register (OMAP_I2C_SYSTEST): */
-#ifdef DEBUG
#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
+/* Functional mode */
+#define OMAP_I2C_SYSTEST_SCL_I_FUNC (1 << 8) /* SCL line input value */
+#define OMAP_I2C_SYSTEST_SCL_O_FUNC (1 << 7) /* SCL line output value */
+#define OMAP_I2C_SYSTEST_SDA_I_FUNC (1 << 6) /* SDA line input value */
+#define OMAP_I2C_SYSTEST_SDA_O_FUNC (1 << 5) /* SDA line output value */
+/* SDA/SCL IO mode */
#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
-#endif
/* OCP_SYSSTATUS bit definitions */
#define SYSS_RESETDONE_MASK (1 << 0)
@@ -206,6 +209,9 @@ struct omap_i2c_dev {
*/
u32 rev;
unsigned b_hw:1; /* bad h/w fixes */
+ unsigned bb_valid:1; /* true when BB-bit reflects
+ * the I2C bus state
+ */
unsigned receiver:1; /* true when we're in receiver mode */
u16 iestate; /* Saved interrupt register */
u16 pscstate;
@@ -294,6 +300,12 @@ static void __omap_i2c_init(struct omap_i2c_dev *dev)
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
/*
+ * NOTE: right after setting CON_EN, STAT_BB could be 0 while the
+ * bus is busy. It will be changed to 1 on the next IP FCLK clock.
+ * udelay(1) will be enough to fix that.
+ */
+
+ /*
* Don't write to this register if the IE state is 0 as it can
* cause deadlock.
*/
@@ -332,7 +344,12 @@ static int omap_i2c_reset(struct omap_i2c_dev *dev)
/* SYSC register is cleared by the reset; rewrite it */
omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, sysc);
+ if (dev->rev > OMAP_I2C_REV_ON_3430_3530) {
+ /* Schedule I2C-bus monitoring on the next transfer */
+ dev->bb_valid = 0;
+ }
}
+
return 0;
}
@@ -445,6 +462,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
dev->scllstate = scll;
dev->sclhstate = sclh;
+ if (dev->rev <= OMAP_I2C_REV_ON_3430_3530) {
+ /* Not implemented */
+ dev->bb_valid = 1;
+ }
+
__omap_i2c_init(dev);
return 0;
@@ -469,6 +491,91 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
return 0;
}
+/*
+ * Wait while BB-bit doesn't reflect the I2C bus state
+ *
+ * In a multimaster environment, after IP software reset, BB-bit value doesn't
+ * correspond to the current bus state. It may happen what BB-bit will be 0,
+ * while the bus is busy due to another I2C master activity.
+ * Here are BB-bit values after reset:
+ * SDA SCL BB NOTES
+ * 0 0 0 1, 2
+ * 1 0 0 1, 2
+ * 0 1 1
+ * 1 1 0 3
+ * Later, if IP detect SDA=0 and SCL=1 (ACK) or SDA 1->0 while SCL=1 (START)
+ * combinations on the bus, it set BB-bit to 1.
+ * If IP detect SDA 0->1 while SCL=1 (STOP) combination on the bus,
+ * it set BB-bit to 0 and BF to 1.
+ * BB and BF bits correctly tracks the bus state while IP is suspended
+ * BB bit became valid on the next FCLK clock after CON_EN bit set
+ *
+ * NOTES:
+ * 1. Any transfer started when BB=0 and bus is busy wouldn't be
+ * completed by IP and results in controller timeout.
+ * 2. Any transfer started when BB=0 and SCL=0 results in IP
+ * starting to drive SDA low. In that case IP corrupt data
+ * on the bus.
+ * 3. Any transfer started in the middle of another master's transfer
+ * results in unpredictable results and data corruption
+ */
+static int omap_i2c_wait_for_bb_valid(struct omap_i2c_dev *dev)
+{
+ unsigned long bus_free_timeout = 0;
+ unsigned long timeout;
+ int bus_free = 0;
+ u16 stat, systest;
+
+ if (dev->bb_valid)
+ return 0;
+
+ timeout = jiffies + OMAP_I2C_TIMEOUT;
+ while (1) {
+ stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+ /*
+ * We will see BB or BF event in a case IP had detected any
+ * activity on the I2C bus. Now IP correctly tracks the bus
+ * state. BB-bit value is valid.
+ */
+ if (stat & (OMAP_I2C_STAT_BB | OMAP_I2C_STAT_BF))
+ break;
+
+ /*
+ * Otherwise, we must look signals on the bus to make
+ * the right decision.
+ */
+ systest = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+ if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) &&
+ (systest & OMAP_I2C_SYSTEST_SDA_I_FUNC)) {
+ if (!bus_free) {
+ bus_free_timeout = jiffies +
+ OMAP_I2C_BUS_FREE_TIMEOUT;
+ bus_free = 1;
+ }
+
+ /*
+ * SDA and SCL lines was high for 10 ms without bus
+ * activity detected. The bus is free. Consider
+ * BB-bit value is valid.
+ */
+ if (time_after(jiffies, bus_free_timeout))
+ break;
+ } else {
+ bus_free = 0;
+ }
+
+ if (time_after(jiffies, timeout)) {
+ dev_warn(dev->dev, "timeout waiting for bus ready\n");
+ return -ETIMEDOUT;
+ }
+
+ msleep(1);
+ }
+
+ dev->bb_valid = 1;
+ return 0;
+}
+
static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
{
u16 buf;
@@ -561,7 +668,11 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
if (!dev->b_hw && stop)
w |= OMAP_I2C_CON_STP;
-
+ /*
+ * NOTE: STAT_BB bit could became 1 here if another master occupy
+ * the bus. IP successfully complete transfer when the bus will be
+ * free again (BB reset to 0).
+ */
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
/*
@@ -604,13 +715,15 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
return 0;
/* We have an error */
- if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
- OMAP_I2C_STAT_XUDF)) {
+ if (dev->cmd_err & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_XUDF)) {
omap_i2c_reset(dev);
__omap_i2c_init(dev);
return -EIO;
}
+ if (dev->cmd_err & OMAP_I2C_STAT_AL)
+ return -EAGAIN;
+
if (dev->cmd_err & OMAP_I2C_STAT_NACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return 0;
@@ -639,6 +752,10 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (r < 0)
goto out;
+ r = omap_i2c_wait_for_bb_valid(dev);
+ if (r < 0)
+ goto out;
+
r = omap_i2c_wait_for_bb(dev);
if (r < 0)
goto out;
@@ -926,14 +1043,12 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
if (stat & OMAP_I2C_STAT_NACK) {
err |= OMAP_I2C_STAT_NACK;
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
- break;
}
if (stat & OMAP_I2C_STAT_AL) {
dev_err(dev->dev, "Arbitration lost\n");
err |= OMAP_I2C_STAT_AL;
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
- break;
}
/*
@@ -958,11 +1073,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
if (dev->fifo_size)
num_bytes = dev->buf_len;
- omap_i2c_receive_data(dev, num_bytes, true);
-
- if (dev->errata & I2C_OMAP_ERRATA_I207)
+ if (dev->errata & I2C_OMAP_ERRATA_I207) {
i2c_omap_errata_i207(dev, stat);
+ num_bytes = (omap_i2c_read_reg(dev,
+ OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F;
+ }
+ omap_i2c_receive_data(dev, num_bytes, true);
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
continue;
}
@@ -1284,7 +1401,6 @@ static int omap_i2c_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-#ifdef CONFIG_PM_RUNTIME
static int omap_i2c_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1322,7 +1438,6 @@ static int omap_i2c_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_RUNTIME */
static struct dev_pm_ops omap_i2c_pm_ops = {
SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
@@ -1338,7 +1453,6 @@ static struct platform_driver omap_i2c_driver = {
.remove = omap_i2c_remove,
.driver = {
.name = "omap_i2c",
- .owner = THIS_MODULE,
.pm = OMAP_I2C_PM_OPS,
.of_match_table = of_match_ptr(omap_i2c_of_match),
},
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index 62f55fe624c..1bcdd10b68b 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -18,10 +18,6 @@
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>
@@ -187,7 +183,6 @@ static int i2c_parport_remove(struct platform_device *pdev)
static struct platform_driver i2c_parport_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = DRVNAME,
},
.probe = i2c_parport_probe,
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index a27aae2d675..a1fac5aa9ba 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -18,10 +18,6 @@
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>
diff --git a/drivers/i2c/busses/i2c-parport.h b/drivers/i2c/busses/i2c-parport.h
index e572f3aac0f..4e129453680 100644
--- a/drivers/i2c/busses/i2c-parport.h
+++ b/drivers/i2c/busses/i2c-parport.h
@@ -12,10 +12,6 @@
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 PORT_DATA 0
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c
index 7a9dce43e11..df1dbc92a02 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi.c
@@ -11,10 +11,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c
index 323f061a316..e0eb4ca0102 100644
--- a/drivers/i2c/busses/i2c-pca-isa.c
+++ b/drivers/i2c/busses/i2c-pca-isa.c
@@ -12,10 +12,6 @@
* 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>
diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c
index 845f12598e7..6336f02ec56 100644
--- a/drivers/i2c/busses/i2c-pca-platform.c
+++ b/drivers/i2c/busses/i2c-pca-platform.c
@@ -280,7 +280,6 @@ static struct platform_driver i2c_pca_pf_driver = {
.remove = i2c_pca_pf_remove,
.driver = {
.name = "i2c-pca-platform",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index a6f54ba27e2..67cbec6796a 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -11,10 +11,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index 8564768fee3..44f03eed00d 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -18,10 +18,6 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * 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>
@@ -628,7 +624,6 @@ static struct platform_driver pmcmsptwi_driver = {
.remove = pmcmsptwi_remove,
.driver = {
.name = DRV_NAME,
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index dc7ff829ad7..e814a36d9b7 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -751,7 +751,6 @@ MODULE_DEVICE_TABLE(of, i2c_pnx_of_match);
static struct platform_driver i2c_pnx_driver = {
.driver = {
.name = "pnx-i2c",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(i2c_pnx_of_match),
.pm = PNX_I2C_PM,
},
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index 01e967763c2..60a53c169ed 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -14,10 +14,6 @@
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/module.h>
diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c
index c83fc3ccdd2..82b6f02544d 100644
--- a/drivers/i2c/busses/i2c-puv3.c
+++ b/drivers/i2c/busses/i2c-puv3.c
@@ -270,7 +270,6 @@ static struct platform_driver puv3_i2c_driver = {
.remove = puv3_i2c_remove,
.driver = {
.name = "PKUnity-v3-I2C",
- .owner = THIS_MODULE,
.pm = PUV3_I2C_PM,
}
};
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index be671f7a0e0..d9c0d6a17ad 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -885,7 +885,9 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
return; /* ignore */
}
- if (isr & ISR_BED) {
+ if ((isr & ISR_BED) &&
+ (!((i2c->msg->flags & I2C_M_IGNORE_NAK) &&
+ (isr & ISR_ACKNAK)))) {
int ret = BUS_ERROR;
/*
@@ -919,12 +921,14 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
icr |= ICR_ALDIE | ICR_TB;
/*
- * If this is the last byte of the last message, send
- * a STOP.
+ * If this is the last byte of the last message or last byte
+ * of any message with I2C_M_STOP (e.g. SCCB), send a STOP.
*/
- if (i2c->msg_ptr == i2c->msg->len &&
- i2c->msg_idx == i2c->msg_num - 1)
- icr |= ICR_STOP;
+ if ((i2c->msg_ptr == i2c->msg->len) &&
+ ((i2c->msg->flags & I2C_M_STOP) ||
+ (i2c->msg_idx == i2c->msg_num - 1)))
+ icr |= ICR_STOP;
+
} else if (i2c->msg_idx < i2c->msg_num - 1) {
/*
* Next segment of the message.
@@ -1071,7 +1075,8 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num
static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
}
static const struct i2c_algorithm i2c_pxa_algorithm = {
@@ -1328,7 +1333,6 @@ static struct platform_driver i2c_pxa_driver = {
.remove = i2c_pxa_remove,
.driver = {
.name = "pxa2xx-i2c",
- .owner = THIS_MODULE,
.pm = I2C_PXA_DEV_PM_OPS,
.of_match_table = i2c_pxa_dt_ids,
},
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 092d89bd322..4dad23bdffb 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -764,7 +764,6 @@ static struct platform_driver qup_i2c_driver = {
.remove = qup_i2c_remove,
.driver = {
.name = "i2c_qup",
- .owner = THIS_MODULE,
.pm = &qup_i2c_qup_pm_ops,
.of_match_table = qup_i2c_dt_match,
},
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index d826e82dd99..71a6e07eb7a 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -48,6 +48,12 @@
#define ICMAR 0x20 /* master address */
#define ICRXTX 0x24 /* data port */
+/* ICSCR */
+#define SDBS (1 << 3) /* slave data buffer select */
+#define SIE (1 << 2) /* slave interface enable */
+#define GCAE (1 << 1) /* general call address enable */
+#define FNA (1 << 0) /* forced non acknowledgment */
+
/* ICMCR */
#define MDBS (1 << 7) /* non-fifo mode switch */
#define FSCL (1 << 6) /* override SCL pin */
@@ -58,6 +64,15 @@
#define FSB (1 << 1) /* force stop bit */
#define ESG (1 << 0) /* en startbit gen */
+/* ICSSR (also for ICSIER) */
+#define GCAR (1 << 6) /* general call received */
+#define STM (1 << 5) /* slave transmit mode */
+#define SSR (1 << 4) /* stop received */
+#define SDE (1 << 3) /* slave data empty */
+#define SDT (1 << 2) /* slave data transmitted */
+#define SDR (1 << 1) /* slave data received */
+#define SAR (1 << 0) /* slave addr received */
+
/* ICMSR (also for ICMIE) */
#define MNR (1 << 6) /* nack received */
#define MAL (1 << 5) /* arbitration lost */
@@ -103,6 +118,7 @@ struct rcar_i2c_priv {
u32 icccr;
u32 flags;
enum rcar_i2c_type devtype;
+ struct i2c_client *slave;
};
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
@@ -126,15 +142,6 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
{
- /*
- * reset slave mode.
- * slave mode is not used on this driver
- */
- rcar_i2c_write(priv, ICSIER, 0);
- rcar_i2c_write(priv, ICSAR, 0);
- rcar_i2c_write(priv, ICSCR, 0);
- rcar_i2c_write(priv, ICSSR, 0);
-
/* reset master mode */
rcar_i2c_write(priv, ICMIER, 0);
rcar_i2c_write(priv, ICMCR, 0);
@@ -360,6 +367,63 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
return 0;
}
+static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
+{
+ u32 ssr_raw, ssr_filtered;
+ u8 value;
+
+ ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff;
+ ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER);
+
+ if (!ssr_filtered)
+ return false;
+
+ /* address detected */
+ if (ssr_filtered & SAR) {
+ /* read or write request */
+ if (ssr_raw & STM) {
+ i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value);
+ rcar_i2c_write(priv, ICRXTX, value);
+ rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR);
+ } else {
+ i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value);
+ rcar_i2c_read(priv, ICRXTX); /* dummy read */
+ rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR);
+ }
+
+ rcar_i2c_write(priv, ICSSR, ~SAR & 0xff);
+ }
+
+ /* master sent stop */
+ if (ssr_filtered & SSR) {
+ i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
+ rcar_i2c_write(priv, ICSIER, SAR | SSR);
+ rcar_i2c_write(priv, ICSSR, ~SSR & 0xff);
+ }
+
+ /* master wants to write to us */
+ if (ssr_filtered & SDR) {
+ int ret;
+
+ value = rcar_i2c_read(priv, ICRXTX);
+ ret = i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_END, &value);
+ /* Send NACK in case of error */
+ rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0));
+ i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value);
+ rcar_i2c_write(priv, ICSSR, ~SDR & 0xff);
+ }
+
+ /* master wants to read from us */
+ if (ssr_filtered & SDE) {
+ i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_END, &value);
+ i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value);
+ rcar_i2c_write(priv, ICRXTX, value);
+ rcar_i2c_write(priv, ICSSR, ~SDE & 0xff);
+ }
+
+ return true;
+}
+
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
{
struct rcar_i2c_priv *priv = ptr;
@@ -369,6 +433,9 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
/*-------------- spin lock -----------------*/
spin_lock(&priv->lock);
+ if (rcar_i2c_slave_irq(priv))
+ goto exit;
+
msr = rcar_i2c_read(priv, ICMSR);
/* Only handle interrupts that are currently enabled */
@@ -499,6 +566,43 @@ out:
return ret;
}
+static int rcar_reg_slave(struct i2c_client *slave)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
+
+ if (priv->slave)
+ return -EBUSY;
+
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ pm_runtime_forbid(rcar_i2c_priv_to_dev(priv));
+
+ priv->slave = slave;
+ rcar_i2c_write(priv, ICSAR, slave->addr);
+ rcar_i2c_write(priv, ICSSR, 0);
+ rcar_i2c_write(priv, ICSIER, SAR | SSR);
+ rcar_i2c_write(priv, ICSCR, SIE | SDBS);
+
+ return 0;
+}
+
+static int rcar_unreg_slave(struct i2c_client *slave)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
+
+ WARN_ON(!priv->slave);
+
+ rcar_i2c_write(priv, ICSIER, 0);
+ rcar_i2c_write(priv, ICSCR, 0);
+
+ priv->slave = NULL;
+
+ pm_runtime_allow(rcar_i2c_priv_to_dev(priv));
+
+ return 0;
+}
+
static u32 rcar_i2c_func(struct i2c_adapter *adap)
{
/* This HW can't do SMBUS_QUICK and NOSTART */
@@ -508,6 +612,8 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)
static const struct i2c_algorithm rcar_i2c_algo = {
.master_xfer = rcar_i2c_master_xfer,
.functionality = rcar_i2c_func,
+ .reg_slave = rcar_reg_slave,
+ .unreg_slave = rcar_unreg_slave,
};
static const struct of_device_id rcar_i2c_dt_ids[] = {
@@ -620,7 +726,6 @@ MODULE_DEVICE_TABLE(platform, rcar_i2c_id_table);
static struct platform_driver rcar_i2c_driver = {
.driver = {
.name = "i2c-rcar",
- .owner = THIS_MODULE,
.of_match_table = rcar_i2c_dt_ids,
},
.probe = rcar_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index af3b3d032a9..d7e3af67154 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -414,7 +414,6 @@ static struct platform_driver riic_i2c_driver = {
.remove = riic_i2c_remove,
.driver = {
.name = "i2c-riic",
- .owner = THIS_MODULE,
.of_match_table = riic_i2c_dt_ids,
},
};
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index f486d0eac4d..92462843db6 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -24,6 +24,7 @@
#include <linux/wait.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
+#include <linux/math64.h>
/* Register Map */
@@ -97,6 +98,7 @@ struct rk3x_i2c {
/* Hardware resources */
void __iomem *regs;
struct clk *clk;
+ struct notifier_block clk_rate_nb;
/* Settings */
unsigned int scl_frequency;
@@ -428,18 +430,231 @@ out:
return IRQ_HANDLED;
}
-static void rk3x_i2c_set_scl_rate(struct rk3x_i2c *i2c, unsigned long scl_rate)
+/**
+ * Calculate divider values for desired SCL frequency
+ *
+ * @clk_rate: I2C input clock rate
+ * @scl_rate: Desired SCL rate
+ * @div_low: Divider output for low
+ * @div_high: Divider output for high
+ *
+ * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
+ * a best-effort divider value is returned in divs. If the target rate is
+ * too high, we silently use the highest possible rate.
+ */
+static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
+ unsigned long *div_low, unsigned long *div_high)
{
- unsigned long i2c_rate = clk_get_rate(i2c->clk);
- unsigned int div;
+ unsigned long min_low_ns, min_high_ns;
+ unsigned long max_data_hold_ns;
+ unsigned long data_hold_buffer_ns;
+ unsigned long max_low_ns, min_total_ns;
+
+ unsigned long clk_rate_khz, scl_rate_khz;
+
+ unsigned long min_low_div, min_high_div;
+ unsigned long max_low_div;
+
+ unsigned long min_div_for_hold, min_total_div;
+ unsigned long extra_div, extra_low_div, ideal_low_div;
+
+ int ret = 0;
+
+ /* Only support standard-mode and fast-mode */
+ if (WARN_ON(scl_rate > 400000))
+ scl_rate = 400000;
+
+ /* prevent scl_rate_khz from becoming 0 */
+ if (WARN_ON(scl_rate < 1000))
+ scl_rate = 1000;
+
+ /*
+ * min_low_ns: The minimum number of ns we need to hold low
+ * to meet i2c spec
+ * min_high_ns: The minimum number of ns we need to hold high
+ * to meet i2c spec
+ * max_low_ns: The maximum number of ns we can hold low
+ * to meet i2c spec
+ *
+ * Note: max_low_ns should be (max data hold time * 2 - buffer)
+ * This is because the i2c host on Rockchip holds the data line
+ * for half the low time.
+ */
+ if (scl_rate <= 100000) {
+ min_low_ns = 4700;
+ min_high_ns = 4000;
+ max_data_hold_ns = 3450;
+ data_hold_buffer_ns = 50;
+ } else {
+ min_low_ns = 1300;
+ min_high_ns = 600;
+ max_data_hold_ns = 900;
+ data_hold_buffer_ns = 50;
+ }
+ max_low_ns = max_data_hold_ns * 2 - data_hold_buffer_ns;
+ min_total_ns = min_low_ns + min_high_ns;
+
+ /* Adjust to avoid overflow */
+ clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
+ scl_rate_khz = scl_rate / 1000;
+
+ /*
+ * We need the total div to be >= this number
+ * so we don't clock too fast.
+ */
+ min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8);
+
+ /* These are the min dividers needed for min hold times. */
+ min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000);
+ min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000);
+ min_div_for_hold = (min_low_div + min_high_div);
+
+ /*
+ * This is the maximum divider so we don't go over the max.
+ * We don't round up here (we round down) since this is a max.
+ */
+ max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000);
+
+ if (min_low_div > max_low_div) {
+ WARN_ONCE(true,
+ "Conflicting, min_low_div %lu, max_low_div %lu\n",
+ min_low_div, max_low_div);
+ max_low_div = min_low_div;
+ }
+
+ if (min_div_for_hold > min_total_div) {
+ /*
+ * Time needed to meet hold requirements is important.
+ * Just use that.
+ */
+ *div_low = min_low_div;
+ *div_high = min_high_div;
+ } else {
+ /*
+ * We've got to distribute some time among the low and high
+ * so we don't run too fast.
+ */
+ extra_div = min_total_div - min_div_for_hold;
+
+ /*
+ * We'll try to split things up perfectly evenly,
+ * biasing slightly towards having a higher div
+ * for low (spend more time low).
+ */
+ ideal_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns,
+ scl_rate_khz * 8 * min_total_ns);
+
+ /* Don't allow it to go over the max */
+ if (ideal_low_div > max_low_div)
+ ideal_low_div = max_low_div;
+
+ /*
+ * Handle when the ideal low div is going to take up
+ * more than we have.
+ */
+ if (ideal_low_div > min_low_div + extra_div)
+ ideal_low_div = min_low_div + extra_div;
- /* set DIV = DIVH = DIVL
- * SCL rate = (clk rate) / (8 * (DIVH + 1 + DIVL + 1))
- * = (clk rate) / (16 * (DIV + 1))
+ /* Give low the "ideal" and give high whatever extra is left */
+ extra_low_div = ideal_low_div - min_low_div;
+ *div_low = ideal_low_div;
+ *div_high = min_high_div + (extra_div - extra_low_div);
+ }
+
+ /*
+ * Adjust to the fact that the hardware has an implicit "+1".
+ * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
*/
- div = DIV_ROUND_UP(i2c_rate, scl_rate * 16) - 1;
+ *div_low = *div_low - 1;
+ *div_high = *div_high - 1;
+
+ /* Maximum divider supported by hw is 0xffff */
+ if (*div_low > 0xffff) {
+ *div_low = 0xffff;
+ ret = -EINVAL;
+ }
+
+ if (*div_high > 0xffff) {
+ *div_high = 0xffff;
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
+{
+ unsigned long div_low, div_high;
+ u64 t_low_ns, t_high_ns;
+ int ret;
+
+ ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, &div_low,
+ &div_high);
+
+ WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
+
+ clk_enable(i2c->clk);
+ i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
+ clk_disable(i2c->clk);
- i2c_writel(i2c, (div << 16) | (div & 0xffff), REG_CLKDIV);
+ t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
+ t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
+ dev_dbg(i2c->dev,
+ "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
+ clk_rate / 1000,
+ 1000000000 / i2c->scl_frequency,
+ t_low_ns, t_high_ns);
+}
+
+/**
+ * rk3x_i2c_clk_notifier_cb - Clock rate change callback
+ * @nb: Pointer to notifier block
+ * @event: Notification reason
+ * @data: Pointer to notification data object
+ *
+ * The callback checks whether a valid bus frequency can be generated after the
+ * change. If so, the change is acknowledged, otherwise the change is aborted.
+ * New dividers are written to the HW in the pre- or post change notification
+ * depending on the scaling direction.
+ *
+ * Code adapted from i2c-cadence.c.
+ *
+ * Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK
+ * to acknowedge the change, NOTIFY_DONE if the notification is
+ * considered irrelevant.
+ */
+static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
+ event, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
+ unsigned long div_low, div_high;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency,
+ &div_low, &div_high) != 0) {
+ return NOTIFY_STOP;
+ }
+
+ /* scale up */
+ if (ndata->new_rate > ndata->old_rate)
+ rk3x_i2c_adapt_div(i2c, ndata->new_rate);
+
+ return NOTIFY_OK;
+ case POST_RATE_CHANGE:
+ /* scale down */
+ if (ndata->new_rate < ndata->old_rate)
+ rk3x_i2c_adapt_div(i2c, ndata->new_rate);
+ return NOTIFY_OK;
+ case ABORT_RATE_CHANGE:
+ /* scale up */
+ if (ndata->new_rate > ndata->old_rate)
+ rk3x_i2c_adapt_div(i2c, ndata->old_rate);
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
}
/**
@@ -536,9 +751,6 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
clk_enable(i2c->clk);
- /* The clock rate might have changed, so setup the divider again */
- rk3x_i2c_set_scl_rate(i2c, i2c->scl_frequency);
-
i2c->is_last_msg = false;
/*
@@ -624,6 +836,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
int bus_nr;
u32 value;
int irq;
+ unsigned long clk_rate;
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
if (!i2c)
@@ -724,16 +937,28 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
return ret;
}
+ i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
+ ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Unable to register clock notifier\n");
+ goto err_clk;
+ }
+
+ clk_rate = clk_get_rate(i2c->clk);
+ rk3x_i2c_adapt_div(i2c, clk_rate);
+
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register adapter\n");
- goto err_clk;
+ goto err_clk_notifier;
}
dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);
return 0;
+err_clk_notifier:
+ clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
err_clk:
clk_unprepare(i2c->clk);
return ret;
@@ -744,6 +969,8 @@ static int rk3x_i2c_remove(struct platform_device *pdev)
struct rk3x_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
+
+ clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
clk_unprepare(i2c->clk);
return 0;
@@ -753,7 +980,6 @@ static struct platform_driver rk3x_i2c_driver = {
.probe = rk3x_i2c_probe,
.remove = rk3x_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "rk3x-i2c",
.of_match_table = rk3x_i2c_match,
},
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index e3b0337faeb..bff20a58962 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -14,10 +14,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
@@ -39,6 +35,8 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <asm/irq.h>
@@ -91,6 +89,9 @@
/* Max time to wait for bus to become idle after a xfer (in us) */
#define S3C2410_IDLE_TIMEOUT 5000
+/* Exynos5 Sysreg offset */
+#define EXYNOS5_SYS_I2C_CFG 0x0234
+
/* i2c controller state */
enum s3c24xx_i2c_state {
STATE_IDLE,
@@ -127,6 +128,8 @@ struct s3c24xx_i2c {
#if defined(CONFIG_ARM_S3C24XX_CPUFREQ)
struct notifier_block freq_transition;
#endif
+ struct regmap *sysreg;
+ unsigned int sys_i2c_cfg;
};
static struct platform_device_id s3c24xx_driver_ids[] = {
@@ -1075,6 +1078,7 @@ static void
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
{
struct s3c2410_platform_i2c *pdata = i2c->pdata;
+ int id;
if (!np)
return;
@@ -1084,6 +1088,21 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
of_property_read_u32(np, "samsung,i2c-max-bus-freq",
(u32 *)&pdata->frequency);
+ /*
+ * Exynos5's legacy i2c controller and new high speed i2c
+ * controller have muxed interrupt sources. By default the
+ * interrupts for 4-channel HS-I2C controller are enabled.
+ * If nodes for first four channels of legacy i2c controller
+ * are available then re-configure the interrupts via the
+ * system register.
+ */
+ id = of_alias_get_id(np, "i2c");
+ i2c->sysreg = syscon_regmap_lookup_by_phandle(np,
+ "samsung,sysreg-phandle");
+ if (IS_ERR(i2c->sysreg))
+ return;
+
+ regmap_update_bits(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, BIT(id), 0);
}
#else
static void
@@ -1264,6 +1283,9 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev)
i2c->suspended = 1;
+ if (!IS_ERR(i2c->sysreg))
+ regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg);
+
return 0;
}
@@ -1272,6 +1294,9 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
+ if (!IS_ERR(i2c->sysreg))
+ regmap_write(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, i2c->sys_i2c_cfg);
+
clk_prepare_enable(i2c->clk);
s3c24xx_i2c_init(i2c);
clk_disable_unprepare(i2c->clk);
@@ -1305,7 +1330,6 @@ static struct platform_driver s3c24xx_i2c_driver = {
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
- .owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
.of_match_table = of_match_ptr(s3c24xx_i2c_match),
diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c
index d76f3d9737e..24968384b40 100644
--- a/drivers/i2c/busses/i2c-sh7760.c
+++ b/drivers/i2c/busses/i2c-sh7760.c
@@ -552,7 +552,6 @@ static int sh7760_i2c_remove(struct platform_device *pdev)
static struct platform_driver sh7760_i2c_drv = {
.driver = {
.name = SH7760_I2C_DEVNAME,
- .owner = THIS_MODULE,
},
.probe = sh7760_i2c_probe,
.remove = sh7760_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 8b5e79cb446..d7efaf44868 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -1,6 +1,8 @@
/*
* SuperH Mobile I2C Controller
*
+ * Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
+ *
* Copyright (C) 2008 Magnus Damm
*
* Portions of the code based on out-of-tree driver i2c-sh7343.c
@@ -14,26 +16,24 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c/i2c-sh_mobile.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/err.h>
#include <linux/pm_runtime.h>
-#include <linux/clk.h>
-#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
-#include <linux/i2c/i2c-sh_mobile.h>
/* Transmit operation: */
/* */
@@ -114,6 +114,7 @@ enum sh_mobile_i2c_op {
OP_TX_FIRST,
OP_TX,
OP_TX_STOP,
+ OP_TX_STOP_DATA,
OP_TX_TO_RX,
OP_RX,
OP_RX_STOP,
@@ -138,6 +139,11 @@ struct sh_mobile_i2c_data {
int pos;
int sr;
bool send_stop;
+
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
+ struct scatterlist sg;
+ enum dma_data_direction dma_direction;
};
struct sh_mobile_dt_config {
@@ -175,6 +181,8 @@ struct sh_mobile_dt_config {
#define ICIC_ICCLB8 0x80
#define ICIC_ICCHB8 0x40
+#define ICIC_TDMAE 0x20
+#define ICIC_RDMAE 0x10
#define ICIC_ALE 0x08
#define ICIC_TACKE 0x04
#define ICIC_WAITE 0x02
@@ -281,6 +289,7 @@ static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
else
pd->icic &= ~ICIC_ICCHB8;
+ dev_dbg(pd->dev, "timing values: L/H=0x%x/0x%x\n", pd->iccl, pd->icch);
return 0;
}
@@ -336,8 +345,10 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
case OP_TX: /* write data */
iic_wr(pd, ICDR, data);
break;
- case OP_TX_STOP: /* write data and issue a stop afterwards */
+ case OP_TX_STOP_DATA: /* write data and issue a stop afterwards */
iic_wr(pd, ICDR, data);
+ /* fallthrough */
+ case OP_TX_STOP: /* issue a stop */
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
: ICCR_ICE | ICCR_TRS | ICCR_BBSY);
break;
@@ -393,13 +404,17 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
{
unsigned char data;
- if (pd->pos == pd->msg->len)
+ if (pd->pos == pd->msg->len) {
+ /* Send stop if we haven't yet (DMA case) */
+ if (pd->send_stop && (iic_rd(pd, ICCR) & ICCR_BBSY))
+ i2c_op(pd, OP_TX_STOP, 0);
return 1;
+ }
sh_mobile_i2c_get_data(pd, &data);
if (sh_mobile_i2c_is_last_byte(pd))
- i2c_op(pd, OP_TX_STOP, data);
+ i2c_op(pd, OP_TX_STOP_DATA, data);
else if (sh_mobile_i2c_is_first_byte(pd))
i2c_op(pd, OP_TX_FIRST, data);
else
@@ -451,10 +466,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
{
- struct platform_device *dev = dev_id;
- struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
+ struct sh_mobile_i2c_data *pd = dev_id;
unsigned char sr;
- int wakeup;
+ int wakeup = 0;
sr = iic_rd(pd, ICSR);
pd->sr |= sr; /* remember state */
@@ -463,15 +477,21 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
(pd->msg->flags & I2C_M_RD) ? "read" : "write",
pd->pos, pd->msg->len);
- if (sr & (ICSR_AL | ICSR_TACK)) {
+ /* Kick off TxDMA after preface was done */
+ if (pd->dma_direction == DMA_TO_DEVICE && pd->pos == 0)
+ iic_set_clr(pd, ICIC, ICIC_TDMAE, 0);
+ else if (sr & (ICSR_AL | ICSR_TACK))
/* don't interrupt transaction - continue to issue stop */
iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
- wakeup = 0;
- } else if (pd->msg->flags & I2C_M_RD)
+ else if (pd->msg->flags & I2C_M_RD)
wakeup = sh_mobile_i2c_isr_rx(pd);
else
wakeup = sh_mobile_i2c_isr_tx(pd);
+ /* Kick off RxDMA after preface was done */
+ if (pd->dma_direction == DMA_FROM_DEVICE && pd->pos == 1)
+ iic_set_clr(pd, ICIC, ICIC_RDMAE, 0);
+
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
@@ -486,6 +506,84 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void sh_mobile_i2c_dma_unmap(struct sh_mobile_i2c_data *pd)
+{
+ struct dma_chan *chan = pd->dma_direction == DMA_FROM_DEVICE
+ ? pd->dma_rx : pd->dma_tx;
+
+ dma_unmap_single(chan->device->dev, sg_dma_address(&pd->sg),
+ pd->msg->len, pd->dma_direction);
+
+ pd->dma_direction = DMA_NONE;
+}
+
+static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd)
+{
+ if (pd->dma_direction == DMA_NONE)
+ return;
+ else if (pd->dma_direction == DMA_FROM_DEVICE)
+ dmaengine_terminate_all(pd->dma_rx);
+ else if (pd->dma_direction == DMA_TO_DEVICE)
+ dmaengine_terminate_all(pd->dma_tx);
+
+ sh_mobile_i2c_dma_unmap(pd);
+}
+
+static void sh_mobile_i2c_dma_callback(void *data)
+{
+ struct sh_mobile_i2c_data *pd = data;
+
+ sh_mobile_i2c_dma_unmap(pd);
+ pd->pos = pd->msg->len;
+
+ iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
+}
+
+static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
+{
+ bool read = pd->msg->flags & I2C_M_RD;
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ struct dma_chan *chan = read ? pd->dma_rx : pd->dma_tx;
+ struct dma_async_tx_descriptor *txdesc;
+ dma_addr_t dma_addr;
+ dma_cookie_t cookie;
+
+ if (!chan)
+ return;
+
+ dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir);
+ if (dma_mapping_error(pd->dev, dma_addr)) {
+ dev_dbg(pd->dev, "dma map failed, using PIO\n");
+ return;
+ }
+
+ sg_dma_len(&pd->sg) = pd->msg->len;
+ sg_dma_address(&pd->sg) = dma_addr;
+
+ pd->dma_direction = dir;
+
+ txdesc = dmaengine_prep_slave_sg(chan, &pd->sg, 1,
+ read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc) {
+ dev_dbg(pd->dev, "dma prep slave sg failed, using PIO\n");
+ sh_mobile_i2c_cleanup_dma(pd);
+ return;
+ }
+
+ txdesc->callback = sh_mobile_i2c_dma_callback;
+ txdesc->callback_param = pd;
+
+ cookie = dmaengine_submit(txdesc);
+ if (dma_submit_error(cookie)) {
+ dev_dbg(pd->dev, "submitting dma failed, using PIO\n");
+ sh_mobile_i2c_cleanup_dma(pd);
+ return;
+ }
+
+ dma_async_issue_pending(chan);
+}
+
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
bool do_init)
{
@@ -510,6 +608,9 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
pd->pos = -1;
pd->sr = 0;
+ if (pd->msg->len > 8)
+ sh_mobile_i2c_xfer_dma(pd);
+
/* Enable all interrupts to begin with */
iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
return 0;
@@ -593,6 +694,9 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
5 * HZ);
if (!k) {
dev_err(pd->dev, "Transfer request timed out\n");
+ if (pd->dma_direction != DMA_NONE)
+ sh_mobile_i2c_cleanup_dma(pd);
+
err = -ETIMEDOUT;
break;
}
@@ -626,22 +730,77 @@ static const struct sh_mobile_dt_config default_dt_config = {
.clks_per_count = 1,
};
-static const struct sh_mobile_dt_config rcar_gen2_dt_config = {
+static const struct sh_mobile_dt_config fast_clock_dt_config = {
.clks_per_count = 2,
};
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
- { .compatible = "renesas,iic-r8a7790", .data = &rcar_gen2_dt_config },
- { .compatible = "renesas,iic-r8a7791", .data = &rcar_gen2_dt_config },
- { .compatible = "renesas,iic-r8a7792", .data = &rcar_gen2_dt_config },
- { .compatible = "renesas,iic-r8a7793", .data = &rcar_gen2_dt_config },
- { .compatible = "renesas,iic-r8a7794", .data = &rcar_gen2_dt_config },
+ { .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
{},
};
MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids);
-static int sh_mobile_i2c_hook_irqs(struct platform_device *dev)
+static int sh_mobile_i2c_request_dma_chan(struct device *dev, enum dma_transfer_direction dir,
+ dma_addr_t port_addr, struct dma_chan **chan_ptr)
+{
+ struct dma_chan *chan;
+ struct dma_slave_config cfg;
+ char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx";
+ int ret;
+
+ *chan_ptr = NULL;
+
+ chan = dma_request_slave_channel_reason(dev, chan_name);
+ if (IS_ERR(chan)) {
+ ret = PTR_ERR(chan);
+ dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret);
+ return ret;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.direction = dir;
+ if (dir == DMA_MEM_TO_DEV) {
+ cfg.dst_addr = port_addr;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ } else {
+ cfg.src_addr = port_addr;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ }
+
+ ret = dmaengine_slave_config(chan, &cfg);
+ if (ret) {
+ dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret);
+ dma_release_channel(chan);
+ return ret;
+ }
+
+ *chan_ptr = chan;
+
+ dev_dbg(dev, "got DMA channel for %s\n", chan_name);
+ return 0;
+}
+
+static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd)
+{
+ if (pd->dma_tx) {
+ dma_release_channel(pd->dma_tx);
+ pd->dma_tx = NULL;
+ }
+
+ if (pd->dma_rx) {
+ dma_release_channel(pd->dma_rx);
+ pd->dma_rx = NULL;
+ }
+}
+
+static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd)
{
struct resource *res;
resource_size_t n;
@@ -650,7 +809,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev)
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
for (n = res->start; n <= res->end; n++) {
ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
- 0, dev_name(&dev->dev), dev);
+ 0, dev_name(&dev->dev), pd);
if (ret) {
dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
return ret;
@@ -681,7 +840,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
return PTR_ERR(pd->clk);
}
- ret = sh_mobile_i2c_hook_irqs(dev);
+ ret = sh_mobile_i2c_hook_irqs(dev, pd);
if (ret)
return ret;
@@ -727,6 +886,21 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
if (ret)
return ret;
+ /* Init DMA */
+ sg_init_table(&pd->sg, 1);
+ pd->dma_direction = DMA_NONE;
+ ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM,
+ res->start + ICDR, &pd->dma_rx);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV,
+ res->start + ICDR, &pd->dma_tx);
+ if (ret == -EPROBE_DEFER) {
+ sh_mobile_i2c_release_dma(pd);
+ return ret;
+ }
+
/* Enable Runtime PM for this device.
*
* Also tell the Runtime PM core to ignore children
@@ -758,13 +932,13 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
ret = i2c_add_numbered_adapter(adap);
if (ret < 0) {
+ sh_mobile_i2c_release_dma(pd);
dev_err(&dev->dev, "cannot add numbered adapter\n");
return ret;
}
- dev_info(&dev->dev,
- "I2C adapter %d with bus speed %lu Hz (L/H=0x%x/0x%x)\n",
- adap->nr, pd->bus_speed, pd->iccl, pd->icch);
+ dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz, DMA=%c\n",
+ adap->nr, pd->bus_speed, (pd->dma_rx || pd->dma_tx) ? 'y' : 'n');
return 0;
}
@@ -774,6 +948,7 @@ static int sh_mobile_i2c_remove(struct platform_device *dev)
struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
i2c_del_adapter(&pd->adap);
+ sh_mobile_i2c_release_dma(pd);
pm_runtime_disable(&dev->dev);
return 0;
}
@@ -798,7 +973,6 @@ static const struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
static struct platform_driver sh_mobile_i2c_driver = {
.driver = {
.name = "i2c-sh_mobile",
- .owner = THIS_MODULE,
.pm = &sh_mobile_i2c_dev_pm_ops,
.of_match_table = sh_mobile_i2c_dt_ids,
},
@@ -810,16 +984,15 @@ static int __init sh_mobile_i2c_adap_init(void)
{
return platform_driver_register(&sh_mobile_i2c_driver);
}
+subsys_initcall(sh_mobile_i2c_adap_init);
static void __exit sh_mobile_i2c_adap_exit(void)
{
platform_driver_unregister(&sh_mobile_i2c_driver);
}
-
-subsys_initcall(sh_mobile_i2c_adap_init);
module_exit(sh_mobile_i2c_adap_exit);
MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver");
-MODULE_AUTHOR("Magnus Damm");
+MODULE_AUTHOR("Magnus Damm and Wolfram Sang");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:i2c-sh_mobile");
diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index 0fe505d7abe..2b6219d86b0 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -12,10 +12,6 @@
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/busses/i2c-simtec.c b/drivers/i2c/busses/i2c-simtec.c
index 964e5c6f84a..b4685bb9b5d 100644
--- a/drivers/i2c/busses/i2c-simtec.c
+++ b/drivers/i2c/busses/i2c-simtec.c
@@ -12,10 +12,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
@@ -158,7 +154,6 @@ static int simtec_i2c_remove(struct platform_device *dev)
static struct platform_driver simtec_i2c_driver = {
.driver = {
.name = "simtec-i2c",
- .owner = THIS_MODULE,
},
.probe = simtec_i2c_probe,
.remove = simtec_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c
index b1336d5f053..1092d4eeeb5 100644
--- a/drivers/i2c/busses/i2c-sirf.c
+++ b/drivers/i2c/busses/i2c-sirf.c
@@ -451,7 +451,6 @@ 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
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index ac9bc33acef..7d58a40faf2 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -11,10 +11,6 @@
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.
*/
/* Note: we assume there can only be one SIS5595 with one SMBus interface */
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index c6366733008..1e6805b5cef 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -10,10 +10,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index 8dc2fc5f74f..44b90442607 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -10,10 +10,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c
index 2e4eccd6599..88057fad9df 100644
--- a/drivers/i2c/busses/i2c-st.c
+++ b/drivers/i2c/busses/i2c-st.c
@@ -863,7 +863,6 @@ MODULE_DEVICE_TABLE(of, st_i2c_match);
static struct platform_driver st_i2c_driver = {
.driver = {
.name = "st-i2c",
- .owner = THIS_MODULE,
.of_match_table = st_i2c_match,
.pm = ST_I2C_PM,
},
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index 6a44f37798c..4885da9e929 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -981,7 +981,6 @@ static const struct of_device_id stu300_dt_match[] = {
static struct platform_driver stu300_i2c_driver = {
.driver = {
.name = NAME,
- .owner = THIS_MODULE,
.pm = STU300_I2C_PM,
.of_match_table = stu300_dt_match,
},
diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c
index 4d75d475970..7668e2e9b8f 100644
--- a/drivers/i2c/busses/i2c-sun6i-p2wi.c
+++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c
@@ -332,7 +332,6 @@ static struct platform_driver p2wi_driver = {
.probe = p2wi_probe,
.remove = p2wi_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "i2c-sunxi-p2wi",
.of_match_table = p2wi_of_match_table,
},
diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c
index 10855a0b7e7..4c7fc2d4701 100644
--- a/drivers/i2c/busses/i2c-taos-evm.c
+++ b/drivers/i2c/busses/i2c-taos-evm.c
@@ -13,10 +13,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index efba1ebe16b..28b87e68350 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -893,7 +893,6 @@ static struct platform_driver tegra_i2c_driver = {
.remove = tegra_i2c_remove,
.driver = {
.name = "tegra-i2c",
- .owner = THIS_MODULE,
.of_match_table = tegra_i2c_of_match,
.pm = TEGRA_I2C_PM,
},
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
index 6bb3a89a440..240637f01d1 100644
--- a/drivers/i2c/busses/i2c-versatile.c
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -138,7 +138,6 @@ static struct platform_driver i2c_versatile_driver = {
.remove = i2c_versatile_remove,
.driver = {
.name = "versatile-i2c",
- .owner = THIS_MODULE,
.of_match_table = i2c_versatile_match,
},
};
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index f4a1ed75761..59b1d233ca7 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -12,10 +12,6 @@
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>
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 6841200b6e5..0ee2646f3b0 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -13,10 +13,6 @@
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.
*/
/*
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
index f80a38c2072..82ea3492548 100644
--- a/drivers/i2c/busses/i2c-wmt.c
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -462,7 +462,6 @@ static struct platform_driver wmt_i2c_driver = {
.remove = wmt_i2c_remove,
.driver = {
.name = "wmt-i2c",
- .owner = THIS_MODULE,
.of_match_table = wmt_i2c_dt_ids,
},
};
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index ade9223912d..e8400042b35 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -12,10 +12,6 @@
* 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.
- *
*
* This code was implemented by Mocean Laboratories AB when porting linux
* to the automotive development board Russellville. The copyright holder
@@ -50,6 +46,11 @@ enum xilinx_i2c_state {
STATE_START
};
+enum xiic_endian {
+ LITTLE,
+ BIG
+};
+
/**
* struct xiic_i2c - Internal representation of the XIIC I2C bus
* @base: Memory base of the HW registers
@@ -74,6 +75,7 @@ struct xiic_i2c {
enum xilinx_i2c_state state;
struct i2c_msg *rx_msg;
int rx_pos;
+ enum xiic_endian endianness;
};
@@ -174,29 +176,58 @@ struct xiic_i2c {
static void xiic_start_xfer(struct xiic_i2c *i2c);
static void __xiic_start_xfer(struct xiic_i2c *i2c);
+/*
+ * For the register read and write functions, a little-endian and big-endian
+ * version are necessary. Endianness is detected during the probe function.
+ * Only the least significant byte [doublet] of the register are ever
+ * accessed. This requires an offset of 3 [2] from the base address for
+ * big-endian systems.
+ */
+
static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value)
{
- iowrite8(value, i2c->base + reg);
+ if (i2c->endianness == LITTLE)
+ iowrite8(value, i2c->base + reg);
+ else
+ iowrite8(value, i2c->base + reg + 3);
}
static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg)
{
- return ioread8(i2c->base + reg);
+ u8 ret;
+
+ if (i2c->endianness == LITTLE)
+ ret = ioread8(i2c->base + reg);
+ else
+ ret = ioread8(i2c->base + reg + 3);
+ return ret;
}
static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value)
{
- iowrite16(value, i2c->base + reg);
+ if (i2c->endianness == LITTLE)
+ iowrite16(value, i2c->base + reg);
+ else
+ iowrite16be(value, i2c->base + reg + 2);
}
static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value)
{
- iowrite32(value, i2c->base + reg);
+ if (i2c->endianness == LITTLE)
+ iowrite32(value, i2c->base + reg);
+ else
+ iowrite32be(value, i2c->base + reg);
}
static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg)
{
- return ioread32(i2c->base + reg);
+ u32 ret;
+
+ if (i2c->endianness == LITTLE)
+ ret = ioread32(i2c->base + reg);
+ else
+ ret = ioread32be(i2c->base + reg);
+ return ret;
}
static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask)
@@ -696,6 +727,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
struct resource *res;
int ret, irq;
u8 i;
+ u32 sr;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
@@ -728,6 +760,18 @@ static int xiic_i2c_probe(struct platform_device *pdev)
return ret;
}
+ /*
+ * Detect endianness
+ * Try to reset the TX FIFO. Then check the EMPTY flag. If it is not
+ * set, assume that the endianness was wrong and swap.
+ */
+ i2c->endianness = LITTLE;
+ xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK);
+ /* Reset is cleared in xiic_reinit */
+ sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET);
+ if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK))
+ i2c->endianness = BIG;
+
xiic_reinit(i2c);
/* add i2c adapter to i2c tree */
@@ -771,7 +815,6 @@ static struct platform_driver xiic_i2c_driver = {
.probe = xiic_i2c_probe,
.remove = xiic_i2c_remove,
.driver = {
- .owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(xiic_of_match),
},
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 17f7352eca6..8b36bcfd952 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -263,7 +263,6 @@ static struct platform_driver xlr_i2c_driver = {
.remove = xlr_i2c_remove,
.driver = {
.name = "xlr-i2cbus",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index ff3f5747e43..0a7e410b619 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -17,10 +17,6 @@
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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -548,7 +544,6 @@ static int scx200_remove(struct platform_device *pdev)
static struct platform_driver scx200_pci_driver = {
.driver = {
.name = "cs5535-smb",
- .owner = THIS_MODULE,
},
.probe = scx200_probe,
.remove = scx200_remove,
diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c
index f24cc64e2e8..90e32295930 100644
--- a/drivers/i2c/i2c-boardinfo.c
+++ b/drivers/i2c/i2c-boardinfo.c
@@ -10,11 +10,6 @@
* 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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 2f90ac6a7f7..39d25a8cb1a 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -10,12 +10,7 @@
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., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA. */
+ GNU General Public License for more details. */
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
@@ -29,6 +24,7 @@
(c) 2013 Wolfram Sang <wsa@the-dreams.de>
I2C ACPI code Copyright (C) 2014 Intel Corp
Author: Lan Tianyu <tianyu.lan@intel.com>
+ I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
*/
#include <linux/module.h>
@@ -54,6 +50,7 @@
#include <linux/acpi.h>
#include <linux/jump_label.h>
#include <asm/uaccess.h>
+#include <linux/err.h>
#include "i2c-core.h"
@@ -265,7 +262,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
struct acpi_resource *ares;
u32 accessor_type = function >> 16;
u8 action = function & ACPI_IO_MASK;
- acpi_status ret = AE_OK;
+ acpi_status ret;
int status;
ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
@@ -408,6 +405,7 @@ static int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
return -ENOMEM;
}
+ acpi_walk_dep_device_list(handle);
return 0;
}
@@ -631,6 +629,17 @@ static int i2c_device_probe(struct device *dev)
if (!client)
return 0;
+ if (!client->irq && dev->of_node) {
+ int irq = of_irq_get(dev->of_node, 0);
+
+ if (irq == -EPROBE_DEFER)
+ return irq;
+ if (irq < 0)
+ irq = 0;
+
+ client->irq = irq;
+ }
+
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
@@ -670,6 +679,9 @@ static int i2c_device_remove(struct device *dev)
status = driver->remove(client);
}
+ if (dev->of_node)
+ irq_dispose_mapping(client->irq);
+
dev_pm_domain_detach(&client->dev, true);
return status;
}
@@ -1370,63 +1382,67 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
/* OF support code */
#if IS_ENABLED(CONFIG_OF)
-static void of_i2c_register_devices(struct i2c_adapter *adap)
+static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
+ struct device_node *node)
{
- void *result;
- struct device_node *node;
+ struct i2c_client *result;
+ struct i2c_board_info info = {};
+ struct dev_archdata dev_ad = {};
+ const __be32 *addr;
+ int len;
- /* Only register child devices if the adapter has a node pointer set */
- if (!adap->dev.of_node)
- return;
+ dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
- dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
+ if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
+ dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
+ node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
- for_each_available_child_of_node(adap->dev.of_node, node) {
- struct i2c_board_info info = {};
- struct dev_archdata dev_ad = {};
- const __be32 *addr;
- int len;
+ addr = of_get_property(node, "reg", &len);
+ if (!addr || (len < sizeof(int))) {
+ dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
+ node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
- dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
+ info.addr = be32_to_cpup(addr);
+ if (info.addr > (1 << 10) - 1) {
+ dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
+ info.addr, node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
- dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
- node->full_name);
- continue;
- }
+ info.of_node = of_node_get(node);
+ info.archdata = &dev_ad;
- addr = of_get_property(node, "reg", &len);
- if (!addr || (len < sizeof(int))) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
- node->full_name);
- continue;
- }
+ if (of_get_property(node, "wakeup-source", NULL))
+ info.flags |= I2C_CLIENT_WAKE;
- info.addr = be32_to_cpup(addr);
- if (info.addr > (1 << 10) - 1) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
- continue;
- }
+ request_module("%s%s", I2C_MODULE_PREFIX, info.type);
+
+ result = i2c_new_device(adap, &info);
+ if (result == NULL) {
+ dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
+ node->full_name);
+ of_node_put(node);
+ return ERR_PTR(-EINVAL);
+ }
+ return result;
+}
- info.irq = irq_of_parse_and_map(node, 0);
- info.of_node = of_node_get(node);
- info.archdata = &dev_ad;
+static void of_i2c_register_devices(struct i2c_adapter *adap)
+{
+ struct device_node *node;
- if (of_get_property(node, "wakeup-source", NULL))
- info.flags |= I2C_CLIENT_WAKE;
+ /* Only register child devices if the adapter has a node pointer set */
+ if (!adap->dev.of_node)
+ return;
- request_module("%s%s", I2C_MODULE_PREFIX, info.type);
+ dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
- result = i2c_new_device(adap, &info);
- if (result == NULL) {
- dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
- node->full_name);
- of_node_put(node);
- irq_dispose_mapping(info.irq);
- continue;
- }
- }
+ for_each_available_child_of_node(adap->dev.of_node, node)
+ of_i2c_register_device(adap, node);
}
static int of_dev_node_match(struct device *dev, void *data)
@@ -1946,6 +1962,52 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
}
EXPORT_SYMBOL(i2c_clients_command);
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
+ void *arg)
+{
+ struct of_reconfig_data *rd = arg;
+ struct i2c_adapter *adap;
+ struct i2c_client *client;
+
+ switch (of_reconfig_get_state_change(action, rd)) {
+ case OF_RECONFIG_CHANGE_ADD:
+ adap = of_find_i2c_adapter_by_node(rd->dn->parent);
+ if (adap == NULL)
+ return NOTIFY_OK; /* not for us */
+
+ client = of_i2c_register_device(adap, rd->dn);
+ put_device(&adap->dev);
+
+ if (IS_ERR(client)) {
+ pr_err("%s: failed to create for '%s'\n",
+ __func__, rd->dn->full_name);
+ return notifier_from_errno(PTR_ERR(client));
+ }
+ break;
+ case OF_RECONFIG_CHANGE_REMOVE:
+ /* find our device by node */
+ client = of_find_i2c_device_by_node(rd->dn);
+ if (client == NULL)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ i2c_unregister_device(client);
+
+ /* and put the reference of the find */
+ put_device(&client->dev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+static struct notifier_block i2c_of_notifier = {
+ .notifier_call = of_i2c_notify,
+};
+#else
+extern struct notifier_block i2c_of_notifier;
+#endif /* CONFIG_OF_DYNAMIC */
+
static int __init i2c_init(void)
{
int retval;
@@ -1963,6 +2025,10 @@ static int __init i2c_init(void)
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
+
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
+
return 0;
class_err:
@@ -1976,6 +2042,8 @@ bus_err:
static void __exit i2c_exit(void)
{
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
@@ -2904,6 +2972,54 @@ trace:
}
EXPORT_SYMBOL(i2c_smbus_xfer);
+int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
+{
+ int ret;
+
+ if (!client || !slave_cb)
+ return -EINVAL;
+
+ if (!(client->flags & I2C_CLIENT_TEN)) {
+ /* Enforce stricter address checking */
+ ret = i2c_check_addr_validity(client->addr);
+ if (ret)
+ return ret;
+ }
+
+ if (!client->adapter->algo->reg_slave)
+ return -EOPNOTSUPP;
+
+ client->slave_cb = slave_cb;
+
+ i2c_lock_adapter(client->adapter);
+ ret = client->adapter->algo->reg_slave(client);
+ i2c_unlock_adapter(client->adapter);
+
+ if (ret)
+ client->slave_cb = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i2c_slave_register);
+
+int i2c_slave_unregister(struct i2c_client *client)
+{
+ int ret;
+
+ if (!client->adapter->algo->unreg_slave)
+ return -EOPNOTSUPP;
+
+ i2c_lock_adapter(client->adapter);
+ ret = client->adapter->algo->unreg_slave(client);
+ i2c_unlock_adapter(client->adapter);
+
+ if (ret == 0)
+ client->slave_cb = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i2c_slave_unregister);
+
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus main module");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h
index 18a8fd21d2c..17700bfddcf 100644
--- a/drivers/i2c/i2c-core.h
+++ b/drivers/i2c/i2c-core.h
@@ -10,11 +10,6 @@
* 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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/rwsem.h>
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 80b47e8ce03..71c7a3975b6 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -14,11 +14,6 @@
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., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA.
*/
/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 2d0847b6be6..593f7ca9adc 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -110,6 +110,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
void *, u32))
{
struct i2c_mux_priv *priv;
+ char symlink_name[20];
int ret;
priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
@@ -183,6 +184,12 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
return NULL;
}
+ WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"),
+ "can't create symlink to mux device\n");
+
+ snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
+ WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name),
+ "can't create symlink for channel %u\n", chan_id);
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
i2c_adapter_id(&priv->adap));
@@ -193,7 +200,12 @@ EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
void i2c_del_mux_adapter(struct i2c_adapter *adap)
{
struct i2c_mux_priv *priv = adap->algo_data;
+ char symlink_name[20];
+
+ snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id);
+ sysfs_remove_link(&adap->dev.parent->kobj, symlink_name);
+ sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
i2c_del_adapter(adap);
kfree(priv);
}
diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c
new file mode 100644
index 00000000000..6631400b5f0
--- /dev/null
+++ b/drivers/i2c/i2c-slave-eeprom.c
@@ -0,0 +1,170 @@
+/*
+ * I2C slave mode EEPROM simulator
+ *
+ * Copyright (C) 2014 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
+ * Copyright (C) 2014 by Renesas Electronics 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; version 2 of the License.
+ *
+ * Because most IP blocks can only detect one I2C slave address anyhow, this
+ * driver does not support simulating EEPROM types which take more than one
+ * address. It is prepared to simulate bigger EEPROMs with an internal 16 bit
+ * pointer, yet implementation is deferred until the need actually arises.
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+
+struct eeprom_data {
+ struct bin_attribute bin;
+ bool first_write;
+ spinlock_t buffer_lock;
+ u8 buffer_idx;
+ u8 buffer[];
+};
+
+static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ struct eeprom_data *eeprom = i2c_get_clientdata(client);
+
+ switch (event) {
+ case I2C_SLAVE_REQ_WRITE_END:
+ if (eeprom->first_write) {
+ eeprom->buffer_idx = *val;
+ eeprom->first_write = false;
+ } else {
+ spin_lock(&eeprom->buffer_lock);
+ eeprom->buffer[eeprom->buffer_idx++] = *val;
+ spin_unlock(&eeprom->buffer_lock);
+ }
+ break;
+
+ case I2C_SLAVE_REQ_READ_START:
+ spin_lock(&eeprom->buffer_lock);
+ *val = eeprom->buffer[eeprom->buffer_idx];
+ spin_unlock(&eeprom->buffer_lock);
+ break;
+
+ case I2C_SLAVE_REQ_READ_END:
+ eeprom->buffer_idx++;
+ break;
+
+ case I2C_SLAVE_STOP:
+ eeprom->first_write = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+ struct eeprom_data *eeprom;
+ unsigned long flags;
+
+ if (off + count >= attr->size)
+ return -EFBIG;
+
+ eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+ spin_lock_irqsave(&eeprom->buffer_lock, flags);
+ memcpy(buf, &eeprom->buffer[off], count);
+ spin_unlock_irqrestore(&eeprom->buffer_lock, flags);
+
+ return count;
+}
+
+static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+ struct eeprom_data *eeprom;
+ unsigned long flags;
+
+ if (off + count >= attr->size)
+ return -EFBIG;
+
+ eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+ spin_lock_irqsave(&eeprom->buffer_lock, flags);
+ memcpy(&eeprom->buffer[off], buf, count);
+ spin_unlock_irqrestore(&eeprom->buffer_lock, flags);
+
+ return count;
+}
+
+static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct eeprom_data *eeprom;
+ int ret;
+ unsigned size = id->driver_data;
+
+ eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL);
+ if (!eeprom)
+ return -ENOMEM;
+
+ eeprom->first_write = true;
+ spin_lock_init(&eeprom->buffer_lock);
+ i2c_set_clientdata(client, eeprom);
+
+ sysfs_bin_attr_init(&eeprom->bin);
+ eeprom->bin.attr.name = "slave-eeprom";
+ eeprom->bin.attr.mode = S_IRUSR | S_IWUSR;
+ eeprom->bin.read = i2c_slave_eeprom_bin_read;
+ eeprom->bin.write = i2c_slave_eeprom_bin_write;
+ eeprom->bin.size = size;
+
+ ret = sysfs_create_bin_file(&client->dev.kobj, &eeprom->bin);
+ if (ret)
+ return ret;
+
+ ret = i2c_slave_register(client, i2c_slave_eeprom_slave_cb);
+ if (ret) {
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
+ return ret;
+ }
+
+ return 0;
+};
+
+static int i2c_slave_eeprom_remove(struct i2c_client *client)
+{
+ struct eeprom_data *eeprom = i2c_get_clientdata(client);
+
+ i2c_slave_unregister(client);
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
+
+ return 0;
+}
+
+static const struct i2c_device_id i2c_slave_eeprom_id[] = {
+ { "slave-24c02", 2048 / 8 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id);
+
+static struct i2c_driver i2c_slave_eeprom_driver = {
+ .driver = {
+ .name = "i2c-slave-eeprom",
+ .owner = THIS_MODULE,
+ },
+ .probe = i2c_slave_eeprom_probe,
+ .remove = i2c_slave_eeprom_remove,
+ .id_table = i2c_slave_eeprom_id,
+};
+module_i2c_driver(i2c_slave_eeprom_driver);
+
+MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
+MODULE_DESCRIPTION("I2C slave mode EEPROM simulator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index fc99f0d6b4a..9ebf9cb4ad7 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -13,11 +13,6 @@
* 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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c
index d241aa295d9..af2a94e1140 100644
--- a/drivers/i2c/i2c-stub.c
+++ b/drivers/i2c/i2c-stub.c
@@ -13,10 +13,6 @@
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 DEBUG 1
diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
index 69afffa8f42..5cf1b60b69e 100644
--- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
+++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
@@ -235,7 +235,6 @@ static struct platform_driver i2c_arbitrator_driver = {
.probe = i2c_arbitrator_probe,
.remove = i2c_arbitrator_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "i2c-arb-gpio-challenge",
.of_match_table = i2c_arbitrator_of_match,
},
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index d8989c823f5..f5798eb4076 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -280,7 +280,6 @@ static struct platform_driver i2c_mux_gpio_driver = {
.probe = i2c_mux_gpio_probe,
.remove = i2c_mux_gpio_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "i2c-mux-gpio",
.of_match_table = i2c_mux_gpio_of_match,
},
diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c
index 4ff0ef3e07a..b48378c4b40 100644
--- a/drivers/i2c/muxes/i2c-mux-pinctrl.c
+++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c
@@ -264,7 +264,6 @@ MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
static struct platform_driver i2c_mux_pinctrl_driver = {
.driver = {
.name = "i2c-mux-pinctrl",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
},
.probe = i2c_mux_pinctrl_probe,