summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-11-06 10:23:50 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2015-11-06 10:23:50 -0800
commitbc914532a08892b30954030a0ba68f8534c67f76 (patch)
tree5e98212add9bbdc31860877ef9ff6860479b7a00 /drivers
parent54727e6e950aacd14ec9cd4260e9fe498322828c (diff)
parent271bb1773b1eeff6153f1bba16ff0cff23f064b6 (diff)
Merge tag 'mfd-for-linus-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones: "New Device Support: - Add support for 88pm860; 88pm80x - Add support for 24c08 EEPROM; at24 - Add support for Broxton Whiskey Cove; intel* - Add support for RTS522A; rts5227 - Add support for I2C devices; intel_quark_i2c_gpio New Functionality: - Add microphone support; arizona - Add general purpose switch support; arizona - Add fuel-gauge support; da9150-core - Add shutdown support; sec-core - Add charger support; tps65217 - Add flexible serial communication unit support; atmel-flexcom - Add power button support; axp20x - Add led-flash support; rt5033 Core Frameworks: - Supply a generic macro for defining Regmap IRQs - Rework ACPI child device matching Fix-ups: - Use Regmap to access registers; tps6105x - Use DEFINE_RES_IRQ_NAMED() macro; da9150 - Re-arrange device registration order; intel_quark_i2c_gpio - Allow OF matching; cros_ec_i2c, atmel-hlcdc, hi6421-pmic, max8997, sm501 - Handle deferred probe; twl6040 - Improve accuracy of headphone detect; arizona - Unnecessary MODULE_ALIAS() removal; bcm590xx, rt5033 - Remove unused code; htc-i2cpld, arizona, pcf50633-irq, sec-core - Simplify code; kempld, rts5209, da903x, lm3533, da9052, arizona - Remove #iffery; arizona - DT binding adaptions; many Bug Fixes: - Fix possible NULL pointer dereference; wm831x, tps6105x - Fix 64bit bug; intel_soc_pmic_bxtwc - Fix signedness issue; arizona" * tag 'mfd-for-linus-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (73 commits) bindings: mfd: s2mps11: Add documentation for s2mps15 PMIC mfd: sec-core: Remove unused s2mpu02-rtc and s2mpu02-clk children extcon: arizona: Add extcon specific device tree binding document MAINTAINERS: Add binding docs for Cirrus Logic/Wolfson Arizona devices mfd: arizona: Remove bindings covered in new subsystem specific docs mfd: rt5033: Add RT5033 Flash led sub device mfd: lpss: Add Intel Broxton PCI IDs mfd: lpss: Add Broxton ACPI IDs mfd: arizona: Signedness bug in arizona_runtime_suspend() mfd: axp20x: Add a cell for the power button part of the, axp288 PMICs mfd: dt-bindings: Document pulled down WRSTBI pin on S2MPS1X mfd: sec-core: Disable buck voltage reset on watchdog falling edge mfd: sec-core: Dump PMIC revision to find out the HW mfd: arizona: Use correct type ID for device tree config mfd: arizona: Remove use of codec build config #ifdefs mfd: arizona: Simplify adding subdevices mfd: arizona: Downgrade type mismatch messages to dev_warn mfd: arizona: Factor out checking of jack detection state mfd: arizona: Factor out DCVDD isolation control mfd: Make TPS6105X select REGMAP_I2C ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/88pm80x.c2
-rw-r--r--drivers/mfd/Kconfig21
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/arizona-core.c300
-rw-r--r--drivers/mfd/arizona-i2c.c33
-rw-r--r--drivers/mfd/arizona-irq.c2
-rw-r--r--drivers/mfd/arizona-spi.c23
-rw-r--r--drivers/mfd/atmel-flexcom.c104
-rw-r--r--drivers/mfd/atmel-hlcdc.c1
-rw-r--r--drivers/mfd/axp20x.c20
-rw-r--r--drivers/mfd/bcm590xx.c1
-rw-r--r--drivers/mfd/cros_ec_i2c.c7
-rw-r--r--drivers/mfd/da903x.c6
-rw-r--r--drivers/mfd/da9052-core.c6
-rw-r--r--drivers/mfd/da9052-i2c.c6
-rw-r--r--drivers/mfd/da9052-spi.c6
-rw-r--r--drivers/mfd/da9062-core.c4
-rw-r--r--drivers/mfd/da9150-core.c191
-rw-r--r--drivers/mfd/hi6421-pmic-core.c1
-rw-r--r--drivers/mfd/htc-i2cpld.c3
-rw-r--r--drivers/mfd/intel-lpss-acpi.c16
-rw-r--r--drivers/mfd/intel-lpss-pci.c45
-rw-r--r--drivers/mfd/intel-lpss.c12
-rw-r--r--drivers/mfd/intel_quark_i2c_gpio.c33
-rw-r--r--drivers/mfd/intel_soc_pmic_bxtwc.c477
-rw-r--r--drivers/mfd/kempld-core.c14
-rw-r--r--drivers/mfd/lm3533-core.c13
-rw-r--r--drivers/mfd/lpc_ich.c42
-rw-r--r--drivers/mfd/max8997.c1
-rw-r--r--drivers/mfd/mfd-core.c52
-rw-r--r--drivers/mfd/pcf50633-irq.c4
-rw-r--r--drivers/mfd/qcom_rpm.c2
-rw-r--r--drivers/mfd/rt5033.c4
-rw-r--r--drivers/mfd/rts5209.c6
-rw-r--r--drivers/mfd/rts5227.c83
-rw-r--r--drivers/mfd/rts5229.c6
-rw-r--r--drivers/mfd/rts5249.c6
-rw-r--r--drivers/mfd/rtsx_pcr.c11
-rw-r--r--drivers/mfd/rtsx_pcr.h3
-rw-r--r--drivers/mfd/sec-core.c73
-rw-r--r--drivers/mfd/sm501.c1
-rw-r--r--drivers/mfd/stmpe.c1
-rw-r--r--drivers/mfd/tps6105x.c72
-rw-r--r--drivers/mfd/tps65217.c4
-rw-r--r--drivers/mfd/twl6040.c2
-rw-r--r--drivers/mfd/wm5110-tables.c188
-rw-r--r--drivers/mfd/wm831x-core.c4
-rw-r--r--drivers/mfd/wm8998-tables.c10
-rw-r--r--drivers/misc/eeprom/at24.c22
-rw-r--r--drivers/platform/x86/Kconfig1
-rw-r--r--drivers/power/Kconfig10
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/da9150-fg.c579
53 files changed, 2160 insertions, 377 deletions
diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c
index 5e72f65ef94c..63445ea6b0bf 100644
--- a/drivers/mfd/88pm80x.c
+++ b/drivers/mfd/88pm80x.c
@@ -33,6 +33,8 @@ static struct pm80x_chip_mapping chip_mapping[] = {
{0x3, CHIP_PM800},
/* 88PM805 chip id number */
{0x0, CHIP_PM805},
+ /* 88PM860 chip id number */
+ {0x4, CHIP_PM860},
};
/*
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 99d63675f073..4d92df6ef9fe 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -60,6 +60,17 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_ATMEL_FLEXCOM
+ tristate "Atmel Flexcom (Flexible Serial Communication Unit)"
+ select MFD_CORE
+ depends on OF
+ help
+ Select this to get support for Atmel Flexcom. This is a wrapper
+ which embeds a SPI controller, a I2C controller and a USART. Only
+ one function can be used at a time. The choice is done at boot time
+ by the probe function of this MFD driver according to a device tree
+ property.
+
config MFD_ATMEL_HLCDC
tristate "Atmel HLCDC (High-end LCD Controller)"
select MFD_CORE
@@ -725,9 +736,10 @@ config MFD_RTSX_PCI
select MFD_CORE
help
This supports for Realtek PCI-Express card reader including rts5209,
- rts5229, rtl8411, etc. Realtek card reader supports access to many
- types of memory cards, such as Memory Stick, Memory Stick Pro,
- Secure Digital and MultiMediaCard.
+ rts5227, rts522A, rts5229, rts5249, rts524A, rts525A, rtl8411, etc.
+ Realtek card reader supports access to many types of memory cards,
+ such as Memory Stick, Memory Stick Pro, Secure Digital and
+ MultiMediaCard.
config MFD_RT5033
tristate "Richtek RT5033 Power Management IC"
@@ -1059,6 +1071,7 @@ config MFD_PALMAS
config TPS6105X
tristate "TI TPS61050/61052 Boost Converters"
depends on I2C
+ select REGMAP_I2C
select REGULATOR
select MFD_CORE
select REGULATOR_FIXED_VOLTAGE
@@ -1471,7 +1484,7 @@ config MFD_WM8994
config MFD_STW481X
tristate "Support for ST Microelectronics STw481x"
- depends on I2C && ARCH_NOMADIK
+ depends on I2C && (ARCH_NOMADIK || COMPILE_TEST)
select REGMAP_I2C
select MFD_CORE
help
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a59e3fcc8626..a8b76b81b467 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -164,6 +164,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o
obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o
obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
@@ -190,5 +191,6 @@ obj-$(CONFIG_MFD_RT5033) += rt5033.o
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
+intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 44cfdbb295db..d474732cc65c 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -24,6 +24,7 @@
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/slab.h>
+#include <linux/platform_device.h>
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/registers.h>
@@ -69,8 +70,6 @@ EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
int arizona_clk32k_disable(struct arizona *arizona)
{
- int ret = 0;
-
mutex_lock(&arizona->clk_lock);
BUG_ON(arizona->clk32k_ref <= 0);
@@ -90,7 +89,7 @@ int arizona_clk32k_disable(struct arizona *arizona)
mutex_unlock(&arizona->clk_lock);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
@@ -462,6 +461,50 @@ static int wm5102_clear_write_sequencer(struct arizona *arizona)
}
#ifdef CONFIG_PM
+static int arizona_isolate_dcvdd(struct arizona *arizona)
+{
+ int ret;
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ISOLATION_CONTROL,
+ ARIZONA_ISOLATE_DCVDD1,
+ ARIZONA_ISOLATE_DCVDD1);
+ if (ret != 0)
+ dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n", ret);
+
+ return ret;
+}
+
+static int arizona_connect_dcvdd(struct arizona *arizona)
+{
+ int ret;
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ISOLATION_CONTROL,
+ ARIZONA_ISOLATE_DCVDD1, 0);
+ if (ret != 0)
+ dev_err(arizona->dev, "Failed to connect DCVDD: %d\n", ret);
+
+ return ret;
+}
+
+static int arizona_is_jack_det_active(struct arizona *arizona)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to check jack det status: %d\n", ret);
+ return ret;
+ } else if (val & ARIZONA_JD1_ENA) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
static int arizona_runtime_resume(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
@@ -501,14 +544,9 @@ static int arizona_runtime_resume(struct device *dev)
switch (arizona->type) {
case WM5102:
if (arizona->external_dcvdd) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_ISOLATION_CONTROL,
- ARIZONA_ISOLATE_DCVDD1, 0);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Failed to connect DCVDD: %d\n", ret);
+ ret = arizona_connect_dcvdd(arizona);
+ if (ret != 0)
goto err;
- }
}
ret = wm5102_patch(arizona);
@@ -533,14 +571,9 @@ static int arizona_runtime_resume(struct device *dev)
goto err;
if (arizona->external_dcvdd) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_ISOLATION_CONTROL,
- ARIZONA_ISOLATE_DCVDD1, 0);
- if (ret) {
- dev_err(arizona->dev,
- "Failed to connect DCVDD: %d\n", ret);
+ ret = arizona_connect_dcvdd(arizona);
+ if (ret != 0)
goto err;
- }
} else {
/*
* As this is only called for the internal regulator
@@ -571,14 +604,9 @@ static int arizona_runtime_resume(struct device *dev)
goto err;
if (arizona->external_dcvdd) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_ISOLATION_CONTROL,
- ARIZONA_ISOLATE_DCVDD1, 0);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Failed to connect DCVDD: %d\n", ret);
+ ret = arizona_connect_dcvdd(arizona);
+ if (ret != 0)
goto err;
- }
}
break;
}
@@ -600,49 +628,50 @@ err:
static int arizona_runtime_suspend(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
- unsigned int val;
+ int jd_active = 0;
int ret;
dev_dbg(arizona->dev, "Entering AoD mode\n");
- ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val);
- if (ret) {
- dev_err(dev, "Failed to check jack det status: %d\n", ret);
- return ret;
- }
-
- if (arizona->external_dcvdd) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_ISOLATION_CONTROL,
- ARIZONA_ISOLATE_DCVDD1,
- ARIZONA_ISOLATE_DCVDD1);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
- ret);
- return ret;
- }
- }
-
switch (arizona->type) {
case WM5110:
case WM8280:
- if (arizona->external_dcvdd)
- break;
+ jd_active = arizona_is_jack_det_active(arizona);
+ if (jd_active < 0)
+ return jd_active;
- /*
- * As this is only called for the internal regulator
- * (where we know voltage ranges available) it is ok
- * to request an exact range.
- */
- ret = regulator_set_voltage(arizona->dcvdd, 1175000, 1175000);
- if (ret < 0) {
- dev_err(arizona->dev,
- "Failed to set suspend voltage: %d\n", ret);
- return ret;
+ if (arizona->external_dcvdd) {
+ ret = arizona_isolate_dcvdd(arizona);
+ if (ret != 0)
+ return ret;
+ } else {
+ /*
+ * As this is only called for the internal regulator
+ * (where we know voltage ranges available) it is ok
+ * to request an exact range.
+ */
+ ret = regulator_set_voltage(arizona->dcvdd,
+ 1175000, 1175000);
+ if (ret < 0) {
+ dev_err(arizona->dev,
+ "Failed to set suspend voltage: %d\n",
+ ret);
+ return ret;
+ }
}
break;
case WM5102:
- if (!(val & ARIZONA_JD1_ENA)) {
+ jd_active = arizona_is_jack_det_active(arizona);
+ if (jd_active < 0)
+ return jd_active;
+
+ if (arizona->external_dcvdd) {
+ ret = arizona_isolate_dcvdd(arizona);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (!jd_active) {
ret = regmap_write(arizona->regmap,
ARIZONA_WRITE_SEQUENCER_CTRL_3, 0x0);
if (ret) {
@@ -654,6 +683,15 @@ static int arizona_runtime_suspend(struct device *dev)
}
break;
default:
+ jd_active = arizona_is_jack_det_active(arizona);
+ if (jd_active < 0)
+ return jd_active;
+
+ if (arizona->external_dcvdd) {
+ ret = arizona_isolate_dcvdd(arizona);
+ if (ret != 0)
+ return ret;
+ }
break;
}
@@ -662,7 +700,7 @@ static int arizona_runtime_suspend(struct device *dev)
regulator_disable(arizona->dcvdd);
/* Allow us to completely power down if no jack detection */
- if (!(val & ARIZONA_JD1_ENA)) {
+ if (!jd_active) {
dev_dbg(arizona->dev, "Fully powering off\n");
arizona->has_fully_powered_off = true;
@@ -928,7 +966,8 @@ int arizona_dev_init(struct arizona *arizona)
const char *type_name;
unsigned int reg, val, mask;
int (*apply_patch)(struct arizona *) = NULL;
- int ret, i;
+ const struct mfd_cell *subdevs = NULL;
+ int n_subdevs, ret, i;
dev_set_drvdata(arizona->dev, arizona);
mutex_init(&arizona->clk_lock);
@@ -1089,74 +1128,95 @@ int arizona_dev_init(struct arizona *arizona)
arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
switch (reg) {
-#ifdef CONFIG_MFD_WM5102
case 0x5102:
- type_name = "WM5102";
- if (arizona->type != WM5102) {
- dev_err(arizona->dev, "WM5102 registered as %d\n",
- arizona->type);
- arizona->type = WM5102;
+ if (IS_ENABLED(CONFIG_MFD_WM5102)) {
+ type_name = "WM5102";
+ if (arizona->type != WM5102) {
+ dev_warn(arizona->dev,
+ "WM5102 registered as %d\n",
+ arizona->type);
+ arizona->type = WM5102;
+ }
+
+ apply_patch = wm5102_patch;
+ arizona->rev &= 0x7;
+ subdevs = wm5102_devs;
+ n_subdevs = ARRAY_SIZE(wm5102_devs);
}
- apply_patch = wm5102_patch;
- arizona->rev &= 0x7;
break;
-#endif
-#ifdef CONFIG_MFD_WM5110
case 0x5110:
- switch (arizona->type) {
- case WM5110:
- type_name = "WM5110";
- break;
- case WM8280:
- type_name = "WM8280";
- break;
- default:
- type_name = "WM5110";
- dev_err(arizona->dev, "WM5110 registered as %d\n",
- arizona->type);
- arizona->type = WM5110;
- break;
+ if (IS_ENABLED(CONFIG_MFD_WM5110)) {
+ switch (arizona->type) {
+ case WM5110:
+ type_name = "WM5110";
+ break;
+ case WM8280:
+ type_name = "WM8280";
+ break;
+ default:
+ type_name = "WM5110";
+ dev_warn(arizona->dev,
+ "WM5110 registered as %d\n",
+ arizona->type);
+ arizona->type = WM5110;
+ break;
+ }
+
+ apply_patch = wm5110_patch;
+ subdevs = wm5110_devs;
+ n_subdevs = ARRAY_SIZE(wm5110_devs);
}
- apply_patch = wm5110_patch;
break;
-#endif
-#ifdef CONFIG_MFD_WM8997
case 0x8997:
- type_name = "WM8997";
- if (arizona->type != WM8997) {
- dev_err(arizona->dev, "WM8997 registered as %d\n",
- arizona->type);
- arizona->type = WM8997;
+ if (IS_ENABLED(CONFIG_MFD_WM8997)) {
+ type_name = "WM8997";
+ if (arizona->type != WM8997) {
+ dev_warn(arizona->dev,
+ "WM8997 registered as %d\n",
+ arizona->type);
+ arizona->type = WM8997;
+ }
+
+ apply_patch = wm8997_patch;
+ subdevs = wm8997_devs;
+ n_subdevs = ARRAY_SIZE(wm8997_devs);
}
- apply_patch = wm8997_patch;
break;
-#endif
-#ifdef CONFIG_MFD_WM8998
case 0x6349:
- switch (arizona->type) {
- case WM8998:
- type_name = "WM8998";
- break;
-
- case WM1814:
- type_name = "WM1814";
- break;
+ if (IS_ENABLED(CONFIG_MFD_WM8998)) {
+ switch (arizona->type) {
+ case WM8998:
+ type_name = "WM8998";
+ break;
+
+ case WM1814:
+ type_name = "WM1814";
+ break;
+
+ default:
+ type_name = "WM8998";
+ dev_warn(arizona->dev,
+ "WM8998 registered as %d\n",
+ arizona->type);
+ arizona->type = WM8998;
+ }
- default:
- type_name = "WM8998";
- dev_err(arizona->dev, "WM8998 registered as %d\n",
- arizona->type);
- arizona->type = WM8998;
+ apply_patch = wm8998_patch;
+ subdevs = wm8998_devs;
+ n_subdevs = ARRAY_SIZE(wm8998_devs);
}
-
- apply_patch = wm8998_patch;
break;
-#endif
default:
dev_err(arizona->dev, "Unknown device ID %x\n", reg);
goto err_reset;
}
+ if (!subdevs) {
+ dev_err(arizona->dev,
+ "No kernel support for device ID %x\n", reg);
+ goto err_reset;
+ }
+
dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
if (apply_patch) {
@@ -1342,28 +1402,10 @@ int arizona_dev_init(struct arizona *arizona)
arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
arizona_underclocked, arizona);
- switch (arizona->type) {
- case WM5102:
- ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
- ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
- break;
- case WM5110:
- case WM8280:
- ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
- ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
- break;
- case WM8997:
- ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
- ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
- break;
- case WM8998:
- case WM1814:
- ret = mfd_add_devices(arizona->dev, -1, wm8998_devs,
- ARRAY_SIZE(wm8998_devs), NULL, 0, NULL);
- break;
- }
+ ret = mfd_add_devices(arizona->dev, PLATFORM_DEVID_NONE,
+ subdevs, n_subdevs, NULL, 0, NULL);
- if (ret != 0) {
+ if (ret) {
dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
goto err_irq;
}
diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
index cea1b409fa27..4e3afd1861fc 100644
--- a/drivers/mfd/arizona-i2c.c
+++ b/drivers/mfd/arizona-i2c.c
@@ -27,7 +27,7 @@ static int arizona_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct arizona *arizona;
- const struct regmap_config *regmap_config;
+ const struct regmap_config *regmap_config = NULL;
unsigned long type;
int ret;
@@ -37,31 +37,32 @@ static int arizona_i2c_probe(struct i2c_client *i2c,
type = id->driver_data;
switch (type) {
-#ifdef CONFIG_MFD_WM5102
case WM5102:
- regmap_config = &wm5102_i2c_regmap;
+ if (IS_ENABLED(CONFIG_MFD_WM5102))
+ regmap_config = &wm5102_i2c_regmap;
break;
-#endif
-#ifdef CONFIG_MFD_WM5110
case WM5110:
case WM8280:
- regmap_config = &wm5110_i2c_regmap;
+ if (IS_ENABLED(CONFIG_MFD_WM5110))
+ regmap_config = &wm5110_i2c_regmap;
break;
-#endif
-#ifdef CONFIG_MFD_WM8997
case WM8997:
- regmap_config = &wm8997_i2c_regmap;
+ if (IS_ENABLED(CONFIG_MFD_WM8997))
+ regmap_config = &wm8997_i2c_regmap;
break;
-#endif
-#ifdef CONFIG_MFD_WM8998
case WM8998:
case WM1814:
- regmap_config = &wm8998_i2c_regmap;
+ if (IS_ENABLED(CONFIG_MFD_WM8998))
+ regmap_config = &wm8998_i2c_regmap;
break;
-#endif
default:
- dev_err(&i2c->dev, "Unknown device type %ld\n",
- id->driver_data);
+ dev_err(&i2c->dev, "Unknown device type %ld\n", type);
+ return -EINVAL;
+ }
+
+ if (!regmap_config) {
+ dev_err(&i2c->dev,
+ "No kernel support for device type %ld\n", type);
return -EINVAL;
}
@@ -77,7 +78,7 @@ static int arizona_i2c_probe(struct i2c_client *i2c,
return ret;
}
- arizona->type = id->driver_data;
+ arizona->type = type;
arizona->dev = &i2c->dev;
arizona->irq = i2c->irq;
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index 2cac4f463f1e..3d425e93ce84 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -169,7 +169,7 @@ static struct irq_chip arizona_irq_chip = {
static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
- struct regmap_irq_chip_data *data = h->host_data;
+ struct arizona *data = h->host_data;
irq_set_chip_data(virq, data);
irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_simple_irq);
diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
index 03d62f7b4720..befbc89bfd34 100644
--- a/drivers/mfd/arizona-spi.c
+++ b/drivers/mfd/arizona-spi.c
@@ -27,7 +27,7 @@ static int arizona_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct arizona *arizona;
- const struct regmap_config *regmap_config;
+ const struct regmap_config *regmap_config = NULL;
unsigned long type;
int ret;
@@ -37,20 +37,23 @@ static int arizona_spi_probe(struct spi_device *spi)
type = id->driver_data;
switch (type) {
-#ifdef CONFIG_MFD_WM5102
case WM5102:
- regmap_config = &wm5102_spi_regmap;
+ if (IS_ENABLED(CONFIG_MFD_WM5102))
+ regmap_config = &wm5102_spi_regmap;
break;
-#endif
-#ifdef CONFIG_MFD_WM5110
case WM5110:
case WM8280:
- regmap_config = &wm5110_spi_regmap;
+ if (IS_ENABLED(CONFIG_MFD_WM5110))
+ regmap_config = &wm5110_spi_regmap;
break;
-#endif
default:
- dev_err(&spi->dev, "Unknown device type %ld\n",
- id->driver_data);
+ dev_err(&spi->dev, "Unknown device type %ld\n", type);
+ return -EINVAL;
+ }
+
+ if (!regmap_config) {
+ dev_err(&spi->dev,
+ "No kernel support for device type %ld\n", type);
return -EINVAL;
}
@@ -66,7 +69,7 @@ static int arizona_spi_probe(struct spi_device *spi)
return ret;
}
- arizona->type = id->driver_data;
+ arizona->type = type;
arizona->dev = &spi->dev;
arizona->irq = spi->irq;
diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
new file mode 100644
index 000000000000..e8e67be6b493
--- /dev/null
+++ b/drivers/mfd/atmel-flexcom.c
@@ -0,0 +1,104 @@
+/*
+ * Driver for Atmel Flexcom
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <dt-bindings/mfd/atmel-flexcom.h>
+
+/* I/O register offsets */
+#define FLEX_MR 0x0 /* Mode Register */
+#define FLEX_VERSION 0xfc /* Version Register */
+
+/* Mode Register bit fields */
+#define FLEX_MR_OPMODE_OFFSET (0) /* Operating Mode */
+#define FLEX_MR_OPMODE_MASK (0x3 << FLEX_MR_OPMODE_OFFSET)
+#define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) & \
+ FLEX_MR_OPMODE_MASK)
+
+
+static int atmel_flexcom_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct clk *clk;
+ struct resource *res;
+ void __iomem *base;
+ u32 opmode;
+ int err;
+
+ err = of_property_read_u32(np, "atmel,flexcom-mode", &opmode);
+ if (err)
+ return err;
+
+ if (opmode < ATMEL_FLEXCOM_MODE_USART ||
+ opmode > ATMEL_FLEXCOM_MODE_TWI)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ err = clk_prepare_enable(clk);
+ if (err)
+ return err;
+
+ /*
+ * Set the Operating Mode in the Mode Register: only the selected device
+ * is clocked. Hence, registers of the other serial devices remain
+ * inaccessible and are read as zero. Also the external I/O lines of the
+ * Flexcom are muxed to reach the selected device.
+ */
+ writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
+
+ clk_disable_unprepare(clk);
+
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static const struct of_device_id atmel_flexcom_of_match[] = {
+ { .compatible = "atmel,sama5d2-flexcom" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
+
+static struct platform_driver atmel_flexcom_driver = {
+ .probe = atmel_flexcom_probe,
+ .driver = {
+ .name = "atmel_flexcom",
+ .of_match_table = atmel_flexcom_of_match,
+ },
+};
+
+module_platform_driver(atmel_flexcom_driver);
+
+MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
+MODULE_DESCRIPTION("Atmel Flexcom MFD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
index 3fff6b5d0426..06c205868573 100644
--- a/drivers/mfd/atmel-hlcdc.c
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -148,6 +148,7 @@ static const struct of_device_id atmel_hlcdc_match[] = {
{ .compatible = "atmel,sama5d4-hlcdc" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
static struct platform_driver atmel_hlcdc_driver = {
.probe = atmel_hlcdc_probe,
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 3f576b76c322..9842199e2e6c 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -161,6 +161,21 @@ static struct resource axp22x_pek_resources[] = {
},
};
+static struct resource axp288_power_button_resources[] = {
+ {
+ .name = "PEK_DBR",
+ .start = AXP288_IRQ_POKN,
+ .end = AXP288_IRQ_POKN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "PEK_DBF",
+ .start = AXP288_IRQ_POKP,
+ .end = AXP288_IRQ_POKP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct resource axp288_fuel_gauge_resources[] = {
{
.start = AXP288_IRQ_QWBTU,
@@ -572,6 +587,11 @@ static struct mfd_cell axp288_cells[] = {
.resources = axp288_fuel_gauge_resources,
},
{
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp288_power_button_resources),
+ .resources = axp288_power_button_resources,
+ },
+ {
.name = "axp288_pmic_acpi",
},
};
diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c
index da2af5b4f855..320aaefee718 100644
--- a/drivers/mfd/bcm590xx.c
+++ b/drivers/mfd/bcm590xx.c
@@ -128,4 +128,3 @@ module_i2c_driver(bcm590xx_i2c_driver);
MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
MODULE_DESCRIPTION("BCM590xx multi-function driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("i2c:bcm590xx");
diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c
index d06e4b46db80..56a466469664 100644
--- a/drivers/mfd/cros_ec_i2c.c
+++ b/drivers/mfd/cros_ec_i2c.c
@@ -344,6 +344,12 @@ static int cros_ec_i2c_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend,
cros_ec_i2c_resume);
+static const struct of_device_id cros_ec_i2c_of_match[] = {
+ { .compatible = "google,cros-ec-i2c", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match);
+
static const struct i2c_device_id cros_ec_i2c_id[] = {
{ "cros-ec-i2c", 0 },
{ }
@@ -353,6 +359,7 @@ MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id);
static struct i2c_driver cros_ec_driver = {
.driver = {
.name = "cros-ec-i2c",
+ .of_match_table = of_match_ptr(cros_ec_i2c_of_match),
.pm = &cros_ec_i2c_pm_ops,
},
.probe = cros_ec_i2c_probe,
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index ef7fe2ae2fa4..37e4426ef061 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -532,11 +532,7 @@ static int da903x_probe(struct i2c_client *client,
return ret;
}
- ret = da903x_add_subdevs(chip, pdata);
- if (ret)
- return ret;
-
- return 0;
+ return da903x_add_subdevs(chip, pdata);
}
static int da903x_remove(struct i2c_client *client)
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 46e3840c7a37..c0bf68a3e614 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -51,6 +51,9 @@ static bool da9052_reg_readable(struct device *dev, unsigned int reg)
case DA9052_GPIO_2_3_REG:
case DA9052_GPIO_4_5_REG:
case DA9052_GPIO_6_7_REG:
+ case DA9052_GPIO_8_9_REG:
+ case DA9052_GPIO_10_11_REG:
+ case DA9052_GPIO_12_13_REG:
case DA9052_GPIO_14_15_REG:
case DA9052_ID_0_1_REG:
case DA9052_ID_2_3_REG:
@@ -178,6 +181,9 @@ static bool da9052_reg_writeable(struct device *dev, unsigned int reg)
case DA9052_GPIO_2_3_REG:
case DA9052_GPIO_4_5_REG:
case DA9052_GPIO_6_7_REG:
+ case DA9052_GPIO_8_9_REG:
+ case DA9052_GPIO_10_11_REG:
+ case DA9052_GPIO_12_13_REG:
case DA9052_GPIO_14_15_REG:
case DA9052_ID_0_1_REG:
case DA9052_ID_2_3_REG:
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 02887001e800..2697ffb08009 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -174,11 +174,7 @@ static int da9052_i2c_probe(struct i2c_client *client,
return ret;
}
- ret = da9052_device_init(da9052, id->driver_data);
- if (ret != 0)
- return ret;
-
- return 0;
+ return da9052_device_init(da9052, id->driver_data);
}
static int da9052_i2c_remove(struct i2c_client *client)
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
index 71b89dd4e8de..b9ea1b27db64 100644
--- a/drivers/mfd/da9052-spi.c
+++ b/drivers/mfd/da9052-spi.c
@@ -56,11 +56,7 @@ static int da9052_spi_probe(struct spi_device *spi)
return ret;
}
- ret = da9052_device_init(da9052, id->driver_data);
- if (ret != 0)
- return ret;
-
- return 0;
+ return da9052_device_init(da9052, id->driver_data);
}
static int da9052_spi_remove(struct spi_device *spi)
diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c
index f80d9471f2e7..a9ad024ec6b0 100644
--- a/drivers/mfd/da9062-core.c
+++ b/drivers/mfd/da9062-core.c
@@ -198,7 +198,7 @@ static int da9062_clear_fault_log(struct da9062 *chip)
return ret;
}
-int get_device_type(struct da9062 *chip)
+static int da9062_get_device_type(struct da9062 *chip)
{
int device_id, variant_id, variant_mrc;
int ret;
@@ -466,7 +466,7 @@ static int da9062_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
dev_warn(chip->dev, "Cannot clear fault log\n");
- ret = get_device_type(chip);
+ ret = da9062_get_device_type(chip);
if (ret)
return ret;
diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c
index 94b9bbd1a69b..195fdcfa1a0d 100644
--- a/drivers/mfd/da9150-core.c
+++ b/drivers/mfd/da9150-core.c
@@ -23,6 +23,77 @@
#include <linux/mfd/da9150/core.h>
#include <linux/mfd/da9150/registers.h>
+/* Raw device access, used for QIF */
+static int da9150_i2c_read_device(struct i2c_client *client, u8 addr, int count,
+ u8 *buf)
+{
+ struct i2c_msg xfer;
+ int ret;
+
+ /*
+ * Read is split into two transfers as device expects STOP/START rather
+ * than repeated start to carry out this kind of access.
+ */
+
+ /* Write address */
+ xfer.addr = client->addr;
+ xfer.flags = 0;
+ xfer.len = 1;
+ xfer.buf = &addr;
+
+ ret = i2c_transfer(client->adapter, &xfer, 1);
+ if (ret != 1) {
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+ }
+
+ /* Read data */
+ xfer.addr = client->addr;
+ xfer.flags = I2C_M_RD;
+ xfer.len = count;
+ xfer.buf = buf;
+
+ ret = i2c_transfer(client->adapter, &xfer, 1);
+ if (ret == 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int da9150_i2c_write_device(struct i2c_client *client, u8 addr,
+ int count, const u8 *buf)
+{
+ struct i2c_msg xfer;
+ u8 *reg_data;
+ int ret;
+
+ reg_data = kzalloc(1 + count, GFP_KERNEL);
+ if (!reg_data)
+ return -ENOMEM;
+
+ reg_data[0] = addr;
+ memcpy(&reg_data[1], buf, count);
+
+ /* Write address & data */
+ xfer.addr = client->addr;
+ xfer.flags = 0;
+ xfer.len = 1 + count;
+ xfer.buf = reg_data;
+
+ ret = i2c_transfer(client->adapter, &xfer, 1);
+ kfree(reg_data);
+ if (ret == 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
static bool da9150_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -107,6 +178,28 @@ static const struct regmap_config da9150_regmap_config = {
.volatile_reg = da9150_volatile_reg,
};
+void da9150_read_qif(struct da9150 *da9150, u8 addr, int count, u8 *buf)
+{
+ int ret;
+
+ ret = da9150_i2c_read_device(da9150->core_qif, addr, count, buf);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to read from QIF 0x%x: %d\n",
+ addr, ret);
+}
+EXPORT_SYMBOL_GPL(da9150_read_qif);
+
+void da9150_write_qif(struct da9150 *da9150, u8 addr, int count, const u8 *buf)
+{
+ int ret;
+
+ ret = da9150_i2c_write_device(da9150->core_qif, addr, count, buf);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to write to QIF 0x%x: %d\n",
+ addr, ret);
+}
+EXPORT_SYMBOL_GPL(da9150_write_qif);
+
u8 da9150_reg_read(struct da9150 *da9150, u16 reg)
{
int val, ret;
@@ -262,54 +355,45 @@ static const struct regmap_irq_chip da9150_regmap_irq_chip = {
};
static struct resource da9150_gpadc_resources[] = {
- {
- .name = "GPADC",
- .start = DA9150_IRQ_GPADC,
- .end = DA9150_IRQ_GPADC,
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_IRQ_NAMED(DA9150_IRQ_GPADC, "GPADC"),
};
static struct resource da9150_charger_resources[] = {
- {
- .name = "CHG_STATUS",
- .start = DA9150_IRQ_CHG,
- .end = DA9150_IRQ_CHG,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CHG_TJUNC",
- .start = DA9150_IRQ_TJUNC,
- .end = DA9150_IRQ_TJUNC,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CHG_VFAULT",
- .start = DA9150_IRQ_VFAULT,
- .end = DA9150_IRQ_VFAULT,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CHG_VBUS",
- .start = DA9150_IRQ_VBUS,
- .end = DA9150_IRQ_VBUS,
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_IRQ_NAMED(DA9150_IRQ_CHG, "CHG_STATUS"),
+ DEFINE_RES_IRQ_NAMED(DA9150_IRQ_TJUNC, "CHG_TJUNC"),
+ DEFINE_RES_IRQ_NAMED(DA9150_IRQ_VFAULT, "CHG_VFAULT"),
+ DEFINE_RES_IRQ_NAMED(DA9150_IRQ_VBUS, "CHG_VBUS"),
+};
+
+static struct resource da9150_fg_resources[] = {
+ DEFINE_RES_IRQ_NAMED(DA9150_IRQ_FG, "FG"),
+};
+
+enum da9150_dev_idx {
+ DA9150_GPADC_IDX = 0,
+ DA9150_CHARGER_IDX,
+ DA9150_FG_IDX,
};
static struct mfd_cell da9150_devs[] = {
- {
+ [DA9150_GPADC_IDX] = {
.name = "da9150-gpadc",
.of_compatible = "dlg,da9150-gpadc",
.resources = da9150_gpadc_resources,
.num_resources = ARRAY_SIZE(da9150_gpadc_resources),
},
- {
+ [DA9150_CHARGER_IDX] = {
.name = "da9150-charger",
.of_compatible = "dlg,da9150-charger",
.resources = da9150_charger_resources,
.num_resources = ARRAY_SIZE(da9150_charger_resources),
},
+ [DA9150_FG_IDX] = {
+ .name = "da9150-fuel-gauge",
+ .of_compatible = "dlg,da9150-fuel-gauge",
+ .resources = da9150_fg_resources,
+ .num_resources = ARRAY_SIZE(da9150_fg_resources),
+ },
};
static int da9150_probe(struct i2c_client *client,
@@ -317,6 +401,7 @@ static int da9150_probe(struct i2c_client *client,
{
struct da9150 *da9150;
struct da9150_pdata *pdata = dev_get_platdata(&client->dev);
+ int qif_addr;
int ret;
da9150 = devm_kzalloc(&client->dev, sizeof(*da9150), GFP_KERNEL);
@@ -335,16 +420,41 @@ static int da9150_probe(struct i2c_client *client,
return ret;
}
- da9150->irq_base = pdata ? pdata->irq_base : -1;
+ /* Setup secondary I2C interface for QIF access */
+ qif_addr = da9150_reg_read(da9150, DA9150_CORE2WIRE_CTRL_A);
+ qif_addr = (qif_addr & DA9150_CORE_BASE_ADDR_MASK) >> 1;
+ qif_addr |= DA9150_QIF_I2C_ADDR_LSB;
+ da9150->core_qif = i2c_new_dummy(client->adapter, qif_addr);
+ if (!da9150->core_qif) {
+ dev_err(da9150->dev, "Failed to attach QIF client\n");
+ return -ENODEV;
+ }
+
+ i2c_set_clientdata(da9150->core_qif, da9150);
+
+ if (pdata) {
+ da9150->irq_base = pdata->irq_base;
+
+ da9150_devs[DA9150_FG_IDX].platform_data = pdata->fg_pdata;
+ da9150_devs[DA9150_FG_IDX].pdata_size =
+ sizeof(struct da9150_fg_pdata);
+ } else {
+ da9150->irq_base = -1;
+ }
ret = regmap_add_irq_chip(da9150->regmap, da9150->irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
da9150->irq_base, &da9150_regmap_irq_chip,
&da9150->regmap_irq_data);
- if (ret)
- return ret;
+ if (ret) {
+ dev_err(da9150->dev, "Failed to add regmap irq chip: %d\n",
+ ret);
+ goto regmap_irq_fail;
+ }
+
da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data);
+
enable_irq_wake(da9150->irq);
ret = mfd_add_devices(da9150->dev, -1, da9150_devs,
@@ -352,11 +462,17 @@ static int da9150_probe(struct i2c_client *client,
da9150->irq_base, NULL);
if (ret) {
dev_err(da9150->dev, "Failed to add child devices: %d\n", ret);
- regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
- return ret;
+ goto mfd_fail;
}
return 0;
+
+mfd_fail:
+ regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
+regmap_irq_fail:
+ i2c_unregister_device(da9150->core_qif);
+
+ return ret;
}
static int da9150_remove(struct i2c_client *client)
@@ -365,6 +481,7 @@ static int da9150_remove(struct i2c_client *client)
regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
mfd_remove_devices(da9150->dev);
+ i2c_unregister_device(da9150->core_qif);
return 0;
}
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
index 95b2ff8f223a..f9ded45a992d 100644
--- a/drivers/mfd/hi6421-pmic-core.c
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -97,6 +97,7 @@ static const struct of_device_id of_hi6421_pmic_match_tbl[] = {
{ .compatible = "hisilicon,hi6421-pmic", },
{ },
};
+MODULE_DEVICE_TABLE(of, of_hi6421_pmic_match_tbl);
static struct platform_driver hi6421_pmic_driver = {
.driver = {
diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c
index 1bd5b042c8b3..0c6ff727b2ec 100644
--- a/drivers/mfd/htc-i2cpld.c
+++ b/drivers/mfd/htc-i2cpld.c
@@ -318,7 +318,6 @@ static int htcpld_setup_chip_irq(
struct htcpld_data *htcpld;
struct htcpld_chip *chip;
unsigned int irq, irq_end;
- int ret = 0;
/* Get the platform and driver data */
htcpld = platform_get_drvdata(pdev);
@@ -333,7 +332,7 @@ static int htcpld_setup_chip_irq(
irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
}
- return ret;
+ return 0;
}
static int htcpld_register_chip_i2c(
diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c
index 0d92d73bfa0e..b6fd9041f82f 100644
--- a/drivers/mfd/intel-lpss-acpi.c
+++ b/drivers/mfd/intel-lpss-acpi.c
@@ -25,10 +25,26 @@ static const struct intel_lpss_platform_info spt_info = {
.clk_rate = 120000000,
};
+static const struct intel_lpss_platform_info bxt_info = {
+ .clk_rate = 100000000,
+};
+
+static const struct intel_lpss_platform_info bxt_i2c_info = {
+ .clk_rate = 133000000,
+};
+
static const struct acpi_device_id intel_lpss_acpi_ids[] = {
/* SPT */
{ "INT3446", (kernel_ulong_t)&spt_info },
{ "INT3447", (kernel_ulong_t)&spt_info },
+ /* BXT */
+ { "80860AAC", (kernel_ulong_t)&bxt_i2c_info },
+ { "80860ABC", (kernel_ulong_t)&bxt_info },
+ { "80860AC2", (kernel_ulong_t)&bxt_info },
+ /* APL */
+ { "80865AAC", (kernel_ulong_t)&bxt_i2c_info },
+ { "80865ABC", (kernel_ulong_t)&bxt_info },
+ { "80865AC2", (kernel_ulong_t)&bxt_info },
{ }
};
MODULE_DEVICE_TABLE(acpi, intel_lpss_acpi_ids);
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 9236dffeb4d6..5bfdfccbb9a1 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -70,7 +70,52 @@ static const struct intel_lpss_platform_info spt_uart_info = {
.clk_con_id = "baudclk",
};
+static const struct intel_lpss_platform_info bxt_info = {
+ .clk_rate = 100000000,
+};
+
+static const struct intel_lpss_platform_info bxt_uart_info = {
+ .clk_rate = 100000000,
+ .clk_con_id = "baudclk",
+};
+
+static const struct intel_lpss_platform_info bxt_i2c_info = {
+ .clk_rate = 133000000,
+};
+
static const struct pci_device_id intel_lpss_pci_ids[] = {
+ /* BXT */
+ { PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0ab0), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0ab2), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0ab4), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0ab6), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0ab8), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0aba), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x0abc), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x0abe), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x0ac0), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x0ac2), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x0aee), (kernel_ulong_t)&bxt_uart_info },
+ /* APL */
+ { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5ab0), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5ab2), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5ab4), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5ab6), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5ab8), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5aba), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x5abc), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x5abe), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x5ac0), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x5ac2), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info },
/* SPT-LP */
{ PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index fdf4d5c1add2..001a7d7708ce 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -26,6 +26,8 @@
#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
+#include <asm-generic/io-64-nonatomic-lo-hi.h>
+
#include "intel-lpss.h"
#define LPSS_DEV_OFFSET 0x000
@@ -52,8 +54,7 @@
#define LPSS_PRIV_SSP_REG 0x20
#define LPSS_PRIV_SSP_REG_DIS_DMA_FIN BIT(0)
-#define LPSS_PRIV_REMAP_ADDR_LO 0x40
-#define LPSS_PRIV_REMAP_ADDR_HI 0x44
+#define LPSS_PRIV_REMAP_ADDR 0x40
#define LPSS_PRIV_CAPS 0xfc
#define LPSS_PRIV_CAPS_NO_IDMA BIT(8)
@@ -250,12 +251,7 @@ static void intel_lpss_set_remap_addr(const struct intel_lpss *lpss)
{
resource_size_t addr = lpss->info->mem->start;
- writel(addr, lpss->priv + LPSS_PRIV_REMAP_ADDR_LO);
-#if BITS_PER_LONG > 32
- writel(addr >> 32, lpss->priv + LPSS_PRIV_REMAP_ADDR_HI);
-#else
- writel(0, lpss->priv + LPSS_PRIV_REMAP_ADDR_HI);
-#endif
+ lo_hi_writeq(addr, lpss->priv + LPSS_PRIV_REMAP_ADDR);
}
static void intel_lpss_deassert_reset(const struct intel_lpss *lpss)
diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c
index 1ce16037d043..042137465300 100644
--- a/drivers/mfd/intel_quark_i2c_gpio.c
+++ b/drivers/mfd/intel_quark_i2c_gpio.c
@@ -31,6 +31,10 @@
#define MFD_I2C_BAR 0
#define MFD_GPIO_BAR 1
+/* ACPI _ADR value to match the child node */
+#define MFD_ACPI_MATCH_GPIO 0ULL
+#define MFD_ACPI_MATCH_I2C 1ULL
+
/* The base GPIO number under GPIOLIB framework */
#define INTEL_QUARK_MFD_GPIO_BASE 8
@@ -82,27 +86,37 @@ static struct resource intel_quark_i2c_res[] = {
},
};
+static struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = {
+ .adr = MFD_ACPI_MATCH_I2C,
+};
+
static struct resource intel_quark_gpio_res[] = {
[INTEL_QUARK_IORES_MEM] = {
.flags = IORESOURCE_MEM,
},
};
+static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = {
+ .adr = MFD_ACPI_MATCH_GPIO,
+};
+
static struct mfd_cell intel_quark_mfd_cells[] = {
{
- .id = MFD_I2C_BAR,
- .name = "i2c_designware",
- .num_resources = ARRAY_SIZE(intel_quark_i2c_res),
- .resources = intel_quark_i2c_res,
- .ignore_resource_conflicts = true,
- },
- {
.id = MFD_GPIO_BAR,
.name = "gpio-dwapb",
+ .acpi_match = &intel_quark_acpi_match_gpio,
.num_resources = ARRAY_SIZE(intel_quark_gpio_res),
.resources = intel_quark_gpio_res,
.ignore_resource_conflicts = true,
},
+ {
+ .id = MFD_I2C_BAR,
+ .name = "i2c_designware",
+ .acpi_match = &intel_quark_acpi_match_i2c,
+ .num_resources = ARRAY_SIZE(intel_quark_i2c_res),
+ .resources = intel_quark_i2c_res,
+ .ignore_resource_conflicts = true,
+ },
};
static const struct pci_device_id intel_quark_mfd_ids[] = {
@@ -248,12 +262,11 @@ static int intel_quark_mfd_probe(struct pci_dev *pdev,
dev_set_drvdata(&pdev->dev, quark_mfd);
- ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[MFD_I2C_BAR]);
+ ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[1]);
if (ret)
return ret;
- ret = intel_quark_gpio_setup(pdev,
- &intel_quark_mfd_cells[MFD_GPIO_BAR]);
+ ret = intel_quark_gpio_setup(pdev, &intel_quark_mfd_cells[0]);
if (ret)
return ret;
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
new file mode 100644
index 000000000000..b9428767e615
--- /dev/null
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -0,0 +1,477 @@
+/*
+ * MFD core driver for Intel Broxton Whiskey Cove PMIC
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_bxtwc.h>
+#include <asm/intel_pmc_ipc.h>
+
+/* PMIC device registers */
+#define REG_ADDR_MASK 0xFF00
+#define REG_ADDR_SHIFT 8
+#define REG_OFFSET_MASK 0xFF
+
+/* Interrupt Status Registers */
+#define BXTWC_IRQLVL1 0x4E02
+#define BXTWC_PWRBTNIRQ 0x4E03
+
+#define BXTWC_THRM0IRQ 0x4E04
+#define BXTWC_THRM1IRQ 0x4E05
+#define BXTWC_THRM2IRQ 0x4E06
+#define BXTWC_BCUIRQ 0x4E07
+#define BXTWC_ADCIRQ 0x4E08
+#define BXTWC_CHGR0IRQ 0x4E09
+#define BXTWC_CHGR1IRQ 0x4E0A
+#define BXTWC_GPIOIRQ0 0x4E0B
+#define BXTWC_GPIOIRQ1 0x4E0C
+#define BXTWC_CRITIRQ 0x4E0D
+
+/* Interrupt MASK Registers */
+#define BXTWC_MIRQLVL1 0x4E0E
+#define BXTWC_MPWRTNIRQ 0x4E0F
+
+#define BXTWC_MTHRM0IRQ 0x4E12
+#define BXTWC_MTHRM1IRQ 0x4E13
+#define BXTWC_MTHRM2IRQ 0x4E14
+#define BXTWC_MBCUIRQ 0x4E15
+#define BXTWC_MADCIRQ 0x4E16
+#define BXTWC_MCHGR0IRQ 0x4E17
+#define BXTWC_MCHGR1IRQ 0x4E18
+#define BXTWC_MGPIO0IRQ 0x4E19
+#define BXTWC_MGPIO1IRQ 0x4E1A
+#define BXTWC_MCRITIRQ 0x4E1B
+
+/* Whiskey Cove PMIC share same ACPI ID between different platforms */
+#define BROXTON_PMIC_WC_HRV 4
+
+/* Manage in two IRQ chips since mask registers are not consecutive */
+enum bxtwc_irqs {
+ /* Level 1 */
+ BXTWC_PWRBTN_LVL1_IRQ = 0,
+ BXTWC_TMU_LVL1_IRQ,
+ BXTWC_THRM_LVL1_IRQ,
+ BXTWC_BCU_LVL1_IRQ,
+ BXTWC_ADC_LVL1_IRQ,
+ BXTWC_CHGR_LVL1_IRQ,
+ BXTWC_GPIO_LVL1_IRQ,
+ BXTWC_CRIT_LVL1_IRQ,
+
+ /* Level 2 */
+ BXTWC_PWRBTN_IRQ,
+};
+
+enum bxtwc_irqs_level2 {
+ /* Level 2 */
+ BXTWC_THRM0_IRQ = 0,
+ BXTWC_THRM1_IRQ,
+ BXTWC_THRM2_IRQ,
+ BXTWC_BCU_IRQ,
+ BXTWC_ADC_IRQ,
+ BXTWC_CHGR0_IRQ,
+ BXTWC_CHGR1_IRQ,
+ BXTWC_GPIO0_IRQ,
+ BXTWC_GPIO1_IRQ,
+ BXTWC_CRIT_IRQ,
+};
+
+static const struct regmap_irq bxtwc_regmap_irqs[] = {
+ REGMAP_IRQ_REG(BXTWC_PWRBTN_LVL1_IRQ, 0, BIT(0)),
+ REGMAP_IRQ_REG(BXTWC_TMU_LVL1_IRQ, 0, BIT(1)),
+ REGMAP_IRQ_REG(BXTWC_THRM_LVL1_IRQ, 0, BIT(2)),
+ REGMAP_IRQ_REG(BXTWC_BCU_LVL1_IRQ, 0, BIT(3)),
+ REGMAP_IRQ_REG(BXTWC_ADC_LVL1_IRQ, 0, BIT(4)),
+ REGMAP_IRQ_REG(BXTWC_CHGR_LVL1_IRQ, 0, BIT(5)),
+ REGMAP_IRQ_REG(BXTWC_GPIO_LVL1_IRQ, 0, BIT(6)),
+ REGMAP_IRQ_REG(BXTWC_CRIT_LVL1_IRQ, 0, BIT(7)),
+ REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 1, 0x03),
+};
+
+static const struct regmap_irq bxtwc_regmap_irqs_level2[] = {
+ REGMAP_IRQ_REG(BXTWC_THRM0_IRQ, 0, 0xff),
+ REGMAP_IRQ_REG(BXTWC_THRM1_IRQ, 1, 0xbf),
+ REGMAP_IRQ_REG(BXTWC_THRM2_IRQ, 2, 0xff),
+ REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 3, 0x1f),
+ REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 4, 0xff),
+ REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 5, 0x1f),
+ REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 6, 0x1f),
+ REGMAP_IRQ_REG(BXTWC_GPIO0_IRQ, 7, 0xff),
+ REGMAP_IRQ_REG(BXTWC_GPIO1_IRQ, 8, 0x3f),
+ REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 9, 0x03),
+};
+
+static struct regmap_irq_chip bxtwc_regmap_irq_chip = {
+ .name = "bxtwc_irq_chip",
+ .status_base = BXTWC_IRQLVL1,
+ .mask_base = BXTWC_MIRQLVL1,
+ .irqs = bxtwc_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs),
+ .num_regs = 2,
+};
+
+static struct regmap_irq_chip bxtwc_regmap_irq_chip_level2 = {
+ .name = "bxtwc_irq_chip_level2",
+ .status_base = BXTWC_THRM0IRQ,
+ .mask_base = BXTWC_MTHRM0IRQ,
+ .irqs = bxtwc_regmap_irqs_level2,
+ .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_level2),
+ .num_regs = 10,
+};
+
+static struct resource gpio_resources[] = {
+ DEFINE_RES_IRQ_NAMED(BXTWC_GPIO0_IRQ, "GPIO0"),
+ DEFINE_RES_IRQ_NAMED(BXTWC_GPIO1_IRQ, "GPIO1"),
+};
+
+static struct resource adc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(BXTWC_ADC_IRQ, "ADC"),
+};
+
+static struct resource charger_resources[] = {
+ DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "CHARGER"),
+ DEFINE_RES_IRQ_NAMED(BXTWC_CHGR1_IRQ, "CHARGER1"),
+};
+
+static struct resource thermal_resources[] = {
+ DEFINE_RES_IRQ(BXTWC_THRM0_IRQ),
+ DEFINE_RES_IRQ(BXTWC_THRM1_IRQ),
+ DEFINE_RES_IRQ(BXTWC_THRM2_IRQ),
+};
+
+static struct resource bcu_resources[] = {
+ DEFINE_RES_IRQ_NAMED(BXTWC_BCU_IRQ, "BCU"),
+};
+
+static struct mfd_cell bxt_wc_dev[] = {
+ {
+ .name = "bxt_wcove_gpadc",
+ .num_resources = ARRAY_SIZE(adc_resources),
+ .resources = adc_resources,
+ },
+ {
+ .name = "bxt_wcove_thermal",
+ .num_resources = ARRAY_SIZE(thermal_resources),
+ .resources = thermal_resources,
+ },
+ {
+ .name = "bxt_wcove_ext_charger",
+ .num_resources = ARRAY_SIZE(charger_resources),
+ .resources = charger_resources,
+ },
+ {
+ .name = "bxt_wcove_bcu",
+ .num_resources = ARRAY_SIZE(bcu_resources),
+ .resources = bcu_resources,
+ },
+ {
+ .name = "bxt_wcove_gpio",
+ .num_resources = ARRAY_SIZE(gpio_resources),
+ .resources = gpio_resources,
+ },
+ {
+ .name = "bxt_wcove_region",
+ },
+};
+
+static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ int ret;
+ int i2c_addr;
+ u8 ipc_in[2];
+ u8 ipc_out[4];
+ struct intel_soc_pmic *pmic = context;
+
+ if (reg & REG_ADDR_MASK)
+ i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
+ else {
+ i2c_addr = BXTWC_DEVICE1_ADDR;
+ if (!i2c_addr) {
+ dev_err(pmic->dev, "I2C address not set\n");
+ return -EINVAL;
+ }
+ }
+ reg &= REG_OFFSET_MASK;
+
+ ipc_in[0] = reg;
+ ipc_in[1] = i2c_addr;
+ ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
+ PMC_IPC_PMIC_ACCESS_READ,
+ ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
+ if (ret) {
+ dev_err(pmic->dev, "Failed to read from PMIC\n");
+ return ret;
+ }
+ *val = ipc_out[0];
+
+ return 0;
+}
+
+static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+ int i2c_addr;
+ u8 ipc_in[3];
+ struct intel_soc_pmic *pmic = context;
+
+ if (reg & REG_ADDR_MASK)
+ i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
+ else {
+ i2c_addr = BXTWC_DEVICE1_ADDR;
+ if (!i2c_addr) {
+ dev_err(pmic->dev, "I2C address not set\n");
+ return -EINVAL;
+ }
+ }
+ reg &= REG_OFFSET_MASK;
+
+ ipc_in[0] = reg;
+ ipc_in[1] = i2c_addr;
+ ipc_in[2] = val;
+ ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
+ PMC_IPC_PMIC_ACCESS_WRITE,
+ ipc_in, sizeof(ipc_in), NULL, 0);
+ if (ret) {
+ dev_err(pmic->dev, "Failed to write to PMIC\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* sysfs interfaces to r/w PMIC registers, required by initial script */
+static unsigned long bxtwc_reg_addr;
+static ssize_t bxtwc_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0x%lx\n", bxtwc_reg_addr);
+}
+
+static ssize_t bxtwc_reg_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ if (kstrtoul(buf, 0, &bxtwc_reg_addr)) {
+ dev_err(dev, "Invalid register address\n");
+ return -EINVAL;
+ }
+ return (ssize_t)count;
+}
+
+static ssize_t bxtwc_val_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ unsigned int val;
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ ret = regmap_read(pmic->regmap, bxtwc_reg_addr, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read 0x%lx\n", bxtwc_reg_addr);
+ return -EIO;
+ }
+
+ return sprintf(buf, "0x%02x\n", val);
+}
+
+static ssize_t bxtwc_val_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned int val;
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(pmic->regmap, bxtwc_reg_addr, val);
+ if (ret) {
+ dev_err(dev, "Failed to write value 0x%02x to address 0x%lx",
+ val, bxtwc_reg_addr);
+ return -EIO;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(addr, S_IWUSR | S_IRUSR, bxtwc_reg_show, bxtwc_reg_store);
+static DEVICE_ATTR(val, S_IWUSR | S_IRUSR, bxtwc_val_show, bxtwc_val_store);
+static struct attribute *bxtwc_attrs[] = {
+ &dev_attr_addr.attr,
+ &dev_attr_val.attr,
+ NULL
+};
+
+static const struct attribute_group bxtwc_group = {
+ .attrs = bxtwc_attrs,
+};
+
+static const struct regmap_config bxtwc_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .reg_write = regmap_ipc_byte_reg_write,
+ .reg_read = regmap_ipc_byte_reg_read,
+};
+
+static int bxtwc_probe(struct platform_device *pdev)
+{
+ int ret;
+ acpi_handle handle;
+ acpi_status status;
+ unsigned long long hrv;
+ struct intel_soc_pmic *pmic;
+
+ handle = ACPI_HANDLE(&pdev->dev);
+ status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n");
+ return -ENODEV;
+ }
+ if (hrv != BROXTON_PMIC_WC_HRV) {
+ dev_err(&pdev->dev, "Invalid PMIC hardware revision: %llu\n",
+ hrv);
+ return -ENODEV;
+ }
+
+ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Invalid IRQ\n");
+ return ret;
+ }
+ pmic->irq = ret;
+
+ dev_set_drvdata(&pdev->dev, pmic);
+ pmic->dev = &pdev->dev;
+
+ pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic,
+ &bxtwc_regmap_config);
+ if (IS_ERR(pmic->regmap)) {
+ ret = PTR_ERR(pmic->regmap);
+ dev_err(&pdev->dev, "Failed to initialise regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_add_irq_chip(pmic->regmap, pmic->irq,
+ IRQF_ONESHOT | IRQF_SHARED,
+ 0, &bxtwc_regmap_irq_chip,
+ &pmic->irq_chip_data);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add IRQ chip\n");
+ return ret;
+ }
+
+ ret = regmap_add_irq_chip(pmic->regmap, pmic->irq,
+ IRQF_ONESHOT | IRQF_SHARED,
+ 0, &bxtwc_regmap_irq_chip_level2,
+ &pmic->irq_chip_data_level2);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add secondary IRQ chip\n");
+ goto err_irq_chip_level2;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev,
+ ARRAY_SIZE(bxt_wc_dev), NULL, 0,
+ NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add devices\n");
+ goto err_mfd;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &bxtwc_group);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs group %d\n", ret);
+ goto err_sysfs;
+ }
+
+ return 0;
+
+err_sysfs:
+ mfd_remove_devices(&pdev->dev);
+err_mfd:
+ regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2);
+err_irq_chip_level2:
+ regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
+
+ return ret;
+}
+
+static int bxtwc_remove(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
+
+ sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
+ mfd_remove_devices(&pdev->dev);
+ regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
+ regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2);
+
+ return 0;
+}
+
+static void bxtwc_shutdown(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
+
+ disable_irq(pmic->irq);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bxtwc_suspend(struct device *dev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ disable_irq(pmic->irq);
+
+ return 0;
+}
+
+static int bxtwc_resume(struct device *dev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ enable_irq(pmic->irq);
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume);
+
+static const struct acpi_device_id bxtwc_acpi_ids[] = {
+ { "INT34D3", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, pmic_acpi_ids);
+
+static struct platform_driver bxtwc_driver = {
+ .probe = bxtwc_probe,
+ .remove = bxtwc_remove,
+ .shutdown = bxtwc_shutdown,
+ .driver = {
+ .name = "BXTWC PMIC",
+ .pm = &bxtwc_pm_ops,
+ .acpi_match_table = ACPI_PTR(bxtwc_acpi_ids),
+ },
+};
+
+module_platform_driver(bxtwc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Qipeng Zha<qipeng.zha@intel.com>");
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
index 463f4eae20c1..05b924542ee2 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -448,7 +448,6 @@ static int kempld_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct kempld_device_data *pld;
struct resource *ioport;
- int ret;
pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
if (!pld)
@@ -471,11 +470,7 @@ static int kempld_probe(struct platform_device *pdev)
mutex_init(&pld->lock);
platform_set_drvdata(pdev, pld);
- ret = kempld_detect_device(pld);
- if (ret)
- return ret;
-
- return 0;
+ return kempld_detect_device(pld);
}
static int kempld_remove(struct platform_device *pdev)
@@ -756,7 +751,6 @@ MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
static int __init kempld_init(void)
{
const struct dmi_system_id *id;
- int ret;
if (force_device_id[0]) {
for (id = kempld_dmi_table;
@@ -771,11 +765,7 @@ static int __init kempld_init(void)
return -ENODEV;
}
- ret = platform_driver_register(&kempld_driver);
- if (ret)
- return ret;
-
- return 0;
+ return platform_driver_register(&kempld_driver);
}
static void __exit kempld_exit(void)
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
index 643f3750e830..5abcbb2e8849 100644
--- a/drivers/mfd/lm3533-core.c
+++ b/drivers/mfd/lm3533-core.c
@@ -472,11 +472,7 @@ static int lm3533_device_setup(struct lm3533 *lm3533,
if (ret)
return ret;
- ret = lm3533_set_boost_ovp(lm3533, pdata->boost_ovp);
- if (ret)
- return ret;
-
- return 0;
+ return lm3533_set_boost_ovp(lm3533, pdata->boost_ovp);
}
static int lm3533_device_init(struct lm3533 *lm3533)
@@ -596,7 +592,6 @@ static int lm3533_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lm3533 *lm3533;
- int ret;
dev_dbg(&i2c->dev, "%s\n", __func__);
@@ -613,11 +608,7 @@ static int lm3533_i2c_probe(struct i2c_client *i2c,
lm3533->dev = &i2c->dev;
lm3533->irq = i2c->irq;
- ret = lm3533_device_init(lm3533);
- if (ret)
- return ret;
-
- return 0;
+ return lm3533_device_init(lm3533);
}
static int lm3533_i2c_remove(struct i2c_client *i2c)
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index c5a9a08b5dfb..b514f3cf140d 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -132,24 +132,18 @@ static struct resource gpio_ich_res[] = {
},
};
-enum lpc_cells {
- LPC_WDT = 0,
- LPC_GPIO,
+static struct mfd_cell lpc_ich_wdt_cell = {
+ .name = "iTCO_wdt",
+ .num_resources = ARRAY_SIZE(wdt_ich_res),
+ .resources = wdt_ich_res,
+ .ignore_resource_conflicts = true,
};
-static struct mfd_cell lpc_ich_cells[] = {
- [LPC_WDT] = {
- .name = "iTCO_wdt",
- .num_resources = ARRAY_SIZE(wdt_ich_res),
- .resources = wdt_ich_res,
- .ignore_resource_conflicts = true,
- },
- [LPC_GPIO] = {
- .name = "gpio_ich",
- .num_resources = ARRAY_SIZE(gpio_ich_res),
- .resources = gpio_ich_res,
- .ignore_resource_conflicts = true,
- },
+static struct mfd_cell lpc_ich_gpio_cell = {
+ .name = "gpio_ich",
+ .num_resources = ARRAY_SIZE(gpio_ich_res),
+ .resources = gpio_ich_res,
+ .ignore_resource_conflicts = true,
};
/* chipset related info */
@@ -841,7 +835,7 @@ static int lpc_ich_finalize_wdt_cell(struct pci_dev *dev)
struct itco_wdt_platform_data *pdata;
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
struct lpc_ich_info *info;
- struct mfd_cell *cell = &lpc_ich_cells[LPC_WDT];
+ struct mfd_cell *cell = &lpc_ich_wdt_cell;
pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -860,7 +854,7 @@ static int lpc_ich_finalize_wdt_cell(struct pci_dev *dev)
static void lpc_ich_finalize_gpio_cell(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
- struct mfd_cell *cell = &lpc_ich_cells[LPC_GPIO];
+ struct mfd_cell *cell = &lpc_ich_gpio_cell;
cell->platform_data = &lpc_chipset_info[priv->chipset];
cell->pdata_size = sizeof(struct lpc_ich_info);
@@ -904,7 +898,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
- lpc_ich_cells[LPC_GPIO].num_resources--;
+ lpc_ich_gpio_cell.num_resources--;
goto gpe0_done;
}
@@ -918,7 +912,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
* the platform_device subsystem doesn't see this resource
* or it will register an invalid region.
*/
- lpc_ich_cells[LPC_GPIO].num_resources--;
+ lpc_ich_gpio_cell.num_resources--;
acpi_conflict = true;
} else {
lpc_ich_enable_acpi_space(dev);
@@ -958,12 +952,12 @@ gpe0_done:
lpc_ich_finalize_gpio_cell(dev);
ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
- &lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL);
+ &lpc_ich_gpio_cell, 1, NULL, 0, NULL);
gpio_done:
if (acpi_conflict)
pr_warn("Resource conflict(s) found affecting %s\n",
- lpc_ich_cells[LPC_GPIO].name);
+ lpc_ich_gpio_cell.name);
return ret;
}
@@ -1007,7 +1001,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
*/
if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
/* Don't register iomem for TCO ver 1 */
- lpc_ich_cells[LPC_WDT].num_resources--;
+ lpc_ich_wdt_cell.num_resources--;
} else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
@@ -1035,7 +1029,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
goto wdt_done;
ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
- &lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL);
+ &lpc_ich_wdt_cell, 1, NULL, 0, NULL);
wdt_done:
return ret;
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index d3cfa9cf5c8f..156ed6f92aa3 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -55,6 +55,7 @@ static const struct of_device_id max8997_pmic_dt_match[] = {
{ .compatible = "maxim,max8997-pmic", .data = (void *)TYPE_MAX8997 },
{},
};
+MODULE_DEVICE_TABLE(of, max8997_pmic_dt_match);
#endif
int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index c17635d3e504..60b60dc63ddd 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -82,29 +82,49 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
static void mfd_acpi_add_device(const struct mfd_cell *cell,
struct platform_device *pdev)
{
- struct acpi_device *parent_adev;
+ const struct mfd_cell_acpi_match *match = cell->acpi_match;
+ struct acpi_device *parent, *child;
struct acpi_device *adev;
- parent_adev = ACPI_COMPANION(pdev->dev.parent);
- if (!parent_adev)
+ parent = ACPI_COMPANION(pdev->dev.parent);
+ if (!parent)
return;
/*
- * MFD child device gets its ACPI handle either from the ACPI
- * device directly under the parent that matches the acpi_pnpid or
- * it will use the parent handle if is no acpi_pnpid is given.
+ * MFD child device gets its ACPI handle either from the ACPI device
+ * directly under the parent that matches the either _HID or _CID, or
+ * _ADR or it will use the parent handle if is no ID is given.
+ *
+ * Note that use of _ADR is a grey area in the ACPI specification,
+ * though Intel Galileo Gen2 is using it to distinguish the children
+ * devices.
*/
- adev = parent_adev;
- if (cell->acpi_pnpid) {
- struct acpi_device_id ids[2] = {};
- struct acpi_device *child_adev;
-
- strlcpy(ids[0].id, cell->acpi_pnpid, sizeof(ids[0].id));
- list_for_each_entry(child_adev, &parent_adev->children, node)
- if (acpi_match_device_ids(child_adev, ids)) {
- adev = child_adev;
- break;
+ adev = parent;
+ if (match) {
+ if (match->pnpid) {
+ struct acpi_device_id ids[2] = {};
+
+ strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
+ list_for_each_entry(child, &parent->children, node) {
+ if (acpi_match_device_ids(child, ids)) {
+ adev = child;
+ break;
+ }
+ }
+ } else {
+ unsigned long long adr;
+ acpi_status status;
+
+ list_for_each_entry(child, &parent->children, node) {
+ status = acpi_evaluate_integer(child->handle,
+ "_ADR", NULL,
+ &adr);
+ if (ACPI_SUCCESS(status) && match->adr == adr) {
+ adev = child;
+ break;
+ }
}
+ }
}
ACPI_COMPANION_SET(&pdev->dev, adev);
diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c
index 498286cbb530..71d43edf110c 100644
--- a/drivers/mfd/pcf50633-irq.c
+++ b/drivers/mfd/pcf50633-irq.c
@@ -55,7 +55,7 @@ EXPORT_SYMBOL_GPL(pcf50633_free_irq);
static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
{
u8 reg, bit;
- int ret = 0, idx;
+ int idx;
idx = irq >> 3;
reg = PCF50633_REG_INT1M + idx;
@@ -72,7 +72,7 @@ static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
mutex_unlock(&pcf->lock);
- return ret;
+ return 0;
}
int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c
index 6afc9fabd94c..207a3bd68559 100644
--- a/drivers/mfd/qcom_rpm.c
+++ b/drivers/mfd/qcom_rpm.c
@@ -550,7 +550,7 @@ static int qcom_rpm_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev,
irq_ack,
qcom_rpm_ack_interrupt,
- IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
+ IRQF_TRIGGER_RISING,
"qcom_rpm_ack",
rpm);
if (ret) {
diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c
index d60f91619c4a..2b95485f0057 100644
--- a/drivers/mfd/rt5033.c
+++ b/drivers/mfd/rt5033.c
@@ -47,6 +47,9 @@ static const struct mfd_cell rt5033_devs[] = {
}, {
.name = "rt5033-battery",
.of_compatible = "richtek,rt5033-battery",
+ }, {
+ .name = "rt5033-led",
+ .of_compatible = "richtek,rt5033-led",
},
};
@@ -137,7 +140,6 @@ static struct i2c_driver rt5033_driver = {
};
module_i2c_driver(rt5033_driver);
-MODULE_ALIAS("i2c:rt5033");
MODULE_DESCRIPTION("Richtek RT5033 multi-function core driver");
MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c
index 373e253c33df..b95beecf767f 100644
--- a/drivers/mfd/rts5209.c
+++ b/drivers/mfd/rts5209.c
@@ -138,11 +138,7 @@ static int rts5209_card_power_on(struct rtsx_pcr *pcr, int card)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, pwr_mask, pwr_on);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
LDO3318_PWR_MASK, 0x00);
- err = rtsx_pci_send_cmd(pcr, 100);
- if (err < 0)
- return err;
-
- return 0;
+ return rtsx_pci_send_cmd(pcr, 100);
}
static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card)
diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c
index ce012d78ce2a..ff296a4bf3d2 100644
--- a/drivers/mfd/rts5227.c
+++ b/drivers/mfd/rts5227.c
@@ -26,6 +26,14 @@
#include "rtsx_pcr.h"
+static u8 rts5227_get_ic_version(struct rtsx_pcr *pcr)
+{
+ u8 val;
+
+ rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val);
+ return val & 0x0F;
+}
+
static void rts5227_fill_driving(struct rtsx_pcr *pcr, u8 voltage)
{
u8 driving_3v3[4][3] = {
@@ -88,7 +96,7 @@ static void rts5227_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0);
if (pm_state == HOST_ENTER_S3)
- rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10);
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x10);
rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03);
}
@@ -121,7 +129,7 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0xB8);
else
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0x88);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, pcr->reg_pm_ctrl3, 0x10, 0x00);
return rtsx_pci_send_cmd(pcr, 100);
}
@@ -179,11 +187,7 @@ static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card)
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;
+ return rtsx_pci_send_cmd(pcr, 100);
}
static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card)
@@ -298,8 +302,73 @@ void rts5227_init_params(struct rtsx_pcr *pcr)
pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 15);
pcr->rx_initial_phase = SET_CLOCK_PHASE(30, 7, 7);
+ pcr->ic_version = rts5227_get_ic_version(pcr);
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;
+
+ pcr->reg_pm_ctrl3 = PM_CTRL3;
+}
+
+static int rts522a_optimize_phy(struct rtsx_pcr *pcr)
+{
+ int err;
+
+ err = rtsx_pci_write_register(pcr, RTS522A_PM_CTRL3, D3_DELINK_MODE_EN,
+ 0x00);
+ if (err < 0)
+ return err;
+
+ if (is_version(pcr, 0x522A, IC_VER_A)) {
+ err = rtsx_pci_write_phy_register(pcr, PHY_RCR2,
+ PHY_RCR2_INIT_27S);
+ if (err)
+ return err;
+
+ rtsx_pci_write_phy_register(pcr, PHY_RCR1, PHY_RCR1_INIT_27S);
+ rtsx_pci_write_phy_register(pcr, PHY_FLD0, PHY_FLD0_INIT_27S);
+ rtsx_pci_write_phy_register(pcr, PHY_FLD3, PHY_FLD3_INIT_27S);
+ rtsx_pci_write_phy_register(pcr, PHY_FLD4, PHY_FLD4_INIT_27S);
+ }
+
+ return 0;
+}
+
+static int rts522a_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ rts5227_extra_init_hw(pcr);
+
+ rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, FUNC_FORCE_UPME_XMT_DBG,
+ FUNC_FORCE_UPME_XMT_DBG);
+ rtsx_pci_write_register(pcr, PCLK_CTL, 0x04, 0x04);
+ rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0);
+ rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 0xFF, 0x11);
+
+ return 0;
+}
+
+/* rts522a operations mainly derived from rts5227, except phy/hw init setting.
+ */
+static const struct pcr_ops rts522a_pcr_ops = {
+ .fetch_vendor_settings = rts5227_fetch_vendor_settings,
+ .extra_init_hw = rts522a_extra_init_hw,
+ .optimize_phy = rts522a_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,
+ .force_power_down = rts5227_force_power_down,
+};
+
+void rts522a_init_params(struct rtsx_pcr *pcr)
+{
+ rts5227_init_params(pcr);
+
+ pcr->reg_pm_ctrl3 = RTS522A_PM_CTRL3;
}
diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c
index ace45384ec8b..9ed9dc84eac8 100644
--- a/drivers/mfd/rts5229.c
+++ b/drivers/mfd/rts5229.c
@@ -129,11 +129,7 @@ static int rts5229_card_power_on(struct rtsx_pcr *pcr, int card)
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;
+ return rtsx_pci_send_cmd(pcr, 100);
}
static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card)
diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c
index eb2d5866f719..40f8bb14fc59 100644
--- a/drivers/mfd/rts5249.c
+++ b/drivers/mfd/rts5249.c
@@ -234,11 +234,7 @@ static int rtsx_base_card_power_on(struct rtsx_pcr *pcr, int card)
SD_POWER_MASK, SD_VCC_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;
+ return rtsx_pci_send_cmd(pcr, 100);
}
static int rtsx_base_card_power_off(struct rtsx_pcr *pcr, int card)
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
index a66540a49079..f3820d08c9a3 100644
--- a/drivers/mfd/rtsx_pcr.c
+++ b/drivers/mfd/rtsx_pcr.c
@@ -55,6 +55,7 @@ static const struct pci_device_id rtsx_pci_ids[] = {
{ 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 },
+ { PCI_DEVICE(0x10EC, 0x522A), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 },
@@ -571,11 +572,7 @@ static int rtsx_pci_set_pull_ctl(struct rtsx_pcr *pcr, const u32 *tbl)
tbl++;
}
- err = rtsx_pci_send_cmd(pcr, 100);
- if (err < 0)
- return err;
-
- return 0;
+ return rtsx_pci_send_cmd(pcr, 100);
}
int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card)
@@ -1102,6 +1099,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)
rts5227_init_params(pcr);
break;
+ case 0x522A:
+ rts522a_init_params(pcr);
+ break;
+
case 0x5249:
rts5249_init_params(pcr);
break;
diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h
index ce48842570d7..931d1ae3ce32 100644
--- a/drivers/mfd/rtsx_pcr.h
+++ b/drivers/mfd/rtsx_pcr.h
@@ -27,6 +27,8 @@
#define MIN_DIV_N_PCR 80
#define MAX_DIV_N_PCR 208
+#define RTS522A_PM_CTRL3 0xFF7E
+
#define RTS524A_PME_FORCE_CTL 0xFF78
#define RTS524A_PM_CTRL3 0xFF7E
@@ -38,6 +40,7 @@ void rts5229_init_params(struct rtsx_pcr *pcr);
void rtl8411_init_params(struct rtsx_pcr *pcr);
void rtl8402_init_params(struct rtsx_pcr *pcr);
void rts5227_init_params(struct rtsx_pcr *pcr);
+void rts522a_init_params(struct rtsx_pcr *pcr);
void rts5249_init_params(struct rtsx_pcr *pcr);
void rts524a_init_params(struct rtsx_pcr *pcr);
void rts525a_init_params(struct rtsx_pcr *pcr);
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index d206a3e8fe87..989076d6cb83 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -103,12 +103,9 @@ static const struct mfd_cell s2mpa01_devs[] = {
};
static const struct mfd_cell s2mpu02_devs[] = {
- { .name = "s2mpu02-pmic", },
- { .name = "s2mpu02-rtc", },
{
- .name = "s2mpu02-clk",
- .of_compatible = "samsung,s2mpu02-clk",
- }
+ .name = "s2mpu02-pmic",
+ },
};
#ifdef CONFIG_OF
@@ -253,6 +250,38 @@ static const struct regmap_config s5m8767_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
+{
+ unsigned int val;
+
+ /* For each device type, the REG_ID is always the first register */
+ if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val))
+ dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val);
+}
+
+static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic)
+{
+ int err;
+
+ if (sec_pmic->device_type != S2MPS13X)
+ return;
+
+ if (sec_pmic->pdata->disable_wrstbi) {
+ /*
+ * If WRSTBI pin is pulled down this feature must be disabled
+ * because each Suspend to RAM will trigger buck voltage reset
+ * to default values.
+ */
+ err = regmap_update_bits(sec_pmic->regmap_pmic,
+ S2MPS13_REG_WRSTBI,
+ S2MPS13_REG_WRSTBI_MASK, 0x0);
+ if (err)
+ dev_warn(sec_pmic->dev,
+ "Cannot initialize WRSTBI config: %d\n",
+ err);
+ }
+}
+
#ifdef CONFIG_OF
/*
* Only the common platform data elements for s5m8767 are parsed here from the
@@ -278,6 +307,10 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
* not parsed here.
*/
+ pd->manual_poweroff = of_property_read_bool(dev->of_node,
+ "samsung,s2mps11-acokb-ground");
+ pd->disable_wrstbi = of_property_read_bool(dev->of_node,
+ "samsung,s2mps11-wrstbi-ground");
return pd;
}
#else
@@ -423,6 +456,8 @@ static int sec_pmic_probe(struct i2c_client *i2c,
goto err_mfd;
device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);
+ sec_pmic_configure(sec_pmic);
+ sec_pmic_dump_rev(sec_pmic);
return ret;
@@ -440,6 +475,33 @@ static int sec_pmic_remove(struct i2c_client *i2c)
return 0;
}
+static void sec_pmic_shutdown(struct i2c_client *i2c)
+{
+ struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
+ unsigned int reg, mask;
+
+ if (!sec_pmic->pdata->manual_poweroff)
+ return;
+
+ switch (sec_pmic->device_type) {
+ case S2MPS11X:
+ reg = S2MPS11_REG_CTRL1;
+ mask = S2MPS11_CTRL1_PWRHOLD_MASK;
+ break;
+ default:
+ /*
+ * Currently only one board with S2MPS11 needs this, so just
+ * ignore the rest.
+ */
+ dev_warn(sec_pmic->dev,
+ "Unsupported device %lu for manual power off\n",
+ sec_pmic->device_type);
+ return;
+ }
+
+ regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, 0);
+}
+
#ifdef CONFIG_PM_SLEEP
static int sec_pmic_suspend(struct device *dev)
{
@@ -491,6 +553,7 @@ static struct i2c_driver sec_pmic_driver = {
},
.probe = sec_pmic_probe,
.remove = sec_pmic_remove,
+ .shutdown = sec_pmic_shutdown,
.id_table = sec_pmic_id,
};
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index 91077efc8050..c646784c5a7d 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -1719,6 +1719,7 @@ static const struct of_device_id of_sm501_match_tbl[] = {
{ .compatible = "smi,sm501", },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_sm501_match_tbl);
static struct platform_driver sm501_plat_driver = {
.driver = {
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index e971af86ce1e..8222e374e4b1 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -795,6 +795,7 @@ static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
return 2;
case STMPE_BLOCK_KEYPAD:
+ case STMPE_BLOCK_PWM:
return 1;
case STMPE_BLOCK_GPIO:
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index 4a174cdb50b6..51c54951c220 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -64,27 +64,47 @@ static int tps6105x_startup(struct tps6105x *tps6105x)
}
/*
- * MFD cells - we have one cell which is selected operation
- * mode, and we always have a GPIO cell.
+ * MFD cells - we always have a GPIO cell and we have one cell
+ * which is selected operation mode.
*/
-static struct mfd_cell tps6105x_cells[] = {
- {
- /* name will be runtime assigned */
- .id = -1,
- },
- {
- .name = "tps6105x-gpio",
- .id = -1,
- },
+static struct mfd_cell tps6105x_gpio_cell = {
+ .name = "tps6105x-gpio",
+};
+
+static struct mfd_cell tps6105x_leds_cell = {
+ .name = "tps6105x-leds",
+};
+
+static struct mfd_cell tps6105x_flash_cell = {
+ .name = "tps6105x-flash",
};
+static struct mfd_cell tps6105x_regulator_cell = {
+ .name = "tps6105x-regulator",
+};
+
+static int tps6105x_add_device(struct tps6105x *tps6105x,
+ struct mfd_cell *cell)
+{
+ cell->platform_data = tps6105x;
+ cell->pdata_size = sizeof(*tps6105x);
+
+ return mfd_add_devices(&tps6105x->client->dev,
+ PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, NULL);
+}
+
static int tps6105x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tps6105x *tps6105x;
struct tps6105x_platform_data *pdata;
int ret;
- int i;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ dev_err(&client->dev, "missing platform data\n");
+ return -ENODEV;
+ }
tps6105x = devm_kmalloc(&client->dev, sizeof(*tps6105x), GFP_KERNEL);
if (!tps6105x)
@@ -96,7 +116,6 @@ static int tps6105x_probe(struct i2c_client *client,
i2c_set_clientdata(client, tps6105x);
tps6105x->client = client;
- pdata = dev_get_platdata(&client->dev);
tps6105x->pdata = pdata;
ret = tps6105x_startup(tps6105x);
@@ -105,38 +124,33 @@ static int tps6105x_probe(struct i2c_client *client,
return ret;
}
- /* Remove warning texts when you implement new cell drivers */
+ ret = tps6105x_add_device(tps6105x, &tps6105x_gpio_cell);
+ if (ret)
+ return ret;
+
switch (pdata->mode) {
case TPS6105X_MODE_SHUTDOWN:
dev_info(&client->dev,
"present, not used for anything, only GPIO\n");
break;
case TPS6105X_MODE_TORCH:
- tps6105x_cells[0].name = "tps6105x-leds";
- dev_warn(&client->dev,
- "torch mode is unsupported\n");
+ ret = tps6105x_add_device(tps6105x, &tps6105x_leds_cell);
break;
case TPS6105X_MODE_TORCH_FLASH:
- tps6105x_cells[0].name = "tps6105x-flash";
- dev_warn(&client->dev,
- "flash mode is unsupported\n");
+ ret = tps6105x_add_device(tps6105x, &tps6105x_flash_cell);
break;
case TPS6105X_MODE_VOLTAGE:
- tps6105x_cells[0].name ="tps6105x-regulator";
+ ret = tps6105x_add_device(tps6105x, &tps6105x_regulator_cell);
break;
default:
+ dev_warn(&client->dev, "invalid mode: %d\n", pdata->mode);
break;
}
- /* Set up and register the platform devices. */
- for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) {
- /* One state holder for all drivers, this is simple */
- tps6105x_cells[i].platform_data = tps6105x;
- tps6105x_cells[i].pdata_size = sizeof(*tps6105x);
- }
+ if (ret)
+ mfd_remove_devices(&client->dev);
- return mfd_add_devices(&client->dev, 0, tps6105x_cells,
- ARRAY_SIZE(tps6105x_cells), NULL, 0, NULL);
+ return ret;
}
static int tps6105x_remove(struct i2c_client *client)
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 55add0453ae9..d32b54426b70 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -39,6 +39,10 @@ static const struct mfd_cell tps65217s[] = {
.name = "tps65217-bl",
.of_compatible = "ti,tps65217-bl",
},
+ {
+ .name = "tps65217-charger",
+ .of_compatible = "ti,tps65217-charger",
+ },
};
/**
diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c
index a151ee2eed2a..08a693cd38cc 100644
--- a/drivers/mfd/twl6040.c
+++ b/drivers/mfd/twl6040.c
@@ -647,6 +647,8 @@ static int twl6040_probe(struct i2c_client *client,
twl6040->clk32k = devm_clk_get(&client->dev, "clk32k");
if (IS_ERR(twl6040->clk32k)) {
+ if (PTR_ERR(twl6040->clk32k) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
dev_info(&client->dev, "clk32k is not handled\n");
twl6040->clk32k = NULL;
}
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
index 28f2ae30507a..2bb2d0467a92 100644
--- a/drivers/mfd/wm5110-tables.c
+++ b/drivers/mfd/wm5110-tables.c
@@ -250,7 +250,7 @@ static const struct reg_sequence wm5110_revd_patch[] = {
};
/* Add extra headphone write sequence locations */
-static const struct reg_default wm5110_reve_patch[] = {
+static const struct reg_sequence wm5110_reve_patch[] = {
{ 0x80, 0x3 },
{ 0x80, 0x3 },
{ 0x4b, 0x138 },
@@ -1633,6 +1633,185 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
{ 0x00000F00, 0x0000 }, /* R3840 - Clock Control */
{ 0x00000F01, 0x0000 }, /* R3841 - ANC_SRC */
+ { 0x00000F08, 0x001c }, /* R3848 - ANC Coefficient */
+ { 0x00000F09, 0x0000 }, /* R3849 - ANC Coefficient */
+ { 0x00000F0A, 0x0000 }, /* R3850 - ANC Coefficient */
+ { 0x00000F0B, 0x0000 }, /* R3851 - ANC Coefficient */
+ { 0x00000F0C, 0x0000 }, /* R3852 - ANC Coefficient */
+ { 0x00000F0D, 0x0000 }, /* R3853 - ANC Coefficient */
+ { 0x00000F0E, 0x0000 }, /* R3854 - ANC Coefficient */
+ { 0x00000F0F, 0x0000 }, /* R3855 - ANC Coefficient */
+ { 0x00000F10, 0x0000 }, /* R3856 - ANC Coefficient */
+ { 0x00000F11, 0x0000 }, /* R3857 - ANC Coefficient */
+ { 0x00000F12, 0x0000 }, /* R3858 - ANC Coefficient */
+ { 0x00000F15, 0x0000 }, /* R3861 - FCL Filter Control */
+ { 0x00000F17, 0x0004 }, /* R3863 - FCL ADC Reformatter Control */
+ { 0x00000F18, 0x0004 }, /* R3864 - ANC Coefficient */
+ { 0x00000F19, 0x0002 }, /* R3865 - ANC Coefficient */
+ { 0x00000F1A, 0x0000 }, /* R3866 - ANC Coefficient */
+ { 0x00000F1B, 0x0010 }, /* R3867 - ANC Coefficient */
+ { 0x00000F1C, 0x0000 }, /* R3868 - ANC Coefficient */
+ { 0x00000F1D, 0x0000 }, /* R3869 - ANC Coefficient */
+ { 0x00000F1E, 0x0000 }, /* R3870 - ANC Coefficient */
+ { 0x00000F1F, 0x0000 }, /* R3871 - ANC Coefficient */
+ { 0x00000F20, 0x0000 }, /* R3872 - ANC Coefficient */
+ { 0x00000F21, 0x0000 }, /* R3873 - ANC Coefficient */
+ { 0x00000F22, 0x0000 }, /* R3874 - ANC Coefficient */
+ { 0x00000F23, 0x0000 }, /* R3875 - ANC Coefficient */
+ { 0x00000F24, 0x0000 }, /* R3876 - ANC Coefficient */
+ { 0x00000F25, 0x0000 }, /* R3877 - ANC Coefficient */
+ { 0x00000F26, 0x0000 }, /* R3878 - ANC Coefficient */
+ { 0x00000F27, 0x0000 }, /* R3879 - ANC Coefficient */
+ { 0x00000F28, 0x0000 }, /* R3880 - ANC Coefficient */
+ { 0x00000F29, 0x0000 }, /* R3881 - ANC Coefficient */
+ { 0x00000F2A, 0x0000 }, /* R3882 - ANC Coefficient */
+ { 0x00000F2B, 0x0000 }, /* R3883 - ANC Coefficient */
+ { 0x00000F2C, 0x0000 }, /* R3884 - ANC Coefficient */
+ { 0x00000F2D, 0x0000 }, /* R3885 - ANC Coefficient */
+ { 0x00000F2E, 0x0000 }, /* R3886 - ANC Coefficient */
+ { 0x00000F2F, 0x0000 }, /* R3887 - ANC Coefficient */
+ { 0x00000F30, 0x0000 }, /* R3888 - ANC Coefficient */
+ { 0x00000F31, 0x0000 }, /* R3889 - ANC Coefficient */
+ { 0x00000F32, 0x0000 }, /* R3890 - ANC Coefficient */
+ { 0x00000F33, 0x0000 }, /* R3891 - ANC Coefficient */
+ { 0x00000F34, 0x0000 }, /* R3892 - ANC Coefficient */
+ { 0x00000F35, 0x0000 }, /* R3893 - ANC Coefficient */
+ { 0x00000F36, 0x0000 }, /* R3894 - ANC Coefficient */
+ { 0x00000F37, 0x0000 }, /* R3895 - ANC Coefficient */
+ { 0x00000F38, 0x0000 }, /* R3896 - ANC Coefficient */
+ { 0x00000F39, 0x0000 }, /* R3897 - ANC Coefficient */
+ { 0x00000F3A, 0x0000 }, /* R3898 - ANC Coefficient */
+ { 0x00000F3B, 0x0000 }, /* R3899 - ANC Coefficient */
+ { 0x00000F3C, 0x0000 }, /* R3900 - ANC Coefficient */
+ { 0x00000F3D, 0x0000 }, /* R3901 - ANC Coefficient */
+ { 0x00000F3E, 0x0000 }, /* R3902 - ANC Coefficient */
+ { 0x00000F3F, 0x0000 }, /* R3903 - ANC Coefficient */
+ { 0x00000F40, 0x0000 }, /* R3904 - ANC Coefficient */
+ { 0x00000F41, 0x0000 }, /* R3905 - ANC Coefficient */
+ { 0x00000F42, 0x0000 }, /* R3906 - ANC Coefficient */
+ { 0x00000F43, 0x0000 }, /* R3907 - ANC Coefficient */
+ { 0x00000F44, 0x0000 }, /* R3908 - ANC Coefficient */
+ { 0x00000F45, 0x0000 }, /* R3909 - ANC Coefficient */
+ { 0x00000F46, 0x0000 }, /* R3910 - ANC Coefficient */
+ { 0x00000F47, 0x0000 }, /* R3911 - ANC Coefficient */
+ { 0x00000F48, 0x0000 }, /* R3912 - ANC Coefficient */
+ { 0x00000F49, 0x0000 }, /* R3913 - ANC Coefficient */
+ { 0x00000F4A, 0x0000 }, /* R3914 - ANC Coefficient */
+ { 0x00000F4B, 0x0000 }, /* R3915 - ANC Coefficient */
+ { 0x00000F4C, 0x0000 }, /* R3916 - ANC Coefficient */
+ { 0x00000F4D, 0x0000 }, /* R3917 - ANC Coefficient */
+ { 0x00000F4E, 0x0000 }, /* R3918 - ANC Coefficient */
+ { 0x00000F4F, 0x0000 }, /* R3919 - ANC Coefficient */
+ { 0x00000F50, 0x0000 }, /* R3920 - ANC Coefficient */
+ { 0x00000F51, 0x0000 }, /* R3921 - ANC Coefficient */
+ { 0x00000F52, 0x0000 }, /* R3922 - ANC Coefficient */
+ { 0x00000F53, 0x0000 }, /* R3923 - ANC Coefficient */
+ { 0x00000F54, 0x0000 }, /* R3924 - ANC Coefficient */
+ { 0x00000F55, 0x0000 }, /* R3925 - ANC Coefficient */
+ { 0x00000F56, 0x0000 }, /* R3926 - ANC Coefficient */
+ { 0x00000F57, 0x0000 }, /* R3927 - ANC Coefficient */
+ { 0x00000F58, 0x0000 }, /* R3928 - ANC Coefficient */
+ { 0x00000F59, 0x0000 }, /* R3929 - ANC Coefficient */
+ { 0x00000F5A, 0x0000 }, /* R3930 - ANC Coefficient */
+ { 0x00000F5B, 0x0000 }, /* R3931 - ANC Coefficient */
+ { 0x00000F5C, 0x0000 }, /* R3932 - ANC Coefficient */
+ { 0x00000F5D, 0x0000 }, /* R3933 - ANC Coefficient */
+ { 0x00000F5E, 0x0000 }, /* R3934 - ANC Coefficient */
+ { 0x00000F5F, 0x0000 }, /* R3935 - ANC Coefficient */
+ { 0x00000F60, 0x0000 }, /* R3936 - ANC Coefficient */
+ { 0x00000F61, 0x0000 }, /* R3937 - ANC Coefficient */
+ { 0x00000F62, 0x0000 }, /* R3938 - ANC Coefficient */
+ { 0x00000F63, 0x0000 }, /* R3939 - ANC Coefficient */
+ { 0x00000F64, 0x0000 }, /* R3940 - ANC Coefficient */
+ { 0x00000F65, 0x0000 }, /* R3941 - ANC Coefficient */
+ { 0x00000F66, 0x0000 }, /* R3942 - ANC Coefficient */
+ { 0x00000F67, 0x0000 }, /* R3943 - ANC Coefficient */
+ { 0x00000F68, 0x0000 }, /* R3944 - ANC Coefficient */
+ { 0x00000F69, 0x0000 }, /* R3945 - ANC Coefficient */
+ { 0x00000F70, 0x0000 }, /* R3952 - FCR Filter Control */
+ { 0x00000F72, 0x0004 }, /* R3954 - FCR ADC Reformatter Control */
+ { 0x00000F73, 0x0004 }, /* R3955 - ANC Coefficient */
+ { 0x00000F74, 0x0002 }, /* R3956 - ANC Coefficient */
+ { 0x00000F75, 0x0000 }, /* R3957 - ANC Coefficient */
+ { 0x00000F76, 0x0010 }, /* R3958 - ANC Coefficient */
+ { 0x00000F77, 0x0000 }, /* R3959 - ANC Coefficient */
+ { 0x00000F78, 0x0000 }, /* R3960 - ANC Coefficient */
+ { 0x00000F79, 0x0000 }, /* R3961 - ANC Coefficient */
+ { 0x00000F7A, 0x0000 }, /* R3962 - ANC Coefficient */
+ { 0x00000F7B, 0x0000 }, /* R3963 - ANC Coefficient */
+ { 0x00000F7C, 0x0000 }, /* R3964 - ANC Coefficient */
+ { 0x00000F7D, 0x0000 }, /* R3965 - ANC Coefficient */
+ { 0x00000F7E, 0x0000 }, /* R3966 - ANC Coefficient */
+ { 0x00000F7F, 0x0000 }, /* R3967 - ANC Coefficient */
+ { 0x00000F80, 0x0000 }, /* R3968 - ANC Coefficient */
+ { 0x00000F81, 0x0000 }, /* R3969 - ANC Coefficient */
+ { 0x00000F82, 0x0000 }, /* R3970 - ANC Coefficient */
+ { 0x00000F83, 0x0000 }, /* R3971 - ANC Coefficient */
+ { 0x00000F84, 0x0000 }, /* R3972 - ANC Coefficient */
+ { 0x00000F85, 0x0000 }, /* R3973 - ANC Coefficient */
+ { 0x00000F86, 0x0000 }, /* R3974 - ANC Coefficient */
+ { 0x00000F87, 0x0000 }, /* R3975 - ANC Coefficient */
+ { 0x00000F88, 0x0000 }, /* R3976 - ANC Coefficient */
+ { 0x00000F89, 0x0000 }, /* R3977 - ANC Coefficient */
+ { 0x00000F8A, 0x0000 }, /* R3978 - ANC Coefficient */
+ { 0x00000F8B, 0x0000 }, /* R3979 - ANC Coefficient */
+ { 0x00000F8C, 0x0000 }, /* R3980 - ANC Coefficient */
+ { 0x00000F8D, 0x0000 }, /* R3981 - ANC Coefficient */
+ { 0x00000F8E, 0x0000 }, /* R3982 - ANC Coefficient */
+ { 0x00000F8F, 0x0000 }, /* R3983 - ANC Coefficient */
+ { 0x00000F90, 0x0000 }, /* R3984 - ANC Coefficient */
+ { 0x00000F91, 0x0000 }, /* R3985 - ANC Coefficient */
+ { 0x00000F92, 0x0000 }, /* R3986 - ANC Coefficient */
+ { 0x00000F93, 0x0000 }, /* R3987 - ANC Coefficient */
+ { 0x00000F94, 0x0000 }, /* R3988 - ANC Coefficient */
+ { 0x00000F95, 0x0000 }, /* R3989 - ANC Coefficient */
+ { 0x00000F96, 0x0000 }, /* R3990 - ANC Coefficient */
+ { 0x00000F97, 0x0000 }, /* R3991 - ANC Coefficient */
+ { 0x00000F98, 0x0000 }, /* R3992 - ANC Coefficient */
+ { 0x00000F99, 0x0000 }, /* R3993 - ANC Coefficient */
+ { 0x00000F9A, 0x0000 }, /* R3994 - ANC Coefficient */
+ { 0x00000F9B, 0x0000 }, /* R3995 - ANC Coefficient */
+ { 0x00000F9C, 0x0000 }, /* R3996 - ANC Coefficient */
+ { 0x00000F9D, 0x0000 }, /* R3997 - ANC Coefficient */
+ { 0x00000F9E, 0x0000 }, /* R3998 - ANC Coefficient */
+ { 0x00000F9F, 0x0000 }, /* R3999 - ANC Coefficient */
+ { 0x00000FA0, 0x0000 }, /* R4000 - ANC Coefficient */
+ { 0x00000FA1, 0x0000 }, /* R4001 - ANC Coefficient */
+ { 0x00000FA2, 0x0000 }, /* R4002 - ANC Coefficient */
+ { 0x00000FA3, 0x0000 }, /* R4003 - ANC Coefficient */
+ { 0x00000FA4, 0x0000 }, /* R4004 - ANC Coefficient */
+ { 0x00000FA5, 0x0000 }, /* R4005 - ANC Coefficient */
+ { 0x00000FA6, 0x0000 }, /* R4006 - ANC Coefficient */
+ { 0x00000FA7, 0x0000 }, /* R4007 - ANC Coefficient */
+ { 0x00000FA8, 0x0000 }, /* R4008 - ANC Coefficient */
+ { 0x00000FA9, 0x0000 }, /* R4009 - ANC Coefficient */
+ { 0x00000FAA, 0x0000 }, /* R4010 - ANC Coefficient */
+ { 0x00000FAB, 0x0000 }, /* R4011 - ANC Coefficient */
+ { 0x00000FAC, 0x0000 }, /* R4012 - ANC Coefficient */
+ { 0x00000FAD, 0x0000 }, /* R4013 - ANC Coefficient */
+ { 0x00000FAE, 0x0000 }, /* R4014 - ANC Coefficient */
+ { 0x00000FAF, 0x0000 }, /* R4015 - ANC Coefficient */
+ { 0x00000FB0, 0x0000 }, /* R4016 - ANC Coefficient */
+ { 0x00000FB1, 0x0000 }, /* R4017 - ANC Coefficient */
+ { 0x00000FB2, 0x0000 }, /* R4018 - ANC Coefficient */
+ { 0x00000FB3, 0x0000 }, /* R4019 - ANC Coefficient */
+ { 0x00000FB4, 0x0000 }, /* R4020 - ANC Coefficient */
+ { 0x00000FB5, 0x0000 }, /* R4021 - ANC Coefficient */
+ { 0x00000FB6, 0x0000 }, /* R4022 - ANC Coefficient */
+ { 0x00000FB7, 0x0000 }, /* R4023 - ANC Coefficient */
+ { 0x00000FB8, 0x0000 }, /* R4024 - ANC Coefficient */
+ { 0x00000FB9, 0x0000 }, /* R4025 - ANC Coefficient */
+ { 0x00000FBA, 0x0000 }, /* R4026 - ANC Coefficient */
+ { 0x00000FBB, 0x0000 }, /* R4027 - ANC Coefficient */
+ { 0x00000FBC, 0x0000 }, /* R4028 - ANC Coefficient */
+ { 0x00000FBD, 0x0000 }, /* R4029 - ANC Coefficient */
+ { 0x00000FBE, 0x0000 }, /* R4030 - ANC Coefficient */
+ { 0x00000FBF, 0x0000 }, /* R4031 - ANC Coefficient */
+ { 0x00000FC0, 0x0000 }, /* R4032 - ANC Coefficient */
+ { 0x00000FC1, 0x0000 }, /* R4033 - ANC Coefficient */
+ { 0x00000FC2, 0x0000 }, /* R4034 - ANC Coefficient */
+ { 0x00000FC3, 0x0000 }, /* R4035 - ANC Coefficient */
+ { 0x00000FC4, 0x0000 }, /* R4036 - ANC Coefficient */
{ 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
{ 0x00001200, 0x0010 }, /* R4608 - DSP2 Control 1 */
{ 0x00001300, 0x0010 }, /* R4864 - DSP3 Control 1 */
@@ -2710,6 +2889,13 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_CLOCK_CONTROL:
case ARIZONA_ANC_SRC:
case ARIZONA_DSP_STATUS:
+ case ARIZONA_ANC_COEFF_START ... ARIZONA_ANC_COEFF_END:
+ case ARIZONA_FCL_FILTER_CONTROL:
+ case ARIZONA_FCL_ADC_REFORMATTER_CONTROL:
+ case ARIZONA_FCL_COEFF_START ... ARIZONA_FCL_COEFF_END:
+ case ARIZONA_FCR_FILTER_CONTROL:
+ case ARIZONA_FCR_ADC_REFORMATTER_CONTROL:
+ case ARIZONA_FCR_COEFF_START ... ARIZONA_FCR_COEFF_END:
case ARIZONA_DSP1_CONTROL_1:
case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 28366a90e1ad..3e0e99ec5836 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -1626,7 +1626,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
dev_set_drvdata(wm831x->dev, wm831x);
- wm831x->soft_shutdown = pdata->soft_shutdown;
+
+ if (pdata)
+ wm831x->soft_shutdown = pdata->soft_shutdown;
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
if (ret < 0) {
diff --git a/drivers/mfd/wm8998-tables.c b/drivers/mfd/wm8998-tables.c
index e6de3cd8a9aa..4c2dce77cdfc 100644
--- a/drivers/mfd/wm8998-tables.c
+++ b/drivers/mfd/wm8998-tables.c
@@ -21,7 +21,7 @@
#define WM8998_NUM_AOD_ISR 2
#define WM8998_NUM_ISR 5
-static const struct reg_default wm8998_rev_a_patch[] = {
+static const struct reg_sequence wm8998_rev_a_patch[] = {
{ 0x0212, 0x0000 },
{ 0x0211, 0x0014 },
{ 0x04E4, 0x0E0D },
@@ -199,8 +199,6 @@ static const struct reg_default wm8998_reg_default[] = {
{ 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 */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
{ 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */
@@ -270,16 +268,13 @@ static const struct reg_default wm8998_reg_default[] = {
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
{ 0x00000293, 0x0080 }, /* R659 - Accessory Detect Mode 1 */
{ 0x0000029B, 0x0000 }, /* R667 - Headphone Detect 1 */
- { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
{ 0x000002A2, 0x0000 }, /* R674 - Micd Clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
- { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
{ 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */
{ 0x000002A7, 0x2C37 }, /* R679 - Mic Detect Level 2 */
{ 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */
{ 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */
- { 0x000002AB, 0x0000 }, /* R683 - Mic Detect 4 */
{ 0x000002CB, 0x0000 }, /* R715 - Isolation control */
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
{ 0x00000300, 0x0000 }, /* R768 - Input Enables */
@@ -707,13 +702,11 @@ static const struct reg_default wm8998_reg_default[] = {
{ 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */
{ 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
{ 0x00000D1C, 0xFEFF }, /* R3356 - IRQ2 Status 5 Mask */
- { 0x00000D1D, 0xFFFF }, /* R3357 - IRQ2 Status 6 Mask */
{ 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
{ 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
{ 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
{ 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
{ 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */
- { 0x00000E01, 0x0000 }, /* R3585 - FX_Ctrl2 */
{ 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */
{ 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */
{ 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */
@@ -833,7 +826,6 @@ static bool wm8998_readable_register(struct device *dev, unsigned int reg)
switch (reg) {
case ARIZONA_SOFTWARE_RESET:
case ARIZONA_DEVICE_REVISION:
- case ARIZONA_CTRL_IF_SPI_CFG_1:
case ARIZONA_CTRL_IF_I2C1_CFG_1:
case ARIZONA_CTRL_IF_I2C1_CFG_2:
case ARIZONA_WRITE_SEQUENCER_CTRL_0:
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index c6cb7f8f325e..5d7c0900fa1b 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -21,6 +21,7 @@
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
+#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
@@ -131,6 +132,12 @@ static const struct i2c_device_id at24_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, at24_ids);
+static const struct acpi_device_id at24_acpi_ids[] = {
+ { "INT3499", AT24_DEVICE_MAGIC(8192 / 8, 0) },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
+
/*-------------------------------------------------------------------------*/
/*
@@ -467,21 +474,29 @@ static void at24_get_ofdata(struct i2c_client *client,
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
+ kernel_ulong_t magic = 0;
bool writable;
int use_smbus = 0;
int use_smbus_write = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
- kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
- if (!id->driver_data)
+ if (id) {
+ magic = id->driver_data;
+ } else {
+ const struct acpi_device_id *aid;
+
+ aid = acpi_match_device(at24_acpi_ids, &client->dev);
+ if (aid)
+ magic = aid->driver_data;
+ }
+ if (!magic)
return -ENODEV;
- magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
@@ -661,6 +676,7 @@ static int at24_remove(struct i2c_client *client)
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
+ .acpi_match_table = ACPI_PTR(at24_acpi_ids),
},
.probe = at24_probe,
.remove = at24_remove,
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 7b492d9b3ec4..02bbc70c332d 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -932,6 +932,7 @@ config PVPANIC
config INTEL_PMC_IPC
tristate "Intel PMC IPC Driver"
+ depends on ACPI
---help---
This driver provides support for PMC control on some Intel platforms.
The PMC is an ARC processor which defines IPC commands for communication
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 02b3b313809a..237d7aa73e8c 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -203,6 +203,16 @@ config CHARGER_DA9150
This driver can also be built as a module. If so, the module will be
called da9150-charger.
+config BATTERY_DA9150
+ tristate "Dialog Semiconductor DA9150 Fuel Gauge support"
+ depends on MFD_DA9150
+ help
+ Say Y here to enable support for the Fuel-Gauge unit of the DA9150
+ Integrated Charger & Fuel-Gauge IC
+
+ This driver can also be built as a module. If so, the module will be
+ called da9150-fg.
+
config AXP288_CHARGER
tristate "X-Powers AXP288 Charger"
depends on MFD_AXP20X && EXTCON_AXP288
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b0e1bf190e3d..b656638f8b39 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o
+obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
diff --git a/drivers/power/da9150-fg.c b/drivers/power/da9150-fg.c
new file mode 100644
index 000000000000..8b8ce978656a
--- /dev/null
+++ b/drivers/power/da9150-fg.c
@@ -0,0 +1,579 @@
+/*
+ * DA9150 Fuel-Gauge Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h>
+#include <linux/list.h>
+#include <asm/div64.h>
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+/* Core2Wire */
+#define DA9150_QIF_READ (0x0 << 7)
+#define DA9150_QIF_WRITE (0x1 << 7)
+#define DA9150_QIF_CODE_MASK 0x7F
+
+#define DA9150_QIF_BYTE_SIZE 8
+#define DA9150_QIF_BYTE_MASK 0xFF
+#define DA9150_QIF_SHORT_SIZE 2
+#define DA9150_QIF_LONG_SIZE 4
+
+/* QIF Codes */
+#define DA9150_QIF_UAVG 6
+#define DA9150_QIF_UAVG_SIZE DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_IAVG 8
+#define DA9150_QIF_IAVG_SIZE DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_NTCAVG 12
+#define DA9150_QIF_NTCAVG_SIZE DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_SHUNT_VAL 36
+#define DA9150_QIF_SHUNT_VAL_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_SD_GAIN 38
+#define DA9150_QIF_SD_GAIN_SIZE DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_FCC_MAH 40
+#define DA9150_QIF_FCC_MAH_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_SOC_PCT 43
+#define DA9150_QIF_SOC_PCT_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_CHARGE_LIMIT 44
+#define DA9150_QIF_CHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_DISCHARGE_LIMIT 45
+#define DA9150_QIF_DISCHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_FW_MAIN_VER 118
+#define DA9150_QIF_FW_MAIN_VER_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_E_FG_STATUS 126
+#define DA9150_QIF_E_FG_STATUS_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_SYNC 127
+#define DA9150_QIF_SYNC_SIZE DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_MAX_CODES 128
+
+/* QIF Sync Timeout */
+#define DA9150_QIF_SYNC_TIMEOUT 1000
+#define DA9150_QIF_SYNC_RETRIES 10
+
+/* QIF E_FG_STATUS */
+#define DA9150_FG_IRQ_LOW_SOC_MASK (1 << 0)
+#define DA9150_FG_IRQ_HIGH_SOC_MASK (1 << 1)
+#define DA9150_FG_IRQ_SOC_MASK \
+ (DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)
+
+/* Private data */
+struct da9150_fg {
+ struct da9150 *da9150;
+ struct device *dev;
+
+ struct mutex io_lock;
+
+ struct power_supply *battery;
+ struct delayed_work work;
+ u32 interval;
+
+ int warn_soc;
+ int crit_soc;
+ int soc;
+};
+
+/* Battery Properties */
+static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
+
+{
+ u8 buf[size];
+ u8 read_addr;
+ u32 res = 0;
+ int i;
+
+ /* Set QIF code (READ mode) */
+ read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;
+
+ da9150_read_qif(fg->da9150, read_addr, size, buf);
+ for (i = 0; i < size; ++i)
+ res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));
+
+ return res;
+}
+
+static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
+ u32 val)
+
+{
+ u8 buf[size];
+ u8 write_addr;
+ int i;
+
+ /* Set QIF code (WRITE mode) */
+ write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;
+
+ for (i = 0; i < size; ++i) {
+ buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &
+ DA9150_QIF_BYTE_MASK;
+ }
+ da9150_write_qif(fg->da9150, write_addr, size, buf);
+}
+
+/* Trigger QIF Sync to update QIF readable data */
+static void da9150_fg_read_sync_start(struct da9150_fg *fg)
+{
+ int i = 0;
+ u32 res = 0;
+
+ mutex_lock(&fg->io_lock);
+
+ /* Check if QIF sync already requested, and write to sync if not */
+ res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+ DA9150_QIF_SYNC_SIZE);
+ if (res > 0)
+ da9150_fg_write_attr(fg, DA9150_QIF_SYNC,
+ DA9150_QIF_SYNC_SIZE, 0);
+
+ /* Wait for sync to complete */
+ res = 0;
+ while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
+ usleep_range(DA9150_QIF_SYNC_TIMEOUT,
+ DA9150_QIF_SYNC_TIMEOUT * 2);
+ res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+ DA9150_QIF_SYNC_SIZE);
+ }
+
+ /* Check if sync completed */
+ if (res == 0)
+ dev_err(fg->dev, "Failed to perform QIF read sync!\n");
+}
+
+/*
+ * Should always be called after QIF sync read has been performed, and all
+ * attributes required have been accessed.
+ */
+static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)
+{
+ mutex_unlock(&fg->io_lock);
+}
+
+/* Sync read of single QIF attribute */
+static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)
+{
+ u32 val;
+
+ da9150_fg_read_sync_start(fg);
+ val = da9150_fg_read_attr(fg, code, size);
+ da9150_fg_read_sync_end(fg);
+
+ return val;
+}
+
+/* Wait for QIF Sync, write QIF data and wait for ack */
+static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,
+ u32 val)
+{
+ int i = 0;
+ u32 res = 0, sync_val;
+
+ mutex_lock(&fg->io_lock);
+
+ /* Check if QIF sync already requested */
+ res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+ DA9150_QIF_SYNC_SIZE);
+
+ /* Wait for an existing sync to complete */
+ while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
+ usleep_range(DA9150_QIF_SYNC_TIMEOUT,
+ DA9150_QIF_SYNC_TIMEOUT * 2);
+ res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+ DA9150_QIF_SYNC_SIZE);
+ }
+
+ if (res == 0) {
+ dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");
+ mutex_unlock(&fg->io_lock);
+ return;
+ }
+
+ /* Write value for QIF code */
+ da9150_fg_write_attr(fg, code, size, val);
+
+ /* Wait for write acknowledgment */
+ i = 0;
+ sync_val = res;
+ while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
+ usleep_range(DA9150_QIF_SYNC_TIMEOUT,
+ DA9150_QIF_SYNC_TIMEOUT * 2);
+ res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+ DA9150_QIF_SYNC_SIZE);
+ }
+
+ mutex_unlock(&fg->io_lock);
+
+ /* Check write was actually successful */
+ if (res != (sync_val + 1))
+ dev_err(fg->dev, "Error performing QIF sync write for code %d\n",
+ code);
+}
+
+/* Power Supply attributes */
+static int da9150_fg_capacity(struct da9150_fg *fg,
+ union power_supply_propval *val)
+{
+ val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
+ DA9150_QIF_SOC_PCT_SIZE);
+
+ if (val->intval > 100)
+ val->intval = 100;
+
+ return 0;
+}
+
+static int da9150_fg_current_avg(struct da9150_fg *fg,
+ union power_supply_propval *val)
+{
+ u32 iavg, sd_gain, shunt_val;
+ u64 div, res;
+
+ da9150_fg_read_sync_start(fg);
+ iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,
+ DA9150_QIF_IAVG_SIZE);
+ shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,
+ DA9150_QIF_SHUNT_VAL_SIZE);
+ sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,
+ DA9150_QIF_SD_GAIN_SIZE);
+ da9150_fg_read_sync_end(fg);
+
+ div = (u64) (sd_gain * shunt_val * 65536ULL);
+ do_div(div, 1000000);
+ res = (u64) (iavg * 1000000ULL);
+ do_div(res, div);
+
+ val->intval = (int) res;
+
+ return 0;
+}
+
+static int da9150_fg_voltage_avg(struct da9150_fg *fg,
+ union power_supply_propval *val)
+{
+ u64 res;
+
+ val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,
+ DA9150_QIF_UAVG_SIZE);
+
+ res = (u64) (val->intval * 186ULL);
+ do_div(res, 10000);
+ val->intval = (int) res;
+
+ return 0;
+}
+
+static int da9150_fg_charge_full(struct da9150_fg *fg,
+ union power_supply_propval *val)
+{
+ val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,
+ DA9150_QIF_FCC_MAH_SIZE);
+
+ val->intval = val->intval * 1000;
+
+ return 0;
+}
+
+/*
+ * Temperature reading from device is only valid if battery/system provides
+ * valid NTC to associated pin of DA9150 chip.
+ */
+static int da9150_fg_temp(struct da9150_fg *fg,
+ union power_supply_propval *val)
+{
+ val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,
+ DA9150_QIF_NTCAVG_SIZE);
+
+ val->intval = (val->intval * 10) / 1048576;
+
+ return 0;
+}
+
+static enum power_supply_property da9150_fg_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static int da9150_fg_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ ret = da9150_fg_capacity(fg, val);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ ret = da9150_fg_current_avg(fg, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ ret = da9150_fg_voltage_avg(fg, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ ret = da9150_fg_charge_full(fg, val);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ ret = da9150_fg_temp(fg, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/* Repeated SOC check */
+static bool da9150_fg_soc_changed(struct da9150_fg *fg)
+{
+ union power_supply_propval val;
+
+ da9150_fg_capacity(fg, &val);
+ if (val.intval != fg->soc) {
+ fg->soc = val.intval;
+ return true;
+ }
+
+ return false;
+}
+
+static void da9150_fg_work(struct work_struct *work)
+{
+ struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);
+
+ /* Report if SOC has changed */
+ if (da9150_fg_soc_changed(fg))
+ power_supply_changed(fg->battery);
+
+ schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));
+}
+
+/* SOC level event configuration */
+static void da9150_fg_soc_event_config(struct da9150_fg *fg)
+{
+ int soc;
+
+ soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
+ DA9150_QIF_SOC_PCT_SIZE);
+
+ if (soc > fg->warn_soc) {
+ /* If SOC > warn level, set discharge warn level event */
+ da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
+ DA9150_QIF_DISCHARGE_LIMIT_SIZE,
+ fg->warn_soc + 1);
+ } else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {
+ /*
+ * If SOC <= warn level, set discharge crit level event,
+ * and set charge warn level event.
+ */
+ da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
+ DA9150_QIF_DISCHARGE_LIMIT_SIZE,
+ fg->crit_soc + 1);
+
+ da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
+ DA9150_QIF_CHARGE_LIMIT_SIZE,
+ fg->warn_soc);
+ } else if (soc <= fg->crit_soc) {
+ /* If SOC <= crit level, set charge crit level event */
+ da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
+ DA9150_QIF_CHARGE_LIMIT_SIZE,
+ fg->crit_soc);
+ }
+}
+
+static irqreturn_t da9150_fg_irq(int irq, void *data)
+{
+ struct da9150_fg *fg = data;
+ u32 e_fg_status;
+
+ /* Read FG IRQ status info */
+ e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,
+ DA9150_QIF_E_FG_STATUS_SIZE);
+
+ /* Handle warning/critical threhold events */
+ if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)
+ da9150_fg_soc_event_config(fg);
+
+ /* Clear any FG IRQs */
+ da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,
+ DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);
+
+ return IRQ_HANDLED;
+}
+
+static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)
+{
+ struct device_node *fg_node = dev->of_node;
+ struct da9150_fg_pdata *pdata;
+
+ pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ of_property_read_u32(fg_node, "dlg,update-interval",
+ &pdata->update_interval);
+ of_property_read_u8(fg_node, "dlg,warn-soc-level",
+ &pdata->warn_soc_lvl);
+ of_property_read_u8(fg_node, "dlg,crit-soc-level",
+ &pdata->crit_soc_lvl);
+
+ return pdata;
+}
+
+static const struct power_supply_desc fg_desc = {
+ .name = "da9150-fg",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = da9150_fg_props,
+ .num_properties = ARRAY_SIZE(da9150_fg_props),
+ .get_property = da9150_fg_get_prop,
+};
+
+static int da9150_fg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+ struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);
+ struct da9150_fg *fg;
+ int ver, irq, ret = 0;
+
+ fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);
+ if (fg == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, fg);
+ fg->da9150 = da9150;
+ fg->dev = dev;
+
+ mutex_init(&fg->io_lock);
+
+ /* Enable QIF */
+ da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,
+ DA9150_FG_QIF_EN_MASK);
+
+ fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);
+ if (IS_ERR(fg->battery)) {
+ ret = PTR_ERR(fg->battery);
+ return ret;
+ }
+
+ ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,
+ DA9150_QIF_FW_MAIN_VER_SIZE);
+ dev_info(dev, "Version: 0x%x\n", ver);
+
+ /* Handle DT data if provided */
+ if (dev->of_node) {
+ fg_pdata = da9150_fg_dt_pdata(dev);
+ dev->platform_data = fg_pdata;
+ }
+
+ /* Handle any pdata provided */
+ if (fg_pdata) {
+ fg->interval = fg_pdata->update_interval;
+
+ if (fg_pdata->warn_soc_lvl > 100)
+ dev_warn(dev, "Invalid SOC warning level provided, Ignoring");
+ else
+ fg->warn_soc = fg_pdata->warn_soc_lvl;
+
+ if ((fg_pdata->crit_soc_lvl > 100) ||
+ (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))
+ dev_warn(dev, "Invalid SOC critical level provided, Ignoring");
+ else
+ fg->crit_soc = fg_pdata->crit_soc_lvl;
+
+
+ }
+
+ /* Configure initial SOC level events */
+ da9150_fg_soc_event_config(fg);
+
+ /*
+ * If an interval period has been provided then setup repeating
+ * work for reporting data updates.
+ */
+ if (fg->interval) {
+ INIT_DELAYED_WORK(&fg->work, da9150_fg_work);
+ schedule_delayed_work(&fg->work,
+ msecs_to_jiffies(fg->interval));
+ }
+
+ /* Register IRQ */
+ irq = platform_get_irq_byname(pdev, "FG");
+ if (irq < 0) {
+ dev_err(dev, "Failed to get IRQ FG: %d\n", irq);
+ ret = irq;
+ goto irq_fail;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
+ IRQF_ONESHOT, "FG", fg);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
+ goto irq_fail;
+ }
+
+ return 0;
+
+irq_fail:
+ if (fg->interval)
+ cancel_delayed_work(&fg->work);
+
+ return ret;
+}
+
+static int da9150_fg_remove(struct platform_device *pdev)
+{
+ struct da9150_fg *fg = platform_get_drvdata(pdev);
+
+ if (fg->interval)
+ cancel_delayed_work(&fg->work);
+
+ return 0;
+}
+
+static int da9150_fg_resume(struct platform_device *pdev)
+{
+ struct da9150_fg *fg = platform_get_drvdata(pdev);
+
+ /*
+ * Trigger SOC check to happen now so as to indicate any value change
+ * since last check before suspend.
+ */
+ if (fg->interval)
+ flush_delayed_work(&fg->work);
+
+ return 0;
+}
+
+static struct platform_driver da9150_fg_driver = {
+ .driver = {
+ .name = "da9150-fuel-gauge",
+ },
+ .probe = da9150_fg_probe,
+ .remove = da9150_fg_remove,
+ .resume = da9150_fg_resume,
+};
+
+module_platform_driver(da9150_fg_driver);
+
+MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");