[linux-yocto] [PATCHv2 1/4] valleyisland-io: add patch for I2C support in Baytrail

rebecca.swee.fun.chang at intel.com rebecca.swee.fun.chang at intel.com
Tue Dec 10 20:19:13 PST 2013


From: "Chang, Rebecca Swee Fun" <rebecca.swee.fun.chang at intel.com>

i2c-designware: Baytrail has 7 I2C controllers which can be PCI
and ACPI enumerated.

Signed-off-by: Chang, Rebecca Swee Fun <rebecca.swee.fun.chang at intel.com>
---
 ...gnware-Add-support-for-Intel-Baytrail-I2C.patch |  961 ++++++++++++++++++++
 1 file changed, 961 insertions(+)
 create mode 100644 meta/cfg/kernel-cache/features/valleyisland-io/0006-i2c-designware-Add-support-for-Intel-Baytrail-I2C.patch

diff --git a/meta/cfg/kernel-cache/features/valleyisland-io/0006-i2c-designware-Add-support-for-Intel-Baytrail-I2C.patch b/meta/cfg/kernel-cache/features/valleyisland-io/0006-i2c-designware-Add-support-for-Intel-Baytrail-I2C.patch
new file mode 100644
index 0000000..668c049
--- /dev/null
+++ b/meta/cfg/kernel-cache/features/valleyisland-io/0006-i2c-designware-Add-support-for-Intel-Baytrail-I2C.patch
@@ -0,0 +1,961 @@
+i2c-designware: Add support for Intel Valley Island I2C
+
+Valley Island has 7 I2C controllers which can be PCI or ACPI enumerated.
+
+Signed-off-by: Chang, Rebecca Swee Fun <rebecca.swee.fun.chang at intel.com>
+---
+ .../devicetree/bindings/i2c/i2c-designware.txt     |   15 ++
+ drivers/i2c/busses/i2c-designware-core.c           |  136 ++++++++----
+ drivers/i2c/busses/i2c-designware-core.h           |   15 +-
+ drivers/i2c/busses/i2c-designware-pcidrv.c         |  227 +++++++++++++++-----
+ drivers/i2c/busses/i2c-designware-platdrv.c        |  165 +++++++++-----
+ 5 files changed, 408 insertions(+), 150 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c-designware.txt b/Documentation/devicetree/bindings/i2c/i2c-designware.txt
+index e42a2ee..fb2eac8 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-designware.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-designware.txt
+@@ -10,6 +10,10 @@ Recommended properties :
+
+  - clock-frequency : desired I2C bus clock frequency in Hz.
+
++Optional properties :
++ - sda-hold-time : If this property is present, the register SDA_HOLD will
++   be initialised with its value.
++
+ Example :
+
+ 	i2c at f0000 {
+@@ -20,3 +24,14 @@ Example :
+ 		interrupts = <11>;
+ 		clock-frequency = <400000>;
+ 	};
++
++	i2c at 1120000 {
++		#address-cells = <1>;
++		#size-cells = <0>;
++		compatible = "snps,designware-i2c";
++		reg = <0x1120000 0x1000>;
++		interrupt-parent = <&ictl>;
++		interrupts = <12 1>;
++		clock-frequency = <400000>;
++		sda-hold-time = <0x64>;
++	};
+diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
+index f5258c2..e730640 100644
+--- a/drivers/i2c/busses/i2c-designware-core.c
++++ b/drivers/i2c/busses/i2c-designware-core.c
+@@ -67,7 +67,9 @@
+ #define DW_IC_STATUS		0x70
+ #define DW_IC_TXFLR		0x74
+ #define DW_IC_RXFLR		0x78
++#define DW_IC_SDA_HOLD		0x7c
+ #define DW_IC_TX_ABRT_SOURCE	0x80
++#define DW_IC_ENABLE_STATUS	0x9c
+ #define DW_IC_COMP_PARAM_1	0xf4
+ #define DW_IC_COMP_TYPE		0xfc
+ #define DW_IC_COMP_TYPE_VALUE	0x44570140
+@@ -248,6 +250,22 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
+ 	return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset;
+ }
+
++static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
++{
++	int timeout = 100;
++
++	do {
++		dw_writel(dev, enable, DW_IC_ENABLE);
++		if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == enable)
++			return;
++
++		usleep_range(25, 250);
++	} while (timeout-- > 0);
++
++	dev_warn(dev->dev, "timeout in %sabling adapter\n",
++		 enable ? "en" : "dis");
++}
++
+ /**
+  * i2c_dw_init() - initialize the designware i2c master hardware
+  * @dev: device private data
+@@ -278,38 +296,52 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
+ 	}
+
+ 	/* Disable the adapter */
+-	dw_writel(dev, 0, DW_IC_ENABLE);
++	__i2c_dw_enable(dev, false);
+
+ 	/* set standard and fast speed deviders for high/low periods */
+
+ 	/* Standard-mode */
+-	hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+-				40,	/* tHD;STA = tHIGH = 4.0 us */
+-				3,	/* tf = 0.3 us */
+-				0,	/* 0: DW default, 1: Ideal */
+-				0);	/* No offset */
+-	lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+-				47,	/* tLOW = 4.7 us */
+-				3,	/* tf = 0.3 us */
+-				0);	/* No offset */
++	if (dev->ss_hcnt && dev->ss_lcnt) {
++		hcnt = dev->ss_hcnt;
++		lcnt = dev->ss_lcnt;
++	} else {
++		hcnt = i2c_dw_scl_hcnt(input_clock_khz,
++					40,	/* tHD;STA = tHIGH = 4.0 us */
++					3,	/* tf = 0.3 us */
++					0,	/* 0: DW default, 1: Ideal */
++					0);	/* No offset */
++		lcnt = i2c_dw_scl_lcnt(input_clock_khz,
++					47,	/* tLOW = 4.7 us */
++					3,	/* tf = 0.3 us */
++					0);	/* No offset */
++	}
+ 	dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
+ 	dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
+ 	dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+
+ 	/* Fast-mode */
+-	hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+-				6,	/* tHD;STA = tHIGH = 0.6 us */
+-				3,	/* tf = 0.3 us */
+-				0,	/* 0: DW default, 1: Ideal */
+-				0);	/* No offset */
+-	lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+-				13,	/* tLOW = 1.3 us */
+-				3,	/* tf = 0.3 us */
+-				0);	/* No offset */
++	if (dev->fs_hcnt && dev->fs_lcnt) {
++		hcnt = dev->fs_hcnt;
++		lcnt = dev->fs_lcnt;
++	} else {
++		hcnt = i2c_dw_scl_hcnt(input_clock_khz,
++					6,	/* tHD;STA = tHIGH = 0.6 us */
++					3,	/* tf = 0.3 us */
++					0,	/* 0: DW default, 1: Ideal */
++					0);	/* No offset */
++		lcnt = i2c_dw_scl_lcnt(input_clock_khz,
++					13,	/* tLOW = 1.3 us */
++					3,	/* tf = 0.3 us */
++					0);	/* No offset */
++	}
+ 	dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
+ 	dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
+ 	dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+
++	/* Configure SDA Hold Time if required */
++	if (dev->sda_hold_time)
++		dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
++
+ 	/* Configure Tx/Rx FIFO threshold levels */
+ 	dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
+ 	dw_writel(dev, 0, DW_IC_RX_TL);
+@@ -333,7 +365,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
+ 			return -ETIMEDOUT;
+ 		}
+ 		timeout--;
+-		mdelay(1);
++		usleep_range(1000, 1000);
+ 	}
+
+ 	return 0;
+@@ -342,24 +374,31 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
+ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
+ {
+ 	struct i2c_msg *msgs = dev->msgs;
+-	u32 ic_con;
++	u32 ic_con, ic_tar = 0;
+
+ 	/* Disable the adapter */
+-	dw_writel(dev, 0, DW_IC_ENABLE);
++	__i2c_dw_enable(dev, false);
+
+-	/* set the slave (target) address */
+-	dw_writel(dev, msgs[dev->msg_write_idx].addr, DW_IC_TAR);
+-
+-	/* if the slave address is ten bit address, enable 10BITADDR */
++	/* If the slave address is ten bit address, enable 10BITADDR.
++	 * However, if I2C_DYNAMIC_TAR_UPDATE is set to 1,the 10 bit
++	 * addressing mode control is resided in bit 12 of IC_TAR
++	 * register instead of IC_CON register.
++	 */
+ 	ic_con = dw_readl(dev, DW_IC_CON);
+-	if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
++
++	if (msgs[dev->msg_write_idx].flags & I2C_M_TEN){
+ 		ic_con |= DW_IC_CON_10BITADDR_MASTER;
+-	else
++		ic_tar = DW_IC_TAR_10BITADDR_MASTER;
++	} else
+ 		ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
++
+ 	dw_writel(dev, ic_con, DW_IC_CON);
+
++	/* set the slave (target) address */
++	dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);
++
+ 	/* Enable the adapter */
+-	dw_writel(dev, 1, DW_IC_ENABLE);
++	__i2c_dw_enable(dev, true);
+
+ 	/* Enable interrupts */
+ 	dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
+@@ -380,6 +419,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
+ 	u32 addr = msgs[dev->msg_write_idx].addr;
+ 	u32 buf_len = dev->tx_buf_len;
+ 	u8 *buf = dev->tx_buf;
++	bool need_restart = false;
+
+ 	intr_mask = DW_IC_INTR_DEFAULT_MASK;
+
+@@ -407,17 +447,39 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
+ 			/* new i2c_msg */
+ 			buf = msgs[dev->msg_write_idx].buf;
+ 			buf_len = msgs[dev->msg_write_idx].len;
++
++			/* insert restart bit between msgs */
++			if ((dev->master_cfg & DW_IC_CON_RESTART_EN) &&
++					(dev->msg_write_idx > 0))
++				need_restart = true;
+ 		}
+
+ 		tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
+ 		rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
+
+ 		while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
++			u32 cmd = 0;
++
++			/*
++			 * If IC_EMPTYFIFO_HOLD_MASTER_EN is set we must
++			 * manually set the stop bit. However, it cannot be
++			 * detected from the registers so we set it always
++			 * when writing/reading the last byte.
++			 */
++			if (dev->msg_write_idx == dev->msgs_num - 1 &&
++					buf_len == 1)
++				cmd |= BIT(9);
++
++			if (need_restart) {
++				cmd |= BIT(10);
++				need_restart = false;
++			}
++
+ 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+-				dw_writel(dev, 0x100, DW_IC_DATA_CMD);
++				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
+ 				rx_limit--;
+ 			} else
+-				dw_writel(dev, *buf++, DW_IC_DATA_CMD);
++				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
+ 			tx_limit--; buf_len--;
+ 		}
+
+@@ -428,8 +490,9 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
+ 			/* more bytes to be written */
+ 			dev->status |= STATUS_WRITE_IN_PROGRESS;
+ 			break;
+-		} else
++		} else {
+ 			dev->status &= ~STATUS_WRITE_IN_PROGRESS;
++		}
+ 	}
+
+ 	/*
+@@ -553,7 +616,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+ 	/* no error */
+ 	if (likely(!dev->cmd_err)) {
+ 		/* Disable the adapter */
+-		dw_writel(dev, 0, DW_IC_ENABLE);
++		__i2c_dw_enable(dev, false);
+ 		ret = num;
+ 		goto done;
+ 	}
+@@ -566,7 +629,8 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+ 	ret = -EIO;
+
+ done:
+-	pm_runtime_put(dev->dev);
++	pm_runtime_mark_last_busy(dev->dev);
++	pm_runtime_put_autosuspend(dev->dev);
+ 	mutex_unlock(&dev->lock);
+
+ 	return ret;
+@@ -688,7 +752,7 @@ EXPORT_SYMBOL_GPL(i2c_dw_isr);
+ void i2c_dw_enable(struct dw_i2c_dev *dev)
+ {
+        /* Enable the adapter */
+-	dw_writel(dev, 1, DW_IC_ENABLE);
++	__i2c_dw_enable(dev, true);
+ }
+ EXPORT_SYMBOL_GPL(i2c_dw_enable);
+
+@@ -701,7 +765,7 @@ EXPORT_SYMBOL_GPL(i2c_dw_is_enabled);
+ void i2c_dw_disable(struct dw_i2c_dev *dev)
+ {
+ 	/* Disable controller */
+-	dw_writel(dev, 0, DW_IC_ENABLE);
++	__i2c_dw_enable(dev, false);
+
+ 	/* Disable all interupts */
+ 	dw_writel(dev, 0, DW_IC_INTR_MASK);
+diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
+index 9c1840e..ac21421 100644
+--- a/drivers/i2c/busses/i2c-designware-core.h
++++ b/drivers/i2c/busses/i2c-designware-core.h
+@@ -33,7 +33,7 @@
+ #define DW_IC_CON_10BITADDR_MASTER	0x10
+ #define DW_IC_CON_RESTART_EN		0x20
+ #define DW_IC_CON_SLAVE_DISABLE		0x40
+-
++#define DW_IC_TAR_10BITADDR_MASTER	0x1000
+
+ /**
+  * struct dw_i2c_dev - private i2c-designware data
+@@ -60,6 +60,14 @@
+  * @adapter: i2c subsystem adapter node
+  * @tx_fifo_depth: depth of the hardware tx fifo
+  * @rx_fifo_depth: depth of the hardware rx fifo
++ * @ss_hcnt: standard speed HCNT value
++ * @ss_lcnt: standard speed LCNT value
++ * @fs_hcnt: fast speed HCNT value
++ * @fs_lcnt: fast speed LCNT value
++ *
++ * HCNT and LCNT parameters can be used if the platform knows more accurate
++ * values than the one computed based only on the input clock frequency.
++ * Leave them to be %0 if not used.
+  */
+ struct dw_i2c_dev {
+ 	struct device		*dev;
+@@ -88,6 +96,11 @@ struct dw_i2c_dev {
+ 	u32			master_cfg;
+ 	unsigned int		tx_fifo_depth;
+ 	unsigned int		rx_fifo_depth;
++	u32			sda_hold_time;
++	u16			ss_hcnt;
++	u16			ss_lcnt;
++	u16			fs_hcnt;
++	u16			fs_lcnt;
+ };
+
+ #define ACCESS_SWAP		0x00000001
+diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
+index 6add851..a181e56 100644
+--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
++++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
+@@ -41,8 +41,14 @@
+ #include <linux/pm_runtime.h>
+ #include "i2c-designware-core.h"
+
++#include <asm/processor.h>
++
+ #define DRIVER_NAME "i2c-designware-pci"
+
++static bool force_std_mode;
++module_param(force_std_mode, bool, 0);
++MODULE_PARM_DESC(force_std_mode, "Force standard mode (100 kHz)");
++
+ enum dw_pci_ctl_id_t {
+ 	moorestown_0,
+ 	moorestown_1,
+@@ -54,6 +60,14 @@ enum dw_pci_ctl_id_t {
+ 	medfield_3,
+ 	medfield_4,
+ 	medfield_5,
++
++	byt_0,
++	byt_1,
++	byt_2,
++	byt_3,
++	byt_4,
++	byt_5,
++	byt_6,
+ };
+
+ struct dw_pci_controller {
+@@ -62,12 +76,22 @@ struct dw_pci_controller {
+ 	u32 tx_fifo_depth;
+ 	u32 rx_fifo_depth;
+ 	u32 clk_khz;
++	u32 ss_hcnt;
++	u32 ss_lcnt;
++	u32 fs_hcnt;
++	u32 fs_lcnt;
++	u32 ss_sda;
++	u32 fs_sda;
+ };
+
+ #define INTEL_MID_STD_CFG  (DW_IC_CON_MASTER |			\
+ 				DW_IC_CON_SLAVE_DISABLE |	\
+ 				DW_IC_CON_RESTART_EN)
+
++#define INTEL_BYT_STD_CFG  (DW_IC_CON_MASTER |		\
++				DW_IC_CON_SLAVE_DISABLE |	\
++				DW_IC_CON_RESTART_EN)
++
+ static struct  dw_pci_controller  dw_pci_controllers[] = {
+ 	[moorestown_0] = {
+ 		.bus_num     = 0,
+@@ -132,6 +156,97 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
+ 		.rx_fifo_depth = 32,
+ 		.clk_khz      = 25000,
+ 	},
++	[byt_0] = {
++		.bus_num     = 0,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
++	[byt_1] = {
++		.bus_num     = 1,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
++	[byt_2] = {
++		.bus_num     = 2,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
++	[byt_3] = {
++		.bus_num     = 3,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
++	[byt_4] = {
++		.bus_num     = 4,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
++	[byt_5] = {
++		.bus_num     = 5,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
++	[byt_6] = {
++		.bus_num     = 6,
++		.bus_cfg   = INTEL_BYT_STD_CFG | DW_IC_CON_SPEED_FAST,
++		.tx_fifo_depth = 32,
++		.rx_fifo_depth = 32,
++		.clk_khz      = 100000,
++		.ss_hcnt	= 0x200,
++		.ss_lcnt	= 0x200,
++		.fs_hcnt	= 0x55,
++		.fs_lcnt	= 0x99,
++		.ss_sda		= 0x6,
++		.fs_sda		= 0x6,
++	},
+ };
+ static struct i2c_algorithm i2c_dw_algo = {
+ 	.master_xfer	= i2c_dw_xfer,
+@@ -208,83 +323,85 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
+ }
+
+ static int i2c_dw_pci_probe(struct pci_dev *pdev,
+-const struct pci_device_id *id)
++			    const struct pci_device_id *id)
+ {
+ 	struct dw_i2c_dev *dev;
+ 	struct i2c_adapter *adap;
+-	unsigned long start, len;
+-	void __iomem *base;
+ 	int r;
++	u32 mode;
+ 	struct  dw_pci_controller *controller;
+
+ 	if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
+-		printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n",
++		dev_err(&pdev->dev, "%s: invalid driver data %ld\n", __func__,
+ 			id->driver_data);
+ 		return -EINVAL;
+ 	}
+
+ 	controller = &dw_pci_controllers[id->driver_data];
+
+-	r = pci_enable_device(pdev);
++	r = pcim_enable_device(pdev);
+ 	if (r) {
+ 		dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n",
+ 			r);
+-		goto exit;
+-	}
+-
+-	/* Determine the address of the I2C area */
+-	start = pci_resource_start(pdev, 0);
+-	len = pci_resource_len(pdev, 0);
+-	if (!start || len == 0) {
+-		dev_err(&pdev->dev, "base address not set\n");
+-		r = -ENODEV;
+-		goto exit;
++		return r;
+ 	}
+
+-	r = pci_request_region(pdev, 0, DRIVER_NAME);
++	r = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
+ 	if (r) {
+-		dev_err(&pdev->dev, "failed to request I2C region "
+-			"0x%lx-0x%lx\n", start,
+-			(unsigned long)pci_resource_end(pdev, 0));
+-		goto exit;
+-	}
+-
+-	base = ioremap_nocache(start, len);
+-	if (!base) {
+ 		dev_err(&pdev->dev, "I/O memory remapping failed\n");
+-		r = -ENOMEM;
+-		goto err_release_region;
++		return r;
+ 	}
+
+-
+-	dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+-	if (!dev) {
+-		r = -ENOMEM;
+-		goto err_release_region;
+-	}
++	dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
++	if (!dev)
++		return -ENOMEM;
+
+ 	init_completion(&dev->cmd_complete);
+ 	mutex_init(&dev->lock);
+ 	dev->clk = NULL;
+ 	dev->controller = controller;
+ 	dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+-	dev->base = base;
+-	dev->dev = get_device(&pdev->dev);
++	dev->base = pcim_iomap_table(pdev)[0];
++	dev->dev = &pdev->dev;
+ 	dev->functionality =
+ 		I2C_FUNC_I2C |
++		I2C_FUNC_10BIT_ADDR |
+ 		I2C_FUNC_SMBUS_BYTE |
+ 		I2C_FUNC_SMBUS_BYTE_DATA |
+ 		I2C_FUNC_SMBUS_WORD_DATA |
+ 		I2C_FUNC_SMBUS_I2C_BLOCK;
+-	dev->master_cfg =  controller->bus_cfg;
+
++	/*
++	 * BYT A0: Override base clock, hcnt and lcnt default settings
++	 */
++	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && boot_cpu_data.x86 == 0x6 &&
++		boot_cpu_data.x86_model == 0x37 && boot_cpu_data.x86_mask < 2) {
++		controller->clk_khz = 133000;
++		controller->ss_hcnt = 0x28f;
++		controller->ss_lcnt = 0x2ba;
++		controller->fs_hcnt = 0x71;
++		controller->fs_lcnt = 0xce;
++	}
++
++	dev->ss_hcnt = controller->ss_hcnt;
++	dev->ss_lcnt = controller->ss_lcnt;
++	dev->fs_hcnt = controller->fs_hcnt;
++	dev->fs_lcnt = controller->fs_lcnt;
++	mode = controller->bus_cfg & (DW_IC_CON_SPEED_STD | DW_IC_CON_SPEED_FAST);
++	if (force_std_mode && !(mode & DW_IC_CON_SPEED_STD)){
++		controller->bus_cfg &= ~mode;
++		controller->bus_cfg |= DW_IC_CON_SPEED_STD;
++	}
++
++	dev->master_cfg =  controller->bus_cfg;
++	dev->sda_hold_time = (dev->master_cfg & DW_IC_CON_SPEED_FAST) ? controller->fs_sda : controller->ss_sda;
+ 	pci_set_drvdata(pdev, dev);
+
+ 	dev->tx_fifo_depth = controller->tx_fifo_depth;
+ 	dev->rx_fifo_depth = controller->rx_fifo_depth;
+ 	r = i2c_dw_init(dev);
+ 	if (r)
+-		goto err_iounmap;
++		return r;
+
+ 	adap = &dev->adapter;
+ 	i2c_set_adapdata(adap, dev);
+@@ -296,10 +413,11 @@ const struct pci_device_id *id)
+ 	snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d",
+ 		adap->nr);
+
+-	r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev);
++	r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr, IRQF_SHARED,
++			adap->name, dev);
+ 	if (r) {
+ 		dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+-		goto err_iounmap;
++		return r;
+ 	}
+
+ 	i2c_dw_disable_int(dev);
+@@ -307,25 +425,15 @@ const struct pci_device_id *id)
+ 	r = i2c_add_numbered_adapter(adap);
+ 	if (r) {
+ 		dev_err(&pdev->dev, "failure adding adapter\n");
+-		goto err_free_irq;
++		return r;
+ 	}
+
+-	pm_runtime_put_noidle(&pdev->dev);
+-	pm_runtime_allow(&pdev->dev);
++	/* Increase reference counter */
++	get_device(&pdev->dev);
+
+-	return 0;
++	pm_runtime_forbid(&pdev->dev);
+
+-err_free_irq:
+-	free_irq(pdev->irq, dev);
+-err_iounmap:
+-	iounmap(dev->base);
+-	pci_set_drvdata(pdev, NULL);
+-	put_device(&pdev->dev);
+-	kfree(dev);
+-err_release_region:
+-	pci_release_region(pdev, 0);
+-exit:
+-	return r;
++	return 0;
+ }
+
+ static void i2c_dw_pci_remove(struct pci_dev *pdev)
+@@ -336,13 +444,8 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
+ 	pm_runtime_forbid(&pdev->dev);
+ 	pm_runtime_get_noresume(&pdev->dev);
+
+-	pci_set_drvdata(pdev, NULL);
+ 	i2c_del_adapter(&dev->adapter);
+ 	put_device(&pdev->dev);
+-
+-	free_irq(dev->irq, dev);
+-	kfree(dev);
+-	pci_release_region(pdev, 0);
+ }
+
+ /* work with hotplug and coldplug */
+@@ -360,6 +463,14 @@ static DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
+ 	{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
+ 	{ PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
+ 	{ PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
++	/* BYT */
++	{ PCI_VDEVICE(INTEL, 0x0F41), byt_0 },
++	{ PCI_VDEVICE(INTEL, 0x0F42), byt_1 },
++	{ PCI_VDEVICE(INTEL, 0x0F43), byt_2 },
++	{ PCI_VDEVICE(INTEL, 0x0F44), byt_3 },
++	{ PCI_VDEVICE(INTEL, 0x0F45), byt_4 },
++	{ PCI_VDEVICE(INTEL, 0x0F46), byt_5 },
++	{ PCI_VDEVICE(INTEL, 0x0F47), byt_6 },
+ 	{ 0,}
+ };
+ MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
+diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
+index 343357a..888e107 100644
+--- a/drivers/i2c/busses/i2c-designware-platdrv.c
++++ b/drivers/i2c/busses/i2c-designware-platdrv.c
+@@ -34,13 +34,20 @@
+ #include <linux/sched.h>
+ #include <linux/err.h>
+ #include <linux/interrupt.h>
++#include <linux/of.h>
+ #include <linux/of_i2c.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm.h>
++#include <linux/pm_runtime.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
++#include <linux/acpi.h>
+ #include "i2c-designware-core.h"
+
++static bool force_std_mode;
++module_param(force_std_mode, bool, 0);
++MODULE_PARM_DESC(force_std_mode, "Force standard mode (100 kHz)");
++
+ static struct i2c_algorithm i2c_dw_algo = {
+ 	.master_xfer	= i2c_dw_xfer,
+ 	.functionality	= i2c_dw_func,
+@@ -50,11 +57,75 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
+ 	return clk_get_rate(dev->clk)/1000;
+ }
+
++#ifdef CONFIG_ACPI
++static void dw_i2c_acpi_get_cnt(struct dw_i2c_dev *dev, acpi_string name,
++				u16 *hcnt, u16 *lcnt, u32 *sda)
++{
++	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
++	acpi_handle handle = ACPI_HANDLE(dev->dev);
++	union acpi_object *obj;
++
++	if (ACPI_FAILURE(acpi_evaluate_object(handle, name, NULL, &buf)))
++		return;
++
++	obj = (union acpi_object *)buf.pointer;
++	if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) {
++		const union acpi_object *objs = obj->package.elements;
++
++		*hcnt = (u16)objs[0].integer.value;
++		*lcnt = (u16)objs[1].integer.value;
++		*sda = (u32)objs[2].integer.value;
++	}
++
++	kfree(buf.pointer);
++}
++
++static int dw_i2c_acpi_configure(struct platform_device *pdev)
++{
++	struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
++	u32 ss_sda, fs_sda;
++
++	if (!ACPI_HANDLE(&pdev->dev))
++		return -ENODEV;
++
++	dev->adapter.nr = -1;
++	dev->tx_fifo_depth = 32;
++	dev->rx_fifo_depth = 32;
++
++	/*
++	 * Try to get platform specific *CNT and SDA hold values from BIOS
++	 * if provided. Otherwise use the defaults.
++	 */
++	ss_sda = fs_sda = 9;
++	dw_i2c_acpi_get_cnt(dev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_sda);
++	dw_i2c_acpi_get_cnt(dev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_sda);
++
++	if (dev->master_cfg & DW_IC_CON_SPEED_FAST)
++		dev->sda_hold_time = fs_sda;
++	else
++		dev->sda_hold_time = ss_sda;
++	return 0;
++}
++
++static const struct acpi_device_id dw_i2c_acpi_match[] = {
++	{ "80860F41", 0 },
++	{ "INT33C2", 0 },
++	{ "INT33C3", 0 },
++	{ }
++};
++MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
++#else
++static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
++{
++	return -ENODEV;
++}
++#endif
++
+ static int dw_i2c_probe(struct platform_device *pdev)
+ {
+ 	struct dw_i2c_dev *dev;
+ 	struct i2c_adapter *adap;
+-	struct resource *mem, *ioarea;
++	struct resource *mem;
+ 	int irq, r;
+
+ 	/* NOTE: driver uses the static register mapping */
+@@ -70,34 +141,33 @@ static int dw_i2c_probe(struct platform_device *pdev)
+ 		return irq; /* -ENXIO */
+ 	}
+
+-	ioarea = request_mem_region(mem->start, resource_size(mem),
+-			pdev->name);
+-	if (!ioarea) {
++	dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
++	if (!dev)
++		return -ENOMEM;
++
++	dev->base = devm_request_and_ioremap(&pdev->dev, mem);
++	if (!dev->base) {
+ 		dev_err(&pdev->dev, "I2C region already claimed\n");
+ 		return -EBUSY;
+ 	}
+
+-	dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+-	if (!dev) {
+-		r = -ENOMEM;
+-		goto err_release_region;
+-	}
+-
+ 	init_completion(&dev->cmd_complete);
+ 	mutex_init(&dev->lock);
+-	dev->dev = get_device(&pdev->dev);
++	dev->dev = &pdev->dev;
+ 	dev->irq = irq;
+ 	platform_set_drvdata(pdev, dev);
+
+-	dev->clk = clk_get(&pdev->dev, NULL);
++	dev->clk = devm_clk_get(&pdev->dev, NULL);
+ 	dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+
+-	if (IS_ERR(dev->clk)) {
+-		r = -ENODEV;
+-		goto err_free_mem;
+-	}
++	if (IS_ERR(dev->clk))
++		return PTR_ERR(dev->clk);
+ 	clk_prepare_enable(dev->clk);
+
++	if (pdev->dev.of_node)
++		of_property_read_u32(pdev->dev.of_node, "sda-hold-time",
++						&dev->sda_hold_time);
++
+ 	dev->functionality =
+ 		I2C_FUNC_I2C |
+ 		I2C_FUNC_10BIT_ADDR |
+@@ -106,29 +176,29 @@ static int dw_i2c_probe(struct platform_device *pdev)
+ 		I2C_FUNC_SMBUS_WORD_DATA |
+ 		I2C_FUNC_SMBUS_I2C_BLOCK;
+ 	dev->master_cfg =  DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+-		DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
++		DW_IC_CON_RESTART_EN;
+
+-	dev->base = ioremap(mem->start, resource_size(mem));
+-	if (dev->base == NULL) {
+-		dev_err(&pdev->dev, "failure mapping io resources\n");
+-		r = -EBUSY;
+-		goto err_unuse_clocks;
+-	}
+-	{
++	dev->master_cfg |= (force_std_mode ? DW_IC_CON_SPEED_STD : DW_IC_CON_SPEED_FAST);
++
++	/* Try first if we can configure the device from ACPI */
++	r = dw_i2c_acpi_configure(pdev);
++	if (r) {
+ 		u32 param1 = i2c_dw_read_comp_param(dev);
+
+ 		dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
+ 		dev->rx_fifo_depth = ((param1 >> 8)  & 0xff) + 1;
++		dev->adapter.nr = pdev->id;
+ 	}
+ 	r = i2c_dw_init(dev);
+ 	if (r)
+-		goto err_iounmap;
++		return r;
+
+ 	i2c_dw_disable_int(dev);
+-	r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
++	r = devm_request_irq(&pdev->dev, dev->irq, i2c_dw_isr, IRQF_SHARED,
++			pdev->name, dev);
+ 	if (r) {
+ 		dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+-		goto err_iounmap;
++		return r;
+ 	}
+
+ 	adap = &dev->adapter;
+@@ -140,54 +210,38 @@ static int dw_i2c_probe(struct platform_device *pdev)
+ 	adap->algo = &i2c_dw_algo;
+ 	adap->dev.parent = &pdev->dev;
+ 	adap->dev.of_node = pdev->dev.of_node;
++	ACPI_HANDLE_SET(&adap->dev, ACPI_HANDLE(&pdev->dev));
+
+-	adap->nr = pdev->id;
+ 	r = i2c_add_numbered_adapter(adap);
+ 	if (r) {
+ 		dev_err(&pdev->dev, "failure adding adapter\n");
+-		goto err_free_irq;
++		return r;
+ 	}
+ 	of_i2c_register_devices(adap);
++	acpi_i2c_register_devices(adap);
+
+-	return 0;
++	/* Increase reference counter */
++	get_device(&pdev->dev);
+
+-err_free_irq:
+-	free_irq(dev->irq, dev);
+-err_iounmap:
+-	iounmap(dev->base);
+-err_unuse_clocks:
+-	clk_disable_unprepare(dev->clk);
+-	clk_put(dev->clk);
+-	dev->clk = NULL;
+-err_free_mem:
+-	platform_set_drvdata(pdev, NULL);
+-	put_device(&pdev->dev);
+-	kfree(dev);
+-err_release_region:
+-	release_mem_region(mem->start, resource_size(mem));
++	pm_runtime_forbid(&pdev->dev);
+
+-	return r;
++	return 0;
+ }
+
+ static int dw_i2c_remove(struct platform_device *pdev)
+ {
+ 	struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+-	struct resource *mem;
+
+-	platform_set_drvdata(pdev, NULL);
++	pm_runtime_get_sync(&pdev->dev);
++
+ 	i2c_del_adapter(&dev->adapter);
+ 	put_device(&pdev->dev);
+
+-	clk_disable_unprepare(dev->clk);
+-	clk_put(dev->clk);
+-	dev->clk = NULL;
+-
+ 	i2c_dw_disable(dev);
+-	free_irq(dev->irq, dev);
+-	kfree(dev);
+
+-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+-	release_mem_region(mem->start, resource_size(mem));
++	pm_runtime_put(&pdev->dev);
++	pm_runtime_disable(&pdev->dev);
++
+ 	return 0;
+ }
+
+@@ -233,6 +287,7 @@ static struct platform_driver dw_i2c_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,
+ 	},
+ };
+--
+1.7.10.4
+
-- 
1.7.10.4



More information about the linux-yocto mailing list