From b4f91c7776c68a6e76a83325d12472109d81677f Mon Sep 17 00:00:00 2001 From: Bin Yang Date: Mon, 21 Nov 2011 16:36:02 +0000 Subject: [PATCH] Port I2C driver from current driver to k30 BZ: 15206 1. some differences of runtime PM implementation, including auto runtime suspend. 2. one bug fixing in ISR. 3. add dump function for debugging. 4. correct clock setting. Change-Id: Ib3d82e91333aa33bd319d5fed88389b15bd698fc Signed-off-by: Bin Yang Reviewed-on: http://android.intel.com:8080/25164 Reviewed-by: buildbot Reviewed-by: Gross, Mark Tested-by: Gross, Mark --- drivers/i2c/busses/i2c-designware-core.c | 65 ++++++++++++++++++++----- drivers/i2c/busses/i2c-designware-core.h | 10 +++- drivers/i2c/busses/i2c-designware-pcidrv.c | 77 ++++++++++++++++++------------ 3 files changed, 109 insertions(+), 43 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index df87992..0480177 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "i2c-designware-core.h" /* @@ -92,13 +93,6 @@ #define DW_IC_ERR_TX_ABRT 0x1 -/* - * status codes - */ -#define STATUS_IDLE 0x0 -#define STATUS_WRITE_IN_PROGRESS 0x1 -#define STATUS_READ_IN_PROGRESS 0x2 - #define TIMEOUT 20 /* ms */ /* @@ -180,6 +174,48 @@ void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset) writel(b, dev->base + offset); } +static void i2c_dw_dump(struct dw_i2c_dev *dev) +{ + u32 value; + + dev_err(dev->dev, "===== REGISTER DUMP (i2c) =====\n"); + value = dw_readl(dev, DW_IC_CON); + dev_err(dev->dev, "DW_IC_CON: 0x%x\n", value); + value = dw_readl(dev, DW_IC_TAR); + dev_err(dev->dev, "DW_IC_TAR: 0x%x\n", value); + value = dw_readl(dev, DW_IC_SS_SCL_HCNT); + dev_err(dev->dev, "DW_IC_SS_SCL_HCNT: 0x%x\n", value); + value = dw_readl(dev, DW_IC_SS_SCL_LCNT); + dev_err(dev->dev, "DW_IC_SS_SCL_LCNT: 0x%x\n", value); + value = dw_readl(dev, DW_IC_FS_SCL_HCNT); + dev_err(dev->dev, "DW_IC_FS_SCL_HCNT: 0x%x\n", value); + value = dw_readl(dev, DW_IC_FS_SCL_LCNT); + dev_err(dev->dev, "DW_IC_FS_SCL_LCNT: 0x%x\n", value); + value = dw_readl(dev, DW_IC_INTR_STAT); + dev_err(dev->dev, "DW_IC_INTR_STAT: 0x%x\n", value); + value = dw_readl(dev, DW_IC_INTR_MASK); + dev_err(dev->dev, "DW_IC_INTR_MASK: 0x%x\n", value); + value = dw_readl(dev, DW_IC_RAW_INTR_STAT); + dev_err(dev->dev, "DW_IC_RAW_INTR_STAT: 0x%x\n", value); + value = dw_readl(dev, DW_IC_RX_TL); + dev_err(dev->dev, "DW_IC_RX_TL: 0x%x\n", value); + value = dw_readl(dev, DW_IC_TX_TL); + dev_err(dev->dev, "DW_IC_TX_TL: 0x%x\n", value); + value = dw_readl(dev, DW_IC_ENABLE); + dev_err(dev->dev, "DW_IC_ENABLE: 0x%x\n", value); + value = dw_readl(dev, DW_IC_STATUS); + dev_err(dev->dev, "DW_IC_STATUS: 0x%x\n", value); + value = dw_readl(dev, DW_IC_TXFLR); + dev_err(dev->dev, "DW_IC_TXFLR: 0x%x\n", value); + value = dw_readl(dev, DW_IC_RXFLR); + dev_err(dev->dev, "DW_IC_RXFLR: 0x%x\n", value); + value = dw_readl(dev, DW_IC_TX_ABRT_SOURCE); + dev_err(dev->dev, "DW_IC_TX_ABRT_SOURCE: 0x%x\n", value); + value = dw_readl(dev, DW_IC_DATA_CMD); + dev_err(dev->dev, "DW_IC_DATA_CMD: 0x%x\n", value); + dev_err(dev->dev, "===============================\n"); +} + static u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) { @@ -366,9 +402,11 @@ 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; + unsigned long flags; intr_mask = DW_IC_INTR_DEFAULT_MASK; + raw_local_irq_save(flags); for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { /* * if target address has changed, we need to @@ -417,6 +455,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) } else dev->status &= ~STATUS_WRITE_IN_PROGRESS; } + raw_local_irq_restore(flags); /* * If i2c_msg index search is completed, we don't need TX_EMPTY @@ -501,7 +540,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); - mutex_lock(&dev->lock); + down(&dev->lock); pm_runtime_get_sync(dev->dev); INIT_COMPLETION(dev->cmd_complete); @@ -522,9 +561,10 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer_init(dev); /* wait for tx to complete */ - ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); + ret = wait_for_completion_timeout(&dev->cmd_complete, HZ); if (ret == 0) { - dev_err(dev->dev, "controller timed out\n"); + dev_WARN(dev->dev, "controller timed out\n"); + i2c_dw_dump(dev); i2c_dw_init(dev); ret = -ETIMEDOUT; goto done; @@ -552,8 +592,9 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ret = -EIO; done: - pm_runtime_put(dev->dev); - mutex_unlock(&dev->lock); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + up(&dev->lock); return ret; } diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 4a75888..119fad6 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -34,6 +34,14 @@ #define DW_IC_CON_RESTART_EN 0x20 #define DW_IC_CON_SLAVE_DISABLE 0x40 +/* + * status codes + */ +#define STATUS_POWERON 0x0 +#define STATUS_IDLE STATUS_POWERON +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + /** * struct dw_i2c_dev - private i2c-designware data @@ -65,7 +73,7 @@ struct dw_i2c_dev { struct device *dev; void __iomem *base; struct completion cmd_complete; - struct mutex lock; + struct semaphore lock; struct clk *clk; u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); struct dw_pci_controller *controller; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 837dd22..142be53 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "i2c-designware-core.h" #define DRIVER_NAME "i2c-designware-pci" @@ -95,42 +96,42 @@ static struct dw_pci_controller dw_pci_controllers[] = { .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, .tx_fifo_depth = 32, .rx_fifo_depth = 32, - .clk_khz = 25000, + .clk_khz = 17000, }, [medfield_1] = { .bus_num = 1, - .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, + .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD, .tx_fifo_depth = 32, .rx_fifo_depth = 32, - .clk_khz = 25000, + .clk_khz = 20500, }, [medfield_2] = { .bus_num = 2, .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, .tx_fifo_depth = 32, .rx_fifo_depth = 32, - .clk_khz = 25000, + .clk_khz = 17000, }, [medfield_3] = { .bus_num = 3, .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD, .tx_fifo_depth = 32, .rx_fifo_depth = 32, - .clk_khz = 25000, + .clk_khz = 17000, }, [medfield_4] = { .bus_num = 4, .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, .tx_fifo_depth = 32, .rx_fifo_depth = 32, - .clk_khz = 25000, + .clk_khz = 17000, }, [medfield_5] = { .bus_num = 5, .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, .tx_fifo_depth = 32, .rx_fifo_depth = 32, - .clk_khz = 25000, + .clk_khz = 17000, }, }; static struct i2c_algorithm i2c_dw_algo = { @@ -142,9 +143,23 @@ static int i2c_dw_pci_suspend(struct device *dev) { struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); - int err; + dev_dbg(dev, "suspend called\n"); + if (down_trylock(&i2c->lock)) + return -EBUSY; + i2c_dw_disable(i2c); + i2c->status &= ~STATUS_POWERON; + + return 0; +} + +static int i2c_dw_pci_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); + int err; + dev_dbg(dev, "runtime suspend called\n"); i2c_dw_disable(i2c); err = pci_save_state(pdev); @@ -166,40 +181,39 @@ static int i2c_dw_pci_resume(struct device *dev) { struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); - int err; - u32 enabled; - enabled = i2c_dw_is_enabled(i2c); - if (enabled) - return 0; + dev_dbg(dev, "resume called\n"); + i2c_dw_init(i2c); + i2c->status |= STATUS_POWERON; + up(&i2c->lock); + + return 0; +} +static int i2c_dw_pci_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); + int err; + + dev_dbg(dev, "runtime resume called\n"); err = pci_set_power_state(pdev, PCI_D0); if (err) { dev_err(&pdev->dev, "pci_set_power_state() failed\n"); return err; } - pci_restore_state(pdev); - i2c_dw_init(i2c); - return 0; -} - -static int i2c_dw_pci_runtime_idle(struct device *dev) -{ - int err = pm_schedule_suspend(dev, 500); - dev_dbg(dev, "runtime_idle called\n"); - if (err != 0) - return 0; - return -EBUSY; + return 0; } static const struct dev_pm_ops i2c_dw_pm_ops = { - .resume = i2c_dw_pci_resume, - .suspend = i2c_dw_pci_suspend, - SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume, - i2c_dw_pci_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(i2c_dw_pci_suspend, + i2c_dw_pci_resume) + SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, + i2c_dw_pci_runtime_resume, + NULL) }; static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) @@ -264,7 +278,8 @@ const struct pci_device_id *id) } init_completion(&dev->cmd_complete); - mutex_init(&dev->lock); + sema_init(&dev->lock, 1); + dev->status = STATUS_IDLE; dev->clk = NULL; dev->controller = controller; dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; @@ -312,6 +327,8 @@ const struct pci_device_id *id) pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 5); return 0; -- 2.7.4