2011-10-29 17:57:23 +08:00
|
|
|
/*
|
2017-06-22 18:17:33 +08:00
|
|
|
* Synopsys DesignWare I2C adapter driver.
|
2011-10-29 17:57:23 +08: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.
|
|
|
|
*
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
*/
|
2017-06-14 18:43:21 +08:00
|
|
|
#include <linux/acpi.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
#include <linux/clk.h>
|
2011-10-29 17:57:23 +08:00
|
|
|
#include <linux/delay.h>
|
2015-09-24 17:06:54 +08:00
|
|
|
#include <linux/dmi.h>
|
2011-10-29 17:57:23 +08:00
|
|
|
#include <linux/err.h>
|
2017-06-14 18:43:21 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/i2c.h>
|
2011-10-29 17:57:23 +08:00
|
|
|
#include <linux/interrupt.h>
|
2017-06-14 18:43:21 +08:00
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
2013-06-26 16:55:06 +08:00
|
|
|
#include <linux/of.h>
|
2017-06-14 18:43:21 +08:00
|
|
|
#include <linux/platform_data/i2c-designware.h>
|
2011-10-29 17:57:23 +08:00
|
|
|
#include <linux/platform_device.h>
|
2012-02-24 19:31:15 +08:00
|
|
|
#include <linux/pm.h>
|
2013-01-17 18:31:06 +08:00
|
|
|
#include <linux/pm_runtime.h>
|
2015-11-30 23:11:44 +08:00
|
|
|
#include <linux/property.h>
|
2016-12-27 22:22:40 +08:00
|
|
|
#include <linux/reset.h>
|
2017-06-14 18:43:21 +08:00
|
|
|
#include <linux/sched.h>
|
2011-10-29 17:57:23 +08:00
|
|
|
#include <linux/slab.h>
|
2017-06-14 18:43:21 +08:00
|
|
|
|
2011-10-29 17:57:23 +08:00
|
|
|
#include "i2c-designware-core.h"
|
|
|
|
|
2011-10-07 02:26:30 +08:00
|
|
|
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
|
|
|
{
|
|
|
|
return clk_get_rate(dev->clk)/1000;
|
|
|
|
}
|
2011-10-29 17:57:23 +08:00
|
|
|
|
2013-01-17 18:31:07 +08:00
|
|
|
#ifdef CONFIG_ACPI
|
2015-09-24 17:06:54 +08: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 20:07:54 +08: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 17:06:54 +08:00
|
|
|
if (dmi_check_system(dw_i2c_no_acpi_params))
|
|
|
|
return;
|
|
|
|
|
2013-08-19 20:07:54 +08: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 16:48:02 +08:00
|
|
|
*sda_hold = (u32)objs[2].integer.value;
|
2013-08-19 20:07:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(buf.pointer);
|
|
|
|
}
|
|
|
|
|
2013-01-17 18:31:07 +08:00
|
|
|
static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
2017-05-22 13:46:55 +08:00
|
|
|
u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
|
2017-03-14 06:25:10 +08:00
|
|
|
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
2015-12-11 20:02:53 +08:00
|
|
|
const struct acpi_device_id *id;
|
2017-03-14 06:25:10 +08:00
|
|
|
struct acpi_device *adev;
|
|
|
|
const char *uid;
|
2013-01-17 18:31:07 +08:00
|
|
|
|
|
|
|
dev->adapter.nr = -1;
|
|
|
|
dev->tx_fifo_depth = 32;
|
|
|
|
dev->rx_fifo_depth = 32;
|
2013-08-19 20:07:54 +08:00
|
|
|
|
|
|
|
/*
|
2017-03-28 16:48:02 +08:00
|
|
|
* Try to get SDA hold time and *CNT values from an ACPI method for
|
|
|
|
* selected speed modes.
|
2013-08-19 20:07:54 +08:00
|
|
|
*/
|
2017-05-19 16:56:40 +08: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);
|
|
|
|
|
2017-03-28 16:48:02 +08:00
|
|
|
switch (dev->clk_freq) {
|
|
|
|
case 100000:
|
2017-05-19 16:56:40 +08:00
|
|
|
dev->sda_hold_time = ss_ht;
|
2017-03-28 16:48:02 +08:00
|
|
|
break;
|
|
|
|
case 1000000:
|
2017-05-19 16:56:40 +08:00
|
|
|
dev->sda_hold_time = fp_ht;
|
2017-03-28 16:48:02 +08:00
|
|
|
break;
|
|
|
|
case 3400000:
|
2017-05-19 16:56:40 +08:00
|
|
|
dev->sda_hold_time = hs_ht;
|
2017-03-28 16:48:02 +08:00
|
|
|
break;
|
|
|
|
case 400000:
|
|
|
|
default:
|
2017-05-19 16:56:40 +08:00
|
|
|
dev->sda_hold_time = fs_ht;
|
2017-03-28 16:48:02 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-08-19 20:07:54 +08:00
|
|
|
|
2015-12-11 20:02:53 +08:00
|
|
|
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
|
|
|
if (id && id->driver_data)
|
2017-02-10 18:27:53 +08:00
|
|
|
dev->flags |= (u32)id->driver_data;
|
2015-12-11 20:02:53 +08:00
|
|
|
|
2017-03-14 06:25:10 +08:00
|
|
|
if (acpi_bus_get_device(handle, &adev))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cherrytrail I2C7 gets used for the PMIC which gets accessed
|
|
|
|
* through ACPI opregions during late suspend / early resume
|
|
|
|
* disable pm for it.
|
|
|
|
*/
|
|
|
|
uid = adev->pnp.unique_id;
|
|
|
|
if ((dev->flags & MODEL_CHERRYTRAIL) && !strcmp(uid, "7"))
|
|
|
|
dev->pm_disabled = true;
|
|
|
|
|
2013-01-17 18:31:07 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
|
|
|
{ "INT33C2", 0 },
|
|
|
|
{ "INT33C3", 0 },
|
2013-11-12 17:57:30 +08:00
|
|
|
{ "INT3432", 0 },
|
|
|
|
{ "INT3433", 0 },
|
2013-05-13 08:54:31 +08:00
|
|
|
{ "80860F41", 0 },
|
2017-02-10 18:27:58 +08:00
|
|
|
{ "808622C1", MODEL_CHERRYTRAIL },
|
2015-12-11 20:02:53 +08:00
|
|
|
{ "AMD0010", ACCESS_INTR_MASK },
|
2016-03-10 19:34:52 +08:00
|
|
|
{ "AMDI0010", ACCESS_INTR_MASK },
|
2015-12-16 05:55:53 +08:00
|
|
|
{ "AMDI0510", 0 },
|
2015-12-11 05:19:17 +08:00
|
|
|
{ "APMC0D0F", 0 },
|
2017-04-22 11:23:44 +08:00
|
|
|
{ "HISI02A1", 0 },
|
|
|
|
{ "HISI02A2", 0 },
|
2013-01-17 18:31:07 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
|
|
|
|
#else
|
|
|
|
static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-06-14 18:43:22 +08:00
|
|
|
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
|
|
|
|
{
|
2017-06-22 18:17:33 +08:00
|
|
|
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
|
|
|
|
|
2017-06-14 18:43:22 +08:00
|
|
|
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
|
|
|
DW_IC_CON_RESTART_EN;
|
|
|
|
|
2017-06-22 18:17:33 +08:00
|
|
|
dev->mode = DW_IC_MASTER;
|
|
|
|
|
2017-06-14 18:43:22 +08:00
|
|
|
switch (dev->clk_freq) {
|
|
|
|
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 18:17:33 +08: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 20:24:44 +08:00
|
|
|
DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
|
2017-06-22 18:17:33 +08:00
|
|
|
|
|
|
|
dev->mode = DW_IC_SLAVE;
|
|
|
|
|
|
|
|
switch (dev->clk_freq) {
|
|
|
|
case 100000:
|
|
|
|
dev->slave_cfg |= DW_IC_CON_SPEED_STD;
|
|
|
|
break;
|
|
|
|
case 3400000:
|
|
|
|
dev->slave_cfg |= DW_IC_CON_SPEED_HIGH;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev->slave_cfg |= DW_IC_CON_SPEED_FAST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-04 23:17:35 +08:00
|
|
|
static int i2c_dw_plat_prepare_clk(struct dw_i2c_dev *i_dev, bool prepare)
|
|
|
|
{
|
|
|
|
if (IS_ERR(i_dev->clk))
|
|
|
|
return PTR_ERR(i_dev->clk);
|
|
|
|
|
|
|
|
if (prepare)
|
|
|
|
return clk_prepare_enable(i_dev->clk);
|
|
|
|
|
|
|
|
clk_disable_unprepare(i_dev->clk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-14 17:23:58 +08: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 05:10:06 +08:00
|
|
|
static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)
|
|
|
|
{
|
|
|
|
pm_runtime_disable(dev->dev);
|
|
|
|
|
|
|
|
if (dev->pm_disabled)
|
|
|
|
pm_runtime_put_noidle(dev->dev);
|
|
|
|
}
|
|
|
|
|
2015-08-31 22:31:32 +08:00
|
|
|
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
2011-10-29 17:57:23 +08:00
|
|
|
{
|
2015-11-30 23:11:44 +08:00
|
|
|
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
2011-10-29 17:57:23 +08:00
|
|
|
struct i2c_adapter *adap;
|
2017-06-14 18:43:21 +08:00
|
|
|
struct dw_i2c_dev *dev;
|
2016-08-12 22:02:54 +08:00
|
|
|
u32 acpi_speed, ht = 0;
|
2017-06-14 18:43:21 +08:00
|
|
|
struct resource *mem;
|
2017-08-29 20:08:35 +08:00
|
|
|
int i, irq, ret;
|
|
|
|
const int supported_speeds[] = { 0, 100000, 400000, 1000000, 3400000 };
|
2011-10-29 17:57:23 +08:00
|
|
|
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
2015-03-09 17:03:12 +08:00
|
|
|
if (irq < 0)
|
|
|
|
return irq;
|
2011-10-29 17:57:23 +08:00
|
|
|
|
2013-04-10 08:36:36 +08:00
|
|
|
dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
2011-10-29 17:57:23 +08:00
|
|
|
|
2013-05-10 16:16:54 +08:00
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
2013-04-10 08:36:36 +08:00
|
|
|
dev->base = devm_ioremap_resource(&pdev->dev, mem);
|
|
|
|
if (IS_ERR(dev->base))
|
|
|
|
return PTR_ERR(dev->base);
|
2011-10-29 17:57:23 +08:00
|
|
|
|
2013-04-10 08:36:36 +08:00
|
|
|
dev->dev = &pdev->dev;
|
2011-10-29 17:57:23 +08:00
|
|
|
dev->irq = irq;
|
|
|
|
platform_set_drvdata(pdev, dev);
|
|
|
|
|
2016-12-27 22:22:40 +08: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);
|
|
|
|
}
|
|
|
|
|
2015-11-30 23:11:44 +08:00
|
|
|
if (pdata) {
|
2016-08-12 22:02:47 +08:00
|
|
|
dev->clk_freq = pdata->i2c_scl_freq;
|
2014-09-03 10:41:38 +08:00
|
|
|
} else {
|
2015-11-30 23:11:44 +08:00
|
|
|
device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns",
|
|
|
|
&ht);
|
|
|
|
device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns",
|
|
|
|
&dev->sda_falling_time);
|
|
|
|
device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns",
|
|
|
|
&dev->scl_falling_time);
|
|
|
|
device_property_read_u32(&pdev->dev, "clock-frequency",
|
2016-08-12 22:02:47 +08:00
|
|
|
&dev->clk_freq);
|
2015-11-30 23:11:44 +08:00
|
|
|
}
|
|
|
|
|
2016-08-12 22:02:54 +08:00
|
|
|
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
2017-08-29 20:08:35 +08: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 19:37:20 +08:00
|
|
|
/*
|
|
|
|
* Find bus speed from the "clock-frequency" device property, ACPI
|
|
|
|
* or by using fast mode if neither is set.
|
|
|
|
*/
|
|
|
|
if (acpi_speed && dev->clk_freq)
|
|
|
|
dev->clk_freq = min(dev->clk_freq, acpi_speed);
|
|
|
|
else if (acpi_speed || dev->clk_freq)
|
|
|
|
dev->clk_freq = max(dev->clk_freq, acpi_speed);
|
|
|
|
else
|
|
|
|
dev->clk_freq = 400000;
|
2016-08-12 22:02:54 +08:00
|
|
|
|
2015-11-30 23:11:44 +08:00
|
|
|
if (has_acpi_companion(&pdev->dev))
|
|
|
|
dw_i2c_acpi_configure(pdev);
|
|
|
|
|
|
|
|
/*
|
2016-08-12 22:02:49 +08:00
|
|
|
* Only standard mode at 100kHz, fast mode at 400kHz,
|
2016-08-12 22:02:51 +08:00
|
|
|
* fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
|
2015-11-30 23:11:44 +08:00
|
|
|
*/
|
2016-08-12 22:02:49 +08:00
|
|
|
if (dev->clk_freq != 100000 && dev->clk_freq != 400000
|
2016-08-12 22:02:51 +08:00
|
|
|
&& dev->clk_freq != 1000000 && dev->clk_freq != 3400000) {
|
2016-08-12 22:02:49 +08:00
|
|
|
dev_err(&pdev->dev,
|
2017-07-13 21:45:01 +08:00
|
|
|
"%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
|
|
|
|
dev->clk_freq);
|
2017-06-14 18:43:21 +08:00
|
|
|
ret = -EINVAL;
|
2016-12-27 22:22:40 +08:00
|
|
|
goto exit_reset;
|
2013-06-26 16:55:06 +08:00
|
|
|
}
|
|
|
|
|
2017-06-14 18:43:21 +08:00
|
|
|
ret = i2c_dw_probe_lock_support(dev);
|
|
|
|
if (ret)
|
2016-12-27 22:22:40 +08:00
|
|
|
goto exit_reset;
|
2015-01-15 17:12:17 +08:00
|
|
|
|
2017-06-22 18:17:33 +08:00
|
|
|
if (i2c_detect_slave_mode(&pdev->dev))
|
|
|
|
i2c_dw_configure_slave(dev);
|
|
|
|
else
|
|
|
|
i2c_dw_configure_master(dev);
|
2011-10-07 02:26:31 +08:00
|
|
|
|
2014-09-30 18:04:54 +08:00
|
|
|
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
2016-01-04 23:17:35 +08:00
|
|
|
if (!i2c_dw_plat_prepare_clk(dev, true)) {
|
|
|
|
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
2014-09-30 18:04:54 +08:00
|
|
|
|
2016-01-04 23:17:35 +08:00
|
|
|
if (!dev->sda_hold_time && ht)
|
|
|
|
dev->sda_hold_time = div_u64(
|
|
|
|
(u64)dev->get_clk_rate_khz(dev) * ht + 500000,
|
|
|
|
1000000);
|
2014-09-30 18:04:54 +08:00
|
|
|
}
|
|
|
|
|
2016-12-14 17:23:58 +08:00
|
|
|
dw_i2c_set_fifo_size(dev, pdev->id);
|
2011-10-29 17:57:23 +08:00
|
|
|
|
|
|
|
adap = &dev->adapter;
|
|
|
|
adap->owner = THIS_MODULE;
|
2014-07-10 19:46:26 +08:00
|
|
|
adap->class = I2C_CLASS_DEPRECATED;
|
2015-10-24 03:27:07 +08:00
|
|
|
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
2011-11-09 04:43:47 +08:00
|
|
|
adap->dev.of_node = pdev->dev.of_node;
|
2011-10-29 17:57:23 +08:00
|
|
|
|
2017-09-26 05:10:06 +08: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);
|
|
|
|
|
|
|
|
if (dev->pm_disabled)
|
|
|
|
pm_runtime_get_noresume(&pdev->dev);
|
|
|
|
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
2013-01-17 18:31:06 +08:00
|
|
|
|
2017-06-22 18:17:33 +08:00
|
|
|
if (dev->mode == DW_IC_SLAVE)
|
|
|
|
ret = i2c_dw_probe_slave(dev);
|
|
|
|
else
|
|
|
|
ret = i2c_dw_probe(dev);
|
|
|
|
|
2017-06-14 18:43:21 +08:00
|
|
|
if (ret)
|
2016-12-27 22:22:40 +08:00
|
|
|
goto exit_probe;
|
2015-10-09 17:39:24 +08:00
|
|
|
|
2017-06-14 18:43:21 +08:00
|
|
|
return ret;
|
2016-12-27 22:22:40 +08:00
|
|
|
|
|
|
|
exit_probe:
|
2017-09-26 05:10:06 +08:00
|
|
|
dw_i2c_plat_pm_cleanup(dev);
|
2016-12-27 22:22:40 +08:00
|
|
|
exit_reset:
|
|
|
|
if (!IS_ERR_OR_NULL(dev->rst))
|
|
|
|
reset_control_assert(dev->rst);
|
2017-06-14 18:43:21 +08:00
|
|
|
return ret;
|
2011-10-29 17:57:23 +08:00
|
|
|
}
|
|
|
|
|
2015-08-31 22:31:32 +08:00
|
|
|
static int dw_i2c_plat_remove(struct platform_device *pdev)
|
2011-10-29 17:57:23 +08:00
|
|
|
{
|
|
|
|
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
|
|
|
|
2013-01-17 18:31:06 +08:00
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
|
|
|
2011-10-29 17:57:23 +08:00
|
|
|
i2c_del_adapter(&dev->adapter);
|
|
|
|
|
2017-06-14 18:43:23 +08:00
|
|
|
dev->disable(dev);
|
2011-10-29 17:57:23 +08:00
|
|
|
|
2015-06-17 17:08:38 +08:00
|
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
2017-09-26 05:10:06 +08:00
|
|
|
dw_i2c_plat_pm_cleanup(dev);
|
|
|
|
|
2016-12-27 22:22:40 +08:00
|
|
|
if (!IS_ERR_OR_NULL(dev->rst))
|
|
|
|
reset_control_assert(dev->rst);
|
2013-01-17 18:31:06 +08:00
|
|
|
|
2017-02-10 18:27:56 +08:00
|
|
|
i2c_dw_remove_lock_support(dev);
|
|
|
|
|
2011-10-29 17:57:23 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-09 04:43:47 +08:00
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static const struct of_device_id dw_i2c_of_match[] = {
|
|
|
|
{ .compatible = "snps,designware-i2c", },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
|
|
|
|
#endif
|
|
|
|
|
2015-05-20 22:33:13 +08:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2015-08-31 22:31:32 +08:00
|
|
|
static int dw_i2c_plat_prepare(struct device *dev)
|
2015-05-20 22:33:13 +08:00
|
|
|
{
|
|
|
|
return pm_runtime_suspended(dev);
|
|
|
|
}
|
|
|
|
|
2015-08-31 22:31:32 +08:00
|
|
|
static void dw_i2c_plat_complete(struct device *dev)
|
2015-05-20 22:33:13 +08:00
|
|
|
{
|
|
|
|
if (dev->power.direct_complete)
|
|
|
|
pm_request_resume(dev);
|
|
|
|
}
|
|
|
|
#else
|
2015-10-21 15:09:17 +08:00
|
|
|
#define dw_i2c_plat_prepare NULL
|
|
|
|
#define dw_i2c_plat_complete NULL
|
2015-05-20 22:33:13 +08:00
|
|
|
#endif
|
|
|
|
|
2014-05-15 22:37:23 +08: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 07:30:51 +08:00
|
|
|
static int dw_i2c_plat_suspend(struct device *dev)
|
2012-02-24 19:31:15 +08:00
|
|
|
{
|
2017-07-28 00:16:24 +08:00
|
|
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
2012-02-24 19:31:15 +08: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 07:30:51 +08:00
|
|
|
if (i_dev->suspended) {
|
|
|
|
i_dev->skip_resume = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-14 18:43:23 +08:00
|
|
|
i_dev->disable(i_dev);
|
2016-01-04 23:17:35 +08:00
|
|
|
i2c_dw_plat_prepare_clk(i_dev, false);
|
2012-02-24 19:31:15 +08: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 07:30:51 +08:00
|
|
|
i_dev->suspended = true;
|
|
|
|
|
2012-02-24 19:31:15 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-31 22:31:32 +08:00
|
|
|
static int dw_i2c_plat_resume(struct device *dev)
|
2012-02-24 19:31:15 +08:00
|
|
|
{
|
2017-07-28 00:16:24 +08:00
|
|
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
2012-02-24 19:31:15 +08: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 07:30:51 +08:00
|
|
|
if (!i_dev->suspended)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (i_dev->skip_resume) {
|
|
|
|
i_dev->skip_resume = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-04 23:17:35 +08:00
|
|
|
i2c_dw_plat_prepare_clk(i_dev, true);
|
2017-06-14 18:43:23 +08:00
|
|
|
i_dev->init(i_dev);
|
2012-02-24 19:31:15 +08: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 07:30:51 +08:00
|
|
|
i_dev->suspended = false;
|
2012-02-24 19:31:15 +08: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 07:30:51 +08: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 21:28:22 +08:00
|
|
|
}
|
|
|
|
|
2015-05-20 22:33:13 +08:00
|
|
|
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
2015-08-31 22:31:32 +08: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 07:30:51 +08: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 22:33:13 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
|
|
|
|
#else
|
|
|
|
#define DW_I2C_DEV_PMOPS NULL
|
|
|
|
#endif
|
2014-05-15 22:37:23 +08:00
|
|
|
|
2017-06-14 18:43:21 +08:00
|
|
|
/* Work with hotplug and coldplug */
|
2011-10-29 17:57:23 +08:00
|
|
|
MODULE_ALIAS("platform:i2c_designware");
|
|
|
|
|
|
|
|
static struct platform_driver dw_i2c_driver = {
|
2015-08-31 22:31:32 +08:00
|
|
|
.probe = dw_i2c_plat_probe,
|
|
|
|
.remove = dw_i2c_plat_remove,
|
2011-10-29 17:57:23 +08:00
|
|
|
.driver = {
|
|
|
|
.name = "i2c_designware",
|
2011-11-09 04:43:47 +08:00
|
|
|
.of_match_table = of_match_ptr(dw_i2c_of_match),
|
2013-01-17 18:31:07 +08:00
|
|
|
.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),
|
2015-05-20 22:33:13 +08:00
|
|
|
.pm = DW_I2C_DEV_PMOPS,
|
2011-10-29 17:57:23 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init dw_i2c_init_driver(void)
|
|
|
|
{
|
2013-10-09 04:35:33 +08:00
|
|
|
return platform_driver_register(&dw_i2c_driver);
|
2011-10-29 17:57:23 +08:00
|
|
|
}
|
2012-02-29 14:57:46 +08:00
|
|
|
subsys_initcall(dw_i2c_init_driver);
|
2011-10-29 17:57:23 +08: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");
|