2018-08-10 17:26:49 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2011-10-29 16:57:23 +07:00
|
|
|
/*
|
2017-06-22 17:17:33 +07:00
|
|
|
* Synopsys DesignWare I2C adapter driver.
|
2011-10-29 16:57:23 +07:00
|
|
|
*
|
|
|
|
* Based on the TI DAVINCI I2C adapter driver.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Texas Instruments.
|
|
|
|
* Copyright (C) 2007 MontaVista Software Inc.
|
|
|
|
* Copyright (C) 2009 Provigent Ltd.
|
|
|
|
*/
|
2017-06-14 17:43:21 +07:00
|
|
|
#include <linux/acpi.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
#include <linux/clk.h>
|
2011-10-29 16:57:23 +07:00
|
|
|
#include <linux/delay.h>
|
2015-09-24 16:06:54 +07:00
|
|
|
#include <linux/dmi.h>
|
2011-10-29 16:57:23 +07:00
|
|
|
#include <linux/err.h>
|
2017-06-14 17:43:21 +07:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/i2c.h>
|
2011-10-29 16:57:23 +07:00
|
|
|
#include <linux/interrupt.h>
|
2017-06-14 17:43:21 +07:00
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
2013-06-26 15:55:06 +07:00
|
|
|
#include <linux/of.h>
|
2017-06-14 17:43:21 +07:00
|
|
|
#include <linux/platform_data/i2c-designware.h>
|
2011-10-29 16:57:23 +07:00
|
|
|
#include <linux/platform_device.h>
|
2012-02-24 18:31:15 +07:00
|
|
|
#include <linux/pm.h>
|
2013-01-17 17:31:06 +07:00
|
|
|
#include <linux/pm_runtime.h>
|
2015-11-30 22:11:44 +07:00
|
|
|
#include <linux/property.h>
|
2016-12-27 21:22:40 +07:00
|
|
|
#include <linux/reset.h>
|
2017-06-14 17:43:21 +07:00
|
|
|
#include <linux/sched.h>
|
2011-10-29 16:57:23 +07:00
|
|
|
#include <linux/slab.h>
|
PM: i2c-designware-platdrv: Optimize power management
Optimize the power management in i2c-designware-platdrv by making it
set the DPM_FLAG_SMART_SUSPEND and DPM_FLAG_LEAVE_SUSPENDED which
allows some code to be dropped from its PM callbacks.
First, setting DPM_FLAG_SMART_SUSPEND causes the intel-lpss driver
to avoid resuming i2c-designware-platdrv devices in its ->prepare
callback, so they can stay in runtime suspend after that point even
if the direct-complete feature is not used for them.
It also causes the ACPI PM domain and the PM core to avoid invoking
"late" and "noirq" suspend callbacks for these devices if they are
in runtime suspend at the beginning of the "late" phase of device
suspend during system suspend. That guarantees dw_i2c_plat_suspend()
to be called for a device only if it is not in runtime suspend.
Moreover, it causes the device's runtime PM status to be set to
"active" after calling dw_i2c_plat_resume() for it, so the
driver doesn't need internal flags to avoid invoking either
dw_i2c_plat_suspend() or dw_i2c_plat_resume() twice in a row.
Second, setting DPM_FLAG_LEAVE_SUSPENDED enables the optimization
allowing the device to stay suspended after system resume under
suitable conditions, so again the driver doesn't need to take
care of that by itself.
Accordingly, the internal "suspended" and "skip_resume" flags
used by the driver are not necessary any more, so drop them and
simplify the driver's PM callbacks.
Additionally, notice that dw_i2c_plat_complete() only needs to
schedule runtime PM resume for the device if platform firmware
has been involved in resuming the system, so make it call
pm_resume_via_firmware() to check that. Also make it check the
runtime PM status of the device instead of its direct_complete
flag which also works if the device remained suspended due to
the DPM_FLAG_LEAVE_SUSPENDED driver flag.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Acked-by: Wolfram Sang <wsa@the-dreams.de>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
2018-01-03 07:37:34 +07:00
|
|
|
#include <linux/suspend.h>
|
2017-06-14 17:43:21 +07:00
|
|
|
|
2011-10-29 16:57:23 +07:00
|
|
|
#include "i2c-designware-core.h"
|
|
|
|
|
2011-10-07 01:26:30 +07:00
|
|
|
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
|
|
|
{
|
|
|
|
return clk_get_rate(dev->clk)/1000;
|
|
|
|
}
|
2011-10-29 16:57:23 +07:00
|
|
|
|
2013-01-17 17:31:07 +07:00
|
|
|
#ifdef CONFIG_ACPI
|
2015-09-24 16:06:54 +07:00
|
|
|
/*
|
|
|
|
* The HCNT/LCNT information coming from ACPI should be the most accurate
|
|
|
|
* for given platform. However, some systems get it wrong. On such systems
|
|
|
|
* we get better results by calculating those based on the input clock.
|
|
|
|
*/
|
|
|
|
static const struct dmi_system_id dw_i2c_no_acpi_params[] = {
|
|
|
|
{
|
|
|
|
.ident = "Dell Inspiron 7348",
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7348"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2013-08-19 19:07:54 +07:00
|
|
|
static void dw_i2c_acpi_params(struct platform_device *pdev, char method[],
|
|
|
|
u16 *hcnt, u16 *lcnt, u32 *sda_hold)
|
|
|
|
{
|
|
|
|
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
|
|
|
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
|
|
|
union acpi_object *obj;
|
|
|
|
|
2015-09-24 16:06:54 +07:00
|
|
|
if (dmi_check_system(dw_i2c_no_acpi_params))
|
|
|
|
return;
|
|
|
|
|
2013-08-19 19:07:54 +07:00
|
|
|
if (ACPI_FAILURE(acpi_evaluate_object(handle, method, 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;
|
2017-03-28 15:48:02 +07:00
|
|
|
*sda_hold = (u32)objs[2].integer.value;
|
2013-08-19 19:07:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(buf.pointer);
|
|
|
|
}
|
|
|
|
|
2013-01-17 17:31:07 +07:00
|
|
|
static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
2018-07-25 21:39:26 +07:00
|
|
|
struct i2c_timings *t = &dev->timings;
|
2017-05-22 12:46:55 +07:00
|
|
|
u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
|
2013-01-17 17:31:07 +07:00
|
|
|
|
|
|
|
dev->adapter.nr = -1;
|
|
|
|
dev->tx_fifo_depth = 32;
|
|
|
|
dev->rx_fifo_depth = 32;
|
2013-08-19 19:07:54 +07:00
|
|
|
|
|
|
|
/*
|
2017-03-28 15:48:02 +07:00
|
|
|
* Try to get SDA hold time and *CNT values from an ACPI method for
|
|
|
|
* selected speed modes.
|
2013-08-19 19:07:54 +07:00
|
|
|
*/
|
2017-05-19 15:56:40 +07:00
|
|
|
dw_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_ht);
|
|
|
|
dw_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, &fp_ht);
|
|
|
|
dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
|
|
|
|
dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
|
|
|
|
|
2018-07-25 21:39:26 +07:00
|
|
|
switch (t->bus_freq_hz) {
|
2017-03-28 15:48:02 +07:00
|
|
|
case 100000:
|
2017-05-19 15:56:40 +07:00
|
|
|
dev->sda_hold_time = ss_ht;
|
2017-03-28 15:48:02 +07:00
|
|
|
break;
|
|
|
|
case 1000000:
|
2017-05-19 15:56:40 +07:00
|
|
|
dev->sda_hold_time = fp_ht;
|
2017-03-28 15:48:02 +07:00
|
|
|
break;
|
|
|
|
case 3400000:
|
2017-05-19 15:56:40 +07:00
|
|
|
dev->sda_hold_time = hs_ht;
|
2017-03-28 15:48:02 +07:00
|
|
|
break;
|
|
|
|
case 400000:
|
|
|
|
default:
|
2017-05-19 15:56:40 +07:00
|
|
|
dev->sda_hold_time = fs_ht;
|
2017-03-28 15:48:02 +07:00
|
|
|
break;
|
|
|
|
}
|
2013-08-19 19:07:54 +07:00
|
|
|
|
2013-01-17 17:31:07 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
|
|
|
{ "INT33C2", 0 },
|
|
|
|
{ "INT33C3", 0 },
|
2013-11-12 16:57:30 +07:00
|
|
|
{ "INT3432", 0 },
|
|
|
|
{ "INT3433", 0 },
|
2018-10-06 15:25:39 +07:00
|
|
|
{ "80860F41", ACCESS_NO_IRQ_SUSPEND },
|
|
|
|
{ "808622C1", ACCESS_NO_IRQ_SUSPEND | MODEL_CHERRYTRAIL },
|
2015-12-11 19:02:53 +07:00
|
|
|
{ "AMD0010", ACCESS_INTR_MASK },
|
2016-03-10 18:34:52 +07:00
|
|
|
{ "AMDI0010", ACCESS_INTR_MASK },
|
2015-12-16 04:55:53 +07:00
|
|
|
{ "AMDI0510", 0 },
|
2015-12-11 04:19:17 +07:00
|
|
|
{ "APMC0D0F", 0 },
|
2017-04-22 10:23:44 +07:00
|
|
|
{ "HISI02A1", 0 },
|
|
|
|
{ "HISI02A2", 0 },
|
2013-01-17 17:31:07 +07:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
|
|
|
|
#else
|
|
|
|
static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-08-31 22:11:09 +07:00
|
|
|
#ifdef CONFIG_OF
|
2018-08-31 22:11:12 +07:00
|
|
|
#define MSCC_ICPU_CFG_TWI_DELAY 0x0
|
|
|
|
#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
|
|
|
|
#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
|
|
|
|
|
|
|
|
static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev)
|
|
|
|
{
|
|
|
|
writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE,
|
|
|
|
dev->ext + MSCC_ICPU_CFG_TWI_DELAY);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dw_i2c_of_configure(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
|
|
|
struct resource *mem;
|
|
|
|
|
|
|
|
switch (dev->flags & MODEL_MASK) {
|
|
|
|
case MODEL_MSCC_OCELOT:
|
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
|
|
dev->ext = devm_ioremap_resource(&pdev->dev, mem);
|
|
|
|
if (!IS_ERR(dev->ext))
|
|
|
|
dev->set_sda_hold_time = mscc_twi_set_sda_hold_time;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-31 22:11:09 +07:00
|
|
|
static const struct of_device_id dw_i2c_of_match[] = {
|
|
|
|
{ .compatible = "snps,designware-i2c", },
|
2018-08-31 22:11:12 +07:00
|
|
|
{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
|
2018-08-31 22:11:09 +07:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
|
2018-08-31 22:11:12 +07:00
|
|
|
#else
|
|
|
|
static inline int dw_i2c_of_configure(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2018-08-31 22:11:09 +07:00
|
|
|
#endif
|
|
|
|
|
2017-06-14 17:43:22 +07:00
|
|
|
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
|
|
|
|
{
|
2018-07-25 21:39:26 +07:00
|
|
|
struct i2c_timings *t = &dev->timings;
|
|
|
|
|
2017-06-22 17:17:33 +07:00
|
|
|
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
|
|
|
|
|
2017-06-14 17:43:22 +07:00
|
|
|
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
|
|
|
DW_IC_CON_RESTART_EN;
|
|
|
|
|
2017-06-22 17:17:33 +07:00
|
|
|
dev->mode = DW_IC_MASTER;
|
|
|
|
|
2018-07-25 21:39:26 +07:00
|
|
|
switch (t->bus_freq_hz) {
|
2017-06-14 17:43:22 +07:00
|
|
|
case 100000:
|
|
|
|
dev->master_cfg |= DW_IC_CON_SPEED_STD;
|
|
|
|
break;
|
|
|
|
case 3400000:
|
|
|
|
dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 17:17:33 +07:00
|
|
|
static void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
|
|
|
|
{
|
|
|
|
dev->functionality = I2C_FUNC_SLAVE | DW_IC_DEFAULT_FUNCTIONALITY;
|
|
|
|
|
|
|
|
dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
|
2017-08-09 19:24:44 +07:00
|
|
|
DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
|
2017-06-22 17:17:33 +07:00
|
|
|
|
|
|
|
dev->mode = DW_IC_SLAVE;
|
2016-01-04 22:17:35 +07:00
|
|
|
}
|
|
|
|
|
2016-12-14 16:23:58 +07:00
|
|
|
static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
|
|
|
|
{
|
|
|
|
u32 param, tx_fifo_depth, rx_fifo_depth;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to detect the FIFO depth if not set by interface driver,
|
|
|
|
* the depth could be from 2 to 256 from HW spec.
|
|
|
|
*/
|
|
|
|
param = i2c_dw_read_comp_param(dev);
|
|
|
|
tx_fifo_depth = ((param >> 16) & 0xff) + 1;
|
|
|
|
rx_fifo_depth = ((param >> 8) & 0xff) + 1;
|
|
|
|
if (!dev->tx_fifo_depth) {
|
|
|
|
dev->tx_fifo_depth = tx_fifo_depth;
|
|
|
|
dev->rx_fifo_depth = rx_fifo_depth;
|
|
|
|
dev->adapter.nr = id;
|
|
|
|
} else if (tx_fifo_depth >= 2) {
|
|
|
|
dev->tx_fifo_depth = min_t(u32, dev->tx_fifo_depth,
|
|
|
|
tx_fifo_depth);
|
|
|
|
dev->rx_fifo_depth = min_t(u32, dev->rx_fifo_depth,
|
|
|
|
rx_fifo_depth);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-26 04:10:06 +07:00
|
|
|
static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)
|
|
|
|
{
|
|
|
|
pm_runtime_disable(dev->dev);
|
|
|
|
|
2018-09-06 02:51:31 +07:00
|
|
|
if (dev->shared_with_punit)
|
2017-09-26 04:10:06 +07:00
|
|
|
pm_runtime_put_noidle(dev->dev);
|
|
|
|
}
|
|
|
|
|
2015-08-31 21:31:32 +07:00
|
|
|
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
2011-10-29 16:57:23 +07:00
|
|
|
{
|
2015-11-30 22:11:44 +07:00
|
|
|
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
2011-10-29 16:57:23 +07:00
|
|
|
struct i2c_adapter *adap;
|
2017-06-14 17:43:21 +07:00
|
|
|
struct dw_i2c_dev *dev;
|
2018-07-25 21:39:26 +07:00
|
|
|
struct i2c_timings *t;
|
|
|
|
u32 acpi_speed;
|
2017-06-14 17:43:21 +07:00
|
|
|
struct resource *mem;
|
2017-08-29 19:08:35 +07:00
|
|
|
int i, irq, ret;
|
2017-09-22 05:30:07 +07:00
|
|
|
static const int supported_speeds[] = {
|
|
|
|
0, 100000, 400000, 1000000, 3400000
|
|
|
|
};
|
2011-10-29 16:57:23 +07:00
|
|
|
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
2015-03-09 16:03:12 +07:00
|
|
|
if (irq < 0)
|
|
|
|
return irq;
|
2011-10-29 16:57:23 +07:00
|
|
|
|
2013-04-10 07:36:36 +07:00
|
|
|
dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
2011-10-29 16:57:23 +07:00
|
|
|
|
2013-05-10 15:16:54 +07:00
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
2013-04-10 07:36:36 +07:00
|
|
|
dev->base = devm_ioremap_resource(&pdev->dev, mem);
|
|
|
|
if (IS_ERR(dev->base))
|
|
|
|
return PTR_ERR(dev->base);
|
2011-10-29 16:57:23 +07:00
|
|
|
|
2013-04-10 07:36:36 +07:00
|
|
|
dev->dev = &pdev->dev;
|
2011-10-29 16:57:23 +07:00
|
|
|
dev->irq = irq;
|
|
|
|
platform_set_drvdata(pdev, dev);
|
|
|
|
|
2016-12-27 21:22:40 +07:00
|
|
|
dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
|
|
|
if (IS_ERR(dev->rst)) {
|
|
|
|
if (PTR_ERR(dev->rst) == -EPROBE_DEFER)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
} else {
|
|
|
|
reset_control_deassert(dev->rst);
|
|
|
|
}
|
|
|
|
|
2018-07-25 21:39:26 +07:00
|
|
|
t = &dev->timings;
|
|
|
|
if (pdata)
|
|
|
|
t->bus_freq_hz = pdata->i2c_scl_freq;
|
|
|
|
else
|
|
|
|
i2c_parse_fw_timings(&pdev->dev, t, false);
|
2015-11-30 22:11:44 +07:00
|
|
|
|
2016-08-12 21:02:54 +07:00
|
|
|
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
2017-08-29 19:08:35 +07:00
|
|
|
/*
|
|
|
|
* Some DSTDs use a non standard speed, round down to the lowest
|
|
|
|
* standard speed.
|
|
|
|
*/
|
|
|
|
for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) {
|
|
|
|
if (acpi_speed < supported_speeds[i])
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
acpi_speed = supported_speeds[i - 1];
|
|
|
|
|
2016-11-10 18:37:20 +07:00
|
|
|
/*
|
|
|
|
* Find bus speed from the "clock-frequency" device property, ACPI
|
|
|
|
* or by using fast mode if neither is set.
|
|
|
|
*/
|
2018-07-25 21:39:26 +07:00
|
|
|
if (acpi_speed && t->bus_freq_hz)
|
|
|
|
t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed);
|
|
|
|
else if (acpi_speed || t->bus_freq_hz)
|
|
|
|
t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
|
2016-11-10 18:37:20 +07:00
|
|
|
else
|
2018-07-25 21:39:26 +07:00
|
|
|
t->bus_freq_hz = 400000;
|
2016-08-12 21:02:54 +07:00
|
|
|
|
2018-08-31 22:11:08 +07:00
|
|
|
dev->flags |= (uintptr_t)device_get_match_data(&pdev->dev);
|
|
|
|
|
2018-08-31 22:11:12 +07:00
|
|
|
if (pdev->dev.of_node)
|
|
|
|
dw_i2c_of_configure(pdev);
|
|
|
|
|
2015-11-30 22:11:44 +07:00
|
|
|
if (has_acpi_companion(&pdev->dev))
|
|
|
|
dw_i2c_acpi_configure(pdev);
|
|
|
|
|
|
|
|
/*
|
2016-08-12 21:02:49 +07:00
|
|
|
* Only standard mode at 100kHz, fast mode at 400kHz,
|
2016-08-12 21:02:51 +07:00
|
|
|
* fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
|
2015-11-30 22:11:44 +07:00
|
|
|
*/
|
2018-07-25 21:39:26 +07:00
|
|
|
if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 &&
|
|
|
|
t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) {
|
2016-08-12 21:02:49 +07:00
|
|
|
dev_err(&pdev->dev,
|
2017-07-13 20:45:01 +07:00
|
|
|
"%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
|
2018-07-25 21:39:26 +07:00
|
|
|
t->bus_freq_hz);
|
2017-06-14 17:43:21 +07:00
|
|
|
ret = -EINVAL;
|
2016-12-27 21:22:40 +07:00
|
|
|
goto exit_reset;
|
2013-06-26 15:55:06 +07:00
|
|
|
}
|
|
|
|
|
2017-06-14 17:43:21 +07:00
|
|
|
ret = i2c_dw_probe_lock_support(dev);
|
|
|
|
if (ret)
|
2016-12-27 21:22:40 +07:00
|
|
|
goto exit_reset;
|
2015-01-15 16:12:17 +07:00
|
|
|
|
2017-06-22 17:17:33 +07:00
|
|
|
if (i2c_detect_slave_mode(&pdev->dev))
|
|
|
|
i2c_dw_configure_slave(dev);
|
|
|
|
else
|
|
|
|
i2c_dw_configure_master(dev);
|
2011-10-07 01:26:31 +07:00
|
|
|
|
2014-09-30 17:04:54 +07:00
|
|
|
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
2017-11-02 09:40:26 +07:00
|
|
|
if (!i2c_dw_prepare_clk(dev, true)) {
|
2018-07-25 21:39:26 +07:00
|
|
|
u64 clk_khz;
|
|
|
|
|
2016-01-04 22:17:35 +07:00
|
|
|
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
2018-07-25 21:39:26 +07:00
|
|
|
clk_khz = dev->get_clk_rate_khz(dev);
|
2014-09-30 17:04:54 +07:00
|
|
|
|
2018-07-25 21:39:26 +07:00
|
|
|
if (!dev->sda_hold_time && t->sda_hold_ns)
|
|
|
|
dev->sda_hold_time =
|
|
|
|
div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000);
|
2014-09-30 17:04:54 +07:00
|
|
|
}
|
|
|
|
|
2016-12-14 16:23:58 +07:00
|
|
|
dw_i2c_set_fifo_size(dev, pdev->id);
|
2011-10-29 16:57:23 +07:00
|
|
|
|
|
|
|
adap = &dev->adapter;
|
|
|
|
adap->owner = THIS_MODULE;
|
2014-07-10 18:46:26 +07:00
|
|
|
adap->class = I2C_CLASS_DEPRECATED;
|
2015-10-24 02:27:07 +07:00
|
|
|
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
2011-11-09 03:43:47 +07:00
|
|
|
adap->dev.of_node = pdev->dev.of_node;
|
2011-10-29 16:57:23 +07:00
|
|
|
|
PM: i2c-designware-platdrv: Optimize power management
Optimize the power management in i2c-designware-platdrv by making it
set the DPM_FLAG_SMART_SUSPEND and DPM_FLAG_LEAVE_SUSPENDED which
allows some code to be dropped from its PM callbacks.
First, setting DPM_FLAG_SMART_SUSPEND causes the intel-lpss driver
to avoid resuming i2c-designware-platdrv devices in its ->prepare
callback, so they can stay in runtime suspend after that point even
if the direct-complete feature is not used for them.
It also causes the ACPI PM domain and the PM core to avoid invoking
"late" and "noirq" suspend callbacks for these devices if they are
in runtime suspend at the beginning of the "late" phase of device
suspend during system suspend. That guarantees dw_i2c_plat_suspend()
to be called for a device only if it is not in runtime suspend.
Moreover, it causes the device's runtime PM status to be set to
"active" after calling dw_i2c_plat_resume() for it, so the
driver doesn't need internal flags to avoid invoking either
dw_i2c_plat_suspend() or dw_i2c_plat_resume() twice in a row.
Second, setting DPM_FLAG_LEAVE_SUSPENDED enables the optimization
allowing the device to stay suspended after system resume under
suitable conditions, so again the driver doesn't need to take
care of that by itself.
Accordingly, the internal "suspended" and "skip_resume" flags
used by the driver are not necessary any more, so drop them and
simplify the driver's PM callbacks.
Additionally, notice that dw_i2c_plat_complete() only needs to
schedule runtime PM resume for the device if platform firmware
has been involved in resuming the system, so make it call
pm_resume_via_firmware() to check that. Also make it check the
runtime PM status of the device instead of its direct_complete
flag which also works if the device remained suspended due to
the DPM_FLAG_LEAVE_SUSPENDED driver flag.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Acked-by: Wolfram Sang <wsa@the-dreams.de>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
2018-01-03 07:37:34 +07:00
|
|
|
dev_pm_set_driver_flags(&pdev->dev,
|
|
|
|
DPM_FLAG_SMART_PREPARE |
|
|
|
|
DPM_FLAG_SMART_SUSPEND |
|
|
|
|
DPM_FLAG_LEAVE_SUSPENDED);
|
2018-01-03 07:35:54 +07:00
|
|
|
|
2017-09-26 04:10:06 +07:00
|
|
|
/* The code below assumes runtime PM to be disabled. */
|
|
|
|
WARN_ON(pm_runtime_enabled(&pdev->dev));
|
|
|
|
|
|
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
|
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_set_active(&pdev->dev);
|
|
|
|
|
2018-09-06 02:51:31 +07:00
|
|
|
if (dev->shared_with_punit)
|
2017-09-26 04:10:06 +07:00
|
|
|
pm_runtime_get_noresume(&pdev->dev);
|
|
|
|
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
2013-01-17 17:31:06 +07:00
|
|
|
|
2017-06-22 17:17:33 +07:00
|
|
|
if (dev->mode == DW_IC_SLAVE)
|
|
|
|
ret = i2c_dw_probe_slave(dev);
|
|
|
|
else
|
|
|
|
ret = i2c_dw_probe(dev);
|
|
|
|
|
2017-06-14 17:43:21 +07:00
|
|
|
if (ret)
|
2016-12-27 21:22:40 +07:00
|
|
|
goto exit_probe;
|
2015-10-09 16:39:24 +07:00
|
|
|
|
2017-06-14 17:43:21 +07:00
|
|
|
return ret;
|
2016-12-27 21:22:40 +07:00
|
|
|
|
|
|
|
exit_probe:
|
2017-09-26 04:10:06 +07:00
|
|
|
dw_i2c_plat_pm_cleanup(dev);
|
2016-12-27 21:22:40 +07:00
|
|
|
exit_reset:
|
|
|
|
if (!IS_ERR_OR_NULL(dev->rst))
|
|
|
|
reset_control_assert(dev->rst);
|
2017-06-14 17:43:21 +07:00
|
|
|
return ret;
|
2011-10-29 16:57:23 +07:00
|
|
|
}
|
|
|
|
|
2015-08-31 21:31:32 +07:00
|
|
|
static int dw_i2c_plat_remove(struct platform_device *pdev)
|
2011-10-29 16:57:23 +07:00
|
|
|
{
|
|
|
|
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
|
|
|
|
2013-01-17 17:31:06 +07:00
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
|
|
|
2011-10-29 16:57:23 +07:00
|
|
|
i2c_del_adapter(&dev->adapter);
|
|
|
|
|
2017-06-14 17:43:23 +07:00
|
|
|
dev->disable(dev);
|
2011-10-29 16:57:23 +07:00
|
|
|
|
2015-06-17 16:08:38 +07:00
|
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
2017-09-26 04:10:06 +07:00
|
|
|
dw_i2c_plat_pm_cleanup(dev);
|
|
|
|
|
2016-12-27 21:22:40 +07:00
|
|
|
if (!IS_ERR_OR_NULL(dev->rst))
|
|
|
|
reset_control_assert(dev->rst);
|
2013-01-17 17:31:06 +07:00
|
|
|
|
2011-10-29 16:57:23 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-20 21:33:13 +07:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2015-08-31 21:31:32 +07:00
|
|
|
static int dw_i2c_plat_prepare(struct device *dev)
|
2015-05-20 21:33:13 +07:00
|
|
|
{
|
2018-01-03 07:35:54 +07:00
|
|
|
/*
|
|
|
|
* If the ACPI companion device object is present for this device, it
|
|
|
|
* may be accessed during suspend and resume of other devices via I2C
|
|
|
|
* operation regions, so tell the PM core and middle layers to avoid
|
|
|
|
* skipping system suspend/resume callbacks for it in that case.
|
|
|
|
*/
|
|
|
|
return !has_acpi_companion(dev);
|
2015-05-20 21:33:13 +07:00
|
|
|
}
|
|
|
|
|
2015-08-31 21:31:32 +07:00
|
|
|
static void dw_i2c_plat_complete(struct device *dev)
|
2015-05-20 21:33:13 +07:00
|
|
|
{
|
PM: i2c-designware-platdrv: Optimize power management
Optimize the power management in i2c-designware-platdrv by making it
set the DPM_FLAG_SMART_SUSPEND and DPM_FLAG_LEAVE_SUSPENDED which
allows some code to be dropped from its PM callbacks.
First, setting DPM_FLAG_SMART_SUSPEND causes the intel-lpss driver
to avoid resuming i2c-designware-platdrv devices in its ->prepare
callback, so they can stay in runtime suspend after that point even
if the direct-complete feature is not used for them.
It also causes the ACPI PM domain and the PM core to avoid invoking
"late" and "noirq" suspend callbacks for these devices if they are
in runtime suspend at the beginning of the "late" phase of device
suspend during system suspend. That guarantees dw_i2c_plat_suspend()
to be called for a device only if it is not in runtime suspend.
Moreover, it causes the device's runtime PM status to be set to
"active" after calling dw_i2c_plat_resume() for it, so the
driver doesn't need internal flags to avoid invoking either
dw_i2c_plat_suspend() or dw_i2c_plat_resume() twice in a row.
Second, setting DPM_FLAG_LEAVE_SUSPENDED enables the optimization
allowing the device to stay suspended after system resume under
suitable conditions, so again the driver doesn't need to take
care of that by itself.
Accordingly, the internal "suspended" and "skip_resume" flags
used by the driver are not necessary any more, so drop them and
simplify the driver's PM callbacks.
Additionally, notice that dw_i2c_plat_complete() only needs to
schedule runtime PM resume for the device if platform firmware
has been involved in resuming the system, so make it call
pm_resume_via_firmware() to check that. Also make it check the
runtime PM status of the device instead of its direct_complete
flag which also works if the device remained suspended due to
the DPM_FLAG_LEAVE_SUSPENDED driver flag.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Acked-by: Wolfram Sang <wsa@the-dreams.de>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
2018-01-03 07:37:34 +07:00
|
|
|
/*
|
|
|
|
* The device can only be in runtime suspend at this point if it has not
|
|
|
|
* been resumed throughout the ending system suspend/resume cycle, so if
|
|
|
|
* the platform firmware might mess up with it, request the runtime PM
|
|
|
|
* framework to resume it.
|
|
|
|
*/
|
|
|
|
if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
|
2015-05-20 21:33:13 +07:00
|
|
|
pm_request_resume(dev);
|
|
|
|
}
|
|
|
|
#else
|
2015-10-21 14:09:17 +07:00
|
|
|
#define dw_i2c_plat_prepare NULL
|
|
|
|
#define dw_i2c_plat_complete NULL
|
2015-05-20 21:33:13 +07:00
|
|
|
#endif
|
|
|
|
|
2014-05-15 21:37:23 +07:00
|
|
|
#ifdef CONFIG_PM
|
PM: i2c-designware-platdrv: Suspend/resume at the late/early stages
As reported by Rajat Jain, there are problems when ACPI operation
region handlers or similar, called at the ->resume_early() time, for
I2C client devices try to access an I2C controller that has already
been suspended at that point. To avoid that, move the suspend/resume
of i2c-designware-platdrv to the late/early stages, respectively.
While at it, avoid resuming the device from runtime suspend in the
driver's ->suspend callback which isn't particularly nice. [A better
approach would be to make the driver track the PM state of the device
so that it doesn't need to resume it in ->suspend, so implement it.]
First, drop dw_i2c_plat_suspend() added by commit a23318feeff6 (i2c:
designware: Fix system suspend) and rename dw_i2c_plat_runtime_suspend()
back to dw_i2c_plat_suspend().
Second, point the driver's ->late_suspend and ->early_resume
callbacks, rather than its ->suspend and ->resume callbacks,
to dw_i2c_plat_suspend() and dw_i2c_plat_resume(), respectively,
so that they are not executed in parallel with each other, for
example if runtime resume of the device takes place during system
suspend.
Finally, add "suspended" and "skip_resume" flags to struct dw_i2c_dev
and make dw_i2c_plat_suspend() and dw_i2c_plat_resume() use them to
avoid suspending or resuming the device twice in a row and to avoid
resuming a previously runtime-suspended device during system resume.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: Johannes Stezenbach <js@sig21.net>
Tested-by: Rajat Jain <rajatja@google.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2017-09-25 06:30:51 +07:00
|
|
|
static int dw_i2c_plat_suspend(struct device *dev)
|
2012-02-24 18:31:15 +07:00
|
|
|
{
|
2017-07-27 23:16:24 +07:00
|
|
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
2012-02-24 18:31:15 +07:00
|
|
|
|
2018-09-06 02:51:31 +07:00
|
|
|
if (i_dev->shared_with_punit)
|
i2c: designware: Re-init controllers with pm_disabled set on resume
On Bay Trail and Cherry Trail devices we set the pm_disabled flag for I2C
busses which the OS shares with the PUNIT as these need special handling.
Until now we called dev_pm_syscore_device(dev, true) for I2C controllers
with this flag set to keep these I2C controllers always on.
After commit 12864ff8545f ("ACPI / LPSS: Avoid PM quirks on suspend and
resume from hibernation"), this no longer works. This commit modifies
lpss_iosf_exit_d3_state() to only run if lpss_iosf_enter_d3_state() has ran
before it, so that it does not run on a resume from hibernate (or from S3).
On these systems the conditions for lpss_iosf_enter_d3_state() to run
never become true, so lpss_iosf_exit_d3_state() never gets called and
the 2 LPSS DMA controllers never get forced into D0 mode, instead they
are left in their default automatic power-on when needed mode.
The not forcing of D0 mode for the DMA controllers enables these systems
to properly enter S0ix modes, which is a good thing.
But after entering S0ix modes the I2C controller connected to the PMIC
no longer works, leading to e.g. broken battery monitoring.
The _PS3 method for this I2C controller looks like this:
Method (_PS3, 0, NotSerialized) // _PS3: Power State 3
{
If ((((PMID == 0x04) || (PMID == 0x05)) || (PMID == 0x06)))
{
Return (Zero)
}
PSAT |= 0x03
Local0 = PSAT /* \_SB_.I2C5.PSAT */
}
Where PMID = 0x05, so we enter the Return (Zero) path on these systems.
So even if we were to not call dev_pm_syscore_device(dev, true) the
I2C controller will be left in D0 rather then be switched to D3.
Yet on other Bay and Cherry Trail devices S0ix is not entered unless *all*
I2C controllers are in D3 mode. This combined with the I2C controller no
longer working now that we reach S0ix states on these systems leads to me
believing that the PUNIT itself puts the I2C controller in D3 when all
other conditions for entering S0ix states are true.
Since now the I2C controller is put in D3 over a suspend/resume we must
re-initialize it afterwards and that does indeed fix it no longer working.
This commit implements this fix by:
1) Making the suspend_late callback a no-op if pm_disabled is set and
making the resume_early callback skip the clock re-enable (since it now was
not disabled) while still doing the necessary I2C controller re-init.
2) Removing the dev_pm_syscore_device(dev, true) call, so that the suspend
and resume callbacks are actually called. Normally this would cause the
ACPI pm code to call _PS3 putting the I2C controller in D3, wreaking havoc
since it is shared with the PUNIT, but in this special case the _PS3 method
is a no-op so we can safely allow a "fake" suspend / resume.
Fixes: 12864ff8545f ("ACPI / LPSS: Avoid PM quirks on suspend and resume ...")
Link: https://bugzilla.kernel.org/show_bug.cgi?id=200861
Cc: 4.15+ <stable@vger.kernel.org> # 4.15+
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2018-08-29 20:06:31 +07:00
|
|
|
return 0;
|
|
|
|
|
2017-06-14 17:43:23 +07:00
|
|
|
i_dev->disable(i_dev);
|
2017-11-02 09:40:26 +07:00
|
|
|
i2c_dw_prepare_clk(i_dev, false);
|
2012-02-24 18:31:15 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-31 21:31:32 +07:00
|
|
|
static int dw_i2c_plat_resume(struct device *dev)
|
2012-02-24 18:31:15 +07:00
|
|
|
{
|
2017-07-27 23:16:24 +07:00
|
|
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
2012-02-24 18:31:15 +07:00
|
|
|
|
2018-09-06 02:51:31 +07:00
|
|
|
if (!i_dev->shared_with_punit)
|
i2c: designware: Re-init controllers with pm_disabled set on resume
On Bay Trail and Cherry Trail devices we set the pm_disabled flag for I2C
busses which the OS shares with the PUNIT as these need special handling.
Until now we called dev_pm_syscore_device(dev, true) for I2C controllers
with this flag set to keep these I2C controllers always on.
After commit 12864ff8545f ("ACPI / LPSS: Avoid PM quirks on suspend and
resume from hibernation"), this no longer works. This commit modifies
lpss_iosf_exit_d3_state() to only run if lpss_iosf_enter_d3_state() has ran
before it, so that it does not run on a resume from hibernate (or from S3).
On these systems the conditions for lpss_iosf_enter_d3_state() to run
never become true, so lpss_iosf_exit_d3_state() never gets called and
the 2 LPSS DMA controllers never get forced into D0 mode, instead they
are left in their default automatic power-on when needed mode.
The not forcing of D0 mode for the DMA controllers enables these systems
to properly enter S0ix modes, which is a good thing.
But after entering S0ix modes the I2C controller connected to the PMIC
no longer works, leading to e.g. broken battery monitoring.
The _PS3 method for this I2C controller looks like this:
Method (_PS3, 0, NotSerialized) // _PS3: Power State 3
{
If ((((PMID == 0x04) || (PMID == 0x05)) || (PMID == 0x06)))
{
Return (Zero)
}
PSAT |= 0x03
Local0 = PSAT /* \_SB_.I2C5.PSAT */
}
Where PMID = 0x05, so we enter the Return (Zero) path on these systems.
So even if we were to not call dev_pm_syscore_device(dev, true) the
I2C controller will be left in D0 rather then be switched to D3.
Yet on other Bay and Cherry Trail devices S0ix is not entered unless *all*
I2C controllers are in D3 mode. This combined with the I2C controller no
longer working now that we reach S0ix states on these systems leads to me
believing that the PUNIT itself puts the I2C controller in D3 when all
other conditions for entering S0ix states are true.
Since now the I2C controller is put in D3 over a suspend/resume we must
re-initialize it afterwards and that does indeed fix it no longer working.
This commit implements this fix by:
1) Making the suspend_late callback a no-op if pm_disabled is set and
making the resume_early callback skip the clock re-enable (since it now was
not disabled) while still doing the necessary I2C controller re-init.
2) Removing the dev_pm_syscore_device(dev, true) call, so that the suspend
and resume callbacks are actually called. Normally this would cause the
ACPI pm code to call _PS3 putting the I2C controller in D3, wreaking havoc
since it is shared with the PUNIT, but in this special case the _PS3 method
is a no-op so we can safely allow a "fake" suspend / resume.
Fixes: 12864ff8545f ("ACPI / LPSS: Avoid PM quirks on suspend and resume ...")
Link: https://bugzilla.kernel.org/show_bug.cgi?id=200861
Cc: 4.15+ <stable@vger.kernel.org> # 4.15+
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2018-08-29 20:06:31 +07:00
|
|
|
i2c_dw_prepare_clk(i_dev, true);
|
|
|
|
|
2017-06-14 17:43:23 +07:00
|
|
|
i_dev->init(i_dev);
|
2012-02-24 18:31:15 +07:00
|
|
|
|
PM: i2c-designware-platdrv: Suspend/resume at the late/early stages
As reported by Rajat Jain, there are problems when ACPI operation
region handlers or similar, called at the ->resume_early() time, for
I2C client devices try to access an I2C controller that has already
been suspended at that point. To avoid that, move the suspend/resume
of i2c-designware-platdrv to the late/early stages, respectively.
While at it, avoid resuming the device from runtime suspend in the
driver's ->suspend callback which isn't particularly nice. [A better
approach would be to make the driver track the PM state of the device
so that it doesn't need to resume it in ->suspend, so implement it.]
First, drop dw_i2c_plat_suspend() added by commit a23318feeff6 (i2c:
designware: Fix system suspend) and rename dw_i2c_plat_runtime_suspend()
back to dw_i2c_plat_suspend().
Second, point the driver's ->late_suspend and ->early_resume
callbacks, rather than its ->suspend and ->resume callbacks,
to dw_i2c_plat_suspend() and dw_i2c_plat_resume(), respectively,
so that they are not executed in parallel with each other, for
example if runtime resume of the device takes place during system
suspend.
Finally, add "suspended" and "skip_resume" flags to struct dw_i2c_dev
and make dw_i2c_plat_suspend() and dw_i2c_plat_resume() use them to
avoid suspending or resuming the device twice in a row and to avoid
resuming a previously runtime-suspended device during system resume.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: Johannes Stezenbach <js@sig21.net>
Tested-by: Rajat Jain <rajatja@google.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2017-09-25 06:30:51 +07:00
|
|
|
return 0;
|
i2c: designware: Fix system suspend
The commit 8503ff166504 ("i2c: designware: Avoid unnecessary resuming
during system suspend"), may suggest to the PM core to try out the so
called direct_complete path for system sleep. In this path, the PM core
treats a runtime suspended device as it's already in a proper low power
state for system sleep, which makes it skip calling the system sleep
callbacks for the device, except for the ->prepare() and the ->complete()
callbacks.
However, the PM core may unset the direct_complete flag for a parent
device, in case its child device are being system suspended before. In this
scenario, the PM core invokes the system sleep callbacks, no matter if the
device is runtime suspended or not.
Particularly in cases of an existing i2c slave device, the above path is
triggered, which breaks the assumption that the i2c device is always
runtime resumed whenever the dw_i2c_plat_suspend() is being called.
More precisely, dw_i2c_plat_suspend() calls clk_core_disable() and
clk_core_unprepare(), for an already disabled/unprepared clock, leading to
a splat in the log about clocks calls being wrongly balanced and breaking
system sleep.
To still allow the direct_complete path in cases when it's possible, but
also to keep the fix simple, let's runtime resume the i2c device in the
->suspend() callback, before continuing to put the device into low power
state.
Note, in cases when the i2c device is attached to the ACPI PM domain, this
problem doesn't occur, because ACPI's ->suspend() callback, assigned to
acpi_subsys_suspend(), already calls pm_runtime_resume() for the device.
It should also be noted that this change does not fix commit 8503ff166504
("i2c: designware: Avoid unnecessary resuming during system suspend").
Because for the non-ACPI case, the system sleep support was already broken
prior that point.
Cc: <stable@vger.kernel.org> # v4.4+
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: John Stultz <john.stultz@linaro.org>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2017-08-09 20:28:22 +07:00
|
|
|
}
|
|
|
|
|
2015-05-20 21:33:13 +07:00
|
|
|
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
2015-08-31 21:31:32 +07:00
|
|
|
.prepare = dw_i2c_plat_prepare,
|
|
|
|
.complete = dw_i2c_plat_complete,
|
PM: i2c-designware-platdrv: Suspend/resume at the late/early stages
As reported by Rajat Jain, there are problems when ACPI operation
region handlers or similar, called at the ->resume_early() time, for
I2C client devices try to access an I2C controller that has already
been suspended at that point. To avoid that, move the suspend/resume
of i2c-designware-platdrv to the late/early stages, respectively.
While at it, avoid resuming the device from runtime suspend in the
driver's ->suspend callback which isn't particularly nice. [A better
approach would be to make the driver track the PM state of the device
so that it doesn't need to resume it in ->suspend, so implement it.]
First, drop dw_i2c_plat_suspend() added by commit a23318feeff6 (i2c:
designware: Fix system suspend) and rename dw_i2c_plat_runtime_suspend()
back to dw_i2c_plat_suspend().
Second, point the driver's ->late_suspend and ->early_resume
callbacks, rather than its ->suspend and ->resume callbacks,
to dw_i2c_plat_suspend() and dw_i2c_plat_resume(), respectively,
so that they are not executed in parallel with each other, for
example if runtime resume of the device takes place during system
suspend.
Finally, add "suspended" and "skip_resume" flags to struct dw_i2c_dev
and make dw_i2c_plat_suspend() and dw_i2c_plat_resume() use them to
avoid suspending or resuming the device twice in a row and to avoid
resuming a previously runtime-suspended device during system resume.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: Johannes Stezenbach <js@sig21.net>
Tested-by: Rajat Jain <rajatja@google.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2017-09-25 06:30:51 +07:00
|
|
|
SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
|
|
|
|
SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
|
2015-05-20 21:33:13 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
|
|
|
|
#else
|
|
|
|
#define DW_I2C_DEV_PMOPS NULL
|
|
|
|
#endif
|
2014-05-15 21:37:23 +07:00
|
|
|
|
2017-06-14 17:43:21 +07:00
|
|
|
/* Work with hotplug and coldplug */
|
2011-10-29 16:57:23 +07:00
|
|
|
MODULE_ALIAS("platform:i2c_designware");
|
|
|
|
|
|
|
|
static struct platform_driver dw_i2c_driver = {
|
2015-08-31 21:31:32 +07:00
|
|
|
.probe = dw_i2c_plat_probe,
|
|
|
|
.remove = dw_i2c_plat_remove,
|
2011-10-29 16:57:23 +07:00
|
|
|
.driver = {
|
|
|
|
.name = "i2c_designware",
|
2011-11-09 03:43:47 +07:00
|
|
|
.of_match_table = of_match_ptr(dw_i2c_of_match),
|
2013-01-17 17:31:07 +07:00
|
|
|
.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),
|
2015-05-20 21:33:13 +07:00
|
|
|
.pm = DW_I2C_DEV_PMOPS,
|
2011-10-29 16:57:23 +07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init dw_i2c_init_driver(void)
|
|
|
|
{
|
2013-10-09 03:35:33 +07:00
|
|
|
return platform_driver_register(&dw_i2c_driver);
|
2011-10-29 16:57:23 +07:00
|
|
|
}
|
2012-02-29 13:57:46 +07:00
|
|
|
subsys_initcall(dw_i2c_init_driver);
|
2011-10-29 16:57:23 +07:00
|
|
|
|
|
|
|
static void __exit dw_i2c_exit_driver(void)
|
|
|
|
{
|
|
|
|
platform_driver_unregister(&dw_i2c_driver);
|
|
|
|
}
|
|
|
|
module_exit(dw_i2c_exit_driver);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
|
|
|
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
|
|
|
|
MODULE_LICENSE("GPL");
|