summaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig45
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/ab3100-otp.c14
-rw-r--r--drivers/mfd/ab8500-gpadc.c16
-rw-r--r--drivers/mfd/as3711.c27
-rw-r--r--drivers/mfd/cros_ec.c196
-rw-r--r--drivers/mfd/cros_ec_i2c.c201
-rw-r--r--drivers/mfd/cros_ec_spi.c375
-rw-r--r--drivers/mfd/davinci_voicecodec.c12
-rw-r--r--drivers/mfd/htc-pasic3.c13
-rw-r--r--drivers/mfd/omap-usb-host.c6
-rw-r--r--drivers/mfd/palmas.c42
-rw-r--r--drivers/mfd/rtsx_pcr.c6
-rw-r--r--drivers/mfd/stmpe.c8
-rw-r--r--drivers/mfd/syscon.c80
-rw-r--r--drivers/mfd/tps65912-core.c1
-rw-r--r--drivers/mfd/twl4030-audio.c2
-rw-r--r--drivers/mfd/twl4030-madc.c2
18 files changed, 949 insertions, 100 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 671f5b171c7..d5b9b051ef1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -21,6 +21,35 @@ config MFD_88PM860X
select individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
+config MFD_CROS_EC
+ tristate "Support ChromeOS Embedded Controller"
+ select MFD_CORE
+ help
+ If you say Y here you get support for the ChromeOS Embedded
+ Controller (EC) providing keyboard, battery and power services.
+ You also ned to enable the driver for the bus you are using. The
+ protocol for talking to the EC is defined by the bus driver.
+
+config MFD_CROS_EC_I2C
+ tristate "ChromeOS Embedded Controller (I2C)"
+ depends on MFD_CROS_EC && I2C
+
+ help
+ If you say Y here, you get support for talking to the ChromeOS
+ EC through an I2C bus. This uses a simple byte-level protocol with
+ a checksum. Failing accesses will be retried three times to
+ improve reliability.
+
+config MFD_CROS_EC_SPI
+ tristate "ChromeOS Embedded Controller (SPI)"
+ depends on MFD_CROS_EC && SPI
+
+ ---help---
+ If you say Y here, you get support for talking to the ChromeOS EC
+ through a SPI bus, using a byte-level protocol. Since the EC's
+ response time cannot be guaranteed, we support ignoring
+ 'pre-amble' bytes before the response actually starts.
+
config MFD_88PM800
tristate "Support Marvell 88PM800"
depends on I2C=y && GENERIC_HARDIRQS
@@ -848,16 +877,10 @@ config AB3100_OTP
programmable memory) support. This exposes a sysfs file to read
out OTP values.
-config EZX_PCAP
- bool "PCAP Support"
- depends on GENERIC_HARDIRQS && SPI_MASTER
- help
- This enables the PCAP ASIC present on EZX Phones. This is
- needed for MMC, TouchScreen, Sound, USB, etc..
-
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
+ select POWER_SUPPLY
select MFD_CORE
select IRQ_DOMAIN
help
@@ -882,6 +905,13 @@ config AB8500_GPADC
help
AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage
+config EZX_PCAP
+ bool "PCAP Support"
+ depends on GENERIC_HARDIRQS && SPI_MASTER
+ help
+ This enables the PCAP ASIC present on EZX Phones. This is
+ needed for MMC, TouchScreen, Sound, USB, etc..
+
config MFD_DB8500_PRCMU
bool "ST-Ericsson DB8500 Power Reset Control Management Unit"
depends on UX500_SOC_DB8500
@@ -1070,7 +1100,6 @@ config MFD_STA2X11
config MFD_SYSCON
bool "System Controller Register R/W Based on Regmap"
- depends on OF
select REGMAP_MMIO
help
Select this option to enable accessing system control registers
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b90409c2366..f62a583b5c8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,6 +8,9 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
+obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
+obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
+obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index 8440010eb2b..d7ce016029f 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -248,19 +248,7 @@ static struct platform_driver ab3100_otp_driver = {
.remove = __exit_p(ab3100_otp_remove),
};
-static int __init ab3100_otp_init(void)
-{
- return platform_driver_probe(&ab3100_otp_driver,
- ab3100_otp_probe);
-}
-
-static void __exit ab3100_otp_exit(void)
-{
- platform_driver_unregister(&ab3100_otp_driver);
-}
-
-module_init(ab3100_otp_init);
-module_exit(ab3100_otp_exit);
+module_platform_driver_probe(ab3100_otp_driver, ab3100_otp_probe);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index af1db2a45e8..65f72284185 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -878,9 +878,12 @@ static int ab8500_gpadc_runtime_suspend(struct device *dev)
static int ab8500_gpadc_runtime_resume(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+ int ret;
- regulator_enable(gpadc->regu);
- return 0;
+ ret = regulator_enable(gpadc->regu);
+ if (ret)
+ dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret);
+ return ret;
}
static int ab8500_gpadc_runtime_idle(struct device *dev)
@@ -968,7 +971,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
}
/* VTVout LDO used to power up ab8500-GPADC */
- gpadc->regu = regulator_get(&pdev->dev, "vddadc");
+ gpadc->regu = devm_regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
ret = PTR_ERR(gpadc->regu);
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
@@ -977,7 +980,11 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gpadc);
- regulator_enable(gpadc->regu);
+ ret = regulator_enable(gpadc->regu);
+ if (ret) {
+ dev_err(gpadc->dev, "Failed to enable vtvout LDO: %d\n", ret);
+ goto fail_enable;
+ }
pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
pm_runtime_use_autosuspend(gpadc->dev);
@@ -990,6 +997,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
return 0;
+fail_enable:
fail_irq:
free_irq(gpadc->irq_sw, gpadc);
free_irq(gpadc->irq_hw, gpadc);
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c969112..01e41416270 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,34 @@ static const struct regmap_config as3711_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+ {.compatible = "ams,as3711",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
static int as3711_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct as3711 *as3711;
- struct as3711_platform_data *pdata = client->dev.platform_data;
+ struct as3711_platform_data *pdata;
unsigned int id1, id2;
int ret;
- if (!pdata)
- dev_dbg(&client->dev, "Platform data not found\n");
+ if (!client->dev.of_node) {
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ dev_dbg(&client->dev, "Platform data not found\n");
+ } else {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allocate pdata\n");
+ return -ENOMEM;
+ }
+ }
as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
if (!as3711) {
@@ -193,7 +211,8 @@ static struct i2c_driver as3711_i2c_driver = {
.driver = {
.name = "as3711",
.owner = THIS_MODULE,
- },
+ .of_match_table = of_match_ptr(as3711_of_match),
+ },
.probe = as3711_i2c_probe,
.remove = as3711_i2c_remove,
.id_table = as3711_i2c_id,
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
new file mode 100644
index 00000000000..10cd14e35eb
--- /dev/null
+++ b/drivers/mfd/cros_ec.c
@@ -0,0 +1,196 @@
+/*
+ * ChromeOS EC multi-function device
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * The ChromeOS EC multi function device is used to mux all the requests
+ * to the EC device for its multiple features: keyboard controller,
+ * battery charging and regulator control, firmware update.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+
+int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
+ struct cros_ec_msg *msg)
+{
+ uint8_t *out;
+ int csum, i;
+
+ BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE);
+ out = ec_dev->dout;
+ out[0] = EC_CMD_VERSION0 + msg->version;
+ out[1] = msg->cmd;
+ out[2] = msg->out_len;
+ csum = out[0] + out[1] + out[2];
+ for (i = 0; i < msg->out_len; i++)
+ csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i];
+ out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff);
+
+ return EC_MSG_TX_PROTO_BYTES + msg->out_len;
+}
+EXPORT_SYMBOL(cros_ec_prepare_tx);
+
+static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev,
+ uint16_t cmd, void *out_buf, int out_len,
+ void *in_buf, int in_len)
+{
+ struct cros_ec_msg msg;
+
+ msg.version = cmd >> 8;
+ msg.cmd = cmd & 0xff;
+ msg.out_buf = out_buf;
+ msg.out_len = out_len;
+ msg.in_buf = in_buf;
+ msg.in_len = in_len;
+
+ return ec_dev->command_xfer(ec_dev, &msg);
+}
+
+static int cros_ec_command_recv(struct cros_ec_device *ec_dev,
+ uint16_t cmd, void *buf, int buf_len)
+{
+ return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len);
+}
+
+static int cros_ec_command_send(struct cros_ec_device *ec_dev,
+ uint16_t cmd, void *buf, int buf_len)
+{
+ return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0);
+}
+
+static irqreturn_t ec_irq_thread(int irq, void *data)
+{
+ struct cros_ec_device *ec_dev = data;
+
+ if (device_may_wakeup(ec_dev->dev))
+ pm_wakeup_event(ec_dev->dev, 0);
+
+ blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);
+
+ return IRQ_HANDLED;
+}
+
+static struct mfd_cell cros_devs[] = {
+ {
+ .name = "cros-ec-keyb",
+ .id = 1,
+ .of_compatible = "google,cros-ec-keyb",
+ },
+};
+
+int cros_ec_register(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+ int err = 0;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
+
+ ec_dev->command_send = cros_ec_command_send;
+ ec_dev->command_recv = cros_ec_command_recv;
+ ec_dev->command_sendrecv = cros_ec_command_sendrecv;
+
+ if (ec_dev->din_size) {
+ ec_dev->din = kmalloc(ec_dev->din_size, GFP_KERNEL);
+ if (!ec_dev->din) {
+ err = -ENOMEM;
+ goto fail_din;
+ }
+ }
+ if (ec_dev->dout_size) {
+ ec_dev->dout = kmalloc(ec_dev->dout_size, GFP_KERNEL);
+ if (!ec_dev->dout) {
+ err = -ENOMEM;
+ goto fail_dout;
+ }
+ }
+
+ if (!ec_dev->irq) {
+ dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
+ goto fail_irq;
+ }
+
+ err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "chromeos-ec", ec_dev);
+ if (err) {
+ dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
+ goto fail_irq;
+ }
+
+ err = mfd_add_devices(dev, 0, cros_devs,
+ ARRAY_SIZE(cros_devs),
+ NULL, ec_dev->irq, NULL);
+ if (err) {
+ dev_err(dev, "failed to add mfd devices\n");
+ goto fail_mfd;
+ }
+
+ dev_info(dev, "Chrome EC (%s)\n", ec_dev->name);
+
+ return 0;
+
+fail_mfd:
+ free_irq(ec_dev->irq, ec_dev);
+fail_irq:
+ kfree(ec_dev->dout);
+fail_dout:
+ kfree(ec_dev->din);
+fail_din:
+ return err;
+}
+EXPORT_SYMBOL(cros_ec_register);
+
+int cros_ec_remove(struct cros_ec_device *ec_dev)
+{
+ mfd_remove_devices(ec_dev->dev);
+ free_irq(ec_dev->irq, ec_dev);
+ kfree(ec_dev->dout);
+ kfree(ec_dev->din);
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_remove);
+
+#ifdef CONFIG_PM_SLEEP
+int cros_ec_suspend(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+
+ if (device_may_wakeup(dev))
+ ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
+
+ disable_irq(ec_dev->irq);
+ ec_dev->was_wake_device = ec_dev->wake_enabled;
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_suspend);
+
+int cros_ec_resume(struct cros_ec_device *ec_dev)
+{
+ enable_irq(ec_dev->irq);
+
+ if (ec_dev->wake_enabled) {
+ disable_irq_wake(ec_dev->irq);
+ ec_dev->wake_enabled = 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_resume);
+
+#endif
diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c
new file mode 100644
index 00000000000..123044608b6
--- /dev/null
+++ b/drivers/mfd/cros_ec_i2c.c
@@ -0,0 +1,201 @@
+/*
+ * ChromeOS EC multi-function device (I2C)
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static inline struct cros_ec_device *to_ec_dev(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_get_clientdata(client);
+}
+
+static int cros_ec_command_xfer(struct cros_ec_device *ec_dev,
+ struct cros_ec_msg *msg)
+{
+ struct i2c_client *client = ec_dev->priv;
+ int ret = -ENOMEM;
+ int i;
+ int packet_len;
+ u8 *out_buf = NULL;
+ u8 *in_buf = NULL;
+ u8 sum;
+ struct i2c_msg i2c_msg[2];
+
+ i2c_msg[0].addr = client->addr;
+ i2c_msg[0].flags = 0;
+ i2c_msg[1].addr = client->addr;
+ i2c_msg[1].flags = I2C_M_RD;
+
+ /*
+ * allocate larger packet (one byte for checksum, one byte for
+ * length, and one for result code)
+ */
+ packet_len = msg->in_len + 3;
+ in_buf = kzalloc(packet_len, GFP_KERNEL);
+ if (!in_buf)
+ goto done;
+ i2c_msg[1].len = packet_len;
+ i2c_msg[1].buf = (char *)in_buf;
+
+ /*
+ * allocate larger packet (one byte for checksum, one for
+ * command code, one for length, and one for command version)
+ */
+ packet_len = msg->out_len + 4;
+ out_buf = kzalloc(packet_len, GFP_KERNEL);
+ if (!out_buf)
+ goto done;
+ i2c_msg[0].len = packet_len;
+ i2c_msg[0].buf = (char *)out_buf;
+
+ out_buf[0] = EC_CMD_VERSION0 + msg->version;
+ out_buf[1] = msg->cmd;
+ out_buf[2] = msg->out_len;
+
+ /* copy message payload and compute checksum */
+ sum = out_buf[0] + out_buf[1] + out_buf[2];
+ for (i = 0; i < msg->out_len; i++) {
+ out_buf[3 + i] = msg->out_buf[i];
+ sum += out_buf[3 + i];
+ }
+ out_buf[3 + msg->out_len] = sum;
+
+ /* send command to EC and read answer */
+ ret = i2c_transfer(client->adapter, i2c_msg, 2);
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "i2c transfer failed: %d\n", ret);
+ goto done;
+ } else if (ret != 2) {
+ dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
+ ret = -EIO;
+ goto done;
+ }
+
+ /* check response error code */
+ if (i2c_msg[1].buf[0]) {
+ dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n",
+ msg->cmd, i2c_msg[1].buf[0]);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* copy response packet payload and compute checksum */
+ sum = in_buf[0] + in_buf[1];
+ for (i = 0; i < msg->in_len; i++) {
+ msg->in_buf[i] = in_buf[2 + i];
+ sum += in_buf[2 + i];
+ }
+ dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",
+ i2c_msg[1].len, in_buf, sum);
+ if (sum != in_buf[2 + msg->in_len]) {
+ dev_err(ec_dev->dev, "bad packet checksum\n");
+ ret = -EBADMSG;
+ goto done;
+ }
+
+ ret = 0;
+ done:
+ kfree(in_buf);
+ kfree(out_buf);
+ return ret;
+}
+
+static int cros_ec_probe_i2c(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct device *dev = &client->dev;
+ struct cros_ec_device *ec_dev = NULL;
+ int err;
+
+ ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
+ if (!ec_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ec_dev);
+ ec_dev->name = "I2C";
+ ec_dev->dev = dev;
+ ec_dev->priv = client;
+ ec_dev->irq = client->irq;
+ ec_dev->command_xfer = cros_ec_command_xfer;
+ ec_dev->ec_name = client->name;
+ ec_dev->phys_name = client->adapter->name;
+ ec_dev->parent = &client->dev;
+
+ err = cros_ec_register(ec_dev);
+ if (err) {
+ dev_err(dev, "cannot register EC\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int cros_ec_remove_i2c(struct i2c_client *client)
+{
+ struct cros_ec_device *ec_dev = i2c_get_clientdata(client);
+
+ cros_ec_remove(ec_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_i2c_suspend(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = to_ec_dev(dev);
+
+ return cros_ec_suspend(ec_dev);
+}
+
+static int cros_ec_i2c_resume(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = to_ec_dev(dev);
+
+ return cros_ec_resume(ec_dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend,
+ cros_ec_i2c_resume);
+
+static const struct i2c_device_id cros_ec_i2c_id[] = {
+ { "cros-ec-i2c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id);
+
+static struct i2c_driver cros_ec_driver = {
+ .driver = {
+ .name = "cros-ec-i2c",
+ .owner = THIS_MODULE,
+ .pm = &cros_ec_i2c_pm_ops,
+ },
+ .probe = cros_ec_probe_i2c,
+ .remove = cros_ec_remove_i2c,
+ .id_table = cros_ec_i2c_id,
+};
+
+module_i2c_driver(cros_ec_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC multi function device");
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c
new file mode 100644
index 00000000000..19193cf1e7a
--- /dev/null
+++ b/drivers/mfd/cros_ec_spi.c
@@ -0,0 +1,375 @@
+/*
+ * ChromeOS EC multi-function device (SPI)
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+
+/* The header byte, which follows the preamble */
+#define EC_MSG_HEADER 0xec
+
+/*
+ * Number of EC preamble bytes we read at a time. Since it takes
+ * about 400-500us for the EC to respond there is not a lot of
+ * point in tuning this. If the EC could respond faster then
+ * we could increase this so that might expect the preamble and
+ * message to occur in a single transaction. However, the maximum
+ * SPI transfer size is 256 bytes, so at 5MHz we need a response
+ * time of perhaps <320us (200 bytes / 1600 bits).
+ */
+#define EC_MSG_PREAMBLE_COUNT 32
+
+/*
+ * We must get a response from the EC in 5ms. This is a very long
+ * time, but the flash write command can take 2-3ms. The EC command
+ * processing is currently not very fast (about 500us). We could
+ * look at speeding this up and making the flash write command a
+ * 'slow' command, requiring a GET_STATUS wait loop, like flash
+ * erase.
+ */
+#define EC_MSG_DEADLINE_MS 5
+
+/*
+ * Time between raising the SPI chip select (for the end of a
+ * transaction) and dropping it again (for the next transaction).
+ * If we go too fast, the EC will miss the transaction. It seems
+ * that 50us is enough with the 16MHz STM32 EC.
+ */
+#define EC_SPI_RECOVERY_TIME_NS (50 * 1000)
+
+/**
+ * struct cros_ec_spi - information about a SPI-connected EC
+ *
+ * @spi: SPI device we are connected to
+ * @last_transfer_ns: time that we last finished a transfer, or 0 if there
+ * if no record
+ */
+struct cros_ec_spi {
+ struct spi_device *spi;
+ s64 last_transfer_ns;
+};
+
+static void debug_packet(struct device *dev, const char *name, u8 *ptr,
+ int len)
+{
+#ifdef DEBUG
+ int i;
+
+ dev_dbg(dev, "%s: ", name);
+ for (i = 0; i < len; i++)
+ dev_cont(dev, " %02x", ptr[i]);
+#endif
+}
+
+/**
+ * cros_ec_spi_receive_response - Receive a response from the EC.
+ *
+ * This function has two phases: reading the preamble bytes (since if we read
+ * data from the EC before it is ready to send, we just get preamble) and
+ * reading the actual message.
+ *
+ * The received data is placed into ec_dev->din.
+ *
+ * @ec_dev: ChromeOS EC device
+ * @need_len: Number of message bytes we need to read
+ */
+static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
+ int need_len)
+{
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_transfer trans;
+ struct spi_message msg;
+ u8 *ptr, *end;
+ int ret;
+ unsigned long deadline;
+ int todo;
+
+ /* Receive data until we see the header byte */
+ deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
+ do {
+ memset(&trans, '\0', sizeof(trans));
+ trans.cs_change = 1;
+ trans.rx_buf = ptr = ec_dev->din;
+ trans.len = EC_MSG_PREAMBLE_COUNT;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+ ret = spi_sync(ec_spi->spi, &msg);
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
+ if (*ptr == EC_MSG_HEADER) {
+ dev_dbg(ec_dev->dev, "msg found at %ld\n",
+ ptr - ec_dev->din);
+ break;
+ }
+ }
+
+ if (time_after(jiffies, deadline)) {
+ dev_warn(ec_dev->dev, "EC failed to respond in time\n");
+ return -ETIMEDOUT;
+ }
+ } while (ptr == end);
+
+ /*
+ * ptr now points to the header byte. Copy any valid data to the
+ * start of our buffer
+ */
+ todo = end - ++ptr;
+ BUG_ON(todo < 0 || todo > ec_dev->din_size);
+ todo = min(todo, need_len);
+ memmove(ec_dev->din, ptr, todo);
+ ptr = ec_dev->din + todo;
+ dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
+ need_len, todo);
+ need_len -= todo;
+
+ /* Receive data until we have it all */
+ while (need_len > 0) {
+ /*
+ * We can't support transfers larger than the SPI FIFO size
+ * unless we have DMA. We don't have DMA on the ISP SPI ports
+ * for Exynos. We need a way of asking SPI driver for
+ * maximum-supported transfer size.
+ */
+ todo = min(need_len, 256);
+ dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%ld\n",
+ todo, need_len, ptr - ec_dev->din);
+
+ memset(&trans, '\0', sizeof(trans));
+ trans.cs_change = 1;
+ trans.rx_buf = ptr;
+ trans.len = todo;
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+
+ /* send command to EC and read answer */
+ BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo >
+ ec_dev->din_size);
+ ret = spi_sync(ec_spi->spi, &msg);
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ debug_packet(ec_dev->dev, "interim", ptr, todo);
+ ptr += todo;
+ need_len -= todo;
+ }
+
+ dev_dbg(ec_dev->dev, "loop done, ptr=%ld\n", ptr - ec_dev->din);
+
+ return 0;
+}
+
+/**
+ * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply
+ *
+ * @ec_dev: ChromeOS EC device
+ * @ec_msg: Message to transfer
+ */
+static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
+ struct cros_ec_msg *ec_msg)
+{
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_transfer trans;
+ struct spi_message msg;
+ int i, len;
+ u8 *ptr;
+ int sum;
+ int ret = 0, final_ret;
+ struct timespec ts;
+
+ len = cros_ec_prepare_tx(ec_dev, ec_msg);
+ dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
+
+ /* If it's too soon to do another transaction, wait */
+ if (ec_spi->last_transfer_ns) {
+ struct timespec ts;
+ unsigned long delay; /* The delay completed so far */
+
+ ktime_get_ts(&ts);
+ delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns;
+ if (delay < EC_SPI_RECOVERY_TIME_NS)
+ ndelay(delay);
+ }
+
+ /* Transmit phase - send our message */
+ debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
+ memset(&trans, '\0', sizeof(trans));
+ trans.tx_buf = ec_dev->dout;
+ trans.len = len;
+ trans.cs_change = 1;
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+ ret = spi_sync(ec_spi->spi, &msg);
+
+ /* Get the response */
+ if (!ret) {
+ ret = cros_ec_spi_receive_response(ec_dev,
+ ec_msg->in_len + EC_MSG_TX_PROTO_BYTES);
+ } else {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ }
+
+ /* turn off CS */
+ spi_message_init(&msg);
+ final_ret = spi_sync(ec_spi->spi, &msg);
+ ktime_get_ts(&ts);
+ ec_spi->last_transfer_ns = timespec_to_ns(&ts);
+ if (!ret)
+ ret = final_ret;
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ /* check response error code */
+ ptr = ec_dev->din;
+ if (ptr[0]) {
+ dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n",
+ ec_msg->cmd, ptr[0]);
+ debug_packet(ec_dev->dev, "in_err", ptr, len);
+ return -EINVAL;
+ }
+ len = ptr[1];
+ sum = ptr[0] + ptr[1];
+ if (len > ec_msg->in_len) {
+ dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
+ len, ec_msg->in_len);
+ return -ENOSPC;
+ }
+
+ /* copy response packet payload and compute checksum */
+ for (i = 0; i < len; i++) {
+ sum += ptr[i + 2];
+ if (ec_msg->in_len)
+ ec_msg->in_buf[i] = ptr[i + 2];
+ }
+ sum &= 0xff;
+
+ debug_packet(ec_dev->dev, "in", ptr, len + 3);
+
+ if (sum != ptr[len + 2]) {
+ dev_err(ec_dev->dev,
+ "bad packet checksum, expected %02x, got %02x\n",
+ sum, ptr[len + 2]);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+static int cros_ec_probe_spi(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct cros_ec_device *ec_dev;
+ struct cros_ec_spi *ec_spi;
+ int err;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ ec_spi = devm_kzalloc(dev, sizeof(*ec_spi), GFP_KERNEL);
+ if (ec_spi == NULL)
+ return -ENOMEM;
+ ec_spi->spi = spi;
+ ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
+ if (!ec_dev)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, ec_dev);
+ ec_dev->name = "SPI";
+ ec_dev->dev = dev;
+ ec_dev->priv = ec_spi;
+ ec_dev->irq = spi->irq;
+ ec_dev->command_xfer = cros_ec_command_spi_xfer;
+ ec_dev->ec_name = ec_spi->spi->modalias;
+ ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
+ ec_dev->parent = &ec_spi->spi->dev;
+ ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT;
+ ec_dev->dout_size = EC_MSG_BYTES;
+
+ err = cros_ec_register(ec_dev);
+ if (err) {
+ dev_err(dev, "cannot register EC\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int cros_ec_remove_spi(struct spi_device *spi)
+{
+ struct cros_ec_device *ec_dev;
+
+ ec_dev = spi_get_drvdata(spi);
+ cros_ec_remove(ec_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_spi_suspend(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_suspend(ec_dev);
+}
+
+static int cros_ec_spi_resume(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_resume(ec_dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_spi_pm_ops, cros_ec_spi_suspend,
+ cros_ec_spi_resume);
+
+static const struct spi_device_id cros_ec_spi_id[] = {
+ { "cros-ec-spi", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cros_ec_spi_id);
+
+static struct spi_driver cros_ec_driver_spi = {
+ .driver = {
+ .name = "cros-ec-spi",
+ .owner = THIS_MODULE,
+ .pm = &cros_ec_spi_pm_ops,
+ },
+ .probe = cros_ec_probe_spi,
+ .remove = cros_ec_remove_spi,
+ .id_table = cros_ec_spi_id,
+};
+
+module_spi_driver(cros_ec_driver_spi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)");
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index c0bcc872af4..c60ab0c3c4d 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -177,17 +177,7 @@ static struct platform_driver davinci_vc_driver = {
.remove = davinci_vc_remove,
};
-static int __init davinci_vc_init(void)
-{
- return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe);
-}
-module_init(davinci_vc_init);
-
-static void __exit davinci_vc_exit(void)
-{
- platform_driver_unregister(&davinci_vc_driver);
-}
-module_exit(davinci_vc_exit);
+module_platform_driver_probe(davinci_vc_driver, davinci_vc_probe);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface");
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index 9e5453d21a6..0285fceb99a 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -208,18 +208,7 @@ static struct platform_driver pasic3_driver = {
.remove = pasic3_remove,
};
-static int __init pasic3_base_init(void)
-{
- return platform_driver_probe(&pasic3_driver, pasic3_probe);
-}
-
-static void __exit pasic3_base_exit(void)
-{
- platform_driver_unregister(&pasic3_driver);
-}
-
-module_init(pasic3_base_init);
-module_exit(pasic3_base_exit);
+module_platform_driver_probe(pasic3_driver, pasic3_probe);
MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
MODULE_DESCRIPTION("Core driver for HTC PASIC3");
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 6b5edf64de2..4febc5c7fde 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -460,15 +460,15 @@ static void omap_usbhs_init(struct device *dev)
switch (omap->usbhs_rev) {
case OMAP_USBHS_REV1:
- omap_usbhs_rev1_hostconfig(omap, reg);
+ reg = omap_usbhs_rev1_hostconfig(omap, reg);
break;
case OMAP_USBHS_REV2:
- omap_usbhs_rev2_hostconfig(omap, reg);
+ reg = omap_usbhs_rev2_hostconfig(omap, reg);
break;
default: /* newer revisions */
- omap_usbhs_rev2_hostconfig(omap, reg);
+ reg = omap_usbhs_rev2_hostconfig(omap, reg);
break;
}
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index bbdbc50a3cc..c01b69a5a4e 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -257,32 +257,49 @@ static struct regmap_irq_chip palmas_irq_chip = {
PALMAS_INT1_MASK),
};
-static void palmas_dt_to_pdata(struct device_node *node,
+static int palmas_set_pdata_irq_flag(struct i2c_client *i2c,
struct palmas_platform_data *pdata)
{
+ struct irq_data *irq_data = irq_get_irq_data(i2c->irq);
+ if (!irq_data) {
+ dev_err(&i2c->dev, "Invalid IRQ: %d\n", i2c->irq);
+ return -EINVAL;
+ }
+
+ pdata->irq_flags = irqd_get_trigger_type(irq_data);
+ dev_info(&i2c->dev, "Irq flag is 0x%08x\n", pdata->irq_flags);
+ return 0;
+}
+
+static void palmas_dt_to_pdata(struct i2c_client *i2c,
+ struct palmas_platform_data *pdata)
+{
+ struct device_node *node = i2c->dev.of_node;
int ret;
u32 prop;
- ret = of_property_read_u32(node, "ti,mux_pad1", &prop);
+ ret = of_property_read_u32(node, "ti,mux-pad1", &prop);
if (!ret) {
pdata->mux_from_pdata = 1;
pdata->pad1 = prop;
}
- ret = of_property_read_u32(node, "ti,mux_pad2", &prop);
+ ret = of_property_read_u32(node, "ti,mux-pad2", &prop);
if (!ret) {
pdata->mux_from_pdata = 1;
pdata->pad2 = prop;
}
/* The default for this register is all masked */
- ret = of_property_read_u32(node, "ti,power_ctrl", &prop);
+ ret = of_property_read_u32(node, "ti,power-ctrl", &prop);
if (!ret)
pdata->power_ctrl = prop;
else
pdata->power_ctrl = PALMAS_POWER_CTRL_NSLEEP_MASK |
PALMAS_POWER_CTRL_ENABLE1_MASK |
PALMAS_POWER_CTRL_ENABLE2_MASK;
+ if (i2c->irq)
+ palmas_set_pdata_irq_flag(i2c, pdata);
}
static int palmas_i2c_probe(struct i2c_client *i2c,
@@ -304,7 +321,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
if (!pdata)
return -ENOMEM;
- palmas_dt_to_pdata(node, pdata);
+ palmas_dt_to_pdata(i2c, pdata);
}
if (!pdata)
@@ -344,6 +361,19 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
}
}
+ /* Change interrupt line output polarity */
+ if (pdata->irq_flags & IRQ_TYPE_LEVEL_HIGH)
+ reg = PALMAS_POLARITY_CTRL_INT_POLARITY;
+ else
+ reg = 0;
+ ret = palmas_update_bits(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_POLARITY_CTRL, PALMAS_POLARITY_CTRL_INT_POLARITY,
+ reg);
+ if (ret < 0) {
+ dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret);
+ goto err;
+ }
+
/* Change IRQ into clear on read mode for efficiency */
slave = PALMAS_BASE_TO_SLAVE(PALMAS_INTERRUPT_BASE);
addr = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, PALMAS_INT_CTRL);
@@ -352,7 +382,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
regmap_write(palmas->regmap[slave], addr, reg);
ret = regmap_add_irq_chip(palmas->regmap[slave], palmas->irq,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW, 0, &palmas_irq_chip,
+ IRQF_ONESHOT | pdata->irq_flags, 0, &palmas_irq_chip,
&palmas->irq_data);
if (ret < 0)
goto err;
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
index 2f12cc13489..578a1136b6d 100644
--- a/drivers/mfd/rtsx_pcr.c
+++ b/drivers/mfd/rtsx_pcr.c
@@ -1138,7 +1138,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
ret = rtsx_pci_acquire_irq(pcr);
if (ret < 0)
- goto free_dma;
+ goto disable_msi;
pci_set_master(pcidev);
synchronize_irq(pcr->irq);
@@ -1162,7 +1162,9 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
disable_irq:
free_irq(pcr->irq, (void *)pcr);
-free_dma:
+disable_msi:
+ if (pcr->msi_en)
+ pci_disable_msi(pcr->pci);
dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN,
pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);
unmap:
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 4b11202061b..bc858777799 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -1015,7 +1015,10 @@ void stmpe_of_probe(struct stmpe_platform_data *pdata, struct device_node *np)
{
struct device_node *child;
- pdata->id = -1;
+ pdata->id = of_alias_get_id(np, "stmpe-i2c");
+ if (pdata->id < 0)
+ pdata->id = -1;
+
pdata->irq_trigger = IRQF_TRIGGER_NONE;
of_property_read_u32(np, "st,autosleep-timeout",
@@ -1057,6 +1060,9 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
return -ENOMEM;
stmpe_of_probe(pdata, np);
+
+ if (of_find_property(np, "interrupts", NULL) == NULL)
+ ci->irq = -1;
}
stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL);
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 61aea6381cd..7905d36d051 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -25,17 +25,15 @@
static struct platform_driver syscon_driver;
struct syscon {
- struct device *dev;
void __iomem *base;
struct regmap *regmap;
};
-static int syscon_match(struct device *dev, void *data)
+static int syscon_match_node(struct device *dev, void *data)
{
- struct syscon *syscon = dev_get_drvdata(dev);
struct device_node *dn = data;
- return (syscon->dev->of_node == dn) ? 1 : 0;
+ return (dev->of_node == dn) ? 1 : 0;
}
struct regmap *syscon_node_to_regmap(struct device_node *np)
@@ -44,7 +42,7 @@ struct regmap *syscon_node_to_regmap(struct device_node *np)
struct device *dev;
dev = driver_find_device(&syscon_driver.driver, NULL, np,
- syscon_match);
+ syscon_match_node);
if (!dev)
return ERR_PTR(-EPROBE_DEFER);
@@ -70,6 +68,34 @@ struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
}
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
+static int syscon_match_pdevname(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+
+ if (id)
+ if (!strcmp(id->name, (const char *)data))
+ return 1;
+
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+struct regmap *syscon_regmap_lookup_by_pdevname(const char *s)
+{
+ struct device *dev;
+ struct syscon *syscon;
+
+ dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s,
+ syscon_match_pdevname);
+ if (!dev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ syscon = dev_get_drvdata(dev);
+
+ return syscon->regmap;
+}
+EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname);
+
struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
const char *property)
{
@@ -101,28 +127,22 @@ static struct regmap_config syscon_regmap_config = {
static int syscon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct syscon *syscon;
- struct resource res;
- int ret;
-
- if (!np)
- return -ENOENT;
+ struct resource *res;
- syscon = devm_kzalloc(dev, sizeof(struct syscon),
- GFP_KERNEL);
+ syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
if (!syscon)
return -ENOMEM;
- syscon->base = of_iomap(np, 0);
- if (!syscon->base)
- return -EADDRNOTAVAIL;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
- ret = of_address_to_resource(np, 0, &res);
- if (ret)
- return ret;
+ syscon->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!syscon->base)
+ return -ENOMEM;
- syscon_regmap_config.max_register = res.end - res.start - 3;
+ syscon_regmap_config.max_register = res->end - res->start - 3;
syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
&syscon_regmap_config);
if (IS_ERR(syscon->regmap)) {
@@ -130,25 +150,17 @@ static int syscon_probe(struct platform_device *pdev)
return PTR_ERR(syscon->regmap);
}
- syscon->dev = dev;
platform_set_drvdata(pdev, syscon);
- dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n",
- res.start, res.end);
+ dev_info(dev, "regmap 0x%x-0x%x registered\n", res->start, res->end);
return 0;
}
-static int syscon_remove(struct platform_device *pdev)
-{
- struct syscon *syscon;
-
- syscon = platform_get_drvdata(pdev);
- iounmap(syscon->base);
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
+static const struct platform_device_id syscon_ids[] = {
+ { "syscon", },
+ { }
+};
static struct platform_driver syscon_driver = {
.driver = {
@@ -157,7 +169,7 @@ static struct platform_driver syscon_driver = {
.of_match_table = of_syscon_match,
},
.probe = syscon_probe,
- .remove = syscon_remove,
+ .id_table = syscon_ids,
};
static int __init syscon_init(void)
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 4658b5bdcd8..aeb8e40ab42 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -169,6 +169,7 @@ err:
void tps65912_device_exit(struct tps65912 *tps65912)
{
mfd_remove_devices(tps65912->dev);
+ tps65912_irq_exit(tps65912);
kfree(tps65912);
}
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c
index e16edca9267..d2ab222138c 100644
--- a/drivers/mfd/twl4030-audio.c
+++ b/drivers/mfd/twl4030-audio.c
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
* Disable the resource.
* The function returns with error or the content of the register
*/
-int twl4030_audio_disable_resource(unsigned id)
+int twl4030_audio_disable_resource(enum twl4030_audio_res id)
{
struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
int val;
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 88ff9dc8330..942b666a2a0 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -800,7 +800,7 @@ static int twl4030_madc_remove(struct platform_device *pdev)
static struct platform_driver twl4030_madc_driver = {
.probe = twl4030_madc_probe,
- .remove = __exit_p(twl4030_madc_remove),
+ .remove = twl4030_madc_remove,
.driver = {
.name = "twl4030_madc",
.owner = THIS_MODULE,