summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-24 20:00:58 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-24 20:00:58 -0800
commitab7826595e9ec51a51f622c5fc91e2f59440481a (patch)
tree34241b399fa7a12c260e06e6c1c31bc69d46e1e3 /drivers
parent21fbd5809ad126b949206d78e0a0e07ec872ea11 (diff)
parentff7109fa632654eaef657186f2942f5b679023d6 (diff)
Merge tag 'mfd-3.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
Pull MFS updates from Samuel Ortiz: "This is the MFD pull request for the 3.9 merge window. No new drivers this time, but a bunch of fairly big cleanups: - Roger Quadros worked on a OMAP USBHS and TLL platform data consolidation, OMAP5 support and clock management code cleanup. - The first step of a major sync for the ab8500 driver from Lee Jones. In particular, the debugfs and the sysct interfaces got extended and improved. - Peter Ujfalusi sent a nice patchset for cleaning and fixing the twl-core driver, with a much needed module id lookup code improvement. - The regular wm5102 and arizona cleanups and fixes from Mark Brown. - Laxman Dewangan extended the palmas APIs in order to implement the palmas GPIO and rt drivers. - Laxman also added DT support for the tps65090 driver. - The Intel SCH and ICH drivers got a couple fixes from Aaron Sierra and Darren Hart. - Linus Walleij patchset for the ab8500 driver allowed ab8500 and ab9540 based devices to switch to the new abx500 pin-ctrl driver. - The max8925 now has device tree and irqdomain support thanks to Qing Xu. - The recently added rtsx driver got a few cleanups and fixes for a better card detection code path and now also supports the RTS5227 chipset, thanks to Wei Wang and Roger Tseng." * tag 'mfd-3.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (109 commits) mfd: lpc_ich: Use devres API to allocate private data mfd: lpc_ich: Add Device IDs for Intel Wellsburg PCH mfd: lpc_sch: Accomodate partial population of the MFD devices mfd: da9052-i2c: Staticize da9052_i2c_fix() mfd: syscon: Fix sparse warning mfd: twl-core: Fix kernel panic on boot mfd: rtsx: Fix issue that booting OS with SD card inserted mfd: ab8500: Fix compile error mfd: Add missing GENERIC_HARDIRQS dependecies Documentation: Add docs for max8925 dt mfd: max8925: Add dts mfd: max8925: Support dt for backlight mfd: max8925: Fix onkey driver irq base mfd: max8925: Fix mfd device register failure mfd: max8925: Add irqdomain for dt mfd: vexpress: Allow vexpress-sysreg to self-initialise mfd: rtsx: Support RTS5227 mfd: rtsx: Implement driving adjustment to device-dependent callbacks mfd: vexpress: Add pseudo-GPIO based LEDs mfd: ab8500: Rename ab8500 to abx500 for hwmon driver ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-palmas.c184
-rw-r--r--drivers/input/misc/max8925_onkey.c3
-rw-r--r--drivers/memstick/host/rtsx_pci_ms.c7
-rw-r--r--drivers/mfd/88pm800.c10
-rw-r--r--drivers/mfd/88pm805.c4
-rw-r--r--drivers/mfd/88pm80x.c22
-rw-r--r--drivers/mfd/Kconfig72
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/ab8500-core.c61
-rw-r--r--drivers/mfd/ab8500-debugfs.c1248
-rw-r--r--drivers/mfd/ab8500-gpadc.c90
-rw-r--r--drivers/mfd/ab8500-sysctrl.c92
-rw-r--r--drivers/mfd/abx500-core.c16
-rw-r--r--drivers/mfd/arizona-core.c55
-rw-r--r--drivers/mfd/da9052-i2c.c3
-rw-r--r--drivers/mfd/db8500-prcmu.c17
-rw-r--r--drivers/mfd/lpc_ich.c144
-rw-r--r--drivers/mfd/lpc_sch.c147
-rw-r--r--drivers/mfd/max8925-core.c89
-rw-r--r--drivers/mfd/max8925-i2c.c36
-rw-r--r--drivers/mfd/omap-usb-host.c558
-rw-r--r--drivers/mfd/omap-usb-tll.c243
-rw-r--r--drivers/mfd/palmas.c14
-rw-r--r--drivers/mfd/rtl8411.c16
-rw-r--r--drivers/mfd/rts5209.c8
-rw-r--r--drivers/mfd/rts5227.c234
-rw-r--r--drivers/mfd/rts5229.c8
-rw-r--r--drivers/mfd/rtsx_pcr.c98
-rw-r--r--drivers/mfd/rtsx_pcr.h4
-rw-r--r--drivers/mfd/syscon.c1
-rw-r--r--drivers/mfd/tps6507x.c9
-rw-r--r--drivers/mfd/tps65090.c47
-rw-r--r--drivers/mfd/twl-core.c370
-rw-r--r--drivers/mfd/vexpress-sysreg.c83
-rw-r--r--drivers/mfd/wm5102-tables.c130
-rw-r--r--drivers/mfd/wm8994-core.c7
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c18
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-palmas.c339
-rw-r--r--drivers/usb/host/ehci-omap.c6
-rw-r--r--drivers/video/backlight/max8925_bl.c31
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/ux500_wdt.c171
47 files changed, 3725 insertions, 1004 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b89d250f56e7..74e17f19cc33 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -657,6 +657,13 @@ config GPIO_JANZ_TTL
This driver provides support for driving the pins in output
mode only. Input mode is not supported.
+config GPIO_PALMAS
+ bool "TI PALMAS series PMICs GPIO"
+ depends on MFD_PALMAS
+ help
+ Select this option to enable GPIO driver for the TI PALMAS
+ series chip family.
+
config GPIO_TPS6586X
bool "TPS6586X GPIO"
depends on MFD_TPS6586X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 45a388c21d04..6dbcba2e5cac 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
+obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
new file mode 100644
index 000000000000..e3a4e56f5a42
--- /dev/null
+++ b/drivers/gpio/gpio-palmas.c
@@ -0,0 +1,184 @@
+/*
+ * TI Palma series PMIC's GPIO driver.
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/palmas.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+struct palmas_gpio {
+ struct gpio_chip gpio_chip;
+ struct palmas *palmas;
+};
+
+static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct palmas_gpio, gpio_chip);
+}
+
+static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ unsigned int val;
+ int ret;
+
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_IN, &val);
+ if (ret < 0) {
+ dev_err(gc->dev, "GPIO_DATA_IN read failed, err = %d\n", ret);
+ return ret;
+ }
+ return !!(val & BIT(offset));
+}
+
+static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ int ret;
+
+ if (value)
+ ret = palmas_write(palmas, PALMAS_GPIO_BASE,
+ PALMAS_GPIO_SET_DATA_OUT, BIT(offset));
+ else
+ ret = palmas_write(palmas, PALMAS_GPIO_BASE,
+ PALMAS_GPIO_CLEAR_DATA_OUT, BIT(offset));
+ if (ret < 0)
+ dev_err(gc->dev, "%s write failed, err = %d\n",
+ (value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT",
+ ret);
+}
+
+static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ int ret;
+
+ /* Set the initial value */
+ palmas_gpio_set(gc, offset, value);
+
+ ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE,
+ PALMAS_GPIO_DATA_DIR, BIT(offset), BIT(offset));
+ if (ret < 0)
+ dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
+ return ret;
+}
+
+static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ int ret;
+
+ ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE,
+ PALMAS_GPIO_DATA_DIR, BIT(offset), 0);
+ if (ret < 0)
+ dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
+ return ret;
+}
+
+static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+
+ return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset);
+}
+
+static int palmas_gpio_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_platform_data *palmas_pdata;
+ struct palmas_gpio *palmas_gpio;
+ int ret;
+
+ palmas_gpio = devm_kzalloc(&pdev->dev,
+ sizeof(*palmas_gpio), GFP_KERNEL);
+ if (!palmas_gpio) {
+ dev_err(&pdev->dev, "Could not allocate palmas_gpio\n");
+ return -ENOMEM;
+ }
+
+ palmas_gpio->palmas = palmas;
+ palmas_gpio->gpio_chip.owner = THIS_MODULE;
+ palmas_gpio->gpio_chip.label = dev_name(&pdev->dev);
+ palmas_gpio->gpio_chip.ngpio = 8;
+ palmas_gpio->gpio_chip.can_sleep = 1;
+ palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
+ palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
+ palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
+ palmas_gpio->gpio_chip.set = palmas_gpio_set;
+ palmas_gpio->gpio_chip.get = palmas_gpio_get;
+ palmas_gpio->gpio_chip.dev = &pdev->dev;
+#ifdef CONFIG_OF_GPIO
+ palmas_gpio->gpio_chip.of_node = palmas->dev->of_node;
+#endif
+ palmas_pdata = dev_get_platdata(palmas->dev);
+ if (palmas_pdata && palmas_pdata->gpio_base)
+ palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base;
+ else
+ palmas_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&palmas_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, palmas_gpio);
+ return ret;
+}
+
+static int palmas_gpio_remove(struct platform_device *pdev)
+{
+ struct palmas_gpio *palmas_gpio = platform_get_drvdata(pdev);
+
+ return gpiochip_remove(&palmas_gpio->gpio_chip);
+}
+
+static struct platform_driver palmas_gpio_driver = {
+ .driver.name = "palmas-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = palmas_gpio_probe,
+ .remove = palmas_gpio_remove,
+};
+
+static int __init palmas_gpio_init(void)
+{
+ return platform_driver_register(&palmas_gpio_driver);
+}
+subsys_initcall(palmas_gpio_init);
+
+static void __exit palmas_gpio_exit(void)
+{
+ platform_driver_unregister(&palmas_gpio_driver);
+}
+module_exit(palmas_gpio_exit);
+
+MODULE_ALIAS("platform:palmas-gpio");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("GPIO driver for TI Palmas series PMICs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c
index 369a39de4ff3..f9179b2585a9 100644
--- a/drivers/input/misc/max8925_onkey.c
+++ b/drivers/input/misc/max8925_onkey.c
@@ -100,9 +100,6 @@ static int max8925_onkey_probe(struct platform_device *pdev)
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_KEY, KEY_POWER);
- irq[0] += chip->irq_base;
- irq[1] += chip->irq_base;
-
error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey-down", info);
if (error < 0) {
diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c
index f5ddb82dadb7..64a779c58a74 100644
--- a/drivers/memstick/host/rtsx_pci_ms.c
+++ b/drivers/memstick/host/rtsx_pci_ms.c
@@ -426,6 +426,9 @@ static void rtsx_pci_ms_request(struct memstick_host *msh)
dev_dbg(ms_dev(host), "--> %s\n", __func__);
+ if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD))
+ return;
+
schedule_work(&host->handle_req);
}
@@ -441,6 +444,10 @@ static int rtsx_pci_ms_set_param(struct memstick_host *msh,
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD);
+ if (err)
+ return err;
+
switch (param) {
case MEMSTICK_POWER:
if (value == MEMSTICK_POWER_ON)
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index 391e23e6a647..582bda543520 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -531,7 +531,7 @@ static int pm800_probe(struct i2c_client *client,
ret = device_800_init(chip, pdata);
if (ret) {
dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
- goto err_800_init;
+ goto err_subchip_alloc;
}
ret = pm800_pages_init(chip);
@@ -546,10 +546,8 @@ static int pm800_probe(struct i2c_client *client,
err_page_init:
mfd_remove_devices(chip->dev);
device_irq_exit_800(chip);
-err_800_init:
- devm_kfree(&client->dev, subchip);
err_subchip_alloc:
- pm80x_deinit(client);
+ pm80x_deinit();
out_init:
return ret;
}
@@ -562,9 +560,7 @@ static int pm800_remove(struct i2c_client *client)
device_irq_exit_800(chip);
pm800_pages_exit(chip);
- devm_kfree(&client->dev, chip->subchip);
-
- pm80x_deinit(client);
+ pm80x_deinit();
return 0;
}
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
index e671230be2b1..65d7ac099b20 100644
--- a/drivers/mfd/88pm805.c
+++ b/drivers/mfd/88pm805.c
@@ -257,7 +257,7 @@ static int pm805_probe(struct i2c_client *client,
pdata->plat_config(chip, pdata);
err_805_init:
- pm80x_deinit(client);
+ pm80x_deinit();
out_init:
return ret;
}
@@ -269,7 +269,7 @@ static int pm805_remove(struct i2c_client *client)
mfd_remove_devices(chip->dev);
device_irq_exit_805(chip);
- pm80x_deinit(client);
+ pm80x_deinit();
return 0;
}
diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c
index 1adb355d86d1..f736a46eb8c0 100644
--- a/drivers/mfd/88pm80x.c
+++ b/drivers/mfd/88pm80x.c
@@ -48,14 +48,12 @@ int pm80x_init(struct i2c_client *client,
ret = PTR_ERR(map);
dev_err(&client->dev, "Failed to allocate register map: %d\n",
ret);
- goto err_regmap_init;
+ return ret;
}
chip->id = id->driver_data;
- if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) {
- ret = -EINVAL;
- goto err_chip_id;
- }
+ if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805)
+ return -EINVAL;
chip->client = client;
chip->regmap = map;
@@ -82,19 +80,11 @@ int pm80x_init(struct i2c_client *client,
}
return 0;
-
-err_chip_id:
- regmap_exit(map);
-err_regmap_init:
- devm_kfree(&client->dev, chip);
- return ret;
}
EXPORT_SYMBOL_GPL(pm80x_init);
-int pm80x_deinit(struct i2c_client *client)
+int pm80x_deinit(void)
{
- struct pm80x_chip *chip = i2c_get_clientdata(client);
-
/*
* workaround: clear the dependency between pm800 and pm805.
* would remove it after HW chip fixes the issue.
@@ -103,10 +93,6 @@ int pm80x_deinit(struct i2c_client *client)
g_pm80x_chip->companion = NULL;
else
g_pm80x_chip = NULL;
-
- regmap_exit(chip->regmap);
- devm_kfree(&client->dev, chip);
-
return 0;
}
EXPORT_SYMBOL_GPL(pm80x_deinit);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ff553babf455..671f5b171c73 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -65,7 +65,7 @@ config MFD_SM501_GPIO
config MFD_RTSX_PCI
tristate "Support for Realtek PCI-E card reader"
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
This supports for Realtek PCI-Express card reader including rts5209,
@@ -95,7 +95,7 @@ config MFD_DM355EVM_MSP
config MFD_TI_SSP
tristate "TI Sequencer Serial Port support"
- depends on ARCH_DAVINCI_TNETV107X
+ depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS
select MFD_CORE
---help---
Say Y here if you want support for the Sequencer Serial Port
@@ -109,6 +109,7 @@ config MFD_TI_AM335X_TSCADC
select MFD_CORE
select REGMAP
select REGMAP_MMIO
+ depends on GENERIC_HARDIRQS
help
If you say yes here you get support for Texas Instruments series
of Touch Screen /ADC chips.
@@ -126,6 +127,7 @@ config HTC_EGPIO
config HTC_PASIC3
tristate "HTC PASIC3 LED/DS1WM chip support"
select MFD_CORE
+ depends on GENERIC_HARDIRQS
help
This core driver provides register access for the LED/DS1WM
chips labeled "AIC2" and "AIC3", found on HTC Blueangel and
@@ -157,6 +159,7 @@ config MFD_LM3533
depends on I2C
select MFD_CORE
select REGMAP_I2C
+ depends on GENERIC_HARDIRQS
help
Say yes here to enable support for National Semiconductor / TI
LM3533 Lighting Power chips.
@@ -171,6 +174,7 @@ config TPS6105X
select REGULATOR
select MFD_CORE
select REGULATOR_FIXED_VOLTAGE
+ depends on GENERIC_HARDIRQS
help
This option enables a driver for the TP61050/TPS61052
high-power "white LED driver". This boost converter is
@@ -193,7 +197,7 @@ config TPS65010
config TPS6507X
tristate "TPS6507x Power Management / Touch Screen chips"
select MFD_CORE
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
help
If you say yes here you get support for the TPS6507x series of
Power Management / Touch Screen chips. These include voltage
@@ -204,7 +208,7 @@ config TPS6507X
config MFD_TPS65217
tristate "TPS65217 Power Management / White LED chips"
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
help
@@ -234,7 +238,7 @@ config MFD_TPS6586X
config MFD_TPS65910
bool "TPS65910 Power Management chip"
- depends on I2C=y && GPIOLIB
+ depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
@@ -251,7 +255,7 @@ config MFD_TPS65912_I2C
bool "TPS65912 Power Management chip with I2C"
select MFD_CORE
select MFD_TPS65912
- depends on I2C=y && GPIOLIB
+ depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
help
If you say yes here you get support for the TPS65912 series of
PM chips with I2C interface.
@@ -260,7 +264,7 @@ config MFD_TPS65912_SPI
bool "TPS65912 Power Management chip with SPI"
select MFD_CORE
select MFD_TPS65912
- depends on SPI_MASTER && GPIOLIB
+ depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS
help
If you say yes here you get support for the TPS65912 series of
PM chips with SPI interface.
@@ -330,13 +334,13 @@ config TWL4030_POWER
config MFD_TWL4030_AUDIO
bool
- depends on TWL4030_CORE
+ depends on TWL4030_CORE && GENERIC_HARDIRQS
select MFD_CORE
default n
config TWL6040_CORE
bool "Support for TWL6040 audio codec"
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
@@ -405,7 +409,7 @@ config MFD_TMIO
config MFD_T7L66XB
bool "Support Toshiba T7L66XB"
- depends on ARM && HAVE_CLK
+ depends on ARM && HAVE_CLK && GENERIC_HARDIRQS
select MFD_CORE
select MFD_TMIO
help
@@ -413,7 +417,7 @@ config MFD_T7L66XB
config MFD_SMSC
bool "Support for the SMSC ECE1099 series chips"
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
help
@@ -460,7 +464,7 @@ config MFD_DA9052_SPI
select REGMAP_SPI
select REGMAP_IRQ
select PMIC_DA9052
- depends on SPI_MASTER=y
+ depends on SPI_MASTER=y && GENERIC_HARDIRQS
help
Support for the Dialog Semiconductor DA9052 PMIC
when controlled using SPI. This driver provides common support
@@ -472,7 +476,7 @@ config MFD_DA9052_I2C
select REGMAP_I2C
select REGMAP_IRQ
select PMIC_DA9052
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
help
Support for the Dialog Semiconductor DA9052 PMIC
when controlled using I2C. This driver provides common support
@@ -485,7 +489,7 @@ config MFD_DA9055
select REGMAP_IRQ
select PMIC_DA9055
select MFD_CORE
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
help
Say yes here for support of Dialog Semiconductor DA9055. This is
a Power Management IC. This driver provides common support for
@@ -508,7 +512,7 @@ config PMIC_ADP5520
config MFD_LP8788
bool "Texas Instruments LP8788 Power Management Unit Driver"
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select IRQ_DOMAIN
@@ -611,7 +615,7 @@ config MFD_ARIZONA_I2C
select MFD_ARIZONA
select MFD_CORE
select REGMAP_I2C
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
help
Support for the Wolfson Microelectronics Arizona platform audio SoC
core functionality controlled via I2C.
@@ -621,7 +625,7 @@ config MFD_ARIZONA_SPI
select MFD_ARIZONA
select MFD_CORE
select REGMAP_SPI
- depends on SPI_MASTER
+ depends on SPI_MASTER && GENERIC_HARDIRQS
help
Support for the Wolfson Microelectronics Arizona platform audio SoC
core functionality controlled via I2C.
@@ -641,7 +645,7 @@ config MFD_WM5110
config MFD_WM8400
bool "Support Wolfson Microelectronics WM8400"
select MFD_CORE
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
select REGMAP_I2C
help
Support for the Wolfson Microelecronics WM8400 PMIC and audio
@@ -785,7 +789,7 @@ config MFD_MC13783
config MFD_MC13XXX
tristate
- depends on SPI_MASTER || I2C
+ depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS
select MFD_CORE
select MFD_MC13783
help
@@ -796,7 +800,7 @@ config MFD_MC13XXX
config MFD_MC13XXX_SPI
tristate "Freescale MC13783 and MC13892 SPI interface"
- depends on SPI_MASTER
+ depends on SPI_MASTER && GENERIC_HARDIRQS
select REGMAP_SPI
select MFD_MC13XXX
help
@@ -804,7 +808,7 @@ config MFD_MC13XXX_SPI
config MFD_MC13XXX_I2C
tristate "Freescale MC13892 I2C interface"
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
select REGMAP_I2C
select MFD_MC13XXX
help
@@ -822,7 +826,7 @@ config ABX500_CORE
config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
- depends on I2C=y && ABX500_CORE
+ depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS
select MFD_CORE
default y if ARCH_U300
help
@@ -909,7 +913,7 @@ config MFD_TIMBERDALE
config LPC_SCH
tristate "Intel SCH LPC"
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
LPC bridge function of the Intel SCH provides support for
@@ -917,7 +921,7 @@ config LPC_SCH
config LPC_ICH
tristate "Intel ICH LPC"
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
The LPC bridge function of the Intel ICH provides support for
@@ -928,7 +932,7 @@ config LPC_ICH
config MFD_RDC321X
tristate "Support for RDC-R321x southbridge"
select MFD_CORE
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
help
Say yes here if you want to have support for the RDC R-321x SoC
southbridge which provides access to GPIOs and Watchdog using the
@@ -937,7 +941,7 @@ config MFD_RDC321X
config MFD_JANZ_CMODIO
tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
select MFD_CORE
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
help
This is the core driver for the Janz CMOD-IO PCI MODULbus
carrier board. This device is a PCI to MODULbus bridge which may
@@ -955,7 +959,7 @@ config MFD_JZ4740_ADC
config MFD_VX855
tristate "Support for VIA VX855/VX875 integrated south bridge"
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
Say yes here to enable support for various functions of the
@@ -964,7 +968,7 @@ config MFD_VX855
config MFD_WL1273_CORE
tristate "Support for TI WL1273 FM radio."
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
select MFD_CORE
default n
help
@@ -1028,7 +1032,7 @@ config MFD_TPS65090
config MFD_AAT2870_CORE
bool "Support for the AnalogicTech AAT2870"
select MFD_CORE
- depends on I2C=y && GPIOLIB
+ depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
help
If you say yes here you get support for the AAT2870.
This driver provides common support for accessing the device,
@@ -1060,7 +1064,7 @@ config MFD_RC5T583
config MFD_STA2X11
bool "STA2X11 multi function device support"
- depends on STA2X11
+ depends on STA2X11 && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_MMIO
@@ -1077,7 +1081,7 @@ config MFD_PALMAS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
help
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
@@ -1085,7 +1089,7 @@ config MFD_PALMAS
config MFD_VIPERBOARD
tristate "Support for Nano River Technologies Viperboard"
select MFD_CORE
- depends on USB
+ depends on USB && GENERIC_HARDIRQS
default n
help
Say yes here if you want support for Nano River Technologies
@@ -1099,7 +1103,7 @@ config MFD_VIPERBOARD
config MFD_RETU
tristate "Support for Retu multi-function device"
select MFD_CORE
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
select REGMAP_IRQ
help
Retu is a multi-function device found on Nokia Internet Tablets
@@ -1110,7 +1114,7 @@ config MFD_AS3711
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
- depends on I2C=y
+ depends on I2C=y && GENERIC_HARDIRQS
help
Support for the AS3711 PMIC from AMS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8b977f8045ae..b90409c23664 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -9,7 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
-rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o
+rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 8b5d685ab980..7c84ced2e01b 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -320,6 +320,7 @@ static struct abx500_ops ab8500_ops = {
.mask_and_set_register = ab8500_mask_and_set_register,
.event_registers_startup_state_get = NULL,
.startup_irq_enabled = NULL,
+ .dump_all_banks = ab8500_dump_all_banks,
};
static void ab8500_irq_lock(struct irq_data *data)
@@ -368,16 +369,48 @@ static void ab8500_irq_mask(struct irq_data *data)
int mask = 1 << (offset % 8);
ab8500->mask[index] |= mask;
+
+ /* The AB8500 GPIOs have two interrupts each (rising & falling). */
+ if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R)
+ ab8500->mask[index + 2] |= mask;
+ if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R)
+ ab8500->mask[index + 1] |= mask;
+ if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R)
+ /* Here the falling IRQ is one bit lower */
+ ab8500->mask[index] |= (mask << 1);
}
static void ab8500_irq_unmask(struct irq_data *data)
{
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
+ unsigned int type = irqd_get_trigger_type(data);
int offset = data->hwirq;
int index = offset / 8;
int mask = 1 << (offset % 8);
- ab8500->mask[index] &= ~mask;
+ if (type & IRQ_TYPE_EDGE_RISING)
+ ab8500->mask[index] &= ~mask;
+
+ /* The AB8500 GPIOs have two interrupts each (rising & falling). */
+ if (type & IRQ_TYPE_EDGE_FALLING) {
+ if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R)
+ ab8500->mask[index + 2] &= ~mask;
+ else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R)
+ ab8500->mask[index + 1] &= ~mask;
+ else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R)
+ /* Here the falling IRQ is one bit lower */
+ ab8500->mask[index] &= ~(mask << 1);
+ else
+ ab8500->mask[index] &= ~mask;
+ } else {
+ /* Satisfies the case where type is not set. */
+ ab8500->mask[index] &= ~mask;
+ }
+}
+
+static int ab8500_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ return 0;
}
static struct irq_chip ab8500_irq_chip = {
@@ -387,6 +420,7 @@ static struct irq_chip ab8500_irq_chip = {
.irq_mask = ab8500_irq_mask,
.irq_disable = ab8500_irq_mask,
.irq_unmask = ab8500_irq_unmask,
+ .irq_set_type = ab8500_irq_set_type,
};
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
@@ -411,6 +445,19 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
line = (i << 3) + int_bit;
latch_val &= ~(1 << int_bit);
+ /*
+ * This handles the falling edge hwirqs from the GPIO
+ * lines. Route them back to the line registered for the
+ * rising IRQ, as this is merely a flag for the same IRQ
+ * in linux terms.
+ */
+ if (line >= AB8500_INT_GPIO6F && line <= AB8500_INT_GPIO41F)
+ line -= 16;
+ if (line >= AB9540_INT_GPIO50F && line <= AB9540_INT_GPIO54F)
+ line -= 8;
+ if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F)
+ line += 1;
+
handle_nested_irq(ab8500->irq_base + line);
} while (latch_val);
@@ -521,6 +568,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
int virq = ab8500_irq_get_virq(ab8500, line);
handle_nested_irq(virq);
+ ab8500_debug_register_interrupt(line);
value &= ~(1 << bit);
} while (value);
@@ -929,7 +977,7 @@ static struct resource ab8505_iddet_resources[] = {
static struct resource ab8500_temp_resources[] = {
{
- .name = "AB8500_TEMP_WARM",
+ .name = "ABX500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
.end = AB8500_INT_TEMP_WARM,
.flags = IORESOURCE_IRQ,
@@ -1005,8 +1053,8 @@ static struct mfd_cell abx500_common_devs[] = {
.of_compatible = "stericsson,ab8500-denc",
},
{
- .name = "ab8500-temp",
- .of_compatible = "stericsson,ab8500-temp",
+ .name = "abx500-temp",
+ .of_compatible = "stericsson,abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
@@ -1049,7 +1097,7 @@ static struct mfd_cell ab8500_bm_devs[] = {
static struct mfd_cell ab8500_devs[] = {
{
- .name = "ab8500-gpio",
+ .name = "pinctrl-ab8500",
.of_compatible = "stericsson,ab8500-gpio",
},
{
@@ -1066,7 +1114,8 @@ static struct mfd_cell ab8500_devs[] = {
static struct mfd_cell ab9540_devs[] = {
{
- .name = "ab8500-gpio",
+ .name = "pinctrl-ab9540",
+ .of_compatible = "stericsson,ab9540-gpio",
},
{
.name = "ab9540-usb",
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 5a8e707bc038..45fe3c50eb03 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -4,6 +4,72 @@
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
* License Terms: GNU General Public License v2
*/
+/*
+ * AB8500 register access
+ * ======================
+ *
+ * read:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # cat <debugfs>/ab8500/register-value
+ *
+ * write:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # echo VALUE > <debugfs>/ab8500/register-value
+ *
+ * read all registers from a bank:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # cat <debugfs>/ab8500/all-bank-register
+ *
+ * BANK target AB8500 register bank
+ * ADDR target AB8500 register address
+ * VALUE decimal or 0x-prefixed hexadecimal
+ *
+ *
+ * User Space notification on AB8500 IRQ
+ * =====================================
+ *
+ * Allows user space entity to be notified when target AB8500 IRQ occurs.
+ * When subscribed, a sysfs entry is created in ab8500.i2c platform device.
+ * One can pool this file to get target IRQ occurence information.
+ *
+ * subscribe to an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-subscribe
+ *
+ * unsubscribe from an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
+ *
+ *
+ * AB8500 register formated read/write access
+ * ==========================================
+ *
+ * Read: read data, data>>SHIFT, data&=MASK, output data
+ * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
+ * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
+ * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
+ *
+ * Usage:
+ * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
+ *
+ * CMD read read access
+ * write write access
+ *
+ * BANK target reg bank
+ * ADDRESS target reg address
+ * VALUE (write) value to be updated
+ *
+ * OPTIONS
+ * -d|-dec (read) output in decimal
+ * -h|-hexa (read) output in 0x-hexa (default)
+ * -l|-w|-b 32bit (default), 16bit or 8bit reg access
+ * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
+ * -s|-shift SHIFT bit shift value (read:left, write:right)
+ * -o|-offset OFFSET address offset to add to ADDRESS value
+ *
+ * Warning: bit shift operation is applied to bit-mask.
+ * Warning: bit shift direction depends on read or right command.
+ */
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@@ -11,13 +77,30 @@
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
static u32 debug_bank;
static u32 debug_address;
+static int irq_first;
+static int irq_last;
+static u32 *irq_count;
+static int num_irqs;
+
+static struct device_attribute **dev_attr;
+static char **event_name;
+
/**
* struct ab8500_reg_range
* @first: the first address of the range
@@ -42,15 +125,35 @@ struct ab8500_prcmu_ranges {
const struct ab8500_reg_range *range;
};
+/* hwreg- "mask" and "shift" entries ressources */
+struct hwreg_cfg {
+ u32 bank; /* target bank */
+ u32 addr; /* target address */
+ uint fmt; /* format */
+ uint mask; /* read/write mask, applied before any bit shift */
+ int shift; /* bit shift (read:right shift, write:left shift */
+};
+/* fmt bit #0: 0=hexa, 1=dec */
+#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
+#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
+
+static struct hwreg_cfg hwreg_cfg = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+};
+
#define AB8500_NAME_STRING "ab8500"
-#define AB8500_NUM_BANKS 22
+#define AB8500_ADC_NAME_STRING "gpadc"
+#define AB8500_NUM_BANKS 24
#define AB8500_REV_REG 0x80
static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
- .range = 0,
+ .range = NULL,
},
[AB8500_SYS_CTRL1_BLOCK] = {
.num_ranges = 3,
@@ -215,7 +318,7 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
[AB8500_CHARGER] = {
- .num_ranges = 8,
+ .num_ranges = 9,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
@@ -249,6 +352,10 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
.first = 0xC0,
.last = 0xC2,
},
+ {
+ .first = 0xf5,
+ .last = 0xf6,
+ },
},
},
[AB8500_GAS_GAUGE] = {
@@ -268,6 +375,24 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
},
+ [AB8500_DEVELOPMENT] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ },
+ },
+ [AB8500_DEBUG] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x05,
+ .last = 0x07,
+ },
+ },
+ },
[AB8500_AUDIO] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
@@ -354,15 +479,30 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
};
-static int ab8500_registers_print(struct seq_file *s, void *p)
+static irqreturn_t ab8500_debug_handler(int irq, void *data)
{
- struct device *dev = s->private;
- unsigned int i;
- u32 bank = debug_bank;
+ char buf[16];
+ struct kobject *kobj = (struct kobject *)data;
+ unsigned int irq_abb = irq - irq_first;
- seq_printf(s, AB8500_NAME_STRING " register values:\n");
+ if (irq_abb < num_irqs)
+ irq_count[irq_abb]++;
+ /*
+ * This makes it possible to use poll for events (POLLPRI | POLLERR)
+ * from userspace on sysfs file named <irq-nr>
+ */
+ sprintf(buf, "%d", irq);
+ sysfs_notify(kobj, NULL, buf);
+
+ return IRQ_HANDLED;
+}
+
+/* Prints to seq_file or log_buf */
+static int ab8500_registers_print(struct device *dev, u32 bank,
+ struct seq_file *s)
+{
+ unsigned int i;
- seq_printf(s, " bank %u:\n", bank);
for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
u32 reg;
@@ -379,22 +519,42 @@ static int ab8500_registers_print(struct seq_file *s, void *p)
return err;
}
- err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
- reg, value);
- if (err < 0) {
- dev_err(dev, "seq_printf overflow\n");
- /* Error is not returned here since
- * the output is wanted in any case */
- return 0;
+ if (s) {
+ err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ if (err < 0) {
+ dev_err(dev,
+ "seq_printf overflow bank=%d reg=%d\n",
+ bank, reg);
+ /* Error is not returned here since
+ * the output is wanted in any case */
+ return 0;
+ }
+ } else {
+ printk(KERN_INFO" [%u/0x%02X]: 0x%02X\n", bank,
+ reg, value);
}
}
}
return 0;
}
+static int ab8500_print_bank_registers(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ u32 bank = debug_bank;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ seq_printf(s, " bank %u:\n", bank);
+
+ ab8500_registers_print(dev, bank, s);
+ return 0;
+}
+
static int ab8500_registers_open(struct inode *inode, struct file *file)
{
- return single_open(file, ab8500_registers_print, inode->i_private);
+ return single_open(file, ab8500_print_bank_registers, inode->i_private);
}
static const struct file_operations ab8500_registers_fops = {
@@ -405,6 +565,64 @@ static const struct file_operations ab8500_registers_fops = {
.owner = THIS_MODULE,
};
+static int ab8500_print_all_banks(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ unsigned int i;
+ int err;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ for (i = 1; i < AB8500_NUM_BANKS; i++) {
+ err = seq_printf(s, " bank %u:\n", i);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow, bank=%d\n", i);
+
+ ab8500_registers_print(dev, i, s);
+ }
+ return 0;
+}
+
+/* Dump registers to kernel log */
+void ab8500_dump_all_banks(struct device *dev)
+{
+ unsigned int i;
+
+ printk(KERN_INFO"ab8500 register values:\n");
+
+ for (i = 1; i < AB8500_NUM_BANKS; i++) {
+ printk(KERN_INFO" bank %u:\n", i);
+ ab8500_registers_print(dev, i, NULL);
+ }
+}
+
+static int ab8500_all_banks_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *s;
+ int err;
+
+ err = single_open(file, ab8500_print_all_banks, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 2);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static const struct file_operations ab8500_all_banks_fops = {
+ .open = ab8500_all_banks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static int ab8500_bank_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", debug_bank);
@@ -519,6 +737,761 @@ static ssize_t ab8500_val_write(struct file *file,
return count;
}
+/*
+ * Interrupt status
+ */
+static u32 num_interrupts[AB8500_MAX_NR_IRQS];
+static int num_interrupt_lines;
+
+void ab8500_debug_register_interrupt(int line)
+{
+ if (line < num_interrupt_lines)
+ num_interrupts[line]++;
+}
+
+static int ab8500_interrupts_print(struct seq_file *s, void *p)
+{
+ int line;
+
+ seq_printf(s, "irq: number of\n");
+
+ for (line = 0; line < num_interrupt_lines; line++)
+ seq_printf(s, "%3i: %6i\n", line, num_interrupts[line]);
+
+ return 0;
+}
+
+static int ab8500_interrupts_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_interrupts_print, inode->i_private);
+}
+
+/*
+ * - HWREG DB8500 formated routines
+ */
+static int ab8500_hwreg_print(struct seq_file *s, void *d)
+{
+ struct device *dev = s->private;
+ int ret;
+ u8 regvalue;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (hwreg_cfg.shift >= 0)
+ regvalue >>= hwreg_cfg.shift;
+ else
+ regvalue <<= -hwreg_cfg.shift;
+ regvalue &= hwreg_cfg.mask;
+
+ if (REG_FMT_DEC(&hwreg_cfg))
+ seq_printf(s, "%d\n", regvalue);
+ else
+ seq_printf(s, "0x%02X\n", regvalue);
+ return 0;
+}
+
+static int ab8500_hwreg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_hwreg_print, inode->i_private);
+}
+
+static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
+{
+ int bat_ctrl_raw;
+ int bat_ctrl_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL);
+ bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ BAT_CTRL, bat_ctrl_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ bat_ctrl_convert, bat_ctrl_raw);
+}
+
+static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
+ .open = ab8500_gpadc_bat_ctrl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
+{
+ int btemp_ball_raw;
+ int btemp_ball_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL);
+ btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
+ btemp_ball_raw);
+
+ return seq_printf(s,
+ "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
+}
+
+static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
+ .open = ab8500_gpadc_btemp_ball_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
+{
+ int main_charger_v_raw;
+ int main_charger_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V);
+ main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ MAIN_CHARGER_V, main_charger_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_charger_v_convert, main_charger_v_raw);
+}
+
+static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_charger_v_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
+ .open = ab8500_gpadc_main_charger_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
+{
+ int acc_detect1_raw;
+ int acc_detect1_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1);
+ acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
+ acc_detect1_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ acc_detect1_convert, acc_detect1_raw);
+}
+
+static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_acc_detect1_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
+ .open = ab8500_gpadc_acc_detect1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
+{
+ int acc_detect2_raw;
+ int acc_detect2_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2);
+ acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ ACC_DETECT2, acc_detect2_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ acc_detect2_convert, acc_detect2_raw);
+}
+
+static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_acc_detect2_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
+ .open = ab8500_gpadc_acc_detect2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
+{
+ int aux1_raw;
+ int aux1_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1);
+ aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
+ aux1_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ aux1_convert, aux1_raw);
+}
+
+static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux1_fops = {
+ .open = ab8500_gpadc_aux1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
+{
+ int aux2_raw;
+ int aux2_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2);
+ aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
+ aux2_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ aux2_convert, aux2_raw);
+}
+
+static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux2_fops = {
+ .open = ab8500_gpadc_aux2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
+{
+ int main_bat_v_raw;
+ int main_bat_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V);
+ main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
+ main_bat_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_bat_v_convert, main_bat_v_raw);
+}
+
+static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
+ .open = ab8500_gpadc_main_bat_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
+{
+ int vbus_v_raw;
+ int vbus_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V);
+ vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
+ vbus_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ vbus_v_convert, vbus_v_raw);
+}
+
+static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_vbus_v_fops = {
+ .open = ab8500_gpadc_vbus_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
+{
+ int main_charger_c_raw;
+ int main_charger_c_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C);
+ main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ MAIN_CHARGER_C, main_charger_c_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_charger_c_convert, main_charger_c_raw);
+}
+
+static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_charger_c_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
+ .open = ab8500_gpadc_main_charger_c_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
+{
+ int usb_charger_c_raw;
+ int usb_charger_c_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C);
+ usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ USB_CHARGER_C, usb_charger_c_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ usb_charger_c_convert, usb_charger_c_raw);
+}
+
+static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_usb_charger_c_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
+ .open = ab8500_gpadc_usb_charger_c_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
+{
+ int bk_bat_v_raw;
+ int bk_bat_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V);
+ bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ BK_BAT_V, bk_bat_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ bk_bat_v_convert, bk_bat_v_raw);
+}
+
+static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
+ .open = ab8500_gpadc_bk_bat_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
+{
+ int die_temp_raw;
+ int die_temp_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP);
+ die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
+ die_temp_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ die_temp_convert, die_temp_raw);
+}
+
+static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_die_temp_fops = {
+ .open = ab8500_gpadc_die_temp_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * return length of an ASCII numerical value, 0 is string is not a
+ * numerical value.
+ * string shall start at value 1st char.
+ * string can be tailed with \0 or space or newline chars only.
+ * value can be decimal or hexadecimal (prefixed 0x or 0X).
+ */
+static int strval_len(char *b)
+{
+ char *s = b;
+ if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
+ s += 2;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isxdigit(*s))
+ return 0;
+ }
+ } else {
+ if (*s == '-')
+ s++;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isdigit(*s))
+ return 0;
+ }
+ }
+ return (int) (s-b);
+}
+
+/*
+ * parse hwreg input data.
+ * update global hwreg_cfg only if input data syntax is ok.
+ */
+static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
+ struct device *dev)
+{
+ uint write, val = 0;
+ u8 regvalue;
+ int ret;
+ struct hwreg_cfg loc = {
+ .bank = 0, /* default: invalid phys addr */
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+ };
+
+ /* read or write ? */
+ if (!strncmp(b, "read ", 5)) {
+ write = 0;
+ b += 5;
+ } else if (!strncmp(b, "write ", 6)) {
+ write = 1;
+ b += 6;
+ } else
+ return -EINVAL;
+
+ /* OPTIONS -l|-w|-b -s -m -o */
+ while ((*b == ' ') || (*b == '-')) {
+ if (*(b-1) != ' ') {
+ b++;
+ continue;
+ }
+ if ((!strncmp(b, "-d ", 3)) ||
+ (!strncmp(b, "-dec ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt |= (1<<0);
+ } else if ((!strncmp(b, "-h ", 3)) ||
+ (!strncmp(b, "-hex ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt &= ~(1<<0);
+ } else if ((!strncmp(b, "-m ", 3)) ||
+ (!strncmp(b, "-mask ", 6))) {
+ b += (*(b+2) == ' ') ? 3 : 6;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.mask = simple_strtoul(b, &b, 0);
+ } else if ((!strncmp(b, "-s ", 3)) ||
+ (!strncmp(b, "-shift ", 7))) {
+ b += (*(b+2) == ' ') ? 3 : 7;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.shift = simple_strtol(b, &b, 0);
+ } else {
+ return -EINVAL;
+ }
+ }
+ /* get arg BANK and ADDRESS */
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.bank = simple_strtoul(b, &b, 0);
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.addr = simple_strtoul(b, &b, 0);
+
+ if (write) {
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ val = simple_strtoul(b, &b, 0);
+ }
+
+ /* args are ok, update target cfg (mainly for read) */
+ *cfg = loc;
+
+#ifdef ABB_HWREG_DEBUG
+ pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
+ "value=0x%X\n", (write) ? "write" : "read",
+ REG_FMT_DEC(cfg) ? "decimal" : "hexa",
+ cfg->addr, cfg->mask, cfg->shift, val);
+#endif
+
+ if (!write)
+ return 0;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (cfg->shift >= 0) {
+ regvalue &= ~(cfg->mask << (cfg->shift));
+ val = (val & cfg->mask) << (cfg->shift);
+ } else {
+ regvalue &= ~(cfg->mask >> (-cfg->shift));
+ val = (val & cfg->mask) >> (-cfg->shift);
+ }
+ val = val | regvalue;
+
+ ret = abx500_set_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, (u8)val);
+ if (ret < 0) {
+ pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t ab8500_hwreg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[128];
+ int buf_size, ret;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* get args and process */
+ ret = hwreg_common_write(buf, &hwreg_cfg, dev);
+ return (ret) ? ret : buf_size;
+}
+
+/*
+ * - irq subscribe/unsubscribe stuff
+ */
+static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
+{
+ seq_printf(s, "%d\n", irq_first);
+
+ return 0;
+}
+
+static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_subscribe_unsubscribe_print,
+ inode->i_private);
+}
+
+/*
+ * Userspace should use poll() on this file. When an event occur
+ * the blocking poll will be released.
+ */
+static ssize_t show_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long name;
+ unsigned int irq_index;
+ int err;
+
+ err = strict_strtoul(attr->attr.name, 0, &name);
+ if (err)
+ return err;
+
+ irq_index = name - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+ else
+ return sprintf(buf, "%u\n", irq_count[irq_index]);
+}
+
+static ssize_t ab8500_subscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+
+ /*
+ * This will create a sysfs file named <irq-nr> which userspace can
+ * use to select or poll and get the AB8500 events
+ */
+ dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
+ GFP_KERNEL);
+ event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL);
+ sprintf(event_name[irq_index], "%lu", user_val);
+ dev_attr[irq_index]->show = show_irq;
+ dev_attr[irq_index]->store = NULL;
+ dev_attr[irq_index]->attr.name = event_name[irq_index];
+ dev_attr[irq_index]->attr.mode = S_IRUGO;
+ err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ if (err < 0) {
+ printk(KERN_ERR "sysfs_create_file failed %d\n", err);
+ return err;
+ }
+
+ err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ "ab8500-debug", &dev->kobj);
+ if (err < 0) {
+ printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
+ err, user_val);
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ return err;
+ }
+
+ return buf_size;
+}
+
+static ssize_t ab8500_unsubscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+
+ /* Set irq count to 0 when unsubscribe */
+ irq_count[irq_index] = 0;
+
+ if (dev_attr[irq_index])
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+
+
+ free_irq(user_val, &dev->kobj);
+ kfree(event_name[irq_index]);
+ kfree(dev_attr[irq_index]);
+
+ return buf_size;
+}
+
+/*
+ * - several deubgfs nodes fops
+ */
+
static const struct file_operations ab8500_bank_fops = {
.open = ab8500_bank_open,
.write = ab8500_bank_write,
@@ -546,64 +1519,231 @@ static const struct file_operations ab8500_val_fops = {
.owner = THIS_MODULE,
};
+static const struct file_operations ab8500_interrupts_fops = {
+ .open = ab8500_interrupts_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_subscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_subscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_unsubscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_unsubscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_hwreg_fops = {
+ .open = ab8500_hwreg_open,
+ .write = ab8500_hwreg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static struct dentry *ab8500_dir;
-static struct dentry *ab8500_reg_file;
-static struct dentry *ab8500_bank_file;
-static struct dentry *ab8500_address_file;
-static struct dentry *ab8500_val_file;
+static struct dentry *ab8500_gpadc_dir;
static int ab8500_debug_probe(struct platform_device *plf)
{
+ struct dentry *file;
+ int ret = -ENOMEM;
+ struct ab8500 *ab8500;
debug_bank = AB8500_MISC;
debug_address = AB8500_REV_REG & 0x00FF;
+ ab8500 = dev_get_drvdata(plf->dev.parent);
+ num_irqs = ab8500->mask_size;
+
+ irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL);
+ if (!irq_count)
+ return -ENOMEM;
+
+ dev_attr = kzalloc(sizeof(*dev_attr)*num_irqs,GFP_KERNEL);
+ if (!dev_attr)
+ goto out_freeirq_count;
+
+ event_name = kzalloc(sizeof(*event_name)*num_irqs, GFP_KERNEL);
+ if (!event_name)
+ goto out_freedev_attr;
+
+ irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
+ if (irq_first < 0) {
+ dev_err(&plf->dev, "First irq not found, err %d\n",
+ irq_first);
+ ret = irq_first;
+ goto out_freeevent_name;
+ }
+
+ irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
+ if (irq_last < 0) {
+ dev_err(&plf->dev, "Last irq not found, err %d\n",
+ irq_last);
+ ret = irq_last;
+ goto out_freeevent_name;
+ }
+
ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
if (!ab8500_dir)
- goto exit_no_debugfs;
+ goto err;
+
+ ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING,
+ ab8500_dir);
+ if (!ab8500_gpadc_dir)
+ goto err;
+
+ file = debugfs_create_file("all-bank-registers", S_IRUGO,
+ ab8500_dir, &plf->dev, &ab8500_registers_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("all-banks", S_IRUGO,
+ ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR),
+ ab8500_dir, &plf->dev, &ab8500_bank_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR),
+ ab8500_dir, &plf->dev, &ab8500_address_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR),
+ ab8500_dir, &plf->dev, &ab8500_val_fops);
+ if (!file)
+ goto err;
- ab8500_reg_file = debugfs_create_file("all-bank-registers",
- S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
- if (!ab8500_reg_file)
- goto exit_destroy_dir;
+ file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR),
+ ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
+ if (!file)
+ goto err;
- ab8500_bank_file = debugfs_create_file("register-bank",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
- if (!ab8500_bank_file)
- goto exit_destroy_reg;
+ if (is_ab8500(ab8500))
+ num_interrupt_lines = AB8500_NR_IRQS;
+ else if (is_ab8505(ab8500))
+ num_interrupt_lines = AB8505_NR_IRQS;
+ else if (is_ab9540(ab8500))
+ num_interrupt_lines = AB9540_NR_IRQS;
- ab8500_address_file = debugfs_create_file("register-address",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
- &ab8500_address_fops);
- if (!ab8500_address_file)
- goto exit_destroy_bank;
+ file = debugfs_create_file("interrupts", (S_IRUGO),
+ ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
+ if (!file)
+ goto err;
- ab8500_val_file = debugfs_create_file("register-value",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
- if (!ab8500_val_file)
- goto exit_destroy_address;
+ file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR),
+ ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR),
+ ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
+ if (!file)
+ goto err;
return 0;
-exit_destroy_address:
- debugfs_remove(ab8500_address_file);
-exit_destroy_bank:
- debugfs_remove(ab8500_bank_file);
-exit_destroy_reg:
- debugfs_remove(ab8500_reg_file);
-exit_destroy_dir:
- debugfs_remove(ab8500_dir);
-exit_no_debugfs:
+err:
+ if (ab8500_dir)
+ debugfs_remove_recursive(ab8500_dir);
dev_err(&plf->dev, "failed to create debugfs entries.\n");
- return -ENOMEM;
+out_freeevent_name:
+ kfree(event_name);
+out_freedev_attr:
+ kfree(dev_attr);
+out_freeirq_count:
+ kfree(irq_count);
+
+ return ret;
}
static int ab8500_debug_remove(struct platform_device *plf)
{
- debugfs_remove(ab8500_val_file);
- debugfs_remove(ab8500_address_file);
- debugfs_remove(ab8500_bank_file);
- debugfs_remove(ab8500_reg_file);
- debugfs_remove(ab8500_dir);
+ debugfs_remove_recursive(ab8500_dir);
+ kfree(event_name);
+ kfree(dev_attr);
+ kfree(irq_count);
return 0;
}
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 3fb1f40d6389..b1f3561b023f 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
@@ -82,6 +83,11 @@
/* This is used to not lose precision when dividing to get gain and offset */
#define CALIB_SCALE 1000
+/* Time in ms before disabling regulator */
+#define GPADC_AUDOSUSPEND_DELAY 1
+
+#define CONVERSION_TIME 500 /* ms */
+
enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
@@ -102,10 +108,10 @@ struct adc_cal_data {
/**
* struct ab8500_gpadc - AB8500 GPADC device information
- * @chip_id ABB chip id
* @dev: pointer to the struct device
* @node: a list of AB8500 GPADCs, hence prepared for
reentrance
+ * @parent: pointer to the struct ab8500
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
@@ -114,9 +120,9 @@ struct adc_cal_data {
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
- u8 chip_id;
struct device *dev;
struct list_head node;
+ struct ab8500 *parent;
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
@@ -282,8 +288,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
return -ENODEV;
mutex_lock(&gpadc->ab8500_gpadc_lock);
+
/* Enable VTVout LDO this is required for GPADC */
- regulator_enable(gpadc->regu);
+ pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */
do {
@@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
EN_BUF | EN_ICHAR);
break;
case BTEMP_BALL:
- if (gpadc->chip_id >= AB8500_CUT3P0) {
+ if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
/* Turn on btemp pull-up on ABB 3.0 */
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
@@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
* Delay might be needed for ABB8500 cut 3.0, if not, remove
* when hardware will be available
*/
- msleep(1);
+ usleep_range(1000, 1000);
break;
}
/* Intentional fallthrough */
@@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out;
}
/* wait for completion of conversion */
- if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+ if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
+ msecs_to_jiffies(CONVERSION_TIME))) {
dev_err(gpadc->dev,
"timeout: didn't receive GPADC conversion interrupt\n");
ret = -EINVAL;
@@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
goto out;
}
- /* Disable VTVout LDO this is required for GPADC */
- regulator_disable(gpadc->regu);
+
+ pm_runtime_mark_last_busy(gpadc->dev);
+ pm_runtime_put_autosuspend(gpadc->dev);
+
mutex_unlock(&gpadc->ab8500_gpadc_lock);
return (high_data << 8) | low_data;
@@ -412,7 +422,9 @@ out:
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
- regulator_disable(gpadc->regu);
+
+ pm_runtime_put(gpadc->dev);
+
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
gpadc->cal_data[ADC_INPUT_VBAT].offset);
}
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ regulator_disable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_runtime_resume(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ regulator_enable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_runtime_idle(struct device *dev)
+{
+ pm_runtime_suspend(dev);
+ return 0;
+}
+
static int ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -591,6 +625,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
}
gpadc->dev = &pdev->dev;
+ gpadc->parent = dev_get_drvdata(pdev->dev.parent);
mutex_init(&gpadc->ab8500_gpadc_lock);
/* Initialize completion used to notify completion of conversion */
@@ -607,14 +642,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
goto fail;
}
- /* Get Chip ID of the ABB ASIC */
- ret = abx500_get_chip_id(gpadc->dev);
- if (ret < 0) {
- dev_err(gpadc->dev, "failed to get chip ID\n");
- goto fail_irq;
- }
- gpadc->chip_id = (u8) ret;
-
/* VTVout LDO used to power up ab8500-GPADC */
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
@@ -622,6 +649,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
goto fail_irq;
}
+
+ platform_set_drvdata(pdev, gpadc);
+
+ regulator_enable(gpadc->regu);
+
+ pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(gpadc->dev);
+ pm_runtime_set_active(gpadc->dev);
+ pm_runtime_enable(gpadc->dev);
+
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
@@ -642,19 +679,34 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
free_irq(gpadc->irq, gpadc);
- /* disable VTVout LDO that is being used by GPADC */
- regulator_put(gpadc->regu);
+
+ pm_runtime_get_sync(gpadc->dev);
+ pm_runtime_disable(gpadc->dev);
+
+ regulator_disable(gpadc->regu);
+
+ pm_runtime_set_suspended(gpadc->dev);
+
+ pm_runtime_put_noidle(gpadc->dev);
+
kfree(gpadc);
gpadc = NULL;
return 0;
}
+static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
+ SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
+ ab8500_gpadc_runtime_resume,
+ ab8500_gpadc_runtime_idle)
+};
+
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
.remove = ab8500_gpadc_remove,
.driver = {
.name = "ab8500-gpadc",
.owner = THIS_MODULE,
+ .pm = &ab8500_gpadc_pm_ops,
},
};
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index 8a33b2c7eead..108fd86552f0 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -7,12 +7,73 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/reboot.h>
+#include <linux/signal.h>
+#include <linux/power_supply.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
static struct device *sysctrl_dev;
+void ab8500_power_off(void)
+{
+ sigset_t old;
+ sigset_t all;
+ static char *pss[] = {"ab8500_ac", "ab8500_usb"};
+ int i;
+ bool charger_present = false;
+ union power_supply_propval val;
+ struct power_supply *psy;
+ int ret;
+
+ /*
+ * If we have a charger connected and we're powering off,
+ * reboot into charge-only mode.
+ */
+
+ for (i = 0; i < ARRAY_SIZE(pss); i++) {
+ psy = power_supply_get_by_name(pss[i]);
+ if (!psy)
+ continue;
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
+
+ if (!ret && val.intval) {
+ charger_present = true;
+ break;
+ }
+ }
+
+ if (!charger_present)
+ goto shutdown;
+
+ /* Check if battery is known */
+ psy = power_supply_get_by_name("ab8500_btemp");
+ if (psy) {
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY,
+ &val);
+ if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
+ printk(KERN_INFO
+ "Charger \"%s\" is connected with known battery."
+ " Rebooting.\n",
+ pss[i]);
+ machine_restart("charging");
+ }
+ }
+
+shutdown:
+ sigfillset(&all);
+
+ if (!sigprocmask(SIG_BLOCK, &all, &old)) {
+ (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
+ AB8500_STW4500CTRL1_SWOFF |
+ AB8500_STW4500CTRL1_SWRESET4500N);
+ (void)sigprocmask(SIG_SETMASK, &old, NULL);
+ }
+}
+
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
@@ -33,6 +94,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
return abx500_get_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), value);
}
+EXPORT_SYMBOL(ab8500_sysctrl_read);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
{
@@ -48,10 +110,40 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), mask, value);
}
+EXPORT_SYMBOL(ab8500_sysctrl_write);
static int ab8500_sysctrl_probe(struct platform_device *pdev)
{
+ struct ab8500_platform_data *plat;
+ struct ab8500_sysctrl_platform_data *pdata;
+
sysctrl_dev = &pdev->dev;
+ plat = dev_get_platdata(pdev->dev.parent);
+ if (plat->pm_power_off)
+ pm_power_off = ab8500_power_off;
+
+ pdata = plat->sysctrl;
+
+ if (pdata) {
+ int ret, i, j;
+
+ for (i = AB8500_SYSCLKREQ1RFCLKBUF;
+ i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
+ j = i - AB8500_SYSCLKREQ1RFCLKBUF;
+ ret = ab8500_sysctrl_write(i, 0xff,
+ pdata->initial_req_buf_config[j]);
+ dev_dbg(&pdev->dev,
+ "Setting SysClkReq%dRfClkBuf 0x%X\n",
+ j + 1,
+ pdata->initial_req_buf_config[j]);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "unable to set sysClkReq%dRfClkBuf: "
+ "%d\n", j + 1, ret);
+ }
+ }
+ }
+
return 0;
}
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
index 7ce65f49480f..9818afba2515 100644
--- a/drivers/mfd/abx500-core.c
+++ b/drivers/mfd/abx500-core.c
@@ -153,6 +153,22 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
}
EXPORT_SYMBOL(abx500_startup_irq_enabled);
+void abx500_dump_all_banks(void)
+{
+ struct abx500_ops *ops;
+ struct device dummy_child = {0};
+ struct abx500_device_entry *dev_entry;
+
+ list_for_each_entry(dev_entry, &abx500_list, list) {
+ dummy_child.parent = dev_entry->dev;
+ ops = &dev_entry->ops;
+
+ if ((ops != NULL) && (ops->dump_all_banks != NULL))
+ ops->dump_all_banks(&dummy_child);
+ }
+}
+EXPORT_SYMBOL(abx500_dump_all_banks);
+
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
MODULE_DESCRIPTION("ABX500 core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 222c03a5ddc0..b562c7bf8a46 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -115,7 +115,7 @@ static irqreturn_t arizona_underclocked(int irq, void *data)
if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
dev_err(arizona->dev, "ADC underclocked\n");
if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
- dev_err(arizona->dev, "Mixer underclocked\n");
+ dev_err(arizona->dev, "Mixer dropped sample\n");
return IRQ_HANDLED;
}
@@ -263,10 +263,36 @@ static int arizona_runtime_suspend(struct device *dev)
}
#endif
+#ifdef CONFIG_PM_SLEEP
+static int arizona_resume_noirq(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
+ disable_irq(arizona->irq);
+
+ return 0;
+}
+
+static int arizona_resume(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
+ enable_irq(arizona->irq);
+
+ return 0;
+}
+#endif
+
const struct dev_pm_ops arizona_pm_ops = {
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
arizona_runtime_resume,
NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
+#ifdef CONFIG_PM_SLEEP
+ .resume_noirq = arizona_resume_noirq,
+#endif
};
EXPORT_SYMBOL_GPL(arizona_pm_ops);
@@ -275,19 +301,19 @@ static struct mfd_cell early_devs[] = {
};
static struct mfd_cell wm5102_devs[] = {
+ { .name = "arizona-micsupp" },
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
{ .name = "arizona-haptics" },
- { .name = "arizona-micsupp" },
{ .name = "arizona-pwm" },
{ .name = "wm5102-codec" },
};
static struct mfd_cell wm5110_devs[] = {
+ { .name = "arizona-micsupp" },
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
{ .name = "arizona-haptics" },
- { .name = "arizona-micsupp" },
{ .name = "arizona-pwm" },
{ .name = "wm5110-codec" },
};
@@ -484,6 +510,29 @@ int arizona_dev_init(struct arizona *arizona)
goto err_reset;
}
+ for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
+ if (!arizona->pdata.micbias[i].mV)
+ continue;
+
+ val = (arizona->pdata.micbias[i].mV - 1500) / 100;
+ val <<= ARIZONA_MICB1_LVL_SHIFT;
+
+ if (arizona->pdata.micbias[i].ext_cap)
+ val |= ARIZONA_MICB1_EXT_CAP;
+
+ if (arizona->pdata.micbias[i].discharge)
+ val |= ARIZONA_MICB1_DISCH;
+
+ if (arizona->pdata.micbias[i].fast_start)
+ val |= ARIZONA_MICB1_RATE;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MIC_BIAS_CTRL_1 + i,
+ ARIZONA_MICB1_LVL_MASK |
+ ARIZONA_MICB1_DISCH |
+ ARIZONA_MICB1_RATE, val);
+ }
+
for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
/* Default for both is 0 so noop with defaults */
val = arizona->pdata.dmic_ref[i]
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 885e56780358..6a9fec40d018 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -60,7 +60,7 @@ static inline bool i2c_safe_reg(unsigned char reg)
* This fix is to follow any read or write with a dummy read to a safe
* register.
*/
-int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
+static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
{
int val;
@@ -85,7 +85,6 @@ int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
return 0;
}
-EXPORT_SYMBOL(da9052_i2c_fix);
static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
{
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index a2bacf95b59e..21f261bf9e95 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -33,6 +33,7 @@
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
#include <linux/cpufreq.h>
+#include <linux/platform_data/ux500_wdt.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
@@ -2207,21 +2208,25 @@ int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
A9WDOG_AUTO_OFF_DIS);
}
+EXPORT_SYMBOL(db8500_prcmu_config_a9wdog);
int db8500_prcmu_enable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
}
+EXPORT_SYMBOL(db8500_prcmu_enable_a9wdog);
int db8500_prcmu_disable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
}
+EXPORT_SYMBOL(db8500_prcmu_disable_a9wdog);
int db8500_prcmu_kick_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
}
+EXPORT_SYMBOL(db8500_prcmu_kick_a9wdog);
/*
* timeout is 28 bit, in ms.
@@ -2239,6 +2244,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)
(u8)((timeout >> 12) & 0xff),
(u8)((timeout >> 20) & 0xff));
}
+EXPORT_SYMBOL(db8500_prcmu_load_a9wdog);
/**
* prcmu_abb_read() - Read register value(s) from the ABB.
@@ -3094,6 +3100,11 @@ static struct resource ab8500_resources[] = {
}
};
+static struct ux500_wdt_data db8500_wdt_pdata = {
+ .timeout = 600, /* 10 minutes */
+ .has_28_bits_resolution = true,
+};
+
static struct mfd_cell db8500_prcmu_devs[] = {
{
.name = "db8500-prcmu-regulators",
@@ -3108,6 +3119,12 @@ static struct mfd_cell db8500_prcmu_devs[] = {
.pdata_size = sizeof(db8500_cpufreq_table),
},
{
+ .name = "ux500_wdt",
+ .platform_data = &db8500_wdt_pdata,
+ .pdata_size = sizeof(db8500_wdt_pdata),
+ .id = -1,
+ },
+ {
.name = "ab8500-core",
.of_compatible = "stericsson,ab8500",
.num_resources = ARRAY_SIZE(ab8500_resources),
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index d9d930302e98..9f12f91d6296 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -50,6 +50,7 @@
* document number TBD : Panther Point
* document number TBD : Lynx Point
* document number TBD : Lynx Point-LP
+ * document number TBD : Wellsburg
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -75,8 +76,10 @@
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
-#define GPIOBASE 0x48
-#define GPIOCTRL 0x4C
+#define GPIOBASE_ICH0 0x58
+#define GPIOCTRL_ICH0 0x5C
+#define GPIOBASE_ICH6 0x48
+#define GPIOCTRL_ICH6 0x4C
#define RCBABASE 0xf0
@@ -84,8 +87,17 @@
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
-static int lpc_ich_acpi_save = -1;
-static int lpc_ich_gpio_save = -1;
+struct lpc_ich_cfg {
+ int base;
+ int ctrl;
+ int save;
+};
+
+struct lpc_ich_priv {
+ int chipset;
+ struct lpc_ich_cfg acpi;
+ struct lpc_ich_cfg gpio;
+};
static struct resource wdt_ich_res[] = {
/* ACPI - TCO */
@@ -194,6 +206,7 @@ enum lpc_chipsets {
LPC_PPT, /* Panther Point */
LPC_LPT, /* Lynx Point */
LPC_LPT_LP, /* Lynx Point-LP */
+ LPC_WBG, /* Wellsburg */
};
struct lpc_ich_info lpc_chipset_info[] = {
@@ -474,6 +487,10 @@ struct lpc_ich_info lpc_chipset_info[] = {
.name = "Lynx Point_LP",
.iTCO_version = 2,
},
+ [LPC_WBG] = {
+ .name = "Wellsburg",
+ .iTCO_version = 2,
+ },
};
/*
@@ -655,45 +672,82 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
{ PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP},
+ { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
static void lpc_ich_restore_config_space(struct pci_dev *dev)
{
- if (lpc_ich_acpi_save >= 0) {
- pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
- lpc_ich_acpi_save = -1;
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+
+ if (priv->acpi.save >= 0) {
+ pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save);
+ priv->acpi.save = -1;
}
- if (lpc_ich_gpio_save >= 0) {
- pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
- lpc_ich_gpio_save = -1;
+ if (priv->gpio.save >= 0) {
+ pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save);
+ priv->gpio.save = -1;
}
}
static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, ACPICTRL, &reg_save);
- pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
- lpc_ich_acpi_save = reg_save;
+ pci_read_config_byte(dev, priv->acpi.ctrl, &reg_save);
+ pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10);
+ priv->acpi.save = reg_save;
}
static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, GPIOCTRL, &reg_save);
- pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
- lpc_ich_gpio_save = reg_save;
+ pci_read_config_byte(dev, priv->gpio.ctrl, &reg_save);
+ pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10);
+ priv->gpio.save = reg_save;
}
-static void lpc_ich_finalize_cell(struct mfd_cell *cell,
- const struct pci_device_id *id)
+static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
{
- cell->platform_data = &lpc_chipset_info[id->driver_data];
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+
+ cell->platform_data = &lpc_chipset_info[priv->chipset];
cell->pdata_size = sizeof(struct lpc_ich_info);
}
@@ -721,9 +775,9 @@ static int lpc_ich_check_conflict_gpio(struct resource *res)
return use_gpio ? use_gpio : ret;
}
-static int lpc_ich_init_gpio(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int lpc_ich_init_gpio(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u32 base_addr_cfg;
u32 base_addr;
int ret;
@@ -731,7 +785,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -757,7 +811,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
gpe0_done:
/* Setup GPIO base register */
- pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
@@ -768,7 +822,7 @@ gpe0_done:
/* Older devices provide fewer GPIO and have a smaller resource size. */
res = &gpio_ich_res[ICH_RES_GPIO];
res->start = base_addr;
- switch (lpc_chipset_info[id->driver_data].gpio_version) {
+ switch (lpc_chipset_info[priv->chipset].gpio_version) {
case ICH_V5_GPIO:
case ICH_V10CORP_GPIO:
res->end = res->start + 128 - 1;
@@ -784,10 +838,10 @@ gpe0_done:
acpi_conflict = true;
goto gpio_done;
}
- lpc_chipset_info[id->driver_data].use_gpio = ret;
+ lpc_chipset_info[priv->chipset].use_gpio = ret;
lpc_ich_enable_gpio_space(dev);
- lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
+ lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
1, NULL, 0, NULL);
@@ -798,16 +852,16 @@ gpio_done:
return ret;
}
-static int lpc_ich_init_wdt(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int lpc_ich_init_wdt(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u32 base_addr_cfg;
u32 base_addr;
int ret;
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -830,7 +884,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
*/
- if (lpc_chipset_info[id->driver_data].iTCO_version == 1) {
+ if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
/* Don't register iomem for TCO ver 1 */
lpc_ich_cells[LPC_WDT].num_resources--;
} else {
@@ -847,7 +901,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
res->end = base_addr + ACPIBASE_GCS_END;
}
- lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
+ lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
1, NULL, 0, NULL);
@@ -858,14 +912,36 @@ wdt_done:
static int lpc_ich_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
+ struct lpc_ich_priv *priv;
int ret;
bool cell_added = false;
- ret = lpc_ich_init_wdt(dev, id);
+ priv = devm_kzalloc(&dev->dev,
+ sizeof(struct lpc_ich_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->chipset = id->driver_data;
+ priv->acpi.save = -1;
+ priv->acpi.base = ACPIBASE;
+ priv->acpi.ctrl = ACPICTRL;
+
+ priv->gpio.save = -1;
+ if (priv->chipset <= LPC_ICH5) {
+ priv->gpio.base = GPIOBASE_ICH0;
+ priv->gpio.ctrl = GPIOCTRL_ICH0;
+ } else {
+ priv->gpio.base = GPIOBASE_ICH6;
+ priv->gpio.ctrl = GPIOCTRL_ICH6;
+ }
+
+ pci_set_drvdata(dev, priv);
+
+ ret = lpc_ich_init_wdt(dev);
if (!ret)
cell_added = true;
- ret = lpc_ich_init_gpio(dev, id);
+ ret = lpc_ich_init_gpio(dev);
if (!ret)
cell_added = true;
@@ -876,6 +952,7 @@ static int lpc_ich_probe(struct pci_dev *dev,
if (!cell_added) {
dev_warn(&dev->dev, "No MFD cells added\n");
lpc_ich_restore_config_space(dev);
+ pci_set_drvdata(dev, NULL);
return -ENODEV;
}
@@ -886,6 +963,7 @@ static void lpc_ich_remove(struct pci_dev *dev)
{
mfd_remove_devices(&dev->dev);
lpc_ich_restore_config_space(dev);
+ pci_set_drvdata(dev, NULL);
}
static struct pci_driver lpc_ich_driver = {
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index 5624fcbba69b..8cc6aac27cb2 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -45,34 +45,32 @@ static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
-
static struct resource gpio_sch_resource = {
.flags = IORESOURCE_IO,
};
-static struct mfd_cell lpc_sch_cells[] = {
- {
- .name = "isch_smbus",
- .num_resources = 1,
- .resources = &smbus_sch_resource,
- },
- {
- .name = "sch_gpio",
- .num_resources = 1,
- .resources = &gpio_sch_resource,
- },
-};
-
static struct resource wdt_sch_resource = {
.flags = IORESOURCE_IO,
};
-static struct mfd_cell tunnelcreek_cells[] = {
- {
- .name = "ie6xx_wdt",
- .num_resources = 1,
- .resources = &wdt_sch_resource,
- },
+static struct mfd_cell lpc_sch_cells[3];
+
+static struct mfd_cell isch_smbus_cell = {
+ .name = "isch_smbus",
+ .num_resources = 1,
+ .resources = &smbus_sch_resource,
+};
+
+static struct mfd_cell sch_gpio_cell = {
+ .name = "sch_gpio",
+ .num_resources = 1,
+ .resources = &gpio_sch_resource,
+};
+
+static struct mfd_cell wdt_sch_cell = {
+ .name = "ie6xx_wdt",
+ .num_resources = 1,
+ .resources = &wdt_sch_resource,
};
static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
@@ -88,79 +86,76 @@ static int lpc_sch_probe(struct pci_dev *dev,
{
unsigned int base_addr_cfg;
unsigned short base_addr;
- int i;
+ int i, cells = 0;
int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
- return -ENODEV;
- }
- base_addr = (unsigned short)base_addr_cfg;
- if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
- return -ENODEV;
- }
-
- smbus_sch_resource.start = base_addr;
- smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
- pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
- return -ENODEV;
- }
- base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
- return -ENODEV;
+ dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n");
+ } else {
+ lpc_sch_cells[cells++] = isch_smbus_cell;
+ smbus_sch_resource.start = base_addr;
+ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
}
- gpio_sch_resource.start = base_addr;
-
- if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
- gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
+ pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n");
else
- gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
-
- for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
- lpc_sch_cells[i].id = id->device;
+ base_addr = (unsigned short)base_addr_cfg;
- ret = mfd_add_devices(&dev->dev, 0,
- lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
- 0, NULL);
- if (ret)
- goto out_dev;
+ if (base_addr == 0) {
+ dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n");
+ } else {
+ lpc_sch_cells[cells++] = sch_gpio_cell;
+ gpio_sch_resource.start = base_addr;
+ if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
+ else
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+ }
if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
- || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
+ || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
- ret = -ENODEV;
- goto out_dev;
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0)
+ dev_warn(&dev->dev, "I/O space for WDT uninitialized\n");
+ else {
+ lpc_sch_cells[cells++] = wdt_sch_cell;
+ wdt_sch_resource.start = base_addr;
+ wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
}
- base_addr = (unsigned short)base_addr_cfg;
- if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
- ret = -ENODEV;
- goto out_dev;
- }
-
- wdt_sch_resource.start = base_addr;
- wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+ }
- for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
- tunnelcreek_cells[i].id = id->device;
+ if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) {
+ dev_err(&dev->dev, "Cell count exceeds array size");
+ return -ENODEV;
+ }
- ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
- ARRAY_SIZE(tunnelcreek_cells), NULL,
- 0, NULL);
+ if (cells == 0) {
+ dev_err(&dev->dev, "All decode registers disabled.\n");
+ return -ENODEV;
}
- return ret;
-out_dev:
- mfd_remove_devices(&dev->dev);
+ for (i = 0; i < cells; i++)
+ lpc_sch_cells[i].id = id->device;
+
+ ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
+ if (ret)
+ mfd_remove_devices(&dev->dev);
+
return ret;
}
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index e32466e865b9..f0cc40296d8c 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -14,10 +14,13 @@
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
#include <linux/platform_device.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8925.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
static struct resource bk_resources[] = {
{ 0x84, 0x84, "mode control", IORESOURCE_REG, },
@@ -639,17 +642,33 @@ static struct irq_chip max8925_irq_chip = {
.irq_disable = max8925_irq_disable,
};
+static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_data(virq, d->host_data);
+ irq_set_chip_and_handler(virq, &max8925_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(virq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ irq_set_noprobe(virq);
+#endif
+ return 0;
+}
+
+static struct irq_domain_ops max8925_irq_domain_ops = {
+ .map = max8925_irq_domain_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+
static int max8925_irq_init(struct max8925_chip *chip, int irq,
struct max8925_platform_data *pdata)
{
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
- int i, ret;
- int __irq;
+ int ret;
+ struct device_node *node = chip->dev->of_node;
- if (!pdata || !pdata->irq_base) {
- dev_warn(chip->dev, "No interrupt support on IRQ base\n");
- return -EINVAL;
- }
/* clear all interrupts */
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2);
@@ -667,35 +686,30 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
mutex_init(&chip->irq_lock);
- chip->core_irq = irq;
- chip->irq_base = pdata->irq_base;
-
- /* register with genirq */
- for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
- __irq = i + chip->irq_base;
- irq_set_chip_data(__irq, chip);
- irq_set_chip_and_handler(__irq, &max8925_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(__irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(__irq, IRQF_VALID);
-#else
- irq_set_noprobe(__irq);
-#endif
- }
- if (!irq) {
- dev_warn(chip->dev, "No interrupt support on core IRQ\n");
- goto tsc_irq;
+ chip->irq_base = irq_alloc_descs(-1, 0, MAX8925_NR_IRQS, 0);
+ if (chip->irq_base < 0) {
+ dev_err(chip->dev, "Failed to allocate interrupts, ret:%d\n",
+ chip->irq_base);
+ return -EBUSY;
}
+ irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0,
+ &max8925_irq_domain_ops, chip);
+
+ /* request irq handler for pmic main irq*/
+ chip->core_irq = irq;
+ if (!chip->core_irq)
+ return -EBUSY;
ret = request_threaded_irq(irq, NULL, max8925_irq, flags | IRQF_ONESHOT,
"max8925", chip);
if (ret) {
dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
chip->core_irq = 0;
+ return -EBUSY;
}
-tsc_irq:
+ /* request irq handler for pmic tsc irq*/
+
/* mask TSC interrupt */
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
@@ -704,7 +718,6 @@ tsc_irq:
return 0;
}
chip->tsc_irq = pdata->tsc_irq;
-
ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq,
flags | IRQF_ONESHOT, "max8925-tsc", chip);
if (ret) {
@@ -846,7 +859,7 @@ int max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs),
- &rtc_resources[0], chip->irq_base, NULL);
+ NULL, chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
@@ -854,7 +867,7 @@ int max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs),
- &onkey_resources[0], 0, NULL);
+ NULL, chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
@@ -873,21 +886,19 @@ int max8925_device_init(struct max8925_chip *chip,
goto out_dev;
}
- if (pdata && pdata->power) {
- ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
- ARRAY_SIZE(power_devs),
- &power_supply_resources[0], 0, NULL);
- if (ret < 0) {
- dev_err(chip->dev, "Failed to add power supply "
- "subdev\n");
- goto out_dev;
- }
+ ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
+ ARRAY_SIZE(power_devs),
+ NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to add power supply subdev, err = %d\n", ret);
+ goto out_dev;
}
if (pdata && pdata->touch) {
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs),
- &touch_resources[0], 0, NULL);
+ NULL, chip->tsc_irq, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add touch subdev\n");
goto out_dev;
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
index 00b5b456063d..92bbebd31598 100644
--- a/drivers/mfd/max8925-i2c.c
+++ b/drivers/mfd/max8925-i2c.c
@@ -135,13 +135,37 @@ static const struct i2c_device_id max8925_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, max8925_id_table);
+static int max8925_dt_init(struct device_node *np, struct device *dev,
+ struct max8925_platform_data *pdata)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq);
+ if (ret) {
+ dev_err(dev, "Not found maxim,tsc-irq property\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int max8925_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max8925_platform_data *pdata = client->dev.platform_data;
static struct max8925_chip *chip;
-
- if (!pdata) {
+ struct device_node *node = client->dev.of_node;
+
+ if (node && !pdata) {
+ /* parse DT to get platform data */
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct max8925_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (max8925_dt_init(node, &client->dev, pdata))
+ return -EINVAL;
+ } else if (!pdata) {
pr_info("%s: platform data is missing\n", __func__);
return -EINVAL;
}
@@ -203,11 +227,18 @@ static int max8925_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume);
+static const struct of_device_id max8925_dt_ids[] = {
+ { .compatible = "maxim,max8925", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max8925_dt_ids);
+
static struct i2c_driver max8925_driver = {
.driver = {
.name = "max8925",
.owner = THIS_MODULE,
.pm = &max8925_pm_ops,
+ .of_match_table = of_match_ptr(max8925_dt_ids),
},
.probe = max8925_probe,
.remove = max8925_remove,
@@ -217,7 +248,6 @@ static struct i2c_driver max8925_driver = {
static int __init max8925_i2c_init(void)
{
int ret;
-
ret = i2c_add_driver(&max8925_driver);
if (ret != 0)
pr_err("Failed to register MAX8925 I2C driver: %d\n", ret);
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 05164d7f054b..6b5edf64de2b 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -23,7 +23,6 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
-#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb-omap.h>
@@ -91,21 +90,23 @@
struct usbhs_hcd_omap {
+ int nports;
+ struct clk **utmi_clk;
+ struct clk **hsic60m_clk;
+ struct clk **hsic480m_clk;
+
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
- struct clk *utmi_p1_fck;
- struct clk *usbhost_p1_fck;
- struct clk *utmi_p2_fck;
- struct clk *usbhost_p2_fck;
+ struct clk *utmi_p1_gfclk;
+ struct clk *utmi_p2_gfclk;
struct clk *init_60m_fclk;
struct clk *ehci_logic_fck;
void __iomem *uhh_base;
- struct usbhs_omap_platform_data platdata;
+ struct usbhs_omap_platform_data *pdata;
u32 usbhs_rev;
- spinlock_t lock;
};
/*-------------------------------------------------------------------------*/
@@ -184,19 +185,13 @@ err_end:
static int omap_usbhs_alloc_children(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct usbhs_hcd_omap *omap;
- struct ehci_hcd_omap_platform_data *ehci_data;
- struct ohci_hcd_omap_platform_data *ohci_data;
+ struct usbhs_omap_platform_data *pdata = dev->platform_data;
struct platform_device *ehci;
struct platform_device *ohci;
struct resource *res;
struct resource resources[2];
int ret;
- omap = platform_get_drvdata(pdev);
- ehci_data = omap->platdata.ehci_data;
- ohci_data = omap->platdata.ohci_data;
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
if (!res) {
dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
@@ -213,8 +208,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
}
resources[1] = *res;
- ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data,
- sizeof(*ehci_data), dev);
+ ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata,
+ sizeof(*pdata), dev);
if (!ehci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
@@ -238,8 +233,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
}
resources[1] = *res;
- ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data,
- sizeof(*ohci_data), dev);
+ ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata,
+ sizeof(*pdata), dev);
if (!ohci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
ret = -ENOMEM;
@@ -278,31 +273,52 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
static int usbhs_runtime_resume(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i, r;
dev_dbg(dev, "usbhs_runtime_resume\n");
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
- }
-
omap_tll_enable();
- spin_lock_irqsave(&omap->lock, flags);
- if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+ if (!IS_ERR(omap->ehci_logic_fck))
clk_enable(omap->ehci_logic_fck);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(omap->usbhost_p1_fck);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(omap->usbhost_p2_fck);
-
- clk_enable(omap->utmi_p1_fck);
- clk_enable(omap->utmi_p2_fck);
+ for (i = 0; i < omap->nports; i++) {
+ switch (pdata->port_mode[i]) {
+ case OMAP_EHCI_PORT_MODE_HSIC:
+ if (!IS_ERR(omap->hsic60m_clk[i])) {
+ r = clk_enable(omap->hsic60m_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d hsic60m clk:%d\n",
+ i, r);
+ }
+ }
- spin_unlock_irqrestore(&omap->lock, flags);
+ if (!IS_ERR(omap->hsic480m_clk[i])) {
+ r = clk_enable(omap->hsic480m_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d hsic480m clk:%d\n",
+ i, r);
+ }
+ }
+ /* Fall through as HSIC mode needs utmi_clk */
+
+ case OMAP_EHCI_PORT_MODE_TLL:
+ if (!IS_ERR(omap->utmi_clk[i])) {
+ r = clk_enable(omap->utmi_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d clk : %d\n",
+ i, r);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
return 0;
}
@@ -310,51 +326,122 @@ static int usbhs_runtime_resume(struct device *dev)
static int usbhs_runtime_suspend(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i;
dev_dbg(dev, "usbhs_runtime_suspend\n");
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
- }
-
- spin_lock_irqsave(&omap->lock, flags);
+ for (i = 0; i < omap->nports; i++) {
+ switch (pdata->port_mode[i]) {
+ case OMAP_EHCI_PORT_MODE_HSIC:
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_disable(omap->hsic60m_clk[i]);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_disable(omap->usbhost_p1_fck);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_disable(omap->usbhost_p2_fck);
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_disable(omap->hsic480m_clk[i]);
+ /* Fall through as utmi_clks were used in HSIC mode */
- clk_disable(omap->utmi_p2_fck);
- clk_disable(omap->utmi_p1_fck);
+ case OMAP_EHCI_PORT_MODE_TLL:
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_disable(omap->utmi_clk[i]);
+ break;
+ default:
+ break;
+ }
+ }
- if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+ if (!IS_ERR(omap->ehci_logic_fck))
clk_disable(omap->ehci_logic_fck);
- spin_unlock_irqrestore(&omap->lock, flags);
omap_tll_disable();
return 0;
}
+static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap,
+ unsigned reg)
+{
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i;
+
+ for (i = 0; i < omap->nports; i++) {
+ switch (pdata->port_mode[i]) {
+ case OMAP_USBHS_PORT_MODE_UNUSED:
+ reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i);
+ break;
+ case OMAP_EHCI_PORT_MODE_PHY:
+ if (pdata->single_ulpi_bypass)
+ break;
+
+ if (i == 0)
+ reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+ else
+ reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
+ << (i-1));
+ break;
+ default:
+ if (pdata->single_ulpi_bypass)
+ break;
+
+ if (i == 0)
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+ else
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
+ << (i-1);
+ break;
+ }
+ }
+
+ if (pdata->single_ulpi_bypass) {
+ /* bypass ULPI only if none of the ports use PHY mode */
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_phy_mode(pdata->port_mode[i])) {
+ reg &= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+ break;
+ }
+ }
+ }
+
+ return reg;
+}
+
+static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap,
+ unsigned reg)
+{
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i;
+
+ for (i = 0; i < omap->nports; i++) {
+ /* Clear port mode fields for PHY mode */
+ reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i);
+
+ if (is_ehci_tll_mode(pdata->port_mode[i]) ||
+ (is_ohci_port(pdata->port_mode[i])))
+ reg |= OMAP4_P1_MODE_TLL << 2 * i;
+ else if (is_ehci_hsic_mode(pdata->port_mode[i]))
+ reg |= OMAP4_P1_MODE_HSIC << 2 * i;
+ }
+
+ return reg;
+}
+
static void omap_usbhs_init(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
unsigned reg;
dev_dbg(dev, "starting TI HSUSB Controller\n");
- if (pdata->ehci_data->phy_reset) {
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
- gpio_request_one(pdata->ehci_data->reset_gpio_port[0],
+ if (pdata->phy_reset) {
+ if (gpio_is_valid(pdata->reset_gpio_port[0]))
+ gpio_request_one(pdata->reset_gpio_port[0],
GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
- gpio_request_one(pdata->ehci_data->reset_gpio_port[1],
+ if (gpio_is_valid(pdata->reset_gpio_port[1]))
+ gpio_request_one(pdata->reset_gpio_port[1],
GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
/* Hold the PHY in RESET for enough time till DIR is high */
@@ -362,9 +449,6 @@ static void omap_usbhs_init(struct device *dev)
}
pm_runtime_get_sync(dev);
- spin_lock_irqsave(&omap->lock, flags);
- omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
- dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
/* setup ULPI bypass and burst configurations */
@@ -374,89 +458,51 @@ static void omap_usbhs_init(struct device *dev)
reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
- if (is_omap_usbhs_rev1(omap)) {
- if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
- if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
- if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
-
- /* Bypass the TLL module for PHY mode operation */
- if (pdata->single_ulpi_bypass) {
- dev_dbg(dev, "OMAP3 ES version <= ES2.1\n");
- if (is_ehci_phy_mode(pdata->port_mode[0]) ||
- is_ehci_phy_mode(pdata->port_mode[1]) ||
- is_ehci_phy_mode(pdata->port_mode[2]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- } else {
- dev_dbg(dev, "OMAP3 ES version > ES2.1\n");
- if (is_ehci_phy_mode(pdata->port_mode[0]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
- if (is_ehci_phy_mode(pdata->port_mode[1]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
- if (is_ehci_phy_mode(pdata->port_mode[2]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- }
- } else if (is_omap_usbhs_rev2(omap)) {
- /* Clear port mode fields for PHY mode*/
- reg &= ~OMAP4_P1_MODE_CLEAR;
- reg &= ~OMAP4_P2_MODE_CLEAR;
+ switch (omap->usbhs_rev) {
+ case OMAP_USBHS_REV1:
+ omap_usbhs_rev1_hostconfig(omap, reg);
+ break;
- if (is_ehci_tll_mode(pdata->port_mode[0]) ||
- (is_ohci_port(pdata->port_mode[0])))
- reg |= OMAP4_P1_MODE_TLL;
- else if (is_ehci_hsic_mode(pdata->port_mode[0]))
- reg |= OMAP4_P1_MODE_HSIC;
+ case OMAP_USBHS_REV2:
+ omap_usbhs_rev2_hostconfig(omap, reg);
+ break;
- if (is_ehci_tll_mode(pdata->port_mode[1]) ||
- (is_ohci_port(pdata->port_mode[1])))
- reg |= OMAP4_P2_MODE_TLL;
- else if (is_ehci_hsic_mode(pdata->port_mode[1]))
- reg |= OMAP4_P2_MODE_HSIC;
+ default: /* newer revisions */
+ omap_usbhs_rev2_hostconfig(omap, reg);
+ break;
}
usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
- spin_unlock_irqrestore(&omap->lock, flags);
-
pm_runtime_put_sync(dev);
- if (pdata->ehci_data->phy_reset) {
+ if (pdata->phy_reset) {
/* Hold the PHY in RESET for enough time till
* PHY is settled and ready
*/
udelay(10);
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
+ if (gpio_is_valid(pdata->reset_gpio_port[0]))
gpio_set_value_cansleep
- (pdata->ehci_data->reset_gpio_port[0], 1);
+ (pdata->reset_gpio_port[0], 1);
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
+ if (gpio_is_valid(pdata->reset_gpio_port[1]))
gpio_set_value_cansleep
- (pdata->ehci_data->reset_gpio_port[1], 1);
+ (pdata->reset_gpio_port[1], 1);
}
}
static void omap_usbhs_deinit(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
- if (pdata->ehci_data->phy_reset) {
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
- gpio_free(pdata->ehci_data->reset_gpio_port[0]);
+ if (pdata->phy_reset) {
+ if (gpio_is_valid(pdata->reset_gpio_port[0]))
+ gpio_free(pdata->reset_gpio_port[0]);
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
- gpio_free(pdata->ehci_data->reset_gpio_port[1]);
+ if (gpio_is_valid(pdata->reset_gpio_port[1]))
+ gpio_free(pdata->reset_gpio_port[1]);
}
}
@@ -474,137 +520,185 @@ static int usbhs_omap_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
int i;
+ bool need_logic_fck;
if (!pdata) {
dev_err(dev, "Missing platform data\n");
- ret = -ENOMEM;
- goto end_probe;
+ return -ENODEV;
}
- omap = kzalloc(sizeof(*omap), GFP_KERNEL);
+ omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
if (!omap) {
dev_err(dev, "Memory allocation failed\n");
- ret = -ENOMEM;
- goto end_probe;
+ return -ENOMEM;
}
- spin_lock_init(&omap->lock);
-
- for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
- omap->platdata.port_mode[i] = pdata->port_mode[i];
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
+ omap->uhh_base = devm_request_and_ioremap(dev, res);
+ if (!omap->uhh_base) {
+ dev_err(dev, "Resource request/ioremap failed\n");
+ return -EADDRNOTAVAIL;
+ }
- omap->platdata.ehci_data = pdata->ehci_data;
- omap->platdata.ohci_data = pdata->ohci_data;
+ omap->pdata = pdata;
pm_runtime_enable(dev);
+ platform_set_drvdata(pdev, omap);
+ pm_runtime_get_sync(dev);
+
+ omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
- for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
- if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
- is_ehci_hsic_mode(i)) {
- omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
- if (IS_ERR(omap->ehci_logic_fck)) {
- ret = PTR_ERR(omap->ehci_logic_fck);
- dev_warn(dev, "ehci_logic_fck failed:%d\n",
- ret);
- }
+ /* we need to call runtime suspend before we update omap->nports
+ * to prevent unbalanced clk_disable()
+ */
+ pm_runtime_put_sync(dev);
+
+ /*
+ * If platform data contains nports then use that
+ * else make out number of ports from USBHS revision
+ */
+ if (pdata->nports) {
+ omap->nports = pdata->nports;
+ } else {
+ switch (omap->usbhs_rev) {
+ case OMAP_USBHS_REV1:
+ omap->nports = 3;
+ break;
+ case OMAP_USBHS_REV2:
+ omap->nports = 2;
+ break;
+ default:
+ omap->nports = OMAP3_HS_USB_PORTS;
+ dev_dbg(dev,
+ "USB HOST Rev:0x%d not recognized, assuming %d ports\n",
+ omap->usbhs_rev, omap->nports);
break;
}
+ }
- omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
- if (IS_ERR(omap->utmi_p1_fck)) {
- ret = PTR_ERR(omap->utmi_p1_fck);
- dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_end;
+ i = sizeof(struct clk *) * omap->nports;
+ omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+ omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+ omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+
+ if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) {
+ dev_err(dev, "Memory allocation failed\n");
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ need_logic_fck = false;
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
+ is_ehci_hsic_mode(i))
+ need_logic_fck |= true;
+ }
+
+ omap->ehci_logic_fck = ERR_PTR(-EINVAL);
+ if (need_logic_fck) {
+ omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
+ if (IS_ERR(omap->ehci_logic_fck)) {
+ ret = PTR_ERR(omap->ehci_logic_fck);
+ dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret);
+ }
+ }
+
+ omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk");
+ if (IS_ERR(omap->utmi_p1_gfclk)) {
+ ret = PTR_ERR(omap->utmi_p1_gfclk);
+ dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
+ goto err_p1_gfclk;
+ }
+
+ omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk");
+ if (IS_ERR(omap->utmi_p2_gfclk)) {
+ ret = PTR_ERR(omap->utmi_p2_gfclk);
+ dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
+ goto err_p2_gfclk;
}
omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
if (IS_ERR(omap->xclk60mhsp1_ck)) {
ret = PTR_ERR(omap->xclk60mhsp1_ck);
dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_utmi_p1_fck;
- }
-
- omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
- if (IS_ERR(omap->utmi_p2_fck)) {
- ret = PTR_ERR(omap->utmi_p2_fck);
- dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_xclk60mhsp1_ck;
+ goto err_xclk60mhsp1;
}
omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_utmi_p2_fck;
- }
-
- omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
- if (IS_ERR(omap->usbhost_p1_fck)) {
- ret = PTR_ERR(omap->usbhost_p1_fck);
- dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
- goto err_xclk60mhsp2_ck;
- }
-
- omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
- if (IS_ERR(omap->usbhost_p2_fck)) {
- ret = PTR_ERR(omap->usbhost_p2_fck);
- dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
- goto err_usbhost_p1_fck;
+ goto err_xclk60mhsp2;
}
omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
if (IS_ERR(omap->init_60m_fclk)) {
ret = PTR_ERR(omap->init_60m_fclk);
dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_usbhost_p2_fck;
+ goto err_init60m;
+ }
+
+ for (i = 0; i < omap->nports; i++) {
+ char clkname[30];
+
+ /* clock names are indexed from 1*/
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_utmi_p%d_clk", i + 1);
+
+ /* If a clock is not found we won't bail out as not all
+ * platforms have all clocks and we can function without
+ * them
+ */
+ omap->utmi_clk[i] = clk_get(dev, clkname);
+ if (IS_ERR(omap->utmi_clk[i]))
+ dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+ clkname, PTR_ERR(omap->utmi_clk[i]));
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_hsic480m_p%d_clk", i + 1);
+ omap->hsic480m_clk[i] = clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic480m_clk[i]))
+ dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+ clkname, PTR_ERR(omap->hsic480m_clk[i]));
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_hsic60m_p%d_clk", i + 1);
+ omap->hsic60m_clk[i] = clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic60m_clk[i]))
+ dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+ clkname, PTR_ERR(omap->hsic60m_clk[i]));
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
- /* for OMAP3 , the clk set paretn fails */
- ret = clk_set_parent(omap->utmi_p1_fck,
+ /* for OMAP3, clk_set_parent fails */
+ ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->xclk60mhsp1_ck);
if (ret != 0)
- dev_err(dev, "xclk60mhsp1_ck set parent"
- "failed error:%d\n", ret);
+ dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n",
+ ret);
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
- ret = clk_set_parent(omap->utmi_p1_fck,
+ ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->init_60m_fclk);
if (ret != 0)
- dev_err(dev, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
+ dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n",
+ ret);
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
- ret = clk_set_parent(omap->utmi_p2_fck,
+ ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->xclk60mhsp2_ck);
if (ret != 0)
- dev_err(dev, "xclk60mhsp2_ck set parent"
- "failed error:%d\n", ret);
+ dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n",
+ ret);
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
- ret = clk_set_parent(omap->utmi_p2_fck,
+ ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->init_60m_fclk);
if (ret != 0)
- dev_err(dev, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
- if (!res) {
- dev_err(dev, "UHH EHCI get resource failed\n");
- ret = -ENODEV;
- goto err_init_60m_fclk;
- }
-
- omap->uhh_base = ioremap(res->start, resource_size(res));
- if (!omap->uhh_base) {
- dev_err(dev, "UHH ioremap failed\n");
- ret = -ENOMEM;
- goto err_init_60m_fclk;
+ dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n",
+ ret);
}
- platform_set_drvdata(pdev, omap);
-
omap_usbhs_init(dev);
ret = omap_usbhs_alloc_children(pdev);
if (ret) {
@@ -612,39 +706,41 @@ static int usbhs_omap_probe(struct platform_device *pdev)
goto err_alloc;
}
- goto end_probe;
+ return 0;
err_alloc:
omap_usbhs_deinit(&pdev->dev);
- iounmap(omap->uhh_base);
-
-err_init_60m_fclk:
- clk_put(omap->init_60m_fclk);
-err_usbhost_p2_fck:
- clk_put(omap->usbhost_p2_fck);
+ for (i = 0; i < omap->nports; i++) {
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_put(omap->utmi_clk[i]);
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_put(omap->hsic60m_clk[i]);
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_put(omap->hsic480m_clk[i]);
+ }
-err_usbhost_p1_fck:
- clk_put(omap->usbhost_p1_fck);
+ clk_put(omap->init_60m_fclk);
-err_xclk60mhsp2_ck:
+err_init60m:
clk_put(omap->xclk60mhsp2_ck);
-err_utmi_p2_fck:
- clk_put(omap->utmi_p2_fck);
-
-err_xclk60mhsp1_ck:
+err_xclk60mhsp2:
clk_put(omap->xclk60mhsp1_ck);
-err_utmi_p1_fck:
- clk_put(omap->utmi_p1_fck);
+err_xclk60mhsp1:
+ clk_put(omap->utmi_p2_gfclk);
-err_end:
- clk_put(omap->ehci_logic_fck);
+err_p2_gfclk:
+ clk_put(omap->utmi_p1_gfclk);
+
+err_p1_gfclk:
+ if (!IS_ERR(omap->ehci_logic_fck))
+ clk_put(omap->ehci_logic_fck);
+
+err_mem:
pm_runtime_disable(dev);
- kfree(omap);
-end_probe:
return ret;
}
@@ -657,19 +753,29 @@ end_probe:
static int usbhs_omap_remove(struct platform_device *pdev)
{
struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
+ int i;
omap_usbhs_deinit(&pdev->dev);
- iounmap(omap->uhh_base);
+
+ for (i = 0; i < omap->nports; i++) {
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_put(omap->utmi_clk[i]);
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_put(omap->hsic60m_clk[i]);
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_put(omap->hsic480m_clk[i]);
+ }
+
clk_put(omap->init_60m_fclk);
- clk_put(omap->usbhost_p2_fck);
- clk_put(omap->usbhost_p1_fck);
+ clk_put(omap->utmi_p1_gfclk);
+ clk_put(omap->utmi_p2_gfclk);
clk_put(omap->xclk60mhsp2_ck);
- clk_put(omap->utmi_p2_fck);
clk_put(omap->xclk60mhsp1_ck);
- clk_put(omap->utmi_p1_fck);
- clk_put(omap->ehci_logic_fck);
+
+ if (!IS_ERR(omap->ehci_logic_fck))
+ clk_put(omap->ehci_logic_fck);
+
pm_runtime_disable(&pdev->dev);
- kfree(omap);
return 0;
}
@@ -685,7 +791,7 @@ static struct platform_driver usbhs_omap_driver = {
.owner = THIS_MODULE,
.pm = &usbhsomap_dev_pm_ops,
},
- .remove = __exit_p(usbhs_omap_remove),
+ .remove = usbhs_omap_remove,
};
MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index eb869153206d..0aef1a768880 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -54,10 +54,13 @@
#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24
+#define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16)
+#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15)
#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
+#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1)
#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1)
#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
@@ -92,21 +95,25 @@
#define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */
#define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */
#define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */
+#define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */
#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL)
+/* only PHY and UNUSED modes don't need TLL */
+#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\
+ (x) != OMAP_EHCI_PORT_MODE_PHY)
+
struct usbtll_omap {
- struct clk *usbtll_p1_fck;
- struct clk *usbtll_p2_fck;
- struct usbtll_omap_platform_data platdata;
- /* secure the register updates */
- spinlock_t lock;
+ int nch; /* num. of channels */
+ struct usbhs_omap_platform_data *pdata;
+ struct clk **ch_clk;
};
/*-------------------------------------------------------------------------*/
-const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
-struct platform_device *tll_pdev;
+static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
+static struct device *tll_dev;
+static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */
/*-------------------------------------------------------------------------*/
@@ -203,84 +210,84 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
static int usbtll_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct usbtll_omap_platform_data *pdata = dev->platform_data;
+ struct usbhs_omap_platform_data *pdata = dev->platform_data;
void __iomem *base;
struct resource *res;
struct usbtll_omap *tll;
unsigned reg;
- unsigned long flags;
int ret = 0;
- int i, ver, count;
+ int i, ver;
+ bool needs_tll;
dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
- tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL);
+ tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL);
if (!tll) {
dev_err(dev, "Memory allocation failed\n");
- ret = -ENOMEM;
- goto end;
+ return -ENOMEM;
}
- spin_lock_init(&tll->lock);
-
- for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
- tll->platdata.port_mode[i] = pdata->port_mode[i];
-
- tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
- if (IS_ERR(tll->usbtll_p1_fck)) {
- ret = PTR_ERR(tll->usbtll_p1_fck);
- dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
- goto err_tll;
+ if (!pdata) {
+ dev_err(dev, "Platform data missing\n");
+ return -ENODEV;
}
- tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
- if (IS_ERR(tll->usbtll_p2_fck)) {
- ret = PTR_ERR(tll->usbtll_p2_fck);
- dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
- goto err_usbtll_p1_fck;
- }
+ tll->pdata = pdata;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "usb tll get resource failed\n");
- ret = -ENODEV;
- goto err_usbtll_p2_fck;
- }
-
- base = ioremap(res->start, resource_size(res));
+ base = devm_request_and_ioremap(dev, res);
if (!base) {
- dev_err(dev, "TLL ioremap failed\n");
- ret = -ENOMEM;
- goto err_usbtll_p2_fck;
+ ret = -EADDRNOTAVAIL;
+ dev_err(dev, "Resource request/ioremap failed:%d\n", ret);
+ return ret;
}
platform_set_drvdata(pdev, tll);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
- spin_lock_irqsave(&tll->lock, flags);
-
ver = usbtll_read(base, OMAP_USBTLL_REVISION);
switch (ver) {
case OMAP_USBTLL_REV1:
- case OMAP_USBTLL_REV2:
- count = OMAP_TLL_CHANNEL_COUNT;
+ case OMAP_USBTLL_REV4:
+ tll->nch = OMAP_TLL_CHANNEL_COUNT;
break;
+ case OMAP_USBTLL_REV2:
case OMAP_USBTLL_REV3:
- count = OMAP_REV2_TLL_CHANNEL_COUNT;
+ tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT;
break;
default:
- dev_err(dev, "TLL version failed\n");
- ret = -ENODEV;
- goto err_ioremap;
+ tll->nch = OMAP_TLL_CHANNEL_COUNT;
+ dev_dbg(dev,
+ "USB TLL Rev : 0x%x not recognized, assuming %d channels\n",
+ ver, tll->nch);
+ break;
}
- if (is_ehci_tll_mode(pdata->port_mode[0]) ||
- is_ehci_tll_mode(pdata->port_mode[1]) ||
- is_ehci_tll_mode(pdata->port_mode[2]) ||
- is_ohci_port(pdata->port_mode[0]) ||
- is_ohci_port(pdata->port_mode[1]) ||
- is_ohci_port(pdata->port_mode[2])) {
+ tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]),
+ GFP_KERNEL);
+ if (!tll->ch_clk) {
+ ret = -ENOMEM;
+ dev_err(dev, "Couldn't allocate memory for channel clocks\n");
+ goto err_clk_alloc;
+ }
+
+ for (i = 0; i < tll->nch; i++) {
+ char clkname[] = "usb_tll_hs_usb_chx_clk";
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_tll_hs_usb_ch%d_clk", i);
+ tll->ch_clk[i] = clk_get(dev, clkname);
+
+ if (IS_ERR(tll->ch_clk[i]))
+ dev_dbg(dev, "can't get clock : %s\n", clkname);
+ }
+
+ needs_tll = false;
+ for (i = 0; i < tll->nch; i++)
+ needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]);
+
+ if (needs_tll) {
/* Program Common TLL register */
reg = usbtll_read(base, OMAP_TLL_SHARED_CONF);
@@ -292,7 +299,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
usbtll_write(base, OMAP_TLL_SHARED_CONF, reg);
/* Enable channels now */
- for (i = 0; i < count; i++) {
+ for (i = 0; i < tll->nch; i++) {
reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i));
if (is_ohci_port(pdata->port_mode[i])) {
@@ -308,6 +315,15 @@ static int usbtll_omap_probe(struct platform_device *pdev)
reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
+ } else if (pdata->port_mode[i] ==
+ OMAP_EHCI_PORT_MODE_HSIC) {
+ /*
+ * HSIC Mode requires UTMI port configurations
+ */
+ reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS
+ | OMAP_TLL_CHANNEL_CONF_CHRGVBUS
+ | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI
+ | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF;
} else {
continue;
}
@@ -320,25 +336,18 @@ static int usbtll_omap_probe(struct platform_device *pdev)
}
}
-err_ioremap:
- spin_unlock_irqrestore(&tll->lock, flags);
- iounmap(base);
pm_runtime_put_sync(dev);
- tll_pdev = pdev;
- if (!ret)
- goto end;
- pm_runtime_disable(dev);
+ /* only after this can omap_tll_enable/disable work */
+ spin_lock(&tll_lock);
+ tll_dev = dev;
+ spin_unlock(&tll_lock);
-err_usbtll_p2_fck:
- clk_put(tll->usbtll_p2_fck);
-
-err_usbtll_p1_fck:
- clk_put(tll->usbtll_p1_fck);
+ return 0;
-err_tll:
- kfree(tll);
+err_clk_alloc:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
-end:
return ret;
}
@@ -351,36 +360,42 @@ end:
static int usbtll_omap_remove(struct platform_device *pdev)
{
struct usbtll_omap *tll = platform_get_drvdata(pdev);
+ int i;
+
+ spin_lock(&tll_lock);
+ tll_dev = NULL;
+ spin_unlock(&tll_lock);
+
+ for (i = 0; i < tll->nch; i++)
+ if (!IS_ERR(tll->ch_clk[i]))
+ clk_put(tll->ch_clk[i]);
- clk_put(tll->usbtll_p2_fck);
- clk_put(tll->usbtll_p1_fck);
pm_runtime_disable(&pdev->dev);
- kfree(tll);
return 0;
}
static int usbtll_runtime_resume(struct device *dev)
{
struct usbtll_omap *tll = dev_get_drvdata(dev);
- struct usbtll_omap_platform_data *pdata = &tll->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = tll->pdata;
+ int i;
dev_dbg(dev, "usbtll_runtime_resume\n");
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
- }
-
- spin_lock_irqsave(&tll->lock, flags);
+ for (i = 0; i < tll->nch; i++) {
+ if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
+ int r;
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(tll->usbtll_p1_fck);
-
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(tll->usbtll_p2_fck);
+ if (IS_ERR(tll->ch_clk[i]))
+ continue;
- spin_unlock_irqrestore(&tll->lock, flags);
+ r = clk_enable(tll->ch_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Error enabling ch %d clock: %d\n", i, r);
+ }
+ }
+ }
return 0;
}
@@ -388,26 +403,18 @@ static int usbtll_runtime_resume(struct device *dev)
static int usbtll_runtime_suspend(struct device *dev)
{
struct usbtll_omap *tll = dev_get_drvdata(dev);
- struct usbtll_omap_platform_data *pdata = &tll->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = tll->pdata;
+ int i;
dev_dbg(dev, "usbtll_runtime_suspend\n");
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
+ for (i = 0; i < tll->nch; i++) {
+ if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
+ if (!IS_ERR(tll->ch_clk[i]))
+ clk_disable(tll->ch_clk[i]);
+ }
}
- spin_lock_irqsave(&tll->lock, flags);
-
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_disable(tll->usbtll_p1_fck);
-
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_disable(tll->usbtll_p2_fck);
-
- spin_unlock_irqrestore(&tll->lock, flags);
-
return 0;
}
@@ -429,21 +436,39 @@ static struct platform_driver usbtll_omap_driver = {
int omap_tll_enable(void)
{
- if (!tll_pdev) {
- pr_err("missing omap usbhs tll platform_data\n");
- return -ENODEV;
+ int ret;
+
+ spin_lock(&tll_lock);
+
+ if (!tll_dev) {
+ pr_err("%s: OMAP USB TLL not initialized\n", __func__);
+ ret = -ENODEV;
+ } else {
+ ret = pm_runtime_get_sync(tll_dev);
}
- return pm_runtime_get_sync(&tll_pdev->dev);
+
+ spin_unlock(&tll_lock);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(omap_tll_enable);
int omap_tll_disable(void)
{
- if (!tll_pdev) {
- pr_err("missing omap usbhs tll platform_data\n");
- return -ENODEV;
+ int ret;
+
+ spin_lock(&tll_lock);
+
+ if (!tll_dev) {
+ pr_err("%s: OMAP USB TLL not initialized\n", __func__);
+ ret = -ENODEV;
+ } else {
+ ret = pm_runtime_put_sync(tll_dev);
}
- return pm_runtime_put_sync(&tll_pdev->dev);
+
+ spin_unlock(&tll_lock);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(omap_tll_disable);
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index 6ffd7a2affdc..bbdbc50a3cca 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -39,6 +39,14 @@ enum palmas_ids {
PALMAS_USB_ID,
};
+static struct resource palmas_rtc_resources[] = {
+ {
+ .start = PALMAS_RTC_ALARM_IRQ,
+ .end = PALMAS_RTC_ALARM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static const struct mfd_cell palmas_children[] = {
{
.name = "palmas-pmic",
@@ -59,6 +67,8 @@ static const struct mfd_cell palmas_children[] = {
{
.name = "palmas-rtc",
.id = PALMAS_RTC_ID,
+ .resources = &palmas_rtc_resources[0],
+ .num_resources = ARRAY_SIZE(palmas_rtc_resources),
},
{
.name = "palmas-pwrbutton",
@@ -456,8 +466,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
ret = mfd_add_devices(palmas->dev, -1,
children, ARRAY_SIZE(palmas_children),
- NULL, regmap_irq_chip_get_base(palmas->irq_data),
- NULL);
+ NULL, 0,
+ regmap_irq_get_domain(palmas->irq_data));
kfree(children);
if (ret < 0)
diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c
index 3d3b4addf81a..2a2d31687b72 100644
--- a/drivers/mfd/rtl8411.c
+++ b/drivers/mfd/rtl8411.c
@@ -115,14 +115,24 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)
static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
{
u8 mask, val;
+ int err;
mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK;
- if (voltage == OUTPUT_3V3)
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
+ if (err < 0)
+ return err;
val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3;
- else if (voltage == OUTPUT_1V8)
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
+ if (err < 0)
+ return err;
val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8;
- else
+ } else {
return -EINVAL;
+ }
return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);
}
diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c
index 98fe0f39463e..ec78d9fb0879 100644
--- a/drivers/mfd/rts5209.c
+++ b/drivers/mfd/rts5209.c
@@ -149,10 +149,18 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
int err;
if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
if (err < 0)
return err;
} else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
if (err < 0)
return err;
diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c
new file mode 100644
index 000000000000..fc831dcb1480
--- /dev/null
+++ b/drivers/mfd/rts5227.c
@@ -0,0 +1,234 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ *
+ * Roger Tseng <rogerable@realtek.com>
+ * No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static int rts5227_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ u16 cap;
+
+ rtsx_pci_init_cmd(pcr);
+
+ /* Configure GPIO as output */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02);
+ /* Switch LDO3318 source from DV33 to card_3v3 */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01);
+ /* LED shine disabled, set initial shine cycle period */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02);
+ /* Configure LTR */
+ pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap);
+ if (cap & PCI_EXP_LTR_EN)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3);
+ /* Configure OBFF */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03);
+ /* Configure force_clock_req
+ * Maybe We should define 0xFF03 as some name
+ */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, 0xFF03, 0x08, 0x08);
+ /* Correct driving */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ SD30_CLK_DRIVE_SEL, 0xFF, 0x96);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ SD30_CMD_DRIVE_SEL, 0xFF, 0x96);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ SD30_DAT_DRIVE_SEL, 0xFF, 0x96);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5227_optimize_phy(struct rtsx_pcr *pcr)
+{
+ /* Optimize RX sensitivity */
+ return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42);
+}
+
+static int rts5227_turn_on_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02);
+}
+
+static int rts5227_turn_off_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00);
+}
+
+static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08);
+}
+
+static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00);
+}
+
+static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_PARTIAL_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x02);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ /* To avoid too large in-rush current */
+ udelay(150);
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x06);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK | PMOS_STRG_MASK,
+ SD_POWER_OFF | PMOS_STRG_400mA);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0X00);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ int err;
+ u8 drive_sel;
+
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+ if (err < 0)
+ return err;
+ drive_sel = 0x96;
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24);
+ if (err < 0)
+ return err;
+ drive_sel = 0xB3;
+ } else {
+ return -EINVAL;
+ }
+
+ /* set pad drive */
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL,
+ 0xFF, drive_sel);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL,
+ 0xFF, drive_sel);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL,
+ 0xFF, drive_sel);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static const struct pcr_ops rts5227_pcr_ops = {
+ .extra_init_hw = rts5227_extra_init_hw,
+ .optimize_phy = rts5227_optimize_phy,
+ .turn_on_led = rts5227_turn_on_led,
+ .turn_off_led = rts5227_turn_off_led,
+ .enable_auto_blink = rts5227_enable_auto_blink,
+ .disable_auto_blink = rts5227_disable_auto_blink,
+ .card_power_on = rts5227_card_power_on,
+ .card_power_off = rts5227_card_power_off,
+ .switch_output_voltage = rts5227_switch_output_voltage,
+ .cd_deglitch = NULL,
+ .conv_clk_and_div_n = NULL,
+};
+
+/* SD Pull Control Enable:
+ * SD_DAT[3:0] ==> pull up
+ * SD_CD ==> pull up
+ * SD_WP ==> pull up
+ * SD_CMD ==> pull up
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5227_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+ 0,
+};
+
+/* SD Pull Control Disable:
+ * SD_DAT[3:0] ==> pull down
+ * SD_CD ==> pull up
+ * SD_WP ==> pull down
+ * SD_CMD ==> pull down
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5227_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+ 0,
+};
+
+/* MS Pull Control Enable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5227_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+/* MS Pull Control Disable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5227_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+void rts5227_init_params(struct rtsx_pcr *pcr)
+{
+ pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+ pcr->num_slots = 2;
+ pcr->ops = &rts5227_pcr_ops;
+
+ pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl;
+ pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl;
+ pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl;
+ pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c
index 29d889cbb9c5..58af4dbe3586 100644
--- a/drivers/mfd/rts5229.c
+++ b/drivers/mfd/rts5229.c
@@ -119,10 +119,18 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
int err;
if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
if (err < 0)
return err;
} else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
if (err < 0)
return err;
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
index 9fc57009e228..481a98a10ecd 100644
--- a/drivers/mfd/rtsx_pcr.c
+++ b/drivers/mfd/rtsx_pcr.c
@@ -55,6 +55,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = {
{ PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ 0, }
};
@@ -325,7 +326,6 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr,
val = ((u64)addr << 32) | ((u64)len << 12) | option;
put_unaligned_le64(val, ptr);
- ptr++;
pcr->sgi++;
}
@@ -591,8 +591,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
{
int err, clk;
- u8 N, min_N, max_N, clk_divider;
- u8 mcu_cnt, div, max_div;
+ u8 n, clk_divider, mcu_cnt, div;
u8 depth[] = {
[RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M,
[RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M,
@@ -616,10 +615,6 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
card_clock /= 1000000;
dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock);
- min_N = 80;
- max_N = 208;
- max_div = CLK_DIV_8;
-
clk = card_clock;
if (!initial_mode && double_clk)
clk = card_clock * 2;
@@ -631,30 +626,30 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
return 0;
if (pcr->ops->conv_clk_and_div_n)
- N = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
+ n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
else
- N = (u8)(clk - 2);
- if ((clk <= 2) || (N > max_N))
+ n = (u8)(clk - 2);
+ if ((clk <= 2) || (n > MAX_DIV_N_PCR))
return -EINVAL;
mcu_cnt = (u8)(125/clk + 3);
if (mcu_cnt > 15)
mcu_cnt = 15;
- /* Make sure that the SSC clock div_n is equal or greater than min_N */
+ /* Make sure that the SSC clock div_n is not less than MIN_DIV_N_PCR */
div = CLK_DIV_1;
- while ((N < min_N) && (div < max_div)) {
+ while ((n < MIN_DIV_N_PCR) && (div < CLK_DIV_8)) {
if (pcr->ops->conv_clk_and_div_n) {
- int dbl_clk = pcr->ops->conv_clk_and_div_n(N,
+ int dbl_clk = pcr->ops->conv_clk_and_div_n(n,
DIV_N_TO_CLK) * 2;
- N = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
+ n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
CLK_TO_DIV_N);
} else {
- N = (N + 2) * 2 - 2;
+ n = (n + 2) * 2 - 2;
}
div++;
}
- dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div);
+ dev_dbg(&(pcr->pci->dev), "n = %d, div = %d\n", n, div);
ssc_depth = depth[ssc_depth];
if (double_clk)
@@ -671,7 +666,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2,
SSC_DEPTH_MASK, ssc_depth);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
if (vpclk) {
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
@@ -713,6 +708,25 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card)
}
EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off);
+int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card)
+{
+ unsigned int cd_mask[] = {
+ [RTSX_SD_CARD] = SD_EXIST,
+ [RTSX_MS_CARD] = MS_EXIST
+ };
+
+ if (!pcr->ms_pmos) {
+ /* When using single PMOS, accessing card is not permitted
+ * if the existing card is not the designated one.
+ */
+ if (pcr->card_exist & (~cd_mask[card]))
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check);
+
int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
{
if (pcr->ops->switch_output_voltage)
@@ -758,7 +772,7 @@ static void rtsx_pci_card_detect(struct work_struct *work)
struct delayed_work *dwork;
struct rtsx_pcr *pcr;
unsigned long flags;
- unsigned int card_detect = 0;
+ unsigned int card_detect = 0, card_inserted, card_removed;
u32 irq_status;
dwork = to_delayed_work(work);
@@ -766,25 +780,35 @@ static void rtsx_pci_card_detect(struct work_struct *work)
dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__);
+ mutex_lock(&pcr->pcr_mutex);
spin_lock_irqsave(&pcr->lock, flags);
irq_status = rtsx_pci_readl(pcr, RTSX_BIPR);
dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status);
- if (pcr->card_inserted || pcr->card_removed) {
+ irq_status &= CARD_EXIST;
+ card_inserted = pcr->card_inserted & irq_status;
+ card_removed = pcr->card_removed;
+ pcr->card_inserted = 0;
+ pcr->card_removed = 0;
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ if (card_inserted || card_removed) {
dev_dbg(&(pcr->pci->dev),
"card_inserted: 0x%x, card_removed: 0x%x\n",
- pcr->card_inserted, pcr->card_removed);
+ card_inserted, card_removed);
if (pcr->ops->cd_deglitch)
- pcr->card_inserted = pcr->ops->cd_deglitch(pcr);
+ card_inserted = pcr->ops->cd_deglitch(pcr);
+
+ card_detect = card_inserted | card_removed;
- card_detect = pcr->card_inserted | pcr->card_removed;
- pcr->card_inserted = 0;
- pcr->card_removed = 0;
+ pcr->card_exist |= card_inserted;
+ pcr->card_exist &= ~card_removed;
}
- spin_unlock_irqrestore(&pcr->lock, flags);
+ mutex_unlock(&pcr->pcr_mutex);
if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event)
pcr->slots[RTSX_SD_CARD].card_event(
@@ -836,10 +860,6 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
}
- if (pcr->card_inserted || pcr->card_removed)
- schedule_delayed_work(&pcr->carddet_work,
- msecs_to_jiffies(200));
-
if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
pcr->trans_result = TRANS_RESULT_FAIL;
@@ -852,6 +872,10 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
}
+ if (pcr->card_inserted || pcr->card_removed)
+ schedule_delayed_work(&pcr->carddet_work,
+ msecs_to_jiffies(200));
+
spin_unlock(&pcr->lock);
return IRQ_HANDLED;
}
@@ -974,6 +998,14 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
return err;
}
+ /* No CD interrupt if probing driver with card inserted.
+ * So we need to initialize pcr->card_exist here.
+ */
+ if (pcr->ops->cd_deglitch)
+ pcr->card_exist = pcr->ops->cd_deglitch(pcr);
+ else
+ pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST;
+
return 0;
}
@@ -997,6 +1029,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)
case 0x5289:
rtl8411_init_params(pcr);
break;
+
+ case 0x5227:
+ rts5227_init_params(pcr);
+ break;
}
dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n",
@@ -1030,6 +1066,10 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device,
(int)pcidev->revision);
+ ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
+ if (ret < 0)
+ return ret;
+
ret = pci_enable_device(pcidev);
if (ret)
return ret;
diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h
index 12462c1df1a9..2b3ab8a04823 100644
--- a/drivers/mfd/rtsx_pcr.h
+++ b/drivers/mfd/rtsx_pcr.h
@@ -25,8 +25,12 @@
#include <linux/mfd/rtsx_pci.h>
+#define MIN_DIV_N_PCR 80
+#define MAX_DIV_N_PCR 208
+
void rts5209_init_params(struct rtsx_pcr *pcr);
void rts5229_init_params(struct rtsx_pcr *pcr);
void rtl8411_init_params(struct rtsx_pcr *pcr);
+void rts5227_init_params(struct rtsx_pcr *pcr);
#endif
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 3f10591ea94e..61aea6381cdf 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -20,6 +20,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
static struct platform_driver syscon_driver;
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index 409afa23d5dc..5ad4b772b097 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6507x.h>
@@ -116,11 +117,19 @@ static const struct i2c_device_id tps6507x_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
+#ifdef CONFIG_OF
+static struct of_device_id tps6507x_of_match[] = {
+ {.compatible = "ti,tps6507x", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tps6507x_of_match);
+#endif
static struct i2c_driver tps6507x_i2c_driver = {
.driver = {
.name = "tps6507x",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tps6507x_of_match),
},
.probe = tps6507x_i2c_probe,
.remove = tps6507x_i2c_remove,
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index 8d12a8e00d9c..98edb5be85c6 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -25,6 +25,8 @@
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65090.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/err.h>
#define NUM_INT_REG 2
@@ -148,18 +150,31 @@ static const struct regmap_config tps65090_regmap_config = {
.volatile_reg = is_volatile_reg,
};
+#ifdef CONFIG_OF
+static const struct of_device_id tps65090_of_match[] = {
+ { .compatible = "ti,tps65090",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, tps65090_of_match);
+#endif
+
static int tps65090_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tps65090_platform_data *pdata = client->dev.platform_data;
+ int irq_base = 0;
struct tps65090 *tps65090;
int ret;
- if (!pdata) {
- dev_err(&client->dev, "tps65090 requires platform data\n");
+ if (!pdata && !client->dev.of_node) {
+ dev_err(&client->dev,
+ "tps65090 requires platform data or of_node\n");
return -EINVAL;
}
+ if (pdata)
+ irq_base = pdata->irq_base;
+
tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL);
if (!tps65090) {
dev_err(&client->dev, "mem alloc for tps65090 failed\n");
@@ -178,7 +193,7 @@ static int tps65090_i2c_probe(struct i2c_client *client,
if (client->irq) {
ret = regmap_add_irq_chip(tps65090->rmap, client->irq,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW, pdata->irq_base,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base,
&tps65090_irq_chip, &tps65090->irq_data);
if (ret) {
dev_err(&client->dev,
@@ -189,7 +204,7 @@ static int tps65090_i2c_probe(struct i2c_client *client,
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
ARRAY_SIZE(tps65090s), NULL,
- regmap_irq_chip_get_base(tps65090->irq_data), NULL);
+ 0, regmap_irq_get_domain(tps65090->irq_data));
if (ret) {
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
ret);
@@ -215,28 +230,6 @@ static int tps65090_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int tps65090_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- if (client->irq)
- disable_irq(client->irq);
- return 0;
-}
-
-static int tps65090_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- if (client->irq)
- enable_irq(client->irq);
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops tps65090_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume)
-};
-
static const struct i2c_device_id tps65090_id_table[] = {
{ "tps65090", 0 },
{ },
@@ -247,7 +240,7 @@ static struct i2c_driver tps65090_driver = {
.driver = {
.name = "tps65090",
.owner = THIS_MODULE,
- .pm = &tps65090_pm_ops,
+ .of_match_table = of_match_ptr(tps65090_of_match),
},
.probe = tps65090_i2c_probe,
.remove = tps65090_i2c_remove,
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 4f3baadd0038..89ab4d970643 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -66,16 +66,6 @@
/* Triton Core internal information (BEGIN) */
-#define TWL_NUM_SLAVES 4
-
-#define SUB_CHIP_ID0 0
-#define SUB_CHIP_ID1 1
-#define SUB_CHIP_ID2 2
-#define SUB_CHIP_ID3 3
-#define SUB_CHIP_ID_INVAL 0xff
-
-#define TWL_MODULE_LAST TWL4030_MODULE_LAST
-
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
@@ -94,10 +84,7 @@
#define TWL4030_BASEADD_MADC 0x0000
#define TWL4030_BASEADD_MAIN_CHARGE 0x0074
#define TWL4030_BASEADD_PRECHARGE 0x00AA
-#define TWL4030_BASEADD_PWM0 0x00F8
-#define TWL4030_BASEADD_PWM1 0x00FB
-#define TWL4030_BASEADD_PWMA 0x00EF
-#define TWL4030_BASEADD_PWMB 0x00F1
+#define TWL4030_BASEADD_PWM 0x00F8
#define TWL4030_BASEADD_KEYPAD 0x00D2
#define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */
@@ -117,7 +104,7 @@
/* subchip/slave 0 0x48 - POWER */
#define TWL6030_BASEADD_RTC 0x0000
-#define TWL6030_BASEADD_MEM 0x0017
+#define TWL6030_BASEADD_SECURED_REG 0x0017
#define TWL6030_BASEADD_PM_MASTER 0x001F
#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
#define TWL6030_BASEADD_PM_MISC 0x00E2
@@ -132,6 +119,7 @@
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0
#define TWL6025_BASEADD_CHARGER 0x00DA
+#define TWL6030_BASEADD_LED 0x00F4
/* subchip/slave 2 0x4A - DFT */
#define TWL6030_BASEADD_DIEID 0x00C0
@@ -153,33 +141,28 @@
/*----------------------------------------------------------------------*/
-/* is driver active, bound to a chip? */
-static bool inuse;
-
-/* TWL IDCODE Register value */
-static u32 twl_idcode;
-
-static unsigned int twl_id;
-unsigned int twl_rev(void)
-{
- return twl_id;
-}
-EXPORT_SYMBOL(twl_rev);
-
/* Structure for each TWL4030/TWL6030 Slave */
struct twl_client {
struct i2c_client *client;
struct regmap *regmap;
};
-static struct twl_client twl_modules[TWL_NUM_SLAVES];
-
/* mapping the module id to slave id and base address */
struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
-static struct twl_mapping *twl_map;
+
+struct twl_private {
+ bool ready; /* The core driver is ready to be used */
+ u32 twl_idcode; /* TWL IDCODE Register value */
+ unsigned int twl_id;
+
+ struct twl_mapping *twl_map;
+ struct twl_client *twl_modules;
+};
+
+static struct twl_private *twl_priv;
static struct twl_mapping twl4030_map[] = {
/*
@@ -188,34 +171,33 @@ static struct twl_mapping twl4030_map[] = {
* so they continue to match the order in this table.
*/
+ /* Common IPs */
{ 0, TWL4030_BASEADD_USB },
+ { 1, TWL4030_BASEADD_PIH },
+ { 2, TWL4030_BASEADD_MAIN_CHARGE },
+ { 3, TWL4030_BASEADD_PM_MASTER },
+ { 3, TWL4030_BASEADD_PM_RECEIVER },
+
+ { 3, TWL4030_BASEADD_RTC },
+ { 2, TWL4030_BASEADD_PWM },
+ { 2, TWL4030_BASEADD_LED },
+ { 3, TWL4030_BASEADD_SECURED_REG },
+
+ /* TWL4030 specific IPs */
{ 1, TWL4030_BASEADD_AUDIO_VOICE },
{ 1, TWL4030_BASEADD_GPIO },
{ 1, TWL4030_BASEADD_INTBR },
- { 1, TWL4030_BASEADD_PIH },
-
{ 1, TWL4030_BASEADD_TEST },
{ 2, TWL4030_BASEADD_KEYPAD },
+
{ 2, TWL4030_BASEADD_MADC },
{ 2, TWL4030_BASEADD_INTERRUPTS },
- { 2, TWL4030_BASEADD_LED },
-
- { 2, TWL4030_BASEADD_MAIN_CHARGE },
{ 2, TWL4030_BASEADD_PRECHARGE },
- { 2, TWL4030_BASEADD_PWM0 },
- { 2, TWL4030_BASEADD_PWM1 },
- { 2, TWL4030_BASEADD_PWMA },
-
- { 2, TWL4030_BASEADD_PWMB },
- { 2, TWL5031_BASEADD_ACCESSORY },
- { 2, TWL5031_BASEADD_INTERRUPTS },
{ 3, TWL4030_BASEADD_BACKUP },
{ 3, TWL4030_BASEADD_INT },
- { 3, TWL4030_BASEADD_PM_MASTER },
- { 3, TWL4030_BASEADD_PM_RECEIVER },
- { 3, TWL4030_BASEADD_RTC },
- { 3, TWL4030_BASEADD_SECURED_REG },
+ { 2, TWL5031_BASEADD_ACCESSORY },
+ { 2, TWL5031_BASEADD_INTERRUPTS },
};
static struct regmap_config twl4030_regmap_config[4] = {
@@ -251,35 +233,25 @@ static struct twl_mapping twl6030_map[] = {
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
- { SUB_CHIP_ID1, TWL6030_BASEADD_USB },
- { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
- { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
-
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-
- { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
- { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
- { SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
- { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
- { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
-
- { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
- { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-
- { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
- { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
- { SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
- { SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
- { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
+
+ /* Common IPs */
+ { 1, TWL6030_BASEADD_USB },
+ { 1, TWL6030_BASEADD_PIH },
+ { 1, TWL6030_BASEADD_CHARGER },
+ { 0, TWL6030_BASEADD_PM_MASTER },
+ { 0, TWL6030_BASEADD_PM_SLAVE_MISC },
+
+ { 0, TWL6030_BASEADD_RTC },
+ { 1, TWL6030_BASEADD_PWM },
+ { 1, TWL6030_BASEADD_LED },
+ { 0, TWL6030_BASEADD_SECURED_REG },
+
+ /* TWL6030 specific IPs */
+ { 0, TWL6030_BASEADD_ZERO },
+ { 1, TWL6030_BASEADD_ZERO },
+ { 2, TWL6030_BASEADD_ZERO },
+ { 1, TWL6030_BASEADD_GPADC_CTRL },
+ { 1, TWL6030_BASEADD_GASGAUGE },
};
static struct regmap_config twl6030_regmap_config[3] = {
@@ -305,8 +277,30 @@ static struct regmap_config twl6030_regmap_config[3] = {
/*----------------------------------------------------------------------*/
+static inline int twl_get_num_slaves(void)
+{
+ if (twl_class_is_4030())
+ return 4; /* TWL4030 class have four slave address */
+ else
+ return 3; /* TWL6030 class have three slave address */
+}
+
+static inline int twl_get_last_module(void)
+{
+ if (twl_class_is_4030())
+ return TWL4030_MODULE_LAST;
+ else
+ return TWL6030_MODULE_LAST;
+}
+
/* Exported Functions */
+unsigned int twl_rev(void)
+{
+ return twl_priv ? twl_priv->twl_id : 0;
+}
+EXPORT_SYMBOL(twl_rev);
+
/**
* twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
@@ -314,9 +308,6 @@ static struct regmap_config twl6030_regmap_config[3] = {
* @reg: register address (just offset will do)
* @num_bytes: number of bytes to transfer
*
- * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and
- * valid data starts at Offset 1.
- *
* Returns the result of operation - 0 is success
*/
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
@@ -325,24 +316,21 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
int sid;
struct twl_client *twl;
- if (unlikely(mod_no >= TWL_MODULE_LAST)) {
- pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
- return -EPERM;
- }
- if (unlikely(!inuse)) {
+ if (unlikely(!twl_priv || !twl_priv->ready)) {
pr_err("%s: not initialized\n", DRIVER_NAME);
return -EPERM;
}
- sid = twl_map[mod_no].sid;
- if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
- pr_err("%s: module %d is not part of the pmic\n",
- DRIVER_NAME, mod_no);
- return -EINVAL;
+ if (unlikely(mod_no >= twl_get_last_module())) {
+ pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+ return -EPERM;
}
- twl = &twl_modules[sid];
- ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg,
- value, num_bytes);
+ sid = twl_priv->twl_map[mod_no].sid;
+ twl = &twl_priv->twl_modules[sid];
+
+ ret = regmap_bulk_write(twl->regmap,
+ twl_priv->twl_map[mod_no].base + reg, value,
+ num_bytes);
if (ret)
pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n",
@@ -367,24 +355,21 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
int sid;
struct twl_client *twl;
- if (unlikely(mod_no >= TWL_MODULE_LAST)) {
- pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
- return -EPERM;
- }
- if (unlikely(!inuse)) {
+ if (unlikely(!twl_priv || !twl_priv->ready)) {
pr_err("%s: not initialized\n", DRIVER_NAME);
return -EPERM;
}
- sid = twl_map[mod_no].sid;
- if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
- pr_err("%s: module %d is not part of the pmic\n",
- DRIVER_NAME, mod_no);
- return -EINVAL;
+ if (unlikely(mod_no >= twl_get_last_module())) {
+ pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+ return -EPERM;
}
- twl = &twl_modules[sid];
- ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg,
- value, num_bytes);
+ sid = twl_priv->twl_map[mod_no].sid;
+ twl = &twl_priv->twl_modules[sid];
+
+ ret = regmap_bulk_read(twl->regmap,
+ twl_priv->twl_map[mod_no].base + reg, value,
+ num_bytes);
if (ret)
pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n",
@@ -394,34 +379,6 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
}
EXPORT_SYMBOL(twl_i2c_read);
-/**
- * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
- * @mod_no: module number
- * @value: the value to be written 8 bit
- * @reg: register address (just offset will do)
- *
- * Returns result of operation - 0 is success
- */
-int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
-{
- return twl_i2c_write(mod_no, &value, reg, 1);
-}
-EXPORT_SYMBOL(twl_i2c_write_u8);
-
-/**
- * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
- * @mod_no: module number
- * @value: the value read 8 bit
- * @reg: register address (just offset will do)
- *
- * Returns result of operation - 0 is success
- */
-int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
-{
- return twl_i2c_read(mod_no, value, reg, 1);
-}
-EXPORT_SYMBOL(twl_i2c_read_u8);
-
/*----------------------------------------------------------------------*/
/**
@@ -440,7 +397,7 @@ static int twl_read_idcode_register(void)
goto fail;
}
- err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode),
+ err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode),
REG_IDCODE_7_0, 4);
if (err) {
pr_err("TWL4030: unable to read IDCODE -%d\n", err);
@@ -461,7 +418,7 @@ fail:
*/
int twl_get_type(void)
{
- return TWL_SIL_TYPE(twl_idcode);
+ return TWL_SIL_TYPE(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_type);
@@ -472,7 +429,7 @@ EXPORT_SYMBOL_GPL(twl_get_type);
*/
int twl_get_version(void)
{
- return TWL_SIL_REV(twl_idcode);
+ return TWL_SIL_REV(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_version);
@@ -509,13 +466,20 @@ int twl_get_hfclk_rate(void)
EXPORT_SYMBOL_GPL(twl_get_hfclk_rate);
static struct device *
-add_numbered_child(unsigned chip, const char *name, int num,
+add_numbered_child(unsigned mod_no, const char *name, int num,
void *pdata, unsigned pdata_len,
bool can_wakeup, int irq0, int irq1)
{
struct platform_device *pdev;
- struct twl_client *twl = &twl_modules[chip];
- int status;
+ struct twl_client *twl;
+ int status, sid;
+
+ if (unlikely(mod_no >= twl_get_last_module())) {
+ pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+ return ERR_PTR(-EPERM);
+ }
+ sid = twl_priv->twl_map[mod_no].sid;
+ twl = &twl_priv->twl_modules[sid];
pdev = platform_device_alloc(name, num);
if (!pdev) {
@@ -560,11 +524,11 @@ err:
return &pdev->dev;
}
-static inline struct device *add_child(unsigned chip, const char *name,
+static inline struct device *add_child(unsigned mod_no, const char *name,
void *pdata, unsigned pdata_len,
bool can_wakeup, int irq0, int irq1)
{
- return add_numbered_child(chip, name, -1, pdata, pdata_len,
+ return add_numbered_child(mod_no, name, -1, pdata, pdata_len,
can_wakeup, irq0, irq1);
}
@@ -573,7 +537,6 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
unsigned num_consumers, unsigned long features)
{
- unsigned sub_chip_id;
struct twl_regulator_driver_data drv_data;
/* regulator framework demands init_data ... */
@@ -600,8 +563,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
}
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
- sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
- return add_numbered_child(sub_chip_id, "twl_reg", num,
+ return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num,
pdata, sizeof(*pdata), false, 0, 0);
}
@@ -623,10 +585,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
unsigned long features)
{
struct device *child;
- unsigned sub_chip_id;
if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) {
- child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
+ child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
false, irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
@@ -634,7 +595,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) {
- child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
+ child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
true, irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
@@ -643,7 +604,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc &&
twl_class_is_4030()) {
- child = add_child(SUB_CHIP_ID2, "twl4030_madc",
+ child = add_child(TWL4030_MODULE_MADC, "twl4030_madc",
pdata->madc, sizeof(*pdata->madc),
true, irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
@@ -658,22 +619,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
- sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
- child = add_child(sub_chip_id, "twl_rtc", NULL, 0,
+ child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0,
true, irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_PWM_TWL)) {
- child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0,
+ child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0,
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_PWM_TWL_LED)) {
- child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0,
+ child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0,
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -725,7 +685,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
- child = add_child(SUB_CHIP_ID0, "twl4030_usb",
+ child = add_child(TWL_MODULE_USB, "twl4030_usb",
pdata->usb, sizeof(*pdata->usb), true,
/* irq0 = USB_PRES, irq1 = USB */
irq_base + USB_PRES_INTR_OFFSET,
@@ -774,7 +734,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
pdata->usb->features = features;
- child = add_child(SUB_CHIP_ID0, "twl6030_usb",
+ child = add_child(TWL_MODULE_USB, "twl6030_usb",
pdata->usb, sizeof(*pdata->usb), true,
/* irq1 = VBUS_PRES, irq0 = USB ID */
irq_base + USBOTG_INTR_OFFSET,
@@ -799,22 +759,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {
- child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0,
- false, 0, 0);
+ child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL,
+ 0, false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) {
- child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0,
- true, irq_base + 8 + 0, 0);
+ child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton",
+ NULL, 0, true, irq_base + 8 + 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio &&
twl_class_is_4030()) {
- child = add_child(SUB_CHIP_ID1, "twl4030-audio",
+ child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio",
pdata->audio, sizeof(*pdata->audio),
false, 0, 0);
if (IS_ERR(child))
@@ -1054,7 +1014,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
- child = add_child(SUB_CHIP_ID3, "twl4030_bci",
+ child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
irq_base + BCI_PRES_INTR_OFFSET,
@@ -1145,25 +1105,23 @@ static int twl_remove(struct i2c_client *client)
unsigned i, num_slaves;
int status;
- if (twl_class_is_4030()) {
+ if (twl_class_is_4030())
status = twl4030_exit_irq();
- num_slaves = TWL_NUM_SLAVES;
- } else {
+ else
status = twl6030_exit_irq();
- num_slaves = TWL_NUM_SLAVES - 1;
- }
if (status < 0)
return status;
+ num_slaves = twl_get_num_slaves();
for (i = 0; i < num_slaves; i++) {
- struct twl_client *twl = &twl_modules[i];
+ struct twl_client *twl = &twl_priv->twl_modules[i];
if (twl->client && twl->client != client)
i2c_unregister_device(twl->client);
- twl_modules[i].client = NULL;
+ twl->client = NULL;
}
- inuse = false;
+ twl_priv->ready = false;
return 0;
}
@@ -1179,6 +1137,17 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
int status;
unsigned i, num_slaves;
+ if (!node && !pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (twl_priv) {
+ dev_dbg(&client->dev, "only one instance of %s allowed\n",
+ DRIVER_NAME);
+ return -EBUSY;
+ }
+
pdev = platform_device_alloc(DRIVER_NAME, -1);
if (!pdev) {
dev_err(&client->dev, "can't alloc pdev\n");
@@ -1191,54 +1160,44 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
return status;
}
- if (node && !pdata) {
- /*
- * XXX: Temporary pdata until the information is correctly
- * retrieved by every TWL modules from DT.
- */
- pdata = devm_kzalloc(&client->dev,
- sizeof(struct twl4030_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- status = -ENOMEM;
- goto free;
- }
- }
-
- if (!pdata) {
- dev_dbg(&client->dev, "no platform data?\n");
- status = -EINVAL;
- goto free;
- }
-
- platform_set_drvdata(pdev, pdata);
-
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
status = -EIO;
goto free;
}
- if (inuse) {
- dev_dbg(&client->dev, "driver is already in use\n");
- status = -EBUSY;
+ twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private),
+ GFP_KERNEL);
+ if (!twl_priv) {
+ status = -ENOMEM;
goto free;
}
if ((id->driver_data) & TWL6030_CLASS) {
- twl_id = TWL6030_CLASS_ID;
- twl_map = &twl6030_map[0];
+ twl_priv->twl_id = TWL6030_CLASS_ID;
+ twl_priv->twl_map = &twl6030_map[0];
+ /* The charger base address is different in twl6025 */
+ if ((id->driver_data) & TWL6025_SUBCLASS)
+ twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base =
+ TWL6025_BASEADD_CHARGER;
twl_regmap_config = twl6030_regmap_config;
- num_slaves = TWL_NUM_SLAVES - 1;
} else {
- twl_id = TWL4030_CLASS_ID;
- twl_map = &twl4030_map[0];
+ twl_priv->twl_id = TWL4030_CLASS_ID;
+ twl_priv->twl_map = &twl4030_map[0];
twl_regmap_config = twl4030_regmap_config;
- num_slaves = TWL_NUM_SLAVES;
+ }
+
+ num_slaves = twl_get_num_slaves();
+ twl_priv->twl_modules = devm_kzalloc(&client->dev,
+ sizeof(struct twl_client) * num_slaves,
+ GFP_KERNEL);
+ if (!twl_priv->twl_modules) {
+ status = -ENOMEM;
+ goto free;
}
for (i = 0; i < num_slaves; i++) {
- struct twl_client *twl = &twl_modules[i];
+ struct twl_client *twl = &twl_priv->twl_modules[i];
if (i == 0) {
twl->client = client;
@@ -1264,19 +1223,19 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
}
- inuse = true;
+ twl_priv->ready = true;
/* setup clock framework */
- clocks_init(&pdev->dev, pdata->clock);
+ clocks_init(&pdev->dev, pdata ? pdata->clock : NULL);
/* read TWL IDCODE Register */
- if (twl_id == TWL4030_CLASS_ID) {
+ if (twl_class_is_4030()) {
status = twl_read_idcode_register();
WARN(status < 0, "Error: reading twl_idcode register value\n");
}
/* load power event scripts */
- if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power)
+ if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata && pdata->power)
twl4030_power_init(pdata->power);
/* Maybe init the T2 Interrupt subsystem */
@@ -1308,10 +1267,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
- status = -ENODEV;
if (node)
status = of_platform_populate(node, NULL, NULL, &client->dev);
- if (status)
+ else
status = add_children(pdata, irq_base, id->driver_data);
fail:
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 558c2928f261..bf75e967a1f3 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -49,6 +49,8 @@
#define SYS_ID_HBI_SHIFT 16
#define SYS_PROCIDx_HBI_SHIFT 0
+#define SYS_LED_LED(n) (1 << (n))
+
#define SYS_MCI_CARDIN (1 << 0)
#define SYS_MCI_WPROT (1 << 1)
@@ -336,34 +338,40 @@ void __init vexpress_sysreg_early_init(void __iomem *base)
void __init vexpress_sysreg_of_early_init(void)
{
- struct device_node *node = of_find_compatible_node(NULL, NULL,
- "arm,vexpress-sysreg");
+ struct device_node *node;
+
+ if (vexpress_sysreg_base)
+ return;
+ node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg");
if (node) {
vexpress_sysreg_base = of_iomap(node, 0);
vexpress_sysreg_setup(node);
- } else {
- pr_info("vexpress-sysreg: No Device Tree node found.");
}
}
+#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \
+ [VEXPRESS_GPIO_##_name] = { \
+ .reg = _reg, \
+ .value = _reg##_##_value, \
+ }
+
static struct vexpress_sysreg_gpio {
unsigned long reg;
u32 value;
} vexpress_sysreg_gpios[] = {
- [VEXPRESS_GPIO_MMC_CARDIN] = {
- .reg = SYS_MCI,
- .value = SYS_MCI_CARDIN,
- },
- [VEXPRESS_GPIO_MMC_WPROT] = {
- .reg = SYS_MCI,
- .value = SYS_MCI_WPROT,
- },
- [VEXPRESS_GPIO_FLASH_WPn] = {
- .reg = SYS_FLASH,
- .value = SYS_FLASH_WPn,
- },
+ VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN),
+ VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT),
+ VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn),
+ VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)),
+ VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)),
+ VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)),
+ VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)),
+ VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)),
+ VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)),
+ VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)),
+ VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)),
};
static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip,
@@ -372,12 +380,6 @@ static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip,
return 0;
}
-static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- return 0;
-}
-
static int vexpress_sysreg_gpio_get(struct gpio_chip *chip,
unsigned offset)
{
@@ -401,6 +403,14 @@ static void vexpress_sysreg_gpio_set(struct gpio_chip *chip,
writel(reg_value, vexpress_sysreg_base + gpio->reg);
}
+static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ vexpress_sysreg_gpio_set(chip, offset, value);
+
+ return 0;
+}
+
static struct gpio_chip vexpress_sysreg_gpio_chip = {
.label = "vexpress-sysreg",
.direction_input = vexpress_sysreg_gpio_direction_input,
@@ -412,6 +422,30 @@ static struct gpio_chip vexpress_sysreg_gpio_chip = {
};
+#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \
+ { \
+ .name = "v2m:green:"_name, \
+ .default_trigger = _default_trigger, \
+ .gpio = VEXPRESS_GPIO_##_gpio, \
+ }
+
+struct gpio_led vexpress_sysreg_leds[] = {
+ VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0),
+ VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1),
+ VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2),
+ VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3),
+ VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4),
+ VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5),
+ VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6),
+ VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7),
+};
+
+struct gpio_led_platform_data vexpress_sysreg_leds_pdata = {
+ .num_leds = ARRAY_SIZE(vexpress_sysreg_leds),
+ .leds = vexpress_sysreg_leds,
+};
+
+
static ssize_t vexpress_sysreg_sys_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -456,6 +490,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
return err;
}
+ platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",
+ PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,
+ sizeof(vexpress_sysreg_leds_pdata));
+
vexpress_sysreg_dev = &pdev->dev;
device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
@@ -478,6 +516,7 @@ static struct platform_driver vexpress_sysreg_driver = {
static int __init vexpress_sysreg_init(void)
{
+ vexpress_sysreg_of_early_init();
return platform_driver_register(&vexpress_sysreg_driver);
}
core_initcall(vexpress_sysreg_init);
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
index a9d9d41d95d3..a433f580aa4c 100644
--- a/drivers/mfd/wm5102-tables.c
+++ b/drivers/mfd/wm5102-tables.c
@@ -59,12 +59,13 @@ static const struct reg_default wm5102_reva_patch[] = {
static const struct reg_default wm5102_revb_patch[] = {
{ 0x80, 0x0003 },
{ 0x081, 0xE022 },
- { 0x410, 0x6080 },
- { 0x418, 0x6080 },
- { 0x420, 0x6080 },
+ { 0x410, 0x4080 },
+ { 0x418, 0x4080 },
+ { 0x420, 0x4080 },
{ 0x428, 0xC000 },
- { 0x441, 0x8014 },
+ { 0x4B0, 0x0066 },
{ 0x458, 0x000b },
+ { 0x212, 0x0000 },
{ 0x80, 0x0000 },
};
@@ -231,11 +232,9 @@ const struct regmap_irq_chip wm5102_irq = {
static const struct reg_default wm5102_reg_default[] = {
{ 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */
{ 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */
- { 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */
{ 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */
{ 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */
{ 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */
- { 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */
{ 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
{ 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
{ 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
@@ -250,12 +249,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
- { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
- { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
- { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
- { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
- { 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */
- { 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */
+ { 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */
+ { 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */
+ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
+ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
+ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
+ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
+ { 0x0000006E, 0x01FF }, /* R110 - Trigger Sequence Select 32 */
+ { 0x0000006F, 0x01FF }, /* R111 - Trigger Sequence Select 33 */
{ 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
@@ -265,13 +266,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
{ 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
{ 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
- { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */
+ { 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */
{ 0x00000101, 0x0304 }, /* R257 - System Clock 1 */
{ 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
{ 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
{ 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
{ 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
{ 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
+ { 0x00000114, 0x0011 }, /* R276 - Async sample rate 2 */
{ 0x00000149, 0x0000 }, /* R329 - Output system clock */
{ 0x0000014A, 0x0000 }, /* R330 - Output async clock */
{ 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
@@ -280,13 +282,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
{ 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
{ 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */
- { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
+ { 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */
{ 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
{ 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
{ 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
{ 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */
{ 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
{ 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */
+ { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
{ 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
{ 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
{ 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
@@ -302,6 +305,7 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */
{ 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
{ 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
+ { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
{ 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
{ 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
{ 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
@@ -317,8 +321,12 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
+ { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */
+ { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
{ 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
+ { 0x0000029F, 0x0000 }, /* R671 - Headphone Detect Test */
{ 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
@@ -350,53 +358,44 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
{ 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
{ 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
- { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
+ { 0x00000410, 0x4080 }, /* R1040 - Output Path Config 1L */
{ 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
- { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */
{ 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
{ 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */
{ 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
- { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */
+ { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */
{ 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
- { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */
+ { 0x00000418, 0x4080 }, /* R1048 - Output Path Config 2L */
{ 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */
- { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */
+ { 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */
{ 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */
{ 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */
{ 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */
- { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */
+ { 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */
{ 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */
- { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */
+ { 0x00000420, 0x4080 }, /* R1056 - Output Path Config 3L */
{ 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */
- { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */
+ { 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */
{ 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */
- { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */
- { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */
- { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */
- { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */
+ { 0x00000428, 0xC000 }, /* R1064 - Output Path Config 4L */
{ 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
- { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */
+ { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */
{ 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
- { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */
{ 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */
- { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */
+ { 0x0000042E, 0x0081 }, /* R1070 - Out Volume 4R */
{ 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */
{ 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */
{ 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */
- { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */
+ { 0x00000432, 0x0081 }, /* R1074 - DAC Volume Limit 5L */
{ 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */
- { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */
{ 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
- { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */
- { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
+ { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */
+ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
{ 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
{ 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */
{ 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
{ 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
- { 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */
- { 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */
- { 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */
- { 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */
{ 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
{ 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
@@ -424,7 +423,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
{ 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
{ 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
- { 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */
{ 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
{ 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
{ 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
@@ -440,7 +438,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
{ 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
{ 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
- { 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */
{ 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
{ 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */
{ 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */
@@ -456,7 +453,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */
{ 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */
{ 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */
- { 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */
{ 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */
{ 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */
{ 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */
@@ -780,22 +776,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
{ 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
{ 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
- { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */
- { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */
- { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */
- { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */
- { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */
- { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */
- { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */
- { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */
- { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */
- { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */
- { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */
- { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */
- { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */
- { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */
- { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */
- { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */
{ 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
{ 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
{ 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
@@ -887,7 +867,7 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
{ 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
{ 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
- { 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */
+ { 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */
{ 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
{ 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
{ 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
@@ -982,11 +962,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
{ 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
{ 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
- { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */
- { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */
- { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */
- { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */
- { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */
{ 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
{ 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
{ 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
@@ -997,16 +972,12 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
{ 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */
{ 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */
- { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */
{ 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
{ 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
{ 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
{ 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
{ 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
{ 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
- { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */
- { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */
- { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
{ 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
{ 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */
};
@@ -1833,17 +1804,24 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_SCRATCH_0:
+ case ARIZONA_DSP1_SCRATCH_1:
+ case ARIZONA_DSP1_SCRATCH_2:
+ case ARIZONA_DSP1_SCRATCH_3:
return true;
default:
- return false;
+ if ((reg >= 0x100000 && reg < 0x106000) ||
+ (reg >= 0x180000 && reg < 0x180800) ||
+ (reg >= 0x190000 && reg < 0x194800) ||
+ (reg >= 0x1a8000 && reg < 0x1a9800))
+ return true;
+ else
+ return false;
}
}
static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
{
- if (reg > 0xffff)
- return true;
-
switch (reg) {
case ARIZONA_SOFTWARE_RESET:
case ARIZONA_DEVICE_REVISION:
@@ -1884,12 +1862,22 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_SCRATCH_0:
+ case ARIZONA_DSP1_SCRATCH_1:
+ case ARIZONA_DSP1_SCRATCH_2:
+ case ARIZONA_DSP1_SCRATCH_3:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_HP_DACVAL:
case ARIZONA_MIC_DETECT_3:
return true;
default:
- return false;
+ if ((reg >= 0x100000 && reg < 0x106000) ||
+ (reg >= 0x180000 && reg < 0x180800) ||
+ (reg >= 0x190000 && reg < 0x194800) ||
+ (reg >= 0x1a8000 && reg < 0x1a9800))
+ return true;
+ else
+ return false;
}
}
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 57c488d42d3e..803e93fae56a 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -467,7 +467,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err;
}
- ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
+ ret = devm_regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
@@ -478,7 +478,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
- goto err_get;
+ goto err;
}
ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
@@ -658,8 +658,6 @@ err_irq:
err_enable:
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
-err_get:
- regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
err:
mfd_remove_devices(wm8994->dev);
return ret;
@@ -672,7 +670,6 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
wm8994_irq_exit(wm8994);
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
- regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
}
static const struct of_device_id wm8994_of_match[] = {
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index f74b5adca642..468c92303167 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -678,12 +678,19 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = mrq->data;
unsigned int data_size = 0;
+ int err;
if (host->eject) {
cmd->error = -ENOMEDIUM;
goto finish;
}
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
+ if (err) {
+ cmd->error = err;
+ goto finish;
+ }
+
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
@@ -901,6 +908,9 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->eject)
return;
+ if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD))
+ return;
+
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
@@ -1073,6 +1083,10 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->eject)
return -ENOMEDIUM;
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
+ if (err)
+ return err;
+
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
@@ -1122,6 +1136,10 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->eject)
return -ENOMEDIUM;
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
+ if (err)
+ return err;
+
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e6ab071fb6fd..79fbe3832dfc 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -305,6 +305,16 @@ config RTC_DRV_X1205
This driver can also be built as a module. If so, the module
will be called rtc-x1205.
+config RTC_DRV_PALMAS
+ tristate "TI Palmas RTC driver"
+ depends on MFD_PALMAS
+ help
+ If you say yes here you get support for the RTC of TI PALMA series PMIC
+ chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-palma.
+
config RTC_DRV_PCF8523
tristate "NXP PCF8523"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e8f2e2fee06f..c33f86f1a69b 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
+obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
new file mode 100644
index 000000000000..59c42986254e
--- /dev/null
+++ b/drivers/rtc/rtc-palmas.c
@@ -0,0 +1,339 @@
+/*
+ * rtc-palmas.c -- Palmas Real Time Clock driver.
+
+ * RTC driver for TI Palma series devices like TPS65913,
+ * TPS65914 power management IC.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/bcd.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/palmas.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+
+struct palmas_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ unsigned int irq;
+};
+
+/* Total number of RTC registers needed to set time*/
+#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1)
+
+static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ /* Copy RTC counting registers to static registers or latches */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME);
+ if (ret < 0) {
+ dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
+ rtc_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_mday = bcd2bin(rtc_data[3]);
+ tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ return ret;
+}
+
+static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_mday);
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[5] = bin2bcd(tm->tm_year - 100);
+
+ /* Stop RTC while updating the RTC time registers */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_STOP_RTC, 0);
+ if (ret < 0) {
+ dev_err(dev, "RTC stop failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
+ rtc_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret);
+ return ret;
+ }
+
+ /* Start back RTC */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC);
+ if (ret < 0)
+ dev_err(dev, "RTC start failed, err = %d\n", ret);
+ return ret;
+}
+
+static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ u8 val;
+
+ val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0;
+ return palmas_write(palmas, PALMAS_RTC_BASE,
+ PALMAS_RTC_INTERRUPTS_REG, val);
+}
+
+static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
+ u32 int_val;
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE,
+ PALMAS_ALARM_SECONDS_REG,
+ alarm_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ alm->time.tm_sec = bcd2bin(alarm_data[0]);
+ alm->time.tm_min = bcd2bin(alarm_data[1]);
+ alm->time.tm_hour = bcd2bin(alarm_data[2]);
+ alm->time.tm_mday = bcd2bin(alarm_data[3]);
+ alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
+ alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
+
+ ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG,
+ &int_val);
+ if (ret < 0) {
+ dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM)
+ alm->enabled = 1;
+ return ret;
+}
+
+static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = palmas_rtc_alarm_irq_enable(dev, 0);
+ if (ret < 0) {
+ dev_err(dev, "Disable RTC alarm failed\n");
+ return ret;
+ }
+
+ alarm_data[0] = bin2bcd(alm->time.tm_sec);
+ alarm_data[1] = bin2bcd(alm->time.tm_min);
+ alarm_data[2] = bin2bcd(alm->time.tm_hour);
+ alarm_data[3] = bin2bcd(alm->time.tm_mday);
+ alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
+ alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
+
+ ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE,
+ PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret);
+ return ret;
+ }
+
+ if (alm->enabled)
+ ret = palmas_rtc_alarm_irq_enable(dev, 1);
+ return ret;
+}
+
+static int palmas_clear_interrupts(struct device *dev)
+{
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ unsigned int rtc_reg;
+ int ret;
+
+ ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
+ &rtc_reg);
+ if (ret < 0) {
+ dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
+ rtc_reg);
+ if (ret < 0) {
+ dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static irqreturn_t palmas_rtc_interrupt(int irq, void *context)
+{
+ struct palmas_rtc *palmas_rtc = context;
+ struct device *dev = palmas_rtc->dev;
+ int ret;
+
+ ret = palmas_clear_interrupts(dev);
+ if (ret < 0) {
+ dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static struct rtc_class_ops palmas_rtc_ops = {
+ .read_time = palmas_rtc_read_time,
+ .set_time = palmas_rtc_set_time,
+ .read_alarm = palmas_rtc_read_alarm,
+ .set_alarm = palmas_rtc_set_alarm,
+ .alarm_irq_enable = palmas_rtc_alarm_irq_enable,
+};
+
+static int palmas_rtc_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_rtc *palmas_rtc = NULL;
+ int ret;
+
+ palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc),
+ GFP_KERNEL);
+ if (!palmas_rtc)
+ return -ENOMEM;
+
+ /* Clear pending interrupts */
+ ret = palmas_clear_interrupts(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret);
+ return ret;
+ }
+
+ palmas_rtc->dev = &pdev->dev;
+ platform_set_drvdata(pdev, palmas_rtc);
+
+ /* Start RTC */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_STOP_RTC,
+ PALMAS_RTC_CTRL_REG_STOP_RTC);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret);
+ return ret;
+ }
+
+ palmas_rtc->irq = platform_get_irq(pdev, 0);
+
+ palmas_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &palmas_rtc_ops, THIS_MODULE);
+ if (IS_ERR(palmas_rtc->rtc)) {
+ ret = PTR_ERR(palmas_rtc->rtc);
+ dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = request_threaded_irq(palmas_rtc->irq, NULL,
+ palmas_rtc_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT |
+ IRQF_EARLY_RESUME,
+ dev_name(&pdev->dev), palmas_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret);
+ rtc_device_unregister(palmas_rtc->rtc);
+ return ret;
+ }
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+ return 0;
+}
+
+static int palmas_rtc_remove(struct platform_device *pdev)
+{
+ struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev);
+
+ palmas_rtc_alarm_irq_enable(&pdev->dev, 0);
+ free_irq(palmas_rtc->irq, palmas_rtc);
+ rtc_device_unregister(palmas_rtc->rtc);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int palmas_rtc_suspend(struct device *dev)
+{
+ struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(palmas_rtc->irq);
+ return 0;
+}
+
+static int palmas_rtc_resume(struct device *dev)
+{
+ struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(palmas_rtc->irq);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops palmas_rtc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume)
+};
+
+static struct platform_driver palmas_rtc_driver = {
+ .probe = palmas_rtc_probe,
+ .remove = palmas_rtc_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "palmas-rtc",
+ .pm = &palmas_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(palmas_rtc_driver);
+
+MODULE_ALIAS("platform:palmas_rtc");
+MODULE_DESCRIPTION("TI PALMAS series RTC driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 99899e808c6a..0555ee42d7cb 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -107,7 +107,7 @@ static int omap_ehci_init(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int rc;
- struct ehci_hcd_omap_platform_data *pdata;
+ struct usbhs_omap_platform_data *pdata;
pdata = hcd->self.controller->platform_data;
@@ -151,7 +151,7 @@ static int omap_ehci_init(struct usb_hcd *hcd)
}
static void disable_put_regulator(
- struct ehci_hcd_omap_platform_data *pdata)
+ struct usbhs_omap_platform_data *pdata)
{
int i;
@@ -176,7 +176,7 @@ static void disable_put_regulator(
static int ehci_hcd_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct ehci_hcd_omap_platform_data *pdata = dev->platform_data;
+ struct usbhs_omap_platform_data *pdata = dev->platform_data;
struct resource *res;
struct usb_hcd *hcd;
void __iomem *regs;
diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c
index 2c9bce050aa9..5ca11b066b7e 100644
--- a/drivers/video/backlight/max8925_bl.c
+++ b/drivers/video/backlight/max8925_bl.c
@@ -101,6 +101,29 @@ static const struct backlight_ops max8925_backlight_ops = {
.get_brightness = max8925_backlight_get_brightness,
};
+#ifdef CONFIG_OF
+static int max8925_backlight_dt_init(struct platform_device *pdev,
+ struct max8925_backlight_pdata *pdata)
+{
+ struct device_node *nproot = pdev->dev.parent->of_node, *np;
+ int dual_string;
+
+ if (!nproot)
+ return -ENODEV;
+ np = of_find_node_by_name(nproot, "backlight");
+ if (!np) {
+ dev_err(&pdev->dev, "failed to find backlight node\n");
+ return -ENODEV;
+ }
+
+ of_property_read_u32(np, "maxim,max8925-dual-string", &dual_string);
+ pdata->dual_string = dual_string;
+ return 0;
+}
+#else
+#define max8925_backlight_dt_init(x, y) (-1)
+#endif
+
static int max8925_backlight_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
@@ -147,6 +170,13 @@ static int max8925_backlight_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bl);
value = 0;
+ if (pdev->dev.parent->of_node && !pdata) {
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct max8925_backlight_pdata),
+ GFP_KERNEL);
+ max8925_backlight_dt_init(pdev, pdata);
+ }
+
if (pdata) {
if (pdata->lxw_scl)
value |= (1 << 7);
@@ -158,7 +188,6 @@ static int max8925_backlight_probe(struct platform_device *pdev)
ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value);
if (ret < 0)
goto out_brt;
-
backlight_update_status(bl);
return 0;
out_brt:
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7f809fd4a57f..26e1fdbddf69 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -364,6 +364,18 @@ config IMX2_WDT
To compile this driver as a module, choose M here: the
module will be called imx2_wdt.
+config UX500_WATCHDOG
+ tristate "ST-Ericsson Ux500 watchdog"
+ depends on MFD_DB8500_PRCMU
+ select WATCHDOG_CORE
+ default y
+ help
+ Say Y here to include Watchdog timer support for the watchdog
+ existing in the prcmu of ST-Ericsson Ux500 series platforms.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ux500_wdt.
+
# AVR32 Architecture
config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 97bbdb3a4648..bec86ee6e9e3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
new file mode 100644
index 000000000000..a614d84121c3
--- /dev/null
+++ b/drivers/watchdog/ux500_wdt.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011-2013
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/ux500_wdt.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+
+#define WATCHDOG_TIMEOUT 600 /* 10 minutes */
+
+#define WATCHDOG_MIN 0
+#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */
+#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */
+
+static unsigned int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int ux500_wdt_start(struct watchdog_device *wdd)
+{
+ return prcmu_enable_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_stop(struct watchdog_device *wdd)
+{
+ return prcmu_disable_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_keepalive(struct watchdog_device *wdd)
+{
+ return prcmu_kick_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ ux500_wdt_stop(wdd);
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+ ux500_wdt_start(wdd);
+
+ return 0;
+}
+
+static const struct watchdog_info ux500_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Ux500 WDT",
+ .firmware_version = 1,
+};
+
+static const struct watchdog_ops ux500_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ux500_wdt_start,
+ .stop = ux500_wdt_stop,
+ .ping = ux500_wdt_keepalive,
+ .set_timeout = ux500_wdt_set_timeout,
+};
+
+static struct watchdog_device ux500_wdt = {
+ .info = &ux500_wdt_info,
+ .ops = &ux500_wdt_ops,
+ .min_timeout = WATCHDOG_MIN,
+ .max_timeout = WATCHDOG_MAX32,
+};
+
+static int ux500_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ux500_wdt_data *pdata = pdev->dev.platform_data;
+
+ if (pdata) {
+ if (pdata->timeout > 0)
+ timeout = pdata->timeout;
+ if (pdata->has_28_bits_resolution)
+ ux500_wdt.max_timeout = WATCHDOG_MAX28;
+ }
+
+ watchdog_set_nowayout(&ux500_wdt, nowayout);
+
+ /* disable auto off on sleep */
+ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
+
+ /* set HW initial value */
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+
+ ret = watchdog_register_device(&ux500_wdt);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "initialized\n");
+
+ return 0;
+}
+
+static int ux500_wdt_remove(struct platform_device *dev)
+{
+ watchdog_unregister_device(&ux500_wdt);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ux500_wdt_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (watchdog_active(&ux500_wdt)) {
+ ux500_wdt_stop(&ux500_wdt);
+ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true);
+
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+ ux500_wdt_start(&ux500_wdt);
+ }
+ return 0;
+}
+
+static int ux500_wdt_resume(struct platform_device *pdev)
+{
+ if (watchdog_active(&ux500_wdt)) {
+ ux500_wdt_stop(&ux500_wdt);
+ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
+
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+ ux500_wdt_start(&ux500_wdt);
+ }
+ return 0;
+}
+#else
+#define ux500_wdt_suspend NULL
+#define ux500_wdt_resume NULL
+#endif
+
+static struct platform_driver ux500_wdt_driver = {
+ .probe = ux500_wdt_probe,
+ .remove = ux500_wdt_remove,
+ .suspend = ux500_wdt_suspend,
+ .resume = ux500_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ux500_wdt",
+ },
+};
+
+module_platform_driver(ux500_wdt_driver);
+
+MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
+MODULE_DESCRIPTION("Ux500 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:ux500_wdt");