mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
Merge branch 'i2c/for-4.15' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
"This contains two bigger than usual tree-wide changes this time. They
all have proper acks, caused no merge conflicts in linux-next where
they have been for a while. They are namely:
- to-gpiod conversion of the i2c-gpio driver and its users (touching
arch/* and drivers/mfd/*)
- adding a sbs-manager based on I2C core updates to SMBus alerts
(touching drivers/power/*)
Other notable changes:
- i2c_boardinfo can now carry a dev_name to be used when the device
is created. This is because some devices in ACPI world need fixed
names to find the regulators.
- the designware driver got a long discussed overhaul of its PM
handling. img-scb and davinci got PM support, too.
- at24 driver has way better OF support. And it has a new maintainer.
Thanks Bartosz for stepping up!
The rest is regular driver updates and fixes"
* 'i2c/for-4.15' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (55 commits)
ARM: sa1100: simpad: Correct I2C GPIO offsets
i2c: aspeed: Deassert reset in probe
eeprom: at24: Add OF device ID table
MAINTAINERS: new maintainer for AT24 driver
i2c: nuc900: remove platform_data, too
i2c: thunderx: Remove duplicate NULL check
i2c: taos-evm: Remove duplicate NULL check
i2c: Make i2c_unregister_device() NULL-aware
i2c: xgene-slimpro: Support v2
i2c: mpc: remove useless variable initialization
i2c: omap: Trigger bus recovery in lockup case
i2c: gpio: Add support for named gpios in DT
dt-bindings: i2c: i2c-gpio: Add support for named gpios
i2c: gpio: Local vars in probe
i2c: gpio: Augment all boardfiles to use open drain
i2c: gpio: Enforce open drain through gpiolib
gpio: Make it possible for consumers to enforce open drain
i2c: gpio: Convert to use descriptors
power: supply: sbs-message: fix some code style issues
power: supply: sbs-battery: remove unchecked return var
...
This commit is contained in:
commit
4008e6a9bc
70 changed files with 1685 additions and 600 deletions
|
|
@ -198,6 +198,11 @@ config I2C_CHT_WC
|
|||
SMBus controller found in the Intel Cherry Trail Whiskey Cove PMIC
|
||||
found on some Intel Cherry Trail systems.
|
||||
|
||||
Note this controller is hooked up to a TI bq24292i charger-IC,
|
||||
combined with a FUSB302 Type-C port-controller as such it is advised
|
||||
to also select CONFIG_CHARGER_BQ24190=m and CONFIG_TYPEC_FUSB302=m
|
||||
(the fusb302 driver currently is in drivers/staging).
|
||||
|
||||
config I2C_NFORCE2
|
||||
tristate "Nvidia nForce2, nForce3 and nForce4"
|
||||
depends on PCI
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* I2C Register */
|
||||
|
|
@ -132,6 +133,7 @@ struct aspeed_i2c_bus {
|
|||
struct i2c_adapter adap;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct reset_control *rst;
|
||||
/* Synchronizes I/O mem access to base. */
|
||||
spinlock_t lock;
|
||||
struct completion cmd_complete;
|
||||
|
|
@ -847,6 +849,14 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
|
|||
/* We just need the clock rate, we don't actually use the clk object. */
|
||||
devm_clk_put(&pdev->dev, parent_clk);
|
||||
|
||||
bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
|
||||
if (IS_ERR(bus->rst)) {
|
||||
dev_err(&pdev->dev,
|
||||
"missing or invalid reset controller device tree entry");
|
||||
return PTR_ERR(bus->rst);
|
||||
}
|
||||
reset_control_deassert(bus->rst);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"bus-frequency", &bus->bus_frequency);
|
||||
if (ret < 0) {
|
||||
|
|
@ -917,6 +927,8 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev)
|
|||
|
||||
spin_unlock_irqrestore(&bus->lock, flags);
|
||||
|
||||
reset_control_assert(bus->rst);
|
||||
|
||||
i2c_del_adapter(&bus->adap);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power/bq24190_charger.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define CHT_WC_I2C_CTRL 0x5e24
|
||||
|
|
@ -232,13 +234,35 @@ static const struct irq_chip cht_wc_i2c_irq_chip = {
|
|||
.name = "cht_wc_ext_chrg_irq_chip",
|
||||
};
|
||||
|
||||
static const char * const bq24190_suppliers[] = { "fusb302-typec-source" };
|
||||
|
||||
static const struct property_entry bq24190_props[] = {
|
||||
PROPERTY_ENTRY_STRING("extcon-name", "cht_wcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_suppliers),
|
||||
PROPERTY_ENTRY_BOOL("omit-battery-class"),
|
||||
PROPERTY_ENTRY_BOOL("disable-reset"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply fusb302_consumer = {
|
||||
.supply = "vbus",
|
||||
/* Must match fusb302 dev_name in intel_cht_int33fe.c */
|
||||
.dev_name = "i2c-fusb302",
|
||||
};
|
||||
|
||||
static const struct regulator_init_data bq24190_vbus_init_data = {
|
||||
.constraints = {
|
||||
/* The name is used in intel_cht_int33fe.c do not change. */
|
||||
.name = "cht_wc_usb_typec_vbus",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.consumer_supplies = &fusb302_consumer,
|
||||
.num_consumer_supplies = 1,
|
||||
};
|
||||
|
||||
static struct bq24190_platform_data bq24190_pdata = {
|
||||
.regulator_init_data = &bq24190_vbus_init_data,
|
||||
};
|
||||
|
||||
static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
|
||||
|
|
@ -246,7 +270,9 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
|
|||
struct i2c_board_info board_info = {
|
||||
.type = "bq24190",
|
||||
.addr = 0x6b,
|
||||
.dev_name = "bq24190",
|
||||
.properties = bq24190_props,
|
||||
.platform_data = &bq24190_pdata,
|
||||
};
|
||||
int ret, reg, irq;
|
||||
|
||||
|
|
@ -314,11 +340,21 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto remove_irq_domain;
|
||||
|
||||
board_info.irq = adap->client_irq;
|
||||
adap->client = i2c_new_device(&adap->adapter, &board_info);
|
||||
if (!adap->client) {
|
||||
ret = -ENOMEM;
|
||||
goto del_adapter;
|
||||
/*
|
||||
* Normally the Whiskey Cove PMIC is paired with a TI bq24292i charger,
|
||||
* connected to this i2c bus, and a max17047 fuel-gauge and a fusb302
|
||||
* USB Type-C controller connected to another i2c bus. In this setup
|
||||
* the max17047 and fusb302 devices are enumerated through an INT33FE
|
||||
* ACPI device. If this device is present register an i2c-client for
|
||||
* the TI bq24292i charger.
|
||||
*/
|
||||
if (acpi_dev_present("INT33FE", NULL, -1)) {
|
||||
board_info.irq = adap->client_irq;
|
||||
adap->client = i2c_new_device(&adap->adapter, &board_info);
|
||||
if (!adap->client) {
|
||||
ret = -ENOMEM;
|
||||
goto del_adapter;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, adap);
|
||||
|
|
@ -335,7 +371,8 @@ static int cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_unregister_device(adap->client);
|
||||
if (adap->client)
|
||||
i2c_unregister_device(adap->client);
|
||||
i2c_del_adapter(&adap->adapter);
|
||||
irq_domain_remove(adap->irq_domain);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_data/i2c-davinci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* ----- global defines ----------------------------------------------- */
|
||||
|
||||
|
|
@ -122,6 +123,9 @@
|
|||
/* set the SDA GPIO low */
|
||||
#define DAVINCI_I2C_DCLR_PDCLR1 BIT(1)
|
||||
|
||||
/* timeout for pm runtime autosuspend */
|
||||
#define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
struct davinci_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
|
@ -500,7 +504,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
|
|||
/* This should be 0 if all bytes were transferred
|
||||
* or dev->cmd_err denotes an error.
|
||||
*/
|
||||
dev_err(dev->dev, "abnormal termination buf_len=%i\n",
|
||||
dev_err(dev->dev, "abnormal termination buf_len=%zu\n",
|
||||
dev->buf_len);
|
||||
dev->terminate = 1;
|
||||
wmb();
|
||||
|
|
@ -541,10 +545,17 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
|
||||
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
|
||||
|
||||
ret = pm_runtime_get_sync(dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to runtime_get device: %d\n", ret);
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_davinci_wait_bus_not_busy(dev);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev->dev, "timeout waiting for bus ready\n");
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
|
|
@ -552,14 +563,19 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
dev_dbg(dev->dev, "%s [%d/%d] ret: %d\n", __func__, i + 1, num,
|
||||
ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = num;
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
complete(&dev->xfr_complete);
|
||||
#endif
|
||||
|
||||
return num;
|
||||
out:
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 i2c_davinci_func(struct i2c_adapter *adap)
|
||||
|
|
@ -599,6 +615,9 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
|
|||
int count = 0;
|
||||
u16 w;
|
||||
|
||||
if (pm_runtime_suspended(dev->dev))
|
||||
return IRQ_NONE;
|
||||
|
||||
while ((stat = davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG))) {
|
||||
dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
|
||||
if (count++ == 100) {
|
||||
|
|
@ -802,13 +821,24 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
|||
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dev->clk))
|
||||
return PTR_ERR(dev->clk);
|
||||
clk_prepare_enable(dev->clk);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dev->base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(dev->base)) {
|
||||
r = PTR_ERR(dev->base);
|
||||
goto err_unuse_clocks;
|
||||
return PTR_ERR(dev->base);
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(dev->dev,
|
||||
DAVINCI_I2C_PM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(dev->dev);
|
||||
|
||||
pm_runtime_enable(dev->dev);
|
||||
|
||||
r = pm_runtime_get_sync(dev->dev);
|
||||
if (r < 0) {
|
||||
dev_err(dev->dev, "failed to runtime_get device: %d\n", r);
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
return r;
|
||||
}
|
||||
|
||||
i2c_davinci_init(dev);
|
||||
|
|
@ -849,27 +879,40 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
|||
if (r)
|
||||
goto err_unuse_clocks;
|
||||
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unuse_clocks:
|
||||
clk_disable_unprepare(dev->clk);
|
||||
dev->clk = NULL;
|
||||
pm_runtime_dont_use_autosuspend(dev->dev);
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
pm_runtime_disable(dev->dev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int davinci_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
i2c_davinci_cpufreq_deregister(dev);
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
|
||||
clk_disable_unprepare(dev->clk);
|
||||
dev->clk = NULL;
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, 0);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(dev->dev);
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
pm_runtime_disable(dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -880,7 +923,6 @@ static int davinci_i2c_suspend(struct device *dev)
|
|||
|
||||
/* put I2C into reset */
|
||||
davinci_i2c_reset_ctrl(i2c_dev, 0);
|
||||
clk_disable_unprepare(i2c_dev->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -889,7 +931,6 @@ static int davinci_i2c_resume(struct device *dev)
|
|||
{
|
||||
struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||
|
||||
clk_prepare_enable(i2c_dev->clk);
|
||||
/* take I2C out of reset */
|
||||
davinci_i2c_reset_ctrl(i2c_dev, 1);
|
||||
|
||||
|
|
@ -899,6 +940,8 @@ static int davinci_i2c_resume(struct device *dev)
|
|||
static const struct dev_pm_ops davinci_i2c_pm = {
|
||||
.suspend = davinci_i2c_suspend,
|
||||
.resume = davinci_i2c_resume,
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
#define davinci_i2c_pm_ops (&davinci_i2c_pm)
|
||||
|
|
|
|||
|
|
@ -280,6 +280,8 @@ struct dw_i2c_dev {
|
|||
int (*acquire_lock)(struct dw_i2c_dev *dev);
|
||||
void (*release_lock)(struct dw_i2c_dev *dev);
|
||||
bool pm_disabled;
|
||||
bool suspended;
|
||||
bool skip_resume;
|
||||
void (*disable)(struct dw_i2c_dev *dev);
|
||||
void (*disable_int)(struct dw_i2c_dev *dev);
|
||||
int (*init)(struct dw_i2c_dev *dev);
|
||||
|
|
|
|||
|
|
@ -249,6 +249,14 @@ static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
|
@ -257,7 +265,9 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
u32 acpi_speed, ht = 0;
|
||||
struct resource *mem;
|
||||
int i, irq, ret;
|
||||
const int supported_speeds[] = { 0, 100000, 400000, 1000000, 3400000 };
|
||||
static const int supported_speeds[] = {
|
||||
0, 100000, 400000, 1000000, 3400000
|
||||
};
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
|
|
@ -362,14 +372,17 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
if (dev->pm_disabled) {
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
} else {
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
}
|
||||
/* 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);
|
||||
|
||||
if (dev->mode == DW_IC_SLAVE)
|
||||
ret = i2c_dw_probe_slave(dev);
|
||||
|
|
@ -382,8 +395,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
exit_probe:
|
||||
if (!dev->pm_disabled)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
dw_i2c_plat_pm_cleanup(dev);
|
||||
exit_reset:
|
||||
if (!IS_ERR_OR_NULL(dev->rst))
|
||||
reset_control_assert(dev->rst);
|
||||
|
|
@ -402,8 +414,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev)
|
|||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
if (!dev->pm_disabled)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
dw_i2c_plat_pm_cleanup(dev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(dev->rst))
|
||||
reset_control_assert(dev->rst);
|
||||
|
||||
|
|
@ -437,13 +449,20 @@ static void dw_i2c_plat_complete(struct device *dev)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dw_i2c_plat_runtime_suspend(struct device *dev)
|
||||
static int dw_i2c_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (i_dev->suspended) {
|
||||
i_dev->skip_resume = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i_dev->disable(i_dev);
|
||||
i2c_dw_plat_prepare_clk(i_dev, false);
|
||||
|
||||
i_dev->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -451,27 +470,27 @@ static int dw_i2c_plat_resume(struct device *dev)
|
|||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (!i_dev->suspended)
|
||||
return 0;
|
||||
|
||||
if (i_dev->skip_resume) {
|
||||
i_dev->skip_resume = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i2c_dw_plat_prepare_clk(i_dev, true);
|
||||
i_dev->init(i_dev);
|
||||
|
||||
i_dev->suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_i2c_plat_suspend(struct device *dev)
|
||||
{
|
||||
pm_runtime_resume(dev);
|
||||
return dw_i2c_plat_runtime_suspend(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
||||
.prepare = dw_i2c_plat_prepare,
|
||||
.complete = dw_i2c_plat_complete,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend,
|
||||
dw_i2c_plat_resume,
|
||||
NULL)
|
||||
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)
|
||||
};
|
||||
|
||||
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
|
||||
|
|
|
|||
|
|
@ -14,27 +14,17 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
struct i2c_gpio_private_data {
|
||||
struct gpio_desc *sda;
|
||||
struct gpio_desc *scl;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_algo_bit_data bit_data;
|
||||
struct i2c_gpio_platform_data pdata;
|
||||
};
|
||||
|
||||
/* Toggle SDA by changing the direction of the pin */
|
||||
static void i2c_gpio_setsda_dir(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
if (state)
|
||||
gpio_direction_input(pdata->sda_pin);
|
||||
else
|
||||
gpio_direction_output(pdata->sda_pin, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle SDA by changing the output value of the pin. This is only
|
||||
* valid for pins configured as open drain (i.e. setting the value
|
||||
|
|
@ -42,20 +32,9 @@ static void i2c_gpio_setsda_dir(void *data, int state)
|
|||
*/
|
||||
static void i2c_gpio_setsda_val(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
gpio_set_value(pdata->sda_pin, state);
|
||||
}
|
||||
|
||||
/* Toggle SCL by changing the direction of the pin. */
|
||||
static void i2c_gpio_setscl_dir(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
if (state)
|
||||
gpio_direction_input(pdata->scl_pin);
|
||||
else
|
||||
gpio_direction_output(pdata->scl_pin, 0);
|
||||
gpiod_set_value(priv->sda, state);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -66,44 +45,23 @@ static void i2c_gpio_setscl_dir(void *data, int state)
|
|||
*/
|
||||
static void i2c_gpio_setscl_val(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
gpio_set_value(pdata->scl_pin, state);
|
||||
gpiod_set_value(priv->scl, state);
|
||||
}
|
||||
|
||||
static int i2c_gpio_getsda(void *data)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
return gpio_get_value(pdata->sda_pin);
|
||||
return gpiod_get_value(priv->sda);
|
||||
}
|
||||
|
||||
static int i2c_gpio_getscl(void *data)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
return gpio_get_value(pdata->scl_pin);
|
||||
}
|
||||
|
||||
static int of_i2c_gpio_get_pins(struct device_node *np,
|
||||
unsigned int *sda_pin, unsigned int *scl_pin)
|
||||
{
|
||||
if (of_gpio_count(np) < 2)
|
||||
return -ENODEV;
|
||||
|
||||
*sda_pin = of_get_gpio(np, 0);
|
||||
*scl_pin = of_get_gpio(np, 1);
|
||||
|
||||
if (*sda_pin == -EPROBE_DEFER || *scl_pin == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) {
|
||||
pr_err("%pOF: invalid GPIO pins, sda=%d/scl=%d\n",
|
||||
np, *sda_pin, *scl_pin);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return gpiod_get_value(priv->scl);
|
||||
}
|
||||
|
||||
static void of_i2c_gpio_get_props(struct device_node *np,
|
||||
|
|
@ -124,72 +82,105 @@ static void of_i2c_gpio_get_props(struct device_node *np,
|
|||
of_property_read_bool(np, "i2c-gpio,scl-output-only");
|
||||
}
|
||||
|
||||
static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
|
||||
const char *con_id,
|
||||
unsigned int index,
|
||||
enum gpiod_flags gflags)
|
||||
{
|
||||
struct gpio_desc *retdesc;
|
||||
int ret;
|
||||
|
||||
retdesc = devm_gpiod_get(dev, con_id, gflags);
|
||||
if (!IS_ERR(retdesc)) {
|
||||
dev_dbg(dev, "got GPIO from name %s\n", con_id);
|
||||
return retdesc;
|
||||
}
|
||||
|
||||
retdesc = devm_gpiod_get_index(dev, NULL, index, gflags);
|
||||
if (!IS_ERR(retdesc)) {
|
||||
dev_dbg(dev, "got GPIO from index %u\n", index);
|
||||
return retdesc;
|
||||
}
|
||||
|
||||
ret = PTR_ERR(retdesc);
|
||||
|
||||
/* FIXME: hack in the old code, is this really necessary? */
|
||||
if (ret == -EINVAL)
|
||||
retdesc = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
/* This happens if the GPIO driver is not yet probed, let's defer */
|
||||
if (ret == -ENOENT)
|
||||
retdesc = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "error trying to get descriptor: %d\n", ret);
|
||||
|
||||
return retdesc;
|
||||
}
|
||||
|
||||
static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv;
|
||||
struct i2c_gpio_platform_data *pdata;
|
||||
struct i2c_algo_bit_data *bit_data;
|
||||
struct i2c_adapter *adap;
|
||||
unsigned int sda_pin, scl_pin;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
enum gpiod_flags gflags;
|
||||
int ret;
|
||||
|
||||
/* First get the GPIO pins; if it fails, we'll defer the probe. */
|
||||
if (pdev->dev.of_node) {
|
||||
ret = of_i2c_gpio_get_pins(pdev->dev.of_node,
|
||||
&sda_pin, &scl_pin);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
if (!dev_get_platdata(&pdev->dev))
|
||||
return -ENXIO;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
sda_pin = pdata->sda_pin;
|
||||
scl_pin = pdata->scl_pin;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev, sda_pin, "sda");
|
||||
if (ret) {
|
||||
if (ret == -EINVAL)
|
||||
ret = -EPROBE_DEFER; /* Try again later */
|
||||
return ret;
|
||||
}
|
||||
ret = devm_gpio_request(&pdev->dev, scl_pin, "scl");
|
||||
if (ret) {
|
||||
if (ret == -EINVAL)
|
||||
ret = -EPROBE_DEFER; /* Try again later */
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
adap = &priv->adap;
|
||||
bit_data = &priv->bit_data;
|
||||
pdata = &priv->pdata;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
pdata->sda_pin = sda_pin;
|
||||
pdata->scl_pin = scl_pin;
|
||||
of_i2c_gpio_get_props(pdev->dev.of_node, pdata);
|
||||
if (np) {
|
||||
of_i2c_gpio_get_props(np, pdata);
|
||||
} else {
|
||||
memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata));
|
||||
/*
|
||||
* If all platform data settings are zero it is OK
|
||||
* to not provide any platform data from the board.
|
||||
*/
|
||||
if (dev_get_platdata(dev))
|
||||
memcpy(pdata, dev_get_platdata(dev), sizeof(*pdata));
|
||||
}
|
||||
|
||||
if (pdata->sda_is_open_drain) {
|
||||
gpio_direction_output(pdata->sda_pin, 1);
|
||||
bit_data->setsda = i2c_gpio_setsda_val;
|
||||
} else {
|
||||
gpio_direction_input(pdata->sda_pin);
|
||||
bit_data->setsda = i2c_gpio_setsda_dir;
|
||||
}
|
||||
/*
|
||||
* First get the GPIO pins; if it fails, we'll defer the probe.
|
||||
* If the SDA line is marked from platform data or device tree as
|
||||
* "open drain" it means something outside of our control is making
|
||||
* this line being handled as open drain, and we should just handle
|
||||
* it as any other output. Else we enforce open drain as this is
|
||||
* required for an I2C bus.
|
||||
*/
|
||||
if (pdata->sda_is_open_drain)
|
||||
gflags = GPIOD_OUT_HIGH;
|
||||
else
|
||||
gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
|
||||
priv->sda = i2c_gpio_get_desc(dev, "sda", 0, gflags);
|
||||
if (IS_ERR(priv->sda))
|
||||
return PTR_ERR(priv->sda);
|
||||
|
||||
if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
|
||||
gpio_direction_output(pdata->scl_pin, 1);
|
||||
bit_data->setscl = i2c_gpio_setscl_val;
|
||||
} else {
|
||||
gpio_direction_input(pdata->scl_pin);
|
||||
bit_data->setscl = i2c_gpio_setscl_dir;
|
||||
}
|
||||
/*
|
||||
* If the SCL line is marked from platform data or device tree as
|
||||
* "open drain" it means something outside of our control is making
|
||||
* this line being handled as open drain, and we should just handle
|
||||
* it as any other output. Else we enforce open drain as this is
|
||||
* required for an I2C bus.
|
||||
*/
|
||||
if (pdata->scl_is_open_drain)
|
||||
gflags = GPIOD_OUT_LOW;
|
||||
else
|
||||
gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
|
||||
priv->scl = i2c_gpio_get_desc(dev, "scl", 1, gflags);
|
||||
if (IS_ERR(priv->scl))
|
||||
return PTR_ERR(priv->scl);
|
||||
|
||||
bit_data->setsda = i2c_gpio_setsda_val;
|
||||
bit_data->setscl = i2c_gpio_setscl_val;
|
||||
|
||||
if (!pdata->scl_is_output_only)
|
||||
bit_data->getscl = i2c_gpio_getscl;
|
||||
|
|
@ -207,18 +198,18 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
else
|
||||
bit_data->timeout = HZ / 10; /* 100 ms */
|
||||
|
||||
bit_data->data = pdata;
|
||||
bit_data->data = priv;
|
||||
|
||||
adap->owner = THIS_MODULE;
|
||||
if (pdev->dev.of_node)
|
||||
strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
|
||||
if (np)
|
||||
strlcpy(adap->name, dev_name(dev), sizeof(adap->name));
|
||||
else
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
|
||||
|
||||
adap->algo_data = bit_data;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
adap->dev.parent = dev;
|
||||
adap->dev.of_node = np;
|
||||
|
||||
adap->nr = pdev->id;
|
||||
ret = i2c_bit_add_numbered_bus(adap);
|
||||
|
|
@ -227,8 +218,13 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n",
|
||||
pdata->sda_pin, pdata->scl_pin,
|
||||
/*
|
||||
* FIXME: using global GPIO numbers is not helpful. If/when we
|
||||
* get accessors to get the actual name of the GPIO line,
|
||||
* from the descriptor, then provide that instead.
|
||||
*/
|
||||
dev_info(dev, "using lines %u (SDA) and %u (SCL%s)\n",
|
||||
desc_to_gpio(priv->sda), desc_to_gpio(priv->scl),
|
||||
pdata->scl_is_output_only
|
||||
? ", no clock stretching" : "");
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
|
|
@ -280,6 +281,8 @@
|
|||
#define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err)))
|
||||
#define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M)
|
||||
|
||||
#define IMG_I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
enum img_i2c_mode {
|
||||
MODE_INACTIVE,
|
||||
MODE_RAW,
|
||||
|
|
@ -408,6 +411,9 @@ struct img_i2c {
|
|||
unsigned int raw_timeout;
|
||||
};
|
||||
|
||||
static int img_i2c_runtime_suspend(struct device *dev);
|
||||
static int img_i2c_runtime_resume(struct device *dev);
|
||||
|
||||
static void img_i2c_writel(struct img_i2c *i2c, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, i2c->base + offset);
|
||||
|
|
@ -826,9 +832,9 @@ next_atomic_cmd:
|
|||
* Timer function to check if something has gone wrong in automatic mode (so we
|
||||
* don't have to handle so many interrupts just to catch an exception).
|
||||
*/
|
||||
static void img_i2c_check_timer(unsigned long arg)
|
||||
static void img_i2c_check_timer(struct timer_list *t)
|
||||
{
|
||||
struct img_i2c *i2c = (struct img_i2c *)arg;
|
||||
struct img_i2c *i2c = from_timer(i2c, t, check_timer);
|
||||
unsigned long flags;
|
||||
unsigned int line_status;
|
||||
|
||||
|
|
@ -1054,8 +1060,8 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
atomic = true;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2c->scb_clk);
|
||||
if (ret)
|
||||
ret = pm_runtime_get_sync(adap->dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
|
|
@ -1131,7 +1137,8 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
break;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(i2c->scb_clk);
|
||||
pm_runtime_mark_last_busy(adap->dev.parent);
|
||||
pm_runtime_put_autosuspend(adap->dev.parent);
|
||||
|
||||
return i2c->msg_status ? i2c->msg_status : num;
|
||||
}
|
||||
|
|
@ -1149,12 +1156,13 @@ static const struct i2c_algorithm img_i2c_algo = {
|
|||
static int img_i2c_init(struct img_i2c *i2c)
|
||||
{
|
||||
unsigned int clk_khz, bitrate_khz, clk_period, tckh, tckl, tsdh;
|
||||
unsigned int i, ret, data, prescale, inc, int_bitrate, filt;
|
||||
unsigned int i, data, prescale, inc, int_bitrate, filt;
|
||||
struct img_i2c_timings timing;
|
||||
u32 rev;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c->scb_clk);
|
||||
if (ret)
|
||||
ret = pm_runtime_get_sync(i2c->adap.dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rev = img_i2c_readl(i2c, SCB_CORE_REV_REG);
|
||||
|
|
@ -1163,7 +1171,8 @@ static int img_i2c_init(struct img_i2c *i2c)
|
|||
"Unknown hardware revision (%d.%d.%d.%d)\n",
|
||||
(rev >> 24) & 0xff, (rev >> 16) & 0xff,
|
||||
(rev >> 8) & 0xff, rev & 0xff);
|
||||
clk_disable_unprepare(i2c->scb_clk);
|
||||
pm_runtime_mark_last_busy(i2c->adap.dev.parent);
|
||||
pm_runtime_put_autosuspend(i2c->adap.dev.parent);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -1314,7 +1323,8 @@ static int img_i2c_init(struct img_i2c *i2c)
|
|||
/* Perform a synchronous sequence to reset the bus */
|
||||
ret = img_i2c_reset_bus(i2c);
|
||||
|
||||
clk_disable_unprepare(i2c->scb_clk);
|
||||
pm_runtime_mark_last_busy(i2c->adap.dev.parent);
|
||||
pm_runtime_put_autosuspend(i2c->adap.dev.parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1362,8 +1372,7 @@ static int img_i2c_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Set up the exception check timer */
|
||||
setup_timer(&i2c->check_timer, img_i2c_check_timer,
|
||||
(unsigned long)i2c);
|
||||
timer_setup(&i2c->check_timer, img_i2c_check_timer, 0);
|
||||
|
||||
i2c->bitrate = timings[0].max_bitrate;
|
||||
if (!of_property_read_u32(node, "clock-frequency", &val))
|
||||
|
|
@ -1384,22 +1393,30 @@ static int img_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
ret = clk_prepare_enable(i2c->sys_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_I2C_PM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_i2c_runtime_resume(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = img_i2c_init(i2c);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
goto rpm_disable;
|
||||
|
||||
ret = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (ret < 0)
|
||||
goto disable_clk;
|
||||
goto rpm_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(i2c->sys_clk);
|
||||
rpm_disable:
|
||||
if (!pm_runtime_enabled(&pdev->dev))
|
||||
img_i2c_runtime_suspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1408,20 +1425,56 @@ static int img_i2c_remove(struct platform_device *dev)
|
|||
struct img_i2c *i2c = platform_get_drvdata(dev);
|
||||
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
pm_runtime_disable(&dev->dev);
|
||||
if (!pm_runtime_status_suspended(&dev->dev))
|
||||
img_i2c_runtime_suspend(&dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct img_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(i2c->scb_clk);
|
||||
clk_disable_unprepare(i2c->sys_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct img_i2c *i2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c->sys_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to enable sys clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2c->scb_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to enable scb clock\n");
|
||||
clk_disable_unprepare(i2c->sys_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int img_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct img_i2c *i2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
img_i2c_switch_mode(i2c, MODE_SUSPEND);
|
||||
|
||||
clk_disable_unprepare(i2c->sys_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1430,7 +1483,7 @@ static int img_i2c_resume(struct device *dev)
|
|||
struct img_i2c *i2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c->sys_clk);
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1440,7 +1493,12 @@ static int img_i2c_resume(struct device *dev)
|
|||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(img_i2c_pm, img_i2c_suspend, img_i2c_resume);
|
||||
static const struct dev_pm_ops img_i2c_pm = {
|
||||
SET_RUNTIME_PM_OPS(img_i2c_runtime_suspend,
|
||||
img_i2c_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(img_i2c_suspend, img_i2c_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id img_scb_i2c_match[] = {
|
||||
{ .compatible = "img,scb-i2c" },
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = {
|
|||
|
||||
static u32 mpc_i2c_get_sec_cfg_8xxx(void)
|
||||
{
|
||||
struct device_node *node = NULL;
|
||||
struct device_node *node;
|
||||
u32 __iomem *reg;
|
||||
u32 val = 0;
|
||||
|
||||
|
|
@ -700,7 +700,7 @@ static int fsl_i2c_probe(struct platform_device *op)
|
|||
}
|
||||
}
|
||||
|
||||
if (of_get_property(op->dev.of_node, "fsl,preserve-clocking", NULL)) {
|
||||
if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) {
|
||||
clock = MPC_I2C_CLOCK_PRESERVE;
|
||||
} else {
|
||||
prop = of_get_property(op->dev.of_node, "clock-frequency",
|
||||
|
|
|
|||
|
|
@ -486,6 +486,22 @@ static int omap_i2c_init(struct omap_i2c_dev *omap)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try bus recovery, but only if SDA is actually low.
|
||||
*/
|
||||
static int omap_i2c_recover_bus(struct omap_i2c_dev *omap)
|
||||
{
|
||||
u16 systest;
|
||||
|
||||
systest = omap_i2c_read_reg(omap, OMAP_I2C_SYSTEST_REG);
|
||||
if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) &&
|
||||
(systest & OMAP_I2C_SYSTEST_SDA_I_FUNC))
|
||||
return 0; /* bus seems to already be fine */
|
||||
if (!(systest & OMAP_I2C_SYSTEST_SCL_I_FUNC))
|
||||
return -EBUSY; /* recovery would not fix SCL */
|
||||
return i2c_recover_bus(&omap->adapter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Waiting on Bus Busy
|
||||
*/
|
||||
|
|
@ -496,7 +512,7 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap)
|
|||
timeout = jiffies + OMAP_I2C_TIMEOUT;
|
||||
while (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return i2c_recover_bus(&omap->adapter);
|
||||
return omap_i2c_recover_bus(omap);
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
|
|
@ -577,8 +593,13 @@ static int omap_i2c_wait_for_bb_valid(struct omap_i2c_dev *omap)
|
|||
}
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
/*
|
||||
* SDA or SCL were low for the entire timeout without
|
||||
* any activity detected. Most likely, a slave is
|
||||
* locking up the bus with no master driving the clock.
|
||||
*/
|
||||
dev_warn(omap->dev, "timeout waiting for bus ready\n");
|
||||
return -ETIMEDOUT;
|
||||
return omap_i2c_recover_bus(omap);
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ static struct i2c_adapter parport_adapter = {
|
|||
|
||||
/* SMBus alert support */
|
||||
static struct i2c_smbus_alert_setup alert_data = {
|
||||
.alert_edge_triggered = 1,
|
||||
};
|
||||
static struct i2c_client *ara;
|
||||
static struct lineop parport_ctrl_irq = {
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@ static void i2c_parport_attach(struct parport *port)
|
|||
|
||||
/* Setup SMBus alert if supported */
|
||||
if (adapter_parm[type].smbus_alert) {
|
||||
adapter->alert_data.alert_edge_triggered = 1;
|
||||
adapter->ara = i2c_setup_smbus_alert(&adapter->adapter,
|
||||
&adapter->alert_data);
|
||||
if (adapter->ara)
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
|
|||
jiffies, expires);
|
||||
|
||||
timer->expires = jiffies + expires;
|
||||
timer->data = (unsigned long)alg_data;
|
||||
|
||||
add_timer(timer);
|
||||
}
|
||||
|
|
@ -435,9 +434,9 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void i2c_pnx_timeout(unsigned long data)
|
||||
static void i2c_pnx_timeout(struct timer_list *t)
|
||||
{
|
||||
struct i2c_pnx_algo_data *alg_data = (struct i2c_pnx_algo_data *)data;
|
||||
struct i2c_pnx_algo_data *alg_data = from_timer(alg_data, t, mif.timer);
|
||||
u32 ctl;
|
||||
|
||||
dev_err(&alg_data->adapter.dev,
|
||||
|
|
@ -659,8 +658,7 @@ static int i2c_pnx_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(alg_data->clk))
|
||||
return PTR_ERR(alg_data->clk);
|
||||
|
||||
setup_timer(&alg_data->mif.timer, i2c_pnx_timeout,
|
||||
(unsigned long)alg_data);
|
||||
timer_setup(&alg_data->mif.timer, i2c_pnx_timeout, 0);
|
||||
|
||||
snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
|
||||
"%s", pdev->name);
|
||||
|
|
|
|||
|
|
@ -84,12 +84,7 @@
|
|||
|
||||
#define ICSR2_NACKF 0x10
|
||||
|
||||
/* ICBRx (@ PCLK 33MHz) */
|
||||
#define ICBR_RESERVED 0xe0 /* Should be 1 on writes */
|
||||
#define ICBRL_SP100K (19 | ICBR_RESERVED)
|
||||
#define ICBRH_SP100K (16 | ICBR_RESERVED)
|
||||
#define ICBRL_SP400K (21 | ICBR_RESERVED)
|
||||
#define ICBRH_SP400K (9 | ICBR_RESERVED)
|
||||
|
||||
#define RIIC_INIT_MSG -1
|
||||
|
||||
|
|
@ -288,48 +283,99 @@ static const struct i2c_algorithm riic_algo = {
|
|||
.functionality = riic_func,
|
||||
};
|
||||
|
||||
static int riic_init_hw(struct riic_dev *riic, u32 spd)
|
||||
static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||
{
|
||||
int ret;
|
||||
unsigned long rate;
|
||||
int total_ticks, cks, brl, brh;
|
||||
|
||||
ret = clk_prepare_enable(riic->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* TODO: Implement formula to calculate the timing values depending on
|
||||
* variable parent clock rate and arbitrary bus speed
|
||||
*/
|
||||
rate = clk_get_rate(riic->clk);
|
||||
if (rate != 33325000) {
|
||||
if (t->bus_freq_hz > 400000) {
|
||||
dev_err(&riic->adapter.dev,
|
||||
"invalid parent clk (%lu). Must be 33325000Hz\n", rate);
|
||||
"unsupported bus speed (%dHz). 400000 max\n",
|
||||
t->bus_freq_hz);
|
||||
clk_disable_unprepare(riic->clk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(riic->clk);
|
||||
|
||||
/*
|
||||
* Assume the default register settings:
|
||||
* FER.SCLE = 1 (SCL sync circuit enabled, adds 2 or 3 cycles)
|
||||
* FER.NFE = 1 (noise circuit enabled)
|
||||
* MR3.NF = 0 (1 cycle of noise filtered out)
|
||||
*
|
||||
* Freq (CKS=000) = (I2CCLK + tr + tf)/ (BRH + 3 + 1) + (BRL + 3 + 1)
|
||||
* Freq (CKS!=000) = (I2CCLK + tr + tf)/ (BRH + 2 + 1) + (BRL + 2 + 1)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Determine reference clock rate. We must be able to get the desired
|
||||
* frequency with only 62 clock ticks max (31 high, 31 low).
|
||||
* Aim for a duty of 60% LOW, 40% HIGH.
|
||||
*/
|
||||
total_ticks = DIV_ROUND_UP(rate, t->bus_freq_hz);
|
||||
|
||||
for (cks = 0; cks < 7; cks++) {
|
||||
/*
|
||||
* 60% low time must be less than BRL + 2 + 1
|
||||
* BRL max register value is 0x1F.
|
||||
*/
|
||||
brl = ((total_ticks * 6) / 10);
|
||||
if (brl <= (0x1F + 3))
|
||||
break;
|
||||
|
||||
total_ticks /= 2;
|
||||
rate /= 2;
|
||||
}
|
||||
|
||||
if (brl > (0x1F + 3)) {
|
||||
dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
|
||||
(unsigned long)t->bus_freq_hz);
|
||||
clk_disable_unprepare(riic->clk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
brh = total_ticks - brl;
|
||||
|
||||
/* Remove automatic clock ticks for sync circuit and NF */
|
||||
if (cks == 0) {
|
||||
brl -= 4;
|
||||
brh -= 4;
|
||||
} else {
|
||||
brl -= 3;
|
||||
brh -= 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove clock ticks for rise and fall times. Convert ns to clock
|
||||
* ticks.
|
||||
*/
|
||||
brl -= t->scl_fall_ns / (1000000000 / rate);
|
||||
brh -= t->scl_rise_ns / (1000000000 / rate);
|
||||
|
||||
/* Adjust for min register values for when SCLE=1 and NFE=1 */
|
||||
if (brl < 1)
|
||||
brl = 1;
|
||||
if (brh < 1)
|
||||
brh = 1;
|
||||
|
||||
pr_debug("i2c-riic: freq=%lu, duty=%d, fall=%lu, rise=%lu, cks=%d, brl=%d, brh=%d\n",
|
||||
rate / total_ticks, ((brl + 3) * 100) / (brl + brh + 6),
|
||||
t->scl_fall_ns / (1000000000 / rate),
|
||||
t->scl_rise_ns / (1000000000 / rate), cks, brl, brh);
|
||||
|
||||
/* Changing the order of accessing IICRST and ICE may break things! */
|
||||
writeb(ICCR1_IICRST | ICCR1_SOWP, riic->base + RIIC_ICCR1);
|
||||
riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1);
|
||||
|
||||
switch (spd) {
|
||||
case 100000:
|
||||
writeb(ICMR1_CKS(3), riic->base + RIIC_ICMR1);
|
||||
writeb(ICBRH_SP100K, riic->base + RIIC_ICBRH);
|
||||
writeb(ICBRL_SP100K, riic->base + RIIC_ICBRL);
|
||||
break;
|
||||
case 400000:
|
||||
writeb(ICMR1_CKS(1), riic->base + RIIC_ICMR1);
|
||||
writeb(ICBRH_SP400K, riic->base + RIIC_ICBRH);
|
||||
writeb(ICBRL_SP400K, riic->base + RIIC_ICBRL);
|
||||
break;
|
||||
default:
|
||||
dev_err(&riic->adapter.dev,
|
||||
"unsupported bus speed (%dHz). Use 100000 or 400000\n", spd);
|
||||
clk_disable_unprepare(riic->clk);
|
||||
return -EINVAL;
|
||||
}
|
||||
writeb(ICMR1_CKS(cks), riic->base + RIIC_ICMR1);
|
||||
writeb(brh | ICBR_RESERVED, riic->base + RIIC_ICBRH);
|
||||
writeb(brl | ICBR_RESERVED, riic->base + RIIC_ICBRL);
|
||||
|
||||
writeb(0, riic->base + RIIC_ICSER);
|
||||
writeb(ICMR3_ACKWP | ICMR3_RDRFS, riic->base + RIIC_ICMR3);
|
||||
|
|
@ -351,11 +397,10 @@ static struct riic_irq_desc riic_irqs[] = {
|
|||
|
||||
static int riic_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct riic_dev *riic;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *res;
|
||||
u32 bus_rate = 0;
|
||||
struct i2c_timings i2c_t;
|
||||
int i, ret;
|
||||
|
||||
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
|
||||
|
|
@ -396,8 +441,9 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
init_completion(&riic->msg_done);
|
||||
|
||||
of_property_read_u32(np, "clock-frequency", &bus_rate);
|
||||
ret = riic_init_hw(riic, bus_rate);
|
||||
i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
|
||||
|
||||
ret = riic_init_hw(riic, &i2c_t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -408,7 +454,8 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, riic);
|
||||
|
||||
dev_info(&pdev->dev, "registered with %dHz bus speed\n", bus_rate);
|
||||
dev_info(&pdev->dev, "registered with %dHz bus speed\n",
|
||||
i2c_t.bus_freq_hz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -881,7 +881,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
|||
struct sh_mobile_i2c_data *pd;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *res;
|
||||
const struct of_device_id *match;
|
||||
const struct sh_mobile_dt_config *config;
|
||||
int ret;
|
||||
u32 bus_speed;
|
||||
|
||||
|
|
@ -913,10 +913,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
|||
pd->bus_speed = ret ? STANDARD_MODE : bus_speed;
|
||||
pd->clks_per_count = 1;
|
||||
|
||||
match = of_match_device(sh_mobile_i2c_dt_ids, &dev->dev);
|
||||
if (match) {
|
||||
const struct sh_mobile_dt_config *config = match->data;
|
||||
|
||||
config = of_device_get_match_data(&dev->dev);
|
||||
if (config) {
|
||||
pd->clks_per_count = config->clks_per_count;
|
||||
|
||||
if (config->setup)
|
||||
|
|
|
|||
|
|
@ -282,8 +282,7 @@ static void taos_disconnect(struct serio *serio)
|
|||
{
|
||||
struct taos_data *taos = serio_get_drvdata(serio);
|
||||
|
||||
if (taos->client)
|
||||
i2c_unregister_device(taos->client);
|
||||
i2c_unregister_device(taos->client);
|
||||
i2c_del_adapter(&taos->adapter);
|
||||
serio_close(serio);
|
||||
kfree(taos);
|
||||
|
|
|
|||
|
|
@ -118,8 +118,6 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)
|
|||
static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
|
||||
struct device_node *node)
|
||||
{
|
||||
u32 type;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -127,10 +125,6 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
|
|||
if (!i2c->alert_data.irq)
|
||||
return -EINVAL;
|
||||
|
||||
type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq));
|
||||
i2c->alert_data.alert_edge_triggered =
|
||||
(type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0;
|
||||
|
||||
i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data);
|
||||
if (!i2c->ara)
|
||||
return -ENODEV;
|
||||
|
|
@ -149,8 +143,7 @@ static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c,
|
|||
|
||||
static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c)
|
||||
{
|
||||
if (i2c->ara)
|
||||
i2c_unregister_device(i2c->ara);
|
||||
i2c_unregister_device(i2c->ara);
|
||||
}
|
||||
|
||||
static int thunder_i2c_probe_pci(struct pci_dev *pdev,
|
||||
|
|
|
|||
|
|
@ -129,6 +129,11 @@ struct slimpro_i2c_dev {
|
|||
#define to_slimpro_i2c_dev(cl) \
|
||||
container_of(cl, struct slimpro_i2c_dev, mbox_client)
|
||||
|
||||
enum slimpro_i2c_version {
|
||||
XGENE_SLIMPRO_I2C_V1 = 0,
|
||||
XGENE_SLIMPRO_I2C_V2 = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function tests and clears a bitmask then returns its old value
|
||||
*/
|
||||
|
|
@ -476,6 +481,15 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
|
|||
}
|
||||
} else {
|
||||
struct acpi_pcct_hw_reduced *cppc_ss;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
int version = XGENE_SLIMPRO_I2C_V1;
|
||||
|
||||
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
|
||||
&pdev->dev);
|
||||
if (!acpi_id)
|
||||
return -EINVAL;
|
||||
|
||||
version = (int)acpi_id->driver_data;
|
||||
|
||||
if (device_property_read_u32(&pdev->dev, "pcc-channel",
|
||||
&ctx->mbox_idx))
|
||||
|
|
@ -514,9 +528,16 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
|
|||
*/
|
||||
ctx->comm_base_addr = cppc_ss->base_address;
|
||||
if (ctx->comm_base_addr) {
|
||||
ctx->pcc_comm_addr = memremap(ctx->comm_base_addr,
|
||||
cppc_ss->length,
|
||||
MEMREMAP_WB);
|
||||
if (version == XGENE_SLIMPRO_I2C_V2)
|
||||
ctx->pcc_comm_addr = memremap(
|
||||
ctx->comm_base_addr,
|
||||
cppc_ss->length,
|
||||
MEMREMAP_WT);
|
||||
else
|
||||
ctx->pcc_comm_addr = memremap(
|
||||
ctx->comm_base_addr,
|
||||
cppc_ss->length,
|
||||
MEMREMAP_WB);
|
||||
} else {
|
||||
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
|
||||
rc = -ENOENT;
|
||||
|
|
@ -581,7 +602,8 @@ MODULE_DEVICE_TABLE(of, xgene_slimpro_i2c_dt_ids);
|
|||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id xgene_slimpro_i2c_acpi_ids[] = {
|
||||
{"APMC0D40", 0},
|
||||
{"APMC0D40", XGENE_SLIMPRO_I2C_V1},
|
||||
{"APMC0D8B", XGENE_SLIMPRO_I2C_V2},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
|
|
@ -81,9 +82,12 @@ struct xlp9xx_i2c_dev {
|
|||
struct completion msg_complete;
|
||||
int irq;
|
||||
bool msg_read;
|
||||
bool len_recv;
|
||||
bool client_pec;
|
||||
u32 __iomem *base;
|
||||
u32 msg_buf_remaining;
|
||||
u32 msg_len;
|
||||
u32 ip_clk_hz;
|
||||
u32 clk_hz;
|
||||
u32 msg_err;
|
||||
u8 *msg_buf;
|
||||
|
|
@ -141,10 +145,25 @@ static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv)
|
|||
static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
|
||||
{
|
||||
u32 len, i;
|
||||
u8 *buf = priv->msg_buf;
|
||||
u8 rlen, *buf = priv->msg_buf;
|
||||
|
||||
len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
|
||||
XLP9XX_I2C_FIFO_WCNT_MASK;
|
||||
if (!len)
|
||||
return;
|
||||
if (priv->len_recv) {
|
||||
/* read length byte */
|
||||
rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
|
||||
*buf++ = rlen;
|
||||
len--;
|
||||
if (priv->client_pec)
|
||||
++rlen;
|
||||
/* update remaining bytes and message length */
|
||||
priv->msg_buf_remaining = rlen;
|
||||
priv->msg_len = rlen + 1;
|
||||
priv->len_recv = false;
|
||||
}
|
||||
|
||||
len = min(priv->msg_buf_remaining, len);
|
||||
for (i = 0; i < len; i++, buf++)
|
||||
*buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
|
||||
|
|
@ -213,7 +232,7 @@ static int xlp9xx_i2c_init(struct xlp9xx_i2c_dev *priv)
|
|||
* The controller uses 5 * SCL clock internally.
|
||||
* So prescale value should be divided by 5.
|
||||
*/
|
||||
prescale = DIV_ROUND_UP(XLP9XX_I2C_IP_CLK_FREQ, priv->clk_hz);
|
||||
prescale = DIV_ROUND_UP(priv->ip_clk_hz, priv->clk_hz);
|
||||
prescale = ((prescale - 8) / 5) - 1;
|
||||
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_RST);
|
||||
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_EN |
|
||||
|
|
@ -228,7 +247,7 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
|
|||
int last_msg)
|
||||
{
|
||||
unsigned long timeleft;
|
||||
u32 intr_mask, cmd, val;
|
||||
u32 intr_mask, cmd, val, len;
|
||||
|
||||
priv->msg_buf = msg->buf;
|
||||
priv->msg_buf_remaining = priv->msg_len = msg->len;
|
||||
|
|
@ -261,9 +280,13 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
|
|||
else
|
||||
val &= ~XLP9XX_I2C_CTRL_ADDMODE;
|
||||
|
||||
priv->len_recv = msg->flags & I2C_M_RECV_LEN;
|
||||
len = priv->len_recv ? XLP9XX_I2C_FIFO_SIZE : msg->len;
|
||||
priv->client_pec = msg->flags & I2C_CLIENT_PEC;
|
||||
|
||||
/* set data length to be transferred */
|
||||
val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
|
||||
(msg->len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
|
||||
(len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
|
||||
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
|
||||
|
||||
/* fill fifo during tx */
|
||||
|
|
@ -310,6 +333,9 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* update msg->len with actual received length */
|
||||
if (msg->flags & I2C_M_RECV_LEN)
|
||||
msg->len = priv->msg_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -342,9 +368,19 @@ static const struct i2c_algorithm xlp9xx_i2c_algo = {
|
|||
static int xlp9xx_i2c_get_frequency(struct platform_device *pdev,
|
||||
struct xlp9xx_i2c_dev *priv)
|
||||
{
|
||||
struct clk *clk;
|
||||
u32 freq;
|
||||
int err;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
priv->ip_clk_hz = XLP9XX_I2C_IP_CLK_FREQ;
|
||||
dev_dbg(&pdev->dev, "using default input frequency %u\n",
|
||||
priv->ip_clk_hz);
|
||||
} else {
|
||||
priv->ip_clk_hz = clk_get_rate(clk);
|
||||
}
|
||||
|
||||
err = device_property_read_u32(&pdev->dev, "clock-frequency", &freq);
|
||||
if (err) {
|
||||
freq = XLP9XX_I2C_DEFAULT_FREQ;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqflags.h>
|
||||
|
|
@ -205,9 +206,6 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
|
|||
*/
|
||||
while (i++ < RECOVERY_CLK_CNT * 2) {
|
||||
if (val) {
|
||||
/* Break if SDA is high */
|
||||
if (bri->get_sda && bri->get_sda(adap))
|
||||
break;
|
||||
/* SCL shouldn't be low here */
|
||||
if (!bri->get_scl(adap)) {
|
||||
dev_err(&adap->dev,
|
||||
|
|
@ -215,6 +213,9 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
|
|||
ret = -EBUSY;
|
||||
break;
|
||||
}
|
||||
/* Break if SDA is high */
|
||||
if (bri->get_sda && bri->get_sda(adap))
|
||||
break;
|
||||
}
|
||||
|
||||
val = !val;
|
||||
|
|
@ -222,6 +223,10 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
|
|||
ndelay(RECOVERY_NDELAY);
|
||||
}
|
||||
|
||||
/* check if recovery actually succeeded */
|
||||
if (bri->get_sda && !bri->get_sda(adap))
|
||||
ret = -EBUSY;
|
||||
|
||||
if (bri->unprepare_recovery)
|
||||
bri->unprepare_recovery(adap);
|
||||
|
||||
|
|
@ -666,10 +671,16 @@ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter,
|
|||
}
|
||||
|
||||
static void i2c_dev_set_name(struct i2c_adapter *adap,
|
||||
struct i2c_client *client)
|
||||
struct i2c_client *client,
|
||||
struct i2c_board_info const *info)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&client->dev);
|
||||
|
||||
if (info && info->dev_name) {
|
||||
dev_set_name(&client->dev, "i2c-%s", info->dev_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (adev) {
|
||||
dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev));
|
||||
return;
|
||||
|
|
@ -766,7 +777,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
|||
client->dev.of_node = info->of_node;
|
||||
client->dev.fwnode = info->fwnode;
|
||||
|
||||
i2c_dev_set_name(adap, client);
|
||||
i2c_dev_set_name(adap, client, info);
|
||||
|
||||
if (info->properties) {
|
||||
status = device_add_properties(&client->dev, info->properties);
|
||||
|
|
@ -808,6 +819,8 @@ EXPORT_SYMBOL_GPL(i2c_new_device);
|
|||
*/
|
||||
void i2c_unregister_device(struct i2c_client *client)
|
||||
{
|
||||
if (!client)
|
||||
return;
|
||||
if (client->dev.of_node)
|
||||
of_node_clear_flag(client->dev.of_node, OF_POPULATED);
|
||||
if (ACPI_COMPANION(&client->dev))
|
||||
|
|
@ -1259,6 +1272,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||
goto out_list;
|
||||
}
|
||||
|
||||
res = of_i2c_setup_smbus_alert(adap);
|
||||
if (res)
|
||||
goto out_reg;
|
||||
|
||||
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
||||
|
||||
pm_runtime_no_callbacks(&adap->dev);
|
||||
|
|
@ -1290,6 +1307,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||
|
||||
return 0;
|
||||
|
||||
out_reg:
|
||||
init_completion(&adap->dev_released);
|
||||
device_unregister(&adap->dev);
|
||||
wait_for_completion(&adap->dev_released);
|
||||
out_list:
|
||||
mutex_lock(&core_lock);
|
||||
idr_remove(&i2c_adapter_idr, adap->nr);
|
||||
|
|
@ -1417,8 +1438,7 @@ static int __unregister_client(struct device *dev, void *dummy)
|
|||
static int __unregister_dummy(struct device *dev, void *dummy)
|
||||
{
|
||||
struct i2c_client *client = i2c_verify_client(dev);
|
||||
if (client)
|
||||
i2c_unregister_device(client);
|
||||
i2c_unregister_device(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/smbus.h>
|
||||
|
|
@ -592,3 +593,57 @@ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
|
|||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);
|
||||
|
||||
/**
|
||||
* i2c_setup_smbus_alert - Setup SMBus alert support
|
||||
* @adapter: the target adapter
|
||||
* @setup: setup data for the SMBus alert handler
|
||||
* Context: can sleep
|
||||
*
|
||||
* Setup handling of the SMBus alert protocol on a given I2C bus segment.
|
||||
*
|
||||
* Handling can be done either through our IRQ handler, or by the
|
||||
* adapter (from its handler, periodic polling, or whatever).
|
||||
*
|
||||
* NOTE that if we manage the IRQ, we *MUST* know if it's level or
|
||||
* edge triggered in order to hand it to the workqueue correctly.
|
||||
* If triggering the alert seems to wedge the system, you probably
|
||||
* should have said it's level triggered.
|
||||
*
|
||||
* This returns the ara client, which should be saved for later use with
|
||||
* i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
|
||||
* to indicate an error.
|
||||
*/
|
||||
struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
|
||||
struct i2c_smbus_alert_setup *setup)
|
||||
{
|
||||
struct i2c_board_info ara_board_info = {
|
||||
I2C_BOARD_INFO("smbus_alert", 0x0c),
|
||||
.platform_data = setup,
|
||||
};
|
||||
|
||||
return i2c_new_device(adapter, &ara_board_info);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
|
||||
int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
int irq;
|
||||
|
||||
irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
|
||||
"smbus_alert");
|
||||
if (irq == -EINVAL || irq == -ENODATA)
|
||||
return 0;
|
||||
else if (irq < 0)
|
||||
return irq;
|
||||
|
||||
client = i2c_setup_smbus_alert(adapter, NULL);
|
||||
if (!client)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct i2c_smbus_alert {
|
||||
unsigned int alert_edge_triggered:1;
|
||||
int irq;
|
||||
struct work_struct alert;
|
||||
struct i2c_client *ara; /* Alert response address */
|
||||
};
|
||||
|
|
@ -72,13 +71,12 @@ static int smbus_do_alert(struct device *dev, void *addrp)
|
|||
* The alert IRQ handler needs to hand work off to a task which can issue
|
||||
* SMBus calls, because those sleeping calls can't be made in IRQ context.
|
||||
*/
|
||||
static void smbus_alert(struct work_struct *work)
|
||||
static irqreturn_t smbus_alert(int irq, void *d)
|
||||
{
|
||||
struct i2c_smbus_alert *alert;
|
||||
struct i2c_smbus_alert *alert = d;
|
||||
struct i2c_client *ara;
|
||||
unsigned short prev_addr = 0; /* Not a valid address */
|
||||
|
||||
alert = container_of(work, struct i2c_smbus_alert, alert);
|
||||
ara = alert->ara;
|
||||
|
||||
for (;;) {
|
||||
|
|
@ -115,21 +113,17 @@ static void smbus_alert(struct work_struct *work)
|
|||
prev_addr = data.addr;
|
||||
}
|
||||
|
||||
/* We handled all alerts; re-enable level-triggered IRQs */
|
||||
if (!alert->alert_edge_triggered)
|
||||
enable_irq(alert->irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t smbalert_irq(int irq, void *d)
|
||||
static void smbalert_work(struct work_struct *work)
|
||||
{
|
||||
struct i2c_smbus_alert *alert = d;
|
||||
struct i2c_smbus_alert *alert;
|
||||
|
||||
/* Disable level-triggered IRQs until we handle them */
|
||||
if (!alert->alert_edge_triggered)
|
||||
disable_irq_nosync(irq);
|
||||
alert = container_of(work, struct i2c_smbus_alert, alert);
|
||||
|
||||
smbus_alert(0, alert);
|
||||
|
||||
schedule_work(&alert->alert);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Setup SMBALERT# infrastructure */
|
||||
|
|
@ -139,28 +133,35 @@ static int smbalert_probe(struct i2c_client *ara,
|
|||
struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
|
||||
struct i2c_smbus_alert *alert;
|
||||
struct i2c_adapter *adapter = ara->adapter;
|
||||
int res;
|
||||
int res, irq;
|
||||
|
||||
alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
|
||||
GFP_KERNEL);
|
||||
if (!alert)
|
||||
return -ENOMEM;
|
||||
|
||||
alert->alert_edge_triggered = setup->alert_edge_triggered;
|
||||
alert->irq = setup->irq;
|
||||
INIT_WORK(&alert->alert, smbus_alert);
|
||||
if (setup) {
|
||||
irq = setup->irq;
|
||||
} else {
|
||||
irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
|
||||
if (irq <= 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
INIT_WORK(&alert->alert, smbalert_work);
|
||||
alert->ara = ara;
|
||||
|
||||
if (setup->irq > 0) {
|
||||
res = devm_request_irq(&ara->dev, setup->irq, smbalert_irq,
|
||||
0, "smbus_alert", alert);
|
||||
if (irq > 0) {
|
||||
res = devm_request_threaded_irq(&ara->dev, irq,
|
||||
NULL, smbus_alert,
|
||||
IRQF_SHARED | IRQF_ONESHOT,
|
||||
"smbus_alert", alert);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(ara, alert);
|
||||
dev_info(&adapter->dev, "supports SMBALERT#, %s trigger\n",
|
||||
setup->alert_edge_triggered ? "edge" : "level");
|
||||
dev_info(&adapter->dev, "supports SMBALERT#\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -189,38 +190,6 @@ static struct i2c_driver smbalert_driver = {
|
|||
.id_table = smbalert_ids,
|
||||
};
|
||||
|
||||
/**
|
||||
* i2c_setup_smbus_alert - Setup SMBus alert support
|
||||
* @adapter: the target adapter
|
||||
* @setup: setup data for the SMBus alert handler
|
||||
* Context: can sleep
|
||||
*
|
||||
* Setup handling of the SMBus alert protocol on a given I2C bus segment.
|
||||
*
|
||||
* Handling can be done either through our IRQ handler, or by the
|
||||
* adapter (from its handler, periodic polling, or whatever).
|
||||
*
|
||||
* NOTE that if we manage the IRQ, we *MUST* know if it's level or
|
||||
* edge triggered in order to hand it to the workqueue correctly.
|
||||
* If triggering the alert seems to wedge the system, you probably
|
||||
* should have said it's level triggered.
|
||||
*
|
||||
* This returns the ara client, which should be saved for later use with
|
||||
* i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
|
||||
* to indicate an error.
|
||||
*/
|
||||
struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
|
||||
struct i2c_smbus_alert_setup *setup)
|
||||
{
|
||||
struct i2c_board_info ara_board_info = {
|
||||
I2C_BOARD_INFO("smbus_alert", 0x0c),
|
||||
.platform_data = setup,
|
||||
};
|
||||
|
||||
return i2c_new_device(adapter, &ara_board_info);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
|
||||
|
||||
/**
|
||||
* i2c_handle_smbus_alert - Handle an SMBus alert
|
||||
* @ara: the ARA client on the relevant adapter
|
||||
|
|
|
|||
|
|
@ -246,36 +246,6 @@ static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
|
|||
return handled ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static void pca954x_irq_mask(struct irq_data *idata)
|
||||
{
|
||||
struct pca954x *data = irq_data_get_irq_chip_data(idata);
|
||||
unsigned int pos = idata->hwirq;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&data->lock, flags);
|
||||
|
||||
data->irq_mask &= ~BIT(pos);
|
||||
if (!data->irq_mask)
|
||||
disable_irq(data->client->irq);
|
||||
|
||||
raw_spin_unlock_irqrestore(&data->lock, flags);
|
||||
}
|
||||
|
||||
static void pca954x_irq_unmask(struct irq_data *idata)
|
||||
{
|
||||
struct pca954x *data = irq_data_get_irq_chip_data(idata);
|
||||
unsigned int pos = idata->hwirq;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&data->lock, flags);
|
||||
|
||||
if (!data->irq_mask)
|
||||
enable_irq(data->client->irq);
|
||||
data->irq_mask |= BIT(pos);
|
||||
|
||||
raw_spin_unlock_irqrestore(&data->lock, flags);
|
||||
}
|
||||
|
||||
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
|
||||
{
|
||||
if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW)
|
||||
|
|
@ -285,8 +255,6 @@ static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
|
|||
|
||||
static struct irq_chip pca954x_irq_chip = {
|
||||
.name = "i2c-mux-pca954x",
|
||||
.irq_mask = pca954x_irq_mask,
|
||||
.irq_unmask = pca954x_irq_unmask,
|
||||
.irq_set_type = pca954x_irq_set_type,
|
||||
};
|
||||
|
||||
|
|
@ -294,7 +262,7 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
|
|||
{
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
int c, err, irq;
|
||||
int c, irq;
|
||||
|
||||
if (!data->chip->has_irq || client->irq <= 0)
|
||||
return 0;
|
||||
|
|
@ -309,29 +277,31 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
|
|||
|
||||
for (c = 0; c < data->chip->nchans; c++) {
|
||||
irq = irq_create_mapping(data->irq, c);
|
||||
if (!irq) {
|
||||
dev_err(&client->dev, "failed irq create map\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
irq_set_chip_data(irq, data);
|
||||
irq_set_chip_and_handler(irq, &pca954x_irq_chip,
|
||||
handle_simple_irq);
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL,
|
||||
pca954x_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
"pca954x", data);
|
||||
if (err)
|
||||
goto err_req_irq;
|
||||
|
||||
disable_irq(data->client->irq);
|
||||
|
||||
return 0;
|
||||
err_req_irq:
|
||||
for (c = 0; c < data->chip->nchans; c++) {
|
||||
irq = irq_find_mapping(data->irq, c);
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
irq_domain_remove(data->irq);
|
||||
}
|
||||
|
||||
return err;
|
||||
static void pca954x_cleanup(struct i2c_mux_core *muxc)
|
||||
{
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
int c, irq;
|
||||
|
||||
if (data->irq) {
|
||||
for (c = 0; c < data->chip->nchans; c++) {
|
||||
irq = irq_find_mapping(data->irq, c);
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
irq_domain_remove(data->irq);
|
||||
}
|
||||
i2c_mux_del_adapters(muxc);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -391,7 +361,7 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
|
||||
ret = pca954x_irq_setup(muxc);
|
||||
if (ret)
|
||||
goto fail_del_adapters;
|
||||
goto fail_cleanup;
|
||||
|
||||
/* Now create an adapter for each channel */
|
||||
for (num = 0; num < data->chip->nchans; num++) {
|
||||
|
|
@ -414,7 +384,16 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
|
||||
ret = i2c_mux_add_adapter(muxc, force, num, class);
|
||||
if (ret)
|
||||
goto fail_del_adapters;
|
||||
goto fail_cleanup;
|
||||
}
|
||||
|
||||
if (data->irq) {
|
||||
ret = devm_request_threaded_irq(&client->dev, data->client->irq,
|
||||
NULL, pca954x_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
"pca954x", data);
|
||||
if (ret)
|
||||
goto fail_cleanup;
|
||||
}
|
||||
|
||||
dev_info(&client->dev,
|
||||
|
|
@ -424,26 +403,16 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
|
||||
return 0;
|
||||
|
||||
fail_del_adapters:
|
||||
i2c_mux_del_adapters(muxc);
|
||||
fail_cleanup:
|
||||
pca954x_cleanup(muxc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pca954x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
int c, irq;
|
||||
|
||||
if (data->irq) {
|
||||
for (c = 0; c < data->chip->nchans; c++) {
|
||||
irq = irq_find_mapping(data->irq, c);
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
irq_domain_remove(data->irq);
|
||||
}
|
||||
|
||||
i2c_mux_del_adapters(muxc);
|
||||
pca954x_cleanup(muxc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,9 +107,9 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
|||
put_device(&adapter->dev);
|
||||
|
||||
mux->data.n_values = of_get_child_count(np);
|
||||
if (of_find_property(np, "little-endian", NULL)) {
|
||||
if (of_property_read_bool(np, "little-endian")) {
|
||||
mux->data.little_endian = true;
|
||||
} else if (of_find_property(np, "big-endian", NULL)) {
|
||||
} else if (of_property_read_bool(np, "big-endian")) {
|
||||
mux->data.little_endian = false;
|
||||
} else {
|
||||
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \
|
||||
|
|
@ -122,10 +122,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
|||
#error Endianness not defined?
|
||||
#endif
|
||||
}
|
||||
if (of_find_property(np, "write-only", NULL))
|
||||
mux->data.write_only = true;
|
||||
else
|
||||
mux->data.write_only = false;
|
||||
mux->data.write_only = of_property_read_bool(np, "write-only");
|
||||
|
||||
values = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*mux->data.values) * mux->data.n_values,
|
||||
|
|
|
|||
Loading…
Reference in a new issue