From: "Chang, Rebecca Swee Fun" <rebecca.swee.fun.ch...@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.ch...@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.ch...@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@f0000 { +@@ -20,3 +24,14 @@ Example : + interrupts = <11>; + clock-frequency = <400000>; + }; ++ ++ i2c@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 _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto