summaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2010-12-10 15:19:18 +0100
committerJiri Kosina <jkosina@suse.cz>2010-12-10 15:19:18 +0100
commit2ade0c1d9d93b7642212657ef76f4a1e30233711 (patch)
tree63bc720c0ffe5f4760cac4ed617b9870b050175e /drivers/mfd
parent504499f22c08a03e2e19dc88d31aa0ecd2ac815e (diff)
parent6313e3c21743cc88bb5bd8aa72948ee1e83937b6 (diff)
downloadtalos-op-linux-2ade0c1d9d93b7642212657ef76f4a1e30233711.tar.gz
talos-op-linux-2ade0c1d9d93b7642212657ef76f4a1e30233711.zip
Merge branch 'master' into upstream
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/88pm860x-core.c51
-rw-r--r--drivers/mfd/Kconfig91
-rw-r--r--drivers/mfd/Makefile9
-rw-r--r--drivers/mfd/ab3100-core.c143
-rw-r--r--drivers/mfd/ab8500-core.c319
-rw-r--r--drivers/mfd/ab8500-debugfs.c652
-rw-r--r--drivers/mfd/ab8500-i2c.c105
-rw-r--r--drivers/mfd/ab8500-spi.c2
-rw-r--r--drivers/mfd/da903x.c8
-rw-r--r--drivers/mfd/ezx-pcap.c11
-rw-r--r--drivers/mfd/htc-pasic3.c7
-rw-r--r--drivers/mfd/jz4740-adc.c2
-rw-r--r--drivers/mfd/max8925-core.c11
-rw-r--r--drivers/mfd/max8998-irq.c258
-rw-r--r--drivers/mfd/max8998.c90
-rw-r--r--drivers/mfd/mc13783-core.c752
-rw-r--r--drivers/mfd/mc13xxx-core.c840
-rw-r--r--drivers/mfd/mfd-core.c18
-rw-r--r--drivers/mfd/pcf50633-core.c9
-rw-r--r--drivers/mfd/sh_mobile_sdhi.c21
-rw-r--r--drivers/mfd/stmpe.c32
-rw-r--r--drivers/mfd/tc6393xb.c2
-rw-r--r--drivers/mfd/timberdale.c14
-rw-r--r--drivers/mfd/tps6507x.c2
-rw-r--r--drivers/mfd/tps6586x.c225
-rw-r--r--drivers/mfd/twl-core.c46
-rw-r--r--drivers/mfd/twl-core.h10
-rw-r--r--drivers/mfd/twl4030-codec.c8
-rw-r--r--drivers/mfd/twl4030-irq.c4
-rw-r--r--drivers/mfd/twl4030-power.c30
-rw-r--r--drivers/mfd/twl6030-irq.c75
-rw-r--r--drivers/mfd/vx855.c147
-rw-r--r--drivers/mfd/wm831x-core.c148
-rw-r--r--drivers/mfd/wm831x-i2c.c143
-rw-r--r--drivers/mfd/wm831x-spi.c232
35 files changed, 3336 insertions, 1181 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 07933f3f7e4c..20895e7a99c9 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -158,6 +158,43 @@ static struct mfd_cell onkey_devs[] = {
},
};
+static struct resource codec_resources[] = {
+ {
+ /* Headset microphone insertion or removal */
+ .name = "micin",
+ .start = PM8607_IRQ_MICIN,
+ .end = PM8607_IRQ_MICIN,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ /* Hook-switch press or release */
+ .name = "hook",
+ .start = PM8607_IRQ_HOOK,
+ .end = PM8607_IRQ_HOOK,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ /* Headset insertion or removal */
+ .name = "headset",
+ .start = PM8607_IRQ_HEADSET,
+ .end = PM8607_IRQ_HEADSET,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ /* Audio short */
+ .name = "audio-short",
+ .start = PM8607_IRQ_AUDIO_SHORT,
+ .end = PM8607_IRQ_AUDIO_SHORT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell codec_devs[] = {
+ {
+ .name = "88pm860x-codec",
+ .num_resources = ARRAY_SIZE(codec_resources),
+ .resources = &codec_resources[0],
+ .id = -1,
+ },
+};
+
static struct resource regulator_resources[] = {
PM8607_REG_RESOURCE(BUCK1, BUCK1),
PM8607_REG_RESOURCE(BUCK2, BUCK2),
@@ -608,10 +645,13 @@ static void __devinit device_8607_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
goto out;
}
- if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
+ switch (ret & PM8607_VERSION_MASK) {
+ case 0x40:
+ case 0x50:
dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
ret);
- else {
+ break;
+ default:
dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
"Chip ID: %02x\n", ret);
goto out;
@@ -687,6 +727,13 @@ static void __devinit device_8607_init(struct pm860x_chip *chip,
goto out_dev;
}
+ ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
+ ARRAY_SIZE(codec_devs),
+ &codec_resources[0], 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to add codec subdev\n");
+ goto out_dev;
+ }
return;
out_dev:
mfd_remove_devices(chip->dev);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index db51ea1c6082..3a1493b8b5e5 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -75,7 +75,7 @@ config MFD_DAVINCI_VOICECODEC
config MFD_DM355EVM_MSP
bool "DaVinci DM355 EVM microcontroller"
- depends on I2C && MACH_DAVINCI_DM355_EVM
+ depends on I2C=y && MACH_DAVINCI_DM355_EVM
help
This driver supports the MSP430 microcontroller used on these
boards. MSP430 firmware manages resets and power sequencing,
@@ -294,14 +294,15 @@ config MFD_MAX8925
to use the functionality of the device.
config MFD_MAX8998
- bool "Maxim Semiconductor MAX8998 PMIC Support"
- depends on I2C=y
+ bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support"
+ depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
help
- Say yes here to support for Maxim Semiconductor MAX8998. This is
- a Power Management IC. This driver provies common support for
- accessing the device, additional drivers must be enabled in order
- to use the functionality of the device.
+ Say yes here to support for Maxim Semiconductor MAX8998 and
+ National Semiconductor LP3974. This is a Power Management IC.
+ This driver provies common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device.
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
@@ -314,14 +315,30 @@ config MFD_WM8400
the functionality of the device.
config MFD_WM831X
- bool "Support Wolfson Microelectronics WM831x/2x PMICs"
+ bool
+ depends on GENERIC_HARDIRQS
+
+config MFD_WM831X_I2C
+ bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
select MFD_CORE
+ select MFD_WM831X
depends on I2C=y && GENERIC_HARDIRQS
help
- Support for the Wolfson Microelecronics WM831x and WM832x PMICs.
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the
- functionality of the device.
+ Support for the Wolfson Microelecronics WM831x and WM832x PMICs
+ when controlled using I2C. This driver provides common support
+ for accessing the device, additional drivers must be enabled in
+ order to use the functionality of the device.
+
+config MFD_WM831X_SPI
+ bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
+ select MFD_CORE
+ select MFD_WM831X
+ depends on SPI_MASTER && GENERIC_HARDIRQS
+ help
+ Support for the Wolfson Microelecronics WM831x and WM832x PMICs
+ when controlled using SPI. This driver provides common support
+ for accessing the device, additional drivers must be enabled in
+ order to use the functionality of the device.
config MFD_WM8350
bool
@@ -408,11 +425,16 @@ config MFD_PCF50633
so that function-specific drivers can bind to them.
config MFD_MC13783
- tristate "Support Freescale MC13783"
+ tristate
+
+config MFD_MC13XXX
+ tristate "Support Freescale MC13783 and MC13892"
depends on SPI_MASTER
select MFD_CORE
+ select MFD_MC13783
help
- Support for the Freescale (Atlas) MC13783 PMIC and audio CODEC.
+ Support for the Freescale (Atlas) PMIC and audio CODECs
+ MC13783 and MC13892.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
@@ -433,7 +455,7 @@ config PCF50633_GPIO
config ABX500_CORE
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
- default y if ARCH_U300
+ default y if ARCH_U300 || ARCH_U8500
help
Say yes here if you have the ABX500 Mixed Signal IC family
chips. This core driver expose register access functions.
@@ -444,6 +466,7 @@ config ABX500_CORE
config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y && ABX500_CORE
+ select MFD_CORE
default y if ARCH_U300
help
Select this to enable the AB3100 Mixed Signal IC core
@@ -473,14 +496,33 @@ config EZX_PCAP
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
- depends on SPI=y && GENERIC_HARDIRQS
+ depends on GENERIC_HARDIRQS && ABX500_CORE && SPI_MASTER && ARCH_U8500
select MFD_CORE
help
Select this option to enable access to AB8500 power management
- chip. This connects to U8500 on the SSP/SPI bus and exports
- read/write functions for the devices to get access to this chip.
+ chip. This connects to U8500 either on the SSP/SPI bus
+ or the I2C bus via PRCMU. It also adds the irq_chip
+ parts for handling the Mixed Signal chip events.
This chip embeds various other multimedia funtionalities as well.
+config AB8500_I2C_CORE
+ bool "AB8500 register access via PRCMU I2C"
+ depends on AB8500_CORE && UX500_SOC_DB8500
+ default y
+ help
+ This enables register access to the AB8500 chip via PRCMU I2C.
+ The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
+ the I2C bus is connected to the Power Reset
+ and Mangagement Unit, PRCMU.
+
+config AB8500_DEBUG
+ bool "Enable debug info via debugfs"
+ depends on AB8500_CORE && DEBUG_FS
+ default y if DEBUG_FS
+ help
+ Select this option if you want debug information using the debug
+ filesystem, debugfs.
+
config AB3550_CORE
bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
select MFD_CORE
@@ -542,8 +584,8 @@ config MFD_JZ4740_ADC
This driver is necessary for jz4740-battery and jz4740-hwmon driver.
config MFD_TPS6586X
- tristate "TPS6586x Power Management chips"
- depends on I2C && GPIOLIB
+ bool "TPS6586x Power Management chips"
+ depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
select MFD_CORE
help
If you say yes here you get support for the TPS6586X series of
@@ -555,6 +597,15 @@ config MFD_TPS6586X
This driver can also be built as a module. If so, the module
will be called tps6586x.
+config MFD_VX855
+ tristate "Support for VIA VX855/VX875 integrated south bridge"
+ depends on PCI
+ select MFD_CORE
+ help
+ Say yes here to enable support for various functions of the
+ VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
+ and/or vx855_gpio drivers for this to do anything useful.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index feaeeaeeddb7..f54b3659abbb 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -24,6 +24,8 @@ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
obj-$(CONFIG_MFD_WM831X) += wm831x.o
+obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o
+obj-$(CONFIG_MFD_WM831X_SPI) += wm831x-spi.o
wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
wm8350-objs += wm8350-irq.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o
@@ -39,7 +41,7 @@ obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
-obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
+obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
@@ -58,7 +60,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
-obj-$(CONFIG_MFD_MAX8998) += max8998.o
+obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
@@ -69,6 +71,8 @@ obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o
+obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o
+obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
@@ -76,3 +80,4 @@ obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
+obj-$(CONFIG_MFD_VX855) += vx855.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index b048ecc56db9..4193af5f2743 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -19,6 +19,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
+#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
/* These are the only registers inside AB3100 used in this main file */
@@ -146,7 +147,7 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
}
static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
- u8 reg, u8 *regval)
+ u8 reg, u8 *regval)
{
int err;
@@ -202,7 +203,7 @@ static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
}
static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 *value)
+ u8 *value)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
@@ -666,7 +667,7 @@ struct ab3100_init_setting {
u8 setting;
};
-static const struct ab3100_init_setting __initconst
+static const struct ab3100_init_setting __devinitconst
ab3100_init_settings[] = {
{
.abreg = AB3100_MCA,
@@ -713,7 +714,7 @@ ab3100_init_settings[] = {
},
};
-static int __init ab3100_setup(struct ab3100 *ab3100)
+static int __devinit ab3100_setup(struct ab3100 *ab3100)
{
int err = 0;
int i;
@@ -743,52 +744,64 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
return err;
}
-/*
- * Here we define all the platform devices that appear
- * as children of the AB3100. These are regular platform
- * devices with the IORESOURCE_IO .start and .end set
- * to correspond to the internal AB3100 register range
- * mapping to the corresponding subdevice.
- */
-
-#define AB3100_DEVICE(devname, devid) \
-static struct platform_device ab3100_##devname##_device = { \
- .name = devid, \
- .id = -1, \
-}
-
-/* This lists all the subdevices */
-AB3100_DEVICE(dac, "ab3100-dac");
-AB3100_DEVICE(leds, "ab3100-leds");
-AB3100_DEVICE(power, "ab3100-power");
-AB3100_DEVICE(regulators, "ab3100-regulators");
-AB3100_DEVICE(sim, "ab3100-sim");
-AB3100_DEVICE(uart, "ab3100-uart");
-AB3100_DEVICE(rtc, "ab3100-rtc");
-AB3100_DEVICE(charger, "ab3100-charger");
-AB3100_DEVICE(boost, "ab3100-boost");
-AB3100_DEVICE(adc, "ab3100-adc");
-AB3100_DEVICE(fuelgauge, "ab3100-fuelgauge");
-AB3100_DEVICE(vibrator, "ab3100-vibrator");
-AB3100_DEVICE(otp, "ab3100-otp");
-AB3100_DEVICE(codec, "ab3100-codec");
-
-static struct platform_device *
-ab3100_platform_devs[] = {
- &ab3100_dac_device,
- &ab3100_leds_device,
- &ab3100_power_device,
- &ab3100_regulators_device,
- &ab3100_sim_device,
- &ab3100_uart_device,
- &ab3100_rtc_device,
- &ab3100_charger_device,
- &ab3100_boost_device,
- &ab3100_adc_device,
- &ab3100_fuelgauge_device,
- &ab3100_vibrator_device,
- &ab3100_otp_device,
- &ab3100_codec_device,
+/* The subdevices of the AB3100 */
+static struct mfd_cell ab3100_devs[] = {
+ {
+ .name = "ab3100-dac",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-leds",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-power",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-regulators",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-sim",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-uart",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-rtc",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-charger",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-boost",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-adc",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-fuelgauge",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-vibrator",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-otp",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-codec",
+ .id = -1,
+ },
};
struct ab_family_id {
@@ -796,7 +809,7 @@ struct ab_family_id {
char *name;
};
-static const struct ab_family_id ids[] __initdata = {
+static const struct ab_family_id ids[] __devinitdata = {
/* AB3100 */
{
.id = 0xc0,
@@ -850,8 +863,8 @@ static const struct ab_family_id ids[] __initdata = {
},
};
-static int __init ab3100_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int __devinit ab3100_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct ab3100 *ab3100;
struct ab3100_platform_data *ab3100_plf_data =
@@ -935,18 +948,14 @@ static int __init ab3100_probe(struct i2c_client *client,
if (err)
goto exit_no_ops;
- /* Set parent and a pointer back to the container in device data */
- for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
- ab3100_platform_devs[i]->dev.parent =
- &client->dev;
- ab3100_platform_devs[i]->dev.platform_data =
- ab3100_plf_data;
- platform_set_drvdata(ab3100_platform_devs[i], ab3100);
+ /* Set up and register the platform devices. */
+ for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) {
+ ab3100_devs[i].platform_data = ab3100_plf_data;
+ ab3100_devs[i].data_size = sizeof(struct ab3100_platform_data);
}
- /* Register the platform devices */
- platform_add_devices(ab3100_platform_devs,
- ARRAY_SIZE(ab3100_platform_devs));
+ err = mfd_add_devices(&client->dev, 0, ab3100_devs,
+ ARRAY_SIZE(ab3100_devs), NULL, 0);
ab3100_setup_debugfs(ab3100);
@@ -962,14 +971,12 @@ static int __init ab3100_probe(struct i2c_client *client,
return err;
}
-static int __exit ab3100_remove(struct i2c_client *client)
+static int __devexit ab3100_remove(struct i2c_client *client)
{
struct ab3100 *ab3100 = i2c_get_clientdata(client);
- int i;
/* Unregister subdevices */
- for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++)
- platform_device_unregister(ab3100_platform_devs[i]);
+ mfd_remove_devices(&client->dev);
ab3100_remove_debugfs();
i2c_unregister_device(ab3100->testreg_client);
@@ -996,7 +1003,7 @@ static struct i2c_driver ab3100_driver = {
},
.id_table = ab3100_id,
.probe = ab3100_probe,
- .remove = __exit_p(ab3100_remove),
+ .remove = __devexit_p(ab3100_remove),
};
static int __init ab3100_i2c_init(void)
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index defa786dee34..dbe1c93c1af3 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -4,6 +4,7 @@
* License Terms: GNU General Public License v2
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
* Author: Rabin Vincent <rabin.vincent@stericsson.com>
+ * Changes: Mattias Wallin <mattias.wallin@stericsson.com>
*/
#include <linux/kernel.h>
@@ -15,6 +16,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
#include <linux/mfd/ab8500.h>
#include <linux/regulator/ab8500.h>
@@ -22,71 +24,71 @@
* Interrupt register offsets
* Bank : 0x0E
*/
-#define AB8500_IT_SOURCE1_REG 0x0E00
-#define AB8500_IT_SOURCE2_REG 0x0E01
-#define AB8500_IT_SOURCE3_REG 0x0E02
-#define AB8500_IT_SOURCE4_REG 0x0E03
-#define AB8500_IT_SOURCE5_REG 0x0E04
-#define AB8500_IT_SOURCE6_REG 0x0E05
-#define AB8500_IT_SOURCE7_REG 0x0E06
-#define AB8500_IT_SOURCE8_REG 0x0E07
-#define AB8500_IT_SOURCE19_REG 0x0E12
-#define AB8500_IT_SOURCE20_REG 0x0E13
-#define AB8500_IT_SOURCE21_REG 0x0E14
-#define AB8500_IT_SOURCE22_REG 0x0E15
-#define AB8500_IT_SOURCE23_REG 0x0E16
-#define AB8500_IT_SOURCE24_REG 0x0E17
+#define AB8500_IT_SOURCE1_REG 0x00
+#define AB8500_IT_SOURCE2_REG 0x01
+#define AB8500_IT_SOURCE3_REG 0x02
+#define AB8500_IT_SOURCE4_REG 0x03
+#define AB8500_IT_SOURCE5_REG 0x04
+#define AB8500_IT_SOURCE6_REG 0x05
+#define AB8500_IT_SOURCE7_REG 0x06
+#define AB8500_IT_SOURCE8_REG 0x07
+#define AB8500_IT_SOURCE19_REG 0x12
+#define AB8500_IT_SOURCE20_REG 0x13
+#define AB8500_IT_SOURCE21_REG 0x14
+#define AB8500_IT_SOURCE22_REG 0x15
+#define AB8500_IT_SOURCE23_REG 0x16
+#define AB8500_IT_SOURCE24_REG 0x17
/*
* latch registers
*/
-#define AB8500_IT_LATCH1_REG 0x0E20
-#define AB8500_IT_LATCH2_REG 0x0E21
-#define AB8500_IT_LATCH3_REG 0x0E22
-#define AB8500_IT_LATCH4_REG 0x0E23
-#define AB8500_IT_LATCH5_REG 0x0E24
-#define AB8500_IT_LATCH6_REG 0x0E25
-#define AB8500_IT_LATCH7_REG 0x0E26
-#define AB8500_IT_LATCH8_REG 0x0E27
-#define AB8500_IT_LATCH9_REG 0x0E28
-#define AB8500_IT_LATCH10_REG 0x0E29
-#define AB8500_IT_LATCH19_REG 0x0E32
-#define AB8500_IT_LATCH20_REG 0x0E33
-#define AB8500_IT_LATCH21_REG 0x0E34
-#define AB8500_IT_LATCH22_REG 0x0E35
-#define AB8500_IT_LATCH23_REG 0x0E36
-#define AB8500_IT_LATCH24_REG 0x0E37
+#define AB8500_IT_LATCH1_REG 0x20
+#define AB8500_IT_LATCH2_REG 0x21
+#define AB8500_IT_LATCH3_REG 0x22
+#define AB8500_IT_LATCH4_REG 0x23
+#define AB8500_IT_LATCH5_REG 0x24
+#define AB8500_IT_LATCH6_REG 0x25
+#define AB8500_IT_LATCH7_REG 0x26
+#define AB8500_IT_LATCH8_REG 0x27
+#define AB8500_IT_LATCH9_REG 0x28
+#define AB8500_IT_LATCH10_REG 0x29
+#define AB8500_IT_LATCH19_REG 0x32
+#define AB8500_IT_LATCH20_REG 0x33
+#define AB8500_IT_LATCH21_REG 0x34
+#define AB8500_IT_LATCH22_REG 0x35
+#define AB8500_IT_LATCH23_REG 0x36
+#define AB8500_IT_LATCH24_REG 0x37
/*
* mask registers
*/
-#define AB8500_IT_MASK1_REG 0x0E40
-#define AB8500_IT_MASK2_REG 0x0E41
-#define AB8500_IT_MASK3_REG 0x0E42
-#define AB8500_IT_MASK4_REG 0x0E43
-#define AB8500_IT_MASK5_REG 0x0E44
-#define AB8500_IT_MASK6_REG 0x0E45
-#define AB8500_IT_MASK7_REG 0x0E46
-#define AB8500_IT_MASK8_REG 0x0E47
-#define AB8500_IT_MASK9_REG 0x0E48
-#define AB8500_IT_MASK10_REG 0x0E49
-#define AB8500_IT_MASK11_REG 0x0E4A
-#define AB8500_IT_MASK12_REG 0x0E4B
-#define AB8500_IT_MASK13_REG 0x0E4C
-#define AB8500_IT_MASK14_REG 0x0E4D
-#define AB8500_IT_MASK15_REG 0x0E4E
-#define AB8500_IT_MASK16_REG 0x0E4F
-#define AB8500_IT_MASK17_REG 0x0E50
-#define AB8500_IT_MASK18_REG 0x0E51
-#define AB8500_IT_MASK19_REG 0x0E52
-#define AB8500_IT_MASK20_REG 0x0E53
-#define AB8500_IT_MASK21_REG 0x0E54
-#define AB8500_IT_MASK22_REG 0x0E55
-#define AB8500_IT_MASK23_REG 0x0E56
-#define AB8500_IT_MASK24_REG 0x0E57
-
-#define AB8500_REV_REG 0x1080
+#define AB8500_IT_MASK1_REG 0x40
+#define AB8500_IT_MASK2_REG 0x41
+#define AB8500_IT_MASK3_REG 0x42
+#define AB8500_IT_MASK4_REG 0x43
+#define AB8500_IT_MASK5_REG 0x44
+#define AB8500_IT_MASK6_REG 0x45
+#define AB8500_IT_MASK7_REG 0x46
+#define AB8500_IT_MASK8_REG 0x47
+#define AB8500_IT_MASK9_REG 0x48
+#define AB8500_IT_MASK10_REG 0x49
+#define AB8500_IT_MASK11_REG 0x4A
+#define AB8500_IT_MASK12_REG 0x4B
+#define AB8500_IT_MASK13_REG 0x4C
+#define AB8500_IT_MASK14_REG 0x4D
+#define AB8500_IT_MASK15_REG 0x4E
+#define AB8500_IT_MASK16_REG 0x4F
+#define AB8500_IT_MASK17_REG 0x50
+#define AB8500_IT_MASK18_REG 0x51
+#define AB8500_IT_MASK19_REG 0x52
+#define AB8500_IT_MASK20_REG 0x53
+#define AB8500_IT_MASK21_REG 0x54
+#define AB8500_IT_MASK22_REG 0x55
+#define AB8500_IT_MASK23_REG 0x56
+#define AB8500_IT_MASK24_REG 0x57
+
+#define AB8500_REV_REG 0x80
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
@@ -99,96 +101,132 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21,
};
-static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_get_chip_id(struct device *dev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+ return (int)ab8500->chip_id;
+}
+
+static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
+ u8 reg, u8 data)
{
int ret;
+ /*
+ * Put the u8 bank and u8 register together into a an u16.
+ * The bank on higher 8 bits and register in lower 8 bits.
+ * */
+ u16 addr = ((u16)bank) << 8 | reg;
dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
+ ret = mutex_lock_interruptible(&ab8500->lock);
+ if (ret)
+ return ret;
+
ret = ab8500->write(ab8500, addr, data);
if (ret < 0)
dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
addr, ret);
+ mutex_unlock(&ab8500->lock);
return ret;
}
-/**
- * ab8500_write() - write an AB8500 register
- * @ab8500: device to write to
- * @addr: address of the register
- * @data: value to write
- */
-int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_set_register(struct device *dev, u8 bank,
+ u8 reg, u8 value)
{
- int ret;
-
- mutex_lock(&ab8500->lock);
- ret = __ab8500_write(ab8500, addr, data);
- mutex_unlock(&ab8500->lock);
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
- return ret;
+ return set_register_interruptible(ab8500, bank, reg, value);
}
-EXPORT_SYMBOL_GPL(ab8500_write);
-static int __ab8500_read(struct ab8500 *ab8500, u16 addr)
+static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
+ u8 reg, u8 *value)
{
int ret;
+ /* put the u8 bank and u8 reg together into a an u16.
+ * bank on higher 8 bits and reg in lower */
+ u16 addr = ((u16)bank) << 8 | reg;
+
+ ret = mutex_lock_interruptible(&ab8500->lock);
+ if (ret)
+ return ret;
ret = ab8500->read(ab8500, addr);
if (ret < 0)
dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
addr, ret);
+ else
+ *value = ret;
+ mutex_unlock(&ab8500->lock);
dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret);
return ret;
}
-/**
- * ab8500_read() - read an AB8500 register
- * @ab8500: device to read from
- * @addr: address of the register
- */
-int ab8500_read(struct ab8500 *ab8500, u16 addr)
+static int ab8500_get_register(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
{
- int ret;
-
- mutex_lock(&ab8500->lock);
- ret = __ab8500_read(ab8500, addr);
- mutex_unlock(&ab8500->lock);
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
- return ret;
+ return get_register_interruptible(ab8500, bank, reg, value);
}
-EXPORT_SYMBOL_GPL(ab8500_read);
-
-/**
- * ab8500_set_bits() - set a bitfield in an AB8500 register
- * @ab8500: device to read from
- * @addr: address of the register
- * @mask: mask of the bitfield to modify
- * @data: value to set to the bitfield
- */
-int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data)
+
+static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues)
{
int ret;
+ u8 data;
+ /* put the u8 bank and u8 reg together into a an u16.
+ * bank on higher 8 bits and reg in lower */
+ u16 addr = ((u16)bank) << 8 | reg;
- mutex_lock(&ab8500->lock);
+ ret = mutex_lock_interruptible(&ab8500->lock);
+ if (ret)
+ return ret;
- ret = __ab8500_read(ab8500, addr);
- if (ret < 0)
+ ret = ab8500->read(ab8500, addr);
+ if (ret < 0) {
+ dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
+ addr, ret);
goto out;
+ }
- ret &= ~mask;
- ret |= data;
+ data = (u8)ret;
+ data = (~bitmask & data) | (bitmask & bitvalues);
- ret = __ab8500_write(ab8500, addr, ret);
+ ret = ab8500->write(ab8500, addr, data);
+ if (ret < 0)
+ dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
+ addr, ret);
+ dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data);
out:
mutex_unlock(&ab8500->lock);
return ret;
}
-EXPORT_SYMBOL_GPL(ab8500_set_bits);
+
+static int ab8500_mask_and_set_register(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+
+ return mask_and_set_register_interruptible(ab8500, bank, reg,
+ bitmask, bitvalues);
+
+}
+
+static struct abx500_ops ab8500_ops = {
+ .get_chip_id = ab8500_get_chip_id,
+ .get_register = ab8500_get_register,
+ .set_register = ab8500_set_register,
+ .get_register_page = NULL,
+ .set_register_page = NULL,
+ .mask_and_set_register = ab8500_mask_and_set_register,
+ .event_registers_startup_state_get = NULL,
+ .startup_irq_enabled = NULL,
+};
static void ab8500_irq_lock(unsigned int irq)
{
@@ -213,7 +251,7 @@ static void ab8500_irq_sync_unlock(unsigned int irq)
ab8500->oldmask[i] = new;
reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
- ab8500_write(ab8500, reg, new);
+ set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
}
mutex_unlock(&ab8500->irq_lock);
@@ -257,9 +295,11 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
int regoffset = ab8500_irq_regoffset[i];
int status;
+ u8 value;
- status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset);
- if (status <= 0)
+ status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_LATCH1_REG + regoffset, &value);
+ if (status < 0 || value == 0)
continue;
do {
@@ -267,8 +307,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
int line = i * 8 + bit;
handle_nested_irq(ab8500->irq_base + line);
- status &= ~(1 << bit);
- } while (status);
+ value &= ~(1 << bit);
+ } while (value);
}
return IRQ_HANDLED;
@@ -338,7 +378,27 @@ static struct resource ab8500_rtc_resources[] = {
},
};
+static struct resource ab8500_poweronkey_db_resources[] = {
+ {
+ .name = "ONKEY_DBF",
+ .start = AB8500_INT_PON_KEY1DB_F,
+ .end = AB8500_INT_PON_KEY1DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ONKEY_DBR",
+ .start = AB8500_INT_PON_KEY1DB_R,
+ .end = AB8500_INT_PON_KEY1DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct mfd_cell ab8500_devs[] = {
+#ifdef CONFIG_DEBUG_FS
+ {
+ .name = "ab8500-debug",
+ },
+#endif
{
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
@@ -349,11 +409,27 @@ static struct mfd_cell ab8500_devs[] = {
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
+ {
+ .name = "ab8500-pwm",
+ .id = 1,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 2,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 3,
+ },
{ .name = "ab8500-charger", },
{ .name = "ab8500-audio", },
{ .name = "ab8500-usb", },
- { .name = "ab8500-pwm", },
{ .name = "ab8500-regulator", },
+ {
+ .name = "ab8500-poweron-key",
+ .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+ .resources = ab8500_poweronkey_db_resources,
+ },
};
int __devinit ab8500_init(struct ab8500 *ab8500)
@@ -361,6 +437,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
int ret;
int i;
+ u8 value;
if (plat)
ab8500->irq_base = plat->irq_base;
@@ -368,7 +445,8 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
mutex_init(&ab8500->lock);
mutex_init(&ab8500->irq_lock);
- ret = ab8500_read(ab8500, AB8500_REV_REG);
+ ret = get_register_interruptible(ab8500, AB8500_MISC,
+ AB8500_REV_REG, &value);
if (ret < 0)
return ret;
@@ -377,28 +455,37 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
* 0x10 - Cut 1.0
* 0x11 - Cut 1.1
*/
- if (ret == 0x0 || ret == 0x10 || ret == 0x11) {
- ab8500->revision = ret;
- dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret);
+ if (value == 0x0 || value == 0x10 || value == 0x11) {
+ ab8500->revision = value;
+ dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
} else {
- dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret);
+ dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value);
return -EINVAL;
}
+ ab8500->chip_id = value;
if (plat && plat->init)
plat->init(ab8500);
/* Clear and mask all interrupts */
for (i = 0; i < 10; i++) {
- ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
- ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
+ get_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_LATCH1_REG + i, &value);
+ set_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_MASK1_REG + i, 0xff);
}
for (i = 18; i < 24; i++) {
- ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
- ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
+ get_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_LATCH1_REG + i, &value);
+ set_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_MASK1_REG + i, 0xff);
}
+ ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
+ if (ret)
+ return ret;
+
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
new file mode 100644
index 000000000000..8d1e05a39815
--- /dev/null
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500.h>
+
+static u32 debug_bank;
+static u32 debug_address;
+
+/**
+ * struct ab8500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab8500_reg_range {
+ u8 first;
+ u8 last;
+ u8 perm;
+};
+
+/**
+ * struct ab8500_i2c_ranges
+ * @num_ranges: the number of ranges in the list
+ * @bankid: bank identifier
+ * @range: the list of register ranges
+ */
+struct ab8500_i2c_ranges {
+ u8 num_ranges;
+ u8 bankid;
+ const struct ab8500_reg_range *range;
+};
+
+#define AB8500_NAME_STRING "ab8500"
+#define AB8500_NUM_BANKS 22
+
+#define AB8500_REV_REG 0x80
+
+static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
+ [0x0] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [AB8500_SYS_CTRL1_BLOCK] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x02,
+ },
+ {
+ .first = 0x42,
+ .last = 0x42,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ },
+ },
+ },
+ [AB8500_SYS_CTRL2_BLOCK] = {
+ .num_ranges = 4,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0D,
+ },
+ {
+ .first = 0x0F,
+ .last = 0x17,
+ },
+ {
+ .first = 0x30,
+ .last = 0x30,
+ },
+ {
+ .first = 0x32,
+ .last = 0x33,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL1] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x03,
+ .last = 0x10,
+ },
+ {
+ .first = 0x80,
+ .last = 0x84,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL2] = {
+ .num_ranges = 5,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x15,
+ },
+ {
+ .first = 0x17,
+ .last = 0x19,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x1D,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ },
+ /* 0x80-0x8B is SIM registers and should
+ * not be accessed from here */
+ },
+ },
+ [AB8500_USB] = {
+ .num_ranges = 2,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x83,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ },
+ },
+ },
+ [AB8500_TVOUT] = {
+ .num_ranges = 9,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x12,
+ },
+ {
+ .first = 0x15,
+ .last = 0x17,
+ },
+ {
+ .first = 0x19,
+ .last = 0x21,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ },
+ },
+ },
+ [AB8500_DBI] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [AB8500_ECI_AV_ACC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ },
+ },
+ [0x9] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [AB8500_GPADC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ },
+ },
+ },
+ [AB8500_CHARGER] = {
+ .num_ranges = 8,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x03,
+ },
+ {
+ .first = 0x05,
+ .last = 0x05,
+ },
+ {
+ .first = 0x40,
+ .last = 0x40,
+ },
+ {
+ .first = 0x42,
+ .last = 0x42,
+ },
+ {
+ .first = 0x44,
+ .last = 0x44,
+ },
+ {
+ .first = 0x50,
+ .last = 0x55,
+ },
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ {
+ .first = 0xC0,
+ .last = 0xC2,
+ },
+ },
+ },
+ [AB8500_GAS_GAUGE] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x07,
+ .last = 0x0A,
+ },
+ {
+ .first = 0x10,
+ .last = 0x14,
+ },
+ },
+ },
+ [AB8500_AUDIO] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x6F,
+ },
+ },
+ },
+ [AB8500_INTERRUPT] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [AB8500_RTC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0F,
+ },
+ },
+ },
+ [AB8500_MISC] = {
+ .num_ranges = 8,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x05,
+ },
+ {
+ .first = 0x10,
+ .last = 0x15,
+ },
+ {
+ .first = 0x20,
+ .last = 0x25,
+ },
+ {
+ .first = 0x30,
+ .last = 0x35,
+ },
+ {
+ .first = 0x40,
+ .last = 0x45,
+ },
+ {
+ .first = 0x50,
+ .last = 0x50,
+ },
+ {
+ .first = 0x60,
+ .last = 0x67,
+ },
+ {
+ .first = 0x80,
+ .last = 0x80,
+ },
+ },
+ },
+ [0x11] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [0x12] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [0x13] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [0x14] = {
+ .num_ranges = 0,
+ .range = 0,
+ },
+ [AB8500_OTP_EMUL] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x0F,
+ },
+ },
+ },
+};
+
+static int ab8500_registers_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ unsigned int i;
+ u32 bank = debug_bank;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ seq_printf(s, " bank %u:\n", bank);
+ for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
+ u32 reg;
+
+ for (reg = debug_ranges[bank].range[i].first;
+ reg <= debug_ranges[bank].range[i].last;
+ reg++) {
+ u8 value;
+ int err;
+
+ err = abx500_get_register_interruptible(dev,
+ (u8)bank, (u8)reg, &value);
+ if (err < 0) {
+ dev_err(dev, "ab->read fail %d\n", err);
+ return err;
+ }
+
+ err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
+ reg, value);
+ if (err < 0) {
+ dev_err(dev, "seq_printf overflow\n");
+ /* Error is not returned here since
+ * the output is wanted in any case */
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int ab8500_registers_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_registers_fops = {
+ .open = ab8500_registers_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_bank_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%d\n", debug_bank);
+}
+
+static int ab8500_bank_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_bank_print, inode->i_private);
+}
+
+static ssize_t ab8500_bank_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_bank;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_bank);
+ if (err)
+ return -EINVAL;
+
+ if (user_bank >= AB8500_NUM_BANKS) {
+ dev_err(dev, "debugfs error input > number of banks\n");
+ return -EINVAL;
+ }
+
+ debug_bank = user_bank;
+
+ return buf_size;
+}
+
+static int ab8500_address_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "0x%02X\n", debug_address);
+}
+
+static int ab8500_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_address_print, inode->i_private);
+}
+
+static ssize_t ab8500_address_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_address;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_address);
+ if (err)
+ return -EINVAL;
+ if (user_address > 0xff) {
+ dev_err(dev, "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ debug_address = user_address;
+ return buf_size;
+}
+
+static int ab8500_val_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int ret;
+ u8 regvalue;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)debug_bank, (u8)debug_address, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+ seq_printf(s, "0x%02X\n", regvalue);
+
+ return 0;
+}
+
+static int ab8500_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_val_print, inode->i_private);
+}
+
+static ssize_t ab8500_val_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val > 0xff) {
+ dev_err(dev, "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ err = abx500_set_register_interruptible(dev,
+ (u8)debug_bank, debug_address, (u8)user_val);
+ if (err < 0) {
+ printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
+ return -EINVAL;
+ }
+
+ return buf_size;
+}
+
+static const struct file_operations ab8500_bank_fops = {
+ .open = ab8500_bank_open,
+ .write = ab8500_bank_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_address_fops = {
+ .open = ab8500_address_open,
+ .write = ab8500_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_val_fops = {
+ .open = ab8500_val_open,
+ .write = ab8500_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_dir;
+static struct dentry *ab8500_reg_file;
+static struct dentry *ab8500_bank_file;
+static struct dentry *ab8500_address_file;
+static struct dentry *ab8500_val_file;
+
+static int __devinit ab8500_debug_probe(struct platform_device *plf)
+{
+ debug_bank = AB8500_MISC;
+ debug_address = AB8500_REV_REG & 0x00FF;
+
+ ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
+ if (!ab8500_dir)
+ goto exit_no_debugfs;
+
+ ab8500_reg_file = debugfs_create_file("all-bank-registers",
+ S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
+ if (!ab8500_reg_file)
+ goto exit_destroy_dir;
+
+ ab8500_bank_file = debugfs_create_file("register-bank",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops);
+ if (!ab8500_bank_file)
+ goto exit_destroy_reg;
+
+ ab8500_address_file = debugfs_create_file("register-address",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
+ &ab8500_address_fops);
+ if (!ab8500_address_file)
+ goto exit_destroy_bank;
+
+ ab8500_val_file = debugfs_create_file("register-value",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops);
+ if (!ab8500_val_file)
+ goto exit_destroy_address;
+
+ return 0;
+
+exit_destroy_address:
+ debugfs_remove(ab8500_address_file);
+exit_destroy_bank:
+ debugfs_remove(ab8500_bank_file);
+exit_destroy_reg:
+ debugfs_remove(ab8500_reg_file);
+exit_destroy_dir:
+ debugfs_remove(ab8500_dir);
+exit_no_debugfs:
+ dev_err(&plf->dev, "failed to create debugfs entries.\n");
+ return -ENOMEM;
+}
+
+static int __devexit ab8500_debug_remove(struct platform_device *plf)
+{
+ debugfs_remove(ab8500_val_file);
+ debugfs_remove(ab8500_address_file);
+ debugfs_remove(ab8500_bank_file);
+ debugfs_remove(ab8500_reg_file);
+ debugfs_remove(ab8500_dir);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_debug_driver = {
+ .driver = {
+ .name = "ab8500-debug",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_debug_probe,
+ .remove = __devexit_p(ab8500_debug_remove)
+};
+
+static int __init ab8500_debug_init(void)
+{
+ return platform_driver_register(&ab8500_debug_driver);
+}
+
+static void __exit ab8500_debug_exit(void)
+{
+ platform_driver_unregister(&ab8500_debug_driver);
+}
+subsys_initcall(ab8500_debug_init);
+module_exit(ab8500_debug_exit);
+
+MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
+MODULE_DESCRIPTION("AB8500 DEBUG");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c
new file mode 100644
index 000000000000..6820327adf4a
--- /dev/null
+++ b/drivers/mfd/ab8500-i2c.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
+ * License Terms: GNU General Public License v2
+ * This file was based on drivers/mfd/ab8500-spi.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ab8500.h>
+
+#include <mach/prcmu.h>
+
+static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+ int ret;
+
+ ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+ if (ret < 0)
+ dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+ return ret;
+}
+
+static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+{
+ int ret;
+ u8 data;
+
+ ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+ if (ret < 0) {
+ dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+ return ret;
+ }
+ return (int)data;
+}
+
+static int __devinit ab8500_i2c_probe(struct platform_device *plf)
+{
+ struct ab8500 *ab8500;
+ struct resource *resource;
+ int ret;
+
+ ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
+ if (!ab8500)
+ return -ENOMEM;
+
+ ab8500->dev = &plf->dev;
+
+ resource = platform_get_resource(plf, IORESOURCE_IRQ, 0);
+ if (!resource) {
+ kfree(ab8500);
+ return -ENODEV;
+ }
+
+ ab8500->irq = resource->start;
+
+ ab8500->read = ab8500_i2c_read;
+ ab8500->write = ab8500_i2c_write;
+
+ platform_set_drvdata(plf, ab8500);
+
+ ret = ab8500_init(ab8500);
+ if (ret)
+ kfree(ab8500);
+
+ return ret;
+}
+
+static int __devexit ab8500_i2c_remove(struct platform_device *plf)
+{
+ struct ab8500 *ab8500 = platform_get_drvdata(plf);
+
+ ab8500_exit(ab8500);
+ kfree(ab8500);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_i2c_driver = {
+ .driver = {
+ .name = "ab8500-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_i2c_probe,
+ .remove = __devexit_p(ab8500_i2c_remove)
+};
+
+static int __init ab8500_i2c_init(void)
+{
+ return platform_driver_register(&ab8500_i2c_driver);
+}
+
+static void __exit ab8500_i2c_exit(void)
+{
+ platform_driver_unregister(&ab8500_i2c_driver);
+}
+subsys_initcall(ab8500_i2c_init);
+module_exit(ab8500_i2c_exit);
+
+MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
+MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
index 01b6d584442c..b1653421edb5 100644
--- a/drivers/mfd/ab8500-spi.c
+++ b/drivers/mfd/ab8500-spi.c
@@ -119,7 +119,7 @@ static int __devexit ab8500_spi_remove(struct spi_device *spi)
static struct spi_driver ab8500_spi_driver = {
.driver = {
- .name = "ab8500",
+ .name = "ab8500-spi",
.owner = THIS_MODULE,
},
.probe = ab8500_spi_probe,
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index c07aece900fb..2fadbaeb1cb1 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -470,13 +470,19 @@ static int __devinit da903x_add_subdevs(struct da903x_chip *chip,
subdev = &pdata->subdevs[i];
pdev = platform_device_alloc(subdev->name, subdev->id);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto failed;
+ }
pdev->dev.parent = chip->dev;
pdev->dev.platform_data = subdev->platform_data;
ret = platform_device_add(pdev);
- if (ret)
+ if (ret) {
+ platform_device_put(pdev);
goto failed;
+ }
}
return 0;
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index 134c69aa4790..c2b698d69a93 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -384,12 +384,20 @@ static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
struct pcap_subdev *subdev)
{
struct platform_device *pdev;
+ int ret;
pdev = platform_device_alloc(subdev->name, subdev->id);
+ if (!pdev)
+ return -ENOMEM;
+
pdev->dev.parent = &pcap->spi->dev;
pdev->dev.platform_data = subdev->platform_data;
- return platform_device_add(pdev);
+ ret = platform_device_add(pdev);
+ if (ret)
+ platform_device_put(pdev);
+
+ return ret;
}
static int __devexit ezx_pcap_remove(struct spi_device *spi)
@@ -457,6 +465,7 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
pcap->irq_base = pdata->irq_base;
pcap->workqueue = create_singlethread_workqueue("pcapd");
if (!pcap->workqueue) {
+ ret = -ENOMEM;
dev_err(&spi->dev, "cant create pcap thread\n");
goto free_pcap;
}
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index f04300e05fd6..7bc752272dc1 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -138,13 +138,6 @@ static int __init pasic3_probe(struct platform_device *pdev)
irq = r->start;
}
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (r) {
- ds1wm_resources[1].flags = IORESOURCE_IRQ | (r->flags &
- (IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE));
- irq = r->start;
- }
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENXIO;
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
index 3ad492cb6c41..9dd1b33f2275 100644
--- a/drivers/mfd/jz4740-adc.c
+++ b/drivers/mfd/jz4740-adc.c
@@ -153,7 +153,7 @@ static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
if (enabled)
val |= BIT(engine);
else
- val &= BIT(engine);
+ val &= ~BIT(engine);
writeb(val, adc->base + JZ_REG_ADC_ENABLE);
spin_unlock_irqrestore(&adc->lock, flags);
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index 428377a5a6f5..44695f5a1800 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -93,8 +93,13 @@ static struct mfd_cell rtc_devs[] = {
static struct resource onkey_resources[] = {
{
.name = "max8925-onkey",
- .start = MAX8925_IRQ_GPM_SW_3SEC,
- .end = MAX8925_IRQ_GPM_SW_3SEC,
+ .start = MAX8925_IRQ_GPM_SW_R,
+ .end = MAX8925_IRQ_GPM_SW_R,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .name = "max8925-onkey",
+ .start = MAX8925_IRQ_GPM_SW_F,
+ .end = MAX8925_IRQ_GPM_SW_F,
.flags = IORESOURCE_IRQ,
},
};
@@ -102,7 +107,7 @@ static struct resource onkey_resources[] = {
static struct mfd_cell onkey_devs[] = {
{
.name = "max8925-onkey",
- .num_resources = 1,
+ .num_resources = 2,
.resources = &onkey_resources[0],
.id = -1,
},
diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c
new file mode 100644
index 000000000000..45bfe77b639b
--- /dev/null
+++ b/drivers/mfd/max8998-irq.c
@@ -0,0 +1,258 @@
+/*
+ * Interrupt controller support for MAX8998
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_irq_data {
+ int reg;
+ int mask;
+};
+
+static struct max8998_irq_data max8998_irqs[] = {
+ [MAX8998_IRQ_DCINF] = {
+ .reg = 1,
+ .mask = MAX8998_IRQ_DCINF_MASK,
+ },
+ [MAX8998_IRQ_DCINR] = {
+ .reg = 1,
+ .mask = MAX8998_IRQ_DCINR_MASK,
+ },
+ [MAX8998_IRQ_JIGF] = {
+ .reg = 1,
+ .mask = MAX8998_IRQ_JIGF_MASK,
+ },
+ [MAX8998_IRQ_JIGR] = {
+ .reg = 1,
+ .mask = MAX8998_IRQ_JIGR_MASK,
+ },
+ [MAX8998_IRQ_PWRONF] = {
+ .reg = 1,
+ .mask = MAX8998_IRQ_PWRONF_MASK,
+ },
+ [MAX8998_IRQ_PWRONR] = {
+ .reg = 1,
+ .mask = MAX8998_IRQ_PWRONR_MASK,
+ },
+ [MAX8998_IRQ_WTSREVNT] = {
+ .reg = 2,
+ .mask = MAX8998_IRQ_WTSREVNT_MASK,
+ },
+ [MAX8998_IRQ_SMPLEVNT] = {
+ .reg = 2,
+ .mask = MAX8998_IRQ_SMPLEVNT_MASK,
+ },
+ [MAX8998_IRQ_ALARM1] = {
+ .reg = 2,
+ .mask = MAX8998_IRQ_ALARM1_MASK,
+ },
+ [MAX8998_IRQ_ALARM0] = {
+ .reg = 2,
+ .mask = MAX8998_IRQ_ALARM0_MASK,
+ },
+ [MAX8998_IRQ_ONKEY1S] = {
+ .reg = 3,
+ .mask = MAX8998_IRQ_ONKEY1S_MASK,
+ },
+ [MAX8998_IRQ_TOPOFFR] = {
+ .reg = 3,
+ .mask = MAX8998_IRQ_TOPOFFR_MASK,
+ },
+ [MAX8998_IRQ_DCINOVPR] = {
+ .reg = 3,
+ .mask = MAX8998_IRQ_DCINOVPR_MASK,
+ },
+ [MAX8998_IRQ_CHGRSTF] = {
+ .reg = 3,
+ .mask = MAX8998_IRQ_CHGRSTF_MASK,
+ },
+ [MAX8998_IRQ_DONER] = {
+ .reg = 3,
+ .mask = MAX8998_IRQ_DONER_MASK,
+ },
+ [MAX8998_IRQ_CHGFAULT] = {
+ .reg = 3,
+ .mask = MAX8998_IRQ_CHGFAULT_MASK,
+ },
+ [MAX8998_IRQ_LOBAT1] = {
+ .reg = 4,
+ .mask = MAX8998_IRQ_LOBAT1_MASK,
+ },
+ [MAX8998_IRQ_LOBAT2] = {
+ .reg = 4,
+ .mask = MAX8998_IRQ_LOBAT2_MASK,
+ },
+};
+
+static inline struct max8998_irq_data *
+irq_to_max8998_irq(struct max8998_dev *max8998, int irq)
+{
+ return &max8998_irqs[irq - max8998->irq_base];
+}
+
+static void max8998_irq_lock(unsigned int irq)
+{
+ struct max8998_dev *max8998 = get_irq_chip_data(irq);
+
+ mutex_lock(&max8998->irqlock);
+}
+
+static void max8998_irq_sync_unlock(unsigned int irq)
+{
+ struct max8998_dev *max8998 = get_irq_chip_data(irq);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max8998->irq_masks_cur); i++) {
+ /*
+ * If there's been a change in the mask write it back
+ * to the hardware.
+ */
+ if (max8998->irq_masks_cur[i] != max8998->irq_masks_cache[i]) {
+ max8998->irq_masks_cache[i] = max8998->irq_masks_cur[i];
+ max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i,
+ max8998->irq_masks_cur[i]);
+ }
+ }
+
+ mutex_unlock(&max8998->irqlock);
+}
+
+static void max8998_irq_unmask(unsigned int irq)
+{
+ struct max8998_dev *max8998 = get_irq_chip_data(irq);
+ struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq);
+
+ max8998->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void max8998_irq_mask(unsigned int irq)
+{
+ struct max8998_dev *max8998 = get_irq_chip_data(irq);
+ struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq);
+
+ max8998->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip max8998_irq_chip = {
+ .name = "max8998",
+ .bus_lock = max8998_irq_lock,
+ .bus_sync_unlock = max8998_irq_sync_unlock,
+ .mask = max8998_irq_mask,
+ .unmask = max8998_irq_unmask,
+};
+
+static irqreturn_t max8998_irq_thread(int irq, void *data)
+{
+ struct max8998_dev *max8998 = data;
+ u8 irq_reg[MAX8998_NUM_IRQ_REGS];
+ int ret;
+ int i;
+
+ ret = max8998_bulk_read(max8998->i2c, MAX8998_REG_IRQ1,
+ MAX8998_NUM_IRQ_REGS, irq_reg);
+ if (ret < 0) {
+ dev_err(max8998->dev, "Failed to read interrupt register: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ /* Apply masking */
+ for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++)
+ irq_reg[i] &= ~max8998->irq_masks_cur[i];
+
+ /* Report */
+ for (i = 0; i < MAX8998_IRQ_NR; i++) {
+ if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask)
+ handle_nested_irq(max8998->irq_base + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int max8998_irq_init(struct max8998_dev *max8998)
+{
+ int i;
+ int cur_irq;
+ int ret;
+
+ if (!max8998->irq) {
+ dev_warn(max8998->dev,
+ "No interrupt specified, no interrupts\n");
+ max8998->irq_base = 0;
+ return 0;
+ }
+
+ if (!max8998->irq_base) {
+ dev_err(max8998->dev,
+ "No interrupt base specified, no interrupts\n");
+ return 0;
+ }
+
+ mutex_init(&max8998->irqlock);
+
+ /* Mask the individual interrupt sources */
+ for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++) {
+ max8998->irq_masks_cur[i] = 0xff;
+ max8998->irq_masks_cache[i] = 0xff;
+ max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i, 0xff);
+ }
+
+ max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff);
+ max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff);
+
+ /* register with genirq */
+ for (i = 0; i < MAX8998_IRQ_NR; i++) {
+ cur_irq = i + max8998->irq_base;
+ set_irq_chip_data(cur_irq, max8998);
+ set_irq_chip_and_handler(cur_irq, &max8998_irq_chip,
+ handle_edge_irq);
+ set_irq_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ set_irq_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max8998-irq", max8998);
+ if (ret) {
+ dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
+ max8998->irq, ret);
+ return ret;
+ }
+
+ if (!max8998->ono)
+ return 0;
+
+ ret = request_threaded_irq(max8998->ono, NULL, max8998_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "max8998-ono", max8998);
+ if (ret)
+ dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
+ max8998->ono, ret);
+
+ return 0;
+}
+
+void max8998_irq_exit(struct max8998_dev *max8998)
+{
+ if (max8998->ono)
+ free_irq(max8998->ono, max8998);
+
+ if (max8998->irq)
+ free_irq(max8998->irq, max8998);
+}
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index 73e6f5c4efc9..bb9977bebe78 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -1,5 +1,5 @@
/*
- * max8698.c - mfd core driver for the Maxim 8998
+ * max8998.c - mfd core driver for the Maxim 8998
*
* Copyright (C) 2009-2010 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
@@ -30,19 +30,23 @@
#include <linux/mfd/max8998.h>
#include <linux/mfd/max8998-private.h>
+#define RTC_I2C_ADDR (0x0c >> 1)
+
static struct mfd_cell max8998_devs[] = {
{
.name = "max8998-pmic",
- }
+ }, {
+ .name = "max8998-rtc",
+ },
};
-static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest)
+int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
{
- struct i2c_client *client = max8998->i2c_client;
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
- ret = i2c_smbus_read_byte_data(client, reg);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
mutex_unlock(&max8998->iolock);
if (ret < 0)
return ret;
@@ -51,40 +55,71 @@ static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest
*dest = ret;
return 0;
}
+EXPORT_SYMBOL(max8998_read_reg);
-static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value)
+int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
- struct i2c_client *client = max8998->i2c_client;
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&max8998->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(max8998_bulk_read);
+
+int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
- ret = i2c_smbus_write_byte_data(client, reg, value);
+ ret = i2c_smbus_write_byte_data(i2c, reg, value);
mutex_unlock(&max8998->iolock);
return ret;
}
+EXPORT_SYMBOL(max8998_write_reg);
-static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg,
- u8 val, u8 mask)
+int max8998_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
- struct i2c_client *client = max8998->i2c_client;
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&max8998->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(max8998_bulk_write);
+
+int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
- ret = i2c_smbus_read_byte_data(client, reg);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
if (ret >= 0) {
u8 old_val = ret & 0xff;
u8 new_val = (val & mask) | (old_val & (~mask));
- ret = i2c_smbus_write_byte_data(client, reg, new_val);
- if (ret >= 0)
- ret = 0;
+ ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
}
mutex_unlock(&max8998->iolock);
return ret;
}
+EXPORT_SYMBOL(max8998_update_reg);
static int max8998_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct max8998_platform_data *pdata = i2c->dev.platform_data;
struct max8998_dev *max8998;
int ret = 0;
@@ -94,12 +129,20 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max8998);
max8998->dev = &i2c->dev;
- max8998->i2c_client = i2c;
- max8998->dev_read = max8998_i2c_device_read;
- max8998->dev_write = max8998_i2c_device_write;
- max8998->dev_update = max8998_i2c_device_update;
+ max8998->i2c = i2c;
+ max8998->irq = i2c->irq;
+ max8998->type = id->driver_data;
+ if (pdata) {
+ max8998->ono = pdata->ono;
+ max8998->irq_base = pdata->irq_base;
+ }
mutex_init(&max8998->iolock);
+ max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ i2c_set_clientdata(max8998->rtc, max8998);
+
+ max8998_irq_init(max8998);
+
ret = mfd_add_devices(max8998->dev, -1,
max8998_devs, ARRAY_SIZE(max8998_devs),
NULL, 0);
@@ -110,6 +153,8 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
err:
mfd_remove_devices(max8998->dev);
+ max8998_irq_exit(max8998);
+ i2c_unregister_device(max8998->rtc);
kfree(max8998);
return ret;
}
@@ -119,14 +164,17 @@ static int max8998_i2c_remove(struct i2c_client *i2c)
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
mfd_remove_devices(max8998->dev);
+ max8998_irq_exit(max8998);
+ i2c_unregister_device(max8998->rtc);
kfree(max8998);
return 0;
}
static const struct i2c_device_id max8998_i2c_id[] = {
- { "max8998", 0 },
- { }
+ { "max8998", TYPE_MAX8998 },
+ { "lp3974", TYPE_LP3974},
+ { }
};
MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
deleted file mode 100644
index 6df34989c1f6..000000000000
--- a/drivers/mfd/mc13783-core.c
+++ /dev/null
@@ -1,752 +0,0 @@
-/*
- * Copyright 2009 Pengutronix
- * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
- *
- * loosely based on an earlier driver that has
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
- *
- * 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.
- */
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/interrupt.h>
-#include <linux/spi/spi.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/mc13783.h>
-
-struct mc13783 {
- struct spi_device *spidev;
- struct mutex lock;
- int irq;
- int flags;
-
- irq_handler_t irqhandler[MC13783_NUM_IRQ];
- void *irqdata[MC13783_NUM_IRQ];
-
- /* XXX these should go as platformdata to the regulator subdevice */
- struct mc13783_regulator_init_data *regulators;
- int num_regulators;
-};
-
-#define MC13783_REG_REVISION 7
-#define MC13783_REG_ADC_0 43
-#define MC13783_REG_ADC_1 44
-#define MC13783_REG_ADC_2 45
-
-#define MC13783_IRQSTAT0 0
-#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
-#define MC13783_IRQSTAT0_ADCBISDONEI (1 << 1)
-#define MC13783_IRQSTAT0_TSI (1 << 2)
-#define MC13783_IRQSTAT0_WHIGHI (1 << 3)
-#define MC13783_IRQSTAT0_WLOWI (1 << 4)
-#define MC13783_IRQSTAT0_CHGDETI (1 << 6)
-#define MC13783_IRQSTAT0_CHGOVI (1 << 7)
-#define MC13783_IRQSTAT0_CHGREVI (1 << 8)
-#define MC13783_IRQSTAT0_CHGSHORTI (1 << 9)
-#define MC13783_IRQSTAT0_CCCVI (1 << 10)
-#define MC13783_IRQSTAT0_CHGCURRI (1 << 11)
-#define MC13783_IRQSTAT0_BPONI (1 << 12)
-#define MC13783_IRQSTAT0_LOBATLI (1 << 13)
-#define MC13783_IRQSTAT0_LOBATHI (1 << 14)
-#define MC13783_IRQSTAT0_UDPI (1 << 15)
-#define MC13783_IRQSTAT0_USBI (1 << 16)
-#define MC13783_IRQSTAT0_IDI (1 << 19)
-#define MC13783_IRQSTAT0_SE1I (1 << 21)
-#define MC13783_IRQSTAT0_CKDETI (1 << 22)
-#define MC13783_IRQSTAT0_UDMI (1 << 23)
-
-#define MC13783_IRQMASK0 1
-#define MC13783_IRQMASK0_ADCDONEM MC13783_IRQSTAT0_ADCDONEI
-#define MC13783_IRQMASK0_ADCBISDONEM MC13783_IRQSTAT0_ADCBISDONEI
-#define MC13783_IRQMASK0_TSM MC13783_IRQSTAT0_TSI
-#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
-#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
-#define MC13783_IRQMASK0_CHGDETM MC13783_IRQSTAT0_CHGDETI
-#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
-#define MC13783_IRQMASK0_CHGREVM MC13783_IRQSTAT0_CHGREVI
-#define MC13783_IRQMASK0_CHGSHORTM MC13783_IRQSTAT0_CHGSHORTI
-#define MC13783_IRQMASK0_CCCVM MC13783_IRQSTAT0_CCCVI
-#define MC13783_IRQMASK0_CHGCURRM MC13783_IRQSTAT0_CHGCURRI
-#define MC13783_IRQMASK0_BPONM MC13783_IRQSTAT0_BPONI
-#define MC13783_IRQMASK0_LOBATLM MC13783_IRQSTAT0_LOBATLI
-#define MC13783_IRQMASK0_LOBATHM MC13783_IRQSTAT0_LOBATHI
-#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
-#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
-#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
-#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
-#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
-#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
-
-#define MC13783_IRQSTAT1 3
-#define MC13783_IRQSTAT1_1HZI (1 << 0)
-#define MC13783_IRQSTAT1_TODAI (1 << 1)
-#define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
-#define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
-#define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
-#define MC13783_IRQSTAT1_SYSRSTI (1 << 6)
-#define MC13783_IRQSTAT1_RTCRSTI (1 << 7)
-#define MC13783_IRQSTAT1_PCI (1 << 8)
-#define MC13783_IRQSTAT1_WARMI (1 << 9)
-#define MC13783_IRQSTAT1_MEMHLDI (1 << 10)
-#define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
-#define MC13783_IRQSTAT1_THWARNLI (1 << 12)
-#define MC13783_IRQSTAT1_THWARNHI (1 << 13)
-#define MC13783_IRQSTAT1_CLKI (1 << 14)
-#define MC13783_IRQSTAT1_SEMAFI (1 << 15)
-#define MC13783_IRQSTAT1_MC2BI (1 << 17)
-#define MC13783_IRQSTAT1_HSDETI (1 << 18)
-#define MC13783_IRQSTAT1_HSLI (1 << 19)
-#define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
-#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
-
-#define MC13783_IRQMASK1 4
-#define MC13783_IRQMASK1_1HZM MC13783_IRQSTAT1_1HZI
-#define MC13783_IRQMASK1_TODAM MC13783_IRQSTAT1_TODAI
-#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
-#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
-#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
-#define MC13783_IRQMASK1_SYSRSTM MC13783_IRQSTAT1_SYSRSTI
-#define MC13783_IRQMASK1_RTCRSTM MC13783_IRQSTAT1_RTCRSTI
-#define MC13783_IRQMASK1_PCM MC13783_IRQSTAT1_PCI
-#define MC13783_IRQMASK1_WARMM MC13783_IRQSTAT1_WARMI
-#define MC13783_IRQMASK1_MEMHLDM MC13783_IRQSTAT1_MEMHLDI
-#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
-#define MC13783_IRQMASK1_THWARNLM MC13783_IRQSTAT1_THWARNLI
-#define MC13783_IRQMASK1_THWARNHM MC13783_IRQSTAT1_THWARNHI
-#define MC13783_IRQMASK1_CLKM MC13783_IRQSTAT1_CLKI
-#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
-#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
-#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
-#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
-#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
-#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
-
-#define MC13783_ADC1 44
-#define MC13783_ADC1_ADEN (1 << 0)
-#define MC13783_ADC1_RAND (1 << 1)
-#define MC13783_ADC1_ADSEL (1 << 3)
-#define MC13783_ADC1_ASC (1 << 20)
-#define MC13783_ADC1_ADTRIGIGN (1 << 21)
-
-#define MC13783_NUMREGS 0x3f
-
-void mc13783_lock(struct mc13783 *mc13783)
-{
- if (!mutex_trylock(&mc13783->lock)) {
- dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
- __func__, __builtin_return_address(0));
-
- mutex_lock(&mc13783->lock);
- }
- dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
- __func__, __builtin_return_address(0));
-}
-EXPORT_SYMBOL(mc13783_lock);
-
-void mc13783_unlock(struct mc13783 *mc13783)
-{
- dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
- __func__, __builtin_return_address(0));
- mutex_unlock(&mc13783->lock);
-}
-EXPORT_SYMBOL(mc13783_unlock);
-
-#define MC13783_REGOFFSET_SHIFT 25
-int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
-{
- struct spi_transfer t;
- struct spi_message m;
- int ret;
-
- BUG_ON(!mutex_is_locked(&mc13783->lock));
-
- if (offset > MC13783_NUMREGS)
- return -EINVAL;
-
- *val = offset << MC13783_REGOFFSET_SHIFT;
-
- memset(&t, 0, sizeof(t));
-
- t.tx_buf = val;
- t.rx_buf = val;
- t.len = sizeof(u32);
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
-
- ret = spi_sync(mc13783->spidev, &m);
-
- /* error in message.status implies error return from spi_sync */
- BUG_ON(!ret && m.status);
-
- if (ret)
- return ret;
-
- *val &= 0xffffff;
-
- dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
-
- return 0;
-}
-EXPORT_SYMBOL(mc13783_reg_read);
-
-int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
-{
- u32 buf;
- struct spi_transfer t;
- struct spi_message m;
- int ret;
-
- BUG_ON(!mutex_is_locked(&mc13783->lock));
-
- dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
-
- if (offset > MC13783_NUMREGS || val > 0xffffff)
- return -EINVAL;
-
- buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
-
- memset(&t, 0, sizeof(t));
-
- t.tx_buf = &buf;
- t.rx_buf = &buf;
- t.len = sizeof(u32);
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
-
- ret = spi_sync(mc13783->spidev, &m);
-
- BUG_ON(!ret && m.status);
-
- if (ret)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL(mc13783_reg_write);
-
-int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
- u32 mask, u32 val)
-{
- int ret;
- u32 valread;
-
- BUG_ON(val & ~mask);
-
- ret = mc13783_reg_read(mc13783, offset, &valread);
- if (ret)
- return ret;
-
- valread = (valread & ~mask) | val;
-
- return mc13783_reg_write(mc13783, offset, valread);
-}
-EXPORT_SYMBOL(mc13783_reg_rmw);
-
-int mc13783_get_flags(struct mc13783 *mc13783)
-{
- return mc13783->flags;
-}
-EXPORT_SYMBOL(mc13783_get_flags);
-
-int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
-{
- int ret;
- unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
- u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
- u32 mask;
-
- if (irq < 0 || irq >= MC13783_NUM_IRQ)
- return -EINVAL;
-
- ret = mc13783_reg_read(mc13783, offmask, &mask);
- if (ret)
- return ret;
-
- if (mask & irqbit)
- /* already masked */
- return 0;
-
- return mc13783_reg_write(mc13783, offmask, mask | irqbit);
-}
-EXPORT_SYMBOL(mc13783_irq_mask);
-
-int mc13783_irq_unmask(struct mc13783 *mc13783, int irq)
-{
- int ret;
- unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
- u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
- u32 mask;
-
- if (irq < 0 || irq >= MC13783_NUM_IRQ)
- return -EINVAL;
-
- ret = mc13783_reg_read(mc13783, offmask, &mask);
- if (ret)
- return ret;
-
- if (!(mask & irqbit))
- /* already unmasked */
- return 0;
-
- return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
-}
-EXPORT_SYMBOL(mc13783_irq_unmask);
-
-int mc13783_irq_status(struct mc13783 *mc13783, int irq,
- int *enabled, int *pending)
-{
- int ret;
- unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
- unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
- u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
-
- if (irq < 0 || irq >= MC13783_NUM_IRQ)
- return -EINVAL;
-
- if (enabled) {
- u32 mask;
-
- ret = mc13783_reg_read(mc13783, offmask, &mask);
- if (ret)
- return ret;
-
- *enabled = mask & irqbit;
- }
-
- if (pending) {
- u32 stat;
-
- ret = mc13783_reg_read(mc13783, offstat, &stat);
- if (ret)
- return ret;
-
- *pending = stat & irqbit;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(mc13783_irq_status);
-
-int mc13783_irq_ack(struct mc13783 *mc13783, int irq)
-{
- unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
- unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
-
- BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
-
- return mc13783_reg_write(mc13783, offstat, val);
-}
-EXPORT_SYMBOL(mc13783_irq_ack);
-
-int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
- irq_handler_t handler, const char *name, void *dev)
-{
- BUG_ON(!mutex_is_locked(&mc13783->lock));
- BUG_ON(!handler);
-
- if (irq < 0 || irq >= MC13783_NUM_IRQ)
- return -EINVAL;
-
- if (mc13783->irqhandler[irq])
- return -EBUSY;
-
- mc13783->irqhandler[irq] = handler;
- mc13783->irqdata[irq] = dev;
-
- return 0;
-}
-EXPORT_SYMBOL(mc13783_irq_request_nounmask);
-
-int mc13783_irq_request(struct mc13783 *mc13783, int irq,
- irq_handler_t handler, const char *name, void *dev)
-{
- int ret;
-
- ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
- if (ret)
- return ret;
-
- ret = mc13783_irq_unmask(mc13783, irq);
- if (ret) {
- mc13783->irqhandler[irq] = NULL;
- mc13783->irqdata[irq] = NULL;
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(mc13783_irq_request);
-
-int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
-{
- int ret;
- BUG_ON(!mutex_is_locked(&mc13783->lock));
-
- if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
- mc13783->irqdata[irq] != dev)
- return -EINVAL;
-
- ret = mc13783_irq_mask(mc13783, irq);
- if (ret)
- return ret;
-
- mc13783->irqhandler[irq] = NULL;
- mc13783->irqdata[irq] = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL(mc13783_irq_free);
-
-static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
-{
- return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
-}
-
-/*
- * returns: number of handled irqs or negative error
- * locking: holds mc13783->lock
- */
-static int mc13783_irq_handle(struct mc13783 *mc13783,
- unsigned int offstat, unsigned int offmask, int baseirq)
-{
- u32 stat, mask;
- int ret = mc13783_reg_read(mc13783, offstat, &stat);
- int num_handled = 0;
-
- if (ret)
- return ret;
-
- ret = mc13783_reg_read(mc13783, offmask, &mask);
- if (ret)
- return ret;
-
- while (stat & ~mask) {
- int irq = __ffs(stat & ~mask);
-
- stat &= ~(1 << irq);
-
- if (likely(mc13783->irqhandler[baseirq + irq])) {
- irqreturn_t handled;
-
- handled = mc13783_irqhandler(mc13783, baseirq + irq);
- if (handled == IRQ_HANDLED)
- num_handled++;
- } else {
- dev_err(&mc13783->spidev->dev,
- "BUG: irq %u but no handler\n",
- baseirq + irq);
-
- mask |= 1 << irq;
-
- ret = mc13783_reg_write(mc13783, offmask, mask);
- }
- }
-
- return num_handled;
-}
-
-static irqreturn_t mc13783_irq_thread(int irq, void *data)
-{
- struct mc13783 *mc13783 = data;
- irqreturn_t ret;
- int handled = 0;
-
- mc13783_lock(mc13783);
-
- ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
- MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
- if (ret > 0)
- handled = 1;
-
- ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
- MC13783_IRQMASK1, MC13783_IRQ_1HZ);
- if (ret > 0)
- handled = 1;
-
- mc13783_unlock(mc13783);
-
- return IRQ_RETVAL(handled);
-}
-
-#define MC13783_ADC1_CHAN0_SHIFT 5
-#define MC13783_ADC1_CHAN1_SHIFT 8
-
-struct mc13783_adcdone_data {
- struct mc13783 *mc13783;
- struct completion done;
-};
-
-static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
-{
- struct mc13783_adcdone_data *adcdone_data = data;
-
- mc13783_irq_ack(adcdone_data->mc13783, irq);
-
- complete_all(&adcdone_data->done);
-
- return IRQ_HANDLED;
-}
-
-#define MC13783_ADC_WORKING (1 << 16)
-
-int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
- unsigned int channel, unsigned int *sample)
-{
- u32 adc0, adc1, old_adc0;
- int i, ret;
- struct mc13783_adcdone_data adcdone_data = {
- .mc13783 = mc13783,
- };
- init_completion(&adcdone_data.done);
-
- dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
-
- mc13783_lock(mc13783);
-
- if (mc13783->flags & MC13783_ADC_WORKING) {
- ret = -EBUSY;
- goto out;
- }
-
- mc13783->flags |= MC13783_ADC_WORKING;
-
- mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
-
- adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
- adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
-
- if (channel > 7)
- adc1 |= MC13783_ADC1_ADSEL;
-
- switch (mode) {
- case MC13783_ADC_MODE_TS:
- adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
- MC13783_ADC0_TSMOD1;
- adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
- break;
-
- case MC13783_ADC_MODE_SINGLE_CHAN:
- adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
- adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
- adc1 |= MC13783_ADC1_RAND;
- break;
-
- case MC13783_ADC_MODE_MULT_CHAN:
- adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
- adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
- break;
-
- default:
- mc13783_unlock(mc13783);
- return -EINVAL;
- }
-
- dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
- mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
- mc13783_handler_adcdone, __func__, &adcdone_data);
- mc13783_irq_ack(mc13783, MC13783_IRQ_ADCDONE);
-
- mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
- mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
-
- mc13783_unlock(mc13783);
-
- ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
-
- if (!ret)
- ret = -ETIMEDOUT;
-
- mc13783_lock(mc13783);
-
- mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
-
- if (ret > 0)
- for (i = 0; i < 4; ++i) {
- ret = mc13783_reg_read(mc13783,
- MC13783_REG_ADC_2, &sample[i]);
- if (ret)
- break;
- }
-
- if (mode == MC13783_ADC_MODE_TS)
- /* restore TSMOD */
- mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
-
- mc13783->flags &= ~MC13783_ADC_WORKING;
-out:
- mc13783_unlock(mc13783);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
-
-static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
- const char *name, void *pdata, size_t pdata_size)
-{
- struct mfd_cell cell = {
- .name = name,
- .platform_data = pdata,
- .data_size = pdata_size,
- };
-
- return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
-}
-
-static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
-{
- return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
-}
-
-static int mc13783_check_revision(struct mc13783 *mc13783)
-{
- u32 rev_id, rev1, rev2, finid, icid;
-
- mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
-
- rev1 = (rev_id & 0x018) >> 3;
- rev2 = (rev_id & 0x007);
- icid = (rev_id & 0x01C0) >> 6;
- finid = (rev_id & 0x01E00) >> 9;
-
- /* Ver 0.2 is actually 3.2a. Report as 3.2 */
- if ((rev1 == 0) && (rev2 == 2))
- rev1 = 3;
-
- if (rev1 == 0 || icid != 2) {
- dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
- return -ENODEV;
- }
-
- dev_info(&mc13783->spidev->dev,
- "MC13783 Rev %d.%d FinVer %x detected\n",
- rev1, rev2, finid);
-
- return 0;
-}
-
-static int mc13783_probe(struct spi_device *spi)
-{
- struct mc13783 *mc13783;
- struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
- int ret;
-
- mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
- if (!mc13783)
- return -ENOMEM;
-
- dev_set_drvdata(&spi->dev, mc13783);
- spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
- spi->bits_per_word = 32;
- spi_setup(spi);
-
- mc13783->spidev = spi;
-
- mutex_init(&mc13783->lock);
- mc13783_lock(mc13783);
-
- ret = mc13783_check_revision(mc13783);
- if (ret)
- goto err_revision;
-
- /* mask all irqs */
- ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
- if (ret)
- goto err_mask;
-
- ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
- if (ret)
- goto err_mask;
-
- ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
- IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
-
- if (ret) {
-err_mask:
-err_revision:
- mutex_unlock(&mc13783->lock);
- dev_set_drvdata(&spi->dev, NULL);
- kfree(mc13783);
- return ret;
- }
-
- /* This should go away (BEGIN) */
- if (pdata) {
- mc13783->flags = pdata->flags;
- mc13783->regulators = pdata->regulators;
- mc13783->num_regulators = pdata->num_regulators;
- }
- /* This should go away (END) */
-
- mc13783_unlock(mc13783);
-
- if (pdata->flags & MC13783_USE_ADC)
- mc13783_add_subdevice(mc13783, "mc13783-adc");
-
- if (pdata->flags & MC13783_USE_CODEC)
- mc13783_add_subdevice(mc13783, "mc13783-codec");
-
- if (pdata->flags & MC13783_USE_REGULATOR) {
- struct mc13783_regulator_platform_data regulator_pdata = {
- .num_regulators = pdata->num_regulators,
- .regulators = pdata->regulators,
- };
-
- mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
- &regulator_pdata, sizeof(regulator_pdata));
- }
-
- if (pdata->flags & MC13783_USE_RTC)
- mc13783_add_subdevice(mc13783, "mc13783-rtc");
-
- if (pdata->flags & MC13783_USE_TOUCHSCREEN)
- mc13783_add_subdevice(mc13783, "mc13783-ts");
-
- if (pdata->flags & MC13783_USE_LED)
- mc13783_add_subdevice_pdata(mc13783, "mc13783-led",
- pdata->leds, sizeof(*pdata->leds));
-
- return 0;
-}
-
-static int __devexit mc13783_remove(struct spi_device *spi)
-{
- struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
-
- free_irq(mc13783->spidev->irq, mc13783);
-
- mfd_remove_devices(&spi->dev);
-
- return 0;
-}
-
-static struct spi_driver mc13783_driver = {
- .driver = {
- .name = "mc13783",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = mc13783_probe,
- .remove = __devexit_p(mc13783_remove),
-};
-
-static int __init mc13783_init(void)
-{
- return spi_register_driver(&mc13783_driver);
-}
-subsys_initcall(mc13783_init);
-
-static void __exit mc13783_exit(void)
-{
- spi_unregister_driver(&mc13783_driver);
-}
-module_exit(mc13783_exit);
-
-MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
-MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
new file mode 100644
index 000000000000..a2ac2ed6d64c
--- /dev/null
+++ b/drivers/mfd/mc13xxx-core.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright 2009-2010 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ *
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13xxx.h>
+
+struct mc13xxx {
+ struct spi_device *spidev;
+ struct mutex lock;
+ int irq;
+
+ irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
+ void *irqdata[MC13XXX_NUM_IRQ];
+};
+
+struct mc13783 {
+ struct mc13xxx mc13xxx;
+
+ int adcflags;
+};
+
+struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783)
+{
+ return &mc13783->mc13xxx;
+}
+EXPORT_SYMBOL(mc13783_to_mc13xxx);
+
+#define MC13XXX_IRQSTAT0 0
+#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0)
+#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1)
+#define MC13XXX_IRQSTAT0_TSI (1 << 2)
+#define MC13783_IRQSTAT0_WHIGHI (1 << 3)
+#define MC13783_IRQSTAT0_WLOWI (1 << 4)
+#define MC13XXX_IRQSTAT0_CHGDETI (1 << 6)
+#define MC13783_IRQSTAT0_CHGOVI (1 << 7)
+#define MC13XXX_IRQSTAT0_CHGREVI (1 << 8)
+#define MC13XXX_IRQSTAT0_CHGSHORTI (1 << 9)
+#define MC13XXX_IRQSTAT0_CCCVI (1 << 10)
+#define MC13XXX_IRQSTAT0_CHGCURRI (1 << 11)
+#define MC13XXX_IRQSTAT0_BPONI (1 << 12)
+#define MC13XXX_IRQSTAT0_LOBATLI (1 << 13)
+#define MC13XXX_IRQSTAT0_LOBATHI (1 << 14)
+#define MC13783_IRQSTAT0_UDPI (1 << 15)
+#define MC13783_IRQSTAT0_USBI (1 << 16)
+#define MC13783_IRQSTAT0_IDI (1 << 19)
+#define MC13783_IRQSTAT0_SE1I (1 << 21)
+#define MC13783_IRQSTAT0_CKDETI (1 << 22)
+#define MC13783_IRQSTAT0_UDMI (1 << 23)
+
+#define MC13XXX_IRQMASK0 1
+#define MC13XXX_IRQMASK0_ADCDONEM MC13XXX_IRQSTAT0_ADCDONEI
+#define MC13XXX_IRQMASK0_ADCBISDONEM MC13XXX_IRQSTAT0_ADCBISDONEI
+#define MC13XXX_IRQMASK0_TSM MC13XXX_IRQSTAT0_TSI
+#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
+#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
+#define MC13XXX_IRQMASK0_CHGDETM MC13XXX_IRQSTAT0_CHGDETI
+#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
+#define MC13XXX_IRQMASK0_CHGREVM MC13XXX_IRQSTAT0_CHGREVI
+#define MC13XXX_IRQMASK0_CHGSHORTM MC13XXX_IRQSTAT0_CHGSHORTI
+#define MC13XXX_IRQMASK0_CCCVM MC13XXX_IRQSTAT0_CCCVI
+#define MC13XXX_IRQMASK0_CHGCURRM MC13XXX_IRQSTAT0_CHGCURRI
+#define MC13XXX_IRQMASK0_BPONM MC13XXX_IRQSTAT0_BPONI
+#define MC13XXX_IRQMASK0_LOBATLM MC13XXX_IRQSTAT0_LOBATLI
+#define MC13XXX_IRQMASK0_LOBATHM MC13XXX_IRQSTAT0_LOBATHI
+#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
+#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
+#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
+#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
+#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
+#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
+
+#define MC13XXX_IRQSTAT1 3
+#define MC13XXX_IRQSTAT1_1HZI (1 << 0)
+#define MC13XXX_IRQSTAT1_TODAI (1 << 1)
+#define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
+#define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
+#define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
+#define MC13XXX_IRQSTAT1_SYSRSTI (1 << 6)
+#define MC13XXX_IRQSTAT1_RTCRSTI (1 << 7)
+#define MC13XXX_IRQSTAT1_PCI (1 << 8)
+#define MC13XXX_IRQSTAT1_WARMI (1 << 9)
+#define MC13XXX_IRQSTAT1_MEMHLDI (1 << 10)
+#define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
+#define MC13XXX_IRQSTAT1_THWARNLI (1 << 12)
+#define MC13XXX_IRQSTAT1_THWARNHI (1 << 13)
+#define MC13XXX_IRQSTAT1_CLKI (1 << 14)
+#define MC13783_IRQSTAT1_SEMAFI (1 << 15)
+#define MC13783_IRQSTAT1_MC2BI (1 << 17)
+#define MC13783_IRQSTAT1_HSDETI (1 << 18)
+#define MC13783_IRQSTAT1_HSLI (1 << 19)
+#define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
+#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
+
+#define MC13XXX_IRQMASK1 4
+#define MC13XXX_IRQMASK1_1HZM MC13XXX_IRQSTAT1_1HZI
+#define MC13XXX_IRQMASK1_TODAM MC13XXX_IRQSTAT1_TODAI
+#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
+#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
+#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
+#define MC13XXX_IRQMASK1_SYSRSTM MC13XXX_IRQSTAT1_SYSRSTI
+#define MC13XXX_IRQMASK1_RTCRSTM MC13XXX_IRQSTAT1_RTCRSTI
+#define MC13XXX_IRQMASK1_PCM MC13XXX_IRQSTAT1_PCI
+#define MC13XXX_IRQMASK1_WARMM MC13XXX_IRQSTAT1_WARMI
+#define MC13XXX_IRQMASK1_MEMHLDM MC13XXX_IRQSTAT1_MEMHLDI
+#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
+#define MC13XXX_IRQMASK1_THWARNLM MC13XXX_IRQSTAT1_THWARNLI
+#define MC13XXX_IRQMASK1_THWARNHM MC13XXX_IRQSTAT1_THWARNHI
+#define MC13XXX_IRQMASK1_CLKM MC13XXX_IRQSTAT1_CLKI
+#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
+#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
+#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
+#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
+#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
+#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
+
+#define MC13XXX_REVISION 7
+#define MC13XXX_REVISION_REVMETAL (0x07 << 0)
+#define MC13XXX_REVISION_REVFULL (0x03 << 3)
+#define MC13XXX_REVISION_ICID (0x07 << 6)
+#define MC13XXX_REVISION_FIN (0x03 << 9)
+#define MC13XXX_REVISION_FAB (0x03 << 11)
+#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
+
+#define MC13783_ADC1 44
+#define MC13783_ADC1_ADEN (1 << 0)
+#define MC13783_ADC1_RAND (1 << 1)
+#define MC13783_ADC1_ADSEL (1 << 3)
+#define MC13783_ADC1_ASC (1 << 20)
+#define MC13783_ADC1_ADTRIGIGN (1 << 21)
+
+#define MC13783_ADC2 45
+
+#define MC13XXX_NUMREGS 0x3f
+
+void mc13xxx_lock(struct mc13xxx *mc13xxx)
+{
+ if (!mutex_trylock(&mc13xxx->lock)) {
+ dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
+ __func__, __builtin_return_address(0));
+
+ mutex_lock(&mc13xxx->lock);
+ }
+ dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+ __func__, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(mc13xxx_lock);
+
+void mc13xxx_unlock(struct mc13xxx *mc13xxx)
+{
+ dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+ __func__, __builtin_return_address(0));
+ mutex_unlock(&mc13xxx->lock);
+}
+EXPORT_SYMBOL(mc13xxx_unlock);
+
+#define MC13XXX_REGOFFSET_SHIFT 25
+int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ int ret;
+
+ BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+ if (offset > MC13XXX_NUMREGS)
+ return -EINVAL;
+
+ *val = offset << MC13XXX_REGOFFSET_SHIFT;
+
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = val;
+ t.rx_buf = val;
+ t.len = sizeof(u32);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(mc13xxx->spidev, &m);
+
+ /* error in message.status implies error return from spi_sync */
+ BUG_ON(!ret && m.status);
+
+ if (ret)
+ return ret;
+
+ *val &= 0xffffff;
+
+ dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13xxx_reg_read);
+
+int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
+{
+ u32 buf;
+ struct spi_transfer t;
+ struct spi_message m;
+ int ret;
+
+ BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+ dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+ if (offset > MC13XXX_NUMREGS || val > 0xffffff)
+ return -EINVAL;
+
+ buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
+
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = &buf;
+ t.rx_buf = &buf;
+ t.len = sizeof(u32);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(mc13xxx->spidev, &m);
+
+ BUG_ON(!ret && m.status);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13xxx_reg_write);
+
+int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
+ u32 mask, u32 val)
+{
+ int ret;
+ u32 valread;
+
+ BUG_ON(val & ~mask);
+
+ ret = mc13xxx_reg_read(mc13xxx, offset, &valread);
+ if (ret)
+ return ret;
+
+ valread = (valread & ~mask) | val;
+
+ return mc13xxx_reg_write(mc13xxx, offset, valread);
+}
+EXPORT_SYMBOL(mc13xxx_reg_rmw);
+
+int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq)
+{
+ int ret;
+ unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
+ u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+ u32 mask;
+
+ if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+ return -EINVAL;
+
+ ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+ if (ret)
+ return ret;
+
+ if (mask & irqbit)
+ /* already masked */
+ return 0;
+
+ return mc13xxx_reg_write(mc13xxx, offmask, mask | irqbit);
+}
+EXPORT_SYMBOL(mc13xxx_irq_mask);
+
+int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq)
+{
+ int ret;
+ unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
+ u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+ u32 mask;
+
+ if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+ return -EINVAL;
+
+ ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+ if (ret)
+ return ret;
+
+ if (!(mask & irqbit))
+ /* already unmasked */
+ return 0;
+
+ return mc13xxx_reg_write(mc13xxx, offmask, mask & ~irqbit);
+}
+EXPORT_SYMBOL(mc13xxx_irq_unmask);
+
+int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq,
+ int *enabled, int *pending)
+{
+ int ret;
+ unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
+ unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1;
+ u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+
+ if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+ return -EINVAL;
+
+ if (enabled) {
+ u32 mask;
+
+ ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+ if (ret)
+ return ret;
+
+ *enabled = mask & irqbit;
+ }
+
+ if (pending) {
+ u32 stat;
+
+ ret = mc13xxx_reg_read(mc13xxx, offstat, &stat);
+ if (ret)
+ return ret;
+
+ *pending = stat & irqbit;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_status);
+
+int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq)
+{
+ unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1;
+ unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
+
+ BUG_ON(irq < 0 || irq >= MC13XXX_NUM_IRQ);
+
+ return mc13xxx_reg_write(mc13xxx, offstat, val);
+}
+EXPORT_SYMBOL(mc13xxx_irq_ack);
+
+int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq,
+ irq_handler_t handler, const char *name, void *dev)
+{
+ BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+ BUG_ON(!handler);
+
+ if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+ return -EINVAL;
+
+ if (mc13xxx->irqhandler[irq])
+ return -EBUSY;
+
+ mc13xxx->irqhandler[irq] = handler;
+ mc13xxx->irqdata[irq] = dev;
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_request_nounmask);
+
+int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
+ irq_handler_t handler, const char *name, void *dev)
+{
+ int ret;
+
+ ret = mc13xxx_irq_request_nounmask(mc13xxx, irq, handler, name, dev);
+ if (ret)
+ return ret;
+
+ ret = mc13xxx_irq_unmask(mc13xxx, irq);
+ if (ret) {
+ mc13xxx->irqhandler[irq] = NULL;
+ mc13xxx->irqdata[irq] = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_request);
+
+int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev)
+{
+ int ret;
+ BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+ if (irq < 0 || irq >= MC13XXX_NUM_IRQ || !mc13xxx->irqhandler[irq] ||
+ mc13xxx->irqdata[irq] != dev)
+ return -EINVAL;
+
+ ret = mc13xxx_irq_mask(mc13xxx, irq);
+ if (ret)
+ return ret;
+
+ mc13xxx->irqhandler[irq] = NULL;
+ mc13xxx->irqdata[irq] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_free);
+
+static inline irqreturn_t mc13xxx_irqhandler(struct mc13xxx *mc13xxx, int irq)
+{
+ return mc13xxx->irqhandler[irq](irq, mc13xxx->irqdata[irq]);
+}
+
+/*
+ * returns: number of handled irqs or negative error
+ * locking: holds mc13xxx->lock
+ */
+static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
+ unsigned int offstat, unsigned int offmask, int baseirq)
+{
+ u32 stat, mask;
+ int ret = mc13xxx_reg_read(mc13xxx, offstat, &stat);
+ int num_handled = 0;
+
+ if (ret)
+ return ret;
+
+ ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+ if (ret)
+ return ret;
+
+ while (stat & ~mask) {
+ int irq = __ffs(stat & ~mask);
+
+ stat &= ~(1 << irq);
+
+ if (likely(mc13xxx->irqhandler[baseirq + irq])) {
+ irqreturn_t handled;
+
+ handled = mc13xxx_irqhandler(mc13xxx, baseirq + irq);
+ if (handled == IRQ_HANDLED)
+ num_handled++;
+ } else {
+ dev_err(&mc13xxx->spidev->dev,
+ "BUG: irq %u but no handler\n",
+ baseirq + irq);
+
+ mask |= 1 << irq;
+
+ ret = mc13xxx_reg_write(mc13xxx, offmask, mask);
+ }
+ }
+
+ return num_handled;
+}
+
+static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
+{
+ struct mc13xxx *mc13xxx = data;
+ irqreturn_t ret;
+ int handled = 0;
+
+ mc13xxx_lock(mc13xxx);
+
+ ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT0,
+ MC13XXX_IRQMASK0, 0);
+ if (ret > 0)
+ handled = 1;
+
+ ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT1,
+ MC13XXX_IRQMASK1, 24);
+ if (ret > 0)
+ handled = 1;
+
+ mc13xxx_unlock(mc13xxx);
+
+ return IRQ_RETVAL(handled);
+}
+
+enum mc13xxx_id {
+ MC13XXX_ID_MC13783,
+ MC13XXX_ID_MC13892,
+ MC13XXX_ID_INVALID,
+};
+
+const char *mc13xxx_chipname[] = {
+ [MC13XXX_ID_MC13783] = "mc13783",
+ [MC13XXX_ID_MC13892] = "mc13892",
+};
+
+#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
+static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
+{
+ u32 icid;
+ u32 revision;
+ const char *name;
+ int ret;
+
+ ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
+ if (ret)
+ return ret;
+
+ icid = (icid >> 6) & 0x7;
+
+ switch (icid) {
+ case 2:
+ *id = MC13XXX_ID_MC13783;
+ name = "mc13783";
+ break;
+ case 7:
+ *id = MC13XXX_ID_MC13892;
+ name = "mc13892";
+ break;
+ default:
+ *id = MC13XXX_ID_INVALID;
+ break;
+ }
+
+ if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
+ ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
+ if (ret)
+ return ret;
+
+ dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
+ "fin: %d, fab: %d, icid: %d/%d\n",
+ mc13xxx_chipname[*id],
+ maskval(revision, MC13XXX_REVISION_REVFULL),
+ maskval(revision, MC13XXX_REVISION_REVMETAL),
+ maskval(revision, MC13XXX_REVISION_FIN),
+ maskval(revision, MC13XXX_REVISION_FAB),
+ maskval(revision, MC13XXX_REVISION_ICID),
+ maskval(revision, MC13XXX_REVISION_ICIDCODE));
+ }
+
+ if (*id != MC13XXX_ID_INVALID) {
+ const struct spi_device_id *devid =
+ spi_get_device_id(mc13xxx->spidev);
+ if (!devid || devid->driver_data != *id)
+ dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
+ "match auto detection!\n");
+ }
+
+ return 0;
+}
+
+static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
+{
+ const struct spi_device_id *devid =
+ spi_get_device_id(mc13xxx->spidev);
+
+ if (!devid)
+ return NULL;
+
+ return mc13xxx_chipname[devid->driver_data];
+}
+
+#include <linux/mfd/mc13783.h>
+
+int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
+{
+ struct mc13xxx_platform_data *pdata =
+ dev_get_platdata(&mc13xxx->spidev->dev);
+
+ return pdata->flags;
+}
+EXPORT_SYMBOL(mc13xxx_get_flags);
+
+#define MC13783_ADC1_CHAN0_SHIFT 5
+#define MC13783_ADC1_CHAN1_SHIFT 8
+
+struct mc13xxx_adcdone_data {
+ struct mc13xxx *mc13xxx;
+ struct completion done;
+};
+
+static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
+{
+ struct mc13xxx_adcdone_data *adcdone_data = data;
+
+ mc13xxx_irq_ack(adcdone_data->mc13xxx, irq);
+
+ complete_all(&adcdone_data->done);
+
+ return IRQ_HANDLED;
+}
+
+#define MC13783_ADC_WORKING (1 << 0)
+
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample)
+{
+ struct mc13xxx *mc13xxx = &mc13783->mc13xxx;
+ u32 adc0, adc1, old_adc0;
+ int i, ret;
+ struct mc13xxx_adcdone_data adcdone_data = {
+ .mc13xxx = mc13xxx,
+ };
+ init_completion(&adcdone_data.done);
+
+ dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
+
+ mc13xxx_lock(mc13xxx);
+
+ if (mc13783->adcflags & MC13783_ADC_WORKING) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ mc13783->adcflags |= MC13783_ADC_WORKING;
+
+ mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0);
+
+ adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+ adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
+
+ if (channel > 7)
+ adc1 |= MC13783_ADC1_ADSEL;
+
+ switch (mode) {
+ case MC13783_ADC_MODE_TS:
+ adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
+ MC13783_ADC0_TSMOD1;
+ adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+
+ case MC13783_ADC_MODE_SINGLE_CHAN:
+ adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+ adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+ adc1 |= MC13783_ADC1_RAND;
+ break;
+
+ case MC13783_ADC_MODE_MULT_CHAN:
+ adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+ adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+
+ default:
+ mc13783_unlock(mc13783);
+ return -EINVAL;
+ }
+
+ dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
+ mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
+ mc13783_handler_adcdone, __func__, &adcdone_data);
+ mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
+
+ mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0);
+ mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1);
+
+ mc13xxx_unlock(mc13xxx);
+
+ ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
+
+ if (!ret)
+ ret = -ETIMEDOUT;
+
+ mc13xxx_lock(mc13xxx);
+
+ mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data);
+
+ if (ret > 0)
+ for (i = 0; i < 4; ++i) {
+ ret = mc13xxx_reg_read(mc13xxx,
+ MC13783_ADC2, &sample[i]);
+ if (ret)
+ break;
+ }
+
+ if (mode == MC13783_ADC_MODE_TS)
+ /* restore TSMOD */
+ mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0);
+
+ mc13783->adcflags &= ~MC13783_ADC_WORKING;
+out:
+ mc13xxx_unlock(mc13xxx);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
+
+static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
+ const char *format, void *pdata, size_t pdata_size)
+{
+ char buf[30];
+ const char *name = mc13xxx_get_chipname(mc13xxx);
+
+ struct mfd_cell cell = {
+ .platform_data = pdata,
+ .data_size = pdata_size,
+ };
+
+ /* there is no asnprintf in the kernel :-( */
+ if (snprintf(buf, sizeof(buf), format, name) > sizeof(buf))
+ return -E2BIG;
+
+ cell.name = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL);
+ if (!cell.name)
+ return -ENOMEM;
+
+ return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
+{
+ return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
+}
+
+static int mc13xxx_probe(struct spi_device *spi)
+{
+ struct mc13xxx *mc13xxx;
+ struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
+ enum mc13xxx_id id;
+ int ret;
+
+ mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+ if (!mc13xxx)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi->dev, mc13xxx);
+ spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+ spi->bits_per_word = 32;
+ spi_setup(spi);
+
+ mc13xxx->spidev = spi;
+
+ mutex_init(&mc13xxx->lock);
+ mc13xxx_lock(mc13xxx);
+
+ ret = mc13xxx_identify(mc13xxx, &id);
+ if (ret || id == MC13XXX_ID_INVALID)
+ goto err_revision;
+
+ /* mask all irqs */
+ ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);
+ if (ret)
+ goto err_mask;
+
+ ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff);
+ if (ret)
+ goto err_mask;
+
+ ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
+
+ if (ret) {
+err_mask:
+err_revision:
+ mutex_unlock(&mc13xxx->lock);
+ dev_set_drvdata(&spi->dev, NULL);
+ kfree(mc13xxx);
+ return ret;
+ }
+
+ mc13xxx_unlock(mc13xxx);
+
+ if (pdata->flags & MC13XXX_USE_ADC)
+ mc13xxx_add_subdevice(mc13xxx, "%s-adc");
+
+ if (pdata->flags & MC13XXX_USE_CODEC)
+ mc13xxx_add_subdevice(mc13xxx, "%s-codec");
+
+ if (pdata->flags & MC13XXX_USE_REGULATOR) {
+ struct mc13xxx_regulator_platform_data regulator_pdata = {
+ .num_regulators = pdata->num_regulators,
+ .regulators = pdata->regulators,
+ };
+
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
+ &regulator_pdata, sizeof(regulator_pdata));
+ }
+
+ if (pdata->flags & MC13XXX_USE_RTC)
+ mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
+
+ if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
+ mc13xxx_add_subdevice(mc13xxx, "%s-ts");
+
+ if (pdata->flags & MC13XXX_USE_LED) {
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
+ pdata->leds, sizeof(*pdata->leds));
+ }
+
+ return 0;
+}
+
+static int __devexit mc13xxx_remove(struct spi_device *spi)
+{
+ struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
+
+ free_irq(mc13xxx->spidev->irq, mc13xxx);
+
+ mfd_remove_devices(&spi->dev);
+
+ kfree(mc13xxx);
+
+ return 0;
+}
+
+static const struct spi_device_id mc13xxx_device_id[] = {
+ {
+ .name = "mc13783",
+ .driver_data = MC13XXX_ID_MC13783,
+ }, {
+ .name = "mc13892",
+ .driver_data = MC13XXX_ID_MC13892,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct spi_driver mc13xxx_driver = {
+ .id_table = mc13xxx_device_id,
+ .driver = {
+ .name = "mc13xxx",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mc13xxx_probe,
+ .remove = __devexit_p(mc13xxx_remove),
+};
+
+static int __init mc13xxx_init(void)
+{
+ return spi_register_driver(&mc13xxx_driver);
+}
+subsys_initcall(mc13xxx_init);
+
+static void __exit mc13xxx_exit(void)
+{
+ spi_unregister_driver(&mc13xxx_driver);
+}
+module_exit(mc13xxx_exit);
+
+MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 1823a57b7d8f..ec99f681e773 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -38,10 +38,12 @@ static int mfd_add_device(struct device *parent, int id,
pdev->dev.parent = parent;
platform_set_drvdata(pdev, cell->driver_data);
- ret = platform_device_add_data(pdev,
- cell->platform_data, cell->data_size);
- if (ret)
- goto fail_res;
+ if (cell->data_size) {
+ ret = platform_device_add_data(pdev,
+ cell->platform_data, cell->data_size);
+ if (ret)
+ goto fail_res;
+ }
for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name;
@@ -65,9 +67,11 @@ static int mfd_add_device(struct device *parent, int id,
res[r].end = cell->resources[r].end;
}
- ret = acpi_check_resource_conflict(res);
- if (ret)
- goto fail_res;
+ if (!cell->ignore_resource_conflicts) {
+ ret = acpi_check_resource_conflict(res);
+ if (ret)
+ goto fail_res;
+ }
}
ret = platform_device_add_resources(pdev, res, cell->num_resources);
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 23e585527285..501ce13b693e 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -25,13 +25,6 @@
#include <linux/mfd/pcf50633/core.h>
-int pcf50633_irq_init(struct pcf50633 *pcf, int irq);
-void pcf50633_irq_free(struct pcf50633 *pcf);
-#ifdef CONFIG_PM
-int pcf50633_irq_suspend(struct pcf50633 *pcf);
-int pcf50633_irq_resume(struct pcf50633 *pcf);
-#endif
-
static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
{
int ret;
@@ -346,12 +339,14 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
struct pcf50633 *pcf = i2c_get_clientdata(client);
int i;
+ sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
pcf50633_irq_free(pcf);
platform_device_unregister(pcf->input_pdev);
platform_device_unregister(pcf->rtc_pdev);
platform_device_unregister(pcf->mbc_pdev);
platform_device_unregister(pcf->adc_pdev);
+ platform_device_unregister(pcf->bl_pdev);
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
platform_device_unregister(pcf->regulator_pdev[i]);
diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c
index cd164595f08a..f1714f93af9d 100644
--- a/drivers/mfd/sh_mobile_sdhi.c
+++ b/drivers/mfd/sh_mobile_sdhi.c
@@ -65,7 +65,18 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
p->set_pwr(pdev, state);
}
-static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
+static int sh_mobile_sdhi_get_cd(struct platform_device *tmio)
+{
+ struct platform_device *pdev = to_platform_device(tmio->dev.parent);
+ struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
+
+ if (p && p->get_cd)
+ return p->get_cd(pdev);
+ else
+ return -ENOSYS;
+}
+
+static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
{
struct sh_mobile_sdhi *priv;
struct tmio_mmc_data *mmc_data;
@@ -106,12 +117,20 @@ static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->hclk = clk_get_rate(priv->clk);
mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
+ mmc_data->get_cd = sh_mobile_sdhi_get_cd;
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
if (p) {
mmc_data->flags = p->tmio_flags;
mmc_data->ocr_mask = p->tmio_ocr_mask;
+ mmc_data->capabilities |= p->tmio_caps;
}
+ /*
+ * All SDHI blocks support 2-byte and larger block sizes in 4-bit
+ * bus width mode.
+ */
+ mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES;
+
if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) {
priv->param_tx.slave_id = p->dma_slave_tx;
priv->param_rx.slave_id = p->dma_slave_rx;
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 0754c5e91995..b11487f1e1cb 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -873,6 +873,28 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe)
return ret;
}
+#ifdef CONFIG_PM
+static int stmpe_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(&i2c->dev))
+ enable_irq_wake(i2c->irq);
+
+ return 0;
+}
+
+static int stmpe_resume(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(&i2c->dev))
+ disable_irq_wake(i2c->irq);
+
+ return 0;
+}
+#endif
+
static int __devinit stmpe_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -960,9 +982,19 @@ static const struct i2c_device_id stmpe_id[] = {
};
MODULE_DEVICE_TABLE(i2c, stmpe_id);
+#ifdef CONFIG_PM
+static const struct dev_pm_ops stmpe_dev_pm_ops = {
+ .suspend = stmpe_suspend,
+ .resume = stmpe_resume,
+};
+#endif
+
static struct i2c_driver stmpe_driver = {
.driver.name = "stmpe",
.driver.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .driver.pm = &stmpe_dev_pm_ops,
+#endif
.probe = stmpe_probe,
.remove = __devexit_p(stmpe_remove),
.id_table = stmpe_id,
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index ef6c42c8917a..1ea80d8ad915 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -155,7 +155,7 @@ static struct resource __devinitdata tc6393xb_nand_resources[] = {
},
};
-static struct resource __devinitdata tc6393xb_mmc_resources[] = {
+static struct resource tc6393xb_mmc_resources[] = {
{
.start = 0x800,
.end = 0x9ff,
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index ac5995026c88..727f62c15a60 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -43,6 +43,8 @@
#include <linux/timb_dma.h>
+#include <linux/ks8842.h>
+
#include "timberdale.h"
#define DRIVER_NAME "timberdale"
@@ -161,6 +163,12 @@ static const __devinitconst struct resource timberdale_spi_resources[] = {
},
};
+static __devinitdata struct ks8842_platform_data
+ timberdale_ks8842_platform_data = {
+ .rx_dma_channel = DMA_ETH_RX,
+ .tx_dma_channel = DMA_ETH_TX
+};
+
static const __devinitconst struct resource timberdale_eth_resources[] = {
{
.start = ETHOFFSET,
@@ -389,6 +397,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
+ .platform_data = &timberdale_ks8842_platform_data,
+ .data_size = sizeof(timberdale_ks8842_platform_data)
},
};
@@ -447,6 +457,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
+ .platform_data = &timberdale_ks8842_platform_data,
+ .data_size = sizeof(timberdale_ks8842_platform_data)
},
};
@@ -538,6 +550,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
+ .platform_data = &timberdale_ks8842_platform_data,
+ .data_size = sizeof(timberdale_ks8842_platform_data)
},
};
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index fc0197649281..33ba7723c967 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -68,7 +68,7 @@ static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg,
u8 msg[TPS6507X_MAX_REGISTER + 1];
int ret;
- if (bytes > (TPS6507X_MAX_REGISTER + 1))
+ if (bytes > TPS6507X_MAX_REGISTER)
return -EINVAL;
msg[0] = reg;
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 4cde31e6a252..b4931ab34929 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -15,6 +15,8 @@
* published by the Free Software Foundation.
*/
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -29,9 +31,64 @@
#define TPS6586X_GPIOSET1 0x5d
#define TPS6586X_GPIOSET2 0x5e
+/* interrupt control registers */
+#define TPS6586X_INT_ACK1 0xb5
+#define TPS6586X_INT_ACK2 0xb6
+#define TPS6586X_INT_ACK3 0xb7
+#define TPS6586X_INT_ACK4 0xb8
+
+/* interrupt mask registers */
+#define TPS6586X_INT_MASK1 0xb0
+#define TPS6586X_INT_MASK2 0xb1
+#define TPS6586X_INT_MASK3 0xb2
+#define TPS6586X_INT_MASK4 0xb3
+#define TPS6586X_INT_MASK5 0xb4
+
/* device id */
#define TPS6586X_VERSIONCRC 0xcd
#define TPS658621A_VERSIONCRC 0x15
+#define TPS658621C_VERSIONCRC 0x2c
+
+struct tps6586x_irq_data {
+ u8 mask_reg;
+ u8 mask_mask;
+};
+
+#define TPS6586X_IRQ(_reg, _mask) \
+ { \
+ .mask_reg = (_reg) - TPS6586X_INT_MASK1, \
+ .mask_mask = (_mask), \
+ }
+
+static const struct tps6586x_irq_data tps6586x_irqs[] = {
+ [TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0),
+ [TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1),
+ [TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2),
+ [TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3),
+ [TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4),
+ [TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5),
+ [TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6),
+ [TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7),
+ [TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0),
+ [TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1),
+ [TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2),
+ [TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3),
+ [TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4),
+ [TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5),
+ [TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6),
+ [TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7),
+ [TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4),
+ [TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03),
+ [TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2),
+ [TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3),
+ [TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0),
+ [TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc),
+ [TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06),
+ [TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0),
+ [TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5),
+ [TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6),
+ [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
+};
struct tps6586x {
struct mutex lock;
@@ -39,6 +96,12 @@ struct tps6586x {
struct i2c_client *client;
struct gpio_chip gpio;
+ struct irq_chip irq_chip;
+ struct mutex irq_lock;
+ int irq_base;
+ u32 irq_en;
+ u8 mask_cache[5];
+ u8 mask_reg[5];
};
static inline int __tps6586x_read(struct i2c_client *client,
@@ -262,6 +325,129 @@ static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
}
+static void tps6586x_irq_lock(unsigned int irq)
+{
+ struct tps6586x *tps6586x = get_irq_chip_data(irq);
+
+ mutex_lock(&tps6586x->irq_lock);
+}
+
+static void tps6586x_irq_enable(unsigned int irq)
+{
+ struct tps6586x *tps6586x = get_irq_chip_data(irq);
+ unsigned int __irq = irq - tps6586x->irq_base;
+ const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
+
+ tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask;
+ tps6586x->irq_en |= (1 << __irq);
+}
+
+static void tps6586x_irq_disable(unsigned int irq)
+{
+ struct tps6586x *tps6586x = get_irq_chip_data(irq);
+
+ unsigned int __irq = irq - tps6586x->irq_base;
+ const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
+
+ tps6586x->mask_reg[data->mask_reg] |= data->mask_mask;
+ tps6586x->irq_en &= ~(1 << __irq);
+}
+
+static void tps6586x_irq_sync_unlock(unsigned int irq)
+{
+ struct tps6586x *tps6586x = get_irq_chip_data(irq);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
+ if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) {
+ if (!WARN_ON(tps6586x_write(tps6586x->dev,
+ TPS6586X_INT_MASK1 + i,
+ tps6586x->mask_reg[i])))
+ tps6586x->mask_cache[i] = tps6586x->mask_reg[i];
+ }
+ }
+
+ mutex_unlock(&tps6586x->irq_lock);
+}
+
+static irqreturn_t tps6586x_irq(int irq, void *data)
+{
+ struct tps6586x *tps6586x = data;
+ u32 acks;
+ int ret = 0;
+
+ ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1,
+ sizeof(acks), (uint8_t *)&acks);
+
+ if (ret < 0) {
+ dev_err(tps6586x->dev, "failed to read interrupt status\n");
+ return IRQ_NONE;
+ }
+
+ acks = le32_to_cpu(acks);
+
+ while (acks) {
+ int i = __ffs(acks);
+
+ if (tps6586x->irq_en & (1 << i))
+ handle_nested_irq(tps6586x->irq_base + i);
+
+ acks &= ~(1 << i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
+ int irq_base)
+{
+ int i, ret;
+ u8 tmp[4];
+
+ if (!irq_base) {
+ dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&tps6586x->irq_lock);
+ for (i = 0; i < 5; i++) {
+ tps6586x->mask_cache[i] = 0xff;
+ tps6586x->mask_reg[i] = 0xff;
+ tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
+ }
+
+ tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp);
+
+ tps6586x->irq_base = irq_base;
+
+ tps6586x->irq_chip.name = "tps6586x";
+ tps6586x->irq_chip.enable = tps6586x_irq_enable;
+ tps6586x->irq_chip.disable = tps6586x_irq_disable;
+ tps6586x->irq_chip.bus_lock = tps6586x_irq_lock;
+ tps6586x->irq_chip.bus_sync_unlock = tps6586x_irq_sync_unlock;
+
+ for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) {
+ int __irq = i + tps6586x->irq_base;
+ set_irq_chip_data(__irq, tps6586x);
+ set_irq_chip_and_handler(__irq, &tps6586x->irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(__irq, IRQF_VALID);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
+ "tps6586x", tps6586x);
+
+ if (!ret) {
+ device_init_wakeup(tps6586x->dev, 1);
+ enable_irq_wake(irq);
+ }
+
+ return ret;
+}
+
static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
struct tps6586x_platform_data *pdata)
{
@@ -273,13 +459,19 @@ static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
subdev = &pdata->subdevs[i];
pdev = platform_device_alloc(subdev->name, subdev->id);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto failed;
+ }
pdev->dev.parent = tps6586x->dev;
pdev->dev.platform_data = subdev->platform_data;
ret = platform_device_add(pdev);
- if (ret)
+ if (ret) {
+ platform_device_put(pdev);
goto failed;
+ }
}
return 0;
@@ -306,7 +498,8 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
return -EIO;
}
- if (ret != TPS658621A_VERSIONCRC) {
+ if ((ret != TPS658621A_VERSIONCRC) &&
+ (ret != TPS658621C_VERSIONCRC)) {
dev_err(&client->dev, "Unsupported chip ID: %x\n", ret);
return -ENODEV;
}
@@ -321,6 +514,15 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
mutex_init(&tps6586x->lock);
+ if (client->irq) {
+ ret = tps6586x_irq_init(tps6586x, client->irq,
+ pdata->irq_base);
+ if (ret) {
+ dev_err(&client->dev, "IRQ init failed: %d\n", ret);
+ goto err_irq_init;
+ }
+ }
+
ret = tps6586x_add_subdevs(tps6586x, pdata);
if (ret) {
dev_err(&client->dev, "add devices failed: %d\n", ret);
@@ -332,12 +534,31 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
return 0;
err_add_devs:
+ if (client->irq)
+ free_irq(client->irq, tps6586x);
+err_irq_init:
kfree(tps6586x);
return ret;
}
static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
{
+ struct tps6586x *tps6586x = i2c_get_clientdata(client);
+ struct tps6586x_platform_data *pdata = client->dev.platform_data;
+ int ret;
+
+ if (client->irq)
+ free_irq(client->irq, tps6586x);
+
+ if (pdata->gpio_base) {
+ ret = gpiochip_remove(&tps6586x->gpio);
+ if (ret)
+ dev_err(&client->dev, "Can't remove gpio chip: %d\n",
+ ret);
+ }
+
+ tps6586x_remove_subdevs(tps6586x);
+ kfree(tps6586x);
return 0;
}
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 720e099e506d..35275ba7096f 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -115,6 +115,12 @@
#define twl_has_codec() false
#endif
+#if defined(CONFIG_CHARGER_TWL4030) || defined(CONFIG_CHARGER_TWL4030_MODULE)
+#define twl_has_bci() true
+#else
+#define twl_has_bci() false
+#endif
+
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
@@ -202,12 +208,6 @@
/* Few power values */
#define R_CFG_BOOT 0x05
-#define R_PROTECT_KEY 0x0E
-
-/* access control values for R_PROTECT_KEY */
-#define KEY_UNLOCK1 0xce
-#define KEY_UNLOCK2 0xec
-#define KEY_LOCK 0x00
/* some fields in R_CFG_BOOT */
#define HFCLK_FREQ_19p2_MHZ (1 << 0)
@@ -255,7 +255,7 @@ struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
-struct twl_mapping *twl_map;
+static struct twl_mapping *twl_map;
static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
/*
@@ -698,17 +698,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl4030_codec",
+ child = add_child(sub_chip_id, "twl4030-audio",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
- /* Phoenix*/
+ /* Phoenix codec driver is probed directly atm */
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl6040_codec",
+ child = add_child(sub_chip_id, "twl6040-codec",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
@@ -832,6 +832,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
+ if (twl_has_bci() && pdata->bci &&
+ !(features & (TPS_SUBSET | TWL5031))) {
+ child = add_child(3, "twl4030_bci",
+ pdata->bci, sizeof(*pdata->bci), false,
+ /* irq0 = CHG_PRES, irq1 = BCI */
+ pdata->irq_base + BCI_PRES_INTR_OFFSET,
+ pdata->irq_base + BCI_INTR_OFFSET);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
return 0;
}
@@ -846,8 +857,8 @@ static inline int __init protect_pm_master(void)
{
int e = 0;
- e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_LOCK,
- R_PROTECT_KEY);
+ e = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
return e;
}
@@ -855,10 +866,13 @@ static inline int __init unprotect_pm_master(void)
{
int e = 0;
- e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK1,
- R_PROTECT_KEY);
- e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK2,
- R_PROTECT_KEY);
+ e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+ e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+
return e;
}
diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h
new file mode 100644
index 000000000000..8c50a556e986
--- /dev/null
+++ b/drivers/mfd/twl-core.h
@@ -0,0 +1,10 @@
+#ifndef __TWL_CORE_H__
+#define __TWL_CORE_H__
+
+extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+extern int twl6030_exit_irq(void);
+extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+extern int twl4030_exit_irq(void);
+extern int twl4030_init_chip_irq(const char *chip);
+
+#endif /* __TWL_CORE_H__ */
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
index add6f67d8032..9a4b196d6deb 100644
--- a/drivers/mfd/twl4030-codec.c
+++ b/drivers/mfd/twl4030-codec.c
@@ -207,14 +207,14 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
if (pdata->audio) {
cell = &codec->cells[childs];
- cell->name = "twl4030_codec_audio";
+ cell->name = "twl4030-codec";
cell->platform_data = pdata->audio;
cell->data_size = sizeof(*pdata->audio);
childs++;
}
if (pdata->vibra) {
cell = &codec->cells[childs];
- cell->name = "twl4030_codec_vibra";
+ cell->name = "twl4030-vibra";
cell->platform_data = pdata->vibra;
cell->data_size = sizeof(*pdata->vibra);
childs++;
@@ -249,14 +249,14 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev)
return 0;
}
-MODULE_ALIAS("platform:twl4030_codec");
+MODULE_ALIAS("platform:twl4030-audio");
static struct platform_driver twl4030_codec_driver = {
.probe = twl4030_codec_probe,
.remove = __devexit_p(twl4030_codec_remove),
.driver = {
.owner = THIS_MODULE,
- .name = "twl4030_codec",
+ .name = "twl4030-audio",
},
};
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index b9fda7018cef..5d3a1478004b 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -35,6 +35,7 @@
#include <linux/i2c/twl.h>
+#include "twl-core.h"
/*
* TWL4030 IRQ handling has two stages in hardware, and thus in software.
@@ -144,6 +145,7 @@ static const struct sih sih_modules_twl4030[6] = {
.name = "bci",
.module = TWL4030_MODULE_INTERRUPTS,
.control_offset = TWL4030_INTERRUPTS_BCISIHCTRL,
+ .set_cor = true,
.bits = 12,
.bytes_ixr = 2,
.edr_offset = TWL4030_INTERRUPTS_BCIEDR1,
@@ -408,7 +410,7 @@ static int twl4030_init_sih_modules(unsigned line)
* set Clear-On-Read (COR) bit.
*
* NOTE that sometimes COR polarity is documented as being
- * inverted: for MADC and BCI, COR=1 means "clear on write".
+ * inverted: for MADC, COR=1 means "clear on write".
* And for PWR_INT it's not documented...
*/
if (sih->set_cor) {
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index 7efa8789a3a2..16422de0823a 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -63,10 +63,6 @@ static u8 twl4030_start_script_address = 0x2b;
#define R_MEMORY_ADDRESS PHY_TO_OFF_PM_MASTER(0x59)
#define R_MEMORY_DATA PHY_TO_OFF_PM_MASTER(0x5a)
-#define R_PROTECT_KEY 0x0E
-#define R_KEY_1 0xC0
-#define R_KEY_2 0x0C
-
/* resource configuration registers
<RESOURCE>_DEV_GRP at address 'n+0'
<RESOURCE>_TYPE at address 'n+1'
@@ -465,15 +461,17 @@ int twl4030_remove_script(u8 flags)
{
int err = 0;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
- R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
- R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
@@ -504,7 +502,8 @@ int twl4030_remove_script(u8 flags)
return err;
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
@@ -518,13 +517,15 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
struct twl4030_resconfig *resconfig;
u8 address = twl4030_start_script_address;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
- R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
goto unlock;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
- R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
goto unlock;
@@ -546,7 +547,8 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
}
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
return;
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 10bf228ad626..aaedb11d9d2c 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -36,6 +36,9 @@
#include <linux/irq.h>
#include <linux/kthread.h>
#include <linux/i2c/twl.h>
+#include <linux/platform_device.h>
+
+#include "twl-core.h"
/*
* TWL6030 (unlike its predecessors, which had two level interrupt handling)
@@ -223,6 +226,78 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
}
EXPORT_SYMBOL(twl6030_interrupt_mask);
+int twl6030_mmc_card_detect_config(void)
+{
+ int ret;
+ u8 reg_val = 0;
+
+ /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
+ twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
+ REG_INT_MSK_LINE_B);
+ twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
+ REG_INT_MSK_STS_B);
+ /*
+ * Intially Configuring MMC_CTRL for receving interrupts &
+ * Card status on TWL6030 for MMC1
+ */
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
+ if (ret < 0) {
+ pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
+ return ret;
+ }
+ reg_val &= ~VMMC_AUTO_OFF;
+ reg_val |= SW_FC;
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
+ if (ret < 0) {
+ pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
+ return ret;
+ }
+
+ /* Configuring PullUp-PullDown register */
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
+ TWL6030_CFG_INPUT_PUPD3);
+ if (ret < 0) {
+ pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
+ ret);
+ return ret;
+ }
+ reg_val &= ~(MMC_PU | MMC_PD);
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
+ TWL6030_CFG_INPUT_PUPD3);
+ if (ret < 0) {
+ pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
+ ret);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
+
+int twl6030_mmc_card_detect(struct device *dev, int slot)
+{
+ int ret = -EIO;
+ u8 read_reg = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (pdev->id) {
+ /* TWL6030 provide's Card detect support for
+ * only MMC1 controller.
+ */
+ pr_err("Unkown MMC controller %d in %s\n", pdev->id, __func__);
+ return ret;
+ }
+ /*
+ * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
+ * 0 - Card not present ,1 - Card present
+ */
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
+ TWL6030_MMCCTRL);
+ if (ret >= 0)
+ ret = read_reg & STS_MMC;
+ return ret;
+}
+EXPORT_SYMBOL(twl6030_mmc_card_detect);
+
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c
new file mode 100644
index 000000000000..ebb059765edd
--- /dev/null
+++ b/drivers/mfd/vx855.c
@@ -0,0 +1,147 @@
+/*
+ * Linux multi-function-device driver (MFD) for the integrated peripherals
+ * of the VIA VX855 chipset
+ *
+ * Copyright (C) 2009 VIA Technologies, Inc.
+ * Copyright (C) 2010 One Laptop per Child
+ * Author: Harald Welte <HaraldWelte@viatech.com>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+
+/* offset into pci config space indicating the 16bit register containing
+ * the power management IO space base */
+#define VX855_CFG_PMIO_OFFSET 0x88
+
+/* ACPI I/O Space registers */
+#define VX855_PMIO_ACPI 0x00
+#define VX855_PMIO_ACPI_LEN 0x0b
+
+/* Processor Power Management */
+#define VX855_PMIO_PPM 0x10
+#define VX855_PMIO_PPM_LEN 0x08
+
+/* General Purpose Power Management */
+#define VX855_PMIO_GPPM 0x20
+#define VX855_PMIO_R_GPI 0x48
+#define VX855_PMIO_R_GPO 0x4c
+#define VX855_PMIO_GPPM_LEN 0x33
+
+#define VSPIC_MMIO_SIZE 0x1000
+
+static struct resource vx855_gpio_resources[] = {
+ {
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct mfd_cell vx855_cells[] = {
+ {
+ .name = "vx855_gpio",
+ .num_resources = ARRAY_SIZE(vx855_gpio_resources),
+ .resources = vx855_gpio_resources,
+
+ /* we must ignore resource conflicts, for reasons outlined in
+ * the vx855_gpio driver */
+ .ignore_resource_conflicts = true,
+ },
+};
+
+static __devinit int vx855_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int ret;
+ u16 gpio_io_offset;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return -ENODEV;
+
+ pci_read_config_word(pdev, VX855_CFG_PMIO_OFFSET, &gpio_io_offset);
+ if (!gpio_io_offset) {
+ dev_warn(&pdev->dev,
+ "BIOS did not assign PMIO base offset?!?\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* mask out the lowest seven bits, as they are always zero, but
+ * hardware returns them as 0x01 */
+ gpio_io_offset &= 0xff80;
+
+ /* As the region identified here includes many non-GPIO things, we
+ * only work with the specific registers that concern us. */
+ vx855_gpio_resources[0].start = gpio_io_offset + VX855_PMIO_R_GPI;
+ vx855_gpio_resources[0].end = vx855_gpio_resources[0].start + 3;
+ vx855_gpio_resources[1].start = gpio_io_offset + VX855_PMIO_R_GPO;
+ vx855_gpio_resources[1].end = vx855_gpio_resources[1].start + 3;
+
+ ret = mfd_add_devices(&pdev->dev, -1, vx855_cells, ARRAY_SIZE(vx855_cells),
+ NULL, 0);
+
+ /* we always return -ENODEV here in order to enable other
+ * drivers like old, not-yet-platform_device ported i2c-viapro */
+ return -ENODEV;
+out:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void vx855_remove(struct pci_dev *pdev)
+{
+ mfd_remove_devices(&pdev->dev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_device_id vx855_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
+ { 0, }
+};
+
+static struct pci_driver vx855_pci_driver = {
+ .name = "vx855",
+ .id_table = vx855_pci_tbl,
+ .probe = vx855_probe,
+ .remove = __devexit_p(vx855_remove),
+};
+
+static int vx855_init(void)
+{
+ return pci_register_driver(&vx855_pci_driver);
+}
+module_init(vx855_init);
+
+static void vx855_exit(void)
+{
+ pci_unregister_driver(&vx855_pci_driver);
+}
+module_exit(vx855_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
+MODULE_DESCRIPTION("Driver for the VIA VX855 chipset");
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 1e7aaaf6cc6f..7d2563fc15c6 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
@@ -90,14 +89,6 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
};
EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
-enum wm831x_parent {
- WM8310 = 0x8310,
- WM8311 = 0x8311,
- WM8312 = 0x8312,
- WM8320 = 0x8320,
- WM8321 = 0x8321,
-};
-
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
{
if (!wm831x->locked)
@@ -1446,7 +1437,7 @@ static struct mfd_cell backlight_devs[] = {
/*
* Instantiate the generic non-control parts of the device.
*/
-static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
+int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
{
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int rev;
@@ -1540,6 +1531,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
break;
+ case WM8325:
+ parent = WM8325;
+ wm831x->num_gpio = 12;
+ dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
+ break;
+
default:
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
ret = -EINVAL;
@@ -1620,6 +1617,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
NULL, 0);
break;
+ case WM8325:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8320_devs, ARRAY_SIZE(wm8320_devs),
+ NULL, 0);
+ break;
+
default:
/* If this happens the bus probe function is buggy */
BUG();
@@ -1660,7 +1663,7 @@ err:
return ret;
}
-static void wm831x_device_exit(struct wm831x *wm831x)
+void wm831x_device_exit(struct wm831x *wm831x)
{
wm831x_otp_exit(wm831x);
mfd_remove_devices(wm831x->dev);
@@ -1670,7 +1673,7 @@ static void wm831x_device_exit(struct wm831x *wm831x)
kfree(wm831x);
}
-static int wm831x_device_suspend(struct wm831x *wm831x)
+int wm831x_device_suspend(struct wm831x *wm831x)
{
int reg, mask;
@@ -1706,125 +1709,6 @@ static int wm831x_device_suspend(struct wm831x *wm831x)
return 0;
}
-static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *dest)
-{
- struct i2c_client *i2c = wm831x->control_data;
- int ret;
- u16 r = cpu_to_be16(reg);
-
- ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
- if (ret < 0)
- return ret;
- if (ret != 2)
- return -EIO;
-
- ret = i2c_master_recv(i2c, dest, bytes);
- if (ret < 0)
- return ret;
- if (ret != bytes)
- return -EIO;
- return 0;
-}
-
-/* Currently we allocate the write buffer on the stack; this is OK for
- * small writes - if we need to do large writes this will need to be
- * revised.
- */
-static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
- int bytes, void *src)
-{
- struct i2c_client *i2c = wm831x->control_data;
- unsigned char msg[bytes + 2];
- int ret;
-
- reg = cpu_to_be16(reg);
- memcpy(&msg[0], &reg, 2);
- memcpy(&msg[2], src, bytes);
-
- ret = i2c_master_send(i2c, msg, bytes + 2);
- if (ret < 0)
- return ret;
- if (ret < bytes + 2)
- return -EIO;
-
- return 0;
-}
-
-static int wm831x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct wm831x *wm831x;
-
- wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
- if (wm831x == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, wm831x);
- wm831x->dev = &i2c->dev;
- wm831x->control_data = i2c;
- wm831x->read_dev = wm831x_i2c_read_device;
- wm831x->write_dev = wm831x_i2c_write_device;
-
- return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
-}
-
-static int wm831x_i2c_remove(struct i2c_client *i2c)
-{
- struct wm831x *wm831x = i2c_get_clientdata(i2c);
-
- wm831x_device_exit(wm831x);
-
- return 0;
-}
-
-static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
-{
- struct wm831x *wm831x = i2c_get_clientdata(i2c);
-
- return wm831x_device_suspend(wm831x);
-}
-
-static const struct i2c_device_id wm831x_i2c_id[] = {
- { "wm8310", WM8310 },
- { "wm8311", WM8311 },
- { "wm8312", WM8312 },
- { "wm8320", WM8320 },
- { "wm8321", WM8321 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
-
-
-static struct i2c_driver wm831x_i2c_driver = {
- .driver = {
- .name = "wm831x",
- .owner = THIS_MODULE,
- },
- .probe = wm831x_i2c_probe,
- .remove = wm831x_i2c_remove,
- .suspend = wm831x_i2c_suspend,
- .id_table = wm831x_i2c_id,
-};
-
-static int __init wm831x_i2c_init(void)
-{
- int ret;
-
- ret = i2c_add_driver(&wm831x_i2c_driver);
- if (ret != 0)
- pr_err("Failed to register wm831x I2C driver: %d\n", ret);
-
- return ret;
-}
-subsys_initcall(wm831x_i2c_init);
-
-static void __exit wm831x_i2c_exit(void)
-{
- i2c_del_driver(&wm831x_i2c_driver);
-}
-module_exit(wm831x_i2c_exit);
-
-MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC");
+MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Brown");
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
new file mode 100644
index 000000000000..156b19859e81
--- /dev/null
+++ b/drivers/mfd/wm831x-i2c.c
@@ -0,0 +1,143 @@
+/*
+ * wm831x-i2c.c -- I2C access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009,2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/i2c.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *dest)
+{
+ struct i2c_client *i2c = wm831x->control_data;
+ int ret;
+ u16 r = cpu_to_be16(reg);
+
+ ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ ret = i2c_master_recv(i2c, dest, bytes);
+ if (ret < 0)
+ return ret;
+ if (ret != bytes)
+ return -EIO;
+ return 0;
+}
+
+/* Currently we allocate the write buffer on the stack; this is OK for
+ * small writes - if we need to do large writes this will need to be
+ * revised.
+ */
+static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *src)
+{
+ struct i2c_client *i2c = wm831x->control_data;
+ unsigned char msg[bytes + 2];
+ int ret;
+
+ reg = cpu_to_be16(reg);
+ memcpy(&msg[0], &reg, 2);
+ memcpy(&msg[2], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 2);
+ if (ret < 0)
+ return ret;
+ if (ret < bytes + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int wm831x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm831x *wm831x;
+
+ wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+ if (wm831x == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm831x);
+ wm831x->dev = &i2c->dev;
+ wm831x->control_data = i2c;
+ wm831x->read_dev = wm831x_i2c_read_device;
+ wm831x->write_dev = wm831x_i2c_write_device;
+
+ return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
+}
+
+static int wm831x_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+ wm831x_device_exit(wm831x);
+
+ return 0;
+}
+
+static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+ return wm831x_device_suspend(wm831x);
+}
+
+static const struct i2c_device_id wm831x_i2c_id[] = {
+ { "wm8310", WM8310 },
+ { "wm8311", WM8311 },
+ { "wm8312", WM8312 },
+ { "wm8320", WM8320 },
+ { "wm8321", WM8321 },
+ { "wm8325", WM8325 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
+
+
+static struct i2c_driver wm831x_i2c_driver = {
+ .driver = {
+ .name = "wm831x",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_i2c_probe,
+ .remove = wm831x_i2c_remove,
+ .suspend = wm831x_i2c_suspend,
+ .id_table = wm831x_i2c_id,
+};
+
+static int __init wm831x_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm831x_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register wm831x I2C driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(wm831x_i2c_init);
+
+static void __exit wm831x_i2c_exit(void)
+{
+ i2c_del_driver(&wm831x_i2c_driver);
+}
+module_exit(wm831x_i2c_exit);
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
new file mode 100644
index 000000000000..2789b151b0f9
--- /dev/null
+++ b/drivers/mfd/wm831x-spi.c
@@ -0,0 +1,232 @@
+/*
+ * wm831x-spi.c -- SPI access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009,2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/spi/spi.h>
+
+#include <linux/mfd/wm831x/core.h>
+
+static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *dest)
+{
+ u16 tx_val;
+ u16 *d = dest;
+ int r, ret;
+
+ /* Go register at a time */
+ for (r = reg; r < reg + (bytes / 2); r++) {
+ tx_val = r | 0x8000;
+
+ ret = spi_write_then_read(wm831x->control_data,
+ (u8 *)&tx_val, 2, (u8 *)d, 2);
+ if (ret != 0)
+ return ret;
+
+ *d = be16_to_cpu(*d);
+
+ d++;
+ }
+
+ return 0;
+}
+
+static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *src)
+{
+ struct spi_device *spi = wm831x->control_data;
+ u16 *s = src;
+ u16 data[2];
+ int ret, r;
+
+ /* Go register at a time */
+ for (r = reg; r < reg + (bytes / 2); r++) {
+ data[0] = r;
+ data[1] = *s++;
+
+ ret = spi_write(spi, (char *)&data, sizeof(data));
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devinit wm831x_spi_probe(struct spi_device *spi)
+{
+ struct wm831x *wm831x;
+ enum wm831x_parent type;
+
+ /* Currently SPI support for ID tables is unmerged, we're faking it */
+ if (strcmp(spi->modalias, "wm8310") == 0)
+ type = WM8310;
+ else if (strcmp(spi->modalias, "wm8311") == 0)
+ type = WM8311;
+ else if (strcmp(spi->modalias, "wm8312") == 0)
+ type = WM8312;
+ else if (strcmp(spi->modalias, "wm8320") == 0)
+ type = WM8320;
+ else if (strcmp(spi->modalias, "wm8321") == 0)
+ type = WM8321;
+ else if (strcmp(spi->modalias, "wm8325") == 0)
+ type = WM8325;
+ else {
+ dev_err(&spi->dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+
+ wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+ if (wm831x == NULL)
+ return -ENOMEM;
+
+ spi->bits_per_word = 16;
+ spi->mode = SPI_MODE_0;
+
+ dev_set_drvdata(&spi->dev, wm831x);
+ wm831x->dev = &spi->dev;
+ wm831x->control_data = spi;
+ wm831x->read_dev = wm831x_spi_read_device;
+ wm831x->write_dev = wm831x_spi_write_device;
+
+ return wm831x_device_init(wm831x, type, spi->irq);
+}
+
+static int __devexit wm831x_spi_remove(struct spi_device *spi)
+{
+ struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+
+ wm831x_device_exit(wm831x);
+
+ return 0;
+}
+
+static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m)
+{
+ struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+
+ return wm831x_device_suspend(wm831x);
+}
+
+static struct spi_driver wm8310_spi_driver = {
+ .driver = {
+ .name = "wm8310",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_spi_probe,
+ .remove = __devexit_p(wm831x_spi_remove),
+ .suspend = wm831x_spi_suspend,
+};
+
+static struct spi_driver wm8311_spi_driver = {
+ .driver = {
+ .name = "wm8311",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_spi_probe,
+ .remove = __devexit_p(wm831x_spi_remove),
+ .suspend = wm831x_spi_suspend,
+};
+
+static struct spi_driver wm8312_spi_driver = {
+ .driver = {
+ .name = "wm8312",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_spi_probe,
+ .remove = __devexit_p(wm831x_spi_remove),
+ .suspend = wm831x_spi_suspend,
+};
+
+static struct spi_driver wm8320_spi_driver = {
+ .driver = {
+ .name = "wm8320",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_spi_probe,
+ .remove = __devexit_p(wm831x_spi_remove),
+ .suspend = wm831x_spi_suspend,
+};
+
+static struct spi_driver wm8321_spi_driver = {
+ .driver = {
+ .name = "wm8321",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_spi_probe,
+ .remove = __devexit_p(wm831x_spi_remove),
+ .suspend = wm831x_spi_suspend,
+};
+
+static struct spi_driver wm8325_spi_driver = {
+ .driver = {
+ .name = "wm8325",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_spi_probe,
+ .remove = __devexit_p(wm831x_spi_remove),
+ .suspend = wm831x_spi_suspend,
+};
+
+static int __init wm831x_spi_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&wm8310_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8310 SPI driver: %d\n", ret);
+
+ ret = spi_register_driver(&wm8311_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8311 SPI driver: %d\n", ret);
+
+ ret = spi_register_driver(&wm8312_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8312 SPI driver: %d\n", ret);
+
+ ret = spi_register_driver(&wm8320_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8320 SPI driver: %d\n", ret);
+
+ ret = spi_register_driver(&wm8321_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8321 SPI driver: %d\n", ret);
+
+ ret = spi_register_driver(&wm8325_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8325 SPI driver: %d\n", ret);
+
+ return 0;
+}
+subsys_initcall(wm831x_spi_init);
+
+static void __exit wm831x_spi_exit(void)
+{
+ spi_unregister_driver(&wm8325_spi_driver);
+ spi_unregister_driver(&wm8321_spi_driver);
+ spi_unregister_driver(&wm8320_spi_driver);
+ spi_unregister_driver(&wm8312_spi_driver);
+ spi_unregister_driver(&wm8311_spi_driver);
+ spi_unregister_driver(&wm8310_spi_driver);
+}
+module_exit(wm831x_spi_exit);
+
+MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown");
OpenPOWER on IntegriCloud