diff options
author | Jiri Kosina <jkosina@suse.cz> | 2010-12-10 15:19:18 +0100 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-12-10 15:19:18 +0100 |
commit | 2ade0c1d9d93b7642212657ef76f4a1e30233711 (patch) | |
tree | 63bc720c0ffe5f4760cac4ed617b9870b050175e /drivers/mfd | |
parent | 504499f22c08a03e2e19dc88d31aa0ecd2ac815e (diff) | |
parent | 6313e3c21743cc88bb5bd8aa72948ee1e83937b6 (diff) | |
download | talos-op-linux-2ade0c1d9d93b7642212657ef76f4a1e30233711.tar.gz talos-op-linux-2ade0c1d9d93b7642212657ef76f4a1e30233711.zip |
Merge branch 'master' into upstream
Diffstat (limited to 'drivers/mfd')
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, ®value); + 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", - ®ulator_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", + ®ulator_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, ®_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, ®_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], ®, 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], ®, 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"); |