[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