#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/semaphore.h>
#include "i2c-designware-core.h"
/*
#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 */
/*
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)
{
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
} 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
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);
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;
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;
}
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
+#include <linux/semaphore.h>
#include "i2c-designware-core.h"
#define DRIVER_NAME "i2c-designware-pci"
.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 = {
{
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);
{
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)
}
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;
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;