summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-08-02 06:45:39 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-08-02 06:45:39 +0200
commit14963b8c461b9d1c310554b414238a6e97d1a8c0 (patch)
treee055fde63a1db05ee4a2592c338538cf33bf37c8 /drivers
parented98f1522d2157a0266095b00ab1481da6085337 (diff)
parentf66e9dd041a91c98b15509a7aebe293491e63e8f (diff)
downloadbarebox-14963b8c461b9d1c310554b414238a6e97d1a8c0.tar.gz
barebox-14963b8c461b9d1c310554b414238a6e97d1a8c0.tar.xz
Merge branch 'for-next/i2c' into HEAD
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig20
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-pca953x.c481
-rw-r--r--drivers/i2c/Makefile2
-rw-r--r--drivers/i2c/busses/Kconfig12
-rw-r--r--drivers/i2c/busses/Makefile6
-rw-r--r--drivers/i2c/busses/i2c-at91.c437
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c647
-rw-r--r--drivers/i2c/i2c-smbus.c371
9 files changed, 1974 insertions, 3 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index f98a9c00ef..6928fcbdff 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -64,6 +64,26 @@ config GPIO_ORION
found on Marvell Orion and MVEBU SoCs (Armada 370/XP,
Dove, Kirkwood, MV78x00, Orion5x).
+config GPIO_PCA953X
+ bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
+ depends on I2C
+ help
+ Say yes here to provide access to several register-oriented
+ SMBus I/O expanders, made mostly by NXP or TI. Compatible
+ models include:
+
+ 4 bits: pca9536, pca9537
+
+ 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
+ pca9556, pca9557, pca9574, tca6408, xra1202
+
+ 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
+ tca6416
+
+ 24 bits: tca6424
+
+ 40 bits: pca9505, pca9698
+
config GPIO_PL061
bool "PrimeCell PL061 GPIO support"
depends on ARM_AMBA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 22d2ac0706..ece5efd9bf 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_GPIO_JZ4740) += gpio-jz4740.o
obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o
obj-$(CONFIG_GPIO_ORION) += gpio-orion.o
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
+obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
new file mode 100644
index 0000000000..aabbb09eb8
--- /dev/null
+++ b/drivers/gpio/gpio-pca953x.c
@@ -0,0 +1,481 @@
+/*
+ * PCA953x 4/8/16/24/40 bit I/O ports
+ *
+ * This code was ported from linux-3.15 kernel by Antony Pavlov.
+ *
+ * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+ * Copyright (C) 2007 Marvell International Ltd.
+ *
+ * Derived from drivers/i2c/chips/pca9539.c
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <i2c/i2c.h>
+
+#include <gpio.h>
+#include <platform_data/pca953x.h>
+
+#define PCA953X_INPUT 0
+#define PCA953X_OUTPUT 1
+#define PCA953X_INVERT 2
+#define PCA953X_DIRECTION 3
+
+#define REG_ADDR_AI 0x80
+
+#define PCA957X_IN 0
+#define PCA957X_INVRT 1
+#define PCA957X_BKEN 2
+#define PCA957X_PUPD 3
+#define PCA957X_CFG 4
+#define PCA957X_OUT 5
+#define PCA957X_MSK 6
+#define PCA957X_INTS 7
+
+#define PCA_GPIO_MASK 0x00FF
+#define PCA_INT 0x0100
+#define PCA953X_TYPE 0x1000
+#define PCA957X_TYPE 0x2000
+
+static struct platform_device_id pca953x_id[] = {
+ { "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
+ { "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
+ { "pca9536", 4 | PCA953X_TYPE, },
+ { "pca9537", 4 | PCA953X_TYPE | PCA_INT, },
+ { "pca9538", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca9539", 16 | PCA953X_TYPE | PCA_INT, },
+ { "pca9554", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca9555", 16 | PCA953X_TYPE | PCA_INT, },
+ { "pca9556", 8 | PCA953X_TYPE, },
+ { "pca9557", 8 | PCA953X_TYPE, },
+ { "pca9574", 8 | PCA957X_TYPE | PCA_INT, },
+ { "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
+ { "pca9698", 40 | PCA953X_TYPE, },
+
+ { "max7310", 8 | PCA953X_TYPE, },
+ { "max7312", 16 | PCA953X_TYPE | PCA_INT, },
+ { "max7313", 16 | PCA953X_TYPE | PCA_INT, },
+ { "max7315", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca6107", 8 | PCA953X_TYPE | PCA_INT, },
+ { "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
+ { "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
+ { "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
+ { "xra1202", 8 | PCA953X_TYPE },
+ { }
+};
+
+#define MAX_BANK 5
+#define BANK_SZ 8
+
+#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
+
+struct pca953x_chip {
+ unsigned gpio_start;
+ u8 reg_output[MAX_BANK];
+ u8 reg_direction[MAX_BANK];
+ struct i2c_client *client;
+ struct gpio_chip gpio_chip;
+ const char *const *names;
+ int chip_type;
+};
+
+static inline struct pca953x_chip *to_pca(struct gpio_chip *gc)
+{
+ return container_of(gc, struct pca953x_chip, gpio_chip);
+}
+
+static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
+ int off)
+{
+ int ret;
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int offset = off / BANK_SZ;
+
+ ret = i2c_smbus_read_byte_data(chip->client,
+ (reg << bank_shift) + offset);
+ *val = ret;
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed reading register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
+ int off)
+{
+ int ret = 0;
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int offset = off / BANK_SZ;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (reg << bank_shift) + offset, val);
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed writing register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
+{
+ int ret = 0;
+
+ if (chip->gpio_chip.ngpio <= 8)
+ ret = i2c_smbus_write_byte_data(chip->client, reg, *val);
+ else if (chip->gpio_chip.ngpio >= 24) {
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ ret = i2c_smbus_write_i2c_block_data(chip->client,
+ (reg << bank_shift) | REG_ADDR_AI,
+ NBANK(chip), val);
+ } else {
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ ret = i2c_smbus_write_word_data(chip->client,
+ reg << 1, (u16) *val);
+ break;
+ case PCA957X_TYPE:
+ ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
+ val[0]);
+ if (ret < 0)
+ break;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (reg << 1) + 1,
+ val[1]);
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed writing register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
+{
+ int ret;
+
+ if (chip->gpio_chip.ngpio <= 8) {
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
+ *val = ret;
+ } else if (chip->gpio_chip.ngpio >= 24) {
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+
+ ret = i2c_smbus_read_i2c_block_data(chip->client,
+ (reg << bank_shift) | REG_ADDR_AI,
+ NBANK(chip), val);
+ } else {
+ ret = i2c_smbus_read_word_data(chip->client, reg << 1);
+ val[0] = (u16)ret & 0xFF;
+ val[1] = (u16)ret >> 8;
+ }
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed reading register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+ int ret, offset = 0;
+
+ reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ));
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_DIRECTION;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_CFG;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_direction[off / BANK_SZ] = reg_val;
+ ret = 0;
+exit:
+ return ret;
+}
+
+static int pca953x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned off, int val)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+ int ret, offset = 0;
+
+ /* set output level */
+ if (val)
+ reg_val = chip->reg_output[off / BANK_SZ]
+ | (1u << (off % BANK_SZ));
+ else
+ reg_val = chip->reg_output[off / BANK_SZ]
+ & ~(1u << (off % BANK_SZ));
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_OUTPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_OUT;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_output[off / BANK_SZ] = reg_val;
+
+ /* then direction */
+ reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_DIRECTION;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_CFG;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_direction[off / BANK_SZ] = reg_val;
+ ret = 0;
+exit:
+ return ret;
+}
+
+static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+
+ reg_val = chip->reg_direction[off / BANK_SZ] & (1u << (off % BANK_SZ));
+
+ if (reg_val)
+ return GPIOF_DIR_IN;
+
+ return GPIOF_DIR_OUT;
+}
+
+static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u32 reg_val;
+ int ret, offset = 0;
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_INPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_IN;
+ break;
+ }
+ ret = pca953x_read_single(chip, offset, &reg_val, off);
+ if (ret < 0) {
+ /* NOTE: diagnostic already emitted; that's all we should
+ * do unless gpio_*_value_cansleep() calls become different
+ * from their nonsleeping siblings (and report faults).
+ */
+ return 0;
+ }
+
+ return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0;
+}
+
+static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+ int ret, offset = 0;
+
+ if (val)
+ reg_val = chip->reg_output[off / BANK_SZ]
+ | (1u << (off % BANK_SZ));
+ else
+ reg_val = chip->reg_output[off / BANK_SZ]
+ & ~(1u << (off % BANK_SZ));
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_OUTPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_OUT;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_output[off / BANK_SZ] = reg_val;
+exit:
+ return;
+}
+
+static struct gpio_ops pca953x_gpio_ops = {
+ .direction_input = pca953x_gpio_direction_input,
+ .direction_output = pca953x_gpio_direction_output,
+ .get_direction = pca953x_gpio_get_direction,
+ .get = pca953x_gpio_get_value,
+ .set = pca953x_gpio_set_value,
+};
+
+static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
+{
+ struct gpio_chip *gc;
+
+ gc = &chip->gpio_chip;
+
+ gc->ops = &pca953x_gpio_ops;
+
+ gc->base = chip->gpio_start;
+ gc->ngpio = gpios;
+ gc->dev = &chip->client->dev;
+}
+
+static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
+{
+ int ret;
+ u8 val[MAX_BANK];
+
+ ret = pca953x_read_regs(chip, PCA953X_OUTPUT, chip->reg_output);
+ if (ret)
+ goto out;
+
+ ret = pca953x_read_regs(chip, PCA953X_DIRECTION,
+ chip->reg_direction);
+ if (ret)
+ goto out;
+
+ /* set platform specific polarity inversion */
+ if (invert)
+ memset(val, 0xFF, NBANK(chip));
+ else
+ memset(val, 0, NBANK(chip));
+
+ ret = pca953x_write_regs(chip, PCA953X_INVERT, val);
+out:
+ return ret;
+}
+
+static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
+{
+ int ret;
+ u8 val[MAX_BANK];
+
+ ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output);
+ if (ret)
+ goto out;
+ ret = pca953x_read_regs(chip, PCA957X_CFG, chip->reg_direction);
+ if (ret)
+ goto out;
+
+ /* set platform specific polarity inversion */
+ if (invert)
+ memset(val, 0xFF, NBANK(chip));
+ else
+ memset(val, 0, NBANK(chip));
+ pca953x_write_regs(chip, PCA957X_INVRT, val);
+
+ /* To enable register 6, 7 to controll pull up and pull down */
+ memset(val, 0x02, NBANK(chip));
+ pca953x_write_regs(chip, PCA957X_BKEN, val);
+
+ return 0;
+out:
+ return ret;
+}
+
+static int pca953x_probe(struct device_d *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned long driver_data;
+ struct pca953x_platform_data *pdata;
+ struct pca953x_chip *chip;
+ int ret;
+ u32 invert = 0;
+
+ chip = xzalloc(sizeof(struct pca953x_chip));
+
+ driver_data = 0;
+ pdata = dev->platform_data;
+ if (pdata) {
+ chip->gpio_start = pdata->gpio_base;
+ invert = pdata->invert;
+ chip->names = pdata->names;
+ } else {
+ int err;
+
+ err = dev_get_drvdata(dev, &driver_data);
+ if (err)
+ return err;
+
+ chip->gpio_start = -1;
+ }
+
+ chip->client = client;
+
+ chip->chip_type = driver_data & (PCA953X_TYPE | PCA957X_TYPE);
+
+ /* initialize cached registers from their original values.
+ * we can't share this chip with another i2c master.
+ */
+ pca953x_setup_gpio(chip, driver_data & PCA_GPIO_MASK);
+
+ if (chip->chip_type == PCA953X_TYPE)
+ ret = device_pca953x_init(chip, invert);
+ else
+ ret = device_pca957x_init(chip, invert);
+ if (ret)
+ return ret;
+
+ ret = gpiochip_add(&chip->gpio_chip);
+ if (ret)
+ return ret;
+
+ if (pdata && pdata->setup) {
+ ret = pdata->setup(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0)
+ dev_warn(&client->dev, "setup failed, %d\n", ret);
+ }
+
+ return 0;
+}
+
+static struct driver_d pca953x_driver = {
+ .name = "pca953x",
+ .probe = pca953x_probe,
+ .id_table = pca953x_id,
+};
+
+static int __init pca953x_init(void)
+{
+ return i2c_driver_register(&pca953x_driver);
+}
+device_initcall(pca953x_init);
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 5ce0324714..648d844252 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_I2C) += i2c.o busses/ algos/
+obj-$(CONFIG_I2C) += i2c.o i2c-smbus.o busses/ algos/
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 370abb0c2e..39622863fb 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -12,10 +12,22 @@ config I2C_GPIO
This is a very simple bitbanging I2C driver utilizing the
arch-neutral GPIO API to control the SCL and SDA lines.
+config I2C_AT91
+ bool "AT91 I2C Master driver"
+ depends on ARCH_AT91
+
config I2C_IMX
bool "MPC85xx/i.MX I2C Master driver"
depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX
+config I2C_MV64XXX
+ bool "Marvell mv64xxx I2C Controller"
+ depends on HAVE_CLK && OFDEVICE
+ help
+ If you say yes to this option, support will be included for the
+ built-in I2C interface on the Marvell 64xxx line of host bridges.
+ This driver is also used for Allwinner SoCs I2C controllers.
+
config I2C_OMAP
bool "OMAP I2C Master driver"
depends on ARCH_OMAP
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 9823d1bd36..1dbfbdf93b 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,5 +1,7 @@
+obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
-obj-$(CONFIG_I2C_IMX) += i2c-imx.o
-obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
+obj-$(CONFIG_I2C_IMX) += i2c-imx.o
+obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
+obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
new file mode 100644
index 0000000000..399f6a94be
--- /dev/null
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -0,0 +1,437 @@
+/*
+ * i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ * Copyright (C) 2011 Weinmann Medical GmbH
+ * Author: Nikolaus Voss <n.voss@weinmann.de>
+ *
+ * Evolved from original work by:
+ * Copyright (C) 2004 Rick Bronson
+ * Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
+ *
+ * Borrowed heavily from original work by:
+ * Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <i2c/i2c.h>
+#include <malloc.h>
+#include <of.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <driver.h>
+#include <init.h>
+
+#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
+#define AT91_I2C_TIMEOUT (100 * MSECOND) /* transfer timeout */
+#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
+
+/* AT91 TWI register definitions */
+#define AT91_TWI_CR 0x0000 /* Control Register */
+#define AT91_TWI_START 0x0001 /* Send a Start Condition */
+#define AT91_TWI_STOP 0x0002 /* Send a Stop Condition */
+#define AT91_TWI_MSEN 0x0004 /* Master Transfer Enable */
+#define AT91_TWI_SVDIS 0x0020 /* Slave Transfer Disable */
+#define AT91_TWI_QUICK 0x0040 /* SMBus quick command */
+#define AT91_TWI_SWRST 0x0080 /* Software Reset */
+
+#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
+#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
+#define AT91_TWI_MREAD 0x1000 /* Master Read Direction */
+
+#define AT91_TWI_IADR 0x000c /* Internal Address Register */
+
+#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
+
+#define AT91_TWI_SR 0x0020 /* Status Register */
+#define AT91_TWI_TXCOMP 0x0001 /* Transmission Complete */
+#define AT91_TWI_RXRDY 0x0002 /* Receive Holding Register Ready */
+#define AT91_TWI_TXRDY 0x0004 /* Transmit Holding Register Ready */
+
+#define AT91_TWI_OVRE 0x0040 /* Overrun Error */
+#define AT91_TWI_UNRE 0x0080 /* Underrun Error */
+#define AT91_TWI_NACK 0x0100 /* Not Acknowledged */
+
+#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
+#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
+#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
+#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
+#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
+
+struct at91_twi_pdata {
+ unsigned clk_max_div;
+ unsigned clk_offset;
+ bool has_unre_flag;
+};
+
+struct at91_twi_dev {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ u8 *buf;
+ size_t buf_len;
+ struct i2c_msg *msg;
+ unsigned imr;
+ unsigned transfer_status;
+ struct i2c_adapter adapter;
+ unsigned twi_cwgr_reg;
+ struct at91_twi_pdata *pdata;
+};
+
+#define to_at91_twi_dev(a) container_of(a, struct at91_twi_dev, adapter)
+
+static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
+{
+ return __raw_readl(dev->base + reg);
+}
+
+static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
+{
+ __raw_writel(val, dev->base + reg);
+}
+
+static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
+{
+ at91_twi_write(dev, AT91_TWI_IDR,
+ AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY);
+}
+
+static void at91_init_twi_bus(struct at91_twi_dev *dev)
+{
+ at91_disable_twi_interrupts(dev);
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN);
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS);
+ at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg);
+}
+
+/*
+ * Calculate symmetric clock as stated in datasheet:
+ * twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
+ */
+static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
+{
+ int ckdiv, cdiv, div;
+ struct at91_twi_pdata *pdata = dev->pdata;
+ int offset = pdata->clk_offset;
+ int max_ckdiv = pdata->clk_max_div;
+
+ div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
+ 2 * twi_clk) - offset);
+ ckdiv = fls(div >> 8);
+ cdiv = div >> ckdiv;
+
+ if (ckdiv > max_ckdiv) {
+ dev_warn(&dev->adapter.dev, "%d exceeds ckdiv max value which is %d.\n",
+ ckdiv, max_ckdiv);
+ ckdiv = max_ckdiv;
+ cdiv = 255;
+ }
+
+ dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv;
+ dev_dbg(&dev->adapter.dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv);
+}
+
+static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
+{
+ if (dev->buf_len <= 0)
+ return;
+
+ at91_twi_write(dev, AT91_TWI_THR, *dev->buf);
+
+ /* send stop when last byte has been written */
+ if (--dev->buf_len == 0)
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
+
+ dev_dbg(&dev->adapter.dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len);
+
+ ++dev->buf;
+}
+
+static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
+{
+ if (dev->buf_len <= 0)
+ return;
+
+ *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff;
+ --dev->buf_len;
+
+ /* send stop if second but last byte has been read */
+ if (dev->buf_len == 1)
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
+
+ dev_dbg(&dev->adapter.dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
+
+ ++dev->buf;
+}
+
+static int at91_twi_wait_completion(struct at91_twi_dev *dev)
+{
+ uint64_t start = get_time_ns();
+ unsigned int status = at91_twi_read(dev, AT91_TWI_SR);
+ unsigned int irqstatus = at91_twi_read(dev, AT91_TWI_IMR);
+
+ if (irqstatus & AT91_TWI_RXRDY)
+ at91_twi_read_next_byte(dev);
+ else if (irqstatus & AT91_TWI_TXRDY)
+ at91_twi_write_next_byte(dev);
+ else
+ dev_warn(&dev->adapter.dev, "neither rx and tx are ready\n");
+
+ dev->transfer_status |= status;
+
+ while(!(at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_TXCOMP)) {
+ if(is_timeout(start, AT91_I2C_TIMEOUT)) {
+ dev_warn(&dev->adapter.dev, "timeout waiting for bus ready\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ at91_disable_twi_interrupts(dev);
+
+ return 0;
+}
+
+static int at91_do_twi_transfer(struct at91_twi_dev *dev)
+{
+ int ret;
+ bool has_unre_flag = dev->pdata->has_unre_flag;
+
+ dev_dbg(&dev->adapter.dev, "transfer: %s %d bytes.\n",
+ (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
+
+ dev->transfer_status = 0;
+
+ if (!dev->buf_len) {
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK);
+ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP);
+ } else if (dev->msg->flags & I2C_M_RD) {
+ unsigned start_flags = AT91_TWI_START;
+
+ if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) {
+ dev_err(&dev->adapter.dev, "RXRDY still set!");
+ at91_twi_read(dev, AT91_TWI_RHR);
+ }
+
+ /* if only one byte is to be read, immediately stop transfer */
+ if (dev->buf_len <= 1)
+ start_flags |= AT91_TWI_STOP;
+
+ at91_twi_write(dev, AT91_TWI_CR, start_flags);
+
+ at91_twi_write(dev, AT91_TWI_IER,
+ AT91_TWI_TXCOMP | AT91_TWI_RXRDY);
+ } else {
+ at91_twi_write_next_byte(dev);
+ at91_twi_write(dev, AT91_TWI_IER,
+ AT91_TWI_TXCOMP | AT91_TWI_TXRDY);
+ }
+
+ ret = at91_twi_wait_completion(dev);
+ if (ret < 0) {
+ dev_err(&dev->adapter.dev, "controller timed out\n");
+ at91_init_twi_bus(dev);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+ if (dev->transfer_status & AT91_TWI_NACK) {
+ dev_dbg(&dev->adapter.dev, "received nack\n");
+ ret = -EREMOTEIO;
+ goto error;
+ }
+ if (dev->transfer_status & AT91_TWI_OVRE) {
+ dev_err(&dev->adapter.dev, "overrun while reading\n");
+ ret = -EIO;
+ goto error;
+ }
+ if (has_unre_flag && dev->transfer_status & AT91_TWI_UNRE) {
+ dev_err(&dev->adapter.dev, "underrun while writing\n");
+ ret = -EIO;
+ goto error;
+ }
+ dev_dbg(&dev->adapter.dev, "transfer complete\n");
+
+ return 0;
+
+error:
+ return ret;
+}
+
+static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
+{
+ struct at91_twi_dev *dev = to_at91_twi_dev(adap);
+ int ret;
+ unsigned int_addr_flag = 0;
+ struct i2c_msg *m_start = msg;
+
+ dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
+
+ /*
+ * The hardware can handle at most two messages concatenated by a
+ * repeated start via it's internal address feature.
+ */
+ if (num > 2) {
+ dev_err(&dev->adapter.dev,
+ "cannot handle more than two concatenated messages.\n");
+ return 0;
+ } else if (num == 2) {
+ int internal_address = 0;
+ int i;
+
+ if (msg->flags & I2C_M_RD) {
+ dev_err(&dev->adapter.dev, "first transfer must be write.\n");
+ return -EINVAL;
+ }
+ if (msg->len > 3) {
+ dev_err(&dev->adapter.dev, "first message size must be <= 3.\n");
+ return -EINVAL;
+ }
+
+ /* 1st msg is put into the internal address, start with 2nd */
+ m_start = &msg[1];
+ for (i = 0; i < msg->len; ++i) {
+ const unsigned addr = msg->buf[msg->len - 1 - i];
+
+ internal_address |= addr << (8 * i);
+ int_addr_flag += AT91_TWI_IADRSZ_1;
+ }
+ at91_twi_write(dev, AT91_TWI_IADR, internal_address);
+ }
+
+ at91_twi_write(dev, AT91_TWI_MMR, (m_start->addr << 16) | int_addr_flag
+ | ((m_start->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0));
+
+ dev->buf_len = m_start->len;
+ dev->buf = m_start->buf;
+ dev->msg = m_start;
+
+ ret = at91_do_twi_transfer(dev);
+
+ return (ret < 0) ? ret : num;
+}
+
+static struct at91_twi_pdata at91rm9200_config = {
+ .clk_max_div = 5,
+ .clk_offset = 3,
+ .has_unre_flag = true,
+};
+
+static struct at91_twi_pdata at91sam9261_config = {
+ .clk_max_div = 5,
+ .clk_offset = 4,
+ .has_unre_flag = false,
+};
+
+static struct at91_twi_pdata at91sam9260_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_unre_flag = false,
+};
+
+static struct at91_twi_pdata at91sam9g20_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_unre_flag = false,
+};
+
+static struct at91_twi_pdata at91sam9g10_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_unre_flag = false,
+};
+
+static struct platform_device_id at91_twi_devtypes[] = {
+ {
+ .name = "i2c-at91rm9200",
+ .driver_data = (unsigned long) &at91rm9200_config,
+ }, {
+ .name = "i2c-at91sam9261",
+ .driver_data = (unsigned long) &at91sam9261_config,
+ }, {
+ .name = "i2c-at91sam9260",
+ .driver_data = (unsigned long) &at91sam9260_config,
+ }, {
+ .name = "i2c-at91sam9g20",
+ .driver_data = (unsigned long) &at91sam9g20_config,
+ }, {
+ .name = "i2c-at91sam9g10",
+ .driver_data = (unsigned long) &at91sam9g10_config,
+ }, {
+ /* sentinel */
+ }
+};
+
+static int at91_twi_probe(struct device_d *dev)
+{
+ struct at91_twi_dev *i2c_at91;
+ struct at91_twi_pdata *i2c_data;
+ int rc;
+ u32 bus_clk_rate;
+
+ i2c_at91 = xzalloc(sizeof(struct at91_twi_dev));
+
+ rc = dev_get_drvdata(dev, (unsigned long *)&i2c_data);
+ if (rc)
+ goto out_free;
+
+ i2c_at91->pdata = i2c_data;
+
+ i2c_at91->base = dev_request_mem_region(dev, 0);
+ if (!i2c_at91->base) {
+ dev_err(dev, "could not get memory region\n");
+ rc = -ENODEV;
+ goto out_free;
+ }
+
+ i2c_at91->clk = clk_get(dev, "twi_clk");
+ if (IS_ERR(i2c_at91->clk)) {
+ dev_err(dev, "no clock defined\n");
+ rc = -ENODEV;
+ goto out_free;
+ }
+
+ clk_enable(i2c_at91->clk);
+
+ bus_clk_rate = DEFAULT_TWI_CLK_HZ;
+
+ at91_calc_twi_clock(i2c_at91, bus_clk_rate);
+ at91_init_twi_bus(i2c_at91);
+
+ i2c_at91->adapter.master_xfer = at91_twi_xfer;
+ i2c_at91->adapter.dev.parent = dev;
+ i2c_at91->adapter.nr = dev->id;
+ i2c_at91->adapter.dev.device_node = dev->device_node;
+
+ rc = i2c_add_numbered_adapter(&i2c_at91->adapter);
+ if (rc) {
+ dev_err(dev, "Failed to add I2C adapter\n");
+ goto out_adap_fail;
+ }
+
+ dev_info(dev, "AT91 i2c bus driver.\n");
+ return 0;
+
+out_adap_fail:
+ clk_disable(i2c_at91->clk);
+ clk_put(i2c_at91->clk);
+out_free:
+ kfree(i2c_at91);
+ return rc;
+}
+
+static struct driver_d at91_twi_driver = {
+ .name = "at91-twi",
+ .probe = at91_twi_probe,
+ .id_table = at91_twi_devtypes,
+};
+device_platform_driver(at91_twi_driver);
+
+MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
+MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
new file mode 100644
index 0000000000..6d8c85b9d7
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -0,0 +1,647 @@
+/*
+ * Driver for the i2c controller on the Marvell line of host bridges
+ * (e.g, gt642[46]0, mv643[46]0, mv644[46]0, and Orion SoC family).
+ *
+ * This code was ported from linux-3.15 kernel by Antony Pavlov.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <of.h>
+#include <malloc.h>
+#include <types.h>
+#include <xfuncs.h>
+#include <clock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <io.h>
+#include <i2c/i2c.h>
+
+#define ADDR_ADDR(val) ((val & 0x7f) << 1)
+#define BAUD_DIV_N(val) (val & 0x7)
+#define BAUD_DIV_M(val) ((val & 0xf) << 3)
+
+#define REG_CONTROL_ACK 0x00000004
+#define REG_CONTROL_IFLG 0x00000008
+#define REG_CONTROL_STOP 0x00000010
+#define REG_CONTROL_START 0x00000020
+#define REG_CONTROL_TWSIEN 0x00000040
+#define REG_CONTROL_INTEN 0x00000080
+
+/* Ctlr status values */
+#define STATUS_MAST_START 0x08
+#define STATUS_MAST_REPEAT_START 0x10
+#define STATUS_MAST_WR_ADDR_ACK 0x18
+#define STATUS_MAST_WR_ADDR_NO_ACK 0x20
+#define STATUS_MAST_WR_ACK 0x28
+#define STATUS_MAST_WR_NO_ACK 0x30
+#define STATUS_MAST_RD_ADDR_ACK 0x40
+#define STATUS_MAST_RD_ADDR_NO_ACK 0x48
+#define STATUS_MAST_RD_DATA_ACK 0x50
+#define STATUS_MAST_RD_DATA_NO_ACK 0x58
+#define STATUS_MAST_WR_ADDR_2_ACK 0xd0
+#define STATUS_MAST_RD_ADDR_2_ACK 0xe0
+
+/* Driver states */
+enum mv64xxx_state {
+ STATE_INVALID,
+ STATE_IDLE,
+ STATE_WAITING_FOR_START_COND,
+ STATE_WAITING_FOR_RESTART,
+ STATE_WAITING_FOR_ADDR_1_ACK,
+ STATE_WAITING_FOR_ADDR_2_ACK,
+ STATE_WAITING_FOR_SLAVE_ACK,
+ STATE_WAITING_FOR_SLAVE_DATA,
+};
+
+/* Driver actions */
+enum mv64xxx_action {
+ ACTION_INVALID,
+ ACTION_CONTINUE,
+ ACTION_SEND_RESTART,
+ ACTION_OFFLOAD_RESTART,
+ ACTION_SEND_ADDR_1,
+ ACTION_SEND_ADDR_2,
+ ACTION_SEND_DATA,
+ ACTION_RCV_DATA,
+ ACTION_RCV_DATA_STOP,
+ ACTION_SEND_STOP,
+ ACTION_OFFLOAD_SEND_STOP,
+};
+
+struct mv64xxx_i2c_regs {
+ u8 addr;
+ u8 ext_addr;
+ u8 data;
+ u8 control;
+ u8 status;
+ u8 clock;
+ u8 soft_reset;
+};
+
+struct mv64xxx_i2c_data {
+ struct i2c_msg *msgs;
+ int num_msgs;
+ enum mv64xxx_state state;
+ enum mv64xxx_action action;
+ u8 cntl_bits;
+ void __iomem *reg_base;
+ struct mv64xxx_i2c_regs reg_offsets;
+ u8 addr1;
+ u8 addr2;
+ u8 bytes_left;
+ u8 byte_posn;
+ u8 send_stop;
+ bool block;
+ int rc;
+ u32 freq_m;
+ u32 freq_n;
+ struct clk *clk;
+ struct i2c_msg *msg;
+ struct i2c_adapter adapter;
+/* 5us delay in order to avoid repeated start timing violation */
+ bool errata_delay;
+ void (*write_reg)(struct mv64xxx_i2c_data *drv_data, u32 v, unsigned reg);
+ u32 (*read_reg)(struct mv64xxx_i2c_data *drv_data, unsigned reg);
+};
+
+static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = {
+ .addr = 0x00,
+ .ext_addr = 0x10,
+ .data = 0x04,
+ .control = 0x08,
+ .status = 0x0c,
+ .clock = 0x0c,
+ .soft_reset = 0x1c,
+};
+
+static void mv64xxx_writeb(struct mv64xxx_i2c_data *drv_data,
+ u32 v, unsigned reg)
+{
+ writeb(v, drv_data->reg_base + reg);
+}
+
+static void mv64xxx_writel(struct mv64xxx_i2c_data *drv_data,
+ u32 v, unsigned reg)
+{
+ writel(v, drv_data->reg_base + reg);
+}
+
+static inline void mv64xxx_write(struct mv64xxx_i2c_data *drv_data,
+ u32 v, unsigned reg)
+{
+ drv_data->write_reg(drv_data, v, reg);
+}
+
+static u32 mv64xxx_readb(struct mv64xxx_i2c_data *drv_data, unsigned reg)
+{
+ return readb(drv_data->reg_base + reg);
+}
+
+static u32 mv64xxx_readl(struct mv64xxx_i2c_data *drv_data, unsigned reg)
+{
+ return readl(drv_data->reg_base + reg);
+}
+
+static inline u32 mv64xxx_read(struct mv64xxx_i2c_data *drv_data, unsigned reg)
+{
+ return drv_data->read_reg(drv_data, reg);
+}
+
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+ struct i2c_msg *msg)
+{
+ u32 dir = 0;
+
+ drv_data->cntl_bits = REG_CONTROL_ACK |
+ REG_CONTROL_INTEN | REG_CONTROL_TWSIEN;
+
+ if (msg->flags & I2C_M_RD)
+ dir = 1;
+
+ if (msg->flags & I2C_M_TEN) {
+ drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+ drv_data->addr2 = (u32)msg->addr & 0xff;
+ } else {
+ drv_data->addr1 = ADDR_ADDR((u32)msg->addr) | dir;
+ drv_data->addr2 = 0;
+ }
+}
+
+/*
+ *****************************************************************************
+ *
+ * Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+
+/* Reset hardware and initialize FSM */
+static void
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+ mv64xxx_write(drv_data, 0, drv_data->reg_offsets.soft_reset);
+ mv64xxx_write(drv_data, BAUD_DIV_M(drv_data->freq_m)
+ | BAUD_DIV_N(drv_data->freq_n),
+ drv_data->reg_offsets.clock);
+ mv64xxx_write(drv_data, 0, drv_data->reg_offsets.addr);
+ mv64xxx_write(drv_data, 0, drv_data->reg_offsets.ext_addr);
+ mv64xxx_write(drv_data, REG_CONTROL_TWSIEN
+ | REG_CONTROL_STOP,
+ drv_data->reg_offsets.control);
+ drv_data->state = STATE_IDLE;
+}
+
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+ /*
+ * If state is idle, then this is likely the remnants of an old
+ * operation that driver has given up on or the user has killed.
+ * If so, issue the stop condition and go to idle.
+ */
+ if (drv_data->state == STATE_IDLE) {
+ drv_data->action = ACTION_SEND_STOP;
+ return;
+ }
+
+ /* The status from the ctlr [mostly] tells us what to do next */
+ switch (status) {
+ /* Start condition interrupt */
+ case STATUS_MAST_START: /* 0x08 */
+ case STATUS_MAST_REPEAT_START: /* 0x10 */
+ drv_data->action = ACTION_SEND_ADDR_1;
+ drv_data->state = STATE_WAITING_FOR_ADDR_1_ACK;
+ break;
+
+ /* Performing a write */
+ case STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+ if (drv_data->msg->flags & I2C_M_TEN) {
+ drv_data->action = ACTION_SEND_ADDR_2;
+ drv_data->state =
+ STATE_WAITING_FOR_ADDR_2_ACK;
+ break;
+ }
+ /* FALLTHRU */
+ case STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+ case STATUS_MAST_WR_ACK: /* 0x28 */
+ if (drv_data->bytes_left == 0) {
+ if (drv_data->send_stop) {
+ drv_data->action = ACTION_SEND_STOP;
+ drv_data->state = STATE_IDLE;
+ } else {
+ drv_data->action =
+ ACTION_SEND_RESTART;
+ drv_data->state =
+ STATE_WAITING_FOR_RESTART;
+ }
+ } else {
+ drv_data->action = ACTION_SEND_DATA;
+ drv_data->state =
+ STATE_WAITING_FOR_SLAVE_ACK;
+ drv_data->bytes_left--;
+ }
+ break;
+
+ /* Performing a read */
+ case STATUS_MAST_RD_ADDR_ACK: /* 40 */
+ if (drv_data->msg->flags & I2C_M_TEN) {
+ drv_data->action = ACTION_SEND_ADDR_2;
+ drv_data->state =
+ STATE_WAITING_FOR_ADDR_2_ACK;
+ break;
+ }
+ /* FALLTHRU */
+ case STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+ if (drv_data->bytes_left == 0) {
+ drv_data->action = ACTION_SEND_STOP;
+ drv_data->state = STATE_IDLE;
+ break;
+ }
+ /* FALLTHRU */
+ case STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+ if (status != STATUS_MAST_RD_DATA_ACK)
+ drv_data->action = ACTION_CONTINUE;
+ else {
+ drv_data->action = ACTION_RCV_DATA;
+ drv_data->bytes_left--;
+ }
+ drv_data->state = STATE_WAITING_FOR_SLAVE_DATA;
+
+ if (drv_data->bytes_left == 1)
+ drv_data->cntl_bits &= ~REG_CONTROL_ACK;
+ break;
+
+ case STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+ drv_data->action = ACTION_RCV_DATA_STOP;
+ drv_data->state = STATE_IDLE;
+ break;
+
+ case STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+ case STATUS_MAST_WR_NO_ACK: /* 30 */
+ case STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+ /* Doesn't seem to be a device at other end */
+ drv_data->action = ACTION_SEND_STOP;
+ drv_data->state = STATE_IDLE;
+ drv_data->rc = -ENXIO;
+ break;
+
+ default:
+ dev_err(&drv_data->adapter.dev,
+ "mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+ "status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+ drv_data->state, status, drv_data->msg->addr,
+ drv_data->msg->flags);
+ drv_data->action = ACTION_SEND_STOP;
+ mv64xxx_i2c_hw_init(drv_data);
+ drv_data->rc = -EIO;
+ }
+}
+
+static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data)
+{
+ drv_data->msg = drv_data->msgs;
+ drv_data->byte_posn = 0;
+ drv_data->bytes_left = drv_data->msg->len;
+ drv_data->rc = 0;
+
+ mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
+ mv64xxx_write(drv_data, drv_data->cntl_bits | REG_CONTROL_START,
+ drv_data->reg_offsets.control);
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+ switch (drv_data->action) {
+ case ACTION_SEND_RESTART:
+ /* We should only get here if we have further messages */
+ BUG_ON(drv_data->num_msgs == 0);
+
+ drv_data->msgs++;
+ drv_data->num_msgs--;
+ mv64xxx_i2c_send_start(drv_data);
+
+ if (drv_data->errata_delay)
+ udelay(5);
+
+ /*
+ * We're never at the start of the message here, and by this
+ * time it's already too late to do any protocol mangling.
+ * Thankfully, do not advertise support for that feature.
+ */
+ drv_data->send_stop = drv_data->num_msgs == 1;
+ break;
+
+ case ACTION_CONTINUE:
+ mv64xxx_write(drv_data, drv_data->cntl_bits,
+ drv_data->reg_offsets.control);
+ break;
+
+ case ACTION_SEND_ADDR_1:
+ mv64xxx_write(drv_data, drv_data->addr1,
+ drv_data->reg_offsets.data);
+ mv64xxx_write(drv_data, drv_data->cntl_bits,
+ drv_data->reg_offsets.control);
+ break;
+
+ case ACTION_SEND_ADDR_2:
+ mv64xxx_write(drv_data, drv_data->addr2,
+ drv_data->reg_offsets.data);
+ mv64xxx_write(drv_data, drv_data->cntl_bits,
+ drv_data->reg_offsets.control);
+ break;
+
+ case ACTION_SEND_DATA:
+ mv64xxx_write(drv_data, drv_data->msg->buf[drv_data->byte_posn++],
+ drv_data->reg_offsets.data);
+ mv64xxx_write(drv_data, drv_data->cntl_bits,
+ drv_data->reg_offsets.control);
+ break;
+
+ case ACTION_RCV_DATA:
+ drv_data->msg->buf[drv_data->byte_posn++] =
+ mv64xxx_read(drv_data, drv_data->reg_offsets.data);
+ mv64xxx_write(drv_data, drv_data->cntl_bits,
+ drv_data->reg_offsets.control);
+ break;
+
+ case ACTION_RCV_DATA_STOP:
+ drv_data->msg->buf[drv_data->byte_posn++] =
+ mv64xxx_read(drv_data, drv_data->reg_offsets.data);
+ drv_data->cntl_bits &= ~REG_CONTROL_INTEN;
+ mv64xxx_write(drv_data, drv_data->cntl_bits | REG_CONTROL_STOP,
+ drv_data->reg_offsets.control);
+ drv_data->block = false;
+ if (drv_data->errata_delay)
+ udelay(5);
+
+ break;
+
+ case ACTION_INVALID:
+ default:
+ dev_err(&drv_data->adapter.dev,
+ "mv64xxx_i2c_do_action: Invalid action: %d\n",
+ drv_data->action);
+ drv_data->rc = -EIO;
+
+ /* FALLTHRU */
+ case ACTION_SEND_STOP:
+ drv_data->cntl_bits &= ~REG_CONTROL_INTEN;
+ mv64xxx_write(drv_data, drv_data->cntl_bits
+ | REG_CONTROL_STOP,
+ drv_data->reg_offsets.control);
+ drv_data->block = false;
+ break;
+ }
+}
+
+static void mv64xxx_i2c_intr(struct mv64xxx_i2c_data *drv_data)
+{
+ u32 status;
+ uint64_t start;
+
+ start = get_time_ns();
+
+ while (mv64xxx_read(drv_data, drv_data->reg_offsets.control) &
+ REG_CONTROL_IFLG) {
+ status = mv64xxx_read(drv_data, drv_data->reg_offsets.status);
+ mv64xxx_i2c_fsm(drv_data, status);
+ mv64xxx_i2c_do_action(drv_data);
+
+ if (is_timeout_non_interruptible(start, 3 * SECOND)) {
+ drv_data->rc = -EIO;
+ break;
+ }
+ }
+}
+
+/*
+ *****************************************************************************
+ *
+ * I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+ do {
+ mv64xxx_i2c_intr(drv_data);
+ if (drv_data->rc) {
+ drv_data->state = STATE_IDLE;
+ dev_err(&drv_data->adapter.dev, "I2C bus error\n");
+ mv64xxx_i2c_hw_init(drv_data);
+ drv_data->block = false;
+ }
+ } while (drv_data->block);
+}
+
+/*
+ *****************************************************************************
+ *
+ * I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct mv64xxx_i2c_data *drv_data = container_of(adap, struct mv64xxx_i2c_data, adapter);
+ int ret = num;
+
+ BUG_ON(drv_data->msgs != NULL);
+
+ drv_data->msgs = msgs;
+ drv_data->num_msgs = num;
+ drv_data->state = STATE_WAITING_FOR_START_COND;
+ drv_data->send_stop = (num == 1);
+ drv_data->block = true;
+ mv64xxx_i2c_send_start(drv_data);
+ mv64xxx_i2c_wait_for_completion(drv_data);
+
+ if (drv_data->rc < 0)
+ ret = drv_data->rc;
+
+ drv_data->num_msgs = 0;
+ drv_data->msgs = NULL;
+
+ return ret;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static struct of_device_id mv64xxx_i2c_of_match_table[] = {
+ { .compatible = "marvell,mv64xxx-i2c", .data = (unsigned long)&mv64xxx_i2c_regs_mv64xxx},
+ { .compatible = "marvell,mv78230-i2c", .data = (unsigned long)&mv64xxx_i2c_regs_mv64xxx},
+ { .compatible = "marvell,mv78230-a0-i2c", .data = (unsigned long)&mv64xxx_i2c_regs_mv64xxx},
+ {}
+};
+
+static inline int
+mv64xxx_calc_freq(const int tclk, const int n, const int m)
+{
+ return tclk / (10 * (m + 1) * (2 << n));
+}
+
+static bool
+mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
+ u32 *best_m)
+{
+ int freq, delta, best_delta = INT_MAX;
+ int m, n;
+
+ for (n = 0; n <= 7; n++)
+ for (m = 0; m <= 15; m++) {
+ freq = mv64xxx_calc_freq(tclk, n, m);
+ delta = req_freq - freq;
+ if (delta >= 0 && delta < best_delta) {
+ *best_m = m;
+ *best_n = n;
+ best_delta = delta;
+ }
+ if (best_delta == 0)
+ return true;
+ }
+ if (best_delta == INT_MAX)
+ return false;
+ return true;
+}
+
+static int
+mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
+ struct device_d *pd)
+{
+ struct device_node *np = pd->device_node;
+ u32 bus_freq, tclk;
+ int rc = 0;
+ u32 prop;
+ struct mv64xxx_i2c_regs *mv64xxx_regs;
+ int freq;
+
+ if (IS_ERR(drv_data->clk)) {
+ rc = -ENODEV;
+ goto out;
+ }
+ tclk = clk_get_rate(drv_data->clk);
+
+ rc = of_property_read_u32(np, "clock-frequency", &bus_freq);
+ if (rc)
+ bus_freq = 100000; /* 100kHz by default */
+
+ if (!mv64xxx_find_baud_factors(bus_freq, tclk,
+ &drv_data->freq_n, &drv_data->freq_m)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ freq = mv64xxx_calc_freq(tclk, drv_data->freq_n, drv_data->freq_m);
+ dev_dbg(pd, "tclk=%d freq_n=%d freq_m=%d freq=%d\n",
+ tclk, drv_data->freq_n, drv_data->freq_m, freq);
+
+ if (of_property_read_u32(np, "reg-io-width", &prop)) {
+ /* Use 32-bit registers by default */
+ prop = 4;
+ }
+
+ switch (prop) {
+ case 1:
+ drv_data->write_reg = mv64xxx_writeb;
+ drv_data->read_reg = mv64xxx_readb;
+ break;
+ case 4:
+ drv_data->write_reg = mv64xxx_writel;
+ drv_data->read_reg = mv64xxx_readl;
+ break;
+ default:
+ dev_err(pd, "unsupported reg-io-width (%d)\n", prop);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ dev_get_drvdata(pd, (unsigned long *)&mv64xxx_regs);
+ memcpy(&drv_data->reg_offsets, mv64xxx_regs,
+ sizeof(drv_data->reg_offsets));
+
+ /*
+ * For controllers embedded in new SoCs activate the errata fix.
+ */
+ if (of_device_is_compatible(np, "marvell,mv78230-i2c")) {
+ drv_data->errata_delay = true;
+ }
+
+ if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) {
+ drv_data->errata_delay = true;
+ }
+
+out:
+ return rc;
+}
+
+static int
+mv64xxx_i2c_probe(struct device_d *pd)
+{
+ struct mv64xxx_i2c_data *drv_data;
+ int rc;
+
+ if (!pd->device_node)
+ return -ENODEV;
+
+ drv_data = xzalloc(sizeof(*drv_data));
+
+ drv_data->reg_base = dev_request_mem_region(pd, 0);
+ if (IS_ERR(drv_data->reg_base))
+ return PTR_ERR(drv_data->reg_base);
+
+ drv_data->clk = clk_get(pd, NULL);
+ if (IS_ERR(drv_data->clk))
+ return PTR_ERR(drv_data->clk);
+
+ clk_enable(drv_data->clk);
+
+ rc = mv64xxx_of_config(drv_data, pd);
+ if (rc)
+ goto exit_clk;
+
+ drv_data->adapter.master_xfer = mv64xxx_i2c_xfer;
+ drv_data->adapter.dev.parent = pd;
+ drv_data->adapter.nr = pd->id;
+ drv_data->adapter.dev.device_node = pd->device_node;
+
+ mv64xxx_i2c_hw_init(drv_data);
+
+ rc = i2c_add_numbered_adapter(&drv_data->adapter);
+ if (rc) {
+ dev_err(pd, "Failed to add I2C adapter\n");
+ goto exit_clk;
+ }
+
+ return 0;
+
+exit_clk:
+ clk_disable(drv_data->clk);
+
+ return rc;
+}
+
+static struct driver_d mv64xxx_i2c_driver = {
+ .probe = mv64xxx_i2c_probe,
+ .name = "mv64xxx_i2c",
+ .of_compatible = DRV_OF_COMPAT(mv64xxx_i2c_of_match_table),
+};
+device_platform_driver(mv64xxx_i2c_driver);
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
new file mode 100644
index 0000000000..e8aeaa7234
--- /dev/null
+++ b/drivers/i2c/i2c-smbus.c
@@ -0,0 +1,371 @@
+#include <common.h>
+#include <errno.h>
+#include <i2c/i2c.h>
+
+/* The SMBus parts */
+
+#define POLY (0x1070U << 3)
+static u8 crc8(u16 data)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (data & 0x8000)
+ data = data ^ POLY;
+ data = data << 1;
+ }
+ return (u8)(data >> 8);
+}
+
+/* Incremental CRC8 over count bytes in the array pointed to by p */
+static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ crc = crc8((crc ^ p[i]) << 8);
+ return crc;
+}
+
+/* Assume a 7-bit address, which is reasonable for SMBus */
+static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
+{
+ /* The address will be sent first */
+ u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD);
+ pec = i2c_smbus_pec(pec, &addr, 1);
+
+ /* The data buffer follows */
+ return i2c_smbus_pec(pec, msg->buf, msg->len);
+}
+
+/* Used for write only transactions */
+static inline void i2c_smbus_add_pec(struct i2c_msg *msg)
+{
+ msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg);
+ msg->len++;
+}
+
+/* Return <0 on CRC error
+ If there was a write before this read (most cases) we need to take the
+ partial CRC from the write part into account.
+ Note that this function does modify the message (we need to decrease the
+ message length to hide the CRC byte from the caller). */
+static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
+{
+ u8 rpec = msg->buf[--msg->len];
+ cpec = i2c_smbus_msg_pec(cpec, msg);
+
+ if (rpec != cpec) {
+ pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n",
+ rpec, cpec);
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+/**
+ * i2c_smbus_read_byte - SMBus "receive byte" protocol
+ * @client: Handle to slave device
+ *
+ * This executes the SMBus "receive byte" protocol, returning negative errno
+ * else the byte received from the device.
+ */
+s32 i2c_smbus_read_byte(const struct i2c_client *client)
+{
+ union i2c_smbus_data data;
+ int status;
+
+ status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_READ, 0,
+ I2C_SMBUS_BYTE, &data);
+ return (status < 0) ? status : data.byte;
+}
+EXPORT_SYMBOL(i2c_smbus_read_byte);
+
+/**
+ * i2c_smbus_write_byte - SMBus "send byte" protocol
+ * @client: Handle to slave device
+ * @value: Byte to be sent
+ *
+ * This executes the SMBus "send byte" protocol, returning negative errno
+ * else zero on success.
+ */
+s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
+{
+ return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
+}
+EXPORT_SYMBOL(i2c_smbus_write_byte);
+
+/**
+ * i2c_smbus_read_byte_data - SMBus "read byte" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ *
+ * This executes the SMBus "read byte" protocol, returning negative errno
+ * else a data byte received from the device.
+ */
+s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
+{
+ union i2c_smbus_data data;
+ int status;
+
+ status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+ return (status < 0) ? status : data.byte;
+}
+EXPORT_SYMBOL(i2c_smbus_read_byte_data);
+
+/**
+ * i2c_smbus_write_byte_data - SMBus "write byte" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ * @value: Byte being written
+ *
+ * This executes the SMBus "write byte" protocol, returning negative errno
+ * else zero on success.
+ */
+s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command,
+ u8 value)
+{
+ union i2c_smbus_data data;
+ data.byte = value;
+ return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+}
+EXPORT_SYMBOL(i2c_smbus_write_byte_data);
+
+/**
+ * i2c_smbus_read_word_data - SMBus "read word" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ *
+ * This executes the SMBus "read word" protocol, returning negative errno
+ * else a 16-bit unsigned "word" received from the device.
+ */
+s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
+{
+ union i2c_smbus_data data;
+ int status;
+
+ status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_WORD_DATA, &data);
+ return (status < 0) ? status : data.word;
+}
+EXPORT_SYMBOL(i2c_smbus_read_word_data);
+
+/**
+ * i2c_smbus_write_word_data - SMBus "write word" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ * @value: 16-bit "word" being written
+ *
+ * This executes the SMBus "write word" protocol, returning negative errno
+ * else zero on success.
+ */
+s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
+ u16 value)
+{
+ union i2c_smbus_data data;
+ data.word = value;
+ return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_WORD_DATA, &data);
+}
+EXPORT_SYMBOL(i2c_smbus_write_word_data);
+
+/* Returns the number of read bytes */
+s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values)
+{
+ union i2c_smbus_data data;
+ int status;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ data.block[0] = length;
+ status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_I2C_BLOCK_DATA, &data);
+ if (status < 0)
+ return status;
+
+ memcpy(values, &data.block[1], data.block[0]);
+ return data.block[0];
+}
+EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
+
+s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values)
+{
+ union i2c_smbus_data data;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ data.block[0] = length;
+ memcpy(data.block + 1, values, length);
+ return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_I2C_BLOCK_DATA, &data);
+}
+EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
+
+/* Simulate a SMBus command using the i2c protocol
+ No checking of parameters is done! */
+static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags,
+ char read_write, u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ /* So we need to generate a series of msgs. In the case of writing, we
+ need to use only one message; when reading, we need two. We initialize
+ most things with sane defaults, to keep the code below somewhat
+ simpler. */
+ unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
+ unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
+ int num = read_write == I2C_SMBUS_READ ? 2 : 1;
+ int i;
+ u8 partial_pec = 0;
+ int status;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = addr,
+ .flags = flags,
+ .len = 1,
+ .buf = msgbuf0,
+ }, {
+ .addr = addr,
+ .flags = flags | I2C_M_RD,
+ .len = 0,
+ .buf = msgbuf1,
+ },
+ };
+
+ msgbuf0[0] = command;
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ msg[0].len = 0;
+ /* Special case: The read/write field is used as data */
+ msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
+ I2C_M_RD : 0);
+ num = 1;
+ break;
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_READ) {
+ /* Special case: only a read! */
+ msg[0].flags = I2C_M_RD | flags;
+ num = 1;
+ }
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_READ)
+ msg[1].len = 1;
+ else {
+ msg[0].len = 2;
+ msgbuf0[1] = data->byte;
+ }
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_READ)
+ msg[1].len = 2;
+ else {
+ msg[0].len = 3;
+ msgbuf0[1] = data->word & 0xff;
+ msgbuf0[2] = data->word >> 8;
+ }
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ msg[1].len = data->block[0];
+ } else {
+ msg[0].len = data->block[0] + 1;
+ if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
+ dev_err(&adapter->dev,
+ "Invalid block write size %d\n",
+ data->block[0]);
+ return -EINVAL;
+ }
+ for (i = 1; i <= data->block[0]; i++)
+ msgbuf0[i] = data->block[i];
+ }
+ break;
+ default:
+ dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
+ && size != I2C_SMBUS_I2C_BLOCK_DATA);
+ if (i) {
+ /* Compute PEC if first message is a write */
+ if (!(msg[0].flags & I2C_M_RD)) {
+ if (num == 1) /* Write only */
+ i2c_smbus_add_pec(&msg[0]);
+ else /* Write followed by read */
+ partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
+ }
+ /* Ask for PEC if last message is a read */
+ if (msg[num-1].flags & I2C_M_RD)
+ msg[num-1].len++;
+ }
+
+ status = i2c_transfer(adapter, msg, num);
+ if (status < 0)
+ return status;
+
+ /* Check PEC if last message is a read */
+ if (i && (msg[num-1].flags & I2C_M_RD)) {
+ status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
+ if (status < 0)
+ return status;
+ }
+
+ if (read_write == I2C_SMBUS_READ)
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ data->byte = msgbuf0[0];
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ data->byte = msgbuf1[0];
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ data->word = msgbuf1[0] | (msgbuf1[1] << 8);
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ for (i = 0; i < data->block[0]; i++)
+ data->block[i+1] = msgbuf1[i];
+ break;
+ }
+ return 0;
+}
+
+/**
+ * i2c_smbus_xfer - execute SMBus protocol operations
+ * @adapter: Handle to I2C bus
+ * @addr: Address of SMBus slave on that bus
+ * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
+ * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
+ * @command: Byte interpreted by slave, for protocols which use such bytes
+ * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
+ * @data: Data to be read or written
+ *
+ * This executes an SMBus protocol operation, and returns a negative
+ * errno code else zero on success.
+ */
+s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
+ char read_write, u8 command, int protocol,
+ union i2c_smbus_data *data)
+{
+ s32 res;
+
+ flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
+
+ res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
+ command, protocol, data);
+
+ return res;
+}
+EXPORT_SYMBOL(i2c_smbus_xfer);