diff options
94 files changed, 6799 insertions, 1750 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 new file mode 100644 index 00000000000..1b62230b33b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 @@ -0,0 +1,15 @@ +What: /sys/bus/i2c/devices/.../output_hvled[n] +Date: April 2012 +KernelVersion: 3.5 +Contact: Johan Hovold <jhovold@gmail.com> +Description: + Set the controlling backlight device for high-voltage current + sink HVLED[n] (n = 1, 2) (0, 1). + +What: /sys/bus/i2c/devices/.../output_lvled[n] +Date: April 2012 +KernelVersion: 3.5 +Contact: Johan Hovold <jhovold@gmail.com> +Description: + Set the controlling led device for low-voltage current sink + LVLED[n] (n = 1..5) (0..3). diff --git a/Documentation/devicetree/bindings/mfd/da9052-i2c.txt b/Documentation/devicetree/bindings/mfd/da9052-i2c.txt new file mode 100644 index 00000000000..1857f4a6b9a --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/da9052-i2c.txt @@ -0,0 +1,60 @@ +* Dialog DA9052/53 Power Management Integrated Circuit (PMIC) + +Required properties: +- compatible : Should be "dlg,da9052", "dlg,da9053-aa", + "dlg,da9053-ab", or "dlg,da9053-bb" + +Sub-nodes: +- regulators : Contain the regulator nodes. The DA9052/53 regulators are + bound using their names as listed below: + + buck0 : regulator BUCK0 + buck1 : regulator BUCK1 + buck2 : regulator BUCK2 + buck3 : regulator BUCK3 + ldo4 : regulator LDO4 + ldo5 : regulator LDO5 + ldo6 : regulator LDO6 + ldo7 : regulator LDO7 + ldo8 : regulator LDO8 + ldo9 : regulator LDO9 + ldo10 : regulator LDO10 + ldo11 : regulator LDO11 + ldo12 : regulator LDO12 + ldo13 : regulator LDO13 + + The bindings details of individual regulator device can be found in: + Documentation/devicetree/bindings/regulator/regulator.txt + +Examples: + +i2c@63fc8000 { /* I2C1 */ + status = "okay"; + + pmic: dialog@48 { + compatible = "dlg,da9053-aa"; + reg = <0x48>; + + regulators { + buck0 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <2075000>; + }; + + buck1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <2075000>; + }; + + buck2 { + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <2500000>; + }; + + buck3 { + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <2500000>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt new file mode 100644 index 00000000000..645f5eaadb3 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/tps65910.txt @@ -0,0 +1,133 @@ +TPS65910 Power Management Integrated Circuit + +Required properties: +- compatible: "ti,tps65910" or "ti,tps65911" +- reg: I2C slave address +- interrupts: the interrupt outputs of the controller +- #gpio-cells: number of cells to describe a GPIO, this should be 2. + The first cell is the GPIO number. + The second cell is used to specify additional options <unused>. +- gpio-controller: mark the device as a GPIO controller +- #interrupt-cells: the number of cells to describe an IRQ, this should be 2. + The first cell is the IRQ number. + The second cell is the flags, encoded as the trigger masks from + Documentation/devicetree/bindings/interrupts.txt +- regulators: This is the list of child nodes that specify the regulator + initialization data for defined regulators. Not all regulators for the given + device need to be present. The definition for each of these nodes is defined + using the standard binding for regulators found at + Documentation/devicetree/bindings/regulator/regulator.txt. + + The valid names for regulators are: + tps65910: vrtc, vio, vdd1, vdd2, vdd3, vdig1, vdig2, vpll, vdac, vaux1, + vaux2, vaux33, vmmc + tps65911: vrtc, vio, vdd1, vdd3, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5, + ldo6, ldo7, ldo8 + +Optional properties: +- ti,vmbch-threshold: (tps65911) main battery charged threshold + comparator. (see VMBCH_VSEL in TPS65910 datasheet) +- ti,vmbch2-threshold: (tps65911) main battery discharged threshold + comparator. (see VMBCH_VSEL in TPS65910 datasheet) +- ti,en-gpio-sleep: enable sleep control for gpios + There should be 9 entries here, one for each gpio. + +Regulator Optional properties: +- ti,regulator-ext-sleep-control: enable external sleep + control through external inputs [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)] + If this property is not defined, it defaults to 0 (not enabled). + +Example: + + pmu: tps65910@d2 { + compatible = "ti,tps65910"; + reg = <0xd2>; + interrupt-parent = <&intc>; + interrupts = < 0 118 0x04 >; + + #gpio-cells = <2>; + gpio-controller; + + #interrupt-cells = <2>; + interrupt-controller; + + ti,vmbch-threshold = 0; + ti,vmbch2-threshold = 0; + + ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; + + regulators { + vdd1_reg: vdd1 { + regulator-min-microvolt = < 600000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <0>; + }; + vdd2_reg: vdd2 { + regulator-min-microvolt = < 600000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <4>; + }; + vddctrl_reg: vddctrl { + regulator-min-microvolt = < 600000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <0>; + }; + vio_reg: vio { + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <1>; + }; + ldo1_reg: ldo1 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + ti,regulator-ext-sleep-control = <0>; + }; + ldo2_reg: ldo2 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + ti,regulator-ext-sleep-control = <0>; + }; + ldo3_reg: ldo3 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + ti,regulator-ext-sleep-control = <0>; + }; + ldo4_reg: ldo4 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + ti,regulator-ext-sleep-control = <0>; + }; + ldo5_reg: ldo5 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + ti,regulator-ext-sleep-control = <0>; + }; + ldo6_reg: ldo6 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + ti,regulator-ext-sleep-control = <0>; + }; + ldo7_reg: ldo7 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <1>; + }; + ldo8_reg: ldo8 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + ti,regulator-ext-sleep-control = <1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/twl6040.txt b/Documentation/devicetree/bindings/mfd/twl6040.txt new file mode 100644 index 00000000000..bc67c6f424a --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/twl6040.txt @@ -0,0 +1,62 @@ +Texas Instruments TWL6040 family + +The TWL6040s are 8-channel high quality low-power audio codecs providing audio +and vibra functionality on OMAP4+ platforms. +They are connected ot the host processor via i2c for commands, McPDM for audio +data and commands. + +Required properties: +- compatible : Must be "ti,twl6040"; +- reg: must be 0x4b for i2c address +- interrupts: twl6040 has one interrupt line connecteded to the main SoC +- interrupt-parent: The parent interrupt controller +- twl6040,audpwron-gpio: Power on GPIO line for the twl6040 + +- vio-supply: Regulator for the twl6040 VIO supply +- v2v1-supply: Regulator for the twl6040 V2V1 supply + +Optional properties, nodes: +- enable-active-high: To power on the twl6040 during boot. + +Vibra functionality +Required properties: +- vddvibl-supply: Regulator for the left vibra motor +- vddvibr-supply: Regulator for the right vibra motor +- vibra { }: Configuration section for vibra parameters containing the following + properties: +- ti,vibldrv-res: Resistance parameter for left driver +- ti,vibrdrv-res: Resistance parameter for right driver +- ti,viblmotor-res: Resistance parameter for left motor +- ti,viblmotor-res: Resistance parameter for right motor + +Optional properties within vibra { } section: +- vddvibl_uV: If the vddvibl default voltage need to be changed +- vddvibr_uV: If the vddvibr default voltage need to be changed + +Example: +&i2c1 { + twl6040: twl@4b { + compatible = "ti,twl6040"; + reg = <0x4b>; + + interrupts = <0 119 4>; + interrupt-parent = <&gic>; + twl6040,audpwron-gpio = <&gpio4 31 0>; + + vio-supply = <&v1v8>; + v2v1-supply = <&v2v1>; + enable-active-high; + + /* regulators for vibra motor */ + vddvibl-supply = <&vbat>; + vddvibr-supply = <&vbat>; + + vibra { + /* Vibra driver, motor resistance parameters */ + ti,vibldrv-res = <8>; + ti,vibrdrv-res = <3>; + ti,viblmotor-res = <10>; + ti,vibrmotor-res = <10>; + }; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 6f90c64a253..cc710d2ef00 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3382,6 +3382,12 @@ W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html S: Supported F: drivers/scsi/ips.* +ICH LPC AND GPIO DRIVER +M: Peter Tyser <ptyser@xes-inc.com> +S: Maintained +F: drivers/mfd/lpc_ich.c +F: drivers/gpio/gpio-ich.c + IDE SUBSYSTEM M: "David S. Miller" <davem@davemloft.net> L: linux-ide@vger.kernel.org diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index b23a643f03f..fba8adea421 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -206,7 +206,7 @@ static struct resource ab8500_resources[] = { }; struct platform_device ab8500_device = { - .name = "ab8500-i2c", + .name = "ab8500-core", .id = 0, .dev = { .platform_data = &ab8500_platdata, diff --git a/arch/x86/include/asm/sta2x11.h b/arch/x86/include/asm/sta2x11.h new file mode 100644 index 00000000000..e9d32df89cc --- /dev/null +++ b/arch/x86/include/asm/sta2x11.h @@ -0,0 +1,12 @@ +/* + * Header file for STMicroelectronics ConneXt (STA2X11) IOHub + */ +#ifndef __ASM_STA2X11_H +#define __ASM_STA2X11_H + +#include <linux/pci.h> + +/* This needs to be called from the MFD to configure its sub-devices */ +struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev); + +#endif /* __ASM_STA2X11_H */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index aa3642cb820..0356099ae04 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -167,6 +167,14 @@ config GPIO_PXA help Say yes here to support the PXA GPIO device +config GPIO_STA2X11 + bool "STA2x11/ConneXt GPIO support" + depends on MFD_STA2X11 + select GENERIC_IRQ_CHIP + help + Say yes here to support the STA2x11/ConneXt GPIO device. + The GPIO module has 128 GPIO pins with alternate functions. + config GPIO_XILINX bool "Xilinx GPIO support" depends on PPC_OF || MICROBLAZE @@ -180,13 +188,13 @@ config GPIO_VR41XX Say yes here to support the NEC VR4100 series General-purpose I/O Uint config GPIO_SCH - tristate "Intel SCH/TunnelCreek GPIO" + tristate "Intel SCH/TunnelCreek/Centerton GPIO" depends on PCI && X86 select MFD_CORE select LPC_SCH help - Say yes here to support GPIO interface on Intel Poulsbo SCH - or Intel Tunnel Creek processor. + Say yes here to support GPIO interface on Intel Poulsbo SCH, + Intel Tunnel Creek processor or Intel Centerton processor. The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are powered by the core power rail and are turned off during sleep modes (S3 and higher). The remaining four GPIOs are powered by @@ -195,6 +203,22 @@ config GPIO_SCH system from the Suspend-to-RAM state. The Intel Tunnel Creek processor has 5 GPIOs powered by the core power rail and 9 from suspend power supply. + The Intel Centerton processor has a total of 30 GPIO pins. + Twenty-one are powered by the core power rail and 9 from the + suspend power supply. + +config GPIO_ICH + tristate "Intel ICH GPIO" + depends on PCI && X86 + select MFD_CORE + select LPC_ICH + help + Say yes here to support the GPIO functionality of a number of Intel + ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8 + ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg + Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake). + + If unsure, say N. config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 07a79e24540..fde36e5e353 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o +obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o @@ -51,6 +52,7 @@ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o +obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c new file mode 100644 index 00000000000..b7c06517403 --- /dev/null +++ b/drivers/gpio/gpio-ich.c @@ -0,0 +1,419 @@ +/* + * Intel ICH6-10, Series 5 and 6 GPIO driver + * + * Copyright (C) 2010 Extreme Engineering Solutions. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/mfd/lpc_ich.h> + +#define DRV_NAME "gpio_ich" + +/* + * GPIO register offsets in GPIO I/O space. + * Each chunk of 32 GPIOs is manipulated via its own USE_SELx, IO_SELx, and + * LVLx registers. Logic in the read/write functions takes a register and + * an absolute bit number and determines the proper register offset and bit + * number in that register. For example, to read the value of GPIO bit 50 + * the code would access offset ichx_regs[2(=GPIO_LVL)][1(=50/32)], + * bit 18 (50%32). + */ +enum GPIO_REG { + GPIO_USE_SEL = 0, + GPIO_IO_SEL, + GPIO_LVL, +}; + +static const u8 ichx_regs[3][3] = { + {0x00, 0x30, 0x40}, /* USE_SEL[1-3] offsets */ + {0x04, 0x34, 0x44}, /* IO_SEL[1-3] offsets */ + {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ +}; + +#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start) +#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start) + +struct ichx_desc { + /* Max GPIO pins the chipset can have */ + uint ngpio; + + /* Whether the chipset has GPIO in GPE0_STS in the PM IO region */ + bool uses_gpe0; + + /* USE_SEL is bogus on some chipsets, eg 3100 */ + u32 use_sel_ignore[3]; + + /* Some chipsets have quirks, let these use their own request/get */ + int (*request)(struct gpio_chip *chip, unsigned offset); + int (*get)(struct gpio_chip *chip, unsigned offset); +}; + +static struct { + spinlock_t lock; + struct platform_device *dev; + struct gpio_chip chip; + struct resource *gpio_base; /* GPIO IO base */ + struct resource *pm_base; /* Power Mangagment IO base */ + struct ichx_desc *desc; /* Pointer to chipset-specific description */ + u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */ +} ichx_priv; + +static int modparam_gpiobase = -1; /* dynamic */ +module_param_named(gpiobase, modparam_gpiobase, int, 0444); +MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, " + "which is the default."); + +static int ichx_write_bit(int reg, unsigned nr, int val, int verify) +{ + unsigned long flags; + u32 data, tmp; + int reg_nr = nr / 32; + int bit = nr & 0x1f; + int ret = 0; + + spin_lock_irqsave(&ichx_priv.lock, flags); + + data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + if (val) + data |= 1 << bit; + else + data &= ~(1 << bit); + ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + if (verify && data != tmp) + ret = -EPERM; + + spin_unlock_irqrestore(&ichx_priv.lock, flags); + + return ret; +} + +static int ichx_read_bit(int reg, unsigned nr) +{ + unsigned long flags; + u32 data; + int reg_nr = nr / 32; + int bit = nr & 0x1f; + + spin_lock_irqsave(&ichx_priv.lock, flags); + + data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + + spin_unlock_irqrestore(&ichx_priv.lock, flags); + + return data & (1 << bit) ? 1 : 0; +} + +static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + /* + * Try setting pin as an input and verify it worked since many pins + * are output-only. + */ + if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1)) + return -EINVAL; + + return 0; +} + +static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, + int val) +{ + /* Set GPIO output value. */ + ichx_write_bit(GPIO_LVL, nr, val, 0); + + /* + * Try setting pin as an output and verify it worked since many pins + * are input-only. + */ + if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1)) + return -EINVAL; + + return 0; +} + +static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) +{ + return ichx_read_bit(GPIO_LVL, nr); +} + +static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) +{ + unsigned long flags; + u32 data; + + /* + * GPI 0 - 15 need to be read from the power management registers on + * a ICH6/3100 bridge. + */ + if (nr < 16) { + if (!ichx_priv.pm_base) + return -ENXIO; + + spin_lock_irqsave(&ichx_priv.lock, flags); + + /* GPI 0 - 15 are latched, write 1 to clear*/ + ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base); + data = ICHX_READ(0, ichx_priv.pm_base); + + spin_unlock_irqrestore(&ichx_priv.lock, flags); + + return (data >> 16) & (1 << nr) ? 1 : 0; + } else { + return ichx_gpio_get(chip, nr); + } +} + +static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) +{ + /* + * Note we assume the BIOS properly set a bridge's USE value. Some + * chips (eg Intel 3100) have bogus USE values though, so first see if + * the chipset's USE value can be trusted for this specific bit. + * If it can't be trusted, assume that the pin can be used as a GPIO. + */ + if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f))) + return 1; + + return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV; +} + +static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr) +{ + /* + * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100 + * bridge as they are controlled by USE register bits 0 and 1. See + * "Table 704 GPIO_USE_SEL1 register" in the i3100 datasheet for + * additional info. + */ + if (nr == 16 || nr == 17) + nr -= 16; + + return ichx_gpio_request(chip, nr); +} + +static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val) +{ + ichx_write_bit(GPIO_LVL, nr, val, 0); +} + +static void __devinit ichx_gpiolib_setup(struct gpio_chip *chip) +{ + chip->owner = THIS_MODULE; + chip->label = DRV_NAME; + chip->dev = &ichx_priv.dev->dev; + + /* Allow chip-specific overrides of request()/get() */ + chip->request = ichx_priv.desc->request ? + ichx_priv.desc->request : ichx_gpio_request; + chip->get = ichx_priv.desc->get ? + ichx_priv.desc->get : ichx_gpio_get; + + chip->set = ichx_gpio_set; + chip->direction_input = ichx_gpio_direction_input; + chip->direction_output = ichx_gpio_direction_output; + chip->base = modparam_gpiobase; + chip->ngpio = ichx_priv.desc->ngpio; + chip->can_sleep = 0; + chip->dbg_show = NULL; +} + +/* ICH6-based, 631xesb-based */ +static struct ichx_desc ich6_desc = { + /* Bridges using the ICH6 controller need fixups for GPIO 0 - 17 */ + .request = ich6_gpio_request, + .get = ich6_gpio_get, + + /* GPIO 0-15 are read in the GPE0_STS PM register */ + .uses_gpe0 = true, + + .ngpio = 50, +}; + +/* Intel 3100 */ +static struct ichx_desc i3100_desc = { + /* + * Bits 16,17, 20 of USE_SEL and bit 16 of USE_SEL2 always read 0 on + * the Intel 3100. See "Table 712. GPIO Summary Table" of 3100 + * Datasheet for more info. + */ + .use_sel_ignore = {0x00130000, 0x00010000, 0x0}, + + /* The 3100 needs fixups for GPIO 0 - 17 */ + .request = ich6_gpio_request, + .get = ich6_gpio_get, + + /* GPIO 0-15 are read in the GPE0_STS PM register */ + .uses_gpe0 = true, + + .ngpio = 50, +}; + +/* ICH7 and ICH8-based */ +static struct ichx_desc ich7_desc = { + .ngpio = 50, +}; + +/* ICH9-based */ +static struct ichx_desc ich9_desc = { + .ngpio = 61, +}; + +/* ICH10-based - Consumer/corporate versions have different amount of GPIO */ +static struct ichx_desc ich10_cons_desc = { + .ngpio = 61, +}; +static struct ichx_desc ich10_corp_desc = { + .ngpio = 72, +}; + +/* Intel 5 series, 6 series, 3400 series, and C200 series */ +static struct ichx_desc intel5_desc = { + .ngpio = 76, +}; + +static int __devinit ichx_gpio_probe(struct platform_device *pdev) +{ + struct resource *res_base, *res_pm; + int err; + struct lpc_ich_info *ich_info = pdev->dev.platform_data; + + if (!ich_info) + return -ENODEV; + + ichx_priv.dev = pdev; + + switch (ich_info->gpio_version) { + case ICH_I3100_GPIO: + ichx_priv.desc = &i3100_desc; + break; + case ICH_V5_GPIO: + ichx_priv.desc = &intel5_desc; + break; + case ICH_V6_GPIO: + ichx_priv.desc = &ich6_desc; + break; + case ICH_V7_GPIO: + ichx_priv.desc = &ich7_desc; + break; + case ICH_V9_GPIO: + ichx_priv.desc = &ich9_desc; + break; + case ICH_V10CORP_GPIO: + ichx_priv.desc = &ich10_corp_desc; + break; + case ICH_V10CONS_GPIO: + ichx_priv.desc = &ich10_cons_desc; + break; + default: + return -ENODEV; + } + + res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); + if (!res_base || !res_base->start || !res_base->end) + return -ENODEV; + + if (!request_region(res_base->start, resource_size(res_base), + pdev->name)) + return -EBUSY; + + ichx_priv.gpio_base = res_base; + + /* + * If necessary, determine the I/O address of ACPI/power management + * registers which are needed to read the the GPE0 register for GPI pins + * 0 - 15 on some chipsets. + */ + if (!ichx_priv.desc->uses_gpe0) + goto init; + + res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0); + if (!res_pm) { + pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n"); + goto init; + } + + if (!request_region(res_pm->start, resource_size(res_pm), + pdev->name)) { + pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n"); + goto init; + } + + ichx_priv.pm_base = res_pm; + +init: + ichx_gpiolib_setup(&ichx_priv.chip); + err = gpiochip_add(&ichx_priv.chip); + if (err) { + pr_err("Failed to register GPIOs\n"); + goto add_err; + } + + pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base, + ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME); + + return 0; + +add_err: + release_region(ichx_priv.gpio_base->start, + resource_size(ichx_priv.gpio_base)); + if (ichx_priv.pm_base) + release_region(ichx_priv.pm_base->start, + resource_size(ichx_priv.pm_base)); + return err; +} + +static int __devexit ichx_gpio_remove(struct platform_device *pdev) +{ + int err; + + err = gpiochip_remove(&ichx_priv.chip); + if (err) { + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", err); + return err; + } + + release_region(ichx_priv.gpio_base->start, + resource_size(ichx_priv.gpio_base)); + if (ichx_priv.pm_base) + release_region(ichx_priv.pm_base->start, + resource_size(ichx_priv.pm_base)); + + return 0; +} + +static struct platform_driver ichx_gpio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, + .probe = ichx_gpio_probe, + .remove = __devexit_p(ichx_gpio_remove), +}; + +module_platform_driver(ichx_gpio_driver); + +MODULE_AUTHOR("Peter Tyser <ptyser@xes-inc.com>"); +MODULE_DESCRIPTION("GPIO interface for Intel ICH series"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:"DRV_NAME); diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 8cadf4d683a..424dce8e3f3 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -232,6 +232,14 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev) sch_gpio_resume.ngpio = 9; break; + case PCI_DEVICE_ID_INTEL_CENTERTON_ILB: + sch_gpio_core.base = 0; + sch_gpio_core.ngpio = 21; + + sch_gpio_resume.base = 21; + sch_gpio_resume.ngpio = 9; + break; + default: return -ENODEV; } diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c new file mode 100644 index 00000000000..38416be8ba1 --- /dev/null +++ b/drivers/gpio/gpio-sta2x11.c @@ -0,0 +1,435 @@ +/* + * STMicroelectronics ConneXt (STA2X11) GPIO driver + * + * Copyright 2012 ST Microelectronics (Alessandro Rubini) + * Based on gpio-ml-ioh.c, Copyright 2010 OKI Semiconductors Ltd. + * Also based on previous sta2x11 work, Copyright 2011 Wind River Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/mfd/sta2x11-mfd.h> + +struct gsta_regs { + u32 dat; /* 0x00 */ + u32 dats; + u32 datc; + u32 pdis; + u32 dir; /* 0x10 */ + u32 dirs; + u32 dirc; + u32 unused_1c; + u32 afsela; /* 0x20 */ + u32 unused_24[7]; + u32 rimsc; /* 0x40 */ + u32 fimsc; + u32 is; + u32 ic; +}; + +struct gsta_gpio { + spinlock_t lock; + struct device *dev; + void __iomem *reg_base; + struct gsta_regs __iomem *regs[GSTA_NR_BLOCKS]; + struct gpio_chip gpio; + int irq_base; + /* FIXME: save the whole config here (AF, ...) */ + unsigned irq_type[GSTA_NR_GPIO]; +}; + +static inline struct gsta_regs __iomem *__regs(struct gsta_gpio *chip, int nr) +{ + return chip->regs[nr / GSTA_GPIO_PER_BLOCK]; +} + +static inline u32 __bit(int nr) +{ + return 1U << (nr % GSTA_GPIO_PER_BLOCK); +} + +/* + * gpio methods + */ + +static void gsta_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +{ + struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio); + struct gsta_regs __iomem *regs = __regs(chip, nr); + u32 bit = __bit(nr); + + if (val) + writel(bit, ®s->dats); + else + writel(bit, ®s->datc); +} + +static int gsta_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio); + struct gsta_regs __iomem *regs = __regs(chip, nr); + u32 bit = __bit(nr); + + return readl(®s->dat) & bit; +} + +static int gsta_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, + int val) +{ + struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio); + struct gsta_regs __iomem *regs = __regs(chip, nr); + u32 bit = __bit(nr); + + writel(bit, ®s->dirs); + /* Data register after direction, otherwise pullup/down is selected */ + if (val) + writel(bit, ®s->dats); + else + writel(bit, ®s->datc); + return 0; +} + +static int gsta_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio); + struct gsta_regs __iomem *regs = __regs(chip, nr); + u32 bit = __bit(nr); + + writel(bit, ®s->dirc); + return 0; +} + +static int gsta_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio); + return chip->irq_base + offset; +} + +static void gsta_gpio_setup(struct gsta_gpio *chip) /* called from probe */ +{ + struct gpio_chip *gpio = &chip->gpio; + + /* + * ARCH_NR_GPIOS is currently 256 and dynamic allocation starts + * from the end. However, for compatibility, we need the first + * ConneXt device to start from gpio 0: it's the main chipset + * on most boards so documents and drivers assume gpio0..gpio127 + */ + static int gpio_base; + + gpio->label = dev_name(chip->dev); + gpio->owner = THIS_MODULE; + gpio->direction_input = gsta_gpio_direction_input; + gpio->get = gsta_gpio_get; + gpio->direction_output = gsta_gpio_direction_output; + gpio->set = gsta_gpio_set; + gpio->dbg_show = NULL; + gpio->base = gpio_base; + gpio->ngpio = GSTA_NR_GPIO; + gpio->can_sleep = 0; + gpio->to_irq = gsta_gpio_to_irq; + + /* + * After the first device, turn to dynamic gpio numbers. + * For example, with ARCH_NR_GPIOS = 256 we can fit two cards + */ + if (!gpio_base) + gpio_base = -1; +} + +/* + * Special method: alternate functions and pullup/pulldown. This is only + * invoked on startup to configure gpio's according to platform data. + * FIXME : this functionality shall be managed (and exported to other drivers) + * via the pin control subsystem. + */ +static void gsta_set_config(struct gsta_gpio *chip, int nr, unsigned cfg) +{ + struct gsta_regs __iomem *regs = __regs(chip, nr); + unsigned long flags; + u32 bit = __bit(nr); + u32 val; + int err = 0; + + pr_info("%s: %p %i %i\n", __func__, chip, nr, cfg); + + if (cfg == PINMUX_TYPE_NONE) + return; + + /* Alternate function or not? */ + spin_lock_irqsave(&chip->lock, flags); + val = readl(®s->afsela); + if (cfg == PINMUX_TYPE_FUNCTION) + val |= bit; + else + val &= ~bit; + writel(val | bit, ®s->afsela); + if (cfg == PINMUX_TYPE_FUNCTION) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + + /* not alternate function: set details */ + switch (cfg) { + case PINMUX_TYPE_OUTPUT_LOW: + writel(bit, ®s->dirs); + writel(bit, ®s->datc); + break; + case PINMUX_TYPE_OUTPUT_HIGH: + writel(bit, ®s->dirs); + writel(bit, ®s->dats); + break; + case PINMUX_TYPE_INPUT: + writel(bit, ®s->dirc); + val = readl(®s->pdis) | bit; + writel(val, ®s->pdis); + break; + case PINMUX_TYPE_INPUT_PULLUP: + writel(bit, ®s->dirc); + val = readl(®s->pdis) & ~bit; + writel(val, ®s->pdis); + writel(bit, ®s->dats); + break; + case PINMUX_TYPE_INPUT_PULLDOWN: + writel(bit, ®s->dirc); + val = readl(®s->pdis) & ~bit; + writel(val, ®s->pdis); + writel(bit, ®s->datc); + break; + default: + err = 1; + } + spin_unlock_irqrestore(&chip->lock, flags); + if (err) + pr_err("%s: chip %p, pin %i, cfg %i is invalid\n", + __func__, chip, nr, cfg); +} + +/* + * Irq methods + */ + +static void gsta_irq_disable(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct gsta_gpio *chip = gc->private; + int nr = data->irq - chip->irq_base; + struct gsta_regs __iomem *regs = __regs(chip, nr); + u32 bit = __bit(nr); + u32 val; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + if (chip->irq_type[nr] & IRQ_TYPE_EDGE_RISING) { + val = readl(®s->rimsc) & ~bit; + writel(val, ®s->rimsc); + } + if (chip->irq_type[nr] & IRQ_TYPE_EDGE_FALLING) { + val = readl(®s->fimsc) & ~bit; + writel(val, ®s->fimsc); + } + spin_unlock_irqrestore(&chip->lock, flags); + return; +} + +static void gsta_irq_enable(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct gsta_gpio *chip = gc->private; + int nr = data->irq - chip->irq_base; + struct gsta_regs __iomem *regs = __regs(chip, nr); + u32 bit = __bit(nr); + u32 val; + int type; + unsigned long flags; + + type = chip->irq_type[nr]; + + spin_lock_irqsave(&chip->lock, flags); + val = readl(®s->rimsc); + if (type & IRQ_TYPE_EDGE_RISING) + writel(val | bit, ®s->rimsc); + else + writel(val & ~bit, ®s->rimsc); + val = readl(®s->rimsc); + if (type & IRQ_TYPE_EDGE_FALLING) + writel(val | bit, ®s->fimsc); + else + writel(val & ~bit, ®s->fimsc); + spin_unlock_irqrestore(&chip->lock, flags); + return; +} + +static int gsta_irq_type(struct irq_data *d, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct gsta_gpio *chip = gc->private; + int nr = d->irq - chip->irq_base; + + /* We only support edge interrupts */ + if (!(type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) { + pr_debug("%s: unsupported type 0x%x\n", __func__, type); + return -EINVAL; + } + + chip->irq_type[nr] = type; /* used for enable/disable */ + + gsta_irq_enable(d); + return 0; +} + +static irqreturn_t gsta_gpio_handler(int irq, void *dev_id) +{ + struct gsta_gpio *chip = dev_id; + struct gsta_regs __iomem *regs; + u32 is; + int i, nr, base; + irqreturn_t ret = IRQ_NONE; + + for (i = 0; i < GSTA_NR_BLOCKS; i++) { + regs = chip->regs[i]; + base = chip->irq_base + i * GSTA_GPIO_PER_BLOCK; + while ((is = readl(®s->is))) { + nr = __ffs(is); + irq = base + nr; + generic_handle_irq(irq); + writel(1 << nr, ®s->ic); + ret = IRQ_HANDLED; + } + } + return ret; +} + +static __devinit void gsta_alloc_irq_chip(struct gsta_gpio *chip) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base, + chip->reg_base, handle_simple_irq); + gc->private = chip; + ct = gc->chip_types; + + ct->chip.irq_set_type = gsta_irq_type; + ct->chip.irq_disable = gsta_irq_disable; + ct->chip.irq_enable = gsta_irq_enable; + + /* FIXME: this makes at most 32 interrupts. Request 0 by now */ + irq_setup_generic_chip(gc, 0 /* IRQ_MSK(GSTA_GPIO_PER_BLOCK) */, 0, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); + + /* Set up all all 128 interrupts: code from setup_generic_chip */ + { + struct irq_chip_type *ct = gc->chip_types; + int i, j; + for (j = 0; j < GSTA_NR_GPIO; j++) { + i = chip->irq_base + j; + irq_set_chip_and_handler(i, &ct->chip, ct->handler); + irq_set_chip_data(i, gc); + irq_modify_status(i, IRQ_NOREQUEST | IRQ_NOPROBE, 0); + } + gc->irq_cnt = i - gc->irq_base; + } +} + +/* The platform device used here is instantiated by the MFD device */ +static int __devinit gsta_probe(struct platform_device *dev) +{ + int i, err; + struct pci_dev *pdev; + struct sta2x11_gpio_pdata *gpio_pdata; + struct gsta_gpio *chip; + struct resource *res; + + pdev = *(struct pci_dev **)(dev->dev.platform_data); + gpio_pdata = dev_get_platdata(&pdev->dev); + + if (gpio_pdata == NULL) + dev_err(&dev->dev, "no gpio config\n"); + pr_debug("gpio config: %p\n", gpio_pdata); + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + + chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL); + chip->dev = &dev->dev; + chip->reg_base = devm_request_and_ioremap(&dev->dev, res); + + for (i = 0; i < GSTA_NR_BLOCKS; i++) { + chip->regs[i] = chip->reg_base + i * 4096; + /* disable all irqs */ + writel(0, &chip->regs[i]->rimsc); + writel(0, &chip->regs[i]->fimsc); + writel(~0, &chip->regs[i]->ic); + } + spin_lock_init(&chip->lock); + gsta_gpio_setup(chip); + for (i = 0; i < GSTA_NR_GPIO; i++) + gsta_set_config(chip, i, gpio_pdata->pinconfig[i]); + + /* 384 was used in previous code: be compatible for other drivers */ + err = irq_alloc_descs(-1, 384, GSTA_NR_GPIO, NUMA_NO_NODE); + if (err < 0) { + dev_warn(&dev->dev, "sta2x11 gpio: Can't get irq base (%i)\n", + -err); + return err; + } + chip->irq_base = err; + gsta_alloc_irq_chip(chip); + + err = request_irq(pdev->irq, gsta_gpio_handler, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (err < 0) { + dev_err(&dev->dev, "sta2x11 gpio: Can't request irq (%i)\n", + -err); + goto err_free_descs; + } + + err = gpiochip_add(&chip->gpio); + if (err < 0) { + dev_err(&dev->dev, "sta2x11 gpio: Can't register (%i)\n", + -err); + goto err_free_irq; + } + + platform_set_drvdata(dev, chip); + return 0; + +err_free_irq: + free_irq(pdev->irq, chip); +err_free_descs: + irq_free_descs(chip->irq_base, GSTA_NR_GPIO); + return err; +} + +static struct platform_driver sta2x11_gpio_platform_driver = { + .driver = { + .name = "sta2x11-gpio", + .owner = THIS_MODULE, + }, + .probe = gsta_probe, +}; + +module_platform_driver(sta2x11_gpio_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("sta2x11_gpio GPIO driver"); diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 7eef648a335..c1ad2884f2e 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -18,14 +18,27 @@ #include <linux/errno.h> #include <linux/gpio.h> #include <linux/i2c.h> +#include <linux/platform_device.h> #include <linux/mfd/tps65910.h> +#include <linux/of_device.h> + +struct tps65910_gpio { + struct gpio_chip gpio_chip; + struct tps65910 *tps65910; +}; + +static inline struct tps65910_gpio *to_tps65910_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tps65910_gpio, gpio_chip); +} static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - uint8_t val; + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; + unsigned int val; - tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val); + tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val); if (val & GPIO_STS_MASK) return 1; @@ -36,83 +49,170 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; if (value) - tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_SET_MASK); else - tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_SET_MASK); } static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; /* Set the initial value */ tps65910_gpio_set(gc, offset, value); - return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); } static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; - return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); } -void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base) +#ifdef CONFIG_OF +static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev, + struct tps65910 *tps65910, int chip_ngpio) { + struct tps65910_board *tps65910_board = tps65910->of_plat_data; + unsigned int prop_array[TPS6591X_MAX_NUM_GPIO]; + int ngpio = min(chip_ngpio, TPS6591X_MAX_NUM_GPIO); int ret; - struct tps65910_board *board_data; + int idx; + + tps65910_board->gpio_base = -1; + ret = of_property_read_u32_array(tps65910->dev->of_node, + "ti,en-gpio-sleep", prop_array, ngpio); + if (ret < 0) { + dev_dbg(dev, "ti,en-gpio-sleep not specified\n"); + return tps65910_board; + } - if (!gpio_base) - return; + for (idx = 0; idx < ngpio; idx++) + tps65910_board->en_gpio_sleep[idx] = (prop_array[idx] != 0); - tps65910->gpio.owner = THIS_MODULE; - tps65910->gpio.label = tps65910->i2c_client->name; - tps65910->gpio.dev = tps65910->dev; - tps65910->gpio.base = gpio_base; + return tps65910_board; +} +#else +static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev, + struct tps65910 *tps65910, int chip_ngpio) +{ + return NULL; +} +#endif + +static int __devinit tps65910_gpio_probe(struct platform_device *pdev) +{ + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct tps65910_board *pdata = dev_get_platdata(tps65910->dev); + struct tps65910_gpio *tps65910_gpio; + int ret; + int i; + + tps65910_gpio = devm_kzalloc(&pdev->dev, + sizeof(*tps65910_gpio), GFP_KERNEL); + if (!tps65910_gpio) { + dev_err(&pdev->dev, "Could not allocate tps65910_gpio\n"); + return -ENOMEM; + } + + tps65910_gpio->tps65910 = tps65910; + + tps65910_gpio->gpio_chip.owner = THIS_MODULE; + tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name; switch(tps65910_chip_id(tps65910)) { case TPS65910: - tps65910->gpio.ngpio = TPS65910_NUM_GPIO; + tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO; break; case TPS65911: - tps65910->gpio.ngpio = TPS65911_NUM_GPIO; + tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO; break; default: - return; + return -EINVAL; + } + tps65910_gpio->gpio_chip.can_sleep = 1; + tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input; + tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output; + tps65910_gpio->gpio_chip.set = tps65910_gpio_set; + tps65910_gpio->gpio_chip.get = tps65910_gpio_get; + tps65910_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + tps65910_gpio->gpio_chip.base = pdata->gpio_base; + else + tps65910_gpio->gpio_chip.base = -1; + + if (!pdata && tps65910->dev->of_node) + pdata = tps65910_parse_dt_for_gpio(&pdev->dev, tps65910, + tps65910_gpio->gpio_chip.ngpio); + + if (!pdata) + goto skip_init; + + /* Configure sleep control for gpios if provided */ + for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) { + if (!pdata->en_gpio_sleep[i]) + continue; + + ret = tps65910_reg_set_bits(tps65910, + TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); + if (ret < 0) + dev_warn(tps65910->dev, + "GPIO Sleep setting failed with err %d\n", ret); } - tps65910->gpio.can_sleep = 1; - - tps65910->gpio.direction_input = tps65910_gpio_input; - tps65910->gpio.direction_output = tps65910_gpio_output; - tps65910->gpio.set = tps65910_gpio_set; - tps65910->gpio.get = tps65910_gpio_get; - - /* Configure sleep control for gpios */ - board_data = dev_get_platdata(tps65910->dev); - if (board_data) { - int i; - for (i = 0; i < tps65910->gpio.ngpio; ++i) { - if (board_data->en_gpio_sleep[i]) { - ret = tps65910_set_bits(tps65910, - TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); - if (ret < 0) - dev_warn(tps65910->dev, - "GPIO Sleep setting failed\n"); - } - } + +skip_init: + ret = gpiochip_add(&tps65910_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; } - ret = gpiochip_add(&tps65910->gpio); + platform_set_drvdata(pdev, tps65910_gpio); + + return ret; +} + +static int __devexit tps65910_gpio_remove(struct platform_device *pdev) +{ + struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev); - if (ret) - dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret); + return gpiochip_remove(&tps65910_gpio->gpio_chip); } + +static struct platform_driver tps65910_gpio_driver = { + .driver.name = "tps65910-gpio", + .driver.owner = THIS_MODULE, + .probe = tps65910_gpio_probe, + .remove = __devexit_p(tps65910_gpio_remove), +}; + +static int __init tps65910_gpio_init(void) +{ + return platform_driver_register(&tps65910_gpio_driver); +} +subsys_initcall(tps65910_gpio_init); + +static void __exit tps65910_gpio_exit(void) +{ + platform_driver_unregister(&tps65910_gpio_driver); +} +module_exit(tps65910_gpio_exit); + +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_AUTHOR("Jorge Eduardo Candelaria jedu@slimlogic.co.uk>"); +MODULE_DESCRIPTION("GPIO interface for TPS65910/TPS6511 PMICs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65910-gpio"); diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index deb949e75ec..e56a2165641 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -102,10 +102,8 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; - if (!wm831x->irq_base) - return -EINVAL; - - return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset; + return irq_create_mapping(wm831x->irq_domain, + WM831X_IRQ_GPIO_1 + offset); } static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c index 47f18d6bce4..6790a812a1d 100644 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -73,7 +73,7 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_on *wm831x_on; - int irq = platform_get_irq(pdev, 0); + int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); int ret; wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c index 4bc851a9dc3..e83410721e3 100644 --- a/drivers/input/touchscreen/wm831x-ts.c +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -260,15 +260,16 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev) * If we have a direct IRQ use it, otherwise use the interrupt * from the WM831x IRQ controller. */ + wm831x_ts->data_irq = wm831x_irq(wm831x, + platform_get_irq_byname(pdev, + "TCHDATA")); if (pdata && pdata->data_irq) wm831x_ts->data_irq = pdata->data_irq; - else - wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA"); + wm831x_ts->pd_irq = wm831x_irq(wm831x, + platform_get_irq_byname(pdev, "TCHPD")); if (pdata && pdata->pd_irq) wm831x_ts->pd_irq = pdata->pd_irq; - else - wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD"); if (pdata) wm831x_ts->pressure = pdata->pressure; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f4b4dad7739..e129c820df7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -106,6 +106,19 @@ config UCB1400_CORE To compile this driver as a module, choose M here: the module will be called ucb1400_core. +config MFD_LM3533 + tristate "LM3533 Lighting Power chip" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + Say yes here to enable support for National Semiconductor / TI + LM3533 Lighting Power chips. + + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the LED, + backlight or ambient-light-sensor functionality of the device. + config TPS6105X tristate "TPS61050/61052 Boost Converters" depends on I2C @@ -177,8 +190,8 @@ config MFD_TPS65910 bool "TPS65910 Power Management chip" depends on I2C=y && GPIOLIB select MFD_CORE - select GPIO_TPS65910 select REGMAP_I2C + select IRQ_DOMAIN help if you say yes here you get support for the TPS65910 series of Power Management chips. @@ -409,6 +422,19 @@ config PMIC_ADP5520 individual components like LCD backlight, LEDs, GPIOs and Kepad under the corresponding menus. +config MFD_MAX77693 + bool "Maxim Semiconductor MAX77693 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + Say yes here to support for Maxim Semiconductor MAX77693. + This is a companion Power Management IC with Flash, Haptic, Charger, + and MUIC(Micro USB Interface Controller) controls on chip. + 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_MAX8925 bool "Maxim Semiconductor MAX8925 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -454,9 +480,9 @@ config MFD_S5M_CORE of the device config MFD_WM8400 - tristate "Support Wolfson Microelectronics WM8400" + bool "Support Wolfson Microelectronics WM8400" select MFD_CORE - depends on I2C + depends on I2C=y select REGMAP_I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio @@ -473,6 +499,7 @@ config MFD_WM831X_I2C select MFD_CORE select MFD_WM831X select REGMAP_I2C + select IRQ_DOMAIN depends on I2C=y && GENERIC_HARDIRQS help Support for the Wolfson Microelecronics WM831x and WM832x PMICs @@ -485,6 +512,7 @@ config MFD_WM831X_SPI select MFD_CORE select MFD_WM831X select REGMAP_SPI + select IRQ_DOMAIN depends on SPI_MASTER && GENERIC_HARDIRQS help Support for the Wolfson Microelecronics WM831x and WM832x PMICs @@ -597,17 +625,32 @@ config MFD_MC13783 tristate config MFD_MC13XXX - tristate "Support Freescale MC13783 and MC13892" - depends on SPI_MASTER + tristate + depends on SPI_MASTER || I2C select MFD_CORE select MFD_MC13783 help - Support for the Freescale (Atlas) PMIC and audio CODECs - MC13783 and MC13892. - This driver provides common support for accessing the device, + Enable support for the Freescale MC13783 and MC13892 PMICs. + 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_MC13XXX_SPI + tristate "Freescale MC13783 and MC13892 SPI interface" + depends on SPI_MASTER + select REGMAP_SPI + select MFD_MC13XXX + help + Select this if your MC13xxx is connected via an SPI bus. + +config MFD_MC13XXX_I2C + tristate "Freescale MC13892 I2C interface" + depends on I2C + select REGMAP_I2C + select MFD_MC13XXX + help + Select this if your MC13xxx is connected via an I2C bus. + config ABX500_CORE bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" default y if ARCH_U300 || ARCH_U8500 @@ -651,7 +694,7 @@ config EZX_PCAP config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" - depends on GENERIC_HARDIRQS && ABX500_CORE + depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU select MFD_CORE help Select this option to enable access to AB8500 power management @@ -722,6 +765,16 @@ config LPC_SCH LPC bridge function of the Intel SCH provides support for System Management Bus and General Purpose I/O. +config LPC_ICH + tristate "Intel ICH LPC" + depends on PCI + select MFD_CORE + help + The LPC bridge function of the Intel ICH provides support for + many functional units. This driver provides needed support for + other drivers to control these functions, currently GPIO and + watchdog. + config MFD_RDC321X tristate "Support for RDC-R321x southbridge" select MFD_CORE @@ -854,6 +907,11 @@ config MFD_RC5T583 Additional drivers must be enabled in order to use the different functionality of the device. +config MFD_STA2X11 + bool "STA2X11 multi function device support" + depends on STA2X11 + select MFD_CORE + config MFD_ANATOP bool "Support for Freescale i.MX on-chip ANATOP controller" depends on SOC_IMX6Q diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 43672b87805..75f6ed68a4b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o +obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o @@ -54,6 +55,8 @@ obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o +obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o +obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o obj-$(CONFIG_MFD_CORE) += mfd-core.o @@ -75,6 +78,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o +obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o @@ -87,15 +91,15 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o -obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o -# ab8500-i2c need to come after db8500-prcmu (which provides the channel) -obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o +# ab8500-core need to come after db8500-prcmu (which provides the channel) +obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o +obj-$(CONFIG_LPC_ICH) += lpc_ich.o obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 1f08704f7ae..dac0e299860 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -18,7 +18,10 @@ #include <linux/mfd/core.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> +#include <linux/mfd/dbx500-prcmu.h> #include <linux/regulator/ab8500.h> +#include <linux/of.h> +#include <linux/of_device.h> /* * Interrupt register offsets @@ -91,12 +94,24 @@ #define AB8500_IT_MASK23_REG 0x56 #define AB8500_IT_MASK24_REG 0x57 +/* + * latch hierarchy registers + */ +#define AB8500_IT_LATCHHIER1_REG 0x60 +#define AB8500_IT_LATCHHIER2_REG 0x61 +#define AB8500_IT_LATCHHIER3_REG 0x62 + +#define AB8500_IT_LATCHHIER_NUM 3 + #define AB8500_REV_REG 0x80 #define AB8500_IC_NAME_REG 0x82 #define AB8500_SWITCH_OFF_STATUS 0x00 #define AB8500_TURN_ON_STATUS 0x00 +static bool no_bm; /* No battery management */ +module_param(no_bm, bool, S_IRUGO); + #define AB9540_MODEM_CTRL2_REG 0x23 #define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2) @@ -125,6 +140,41 @@ static const char ab8500_version_str[][7] = { [AB8500_VERSION_AB8540] = "AB8540", }; +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_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, + u8 data) +{ + int ret; + + ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data, + &mask, 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 ab8500_get_chip_id(struct device *dev) { struct ab8500 *ab8500; @@ -161,9 +211,13 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank, static int ab8500_set_register(struct device *dev, u8 bank, u8 reg, u8 value) { + int ret; struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return set_register_interruptible(ab8500, bank, reg, value); + atomic_inc(&ab8500->transfer_ongoing); + ret = set_register_interruptible(ab8500, bank, reg, value); + atomic_dec(&ab8500->transfer_ongoing); + return ret; } static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, @@ -192,9 +246,13 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, static int ab8500_get_register(struct device *dev, u8 bank, u8 reg, u8 *value) { + int ret; struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab8500, bank, reg, value); + atomic_inc(&ab8500->transfer_ongoing); + ret = get_register_interruptible(ab8500, bank, reg, value); + atomic_dec(&ab8500->transfer_ongoing); + return ret; } static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank, @@ -241,11 +299,14 @@ out: static int ab8500_mask_and_set_register(struct device *dev, u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { + int ret; struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab8500, bank, reg, - bitmask, bitvalues); - + atomic_inc(&ab8500->transfer_ongoing); + ret= mask_and_set_register_interruptible(ab8500, bank, reg, + bitmask, bitvalues); + atomic_dec(&ab8500->transfer_ongoing); + return ret; } static struct abx500_ops ab8500_ops = { @@ -264,6 +325,7 @@ static void ab8500_irq_lock(struct irq_data *data) struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); mutex_lock(&ab8500->irq_lock); + atomic_inc(&ab8500->transfer_ongoing); } static void ab8500_irq_sync_unlock(struct irq_data *data) @@ -292,7 +354,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i]; set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new); } - + atomic_dec(&ab8500->transfer_ongoing); mutex_unlock(&ab8500->irq_lock); } @@ -325,6 +387,90 @@ static struct irq_chip ab8500_irq_chip = { .irq_unmask = ab8500_irq_unmask, }; +static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, + int latch_offset, u8 latch_val) +{ + int int_bit = __ffs(latch_val); + int line, i; + + do { + int_bit = __ffs(latch_val); + + for (i = 0; i < ab8500->mask_size; i++) + if (ab8500->irq_reg_offset[i] == latch_offset) + break; + + if (i >= ab8500->mask_size) { + dev_err(ab8500->dev, "Register offset 0x%2x not declared\n", + latch_offset); + return -ENXIO; + } + + line = (i << 3) + int_bit; + latch_val &= ~(1 << int_bit); + + handle_nested_irq(ab8500->irq_base + line); + } while (latch_val); + + return 0; +} + +static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500, + int hier_offset, u8 hier_val) +{ + int latch_bit, status; + u8 latch_offset, latch_val; + + do { + latch_bit = __ffs(hier_val); + latch_offset = (hier_offset << 3) + latch_bit; + + /* Fix inconsistent ITFromLatch25 bit mapping... */ + if (unlikely(latch_offset == 17)) + latch_offset = 24; + + status = get_register_interruptible(ab8500, + AB8500_INTERRUPT, + AB8500_IT_LATCH1_REG + latch_offset, + &latch_val); + if (status < 0 || latch_val == 0) + goto discard; + + status = ab8500_handle_hierarchical_line(ab8500, + latch_offset, latch_val); + if (status < 0) + return status; +discard: + hier_val &= ~(1 << latch_bit); + } while (hier_val); + + return 0; +} + +static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev) +{ + struct ab8500 *ab8500 = dev; + u8 i; + + dev_vdbg(ab8500->dev, "interrupt\n"); + + /* Hierarchical interrupt version */ + for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) { + int status; + u8 hier_val; + + status = get_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_LATCHHIER1_REG + i, &hier_val); + if (status < 0 || hier_val == 0) + continue; + + status = ab8500_handle_hierarchical_latch(ab8500, i, hier_val); + if (status < 0) + break; + } + return IRQ_HANDLED; +} + static irqreturn_t ab8500_irq(int irq, void *dev) { struct ab8500 *ab8500 = dev; @@ -332,6 +478,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev) dev_vdbg(ab8500->dev, "interrupt\n"); + atomic_inc(&ab8500->transfer_ongoing); + for (i = 0; i < ab8500->mask_size; i++) { int regoffset = ab8500->irq_reg_offset[i]; int status; @@ -355,9 +503,10 @@ static irqreturn_t ab8500_irq(int irq, void *dev) handle_nested_irq(ab8500->irq_base + line); value &= ~(1 << bit); + } while (value); } - + atomic_dec(&ab8500->transfer_ongoing); return IRQ_HANDLED; } @@ -411,6 +560,14 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) } } +int ab8500_suspend(struct ab8500 *ab8500) +{ + if (atomic_read(&ab8500->transfer_ongoing)) + return -EINVAL; + else + return 0; +} + /* AB8500 GPIO Resources */ static struct resource __devinitdata ab8500_gpio_resources[] = { { @@ -744,6 +901,39 @@ static struct resource __devinitdata ab8500_usb_resources[] = { }, }; +static struct resource __devinitdata ab8505_iddet_resources[] = { + { + .name = "KeyDeglitch", + .start = AB8505_INT_KEYDEGLITCH, + .end = AB8505_INT_KEYDEGLITCH, + .flags = IORESOURCE_IRQ, + }, + { + .name = "KP", + .start = AB8505_INT_KP, + .end = AB8505_INT_KP, + .flags = IORESOURCE_IRQ, + }, + { + .name = "IKP", + .start = AB8505_INT_IKP, + .end = AB8505_INT_IKP, + .flags = IORESOURCE_IRQ, + }, + { + .name = "IKR", + .start = AB8505_INT_IKR, + .end = AB8505_INT_IKR, + .flags = IORESOURCE_IRQ, + }, + { + .name = "KeyStuck", + .start = AB8505_INT_KEYSTUCK, + .end = AB8505_INT_KEYSTUCK, + .flags = IORESOURCE_IRQ, + }, +}; + static struct resource __devinitdata ab8500_temp_resources[] = { { .name = "AB8500_TEMP_WARM", @@ -778,35 +968,11 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { .resources = ab8500_rtc_resources, }, { - .name = "ab8500-charger", - .num_resources = ARRAY_SIZE(ab8500_charger_resources), - .resources = ab8500_charger_resources, - }, - { - .name = "ab8500-btemp", - .num_resources = ARRAY_SIZE(ab8500_btemp_resources), - .resources = ab8500_btemp_resources, - }, - { - .name = "ab8500-fg", - .num_resources = ARRAY_SIZE(ab8500_fg_resources), - .resources = ab8500_fg_resources, - }, - { - .name = "ab8500-chargalg", - .num_resources = ARRAY_SIZE(ab8500_chargalg_resources), - .resources = ab8500_chargalg_resources, - }, - { .name = "ab8500-acc-det", .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources), .resources = ab8500_av_acc_detect_resources, }, { - .name = "ab8500-codec", - }, - - { .name = "ab8500-poweron-key", .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), .resources = ab8500_poweronkey_db_resources, @@ -834,6 +1000,29 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { }, }; +static struct mfd_cell __devinitdata ab8500_bm_devs[] = { + { + .name = "ab8500-charger", + .num_resources = ARRAY_SIZE(ab8500_charger_resources), + .resources = ab8500_charger_resources, + }, + { + .name = "ab8500-btemp", + .num_resources = ARRAY_SIZE(ab8500_btemp_resources), + .resources = ab8500_btemp_resources, + }, + { + .name = "ab8500-fg", + .num_resources = ARRAY_SIZE(ab8500_fg_resources), + .resources = ab8500_fg_resources, + }, + { + .name = "ab8500-chargalg", + .num_resources = ARRAY_SIZE(ab8500_chargalg_resources), + .resources = ab8500_chargalg_resources, + }, +}; + static struct mfd_cell __devinitdata ab8500_devs[] = { { .name = "ab8500-gpio", @@ -845,6 +1034,9 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { .num_resources = ARRAY_SIZE(ab8500_usb_resources), .resources = ab8500_usb_resources, }, + { + .name = "ab8500-codec", + }, }; static struct mfd_cell __devinitdata ab9540_devs[] = { @@ -858,6 +1050,18 @@ static struct mfd_cell __devinitdata ab9540_devs[] = { .num_resources = ARRAY_SIZE(ab8500_usb_resources), .resources = ab8500_usb_resources, }, + { + .name = "ab9540-codec", + }, +}; + +/* Device list common to ab9540 and ab8505 */ +static struct mfd_cell __devinitdata ab9540_ab8505_devs[] = { + { + .name = "ab-iddet", + .num_resources = ARRAY_SIZE(ab8505_iddet_resources), + .resources = ab8505_iddet_resources, + }, }; static ssize_t show_chip_id(struct device *dev, @@ -1003,18 +1207,66 @@ static struct attribute_group ab9540_attr_group = { .attrs = ab9540_sysfs_entries, }; -int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) +static const struct of_device_id ab8500_match[] = { + { + .compatible = "stericsson,ab8500", + .data = (void *)AB8500_VERSION_AB8500, + }, + {}, +}; + +static int __devinit ab8500_probe(struct platform_device *pdev) { - struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); + struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev); + const struct platform_device_id *platid = platform_get_device_id(pdev); + enum ab8500_version version = AB8500_VERSION_UNDEFINED; + struct device_node *np = pdev->dev.of_node; + struct ab8500 *ab8500; + struct resource *resource; int ret; int i; u8 value; + ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); + if (!ab8500) + return -ENOMEM; + if (plat) ab8500->irq_base = plat->irq_base; + else if (np) + ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base); + + if (!ab8500->irq_base) { + dev_info(&pdev->dev, "couldn't find irq-base\n"); + ret = -EINVAL; + goto out_free_ab8500; + } + + ab8500->dev = &pdev->dev; + + resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!resource) { + ret = -ENODEV; + goto out_free_ab8500; + } + + ab8500->irq = resource->start; + + ab8500->read = ab8500_i2c_read; + ab8500->write = ab8500_i2c_write; + ab8500->write_masked = ab8500_i2c_write_masked; mutex_init(&ab8500->lock); mutex_init(&ab8500->irq_lock); + atomic_set(&ab8500->transfer_ongoing, 0); + + platform_set_drvdata(pdev, ab8500); + + if (platid) + version = platid->driver_data; + else if (np) + version = (unsigned int) + of_match_device(ab8500_match, &pdev->dev)->data; if (version != AB8500_VERSION_UNDEFINED) ab8500->version = version; @@ -1022,7 +1274,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_IC_NAME_REG, &value); if (ret < 0) - return ret; + goto out_free_ab8500; ab8500->version = value; } @@ -1030,7 +1282,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_REV_REG, &value); if (ret < 0) - return ret; + goto out_free_ab8500; ab8500->chip_id = value; @@ -1105,30 +1357,57 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) if (ret) goto out_freeoldmask; - ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, - IRQF_ONESHOT | IRQF_NO_SUSPEND, - "ab8500", ab8500); + /* Activate this feature only in ab9540 */ + /* till tests are done on ab8500 1p2 or later*/ + if (is_ab9540(ab8500)) + ret = request_threaded_irq(ab8500->irq, NULL, + ab8500_hierarchical_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); + else + ret = request_threaded_irq(ab8500->irq, NULL, + ab8500_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); if (ret) goto out_removeirq; } - ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, - ARRAY_SIZE(abx500_common_devs), NULL, - ab8500->irq_base); + if (!np) { + ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, + ARRAY_SIZE(abx500_common_devs), NULL, + ab8500->irq_base); - if (ret) - goto out_freeirq; + if (ret) + goto out_freeirq; + + if (is_ab9540(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, + ab8500->irq_base); + else + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; - if (is_ab9540(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, - ARRAY_SIZE(ab9540_devs), NULL, - ab8500->irq_base); - else - ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, - ARRAY_SIZE(ab9540_devs), NULL, - ab8500->irq_base); - if (ret) - goto out_freeirq; + if (is_ab9540(ab8500) || is_ab8505(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs, + ARRAY_SIZE(ab9540_ab8505_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; + } + + if (!no_bm) { + /* Add battery management devices */ + ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, + ARRAY_SIZE(ab8500_bm_devs), NULL, + ab8500->irq_base); + if (ret) + dev_err(ab8500->dev, "error adding bm devices\n"); + } if (is_ab9540(ab8500)) ret = sysfs_create_group(&ab8500->dev->kobj, @@ -1151,12 +1430,16 @@ out_freeoldmask: kfree(ab8500->oldmask); out_freemask: kfree(ab8500->mask); +out_free_ab8500: + kfree(ab8500); return ret; } -int __devexit ab8500_exit(struct ab8500 *ab8500) +static int __devexit ab8500_remove(struct platform_device *pdev) { + struct ab8500 *ab8500 = platform_get_drvdata(pdev); + if (is_ab9540(ab8500)) sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); else @@ -1168,10 +1451,42 @@ int __devexit ab8500_exit(struct ab8500 *ab8500) } kfree(ab8500->oldmask); kfree(ab8500->mask); + kfree(ab8500); return 0; } +static const struct platform_device_id ab8500_id[] = { + { "ab8500-core", AB8500_VERSION_AB8500 }, + { "ab8505-i2c", AB8500_VERSION_AB8505 }, + { "ab9540-i2c", AB8500_VERSION_AB9540 }, + { "ab8540-i2c", AB8500_VERSION_AB8540 }, + { } +}; + +static struct platform_driver ab8500_core_driver = { + .driver = { + .name = "ab8500-core", + .owner = THIS_MODULE, + .of_match_table = ab8500_match, + }, + .probe = ab8500_probe, + .remove = __devexit_p(ab8500_remove), + .id_table = ab8500_id, +}; + +static int __init ab8500_core_init(void) +{ + return platform_driver_register(&ab8500_core_driver); +} + +static void __exit ab8500_core_exit(void) +{ + platform_driver_unregister(&ab8500_core_driver); +} +arch_initcall(ab8500_core_init); +module_exit(ab8500_core_exit); + MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent"); MODULE_DESCRIPTION("AB8500 MFD core"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 9a0211aa889..50c4c89ab22 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -608,10 +608,16 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf) return 0; } +static const struct of_device_id ab8500_debug_match[] = { + { .compatible = "stericsson,ab8500-debug", }, + {} +}; + static struct platform_driver ab8500_debug_driver = { .driver = { .name = "ab8500-debug", .owner = THIS_MODULE, + .of_match_table = ab8500_debug_match, }, .probe = ab8500_debug_probe, .remove = __devexit_p(ab8500_debug_remove) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index c39fc716e1d..b86fd8e1ec3 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -584,7 +584,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); if (gpadc->irq < 0) { - dev_err(gpadc->dev, "failed to get platform irq-%d\n", + dev_err(&pdev->dev, "failed to get platform irq-%d\n", gpadc->irq); ret = gpadc->irq; goto fail; @@ -648,12 +648,18 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ab8500_gpadc_match[] = { + { .compatible = "stericsson,ab8500-gpadc", }, + {} +}; + static struct platform_driver ab8500_gpadc_driver = { .probe = ab8500_gpadc_probe, .remove = __devexit_p(ab8500_gpadc_remove), .driver = { .name = "ab8500-gpadc", .owner = THIS_MODULE, + .of_match_table = ab8500_gpadc_match, }, }; diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c deleted file mode 100644 index b83045f102b..00000000000 --- a/drivers/mfd/ab8500-i2c.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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/abx500/ab8500.h> -#include <linux/mfd/dbx500-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_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, - u8 data) -{ - int ret; - - ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data, - &mask, 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) -{ - const struct platform_device_id *platid = platform_get_device_id(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; - ab8500->write_masked = ab8500_i2c_write_masked; - - platform_set_drvdata(plf, ab8500); - - ret = ab8500_init(ab8500, platid->driver_data); - 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 const struct platform_device_id ab8500_id[] = { - { "ab8500-i2c", AB8500_VERSION_AB8500 }, - { "ab8505-i2c", AB8500_VERSION_AB8505 }, - { "ab9540-i2c", AB8500_VERSION_AB9540 }, - { "ab8540-i2c", AB8500_VERSION_AB8540 }, - { } -}; - -static struct platform_driver ab8500_i2c_driver = { - .driver = { - .name = "ab8500-i2c", - .owner = THIS_MODULE, - }, - .probe = ab8500_i2c_probe, - .remove = __devexit_p(ab8500_i2c_remove), - .id_table = ab8500_id, -}; - -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); -} -arch_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-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index c28d4eb1eff..5a3e51ccf25 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -61,10 +61,16 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ab8500_sysctrl_match[] = { + { .compatible = "stericsson,ab8500-sysctrl", }, + {} +}; + static struct platform_driver ab8500_sysctrl_driver = { .driver = { .name = "ab8500-sysctrl", .owner = THIS_MODULE, + .of_match_table = ab8500_sysctrl_match, }, .probe = ab8500_sysctrl_probe, .remove = __devexit_p(ab8500_sysctrl_remove), diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c index 2af42480635..6da06341f6c 100644 --- a/drivers/mfd/anatop-mfd.c +++ b/drivers/mfd/anatop-mfd.c @@ -41,39 +41,26 @@ #include <linux/of_address.h> #include <linux/mfd/anatop.h> -u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift, - int bit_width) +u32 anatop_read_reg(struct anatop *adata, u32 addr) { - u32 val, mask; - - if (bit_width == 32) - mask = ~0; - else - mask = (1 << bit_width) - 1; - - val = readl(adata->ioreg + addr); - val = (val >> bit_shift) & mask; - - return val; + return readl(adata->ioreg + addr); } -EXPORT_SYMBOL_GPL(anatop_get_bits); +EXPORT_SYMBOL_GPL(anatop_read_reg); -void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift, - int bit_width, u32 data) +void anatop_write_reg(struct anatop *adata, u32 addr, u32 data, u32 mask) { - u32 val, mask; + u32 val; - if (bit_width == 32) - mask = ~0; - else - mask = (1 << bit_width) - 1; + data &= mask; spin_lock(&adata->reglock); - val = readl(adata->ioreg + addr) & ~(mask << bit_shift); - writel((data << bit_shift) | val, adata->ioreg + addr); + val = readl(adata->ioreg + addr); + val &= ~mask; + val |= data; + writel(val, adata->ioreg + addr); spin_unlock(&adata->reglock); } -EXPORT_SYMBOL_GPL(anatop_set_bits); +EXPORT_SYMBOL_GPL(anatop_write_reg); static const struct of_device_id of_anatop_match[] = { { .compatible = "fsl,imx6q-anatop", }, diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 1582c3d9525..383421bf576 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -353,12 +353,28 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) return 0; } +static int asic3_gpio_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct asic3 *asic = irq_data_get_irq_chip_data(data); + u32 bank, index; + u16 bit; + + bank = asic3_irq_to_bank(asic, data->irq); + index = asic3_irq_to_index(asic, data->irq); + bit = 1<<index; + + asic3_set_register(asic, bank + ASIC3_GPIO_SLEEP_MASK, bit, !on); + + return 0; +} + static struct irq_chip asic3_gpio_irq_chip = { .name = "ASIC3-GPIO", .irq_ack = asic3_mask_gpio_irq, .irq_mask = asic3_mask_gpio_irq, .irq_unmask = asic3_unmask_gpio_irq, .irq_set_type = asic3_gpio_irq_type, + .irq_set_wake = asic3_gpio_irq_set_wake, }; static struct irq_chip asic3_irq_chip = { @@ -529,7 +545,7 @@ static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct asic3 *asic = container_of(chip, struct asic3, gpio); - return (offset < ASIC3_NUM_GPIOS) ? asic->irq_base + offset : -ENXIO; + return asic->irq_base + offset; } static __init int asic3_gpio_probe(struct platform_device *pdev, @@ -894,10 +910,13 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, asic3_mmc_resources[0].start >>= asic->bus_shift; asic3_mmc_resources[0].end >>= asic->bus_shift; - ret = mfd_add_devices(&pdev->dev, pdev->id, + if (pdata->clock_rate) { + ds1wm_pdata.clock_rate = pdata->clock_rate; + ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_ds1wm, 1, mem, asic->irq_base); - if (ret < 0) - goto out; + if (ret < 0) + goto out; + } if (mem_sdio && (irq >= 0)) { ret = mfd_add_devices(&pdev->dev, pdev->id, @@ -1000,6 +1019,9 @@ static int __init asic3_probe(struct platform_device *pdev) asic3_mfd_probe(pdev, pdata, mem); + asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), + (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 1); + dev_info(asic->dev, "ASIC3 Core driver\n"); return 0; @@ -1021,6 +1043,9 @@ static int __devexit asic3_remove(struct platform_device *pdev) int ret; struct asic3 *asic = platform_get_drvdata(pdev); + asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), + (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 0); + asic3_mfd_remove(pdev); ret = asic3_gpio_remove(pdev); diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 315fef5d466..3419e726de4 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -186,18 +186,7 @@ static struct pci_driver cs5535_mfd_driver = { .remove = __devexit_p(cs5535_mfd_remove), }; -static int __init cs5535_mfd_init(void) -{ - return pci_register_driver(&cs5535_mfd_driver); -} - -static void __exit cs5535_mfd_exit(void) -{ - pci_unregister_driver(&cs5535_mfd_driver); -} - -module_init(cs5535_mfd_init); -module_exit(cs5535_mfd_exit); +module_pci_driver(cs5535_mfd_driver); MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device"); diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 7776aff4626..1f1313c9057 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -318,6 +318,135 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg) } } +/* + * TBAT look-up table is computed from the R90 reg (8 bit register) + * reading as below. The battery temperature is in milliCentigrade + * TBAT = (1/(t1+1/298) - 273) * 1000 mC + * where t1 = (1/B)* ln(( ADCval * 2.5)/(R25*ITBAT*255)) + * Default values are R25 = 10e3, B = 3380, ITBAT = 50e-6 + * Example: + * R25=10E3, B=3380, ITBAT=50e-6, ADCVAL=62d calculates + * TBAT = 20015 mili degrees Centrigrade + * +*/ +static const int32_t tbat_lookup[255] = { + 183258, 144221, 124334, 111336, 101826, 94397, 88343, 83257, + 78889, 75071, 71688, 68656, 65914, 63414, 61120, 59001, + 570366, 55204, 53490, 51881, 50364, 48931, 47574, 46285, + 45059, 43889, 42772, 41703, 40678, 39694, 38748, 37838, + 36961, 36115, 35297, 34507, 33743, 33002, 32284, 31588, + 30911, 30254, 29615, 28994, 28389, 27799, 27225, 26664, + 26117, 25584, 25062, 24553, 24054, 23567, 23091, 22624, + 22167, 21719, 21281, 20851, 20429, 20015, 19610, 19211, + 18820, 18436, 18058, 17688, 17323, 16965, 16612, 16266, + 15925, 15589, 15259, 14933, 14613, 14298, 13987, 13681, + 13379, 13082, 12788, 12499, 12214, 11933, 11655, 11382, + 11112, 10845, 10582, 10322, 10066, 9812, 9562, 9315, + 9071, 8830, 8591, 8356, 8123, 7893, 7665, 7440, + 7218, 6998, 6780, 6565, 6352, 6141, 5933, 5726, + 5522, 5320, 5120, 4922, 4726, 4532, 4340, 4149, + 3961, 3774, 3589, 3406, 3225, 3045, 2867, 2690, + 2516, 2342, 2170, 2000, 1831, 1664, 1498, 1334, + 1171, 1009, 849, 690, 532, 376, 221, 67, + -84, -236, -386, -535, -683, -830, -975, -1119, + -1263, -1405, -1546, -1686, -1825, -1964, -2101, -2237, + -2372, -2506, -2639, -2771, -2902, -3033, -3162, -3291, + -3418, -3545, -3671, -3796, -3920, -4044, -4166, -4288, + -4409, -4529, -4649, -4767, -4885, -5002, -5119, -5235, + -5349, -5464, -5577, -5690, -5802, -5913, -6024, -6134, + -6244, -6352, -6461, -6568, -6675, -6781, -6887, -6992, + -7096, -7200, -7303, -7406, -7508, -7609, -7710, -7810, + -7910, -8009, -8108, -8206, -8304, -8401, -8497, -8593, + -8689, -8784, -8878, -8972, -9066, -9159, -9251, -9343, + -9435, -9526, -9617, -9707, -9796, -9886, -9975, -10063, + -10151, -10238, -10325, -10412, -10839, -10923, -11007, -11090, + -11173, -11256, -11338, -11420, -11501, -11583, -11663, -11744, + -11823, -11903, -11982 +}; + +static const u8 chan_mux[DA9052_ADC_VBBAT + 1] = { + [DA9052_ADC_VDDOUT] = DA9052_ADC_MAN_MUXSEL_VDDOUT, + [DA9052_ADC_ICH] = DA9052_ADC_MAN_MUXSEL_ICH, + [DA9052_ADC_TBAT] = DA9052_ADC_MAN_MUXSEL_TBAT, + [DA9052_ADC_VBAT] = DA9052_ADC_MAN_MUXSEL_VBAT, + [DA9052_ADC_IN4] = DA9052_ADC_MAN_MUXSEL_AD4, + [DA9052_ADC_IN5] = DA9052_ADC_MAN_MUXSEL_AD5, + [DA9052_ADC_IN6] = DA9052_ADC_MAN_MUXSEL_AD6, + [DA9052_ADC_VBBAT] = DA9052_ADC_MAN_MUXSEL_VBBAT +}; + +int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel) +{ + int ret; + unsigned short calc_data; + unsigned short data; + unsigned char mux_sel; + + if (channel > DA9052_ADC_VBBAT) + return -EINVAL; + + mutex_lock(&da9052->auxadc_lock); + + /* Channel gets activated on enabling the Conversion bit */ + mux_sel = chan_mux[channel] | DA9052_ADC_MAN_MAN_CONV; + + ret = da9052_reg_write(da9052, DA9052_ADC_MAN_REG, mux_sel); + if (ret < 0) + goto err; + + /* Wait for an interrupt */ + if (!wait_for_completion_timeout(&da9052->done, + msecs_to_jiffies(500))) { + dev_err(da9052->dev, + "timeout waiting for ADC conversion interrupt\n"); + ret = -ETIMEDOUT; + goto err; + } + + ret = da9052_reg_read(da9052, DA9052_ADC_RES_H_REG); + if (ret < 0) + goto err; + + calc_data = (unsigned short)ret; + data = calc_data << 2; + + ret = da9052_reg_read(da9052, DA9052_ADC_RES_L_REG); + if (ret < 0) + goto err; + + calc_data = (unsigned short)(ret & DA9052_ADC_RES_LSB); + data |= calc_data; + + ret = data; + +err: + mutex_unlock(&da9052->auxadc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(da9052_adc_manual_read); + +static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data) +{ + struct da9052 *da9052 = irq_data; + + complete(&da9052->done); + + return IRQ_HANDLED; +} + +int da9052_adc_read_temp(struct da9052 *da9052) +{ + int tbat; + + tbat = da9052_reg_read(da9052, DA9052_TBAT_RES_REG); + if (tbat <= 0) + return tbat; + + /* ARRAY_SIZE check is not needed since TBAT is a 8-bit register */ + return tbat_lookup[tbat - 1]; +} +EXPORT_SYMBOL_GPL(da9052_adc_read_temp); + static struct resource da9052_rtc_resource = { .name = "ALM", .start = DA9052_IRQ_ALARM, @@ -646,6 +775,9 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) struct irq_desc *desc; int ret; + mutex_init(&da9052->auxadc_lock); + init_completion(&da9052->done); + if (pdata && pdata->init != NULL) pdata->init(da9052); @@ -665,6 +797,12 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data); + ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "adc irq", da9052); + if (ret != 0) + dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret); + ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0); if (ret) @@ -673,6 +811,7 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) return 0; err: + free_irq(DA9052_IRQ_ADC_EOM, da9052); mfd_remove_devices(da9052->dev); regmap_err: return ret; @@ -680,6 +819,7 @@ regmap_err: void da9052_device_exit(struct da9052 *da9052) { + free_irq(DA9052_IRQ_ADC_EOM, da9052); regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); mfd_remove_devices(da9052->dev); } diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 36b88e39549..82c9d645028 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -22,6 +22,11 @@ #include <linux/mfd/da9052/da9052.h> #include <linux/mfd/da9052/reg.h> +#ifdef CONFIG_OF +#include <linux/of.h> +#include <linux/of_device.h> +#endif + static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) { int reg_val, ret; @@ -41,13 +46,31 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) return 0; } +static struct i2c_device_id da9052_i2c_id[] = { + {"da9052", DA9052}, + {"da9053-aa", DA9053_AA}, + {"da9053-ba", DA9053_BA}, + {"da9053-bb", DA9053_BB}, + {} +}; + +#ifdef CONFIG_OF +static const struct of_device_id dialog_dt_ids[] = { + { .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] }, + { .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] }, + { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] }, + { .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] }, + { /* sentinel */ } +}; +#endif + static int __devinit da9052_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct da9052 *da9052; int ret; - da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL); + da9052 = devm_kzalloc(&client->dev, sizeof(struct da9052), GFP_KERNEL); if (!da9052) return -ENOMEM; @@ -55,8 +78,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_info(&client->dev, "Error in %s:i2c_check_functionality\n", __func__); - ret = -ENODEV; - goto err; + return -ENODEV; } da9052->dev = &client->dev; @@ -64,29 +86,39 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, da9052); - da9052->regmap = regmap_init_i2c(client, &da9052_regmap_config); + da9052->regmap = devm_regmap_init_i2c(client, &da9052_regmap_config); if (IS_ERR(da9052->regmap)) { ret = PTR_ERR(da9052->regmap); dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } ret = da9052_i2c_enable_multiwrite(da9052); if (ret < 0) - goto err_regmap; + return ret; + +#ifdef CONFIG_OF + if (!id) { + struct device_node *np = client->dev.of_node; + const struct of_device_id *deviceid; + + deviceid = of_match_node(dialog_dt_ids, np); + id = (const struct i2c_device_id *)deviceid->data; + } +#endif + + if (!id) { + ret = -ENODEV; + dev_err(&client->dev, "id is null.\n"); + return ret; + } ret = da9052_device_init(da9052, id->driver_data); if (ret != 0) - goto err_regmap; + return ret; return 0; - -err_regmap: - regmap_exit(da9052->regmap); -err: - kfree(da9052); - return ret; } static int __devexit da9052_i2c_remove(struct i2c_client *client) @@ -94,20 +126,9 @@ static int __devexit da9052_i2c_remove(struct i2c_client *client) struct da9052 *da9052 = i2c_get_clientdata(client); da9052_device_exit(da9052); - regmap_exit(da9052->regmap); - kfree(da9052); - return 0; } -static struct i2c_device_id da9052_i2c_id[] = { - {"da9052", DA9052}, - {"da9053-aa", DA9053_AA}, - {"da9053-ba", DA9053_BA}, - {"da9053-bb", DA9053_BB}, - {} -}; - static struct i2c_driver da9052_i2c_driver = { .probe = da9052_i2c_probe, .remove = __devexit_p(da9052_i2c_remove), @@ -115,6 +136,9 @@ static struct i2c_driver da9052_i2c_driver = { .driver = { .name = "da9052", .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = dialog_dt_ids, +#endif }, }; diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 6faf149e8d9..dbeadc5a643 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -25,8 +25,9 @@ static int __devinit da9052_spi_probe(struct spi_device *spi) { int ret; const struct spi_device_id *id = spi_get_device_id(spi); - struct da9052 *da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL); + struct da9052 *da9052; + da9052 = devm_kzalloc(&spi->dev, sizeof(struct da9052), GFP_KERNEL); if (!da9052) return -ENOMEM; @@ -42,25 +43,19 @@ static int __devinit da9052_spi_probe(struct spi_device *spi) da9052_regmap_config.read_flag_mask = 1; da9052_regmap_config.write_flag_mask = 0; - da9052->regmap = regmap_init_spi(spi, &da9052_regmap_config); + da9052->regmap = devm_regmap_init_spi(spi, &da9052_regmap_config); if (IS_ERR(da9052->regmap)) { ret = PTR_ERR(da9052->regmap); dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } ret = da9052_device_init(da9052, id->driver_data); if (ret != 0) - goto err_regmap; + return ret; return 0; - -err_regmap: - regmap_exit(da9052->regmap); -err: - kfree(da9052); - return ret; } static int __devexit da9052_spi_remove(struct spi_device *spi) @@ -68,9 +63,6 @@ static int __devexit da9052_spi_remove(struct spi_device *spi) struct da9052 *da9052 = dev_get_drvdata(&spi->dev); da9052_device_exit(da9052); - regmap_exit(da9052->regmap); - kfree(da9052); - return 0; } @@ -88,7 +80,6 @@ static struct spi_driver da9052_spi_driver = { .id_table = da9052_spi_id, .driver = { .name = "da9052", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, }; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 5be32489714..671c8bc14bb 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2720,6 +2720,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = { REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"), REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"), REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"), + REGULATOR_SUPPLY("v-i2c", "nmk-i2c.4"), /* "v-mmc" changed to "vcore" in the mainline kernel */ REGULATOR_SUPPLY("vcore", "sdi0"), REGULATOR_SUPPLY("vcore", "sdi1"), @@ -2958,9 +2959,10 @@ static struct mfd_cell db8500_prcmu_devs[] = { * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic * */ -static int __init db8500_prcmu_probe(struct platform_device *pdev) +static int __devinit db8500_prcmu_probe(struct platform_device *pdev) { - int err = 0; + struct device_node *np = pdev->dev.of_node; + int irq = 0, err = 0; if (ux500_is_svp()) return -ENODEV; @@ -2970,8 +2972,14 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); - err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler, - prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); + if (np) + irq = platform_get_irq(pdev, 0); + + if (!np || irq <= 0) + irq = IRQ_DB8500_PRCMU1; + + err = request_threaded_irq(irq, prcmu_irq_handler, + prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); if (err < 0) { pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n"); err = -EBUSY; @@ -2981,14 +2989,16 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) if (cpu_is_u8500v20_or_later()) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); - err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, - ARRAY_SIZE(db8500_prcmu_devs), NULL, - 0); + if (!np) { + err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, + ARRAY_SIZE(db8500_prcmu_devs), NULL, 0); + if (err) { + pr_err("prcmu: Failed to add subdevices\n"); + return err; + } + } - if (err) - pr_err("prcmu: Failed to add subdevices\n"); - else - pr_info("DB8500 PRCMU initialized\n"); + pr_info("DB8500 PRCMU initialized\n"); no_irq_return: return err; @@ -2999,11 +3009,12 @@ static struct platform_driver db8500_prcmu_driver = { .name = "db8500-prcmu", .owner = THIS_MODULE, }, + .probe = db8500_prcmu_probe, }; static int __init db8500_prcmu_init(void) { - return platform_driver_probe(&db8500_prcmu_driver, db8500_prcmu_probe); + return platform_driver_register(&db8500_prcmu_driver); } arch_initcall(db8500_prcmu_init); diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index b76657eb0c5..59df5584cb5 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -406,7 +406,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) return -ENXIO; } - msic = kzalloc(sizeof(*msic), GFP_KERNEL); + msic = devm_kzalloc(&pdev->dev, sizeof(*msic), GFP_KERNEL); if (!msic) return -ENOMEM; @@ -421,21 +421,13 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get SRAM iomem resource\n"); - ret = -ENODEV; - goto fail_free_msic; + return -ENODEV; } - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - ret = -EBUSY; - goto fail_free_msic; - } - - msic->irq_base = ioremap_nocache(res->start, resource_size(res)); + msic->irq_base = devm_request_and_ioremap(&pdev->dev, res); if (!msic->irq_base) { dev_err(&pdev->dev, "failed to map SRAM memory\n"); - ret = -ENOMEM; - goto fail_release_region; + return -ENOMEM; } platform_set_drvdata(pdev, msic); @@ -443,7 +435,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) ret = intel_msic_init_devices(msic); if (ret) { dev_err(&pdev->dev, "failed to initialize MSIC devices\n"); - goto fail_unmap_mem; + return ret; } dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n", @@ -451,27 +443,14 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) msic->vendor); return 0; - -fail_unmap_mem: - iounmap(msic->irq_base); -fail_release_region: - release_mem_region(res->start, resource_size(res)); -fail_free_msic: - kfree(msic); - - return ret; } static int __devexit intel_msic_remove(struct platform_device *pdev) { struct intel_msic *msic = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); intel_msic_remove_devices(msic); platform_set_drvdata(pdev, NULL); - iounmap(msic->irq_base); - release_mem_region(res->start, resource_size(res)); - kfree(msic); return 0; } diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index a9223ed1b7c..2ea99989551 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -283,23 +283,8 @@ static struct pci_driver cmodio_pci_driver = { .remove = __devexit_p(cmodio_pci_remove), }; -/* - * Module Init / Exit - */ - -static int __init cmodio_init(void) -{ - return pci_register_driver(&cmodio_pci_driver); -} - -static void __exit cmodio_exit(void) -{ - pci_unregister_driver(&cmodio_pci_driver); -} +module_pci_driver(cmodio_pci_driver); MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver"); MODULE_LICENSE("GPL"); - -module_init(cmodio_init); -module_exit(cmodio_exit); diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c new file mode 100644 index 00000000000..0b2879b87fd --- /dev/null +++ b/drivers/mfd/lm3533-core.c @@ -0,0 +1,667 @@ +/* + * lm3533-core.c -- LM3533 Core + * + * Copyright (C) 2011-2012 Texas Instruments + * + * Author: Johan Hovold <jhovold@gmail.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/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <linux/mfd/lm3533.h> + + +#define LM3533_BOOST_OVP_MASK 0x06 +#define LM3533_BOOST_OVP_SHIFT 1 + +#define LM3533_BOOST_FREQ_MASK 0x01 +#define LM3533_BOOST_FREQ_SHIFT 0 + +#define LM3533_BL_ID_MASK 1 +#define LM3533_LED_ID_MASK 3 +#define LM3533_BL_ID_MAX 1 +#define LM3533_LED_ID_MAX 3 + +#define LM3533_HVLED_ID_MAX 2 +#define LM3533_LVLED_ID_MAX 5 + +#define LM3533_REG_OUTPUT_CONF1 0x10 +#define LM3533_REG_OUTPUT_CONF2 0x11 +#define LM3533_REG_BOOST_PWM 0x2c + +#define LM3533_REG_MAX 0xb2 + + +static struct mfd_cell lm3533_als_devs[] = { + { + .name = "lm3533-als", + .id = -1, + }, +}; + +static struct mfd_cell lm3533_bl_devs[] = { + { + .name = "lm3533-backlight", + .id = 0, + }, + { + .name = "lm3533-backlight", + .id = 1, + }, +}; + +static struct mfd_cell lm3533_led_devs[] = { + { + .name = "lm3533-leds", + .id = 0, + }, + { + .name = "lm3533-leds", + .id = 1, + }, + { + .name = "lm3533-leds", + .id = 2, + }, + { + .name = "lm3533-leds", + .id = 3, + }, +}; + +int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val) +{ + int tmp; + int ret; + + ret = regmap_read(lm3533->regmap, reg, &tmp); + if (ret < 0) { + dev_err(lm3533->dev, "failed to read register %02x: %d\n", + reg, ret); + return ret; + } + + *val = tmp; + + dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_read); + +int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val) +{ + int ret; + + dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val); + + ret = regmap_write(lm3533->regmap, reg, val); + if (ret < 0) { + dev_err(lm3533->dev, "failed to write register %02x: %d\n", + reg, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_write); + +int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask) +{ + int ret; + + dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask); + + ret = regmap_update_bits(lm3533->regmap, reg, mask, val); + if (ret < 0) { + dev_err(lm3533->dev, "failed to update register %02x: %d\n", + reg, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_update); + +static int lm3533_set_boost_freq(struct lm3533 *lm3533, + enum lm3533_boost_freq freq) +{ + int ret; + + ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM, + freq << LM3533_BOOST_FREQ_SHIFT, + LM3533_BOOST_FREQ_MASK); + if (ret) + dev_err(lm3533->dev, "failed to set boost frequency\n"); + + return ret; +} + + +static int lm3533_set_boost_ovp(struct lm3533 *lm3533, + enum lm3533_boost_ovp ovp) +{ + int ret; + + ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM, + ovp << LM3533_BOOST_OVP_SHIFT, + LM3533_BOOST_OVP_MASK); + if (ret) + dev_err(lm3533->dev, "failed to set boost ovp\n"); + + return ret; +} + +/* + * HVLED output config -- output hvled controlled by backlight bl + */ +static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl) +{ + u8 val; + u8 mask; + int shift; + int ret; + + if (hvled == 0 || hvled > LM3533_HVLED_ID_MAX) + return -EINVAL; + + if (bl > LM3533_BL_ID_MAX) + return -EINVAL; + + shift = hvled - 1; + mask = LM3533_BL_ID_MASK << shift; + val = bl << shift; + + ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask); + if (ret) + dev_err(lm3533->dev, "failed to set hvled config\n"); + + return ret; +} + +/* + * LVLED output config -- output lvled controlled by LED led + */ +static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led) +{ + u8 reg; + u8 val; + u8 mask; + int shift; + int ret; + + if (lvled == 0 || lvled > LM3533_LVLED_ID_MAX) + return -EINVAL; + + if (led > LM3533_LED_ID_MAX) + return -EINVAL; + + if (lvled < 4) { + reg = LM3533_REG_OUTPUT_CONF1; + shift = 2 * lvled; + } else { + reg = LM3533_REG_OUTPUT_CONF2; + shift = 2 * (lvled - 4); + } + + mask = LM3533_LED_ID_MASK << shift; + val = led << shift; + + ret = lm3533_update(lm3533, reg, val, mask); + if (ret) + dev_err(lm3533->dev, "failed to set lvled config\n"); + + return ret; +} + +static void lm3533_enable(struct lm3533 *lm3533) +{ + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_set_value(lm3533->gpio_hwen, 1); +} + +static void lm3533_disable(struct lm3533 *lm3533) +{ + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_set_value(lm3533->gpio_hwen, 0); +} + +enum lm3533_attribute_type { + LM3533_ATTR_TYPE_BACKLIGHT, + LM3533_ATTR_TYPE_LED, +}; + +struct lm3533_device_attribute { + struct device_attribute dev_attr; + enum lm3533_attribute_type type; + union { + struct { + u8 id; + } output; + } u; +}; + +#define to_lm3533_dev_attr(_attr) \ + container_of(_attr, struct lm3533_device_attribute, dev_attr) + +static ssize_t show_output(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm3533 *lm3533 = dev_get_drvdata(dev); + struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); + int id = lattr->u.output.id; + u8 reg; + u8 val; + u8 mask; + int shift; + int ret; + + if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) { + reg = LM3533_REG_OUTPUT_CONF1; + shift = id - 1; + mask = LM3533_BL_ID_MASK << shift; + } else { + if (id < 4) { + reg = LM3533_REG_OUTPUT_CONF1; + shift = 2 * id; + } else { + reg = LM3533_REG_OUTPUT_CONF2; + shift = 2 * (id - 4); + } + mask = LM3533_LED_ID_MASK << shift; + } + + ret = lm3533_read(lm3533, reg, &val); + if (ret) + return ret; + + val = (val & mask) >> shift; + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t store_output(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lm3533 *lm3533 = dev_get_drvdata(dev); + struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); + int id = lattr->u.output.id; + u8 val; + int ret; + + if (kstrtou8(buf, 0, &val)) + return -EINVAL; + + if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) + ret = lm3533_set_hvled_config(lm3533, id, val); + else + ret = lm3533_set_lvled_config(lm3533, id, val); + + if (ret) + return ret; + + return len; +} + +#define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \ + struct lm3533_device_attribute lm3533_dev_attr_##_name = \ + { .dev_attr = __ATTR(_name, _mode, _show, _store), \ + .type = _type, \ + .u.output = { .id = _id }, } + +#define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \ + LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \ + show_output, store_output, _type, _id) + +#define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \ + LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr) +#define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \ + LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr) +/* + * Output config: + * + * output_hvled<nr> 0-1 + * output_lvled<nr> 0-3 + */ +static LM3533_OUTPUT_HVLED_ATTR_RW(1); +static LM3533_OUTPUT_HVLED_ATTR_RW(2); +static LM3533_OUTPUT_LVLED_ATTR_RW(1); +static LM3533_OUTPUT_LVLED_ATTR_RW(2); +static LM3533_OUTPUT_LVLED_ATTR_RW(3); +static LM3533_OUTPUT_LVLED_ATTR_RW(4); +static LM3533_OUTPUT_LVLED_ATTR_RW(5); + +static struct attribute *lm3533_attributes[] = { + &lm3533_dev_attr_output_hvled1.dev_attr.attr, + &lm3533_dev_attr_output_hvled2.dev_attr.attr, + &lm3533_dev_attr_output_lvled1.dev_attr.attr, + &lm3533_dev_attr_output_lvled2.dev_attr.attr, + &lm3533_dev_attr_output_lvled3.dev_attr.attr, + &lm3533_dev_attr_output_lvled4.dev_attr.attr, + &lm3533_dev_attr_output_lvled5.dev_attr.attr, + NULL, +}; + +#define to_dev_attr(_attr) \ + container_of(_attr, struct device_attribute, attr) + +static umode_t lm3533_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct lm3533 *lm3533 = dev_get_drvdata(dev); + struct device_attribute *dattr = to_dev_attr(attr); + struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr); + enum lm3533_attribute_type type = lattr->type; + umode_t mode = attr->mode; + + if (!lm3533->have_backlights && type == LM3533_ATTR_TYPE_BACKLIGHT) + mode = 0; + else if (!lm3533->have_leds && type == LM3533_ATTR_TYPE_LED) + mode = 0; + + return mode; +}; + +static struct attribute_group lm3533_attribute_group = { + .is_visible = lm3533_attr_is_visible, + .attrs = lm3533_attributes +}; + +static int __devinit lm3533_device_als_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int ret; + + if (!pdata->als) + return 0; + + lm3533_als_devs[0].platform_data = pdata->als; + lm3533_als_devs[0].pdata_size = sizeof(*pdata->als); + + ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0); + if (ret) { + dev_err(lm3533->dev, "failed to add ALS device\n"); + return ret; + } + + lm3533->have_als = 1; + + return 0; +} + +static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int i; + int ret; + + if (!pdata->backlights || pdata->num_backlights == 0) + return 0; + + if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs)) + pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs); + + for (i = 0; i < pdata->num_backlights; ++i) { + lm3533_bl_devs[i].platform_data = &pdata->backlights[i]; + lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]); + } + + ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs, + pdata->num_backlights, NULL, 0); + if (ret) { + dev_err(lm3533->dev, "failed to add backlight devices\n"); + return ret; + } + + lm3533->have_backlights = 1; + + return 0; +} + +static int __devinit lm3533_device_led_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int i; + int ret; + + if (!pdata->leds || pdata->num_leds == 0) + return 0; + + if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs)) + pdata->num_leds = ARRAY_SIZE(lm3533_led_devs); + + for (i = 0; i < pdata->num_leds; ++i) { + lm3533_led_devs[i].platform_data = &pdata->leds[i]; + lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]); + } + + ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs, + pdata->num_leds, NULL, 0); + if (ret) { + dev_err(lm3533->dev, "failed to add LED devices\n"); + return ret; + } + + lm3533->have_leds = 1; + + return 0; +} + +static int __devinit lm3533_device_setup(struct lm3533 *lm3533, + struct lm3533_platform_data *pdata) +{ + int ret; + + ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq); + if (ret) + return ret; + + ret = lm3533_set_boost_ovp(lm3533, pdata->boost_ovp); + if (ret) + return ret; + + return 0; +} + +static int __devinit lm3533_device_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int ret; + + dev_dbg(lm3533->dev, "%s\n", __func__); + + if (!pdata) { + dev_err(lm3533->dev, "no platform data\n"); + return -EINVAL; + } + + lm3533->gpio_hwen = pdata->gpio_hwen; + + dev_set_drvdata(lm3533->dev, lm3533); + + if (gpio_is_valid(lm3533->gpio_hwen)) { + ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW, + "lm3533-hwen"); + if (ret < 0) { + dev_err(lm3533->dev, + "failed to request HWEN GPIO %d\n", + lm3533->gpio_hwen); + return ret; + } + } + + lm3533_enable(lm3533); + + ret = lm3533_device_setup(lm3533, pdata); + if (ret) + goto err_disable; + + lm3533_device_als_init(lm3533); + lm3533_device_bl_init(lm3533); + lm3533_device_led_init(lm3533); + + ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group); + if (ret < 0) { + dev_err(lm3533->dev, "failed to create sysfs attributes\n"); + goto err_unregister; + } + + return 0; + +err_unregister: + mfd_remove_devices(lm3533->dev); +err_disable: + lm3533_disable(lm3533); + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_free(lm3533->gpio_hwen); + + return ret; +} + +static void __devexit lm3533_device_exit(struct lm3533 *lm3533) +{ + dev_dbg(lm3533->dev, "%s\n", __func__); + + sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group); + + mfd_remove_devices(lm3533->dev); + lm3533_disable(lm3533); + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_free(lm3533->gpio_hwen); +} + +static bool lm3533_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x10 ... 0x2c: + case 0x30 ... 0x38: + case 0x40 ... 0x45: + case 0x50 ... 0x57: + case 0x60 ... 0x6e: + case 0x70 ... 0x75: + case 0x80 ... 0x85: + case 0x90 ... 0x95: + case 0xa0 ... 0xa5: + case 0xb0 ... 0xb2: + return true; + default: + return false; + } +} + +static bool lm3533_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x34 ... 0x36: /* zone */ + case 0x37 ... 0x38: /* adc */ + case 0xb0 ... 0xb1: /* fault */ + return true; + default: + return false; + } +} + +static bool lm3533_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x34: /* zone */ + return true; + default: + return false; + } +} + +static struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LM3533_REG_MAX, + .readable_reg = lm3533_readable_register, + .volatile_reg = lm3533_volatile_register, + .precious_reg = lm3533_precious_register, +}; + +static int __devinit lm3533_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lm3533 *lm3533; + int ret; + + dev_dbg(&i2c->dev, "%s\n", __func__); + + lm3533 = devm_kzalloc(&i2c->dev, sizeof(*lm3533), GFP_KERNEL); + if (!lm3533) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm3533); + + lm3533->regmap = devm_regmap_init_i2c(i2c, ®map_config); + if (IS_ERR(lm3533->regmap)) + return PTR_ERR(lm3533->regmap); + + lm3533->dev = &i2c->dev; + lm3533->irq = i2c->irq; + + ret = lm3533_device_init(lm3533); + if (ret) + return ret; + + return 0; +} + +static int __devexit lm3533_i2c_remove(struct i2c_client *i2c) +{ + struct lm3533 *lm3533 = i2c_get_clientdata(i2c); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + lm3533_device_exit(lm3533); + + return 0; +} + +static const struct i2c_device_id lm3533_i2c_ids[] = { + { "lm3533", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids); + +static struct i2c_driver lm3533_i2c_driver = { + .driver = { + .name = "lm3533", + .owner = THIS_MODULE, + }, + .id_table = lm3533_i2c_ids, + .probe = lm3533_i2c_probe, + .remove = __devexit_p(lm3533_i2c_remove), +}; + +static int __init lm3533_i2c_init(void) +{ + return i2c_add_driver(&lm3533_i2c_driver); +} +subsys_initcall(lm3533_i2c_init); + +static void __exit lm3533_i2c_exit(void) +{ + i2c_del_driver(&lm3533_i2c_driver); +} +module_exit(lm3533_i2c_exit); + +MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); +MODULE_DESCRIPTION("LM3533 Core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c new file mode 100644 index 00000000000..a4cb7a5220a --- /dev/null +++ b/drivers/mfd/lm3533-ctrlbank.c @@ -0,0 +1,148 @@ +/* + * lm3533-ctrlbank.c -- LM3533 Generic Control Bank interface + * + * Copyright (C) 2011-2012 Texas Instruments + * + * Author: Johan Hovold <jhovold@gmail.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/module.h> + +#include <linux/mfd/lm3533.h> + + +#define LM3533_MAX_CURRENT_MIN 5000 +#define LM3533_MAX_CURRENT_MAX 29800 +#define LM3533_MAX_CURRENT_STEP 800 + +#define LM3533_BRIGHTNESS_MAX 255 +#define LM3533_PWM_MAX 0x3f + +#define LM3533_REG_PWM_BASE 0x14 +#define LM3533_REG_MAX_CURRENT_BASE 0x1f +#define LM3533_REG_CTRLBANK_ENABLE 0x27 +#define LM3533_REG_BRIGHTNESS_BASE 0x40 + + +static inline u8 lm3533_ctrlbank_get_reg(struct lm3533_ctrlbank *cb, u8 base) +{ + return base + cb->id; +} + +int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb) +{ + u8 mask; + int ret; + + dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); + + mask = 1 << cb->id; + ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, + mask, mask); + if (ret) + dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_enable); + +int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb) +{ + u8 mask; + int ret; + + dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); + + mask = 1 << cb->id; + ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask); + if (ret) + dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable); + +/* + * Full-scale current. + * + * imax 5000 - 29800 uA (800 uA step) + */ +int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax) +{ + u8 reg; + u8 val; + int ret; + + if (imax < LM3533_MAX_CURRENT_MIN || imax > LM3533_MAX_CURRENT_MAX) + return -EINVAL; + + val = (imax - LM3533_MAX_CURRENT_MIN) / LM3533_MAX_CURRENT_STEP; + + reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_MAX_CURRENT_BASE); + ret = lm3533_write(cb->lm3533, reg, val); + if (ret) + dev_err(cb->dev, "failed to set max current\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_max_current); + +#define lm3533_ctrlbank_set(_name, _NAME) \ +int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val) \ +{ \ + u8 reg; \ + int ret; \ + \ + if (val > LM3533_##_NAME##_MAX) \ + return -EINVAL; \ + \ + reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \ + ret = lm3533_write(cb->lm3533, reg, val); \ + if (ret) \ + dev_err(cb->dev, "failed to set " #_name "\n"); \ + \ + return ret; \ +} \ +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name); + +#define lm3533_ctrlbank_get(_name, _NAME) \ +int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val) \ +{ \ + u8 reg; \ + int ret; \ + \ + reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \ + ret = lm3533_read(cb->lm3533, reg, val); \ + if (ret) \ + dev_err(cb->dev, "failed to get " #_name "\n"); \ + \ + return ret; \ +} \ +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name); + +lm3533_ctrlbank_set(brightness, BRIGHTNESS); +lm3533_ctrlbank_get(brightness, BRIGHTNESS); + +/* + * PWM-input control mask: + * + * bit 5 - PWM-input enabled in Zone 4 + * bit 4 - PWM-input enabled in Zone 3 + * bit 3 - PWM-input enabled in Zone 2 + * bit 2 - PWM-input enabled in Zone 1 + * bit 1 - PWM-input enabled in Zone 0 + * bit 0 - PWM-input enabled + */ +lm3533_ctrlbank_set(pwm, PWM); +lm3533_ctrlbank_get(pwm, PWM); + + +MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); +MODULE_DESCRIPTION("LM3533 Control Bank interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c new file mode 100644 index 00000000000..027cc8f8613 --- /dev/null +++ b/drivers/mfd/lpc_ich.c @@ -0,0 +1,888 @@ +/* + * lpc_ich.c - LPC interface for Intel ICH + * + * LPC bridge function of the Intel ICH contains many other + * functional units, such as Interrupt controllers, Timers, + * Power Management, System Management, GPIO, RTC, and LPC + * Configuration Registers. + * + * This driver is derived from lpc_sch. + + * Copyright (c) 2011 Extreme Engineering Solution, Inc. + * Author: Aaron Sierra <asierra@xes-inc.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver supports the following I/O Controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO) + * document number 290687-002, 298242-027: 82801BA (ICH2) + * document number 290733-003, 290739-013: 82801CA (ICH3-S) + * document number 290716-001, 290718-007: 82801CAM (ICH3-M) + * document number 290744-001, 290745-025: 82801DB (ICH4) + * document number 252337-001, 252663-008: 82801DBM (ICH4-M) + * document number 273599-001, 273645-002: 82801E (C-ICH) + * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R) + * document number 300641-004, 300884-013: 6300ESB + * document number 301473-002, 301474-026: 82801F (ICH6) + * document number 313082-001, 313075-006: 631xESB, 632xESB + * document number 307013-003, 307014-024: 82801G (ICH7) + * document number 322896-001, 322897-001: NM10 + * document number 313056-003, 313057-017: 82801H (ICH8) + * document number 316972-004, 316973-012: 82801I (ICH9) + * document number 319973-002, 319974-002: 82801J (ICH10) + * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) + * document number 320066-003, 320257-008: EP80597 (IICH) + * document number 324645-001, 324646-001: Cougar Point (CPT) + * document number TBD : Patsburg (PBG) + * document number TBD : DH89xxCC + * document number TBD : Panther Point + * document number TBD : Lynx Point + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <linux/mfd/core.h> +#include <linux/mfd/lpc_ich.h> + +#define ACPIBASE 0x40 +#define ACPIBASE_GPE_OFF 0x28 +#define ACPIBASE_GPE_END 0x2f +#define ACPIBASE_SMI_OFF 0x30 +#define ACPIBASE_SMI_END 0x33 +#define ACPIBASE_TCO_OFF 0x60 +#define ACPIBASE_TCO_END 0x7f +#define ACPICTRL 0x44 + +#define ACPIBASE_GCS_OFF 0x3410 +#define ACPIBASE_GCS_END 0x3414 + +#define GPIOBASE 0x48 +#define GPIOCTRL 0x4C + +#define RCBABASE 0xf0 + +#define wdt_io_res(i) wdt_res(0, i) +#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i) +#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)]) + +static int lpc_ich_acpi_save = -1; +static int lpc_ich_gpio_save = -1; + +static struct resource wdt_ich_res[] = { + /* ACPI - TCO */ + { + .flags = IORESOURCE_IO, + }, + /* ACPI - SMI */ + { + .flags = IORESOURCE_IO, + }, + /* GCS */ + { + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource gpio_ich_res[] = { + /* GPIO */ + { + .flags = IORESOURCE_IO, + }, + /* ACPI - GPE0 */ + { + .flags = IORESOURCE_IO, + }, +}; + +enum lpc_cells { + LPC_WDT = 0, + LPC_GPIO, +}; + +static struct mfd_cell lpc_ich_cells[] = { + [LPC_WDT] = { + .name = "iTCO_wdt", + .num_resources = ARRAY_SIZE(wdt_ich_res), + .resources = wdt_ich_res, + .ignore_resource_conflicts = true, + }, + [LPC_GPIO] = { + .name = "gpio_ich", + .num_resources = ARRAY_SIZE(gpio_ich_res), + .resources = gpio_ich_res, + .ignore_resource_conflicts = true, + }, +}; + +/* chipset related info */ +enum lpc_chipsets { + LPC_ICH = 0, /* ICH */ + LPC_ICH0, /* ICH0 */ + LPC_ICH2, /* ICH2 */ + LPC_ICH2M, /* ICH2-M */ + LPC_ICH3, /* ICH3-S */ + LPC_ICH3M, /* ICH3-M */ + LPC_ICH4, /* ICH4 */ + LPC_ICH4M, /* ICH4-M */ + LPC_CICH, /* C-ICH */ + LPC_ICH5, /* ICH5 & ICH5R */ + LPC_6300ESB, /* 6300ESB */ + LPC_ICH6, /* ICH6 & ICH6R */ + LPC_ICH6M, /* ICH6-M */ + LPC_ICH6W, /* ICH6W & ICH6RW */ + LPC_631XESB, /* 631xESB/632xESB */ + LPC_ICH7, /* ICH7 & ICH7R */ + LPC_ICH7DH, /* ICH7DH */ + LPC_ICH7M, /* ICH7-M & ICH7-U */ + LPC_ICH7MDH, /* ICH7-M DH */ + LPC_NM10, /* NM10 */ + LPC_ICH8, /* ICH8 & ICH8R */ + LPC_ICH8DH, /* ICH8DH */ + LPC_ICH8DO, /* ICH8DO */ + LPC_ICH8M, /* ICH8M */ + LPC_ICH8ME, /* ICH8M-E */ + LPC_ICH9, /* ICH9 */ + LPC_ICH9R, /* ICH9R */ + LPC_ICH9DH, /* ICH9DH */ + LPC_ICH9DO, /* ICH9DO */ + LPC_ICH9M, /* ICH9M */ + LPC_ICH9ME, /* ICH9M-E */ + LPC_ICH10, /* ICH10 */ + LPC_ICH10R, /* ICH10R */ + LPC_ICH10D, /* ICH10D */ + LPC_ICH10DO, /* ICH10DO */ + LPC_PCH, /* PCH Desktop Full Featured */ + LPC_PCHM, /* PCH Mobile Full Featured */ + LPC_P55, /* P55 */ + LPC_PM55, /* PM55 */ + LPC_H55, /* H55 */ + LPC_QM57, /* QM57 */ + LPC_H57, /* H57 */ + LPC_HM55, /* HM55 */ + LPC_Q57, /* Q57 */ + LPC_HM57, /* HM57 */ + LPC_PCHMSFF, /* PCH Mobile SFF Full Featured */ + LPC_QS57, /* QS57 */ + LPC_3400, /* 3400 */ + LPC_3420, /* 3420 */ + LPC_3450, /* 3450 */ + LPC_EP80579, /* EP80579 */ + LPC_CPT, /* Cougar Point */ + LPC_CPTD, /* Cougar Point Desktop */ + LPC_CPTM, /* Cougar Point Mobile */ + LPC_PBG, /* Patsburg */ + LPC_DH89XXCC, /* DH89xxCC */ + LPC_PPT, /* Panther Point */ + LPC_LPT, /* Lynx Point */ +}; + +struct lpc_ich_info lpc_chipset_info[] __devinitdata = { + [LPC_ICH] = { + .name = "ICH", + .iTCO_version = 1, + }, + [LPC_ICH0] = { + .name = "ICH0", + .iTCO_version = 1, + }, + [LPC_ICH2] = { + .name = "ICH2", + .iTCO_version = 1, + }, + [LPC_ICH2M] = { + .name = "ICH2-M", + .iTCO_version = 1, + }, + [LPC_ICH3] = { + .name = "ICH3-S", + .iTCO_version = 1, + }, + [LPC_ICH3M] = { + .name = "ICH3-M", + .iTCO_version = 1, + }, + [LPC_ICH4] = { + .name = "ICH4", + .iTCO_version = 1, + }, + [LPC_ICH4M] = { + .name = "ICH4-M", + .iTCO_version = 1, + }, + [LPC_CICH] = { + .name = "C-ICH", + .iTCO_version = 1, + }, + [LPC_ICH5] = { + .name = "ICH5 or ICH5R", + .iTCO_version = 1, + }, + [LPC_6300ESB] = { + .name = "6300ESB", + .iTCO_version = 1, + }, + [LPC_ICH6] = { + .name = "ICH6 or ICH6R", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_ICH6M] = { + .name = "ICH6-M", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_ICH6W] = { + .name = "ICH6W or ICH6RW", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_631XESB] = { + .name = "631xESB/632xESB", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_ICH7] = { + .name = "ICH7 or ICH7R", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH7DH] = { + .name = "ICH7DH", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH7M] = { + .name = "ICH7-M or ICH7-U", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH7MDH] = { + .name = "ICH7-M DH", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_NM10] = { + .name = "NM10", + .iTCO_version = 2, + }, + [LPC_ICH8] = { + .name = "ICH8 or ICH8R", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8DH] = { + .name = "ICH8DH", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8DO] = { + .name = "ICH8DO", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8M] = { + .name = "ICH8M", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8ME] = { + .name = "ICH8M-E", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH9] = { + .name = "ICH9", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9R] = { + .name = "ICH9R", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9DH] = { + .name = "ICH9DH", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9DO] = { + .name = "ICH9DO", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9M] = { + .name = "ICH9M", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9ME] = { + .name = "ICH9M-E", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH10] = { + .name = "ICH10", + .iTCO_version = 2, + .gpio_version = ICH_V10CONS_GPIO, + }, + [LPC_ICH10R] = { + .name = "ICH10R", + .iTCO_version = 2, + .gpio_version = ICH_V10CONS_GPIO, + }, + [LPC_ICH10D] = { + .name = "ICH10D", + .iTCO_version = 2, + .gpio_version = ICH_V10CORP_GPIO, + }, + [LPC_ICH10DO] = { + .name = "ICH10DO", + .iTCO_version = 2, + .gpio_version = ICH_V10CORP_GPIO, + }, + [LPC_PCH] = { + .name = "PCH Desktop Full Featured", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PCHM] = { + .name = "PCH Mobile Full Featured", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_P55] = { + .name = "P55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PM55] = { + .name = "PM55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_H55] = { + .name = "H55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_QM57] = { + .name = "QM57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_H57] = { + .name = "H57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_HM55] = { + .name = "HM55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_Q57] = { + .name = "Q57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_HM57] = { + .name = "HM57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PCHMSFF] = { + .name = "PCH Mobile SFF Full Featured", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_QS57] = { + .name = "QS57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_3400] = { + .name = "3400", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_3420] = { + .name = "3420", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_3450] = { + .name = "3450", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_EP80579] = { + .name = "EP80579", + .iTCO_version = 2, + }, + [LPC_CPT] = { + .name = "Cougar Point", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_CPTD] = { + .name = "Cougar Point Desktop", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_CPTM] = { + .name = "Cougar Point Mobile", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PBG] = { + .name = "Patsburg", + .iTCO_version = 2, + }, + [LPC_DH89XXCC] = { + .name = "DH89xxCC", + .iTCO_version = 2, + }, + [LPC_PPT] = { + .name = "Panther Point", + .iTCO_version = 2, + }, + [LPC_LPT] = { + .name = "Lynx Point", + .iTCO_version = 2, + }, +}; + +/* + * This data only exists for exporting the supported PCI ids + * via MODULE_DEVICE_TABLE. We do not actually register a + * pci_driver, because the I/O Controller Hub has also other + * functions that probably will be registered by other drivers. + */ +static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = { + { PCI_VDEVICE(INTEL, 0x2410), LPC_ICH}, + { PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0}, + { PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2}, + { PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M}, + { PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3}, + { PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M}, + { PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4}, + { PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M}, + { PCI_VDEVICE(INTEL, 0x2450), LPC_CICH}, + { PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5}, + { PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB}, + { PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6}, + { PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M}, + { PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W}, + { PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7}, + { PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH}, + { PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M}, + { PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH}, + { PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10}, + { PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8}, + { PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH}, + { PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO}, + { PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M}, + { PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME}, + { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, + { PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R}, + { PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH}, + { PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO}, + { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, + { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, + { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10}, + { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, + { PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D}, + { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, + { PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH}, + { PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM}, + { PCI_VDEVICE(INTEL, 0x3b02), LPC_P55}, + { PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55}, + { PCI_VDEVICE(INTEL, 0x3b06), LPC_H55}, + { PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57}, + { PCI_VDEVICE(INTEL, 0x3b08), LPC_H57}, + { PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55}, + { PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57}, + { PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57}, + { PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF}, + { PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57}, + { PCI_VDEVICE(INTEL, 0x3b12), LPC_3400}, + { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420}, + { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450}, + { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579}, + { PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD}, + { PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM}, + { PCI_VDEVICE(INTEL, 0x1c44), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c45), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c46), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c47), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c48), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c49), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4a), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4b), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4c), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4d), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4e), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4f), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c50), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c51), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c52), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c53), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c54), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c55), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c56), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c57), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c58), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c59), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5a), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5b), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5c), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5d), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5e), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG}, + { PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG}, + { PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC}, + { PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e43), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e44), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e45), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e46), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e47), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e48), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e49), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4a), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4b), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4c), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4d), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4e), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4f), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e50), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e51), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e52), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e53), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e54), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e55), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e56), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e57), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e58), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e59), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5a), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5b), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5c), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c43), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c44), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c45), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c46), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c47), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c48), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c49), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4a), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4b), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4c), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4d), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4e), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4f), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c50), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c51), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c52), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c53), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c54), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c55), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c56), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c57), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c58), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c59), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5a), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5b), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5c), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT}, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE(pci, lpc_ich_ids); + +static void lpc_ich_restore_config_space(struct pci_dev *dev) +{ + if (lpc_ich_acpi_save >= 0) { + pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save); + lpc_ich_acpi_save = -1; + } + + if (lpc_ich_gpio_save >= 0) { + pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save); + lpc_ich_gpio_save = -1; + } +} + +static void __devinit lpc_ich_enable_acpi_space(struct pci_dev *dev) +{ + u8 reg_save; + + pci_read_config_byte(dev, ACPICTRL, ®_save); + pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10); + lpc_ich_acpi_save = reg_save; +} + +static void __devinit lpc_ich_enable_gpio_space(struct pci_dev *dev) +{ + u8 reg_save; + + pci_read_config_byte(dev, GPIOCTRL, ®_save); + pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10); + lpc_ich_gpio_save = reg_save; +} + +static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell, + const struct pci_device_id *id) +{ + cell->platform_data = &lpc_chipset_info[id->driver_data]; + cell->pdata_size = sizeof(struct lpc_ich_info); +} + +static int __devinit lpc_ich_init_gpio(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u32 base_addr_cfg; + u32 base_addr; + int ret; + bool acpi_conflict = false; + struct resource *res; + + /* Setup power management base register */ + pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); + base_addr = base_addr_cfg & 0x0000ff80; + if (!base_addr) { + dev_err(&dev->dev, "I/O space for ACPI uninitialized\n"); + lpc_ich_cells[LPC_GPIO].num_resources--; + goto gpe0_done; + } + + res = &gpio_ich_res[ICH_RES_GPE0]; + res->start = base_addr + ACPIBASE_GPE_OFF; + res->end = base_addr + ACPIBASE_GPE_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + /* + * This isn't fatal for the GPIO, but we have to make sure that + * the platform_device subsystem doesn't see this resource + * or it will register an invalid region. + */ + lpc_ich_cells[LPC_GPIO].num_resources--; + acpi_conflict = true; + } else { + lpc_ich_enable_acpi_space(dev); + } + +gpe0_done: + /* Setup GPIO base register */ + pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); + base_addr = base_addr_cfg & 0x0000ff80; + if (!base_addr) { + dev_err(&dev->dev, "I/O space for GPIO uninitialized\n"); + ret = -ENODEV; + goto gpio_done; + } + + /* Older devices provide fewer GPIO and have a smaller resource size. */ + res = &gpio_ich_res[ICH_RES_GPIO]; + res->start = base_addr; + switch (lpc_chipset_info[id->driver_data].gpio_version) { + case ICH_V5_GPIO: + case ICH_V10CORP_GPIO: + res->end = res->start + 128 - 1; + break; + default: + res->end = res->start + 64 - 1; + break; + } + + ret = acpi_check_resource_conflict(res); + if (ret) { + /* this isn't necessarily fatal for the GPIO */ + acpi_conflict = true; + goto gpio_done; + } + lpc_ich_enable_gpio_space(dev); + + lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id); + ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO], + 1, NULL, 0); + +gpio_done: + if (acpi_conflict) + pr_warn("Resource conflict(s) found affecting %s\n", + lpc_ich_cells[LPC_GPIO].name); + return ret; +} + +static int __devinit lpc_ich_init_wdt(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u32 base_addr_cfg; + u32 base_addr; + int ret; + bool acpi_conflict = false; + struct resource *res; + + /* Setup power management base register */ + pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); + base_addr = base_addr_cfg & 0x0000ff80; + if (!base_addr) { + dev_err(&dev->dev, "I/O space for ACPI uninitialized\n"); + ret = -ENODEV; + goto wdt_done; + } + + res = wdt_io_res(ICH_RES_IO_TCO); + res->start = base_addr + ACPIBASE_TCO_OFF; + res->end = base_addr + ACPIBASE_TCO_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + acpi_conflict = true; + goto wdt_done; + } + + res = wdt_io_res(ICH_RES_IO_SMI); + res->start = base_addr + ACPIBASE_SMI_OFF; + res->end = base_addr + ACPIBASE_SMI_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + acpi_conflict = true; + goto wdt_done; + } + lpc_ich_enable_acpi_space(dev); + + /* + * Get the Memory-Mapped GCS register. To get access to it + * we have to read RCBA from PCI Config space 0xf0 and use + * it as base. GCS = RCBA + ICH6_GCS(0x3410). + */ + if (lpc_chipset_info[id->driver_data].iTCO_version == 2) { + pci_read_config_dword(dev, RCBABASE, &base_addr_cfg); + base_addr = base_addr_cfg & 0xffffc000; + if (!(base_addr_cfg & 1)) { + pr_err("RCBA is disabled by hardware/BIOS, " + "device disabled\n"); + ret = -ENODEV; + goto wdt_done; + } + res = wdt_mem_res(ICH_RES_MEM_GCS); + res->start = base_addr + ACPIBASE_GCS_OFF; + res->end = base_addr + ACPIBASE_GCS_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + acpi_conflict = true; + goto wdt_done; + } + } + + lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id); + ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT], + 1, NULL, 0); + +wdt_done: + if (acpi_conflict) + pr_warn("Resource conflict(s) found affecting %s\n", + lpc_ich_cells[LPC_WDT].name); + return ret; +} + +static int __devinit lpc_ich_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + int ret; + bool cell_added = false; + + ret = lpc_ich_init_wdt(dev, id); + if (!ret) + cell_added = true; + + ret = lpc_ich_init_gpio(dev, id); + if (!ret) + cell_added = true; + + /* + * We only care if at least one or none of the cells registered + * successfully. + */ + if (!cell_added) { + lpc_ich_restore_config_space(dev); + return -ENODEV; + } + + return 0; +} + +static void __devexit lpc_ich_remove(struct pci_dev *dev) +{ + mfd_remove_devices(&dev->dev); + lpc_ich_restore_config_space(dev); +} + +static struct pci_driver lpc_ich_driver = { + .name = "lpc_ich", + .id_table = lpc_ich_ids, + .probe = lpc_ich_probe, + .remove = __devexit_p(lpc_ich_remove), +}; + +static int __init lpc_ich_init(void) +{ + return pci_register_driver(&lpc_ich_driver); +} + +static void __exit lpc_ich_exit(void) +{ + pci_unregister_driver(&lpc_ich_driver); +} + +module_init(lpc_ich_init); +module_exit(lpc_ich_exit); + +MODULE_AUTHOR("Aaron Sierra <asierra@xes-inc.com>"); +MODULE_DESCRIPTION("LPC interface for Intel ICH"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index abc421364a4..9f20abc5e39 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -36,6 +36,7 @@ #define GPIOBASE 0x44 #define GPIO_IO_SIZE 64 +#define GPIO_IO_SIZE_CENTERTON 128 #define WDTBASE 0x84 #define WDT_IO_SIZE 64 @@ -68,7 +69,7 @@ static struct resource wdt_sch_resource = { static struct mfd_cell tunnelcreek_cells[] = { { - .name = "tunnelcreek_wdt", + .name = "ie6xx_wdt", .num_resources = 1, .resources = &wdt_sch_resource, }, @@ -77,6 +78,7 @@ static struct mfd_cell tunnelcreek_cells[] = { static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) }, { 0, } }; MODULE_DEVICE_TABLE(pci, lpc_sch_ids); @@ -115,7 +117,11 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, } gpio_sch_resource.start = base_addr; - gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; + + if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1; + else + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++) lpc_sch_cells[i].id = id->device; @@ -125,7 +131,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, if (ret) goto out_dev; - if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) { + if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC + || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); if (!(base_addr_cfg & (1 << 31))) { dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n"); @@ -167,18 +174,7 @@ static struct pci_driver lpc_sch_driver = { .remove = __devexit_p(lpc_sch_remove), }; -static int __init lpc_sch_init(void) -{ - return pci_register_driver(&lpc_sch_driver); -} - -static void __exit lpc_sch_exit(void) -{ - pci_unregister_driver(&lpc_sch_driver); -} - -module_init(lpc_sch_init); -module_exit(lpc_sch_exit); +module_pci_driver(lpc_sch_driver); MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c new file mode 100644 index 00000000000..2b403569e0a --- /dev/null +++ b/drivers/mfd/max77693-irq.c @@ -0,0 +1,309 @@ +/* + * max77693-irq.c - Interrupt controller support for MAX77693 + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * SangYoung Son <hello.son@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * 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 + * + * This driver is based on max8997-irq.c + */ + +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/irqdomain.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> + +static const u8 max77693_mask_reg[] = { + [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK, + [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK, + [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK, + [MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1, + [MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2, + [MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3, +}; + +static struct regmap *max77693_get_regmap(struct max77693_dev *max77693, + enum max77693_irq_source src) +{ + switch (src) { + case LED_INT ... CHG_INT: + return max77693->regmap; + case MUIC_INT1 ... MUIC_INT3: + return max77693->regmap_muic; + default: + return ERR_PTR(-EINVAL); + } +} + +struct max77693_irq_data { + int mask; + enum max77693_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct max77693_irq_data max77693_irqs[] = { + DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN, LED_INT, 1 << 0), + DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT, LED_INT, 1 << 1), + DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN, LED_INT, 1 << 2), + DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT, LED_INT, 1 << 3), + DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH, LED_INT, 1 << 4), + + DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT, TOPSYS_INT, 1 << 0), + DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT, TOPSYS_INT, 1 << 1), + DECLARE_IRQ(MAX77693_TOPSYS_IRQ_LOWSYS_INT, TOPSYS_INT, 1 << 3), + + DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I, CHG_INT, 1 << 0), + DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I, CHG_INT, 1 << 2), + DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I, CHG_INT, 1 << 3), + DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I, CHG_INT, 1 << 4), + DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I, CHG_INT, 1 << 6), + + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC, MUIC_INT1, 1 << 0), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_LOW, MUIC_INT1, 1 << 1), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_ERR, MUIC_INT1, 1 << 2), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K, MUIC_INT1, 1 << 3), + + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP, MUIC_INT2, 1 << 0), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN, MUIC_INT2, 1 << 1), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR, MUIC_INT2, 1 << 2), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP, MUIC_INT2, 1 << 3), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT, MUIC_INT2, 1 << 4), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM, MUIC_INT2, 1 << 5), + + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC, MUIC_INT3, 1 << 0), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC, MUIC_INT3, 1 << 1), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP, MUIC_INT3, 1 << 2), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR, MUIC_INT3, 1 << 3), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MUIC_INT3, 1 << 4), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BAT_DET, MUIC_INT3, 1 << 5), +}; + +static void max77693_irq_lock(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + + mutex_lock(&max77693->irqlock); +} + +static void max77693_irq_sync_unlock(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { + u8 mask_reg = max77693_mask_reg[i]; + struct regmap *map = max77693_get_regmap(max77693, i); + + if (mask_reg == MAX77693_REG_INVALID || + IS_ERR_OR_NULL(map)) + continue; + max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i]; + + max77693_write_reg(map, max77693_mask_reg[i], + max77693->irq_masks_cur[i]); + } + + mutex_unlock(&max77693->irqlock); +} + +static const inline struct max77693_irq_data * +irq_to_max77693_irq(struct max77693_dev *max77693, int irq) +{ + return &max77693_irqs[irq]; +} + +static void max77693_irq_mask(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + const struct max77693_irq_data *irq_data = + irq_to_max77693_irq(max77693, data->irq); + + if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) + max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; + else + max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; +} + +static void max77693_irq_unmask(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + const struct max77693_irq_data *irq_data = + irq_to_max77693_irq(max77693, data->irq); + + if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) + max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; + else + max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; +} + +static struct irq_chip max77693_irq_chip = { + .name = "max77693", + .irq_bus_lock = max77693_irq_lock, + .irq_bus_sync_unlock = max77693_irq_sync_unlock, + .irq_mask = max77693_irq_mask, + .irq_unmask = max77693_irq_unmask, +}; + +#define MAX77693_IRQSRC_CHG (1 << 0) +#define MAX77693_IRQSRC_TOP (1 << 1) +#define MAX77693_IRQSRC_FLASH (1 << 2) +#define MAX77693_IRQSRC_MUIC (1 << 3) +static irqreturn_t max77693_irq_thread(int irq, void *data) +{ + struct max77693_dev *max77693 = data; + u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {}; + u8 irq_src; + int ret; + int i, cur_irq; + + ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_INTSRC, + &irq_src); + if (ret < 0) { + dev_err(max77693->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (irq_src & MAX77693_IRQSRC_CHG) + /* CHG_INT */ + ret = max77693_read_reg(max77693->regmap, MAX77693_CHG_REG_CHG_INT, + &irq_reg[CHG_INT]); + + if (irq_src & MAX77693_IRQSRC_TOP) + /* TOPSYS_INT */ + ret = max77693_read_reg(max77693->regmap, + MAX77693_PMIC_REG_TOPSYS_INT, &irq_reg[TOPSYS_INT]); + + if (irq_src & MAX77693_IRQSRC_FLASH) + /* LED_INT */ + ret = max77693_read_reg(max77693->regmap, + MAX77693_LED_REG_FLASH_INT, &irq_reg[LED_INT]); + + if (irq_src & MAX77693_IRQSRC_MUIC) + /* MUIC INT1 ~ INT3 */ + max77693_bulk_read(max77693->regmap, MAX77693_MUIC_REG_INT1, + MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]); + + /* Apply masking */ + for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { + if (i >= MUIC_INT1 && i <= MUIC_INT3) + irq_reg[i] &= max77693->irq_masks_cur[i]; + else + irq_reg[i] &= ~max77693->irq_masks_cur[i]; + } + + /* Report */ + for (i = 0; i < MAX77693_IRQ_NR; i++) { + if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask) { + cur_irq = irq_find_mapping(max77693->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } + } + + return IRQ_HANDLED; +} + +int max77693_irq_resume(struct max77693_dev *max77693) +{ + if (max77693->irq) + max77693_irq_thread(0, max77693); + + return 0; +} + +static int max77693_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max77693_dev *max77693 = d->host_data; + + irq_set_chip_data(irq, max77693); + irq_set_chip_and_handler(irq, &max77693_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + return 0; +} + +static struct irq_domain_ops max77693_irq_domain_ops = { + .map = max77693_irq_domain_map, +}; + +int max77693_irq_init(struct max77693_dev *max77693) +{ + struct irq_domain *domain; + int i; + int ret; + + mutex_init(&max77693->irqlock); + + /* Mask individual interrupt sources */ + for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { + struct regmap *map; + /* MUIC IRQ 0:MASK 1:NOT MASK */ + /* Other IRQ 1:MASK 0:NOT MASK */ + if (i >= MUIC_INT1 && i <= MUIC_INT3) { + max77693->irq_masks_cur[i] = 0x00; + max77693->irq_masks_cache[i] = 0x00; + } else { + max77693->irq_masks_cur[i] = 0xff; + max77693->irq_masks_cache[i] = 0xff; + } + map = max77693_get_regmap(max77693, i); + + if (IS_ERR_OR_NULL(map)) + continue; + if (max77693_mask_reg[i] == MAX77693_REG_INVALID) + continue; + if (i >= MUIC_INT1 && i <= MUIC_INT3) + max77693_write_reg(map, max77693_mask_reg[i], 0x00); + else + max77693_write_reg(map, max77693_mask_reg[i], 0xff); + } + + domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR, + &max77693_irq_domain_ops, max77693); + if (!domain) { + dev_err(max77693->dev, "could not create irq domain\n"); + return -ENODEV; + } + max77693->irq_domain = domain; + + ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max77693-irq", max77693); + + if (ret) + dev_err(max77693->dev, "Failed to request IRQ %d: %d\n", + max77693->irq, ret); + + return 0; +} + +void max77693_irq_exit(struct max77693_dev *max77693) +{ + if (max77693->irq) + free_irq(max77693->irq, max77693); +} diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c new file mode 100644 index 00000000000..e9e4278722f --- /dev/null +++ b/drivers/mfd/max77693.c @@ -0,0 +1,249 @@ +/* + * max77693.c - mfd core driver for the MAX 77693 + * + * Copyright (C) 2012 Samsung Electronics + * SangYoung Son <hello.son@smasung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * 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 + * + * This driver is based on max8997.c + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> +#include <linux/regulator/machine.h> +#include <linux/regmap.h> + +#define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */ +#define I2C_ADDR_MUIC (0x4A >> 1) +#define I2C_ADDR_HAPTIC (0x90 >> 1) + +static struct mfd_cell max77693_devs[] = { + { .name = "max77693-pmic", }, + { .name = "max77693-charger", }, + { .name = "max77693-flash", }, + { .name = "max77693-muic", }, + { .name = "max77693-haptic", }, +}; + +int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest) +{ + unsigned int val; + int ret; + + ret = regmap_read(map, reg, &val); + *dest = val; + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_read_reg); + +int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf) +{ + int ret; + + ret = regmap_bulk_read(map, reg, buf, count); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_bulk_read); + +int max77693_write_reg(struct regmap *map, u8 reg, u8 value) +{ + int ret; + + ret = regmap_write(map, reg, value); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_write_reg); + +int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf) +{ + int ret; + + ret = regmap_bulk_write(map, reg, buf, count); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_bulk_write); + +int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask) +{ + int ret; + + ret = regmap_update_bits(map, reg, mask, val); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_update_reg); + +static const struct regmap_config max77693_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77693_PMIC_REG_END, +}; + +static int max77693_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77693_dev *max77693; + struct max77693_platform_data *pdata = i2c->dev.platform_data; + u8 reg_data; + int ret = 0; + + max77693 = devm_kzalloc(&i2c->dev, + sizeof(struct max77693_dev), GFP_KERNEL); + if (max77693 == NULL) + return -ENOMEM; + + max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config); + if (IS_ERR(max77693->regmap)) { + ret = PTR_ERR(max77693->regmap); + dev_err(max77693->dev,"failed to allocate register map: %d\n", + ret); + goto err_regmap; + } + + i2c_set_clientdata(i2c, max77693); + max77693->dev = &i2c->dev; + max77693->i2c = i2c; + max77693->irq = i2c->irq; + max77693->type = id->driver_data; + + if (!pdata) + goto err_regmap; + + max77693->wakeup = pdata->wakeup; + + mutex_init(&max77693->iolock); + + if (max77693_read_reg(max77693->regmap, + MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) { + dev_err(max77693->dev, "device not found on this channel\n"); + ret = -ENODEV; + goto err_regmap; + } else + dev_info(max77693->dev, "device ID: 0x%x\n", reg_data); + + max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); + i2c_set_clientdata(max77693->muic, max77693); + + max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); + i2c_set_clientdata(max77693->haptic, max77693); + + ret = max77693_irq_init(max77693); + if (ret < 0) + goto err_mfd; + + pm_runtime_set_active(max77693->dev); + + ret = mfd_add_devices(max77693->dev, -1, max77693_devs, + ARRAY_SIZE(max77693_devs), NULL, 0); + if (ret < 0) + goto err_mfd; + + device_init_wakeup(max77693->dev, pdata->wakeup); + + return ret; + +err_mfd: + i2c_unregister_device(max77693->muic); + i2c_unregister_device(max77693->haptic); +err_regmap: + kfree(max77693); + + return ret; +} + +static int max77693_i2c_remove(struct i2c_client *i2c) +{ + struct max77693_dev *max77693 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77693->dev); + i2c_unregister_device(max77693->muic); + i2c_unregister_device(max77693->haptic); + + return 0; +} + +static const struct i2c_device_id max77693_i2c_id[] = { + { "max77693", TYPE_MAX77693 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77693_i2c_id); + +static int max77693_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77693_dev *max77693 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max77693->irq, 1); + return 0; +} + +static int max77693_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77693_dev *max77693 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max77693->irq, 0); + return max77693_irq_resume(max77693); +} + +const struct dev_pm_ops max77693_pm = { + .suspend = max77693_suspend, + .resume = max77693_resume, +}; + +static struct i2c_driver max77693_i2c_driver = { + .driver = { + .name = "max77693", + .owner = THIS_MODULE, + .pm = &max77693_pm, + }, + .probe = max77693_i2c_probe, + .remove = max77693_i2c_remove, + .id_table = max77693_i2c_id, +}; + +static int __init max77693_i2c_init(void) +{ + return i2c_add_driver(&max77693_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max77693_i2c_init); + +static void __exit max77693_i2c_exit(void) +{ + i2c_del_driver(&max77693_i2c_driver); +} +module_exit(max77693_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver"); +MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 738722cdeca..f0ea3b8b3e4 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -15,24 +15,13 @@ #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> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> -struct mc13xxx { - struct spi_device *spidev; - struct mutex lock; - int irq; - int flags; - - irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; - void *irqdata[MC13XXX_NUM_IRQ]; - - int adcflags; -}; +#include "mc13xxx.h" #define MC13XXX_IRQSTAT0 0 #define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0) @@ -139,34 +128,29 @@ struct mc13xxx { #define MC13XXX_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", + dev_dbg(mc13xxx->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", + dev_dbg(mc13xxx->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", + dev_dbg(mc13xxx->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)); @@ -174,84 +158,35 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val) 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); + ret = regmap_read(mc13xxx->regmap, offset, val); + dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val); - if (ret) - return ret; - - *val &= 0xffffff; - - dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val); - - return 0; + return ret; } 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); + dev_vdbg(mc13xxx->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; + return regmap_write(mc13xxx->regmap, offset, val); } 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(!mutex_is_locked(&mc13xxx->lock)); BUG_ON(val & ~mask); + dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n", + offset, val, mask); - ret = mc13xxx_reg_read(mc13xxx, offset, &valread); - if (ret) - return ret; - - valread = (valread & ~mask) | val; - - return mc13xxx_reg_write(mc13xxx, offset, valread); + return regmap_update_bits(mc13xxx->regmap, offset, mask, val); } EXPORT_SYMBOL(mc13xxx_reg_rmw); @@ -439,7 +374,7 @@ static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx, if (handled == IRQ_HANDLED) num_handled++; } else { - dev_err(&mc13xxx->spidev->dev, + dev_err(mc13xxx->dev, "BUG: irq %u but no handler\n", baseirq + irq); @@ -475,25 +410,23 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data) return IRQ_RETVAL(handled); } -enum mc13xxx_id { - MC13XXX_ID_MC13783, - MC13XXX_ID_MC13892, - MC13XXX_ID_INVALID, -}; - static 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) +static int mc13xxx_identify(struct mc13xxx *mc13xxx) { u32 icid; u32 revision; - const char *name; int ret; + /* + * Get the generation ID from register 46, as apparently some older + * IC revisions only have this info at this location. Newer ICs seem to + * have both. + */ ret = mc13xxx_reg_read(mc13xxx, 46, &icid); if (ret) return ret; @@ -502,26 +435,23 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id) switch (icid) { case 2: - *id = MC13XXX_ID_MC13783; - name = "mc13783"; + mc13xxx->ictype = MC13XXX_ID_MC13783; break; case 7: - *id = MC13XXX_ID_MC13892; - name = "mc13892"; + mc13xxx->ictype = MC13XXX_ID_MC13892; break; default: - *id = MC13XXX_ID_INVALID; + mc13xxx->ictype = MC13XXX_ID_INVALID; break; } - if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) { + if (mc13xxx->ictype == MC13XXX_ID_MC13783 || + mc13xxx->ictype == MC13XXX_ID_MC13892) { ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); - if (ret) - return ret; - dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, " + dev_info(mc13xxx->dev, "%s: rev: %d.%d, " "fin: %d, fab: %d, icid: %d/%d\n", - mc13xxx_chipname[*id], + mc13xxx_chipname[mc13xxx->ictype], maskval(revision, MC13XXX_REVISION_REVFULL), maskval(revision, MC13XXX_REVISION_REVMETAL), maskval(revision, MC13XXX_REVISION_FIN), @@ -530,26 +460,12 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id) 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; + return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 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]; + return mc13xxx_chipname[mc13xxx->ictype]; } int mc13xxx_get_flags(struct mc13xxx *mc13xxx) @@ -592,7 +508,7 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, }; init_completion(&adcdone_data.done); - dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__); + dev_dbg(mc13xxx->dev, "%s\n", __func__); mc13xxx_lock(mc13xxx); @@ -637,7 +553,8 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, adc1 |= ato << MC13783_ADC1_ATO_SHIFT; if (atox) adc1 |= MC13783_ADC1_ATOX; - dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__); + + dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__); mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, mc13xxx_handler_adcdone, __func__, &adcdone_data); mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE); @@ -695,7 +612,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, if (!cell.name) return -ENOMEM; - return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0); + return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0); } static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) @@ -706,7 +623,7 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) #ifdef CONFIG_OF static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx) { - struct device_node *np = mc13xxx->spidev->dev.of_node; + struct device_node *np = mc13xxx->dev->of_node; if (!np) return -ENODEV; @@ -732,55 +649,15 @@ static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx) } #endif -static const struct spi_device_id mc13xxx_device_id[] = { - { - .name = "mc13783", - .driver_data = MC13XXX_ID_MC13783, - }, { - .name = "mc13892", - .driver_data = MC13XXX_ID_MC13892, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); - -static const struct of_device_id mc13xxx_dt_ids[] = { - { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, }, - { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); - -static int mc13xxx_probe(struct spi_device *spi) +int mc13xxx_common_init(struct mc13xxx *mc13xxx, + struct mc13xxx_platform_data *pdata, int irq) { - const struct of_device_id *of_id; - struct spi_driver *sdrv = to_spi_driver(spi->dev.driver); - struct mc13xxx *mc13xxx; - struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev); - enum mc13xxx_id id; int ret; - of_id = of_match_device(mc13xxx_dt_ids, &spi->dev); - if (of_id) - sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data]; - - 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) + ret = mc13xxx_identify(mc13xxx); + if (ret) goto err_revision; /* mask all irqs */ @@ -792,18 +669,19 @@ static int mc13xxx_probe(struct spi_device *spi) if (ret) goto err_mask; - ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread, + ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); if (ret) { err_mask: err_revision: mc13xxx_unlock(mc13xxx); - dev_set_drvdata(&spi->dev, NULL); kfree(mc13xxx); return ret; } + mc13xxx->irq = irq; + mc13xxx_unlock(mc13xxx); if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata) @@ -838,42 +716,19 @@ err_revision: return 0; } +EXPORT_SYMBOL_GPL(mc13xxx_common_init); -static int __devexit mc13xxx_remove(struct spi_device *spi) +void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx) { - struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); + free_irq(mc13xxx->irq, mc13xxx); - free_irq(mc13xxx->spidev->irq, mc13xxx); + mfd_remove_devices(mc13xxx->dev); - mfd_remove_devices(&spi->dev); + regmap_exit(mc13xxx->regmap); kfree(mc13xxx); - - return 0; -} - -static struct spi_driver mc13xxx_driver = { - .id_table = mc13xxx_device_id, - .driver = { - .name = "mc13xxx", - .owner = THIS_MODULE, - .of_match_table = mc13xxx_dt_ids, - }, - .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); +EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup); MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC"); MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c new file mode 100644 index 00000000000..d22501dad6a --- /dev/null +++ b/drivers/mfd/mc13xxx-i2c.c @@ -0,0 +1,128 @@ +/* + * Copyright 2009-2010 Creative Product Design + * Marc Reilly marc@cpdesign.com.au + * + * 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/mfd/core.h> +#include <linux/mfd/mc13xxx.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/i2c.h> +#include <linux/err.h> + +#include "mc13xxx.h" + +static const struct i2c_device_id mc13xxx_i2c_device_id[] = { + { + .name = "mc13892", + .driver_data = MC13XXX_ID_MC13892, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id); + +static const struct of_device_id mc13xxx_dt_ids[] = { + { + .compatible = "fsl,mc13892", + .data = (void *) &mc13xxx_i2c_device_id[0], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); + +static struct regmap_config mc13xxx_regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 24, + + .max_register = MC13XXX_NUMREGS, + + .cache_type = REGCACHE_NONE, +}; + +static int mc13xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct of_device_id *of_id; + struct i2c_driver *idrv = to_i2c_driver(client->dev.driver); + struct mc13xxx *mc13xxx; + struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev); + int ret; + + of_id = of_match_device(mc13xxx_dt_ids, &client->dev); + if (of_id) + idrv->id_table = (const struct i2c_device_id*) of_id->data; + + mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); + if (!mc13xxx) + return -ENOMEM; + + dev_set_drvdata(&client->dev, mc13xxx); + + mc13xxx->dev = &client->dev; + mutex_init(&mc13xxx->lock); + + mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config); + if (IS_ERR(mc13xxx->regmap)) { + ret = PTR_ERR(mc13xxx->regmap); + dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", + ret); + dev_set_drvdata(&client->dev, NULL); + kfree(mc13xxx); + return ret; + } + + ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); + + if (ret == 0 && (id->driver_data != mc13xxx->ictype)) + dev_warn(mc13xxx->dev, + "device id doesn't match auto detection!\n"); + + return ret; +} + +static int __devexit mc13xxx_i2c_remove(struct i2c_client *client) +{ + struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev); + + mc13xxx_common_cleanup(mc13xxx); + + return 0; +} + +static struct i2c_driver mc13xxx_i2c_driver = { + .id_table = mc13xxx_i2c_device_id, + .driver = { + .owner = THIS_MODULE, + .name = "mc13xxx", + .of_match_table = mc13xxx_dt_ids, + }, + .probe = mc13xxx_i2c_probe, + .remove = __devexit_p(mc13xxx_i2c_remove), +}; + +static int __init mc13xxx_i2c_init(void) +{ + return i2c_add_driver(&mc13xxx_i2c_driver); +} +subsys_initcall(mc13xxx_i2c_init); + +static void __exit mc13xxx_i2c_exit(void) +{ + i2c_del_driver(&mc13xxx_i2c_driver); +} +module_exit(mc13xxx_i2c_exit); + +MODULE_DESCRIPTION("i2c driver for Freescale MC13XXX PMIC"); +MODULE_AUTHOR("Marc Reilly <marc@cpdesign.com.au"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c new file mode 100644 index 00000000000..3fcdab3eb8e --- /dev/null +++ b/drivers/mfd/mc13xxx-spi.c @@ -0,0 +1,140 @@ +/* + * 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/mfd/core.h> +#include <linux/mfd/mc13xxx.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/err.h> +#include <linux/spi/spi.h> + +#include "mc13xxx.h" + +static const struct spi_device_id mc13xxx_device_id[] = { + { + .name = "mc13783", + .driver_data = MC13XXX_ID_MC13783, + }, { + .name = "mc13892", + .driver_data = MC13XXX_ID_MC13892, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); + +static const struct of_device_id mc13xxx_dt_ids[] = { + { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, }, + { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); + +static struct regmap_config mc13xxx_regmap_spi_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 24, + + .max_register = MC13XXX_NUMREGS, + + .cache_type = REGCACHE_NONE, +}; + +static int mc13xxx_spi_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id; + struct spi_driver *sdrv = to_spi_driver(spi->dev.driver); + struct mc13xxx *mc13xxx; + struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev); + int ret; + + of_id = of_match_device(mc13xxx_dt_ids, &spi->dev); + if (of_id) + sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data]; + + 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; + + mc13xxx->dev = &spi->dev; + mutex_init(&mc13xxx->lock); + + mc13xxx->regmap = regmap_init_spi(spi, &mc13xxx_regmap_spi_config); + if (IS_ERR(mc13xxx->regmap)) { + ret = PTR_ERR(mc13xxx->regmap); + dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", + ret); + dev_set_drvdata(&spi->dev, NULL); + kfree(mc13xxx); + return ret; + } + + ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq); + + if (ret) { + dev_set_drvdata(&spi->dev, NULL); + } else { + const struct spi_device_id *devid = + spi_get_device_id(spi); + if (!devid || devid->driver_data != mc13xxx->ictype) + dev_warn(mc13xxx->dev, + "device id doesn't match auto detection!\n"); + } + + return ret; +} + +static int __devexit mc13xxx_spi_remove(struct spi_device *spi) +{ + struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); + + mc13xxx_common_cleanup(mc13xxx); + + return 0; +} + +static struct spi_driver mc13xxx_spi_driver = { + .id_table = mc13xxx_device_id, + .driver = { + .name = "mc13xxx", + .owner = THIS_MODULE, + .of_match_table = mc13xxx_dt_ids, + }, + .probe = mc13xxx_spi_probe, + .remove = __devexit_p(mc13xxx_spi_remove), +}; + +static int __init mc13xxx_init(void) +{ + return spi_register_driver(&mc13xxx_spi_driver); +} +subsys_initcall(mc13xxx_init); + +static void __exit mc13xxx_exit(void) +{ + spi_unregister_driver(&mc13xxx_spi_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/mc13xxx.h b/drivers/mfd/mc13xxx.h new file mode 100644 index 00000000000..bbba06feea0 --- /dev/null +++ b/drivers/mfd/mc13xxx.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012 Creative Product Design + * Marc Reilly <marc@cpdesign.com.au> + * + * 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. + */ +#ifndef __DRIVERS_MFD_MC13XXX_H +#define __DRIVERS_MFD_MC13XXX_H + +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/mfd/mc13xxx.h> + +enum mc13xxx_id { + MC13XXX_ID_MC13783, + MC13XXX_ID_MC13892, + MC13XXX_ID_INVALID, +}; + +#define MC13XXX_NUMREGS 0x3f + +struct mc13xxx { + struct regmap *regmap; + + struct device *dev; + enum mc13xxx_id ictype; + + struct mutex lock; + int irq; + int flags; + + irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; + void *irqdata[MC13XXX_NUM_IRQ]; + + int adcflags; +}; + +int mc13xxx_common_init(struct mc13xxx *mc13xxx, + struct mc13xxx_platform_data *pdata, int irq); + +void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx); + +#endif /* __DRIVERS_MFD_MC13XXX_H */ diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 189c2f07b83..29c122bf28e 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -204,7 +204,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client, return -ENOENT; } - pcf = kzalloc(sizeof(*pcf), GFP_KERNEL); + pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL); if (!pcf) return -ENOMEM; @@ -212,12 +212,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client, mutex_init(&pcf->lock); - pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config); + pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config); if (IS_ERR(pcf->regmap)) { ret = PTR_ERR(pcf->regmap); - dev_err(pcf->dev, "Failed to allocate register map: %d\n", - ret); - goto err_free; + dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret); + return ret; } i2c_set_clientdata(client, pcf); @@ -228,7 +227,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client, if (version < 0 || variant < 0) { dev_err(pcf->dev, "Unable to probe pcf50633\n"); ret = -ENODEV; - goto err_regmap; + return ret; } dev_info(pcf->dev, "Probed device version %d variant %d\n", @@ -237,16 +236,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client, pcf50633_irq_init(pcf, client->irq); /* Create sub devices */ - pcf50633_client_dev_register(pcf, "pcf50633-input", - &pcf->input_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-rtc", - &pcf->rtc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-mbc", - &pcf->mbc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-adc", - &pcf->adc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-backlight", - &pcf->bl_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev); for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { @@ -274,13 +268,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client, pdata->probe_done(pcf); return 0; - -err_regmap: - regmap_exit(pcf->regmap); -err_free: - kfree(pcf); - - return ret; } static int __devexit pcf50633_remove(struct i2c_client *client) @@ -300,9 +287,6 @@ static int __devexit pcf50633_remove(struct i2c_client *client) for (i = 0; i < PCF50633_NUM_REGULATORS; i++) platform_device_unregister(pcf->regulator_pdev[i]); - regmap_exit(pcf->regmap); - kfree(pcf); - return 0; } diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index 44afae0a69c..cdc1df7fa0e 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -75,6 +75,7 @@ static struct deepsleep_control_data deepsleep_data[] = { (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) static struct mfd_cell rc5t583_subdevs[] = { + {.name = "rc5t583-gpio",}, {.name = "rc5t583-regulator",}, {.name = "rc5t583-rtc", }, {.name = "rc5t583-key", } @@ -267,7 +268,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, rc5t583->dev = &i2c->dev; i2c_set_clientdata(i2c, rc5t583); - rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + rc5t583->regmap = devm_regmap_init_i2c(i2c, &rc5t583_regmap_config); if (IS_ERR(rc5t583->regmap)) { ret = PTR_ERR(rc5t583->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); @@ -276,7 +277,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, ret = rc5t583_clear_ext_power_req(rc5t583, pdata); if (ret < 0) - goto err_irq_init; + return ret; if (i2c->irq) { ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); @@ -299,8 +300,6 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, err_add_devs: if (irq_init_success) rc5t583_irq_exit(rc5t583); -err_irq_init: - regmap_exit(rc5t583->regmap); return ret; } @@ -310,7 +309,6 @@ static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(rc5t583->dev); rc5t583_irq_exit(rc5t583); - regmap_exit(rc5t583->regmap); return 0; } diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 809bd4a6108..685d61e431a 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -108,18 +108,7 @@ static struct pci_driver rdc321x_sb_driver = { .remove = __devexit_p(rdc321x_sb_remove), }; -static int __init rdc321x_sb_init(void) -{ - return pci_register_driver(&rdc321x_sb_driver); -} - -static void __exit rdc321x_sb_exit(void) -{ - pci_unregister_driver(&rdc321x_sb_driver); -} - -module_init(rdc321x_sb_init); -module_exit(rdc321x_sb_exit); +module_pci_driver(rdc321x_sb_driver); MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 48949d998d1..dd170307e60 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -114,12 +114,12 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, s5m87xx->wakeup = pdata->wakeup; } - s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config); + s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config); if (IS_ERR(s5m87xx->regmap)) { ret = PTR_ERR(s5m87xx->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); @@ -159,7 +159,6 @@ err: mfd_remove_devices(s5m87xx->dev); s5m_irq_exit(s5m87xx); i2c_unregister_device(s5m87xx->rtc); - regmap_exit(s5m87xx->regmap); return ret; } @@ -170,7 +169,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(s5m87xx->dev); s5m_irq_exit(s5m87xx); i2c_unregister_device(s5m87xx->rtc); - regmap_exit(s5m87xx->regmap); return 0; } diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c new file mode 100644 index 00000000000..d31fed07aef --- /dev/null +++ b/drivers/mfd/sta2x11-mfd.c @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2009-2011 Wind River Systems, Inc. + * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, 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/spinlock.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/sta2x11-mfd.h> + +#include <asm/sta2x11.h> + +/* This describes STA2X11 MFD chip for us, we may have several */ +struct sta2x11_mfd { + struct sta2x11_instance *instance; + spinlock_t lock; + struct list_head list; + void __iomem *sctl_regs; + void __iomem *apbreg_regs; +}; + +static LIST_HEAD(sta2x11_mfd_list); + +/* Three functions to act on the list */ +static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance; + struct sta2x11_mfd *mfd; + + if (!pdev && !list_empty(&sta2x11_mfd_list)) { + pr_warning("%s: Unspecified device, " + "using first instance\n", __func__); + return list_entry(sta2x11_mfd_list.next, + struct sta2x11_mfd, list); + } + + instance = sta2x11_get_instance(pdev); + if (!instance) + return NULL; + list_for_each_entry(mfd, &sta2x11_mfd_list, list) { + if (mfd->instance == instance) + return mfd; + } + return NULL; +} + +static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + struct sta2x11_instance *instance; + + if (mfd) + return -EBUSY; + instance = sta2x11_get_instance(pdev); + if (!instance) + return -EINVAL; + mfd = kzalloc(sizeof(*mfd), flags); + if (!mfd) + return -ENOMEM; + INIT_LIST_HEAD(&mfd->list); + spin_lock_init(&mfd->lock); + mfd->instance = instance; + list_add(&mfd->list, &sta2x11_mfd_list); + return 0; +} + +static int __devexit mfd_remove(struct pci_dev *pdev) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + + if (!mfd) + return -ENODEV; + list_del(&mfd->list); + kfree(mfd); + return 0; +} + +/* These two functions are exported and are not expected to fail */ +u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + u32 r; + unsigned long flags; + + if (!mfd) { + dev_warn(&pdev->dev, ": can't access sctl regs\n"); + return 0; + } + if (!mfd->sctl_regs) { + dev_warn(&pdev->dev, ": system ctl not initialized\n"); + return 0; + } + spin_lock_irqsave(&mfd->lock, flags); + r = readl(mfd->sctl_regs + reg); + r &= ~mask; + r |= val; + if (mask) + writel(r, mfd->sctl_regs + reg); + spin_unlock_irqrestore(&mfd->lock, flags); + return r; +} +EXPORT_SYMBOL(sta2x11_sctl_mask); + +u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + u32 r; + unsigned long flags; + + if (!mfd) { + dev_warn(&pdev->dev, ": can't access apb regs\n"); + return 0; + } + if (!mfd->apbreg_regs) { + dev_warn(&pdev->dev, ": apb bridge not initialized\n"); + return 0; + } + spin_lock_irqsave(&mfd->lock, flags); + r = readl(mfd->apbreg_regs + reg); + r &= ~mask; + r |= val; + if (mask) + writel(r, mfd->apbreg_regs + reg); + spin_unlock_irqrestore(&mfd->lock, flags); + return r; +} +EXPORT_SYMBOL(sta2x11_apbreg_mask); + +/* Two debugfs files, for our registers (FIXME: one instance only) */ +#define REG(regname) {.name = #regname, .offset = SCTL_ ## regname} +static struct debugfs_reg32 sta2x11_sctl_regs[] = { + REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL), + REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0), + REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1), + REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3), + REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1), + REG(SCCLKSTAT2), REG(SCRSTSTA), +}; +#undef REG + +static struct debugfs_regset32 sctl_regset = { + .regs = sta2x11_sctl_regs, + .nregs = ARRAY_SIZE(sta2x11_sctl_regs), +}; + +#define REG(regname) {.name = #regname, .offset = regname} +static struct debugfs_reg32 sta2x11_apbreg_regs[] = { + REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC), + REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG), +}; +#undef REG + +static struct debugfs_regset32 apbreg_regset = { + .regs = sta2x11_apbreg_regs, + .nregs = ARRAY_SIZE(sta2x11_apbreg_regs), +}; + +static struct dentry *sta2x11_sctl_debugfs; +static struct dentry *sta2x11_apbreg_debugfs; + +/* Probe for the two platform devices */ +static int sta2x11_sctl_probe(struct platform_device *dev) +{ + struct pci_dev **pdev; + struct sta2x11_mfd *mfd; + struct resource *res; + + pdev = dev->dev.platform_data; + mfd = sta2x11_mfd_find(*pdev); + if (!mfd) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + if (!request_mem_region(res->start, resource_size(res), + "sta2x11-sctl")) + return -EBUSY; + + mfd->sctl_regs = ioremap(res->start, resource_size(res)); + if (!mfd->sctl_regs) { + release_mem_region(res->start, resource_size(res)); + return -ENOMEM; + } + sctl_regset.base = mfd->sctl_regs; + sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl", + S_IFREG | S_IRUGO, + NULL, &sctl_regset); + return 0; +} + +static int sta2x11_apbreg_probe(struct platform_device *dev) +{ + struct pci_dev **pdev; + struct sta2x11_mfd *mfd; + struct resource *res; + + pdev = dev->dev.platform_data; + dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev); + dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev); + + mfd = sta2x11_mfd_find(*pdev); + if (!mfd) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + if (!request_mem_region(res->start, resource_size(res), + "sta2x11-apbreg")) + return -EBUSY; + + mfd->apbreg_regs = ioremap(res->start, resource_size(res)); + if (!mfd->apbreg_regs) { + release_mem_region(res->start, resource_size(res)); + return -ENOMEM; + } + dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs); + + apbreg_regset.base = mfd->apbreg_regs; + sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg", + S_IFREG | S_IRUGO, + NULL, &apbreg_regset); + return 0; +} + +/* The two platform drivers */ +static struct platform_driver sta2x11_sctl_platform_driver = { + .driver = { + .name = "sta2x11-sctl", + .owner = THIS_MODULE, + }, + .probe = sta2x11_sctl_probe, +}; + +static int __init sta2x11_sctl_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_sctl_platform_driver); +} + +static struct platform_driver sta2x11_platform_driver = { + .driver = { + .name = "sta2x11-apbreg", + .owner = THIS_MODULE, + }, + .probe = sta2x11_apbreg_probe, +}; + +static int __init sta2x11_apbreg_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_platform_driver); +} + +/* + * What follows is the PCI device that hosts the above two pdevs. + * Each logic block is 4kB and they are all consecutive: we use this info. + */ + +/* Bar 0 */ +enum bar0_cells { + STA2X11_GPIO_0 = 0, + STA2X11_GPIO_1, + STA2X11_GPIO_2, + STA2X11_GPIO_3, + STA2X11_SCTL, + STA2X11_SCR, + STA2X11_TIME, +}; +/* Bar 1 */ +enum bar1_cells { + STA2X11_APBREG = 0, +}; +#define CELL_4K(_name, _cell) { \ + .name = _name, \ + .start = _cell * 4096, .end = _cell * 4096 + 4095, \ + .flags = IORESOURCE_MEM, \ + } + +static const __devinitconst struct resource gpio_resources[] = { + { + .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */ + .start = 0, + .end = (4 * 4096) - 1, + .flags = IORESOURCE_MEM, + } +}; +static const __devinitconst struct resource sctl_resources[] = { + CELL_4K("sta2x11-sctl", STA2X11_SCTL), +}; +static const __devinitconst struct resource scr_resources[] = { + CELL_4K("sta2x11-scr", STA2X11_SCR), +}; +static const __devinitconst struct resource time_resources[] = { + CELL_4K("sta2x11-time", STA2X11_TIME), +}; + +static const __devinitconst struct resource apbreg_resources[] = { + CELL_4K("sta2x11-apbreg", STA2X11_APBREG), +}; + +#define DEV(_name, _r) \ + { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } + +static __devinitdata struct mfd_cell sta2x11_mfd_bar0[] = { + DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */ + DEV("sta2x11-sctl", sctl_resources), + DEV("sta2x11-scr", scr_resources), + DEV("sta2x11-time", time_resources), +}; + +static __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = { + DEV("sta2x11-apbreg", apbreg_resources), +}; + +static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int sta2x11_mfd_resume(struct pci_dev *pdev) +{ + int err; + + pci_set_power_state(pdev, 0); + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + + return 0; +} + +static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + int err, i; + struct sta2x11_gpio_pdata *gpio_data; + + dev_info(&pdev->dev, "%s\n", __func__); + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Can't enable device.\n"); + return err; + } + + err = pci_enable_msi(pdev); + if (err) + dev_info(&pdev->dev, "Enable msi failed\n"); + + /* Read gpio config data as pci device's platform data */ + gpio_data = dev_get_platdata(&pdev->dev); + if (!gpio_data) + dev_warn(&pdev->dev, "no gpio configuration\n"); + + dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__, + gpio_data, &gpio_data); + dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__, + pdev, &pdev); + + /* platform data is the pci device for all of them */ + for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) { + sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev); + sta2x11_mfd_bar0[i].platform_data = &pdev; + } + sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev); + sta2x11_mfd_bar1[0].platform_data = &pdev; + + /* Record this pdev before mfd_add_devices: their probe looks for it */ + sta2x11_mfd_add(pdev, GFP_ATOMIC); + + + err = mfd_add_devices(&pdev->dev, -1, + sta2x11_mfd_bar0, + ARRAY_SIZE(sta2x11_mfd_bar0), + &pdev->resource[0], + 0); + if (err) { + dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err); + goto err_disable; + } + + err = mfd_add_devices(&pdev->dev, -1, + sta2x11_mfd_bar1, + ARRAY_SIZE(sta2x11_mfd_bar1), + &pdev->resource[1], + 0); + if (err) { + dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err); + goto err_disable; + } + + return 0; + +err_disable: + mfd_remove_devices(&pdev->dev); + pci_disable_device(pdev); + pci_disable_msi(pdev); + return err; +} + +static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = { + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)}, + {0,}, +}; + +static struct pci_driver sta2x11_mfd_driver = { + .name = "sta2x11-mfd", + .id_table = sta2x11_mfd_tbl, + .probe = sta2x11_mfd_probe, + .suspend = sta2x11_mfd_suspend, + .resume = sta2x11_mfd_resume, +}; + +static int __init sta2x11_mfd_init(void) +{ + pr_info("%s\n", __func__); + return pci_register_driver(&sta2x11_mfd_driver); +} + +/* + * All of this must be ready before "normal" devices like MMCI appear. + * But MFD (the pci device) can't be too early. The following choice + * prepares platform drivers very early and probe the PCI device later, + * but before other PCI devices. + */ +subsys_initcall(sta2x11_apbreg_init); +subsys_initcall(sta2x11_sctl_init); +rootfs_initcall(sta2x11_mfd_init); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wind River"); +MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG"); +MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl); diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c index b58c43c7ea9..afd459013ec 100644 --- a/drivers/mfd/stmpe-spi.c +++ b/drivers/mfd/stmpe-spi.c @@ -122,7 +122,6 @@ MODULE_DEVICE_TABLE(spi, stmpe_id); static struct spi_driver stmpe_spi_driver = { .driver = { .name = "stmpe-spi", - .bus = &spi_bus_type, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &stmpe_dev_pm_ops, diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 47f802bf184..396b9d1b6bd 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -283,27 +283,24 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, } } - tps65090->rmap = regmap_init_i2c(tps65090->client, - &tps65090_regmap_config); + tps65090->rmap = devm_regmap_init_i2c(tps65090->client, + &tps65090_regmap_config); if (IS_ERR(tps65090->rmap)) { - dev_err(&client->dev, "regmap_init failed with err: %ld\n", - PTR_ERR(tps65090->rmap)); + ret = PTR_ERR(tps65090->rmap); + dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); goto err_irq_exit; - }; + } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, ARRAY_SIZE(tps65090s), NULL, 0); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); - goto err_regmap_exit; + goto err_irq_exit; } return 0; -err_regmap_exit: - regmap_exit(tps65090->rmap); - err_irq_exit: if (client->irq) free_irq(client->irq, tps65090); @@ -316,29 +313,34 @@ static int __devexit tps65090_i2c_remove(struct i2c_client *client) struct tps65090 *tps65090 = i2c_get_clientdata(client); mfd_remove_devices(tps65090->dev); - regmap_exit(tps65090->rmap); if (client->irq) free_irq(client->irq, tps65090); return 0; } -#ifdef CONFIG_PM -static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tps65090_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); if (client->irq) disable_irq(client->irq); return 0; } -static int tps65090_i2c_resume(struct i2c_client *client) +static int tps65090_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); if (client->irq) enable_irq(client->irq); return 0; } #endif +static const struct dev_pm_ops tps65090_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume) +}; + static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, { }, @@ -349,13 +351,10 @@ static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", .owner = THIS_MODULE, + .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, .remove = __devexit_p(tps65090_i2c_remove), -#ifdef CONFIG_PM - .suspend = tps65090_i2c_suspend, - .resume = tps65090_i2c_resume, -#endif .id_table = tps65090_id_table, }; diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index f7d854e4cc6..db194e433c0 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -96,7 +96,7 @@ EXPORT_SYMBOL_GPL(tps65217_reg_write); * @val: Value to write. * @level: Password protected level */ -int tps65217_update_bits(struct tps65217 *tps, unsigned int reg, +static int tps65217_update_bits(struct tps65217 *tps, unsigned int reg, unsigned int mask, unsigned int val, unsigned int level) { int ret; @@ -150,7 +150,7 @@ static int __devinit tps65217_probe(struct i2c_client *client, return -ENOMEM; tps->pdata = pdata; - tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config); + tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config); if (IS_ERR(tps->regmap)) { ret = PTR_ERR(tps->regmap); dev_err(tps->dev, "Failed to allocate register map: %d\n", @@ -163,9 +163,9 @@ static int __devinit tps65217_probe(struct i2c_client *client, ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version); if (ret < 0) { - dev_err(tps->dev, "Failed to read revision" - " register: %d\n", ret); - goto err_regmap; + dev_err(tps->dev, "Failed to read revision register: %d\n", + ret); + return ret; } dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", @@ -190,11 +190,6 @@ static int __devinit tps65217_probe(struct i2c_client *client, } return 0; - -err_regmap: - regmap_exit(tps->regmap); - - return ret; } static int __devexit tps65217_remove(struct i2c_client *client) @@ -205,8 +200,6 @@ static int __devexit tps65217_remove(struct i2c_client *client) for (i = 0; i < TPS65217_NUM_REGULATOR; i++) platform_device_unregister(tps->regulator_pdev[i]); - regmap_exit(tps->regmap); - return 0; } diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index c9ed5c00a62..09aab3e4776 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -20,15 +20,10 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/gpio.h> #include <linux/mfd/tps65910.h> -static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, - int irq) -{ - return (irq - tps65910->irq_base); -} - /* * This is a threaded IRQ handler so can access I2C/SPI. Since all * interrupts are clear on read the IRQ line will be reasserted and @@ -41,28 +36,28 @@ static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, static irqreturn_t tps65910_irq(int irq, void *irq_data) { struct tps65910 *tps65910 = irq_data; + unsigned int reg; u32 irq_sts; u32 irq_mask; - u8 reg; int i; - tps65910->read(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS, ®); irq_sts = reg; - tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®); irq_sts |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®); irq_sts |= reg << 16; } - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); irq_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); irq_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); irq_mask |= reg << 16; } @@ -76,19 +71,19 @@ static irqreturn_t tps65910_irq(int irq, void *irq_data) if (!(irq_sts & (1 << i))) continue; - handle_nested_irq(tps65910->irq_base + i); + handle_nested_irq(irq_find_mapping(tps65910->domain, i)); } /* Write the STS register back to clear IRQs we handled */ reg = irq_sts & 0xFF; irq_sts >>= 8; - tps65910->write(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS, reg); reg = irq_sts & 0xFF; - tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = irq_sts >> 8; - tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg); } return IRQ_HANDLED; @@ -105,27 +100,27 @@ static void tps65910_irq_sync_unlock(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); u32 reg_mask; - u8 reg; + unsigned int reg; - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); reg_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); reg_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); reg_mask |= reg << 16; } if (tps65910->irq_mask != reg_mask) { reg = tps65910->irq_mask & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg); reg = tps65910->irq_mask >> 8 & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = tps65910->irq_mask >> 16; - tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg); } } mutex_unlock(&tps65910->irq_lock); @@ -135,14 +130,14 @@ static void tps65910_irq_enable(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq)); + tps65910->irq_mask &= ~(1 << data->hwirq); } static void tps65910_irq_disable(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq)); + tps65910->irq_mask |= (1 << data->hwirq); } #ifdef CONFIG_PM_SLEEP @@ -164,10 +159,35 @@ static struct irq_chip tps65910_irq_chip = { .irq_set_wake = tps65910_irq_set_wake, }; +static int tps65910_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct tps65910 *tps65910 = h->host_data; + + irq_set_chip_data(virq, tps65910); + irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops tps65910_domain_ops = { + .map = tps65910_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + int tps65910_irq_init(struct tps65910 *tps65910, int irq, struct tps65910_platform_data *pdata) { - int ret, cur_irq; + int ret; int flags = IRQF_ONESHOT; if (!irq) { @@ -175,17 +195,11 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq, return -EINVAL; } - if (!pdata || !pdata->irq_base) { - dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n"); + if (!pdata) { + dev_warn(tps65910->dev, "No interrupt support, no pdata\n"); return -EINVAL; } - tps65910->irq_mask = 0xFFFFFF; - - mutex_init(&tps65910->irq_lock); - tps65910->chip_irq = irq; - tps65910->irq_base = pdata->irq_base; - switch (tps65910_chip_id(tps65910)) { case TPS65910: tps65910->irq_num = TPS65910_NUM_IRQ; @@ -195,22 +209,36 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq, break; } - /* Register with genirq */ - for (cur_irq = tps65910->irq_base; - cur_irq < tps65910->irq_num + tps65910->irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, tps65910); - irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + if (pdata->irq_base > 0) { + pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0, + tps65910->irq_num, -1); + if (pdata->irq_base < 0) { + dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n", + pdata->irq_base); + return pdata->irq_base; + } + } + + tps65910->irq_mask = 0xFFFFFF; + + mutex_init(&tps65910->irq_lock); + tps65910->chip_irq = irq; + tps65910->irq_base = pdata->irq_base; + + if (pdata->irq_base > 0) + tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node, + tps65910->irq_num, + pdata->irq_base, + 0, + &tps65910_domain_ops, tps65910); + else + tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node, + tps65910->irq_num, + &tps65910_domain_ops, tps65910); + + if (!tps65910->domain) { + dev_err(tps65910->dev, "Failed to create IRQ domain\n"); + return -ENOMEM; } ret = request_threaded_irq(irq, NULL, tps65910_irq, flags, diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index bf2b25ebf2c..be9e07b7732 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -19,13 +19,16 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/mfd/core.h> #include <linux/regmap.h> #include <linux/mfd/tps65910.h> +#include <linux/of_device.h> static struct mfd_cell tps65910s[] = { { + .name = "tps65910-gpio", + }, + { .name = "tps65910-pmic", }, { @@ -37,30 +40,6 @@ static struct mfd_cell tps65910s[] = { }; -static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg, - int bytes, void *dest) -{ - return regmap_bulk_read(tps65910->regmap, reg, dest, bytes); -} - -static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, - int bytes, void *src) -{ - return regmap_bulk_write(tps65910->regmap, reg, src, bytes); -} - -int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, mask); -} -EXPORT_SYMBOL_GPL(tps65910_set_bits); - -int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, 0); -} -EXPORT_SYMBOL_GPL(tps65910_clear_bits); - static bool is_volatile_reg(struct device *dev, unsigned int reg) { struct tps65910 *tps65910 = dev_get_drvdata(dev); @@ -85,80 +64,197 @@ static const struct regmap_config tps65910_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_reg = is_volatile_reg, - .max_register = TPS65910_MAX_REGISTER, - .num_reg_defaults_raw = TPS65910_MAX_REGISTER, + .max_register = TPS65910_MAX_REGISTER - 1, .cache_type = REGCACHE_RBTREE, }; -static int tps65910_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + struct device *dev = NULL; + int ret = 0; + + dev = tps65910->dev; + + if (!pmic_pdata->en_dev_slp) + return 0; + + /* enabling SLEEP device state */ + ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + if (ret < 0) { + dev_err(dev, "set dev_slp failed: %d\n", ret); + goto err_sleep_init; + } + + /* Return if there is no sleep keepon data. */ + if (!pmic_pdata->slp_keepon) + return 0; + + if (pmic_pdata->slp_keepon->therm_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set therm_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->clkout32k_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set clkout32k_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->i2chs_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set i2chs_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + return 0; + +disable_dev_slp: + tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + +err_sleep_init: + return ret; +} + +#ifdef CONFIG_OF +static struct of_device_id tps65910_of_match[] = { + { .compatible = "ti,tps65910", .data = (void *)TPS65910}, + { .compatible = "ti,tps65911", .data = (void *)TPS65911}, + { }, +}; +MODULE_DEVICE_TABLE(of, tps65910_of_match); + +static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + struct device_node *np = client->dev.of_node; + struct tps65910_board *board_info; + unsigned int prop; + const struct of_device_id *match; + int ret = 0; + + match = of_match_device(tps65910_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + *chip_id = (int)match->data; + + board_info = devm_kzalloc(&client->dev, sizeof(*board_info), + GFP_KERNEL); + if (!board_info) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return NULL; + } + + ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop); + if (!ret) + board_info->vmbch_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH-Threshold not specified"); + + ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop); + if (!ret) + board_info->vmbch2_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH2-Threshold not specified"); + + board_info->irq = client->irq; + board_info->irq_base = -1; + + return board_info; +} +#else +static inline +struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + return NULL; +} +#endif + +static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct tps65910 *tps65910; struct tps65910_board *pmic_plat_data; + struct tps65910_board *of_pmic_plat_data = NULL; struct tps65910_platform_data *init_data; int ret = 0; + int chip_id = id->driver_data; pmic_plat_data = dev_get_platdata(&i2c->dev); + + if (!pmic_plat_data && i2c->dev.of_node) { + pmic_plat_data = tps65910_parse_dt(i2c, &chip_id); + of_pmic_plat_data = pmic_plat_data; + } + if (!pmic_plat_data) return -EINVAL; - init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL); + init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL); if (init_data == NULL) return -ENOMEM; - tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL); - if (tps65910 == NULL) { - kfree(init_data); + tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL); + if (tps65910 == NULL) return -ENOMEM; - } + tps65910->of_plat_data = of_pmic_plat_data; i2c_set_clientdata(i2c, tps65910); tps65910->dev = &i2c->dev; tps65910->i2c_client = i2c; - tps65910->id = id->driver_data; - tps65910->read = tps65910_i2c_read; - tps65910->write = tps65910_i2c_write; + tps65910->id = chip_id; mutex_init(&tps65910->io_mutex); - tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config); + tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { ret = PTR_ERR(tps65910->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); - goto regmap_err; + return ret; } ret = mfd_add_devices(tps65910->dev, -1, tps65910s, ARRAY_SIZE(tps65910s), NULL, 0); - if (ret < 0) - goto err; + if (ret < 0) { + dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); + return ret; + } init_data->irq = pmic_plat_data->irq; init_data->irq_base = pmic_plat_data->irq_base; - tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base); - tps65910_irq_init(tps65910, init_data->irq, init_data); - kfree(init_data); - return ret; + tps65910_sleepinit(tps65910, pmic_plat_data); -err: - regmap_exit(tps65910->regmap); -regmap_err: - kfree(tps65910); - kfree(init_data); return ret; } -static int tps65910_i2c_remove(struct i2c_client *i2c) +static __devexit int tps65910_i2c_remove(struct i2c_client *i2c) { struct tps65910 *tps65910 = i2c_get_clientdata(i2c); tps65910_irq_exit(tps65910); mfd_remove_devices(tps65910->dev); - regmap_exit(tps65910->regmap); - kfree(tps65910); return 0; } @@ -175,9 +271,10 @@ static struct i2c_driver tps65910_i2c_driver = { .driver = { .name = "tps65910", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65910_of_match), }, .probe = tps65910_i2c_probe, - .remove = tps65910_i2c_remove, + .remove = __devexit_p(tps65910_i2c_remove), .id_table = tps65910_i2c_id, }; diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 5d656e81435..ad733d76207 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -757,6 +757,7 @@ int twl4030_init_irq(struct device *dev, int irq_num) dev_err(dev, "could not claim irq%d: %d\n", irq_num, status); goto fail_rqirq; } + enable_irq_wake(irq_num); return irq_base; fail_rqirq: diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 2d6bedadca0..4ded9e7aa24 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -27,7 +27,12 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/err.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> #include <linux/gpio.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -35,8 +40,24 @@ #include <linux/err.h> #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h> +#include <linux/regulator/consumer.h> #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) +#define TWL6040_NUM_SUPPLIES (2) + +static bool twl6040_has_vibra(struct twl6040_platform_data *pdata, + struct device_node *node) +{ + if (pdata && pdata->vibra) + return true; + +#ifdef CONFIG_OF + if (of_find_node_by_name(node, "vibra")) + return true; +#endif + + return false; +} int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) { @@ -502,17 +523,18 @@ static int __devinit twl6040_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct twl6040_platform_data *pdata = client->dev.platform_data; + struct device_node *node = client->dev.of_node; struct twl6040 *twl6040; struct mfd_cell *cell = NULL; - int ret, children = 0; + int irq, ret, children = 0; - if (!pdata) { + if (!pdata && !node) { dev_err(&client->dev, "Platform data is missing\n"); return -EINVAL; } /* In order to operate correctly we need valid interrupt config */ - if (!client->irq || !pdata->irq_base) { + if (!client->irq) { dev_err(&client->dev, "Invalid IRQ configuration\n"); return -EINVAL; } @@ -524,7 +546,7 @@ static int __devinit twl6040_probe(struct i2c_client *client, goto err; } - twl6040->regmap = regmap_init_i2c(client, &twl6040_regmap_config); + twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config); if (IS_ERR(twl6040->regmap)) { ret = PTR_ERR(twl6040->regmap); goto err; @@ -532,9 +554,23 @@ static int __devinit twl6040_probe(struct i2c_client *client, i2c_set_clientdata(client, twl6040); + twl6040->supplies[0].supply = "vio"; + twl6040->supplies[1].supply = "v2v1"; + ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, + twl6040->supplies); + if (ret != 0) { + dev_err(&client->dev, "Failed to get supplies: %d\n", ret); + goto regulator_get_err; + } + + ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies); + if (ret != 0) { + dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); + goto power_err; + } + twl6040->dev = &client->dev; twl6040->irq = client->irq; - twl6040->irq_base = pdata->irq_base; mutex_init(&twl6040->mutex); mutex_init(&twl6040->io_mutex); @@ -543,22 +579,26 @@ static int __devinit twl6040_probe(struct i2c_client *client, twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) - twl6040->audpwron = pdata->audpwron_gpio; - else + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) { + if (pdata) + twl6040->audpwron = pdata->audpwron_gpio; + else + twl6040->audpwron = of_get_named_gpio(node, + "ti,audpwron-gpio", 0); + } else twl6040->audpwron = -EINVAL; if (gpio_is_valid(twl6040->audpwron)) { ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW, "audpwron"); if (ret) - goto gpio1_err; + goto gpio_err; } /* codec interrupt */ ret = twl6040_irq_init(twl6040); if (ret) - goto gpio2_err; + goto irq_init_err; ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, NULL, twl6040_naudint_handler, 0, @@ -572,22 +612,27 @@ static int __devinit twl6040_probe(struct i2c_client *client, /* dual-access registers controlled by I2C only */ twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); - if (pdata->codec) { - int irq = twl6040->irq_base + TWL6040_IRQ_PLUG; - - cell = &twl6040->cells[children]; - cell->name = "twl6040-codec"; - twl6040_codec_rsrc[0].start = irq; - twl6040_codec_rsrc[0].end = irq; - cell->resources = twl6040_codec_rsrc; - cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); + /* + * The main functionality of twl6040 to provide audio on OMAP4+ systems. + * We can add the ASoC codec child whenever this driver has been loaded. + * The ASoC codec can work without pdata, pass the platform_data only if + * it has been provided. + */ + irq = twl6040->irq_base + TWL6040_IRQ_PLUG; + cell = &twl6040->cells[children]; + cell->name = "twl6040-codec"; + twl6040_codec_rsrc[0].start = irq; + twl6040_codec_rsrc[0].end = irq; + cell->resources = twl6040_codec_rsrc; + cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); + if (pdata && pdata->codec) { cell->platform_data = pdata->codec; cell->pdata_size = sizeof(*pdata->codec); - children++; } + children++; - if (pdata->vibra) { - int irq = twl6040->irq_base + TWL6040_IRQ_VIB; + if (twl6040_has_vibra(pdata, node)) { + irq = twl6040->irq_base + TWL6040_IRQ_VIB; cell = &twl6040->cells[children]; cell->name = "twl6040-vibra"; @@ -596,21 +641,17 @@ static int __devinit twl6040_probe(struct i2c_client *client, cell->resources = twl6040_vibra_rsrc; cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); - cell->platform_data = pdata->vibra; - cell->pdata_size = sizeof(*pdata->vibra); + if (pdata && pdata->vibra) { + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); + } children++; } - if (children) { - ret = mfd_add_devices(&client->dev, -1, twl6040->cells, - children, NULL, 0); - if (ret) - goto mfd_err; - } else { - dev_err(&client->dev, "No platform data found for children\n"); - ret = -ENODEV; + ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, + NULL, 0); + if (ret) goto mfd_err; - } return 0; @@ -618,12 +659,15 @@ mfd_err: free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); irq_err: twl6040_irq_exit(twl6040); -gpio2_err: +irq_init_err: if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); -gpio1_err: +gpio_err: + regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); +power_err: + regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies); +regulator_get_err: i2c_set_clientdata(client, NULL); - regmap_exit(twl6040->regmap); err: return ret; } @@ -643,7 +687,9 @@ static int __devexit twl6040_remove(struct i2c_client *client) mfd_remove_devices(&client->dev); i2c_set_clientdata(client, NULL); - regmap_exit(twl6040->regmap); + + regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); + regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies); return 0; } diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c index b3f8ddaa28a..4b42543da22 100644 --- a/drivers/mfd/twl6040-irq.c +++ b/drivers/mfd/twl6040-irq.c @@ -23,7 +23,10 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/err.h> #include <linux/irq.h> +#include <linux/of.h> +#include <linux/irqdomain.h> #include <linux/interrupt.h> #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h> @@ -138,7 +141,8 @@ static irqreturn_t twl6040_irq_thread(int irq, void *data) int twl6040_irq_init(struct twl6040 *twl6040) { - int cur_irq, ret; + struct device_node *node = twl6040->dev->of_node; + int i, nr_irqs, irq_base, ret; u8 val; mutex_init(&twl6040->irq_mutex); @@ -148,21 +152,31 @@ int twl6040_irq_init(struct twl6040 *twl6040) twl6040->irq_masks_cache = TWL6040_ALLINT_MSK; twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK); + nr_irqs = ARRAY_SIZE(twl6040_irqs); + + irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (IS_ERR_VALUE(irq_base)) { + dev_err(twl6040->dev, "Fail to allocate IRQ descs\n"); + return irq_base; + } + twl6040->irq_base = irq_base; + + irq_domain_add_legacy(node, ARRAY_SIZE(twl6040_irqs), irq_base, 0, + &irq_domain_simple_ops, NULL); + /* Register them with genirq */ - for (cur_irq = twl6040->irq_base; - cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs); - cur_irq++) { - irq_set_chip_data(cur_irq, twl6040); - irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip, + for (i = irq_base; i < irq_base + nr_irqs; i++) { + irq_set_chip_data(i, twl6040); + irq_set_chip_and_handler(i, &twl6040_irq_chip, handle_level_irq); - irq_set_nested_thread(cur_irq, 1); + irq_set_nested_thread(i, 1); /* ARM needs us to explicitly flag the IRQ as valid * and will set them noprobe when we do so. */ #ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); + set_irq_flags(i, IRQF_VALID); #else - irq_set_noprobe(cur_irq); + irq_set_noprobe(i); #endif } diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c index b73cc15e008..872aff21e4b 100644 --- a/drivers/mfd/vx855.c +++ b/drivers/mfd/vx855.c @@ -131,17 +131,7 @@ static struct pci_driver vx855_pci_driver = { .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_pci_driver(vx855_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c index 87210954a06..6ee3018d865 100644 --- a/drivers/mfd/wm831x-auxadc.c +++ b/drivers/mfd/wm831x-auxadc.c @@ -280,11 +280,11 @@ void wm831x_auxadc_init(struct wm831x *wm831x) mutex_init(&wm831x->auxadc_lock); INIT_LIST_HEAD(&wm831x->auxadc_pending); - if (wm831x->irq && wm831x->irq_base) { + if (wm831x->irq) { wm831x->auxadc_read = wm831x_auxadc_read_irq; - ret = request_threaded_irq(wm831x->irq_base + - WM831X_IRQ_AUXADC_DATA, + ret = request_threaded_irq(wm831x_irq(wm831x, + WM831X_IRQ_AUXADC_DATA), NULL, wm831x_auxadc_irq, 0, "auxadc", wm831x); if (ret < 0) { diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 838056c3493..946698fd2dc 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -614,8 +614,15 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg, } EXPORT_SYMBOL_GPL(wm831x_set_bits); +static struct resource wm831x_io_parent = { + .start = 0, + .end = 0xffffffff, + .flags = IORESOURCE_IO, +}; + static struct resource wm831x_dcdc1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC1_CONTROL_1, .end = WM831X_DC1_DVS_CONTROL, .flags = IORESOURCE_IO, @@ -637,6 +644,7 @@ static struct resource wm831x_dcdc1_resources[] = { static struct resource wm831x_dcdc2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC2_CONTROL_1, .end = WM831X_DC2_DVS_CONTROL, .flags = IORESOURCE_IO, @@ -657,6 +665,7 @@ static struct resource wm831x_dcdc2_resources[] = { static struct resource wm831x_dcdc3_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC3_CONTROL_1, .end = WM831X_DC3_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -671,6 +680,7 @@ static struct resource wm831x_dcdc3_resources[] = { static struct resource wm831x_dcdc4_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC4_CONTROL, .end = WM831X_DC4_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -685,6 +695,7 @@ static struct resource wm831x_dcdc4_resources[] = { static struct resource wm8320_dcdc4_buck_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC4_CONTROL, .end = WM832X_DC4_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -707,6 +718,7 @@ static struct resource wm831x_gpio_resources[] = { static struct resource wm831x_isink1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_CURRENT_SINK_1, .end = WM831X_CURRENT_SINK_1, .flags = IORESOURCE_IO, @@ -720,6 +732,7 @@ static struct resource wm831x_isink1_resources[] = { static struct resource wm831x_isink2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_CURRENT_SINK_2, .end = WM831X_CURRENT_SINK_2, .flags = IORESOURCE_IO, @@ -733,6 +746,7 @@ static struct resource wm831x_isink2_resources[] = { static struct resource wm831x_ldo1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO1_CONTROL, .end = WM831X_LDO1_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -747,6 +761,7 @@ static struct resource wm831x_ldo1_resources[] = { static struct resource wm831x_ldo2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO2_CONTROL, .end = WM831X_LDO2_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -761,6 +776,7 @@ static struct resource wm831x_ldo2_resources[] = { static struct resource wm831x_ldo3_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO3_CONTROL, .end = WM831X_LDO3_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -775,6 +791,7 @@ static struct resource wm831x_ldo3_resources[] = { static struct resource wm831x_ldo4_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO4_CONTROL, .end = WM831X_LDO4_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -789,6 +806,7 @@ static struct resource wm831x_ldo4_resources[] = { static struct resource wm831x_ldo5_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO5_CONTROL, .end = WM831X_LDO5_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -803,6 +821,7 @@ static struct resource wm831x_ldo5_resources[] = { static struct resource wm831x_ldo6_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO6_CONTROL, .end = WM831X_LDO6_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -817,6 +836,7 @@ static struct resource wm831x_ldo6_resources[] = { static struct resource wm831x_ldo7_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO7_CONTROL, .end = WM831X_LDO7_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -831,6 +851,7 @@ static struct resource wm831x_ldo7_resources[] = { static struct resource wm831x_ldo8_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO8_CONTROL, .end = WM831X_LDO8_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -845,6 +866,7 @@ static struct resource wm831x_ldo8_resources[] = { static struct resource wm831x_ldo9_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO9_CONTROL, .end = WM831X_LDO9_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -859,6 +881,7 @@ static struct resource wm831x_ldo9_resources[] = { static struct resource wm831x_ldo10_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO10_CONTROL, .end = WM831X_LDO10_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -873,6 +896,7 @@ static struct resource wm831x_ldo10_resources[] = { static struct resource wm831x_ldo11_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO11_ON_CONTROL, .end = WM831X_LDO11_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -974,6 +998,7 @@ static struct resource wm831x_rtc_resources[] = { static struct resource wm831x_status1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_STATUS_LED_1, .end = WM831X_STATUS_LED_1, .flags = IORESOURCE_IO, @@ -982,6 +1007,7 @@ static struct resource wm831x_status1_resources[] = { static struct resource wm831x_status2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_STATUS_LED_2, .end = WM831X_STATUS_LED_2, .flags = IORESOURCE_IO, @@ -1787,27 +1813,27 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8310: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8310_devs, ARRAY_SIZE(wm8310_devs), - NULL, wm831x->irq_base); + NULL, 0); break; case WM8311: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8311_devs, ARRAY_SIZE(wm8311_devs), - NULL, wm831x->irq_base); + NULL, 0); if (!pdata || !pdata->disable_touch) mfd_add_devices(wm831x->dev, wm831x_num, touch_devs, ARRAY_SIZE(touch_devs), - NULL, wm831x->irq_base); + NULL, 0); break; case WM8312: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8312_devs, ARRAY_SIZE(wm8312_devs), - NULL, wm831x->irq_base); + NULL, 0); if (!pdata || !pdata->disable_touch) mfd_add_devices(wm831x->dev, wm831x_num, touch_devs, ARRAY_SIZE(touch_devs), - NULL, wm831x->irq_base); + NULL, 0); break; case WM8320: @@ -1816,7 +1842,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8326: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, wm831x->irq_base); + NULL, 0); break; default: @@ -1841,7 +1867,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) if (ret & WM831X_XTAL_ENA) { ret = mfd_add_devices(wm831x->dev, wm831x_num, rtc_devs, ARRAY_SIZE(rtc_devs), - NULL, wm831x->irq_base); + NULL, 0); if (ret != 0) { dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret); goto err_irq; @@ -1854,7 +1880,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) /* Treat errors as non-critical */ ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs, ARRAY_SIZE(backlight_devs), NULL, - wm831x->irq_base); + 0); if (ret < 0) dev_err(wm831x->dev, "Failed to add backlight: %d\n", ret); @@ -1883,8 +1909,7 @@ void wm831x_device_exit(struct wm831x *wm831x) { wm831x_otp_exit(wm831x); mfd_remove_devices(wm831x->dev); - if (wm831x->irq_base) - free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); + free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x); wm831x_irq_exit(wm831x); } diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index bec4d053916..804e56ec99e 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -18,6 +18,7 @@ #include <linux/irq.h> #include <linux/mfd/core.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/pdata.h> @@ -328,7 +329,7 @@ static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data) static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x, int irq) { - return &wm831x_irqs[irq - wm831x->irq_base]; + return &wm831x_irqs[irq]; } static void wm831x_irq_lock(struct irq_data *data) @@ -374,7 +375,7 @@ static void wm831x_irq_enable(struct irq_data *data) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, - data->irq); + data->hwirq); wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } @@ -383,7 +384,7 @@ static void wm831x_irq_disable(struct irq_data *data) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, - data->irq); + data->hwirq); wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; } @@ -393,7 +394,7 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) struct wm831x *wm831x = irq_data_get_irq_chip_data(data); int irq; - irq = data->irq - wm831x->irq_base; + irq = data->hwirq; if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) { /* Ignore internal-only IRQs */ @@ -412,22 +413,25 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) * do the update here as we can be called with the bus lock * held. */ + wm831x->gpio_level_low[irq] = false; + wm831x->gpio_level_high[irq] = false; switch (type) { case IRQ_TYPE_EDGE_BOTH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; - wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_RISING: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; - wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_FALLING: wm831x->gpio_update[irq] = 0x10000; - wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_LEVEL_HIGH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; - wm831x->gpio_level[irq] = true; + wm831x->gpio_level_high[irq] = true; + break; + case IRQ_TYPE_LEVEL_LOW: + wm831x->gpio_update[irq] = 0x10000; + wm831x->gpio_level_low[irq] = true; break; default: return -EINVAL; @@ -469,9 +473,11 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) * descriptors. */ if (primary & WM831X_TCHPD_INT) - handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + WM831X_IRQ_TCHPD)); if (primary & WM831X_TCHDATA_INT) - handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + WM831X_IRQ_TCHDATA)); primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT); for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) { @@ -507,16 +513,29 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) } if (*status & wm831x_irqs[i].mask) - handle_nested_irq(wm831x->irq_base + i); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + i)); /* Simulate an edge triggered IRQ by polling the input * status. This is sucky but improves interoperability. */ if (primary == WM831X_GP_INT && - wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) { + wm831x->gpio_level_high[i - WM831X_IRQ_GPIO_1]) { ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) { - handle_nested_irq(wm831x->irq_base + i); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + i)); + ret = wm831x_reg_read(wm831x, + WM831X_GPIO_LEVEL); + } + } + + if (primary == WM831X_GP_INT && + wm831x->gpio_level_low[i - WM831X_IRQ_GPIO_1]) { + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + while (!(ret & 1 << (i - WM831X_IRQ_GPIO_1))) { + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + i)); ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); } @@ -527,10 +546,34 @@ out: return IRQ_HANDLED; } +static int wm831x_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &wm831x_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops wm831x_irq_domain_ops = { + .map = wm831x_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + int wm831x_irq_init(struct wm831x *wm831x, int irq) { struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int i, cur_irq, ret; + struct irq_domain *domain; + int i, ret, irq_base; mutex_init(&wm831x->irq_lock); @@ -543,18 +586,33 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) } /* Try to dynamically allocate IRQs if no base is specified */ - if (!pdata || !pdata->irq_base) - wm831x->irq_base = -1; + if (pdata && pdata->irq_base) { + irq_base = irq_alloc_descs(pdata->irq_base, 0, + WM831X_NUM_IRQS, 0); + if (irq_base < 0) { + dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n", + irq_base); + irq_base = 0; + } + } else { + irq_base = 0; + } + + if (irq_base) + domain = irq_domain_add_legacy(wm831x->dev->of_node, + ARRAY_SIZE(wm831x_irqs), + irq_base, 0, + &wm831x_irq_domain_ops, + wm831x); else - wm831x->irq_base = pdata->irq_base; + domain = irq_domain_add_linear(wm831x->dev->of_node, + ARRAY_SIZE(wm831x_irqs), + &wm831x_irq_domain_ops, + wm831x); - wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0, - WM831X_NUM_IRQS, 0); - if (wm831x->irq_base < 0) { - dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n", - wm831x->irq_base); - wm831x->irq_base = 0; - return 0; + if (!domain) { + dev_warn(wm831x->dev, "Failed to allocate IRQ domain\n"); + return -EINVAL; } if (pdata && pdata->irq_cmos) @@ -565,38 +623,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, WM831X_IRQ_OD, i); - /* Try to flag /IRQ as a wake source; there are a number of - * unconditional wake sources in the PMIC so this isn't - * conditional but we don't actually care *too* much if it - * fails. - */ - ret = enable_irq_wake(irq); - if (ret != 0) { - dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n", - ret); - } - wm831x->irq = irq; - - /* Register them with genirq */ - for (cur_irq = wm831x->irq_base; - cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, wm831x); - irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif - } + wm831x->irq_domain = domain; if (irq) { + /* Try to flag /IRQ as a wake source; there are a number of + * unconditional wake sources in the PMIC so this isn't + * conditional but we don't actually care *too* much if it + * fails. + */ + ret = enable_irq_wake(irq); + if (ret != 0) { + dev_warn(wm831x->dev, + "Can't enable IRQ as wake source: %d\n", + ret); + } + ret = request_threaded_irq(irq, NULL, wm831x_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "wm831x", wm831x); diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index dd1caaac55e..8a9b11ca076 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -20,6 +20,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/regmap.h> #include <linux/workqueue.h> #include <linux/mfd/wm8350/core.h> @@ -74,7 +75,7 @@ static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, int bytes = num_regs * 2; dev_dbg(wm8350->dev, "volatile read\n"); - ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest); + ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes); for (i = reg; i < reg + num_regs; i++) { /* Cache is CPU endian */ @@ -96,9 +97,6 @@ static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) int ret = 0; int bytes = num_regs * 2; - if (wm8350->read_dev == NULL) - return -ENODEV; - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { dev_err(wm8350->dev, "invalid reg %x\n", reg + num_regs - 1); @@ -149,9 +147,6 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) int end = reg + num_regs; int bytes = num_regs * 2; - if (wm8350->write_dev == NULL) - return -ENODEV; - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { dev_err(wm8350->dev, "invalid reg %x\n", reg + num_regs - 1); @@ -182,7 +177,7 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) } /* Actually write it out */ - return wm8350->write_dev(wm8350, reg, bytes, (char *)src); + return regmap_raw_write(wm8350->regmap, reg, src, bytes); } /* @@ -515,9 +510,8 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) * a PMIC so the device many not be in a virgin state and we * can't rely on the silicon values. */ - ret = wm8350->read_dev(wm8350, 0, - sizeof(u16) * (WM8350_MAX_REGISTER + 1), - wm8350->reg_cache); + ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache, + sizeof(u16) * (WM8350_MAX_REGISTER + 1)); if (ret < 0) { dev_err(wm8350->dev, "failed to read initial cache values\n"); @@ -570,35 +564,30 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { int ret; - u16 id1, id2, mask_rev; - u16 cust_id, mode, chip_rev; + unsigned int id1, id2, mask_rev; + unsigned int cust_id, mode, chip_rev; dev_set_drvdata(wm8350->dev, wm8350); /* get WM8350 revision and config mode */ - ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); + ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1); if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); goto err; } - ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + ret = regmap_read(wm8350->regmap, WM8350_ID, &id2); if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); goto err; } - ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), - &mask_rev); + ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev); if (ret != 0) { dev_err(wm8350->dev, "Failed to read revision: %d\n", ret); goto err; } - id1 = be16_to_cpu(id1); - id2 = be16_to_cpu(id2); - mask_rev = be16_to_cpu(mask_rev); - if (id1 != 0x6143) { dev_err(wm8350->dev, "Device with ID %x is not a WM8350\n", id1); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index d955faaf27c..a68aceb4e48 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -15,47 +15,18 @@ #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/mfd/wm8350/core.h> +#include <linux/regmap.h> #include <linux/slab.h> -static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg, - int bytes, void *dest) -{ - int ret; - - ret = i2c_master_send(wm8350->i2c_client, ®, 1); - if (ret < 0) - return ret; - ret = i2c_master_recv(wm8350->i2c_client, dest, bytes); - if (ret < 0) - return ret; - if (ret != bytes) - return -EIO; - return 0; -} - -static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg, - int bytes, void *src) -{ - /* we add 1 byte for device register */ - u8 msg[(WM8350_MAX_REGISTER << 1) + 1]; - int ret; - - if (bytes > ((WM8350_MAX_REGISTER << 1) + 1)) - return -EINVAL; - - msg[0] = reg; - memcpy(&msg[1], src, bytes); - ret = i2c_master_send(wm8350->i2c_client, msg, bytes + 1); - if (ret < 0) - return ret; - if (ret != bytes + 1) - return -EIO; - return 0; -} +static const struct regmap_config wm8350_regmap = { + .reg_bits = 8, + .val_bits = 16, +}; static int wm8350_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -67,20 +38,18 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, if (wm8350 == NULL) return -ENOMEM; + wm8350->regmap = devm_regmap_init_i2c(i2c, &wm8350_regmap); + if (IS_ERR(wm8350->regmap)) { + ret = PTR_ERR(wm8350->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(i2c, wm8350); wm8350->dev = &i2c->dev; - wm8350->i2c_client = i2c; - wm8350->read_dev = wm8350_i2c_read_device; - wm8350->write_dev = wm8350_i2c_write_device; - - ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data); - if (ret < 0) - goto err; - - return ret; -err: - return ret; + return wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data); } static int wm8350_i2c_remove(struct i2c_client *i2c) diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 1189a17f0f2..4b7d378551d 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -23,136 +23,16 @@ #include <linux/regmap.h> #include <linux/slab.h> -static struct { - u16 readable; /* Mask of readable bits */ - u16 writable; /* Mask of writable bits */ - u16 vol; /* Mask of volatile bits */ - int is_codec; /* Register controlled by codec reset */ - u16 default_val; /* Value on reset */ -} reg_data[] = { - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */ - { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */ - { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */ - { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */ - { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4 */ - { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5 */ - { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6 */ - { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7 */ - { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8 */ - { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9 */ - { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */ - { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */ - { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */ - { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */ - { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */ - { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */ - { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */ - { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */ - { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */ - { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */ - { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */ - { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */ - { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */ - { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */ - { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */ - { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */ - { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */ - { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */ - { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */ - { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */ - { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */ - { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */ - { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */ - { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */ - { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */ - { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */ - { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */ - { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */ - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */ - { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */ - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */ - { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */ - { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */ - { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */ - { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */ - { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */ - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */ - { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */ - { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */ - { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */ -}; - -static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest) +static bool wm8400_volatile(struct device *dev, unsigned int reg) { - int i, ret = 0; - - BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache)); - - /* If there are any volatile reads then read back the entire block */ - for (i = reg; i < reg + num_regs; i++) - if (reg_data[i].vol) { - ret = regmap_bulk_read(wm8400->regmap, reg, dest, - num_regs); - return ret; - } - - /* Otherwise use the cache */ - memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16)); - - return 0; -} - -static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, - u16 *src) -{ - int ret, i; - - BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache)); - - for (i = 0; i < num_regs; i++) { - BUG_ON(!reg_data[reg + i].writable); - wm8400->reg_cache[reg + i] = src[i]; - ret = regmap_write(wm8400->regmap, reg, src[i]); - if (ret != 0) - return ret; + switch (reg) { + case WM8400_INTERRUPT_STATUS_1: + case WM8400_INTERRUPT_LEVELS: + case WM8400_SHUTDOWN_REASON: + return true; + default: + return false; } - - return 0; } /** @@ -165,13 +45,12 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, */ u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg) { - u16 val; - - mutex_lock(&wm8400->io_lock); - - wm8400_read(wm8400, reg, 1, &val); + unsigned int val; + int ret; - mutex_unlock(&wm8400->io_lock); + ret = regmap_read(wm8400->regmap, reg, &val); + if (ret < 0) + return ret; return val; } @@ -179,63 +58,10 @@ EXPORT_SYMBOL_GPL(wm8400_reg_read); int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data) { - int ret; - - mutex_lock(&wm8400->io_lock); - - ret = wm8400_read(wm8400, reg, count, data); - - mutex_unlock(&wm8400->io_lock); - - return ret; + return regmap_bulk_read(wm8400->regmap, reg, data, count); } EXPORT_SYMBOL_GPL(wm8400_block_read); -/** - * wm8400_set_bits - Bitmask write - * - * @wm8400: Pointer to wm8400 control structure - * @reg: Register to access - * @mask: Mask of bits to change - * @val: Value to set for masked bits - */ -int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val) -{ - u16 tmp; - int ret; - - mutex_lock(&wm8400->io_lock); - - ret = wm8400_read(wm8400, reg, 1, &tmp); - tmp = (tmp & ~mask) | val; - if (ret == 0) - ret = wm8400_write(wm8400, reg, 1, &tmp); - - mutex_unlock(&wm8400->io_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(wm8400_set_bits); - -/** - * wm8400_reset_codec_reg_cache - Reset cached codec registers to - * their default values. - */ -void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) -{ - int i; - - mutex_lock(&wm8400->io_lock); - - /* Reset all codec registers to their initial value */ - for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) - if (reg_data[i].is_codec) - wm8400->reg_cache[i] = reg_data[i].default_val; - - mutex_unlock(&wm8400->io_lock); -} -EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); - static int wm8400_register_codec(struct wm8400 *wm8400) { struct mfd_cell cell = { @@ -257,44 +83,24 @@ static int wm8400_register_codec(struct wm8400 *wm8400) static int wm8400_init(struct wm8400 *wm8400, struct wm8400_platform_data *pdata) { - u16 reg; - int ret, i; - - mutex_init(&wm8400->io_lock); + unsigned int reg; + int ret; dev_set_drvdata(wm8400->dev, wm8400); /* Check that this is actually a WM8400 */ - ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i); + ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, ®); if (ret != 0) { dev_err(wm8400->dev, "Chip ID register read failed\n"); return -EIO; } - if (i != reg_data[WM8400_RESET_ID].default_val) { - dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i); + if (reg != 0x6172) { + dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", + reg); return -ENODEV; } - /* We don't know what state the hardware is in and since this - * is a PMIC we can't reset it safely so initialise the register - * cache from the hardware. - */ - ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache, - ARRAY_SIZE(wm8400->reg_cache)); - if (ret != 0) { - dev_err(wm8400->dev, "Register cache read failed\n"); - return -EIO; - } - for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) - wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]); - - /* If the codec is in reset use hard coded values */ - if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA)) - for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) - if (reg_data[i].is_codec) - wm8400->reg_cache[i] = reg_data[i].default_val; - - ret = wm8400_read(wm8400, WM8400_ID, 1, ®); + ret = regmap_read(wm8400->regmap, WM8400_ID, ®); if (ret != 0) { dev_err(wm8400->dev, "ID register read failed: %d\n", ret); return ret; @@ -334,8 +140,22 @@ static const struct regmap_config wm8400_regmap_config = { .reg_bits = 8, .val_bits = 16, .max_register = WM8400_REGISTER_COUNT - 1, + + .volatile_reg = wm8400_volatile, + + .cache_type = REGCACHE_RBTREE, }; +/** + * wm8400_reset_codec_reg_cache - Reset cached codec registers to + * their default values. + */ +void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) +{ + regmap_reinit_cache(wm8400->regmap, &wm8400_regmap_config); +} +EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int wm8400_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 9d7ca1e978f..1e321d34977 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -500,7 +500,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) ret); goto err_enable; } - wm8994->revision = ret; + wm8994->revision = ret & WM8994_CHIP_REV_MASK; + wm8994->cust_id = (ret & WM8994_CUST_ID_MASK) >> WM8994_CUST_ID_SHIFT; switch (wm8994->type) { case WM8994: @@ -553,8 +554,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) break; } - dev_info(wm8994->dev, "%s revision %c\n", devname, - 'A' + wm8994->revision); + dev_info(wm8994->dev, "%s revision %c CUST_ID %02x\n", devname, + 'A' + wm8994->revision, wm8994->cust_id); switch (wm8994->type) { case WM1811: @@ -732,23 +733,7 @@ static struct i2c_driver wm8994_i2c_driver = { .id_table = wm8994_i2c_id, }; -static int __init wm8994_i2c_init(void) -{ - int ret; - - ret = i2c_add_driver(&wm8994_i2c_driver); - if (ret != 0) - pr_err("Failed to register wm8994 I2C driver: %d\n", ret); - - return ret; -} -module_init(wm8994_i2c_init); - -static void __exit wm8994_i2c_exit(void) -{ - i2c_del_driver(&wm8994_i2c_driver); -} -module_exit(wm8994_i2c_exit); +module_i2c_driver(wm8994_i2c_driver); MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index bfd25af6ecb..52e9e294494 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -1122,7 +1122,6 @@ static bool wm8994_volatile_register(struct device *dev, unsigned int reg) case WM8994_RATE_STATUS: case WM8958_MIC_DETECT_3: case WM8994_DC_SERVO_4E: - case WM8994_CHIP_REVISION: case WM8994_INTERRUPT_STATUS_1: case WM8994_INTERRUPT_STATUS_2: return true; diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c index d7a9aa14e5d..042a8fe4efa 100644 --- a/drivers/misc/ab8500-pwm.c +++ b/drivers/misc/ab8500-pwm.c @@ -142,10 +142,16 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ab8500_pwm_match[] = { + { .compatible = "stericsson,ab8500-pwm", }, + {} +}; + static struct platform_driver ab8500_pwm_driver = { .driver = { .name = "ab8500-pwm", .owner = THIS_MODULE, + .of_match_table = ab8500_pwm_match, }, .probe = ab8500_pwm_probe, .remove = __devexit_p(ab8500_pwm_remove), diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index 987332b71d8..fc1ad955118 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -565,7 +565,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) goto err_usb; } - irq = platform_get_irq_byname(pdev, "SYSLO"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq, IRQF_TRIGGER_RISING, "System power low", power); @@ -575,7 +575,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) goto err_battery; } - irq = platform_get_irq_byname(pdev, "PWR SRC"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC")); ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq, IRQF_TRIGGER_RISING, "Power source", power); @@ -586,7 +586,9 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) } for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { - irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); + irq = wm831x_irq(wm831x, + platform_get_irq_byname(pdev, + wm831x_bat_irqs[i])); ret = request_threaded_irq(irq, NULL, wm831x_bat_irq, IRQF_TRIGGER_RISING, wm831x_bat_irqs[i], @@ -606,10 +608,10 @@ err_bat_irq: irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); free_irq(irq, power); } - irq = platform_get_irq_byname(pdev, "PWR SRC"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC")); free_irq(irq, power); err_syslo: - irq = platform_get_irq_byname(pdev, "SYSLO"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); free_irq(irq, power); err_battery: if (power->have_battery) @@ -626,17 +628,20 @@ err_kmalloc: static __devexit int wm831x_power_remove(struct platform_device *pdev) { struct wm831x_power *wm831x_power = platform_get_drvdata(pdev); + struct wm831x *wm831x = wm831x_power->wm831x; int irq, i; for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { - irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); + irq = wm831x_irq(wm831x, + platform_get_irq_byname(pdev, + wm831x_bat_irqs[i])); free_irq(irq, wm831x_power); } - irq = platform_get_irq_byname(pdev, "PWR SRC"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC")); free_irq(irq, wm831x_power); - irq = platform_get_irq_byname(pdev, "SYSLO"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); free_irq(irq, wm831x_power); if (wm831x_power->have_battery) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 49b2112b048..3660bace123 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -47,7 +47,7 @@ static int anatop_set_voltage(struct regulator_dev *reg, int min_uV, int max_uV, unsigned *selector) { struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); - u32 val, sel; + u32 val, sel, mask; int uv; uv = min_uV; @@ -71,11 +71,10 @@ static int anatop_set_voltage(struct regulator_dev *reg, int min_uV, val = anatop_reg->min_bit_val + sel; *selector = sel; dev_dbg(®->dev, "%s: calculated val %d\n", __func__, val); - anatop_set_bits(anatop_reg->mfd, - anatop_reg->control_reg, - anatop_reg->vol_bit_shift, - anatop_reg->vol_bit_width, - val); + mask = ((1 << anatop_reg->vol_bit_width) - 1) << + anatop_reg->vol_bit_shift; + val <<= anatop_reg->vol_bit_shift; + anatop_write_reg(anatop_reg->mfd, anatop_reg->control_reg, val, mask); return 0; } @@ -88,10 +87,9 @@ static int anatop_get_voltage_sel(struct regulator_dev *reg) if (!anatop_reg->control_reg) return -ENOTSUPP; - val = anatop_get_bits(anatop_reg->mfd, - anatop_reg->control_reg, - anatop_reg->vol_bit_shift, - anatop_reg->vol_bit_width); + val = anatop_read_reg(anatop_reg->mfd, anatop_reg->control_reg); + val = (val & ((1 << anatop_reg->vol_bit_width) - 1)) >> + anatop_reg->vol_bit_shift; return val - anatop_reg->min_bit_val; } diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 4e01a423471..6bf864b4bdf 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -331,21 +331,16 @@ struct tps65910_reg { static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg) { - u8 val; + unsigned int val; int err; - err = pmic->mfd->read(pmic->mfd, reg, 1, &val); + err = tps65910_reg_read(pmic->mfd, reg, &val); if (err) return err; return val; } -static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val) -{ - return pmic->mfd->write(pmic->mfd, reg, 1, &val); -} - static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, u8 set_mask, u8 clear_mask) { @@ -362,7 +357,7 @@ static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, data &= ~clear_mask; data |= set_mask; - err = tps65910_write(pmic, reg, data); + err = tps65910_reg_write(pmic->mfd, reg, data); if (err) dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); @@ -371,7 +366,7 @@ out: return err; } -static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) +static int tps65910_reg_read_locked(struct tps65910_reg *pmic, u8 reg) { int data; @@ -385,13 +380,13 @@ static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) return data; } -static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val) +static int tps65910_reg_write_locked(struct tps65910_reg *pmic, u8 reg, u8 val) { int err; mutex_lock(&pmic->mutex); - err = tps65910_write(pmic, reg, val); + err = tps65910_reg_write(pmic->mfd, reg, val); if (err < 0) dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); @@ -490,9 +485,9 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) LDO_ST_MODE_BIT); case REGULATOR_MODE_IDLE: value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; - return tps65910_set_bits(mfd, reg, value); + return tps65910_reg_set_bits(mfd, reg, value); case REGULATOR_MODE_STANDBY: - return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT); + return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT); } return -EINVAL; @@ -507,7 +502,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -527,28 +522,28 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) switch (id) { case TPS65910_REG_VDD1: - opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP); - mult = tps65910_reg_read(pmic, TPS65910_VDD1); + opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_OP); + mult = tps65910_reg_read_locked(pmic, TPS65910_VDD1); mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR); + srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_SR); sr = opvsel & VDD1_OP_CMD_MASK; opvsel &= VDD1_OP_SEL_MASK; srvsel &= VDD1_SR_SEL_MASK; vselmax = 75; break; case TPS65910_REG_VDD2: - opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP); - mult = tps65910_reg_read(pmic, TPS65910_VDD2); + opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_OP); + mult = tps65910_reg_read_locked(pmic, TPS65910_VDD2); mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR); + srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_SR); sr = opvsel & VDD2_OP_CMD_MASK; opvsel &= VDD2_OP_SEL_MASK; srvsel &= VDD2_SR_SEL_MASK; vselmax = 75; break; case TPS65911_REG_VDDCTRL: - opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP); - srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR); + opvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_OP); + srvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_SR); sr = opvsel & VDDCTRL_OP_CMD_MASK; opvsel &= VDDCTRL_OP_SEL_MASK; srvsel &= VDDCTRL_SR_SEL_MASK; @@ -588,7 +583,7 @@ static int tps65910_get_voltage_sel(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -625,7 +620,7 @@ static int tps65911_get_voltage_sel(struct regulator_dev *dev) reg = pmic->get_ctrl_reg(id); - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); switch (id) { case TPS65911_REG_LDO1: @@ -670,7 +665,7 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, tps65910_modify_bits(pmic, TPS65910_VDD1, (dcdc_mult << VDD1_VGAIN_SEL_SHIFT), VDD1_VGAIN_SEL_MASK); - tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65910_VDD1_OP, vsel); break; case TPS65910_REG_VDD2: dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; @@ -681,11 +676,11 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, tps65910_modify_bits(pmic, TPS65910_VDD2, (dcdc_mult << VDD2_VGAIN_SEL_SHIFT), VDD1_VGAIN_SEL_MASK); - tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65910_VDD2_OP, vsel); break; case TPS65911_REG_VDDCTRL: vsel = selector + 3; - tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65911_VDDCTRL_OP, vsel); } return 0; @@ -936,10 +931,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN1 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -949,10 +944,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN2 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -964,10 +959,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, if ((tps65910_chip_id(mfd) == TPS65910) && (id >= TPS65910_REG_VDIG1)) { if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -979,10 +974,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* Return if no external control is selected */ if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) { /* Clear all sleep controls */ - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); if (ret < 0) dev_err(mfd->dev, @@ -1001,32 +996,33 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, (tps65910_chip_id(mfd) == TPS65911))) { int op_reg_add = pmic->get_ctrl_reg(id) + 1; int sr_reg_add = pmic->get_ctrl_reg(id) + 2; - int opvsel = tps65910_reg_read(pmic, op_reg_add); - int srvsel = tps65910_reg_read(pmic, sr_reg_add); + int opvsel = tps65910_reg_read_locked(pmic, op_reg_add); + int srvsel = tps65910_reg_read_locked(pmic, sr_reg_add); if (opvsel & VDD1_OP_CMD_MASK) { u8 reg_val = srvsel & VDD1_OP_SEL_MASK; - ret = tps65910_reg_write(pmic, op_reg_add, reg_val); + ret = tps65910_reg_write_locked(pmic, op_reg_add, + reg_val); if (ret < 0) { dev_err(mfd->dev, "Error in configuring op register\n"); return ret; } } - ret = tps65910_reg_write(pmic, sr_reg_add, 0); + ret = tps65910_reg_write_locked(pmic, sr_reg_add, 0); if (ret < 0) { dev_err(mfd->dev, "Error in settting sr register\n"); return ret; } } - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) { if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); } if (ret < 0) @@ -1177,7 +1173,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); /* Give control of all register to control port */ - tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL, + tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); switch(tps65910_chip_id(tps65910)) { diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index a885911bb5f..099da11e989 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -535,7 +535,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) goto err; } - irq = platform_get_irq_byname(pdev, "UV"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, IRQF_TRIGGER_RISING, dcdc->name, dcdc); if (ret != 0) { @@ -544,7 +544,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) goto err_regulator; } - irq = platform_get_irq_byname(pdev, "HC"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")); ret = request_threaded_irq(irq, NULL, wm831x_dcdc_oc_irq, IRQF_TRIGGER_RISING, dcdc->name, dcdc); if (ret != 0) { @@ -558,7 +558,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) return 0; err_uv: - free_irq(platform_get_irq_byname(pdev, "UV"), dcdc); + free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); err_regulator: regulator_unregister(dcdc->regulator); err: @@ -570,11 +571,14 @@ err: static __devexit int wm831x_buckv_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + struct wm831x *wm831x = dcdc->wm831x; platform_set_drvdata(pdev, NULL); - free_irq(platform_get_irq_byname(pdev, "HC"), dcdc); - free_irq(platform_get_irq_byname(pdev, "UV"), dcdc); + free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), + dcdc); + free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); regulator_unregister(dcdc->regulator); if (dcdc->dvs_gpio) gpio_free(dcdc->dvs_gpio); @@ -726,7 +730,7 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev) goto err; } - irq = platform_get_irq_byname(pdev, "UV"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, IRQF_TRIGGER_RISING, dcdc->name, dcdc); if (ret != 0) { @@ -751,7 +755,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - free_irq(platform_get_irq_byname(pdev, "UV"), dcdc); + free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); regulator_unregister(dcdc->regulator); return 0; @@ -859,7 +864,7 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev) goto err; } - irq = platform_get_irq_byname(pdev, "UV"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, IRQF_TRIGGER_RISING, dcdc->name, dcdc); @@ -885,7 +890,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - free_irq(platform_get_irq_byname(pdev, "UV"), dcdc); + free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); regulator_unregister(dcdc->regulator); return 0; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index b50ab778b09..0d207c29771 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -202,7 +202,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) goto err; } - irq = platform_get_irq(pdev, 0); + irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); ret = request_threaded_irq(irq, NULL, wm831x_isink_irq, IRQF_TRIGGER_RISING, isink->name, isink); if (ret != 0) { @@ -227,7 +227,7 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - free_irq(platform_get_irq(pdev, 0), isink); + free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); regulator_unregister(isink->regulator); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index aa1f8b3fbe1..a9a28d8ac18 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -321,7 +321,7 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) goto err; } - irq = platform_get_irq_byname(pdev, "UV"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq, IRQF_TRIGGER_RISING, ldo->name, ldo); @@ -347,7 +347,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - free_irq(platform_get_irq_byname(pdev, "UV"), ldo); + free_irq(wm831x_irq(ldo->wm831x, + platform_get_irq_byname(pdev, "UV")), ldo); regulator_unregister(ldo->regulator); return 0; @@ -582,7 +583,7 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) goto err; } - irq = platform_get_irq_byname(pdev, "UV"); + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq, IRQF_TRIGGER_RISING, ldo->name, ldo); if (ret != 0) { @@ -605,7 +606,8 @@ static __devexit int wm831x_aldo_remove(struct platform_device *pdev) { struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - free_irq(platform_get_irq_byname(pdev, "UV"), ldo); + free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")), + ldo); regulator_unregister(ldo->regulator); return 0; diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c index 3b6e6a67e76..59c6245e042 100644 --- a/drivers/rtc/rtc-wm831x.c +++ b/drivers/rtc/rtc-wm831x.c @@ -396,7 +396,7 @@ static int wm831x_rtc_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_rtc *wm831x_rtc; - int alm_irq = platform_get_irq_byname(pdev, "ALM"); + int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM")); int ret = 0; wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index a18bf6358eb..d92d7488be1 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -565,6 +565,7 @@ config INTEL_SCU_WATCHDOG config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI + select LPC_ICH ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller diff --git a/drivers/watchdog/iTCO_vendor.h b/drivers/watchdog/iTCO_vendor.h index 9e27e6422f6..3c57b45537a 100644 --- a/drivers/watchdog/iTCO_vendor.h +++ b/drivers/watchdog/iTCO_vendor.h @@ -1,8 +1,8 @@ /* iTCO Vendor Specific Support hooks */ #ifdef CONFIG_ITCO_VENDOR_SUPPORT -extern void iTCO_vendor_pre_start(unsigned long, unsigned int); -extern void iTCO_vendor_pre_stop(unsigned long); -extern void iTCO_vendor_pre_keepalive(unsigned long, unsigned int); +extern void iTCO_vendor_pre_start(struct resource *, unsigned int); +extern void iTCO_vendor_pre_stop(struct resource *); +extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int); extern void iTCO_vendor_pre_set_heartbeat(unsigned int); extern int iTCO_vendor_check_noreboot_on(void); #else diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index 2721d29ce24..b6b2f90b5d4 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -35,11 +35,6 @@ #include "iTCO_vendor.h" -/* iTCO defines */ -#define SMI_EN (acpibase + 0x30) /* SMI Control and Enable Register */ -#define TCOBASE (acpibase + 0x60) /* TCO base address */ -#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */ - /* List of vendor support modes */ /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */ #define SUPERMICRO_OLD_BOARD 1 @@ -82,24 +77,24 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=" * 20.6 seconds. */ -static void supermicro_old_pre_start(unsigned long acpibase) +static void supermicro_old_pre_start(struct resource *smires) { unsigned long val32; /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ - val32 = inl(SMI_EN); + val32 = inl(smires->start); val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); /* Needed to activate watchdog */ + outl(val32, smires->start); /* Needed to activate watchdog */ } -static void supermicro_old_pre_stop(unsigned long acpibase) +static void supermicro_old_pre_stop(struct resource *smires) { unsigned long val32; /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */ - val32 = inl(SMI_EN); + val32 = inl(smires->start); val32 |= 0x00002000; /* Turn on SMI clearing watchdog */ - outl(val32, SMI_EN); /* Needed to deactivate watchdog */ + outl(val32, smires->start); /* Needed to deactivate watchdog */ } /* @@ -270,66 +265,66 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) * Don't use this fix if you don't need to!!! */ -static void broken_bios_start(unsigned long acpibase) +static void broken_bios_start(struct resource *smires) { unsigned long val32; - val32 = inl(SMI_EN); + val32 = inl(smires->start); /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# Bit 0: GBL_SMI_EN -> 0 = No SMI# will be generated by ICH. */ val32 &= 0xffffdffe; - outl(val32, SMI_EN); + outl(val32, smires->start); } -static void broken_bios_stop(unsigned long acpibase) +static void broken_bios_stop(struct resource *smires) { unsigned long val32; - val32 = inl(SMI_EN); + val32 = inl(smires->start); /* Bit 13: TCO_EN -> 1 = Enables TCO logic generating an SMI# Bit 0: GBL_SMI_EN -> 1 = Turn global SMI on again. */ val32 |= 0x00002001; - outl(val32, SMI_EN); + outl(val32, smires->start); } /* * Generic Support Functions */ -void iTCO_vendor_pre_start(unsigned long acpibase, +void iTCO_vendor_pre_start(struct resource *smires, unsigned int heartbeat) { switch (vendorsupport) { case SUPERMICRO_OLD_BOARD: - supermicro_old_pre_start(acpibase); + supermicro_old_pre_start(smires); break; case SUPERMICRO_NEW_BOARD: supermicro_new_pre_start(heartbeat); break; case BROKEN_BIOS: - broken_bios_start(acpibase); + broken_bios_start(smires); break; } } EXPORT_SYMBOL(iTCO_vendor_pre_start); -void iTCO_vendor_pre_stop(unsigned long acpibase) +void iTCO_vendor_pre_stop(struct resource *smires) { switch (vendorsupport) { case SUPERMICRO_OLD_BOARD: - supermicro_old_pre_stop(acpibase); + supermicro_old_pre_stop(smires); break; case SUPERMICRO_NEW_BOARD: supermicro_new_pre_stop(); break; case BROKEN_BIOS: - broken_bios_stop(acpibase); + broken_bios_stop(smires); break; } } EXPORT_SYMBOL(iTCO_vendor_pre_stop); -void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat) +void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat) { if (vendorsupport == SUPERMICRO_NEW_BOARD) supermicro_new_pre_set_heartbeat(heartbeat); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 9fecb95645a..741528b032e 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -66,316 +66,16 @@ #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/io.h> /* For inb/outb/... */ +#include <linux/mfd/core.h> +#include <linux/mfd/lpc_ich.h> #include "iTCO_vendor.h" -/* TCO related info */ -enum iTCO_chipsets { - TCO_ICH = 0, /* ICH */ - TCO_ICH0, /* ICH0 */ - TCO_ICH2, /* ICH2 */ - TCO_ICH2M, /* ICH2-M */ - TCO_ICH3, /* ICH3-S */ - TCO_ICH3M, /* ICH3-M */ - TCO_ICH4, /* ICH4 */ - TCO_ICH4M, /* ICH4-M */ - TCO_CICH, /* C-ICH */ - TCO_ICH5, /* ICH5 & ICH5R */ - TCO_6300ESB, /* 6300ESB */ - TCO_ICH6, /* ICH6 & ICH6R */ - TCO_ICH6M, /* ICH6-M */ - TCO_ICH6W, /* ICH6W & ICH6RW */ - TCO_631XESB, /* 631xESB/632xESB */ - TCO_ICH7, /* ICH7 & ICH7R */ - TCO_ICH7DH, /* ICH7DH */ - TCO_ICH7M, /* ICH7-M & ICH7-U */ - TCO_ICH7MDH, /* ICH7-M DH */ - TCO_NM10, /* NM10 */ - TCO_ICH8, /* ICH8 & ICH8R */ - TCO_ICH8DH, /* ICH8DH */ - TCO_ICH8DO, /* ICH8DO */ - TCO_ICH8M, /* ICH8M */ - TCO_ICH8ME, /* ICH8M-E */ - TCO_ICH9, /* ICH9 */ - TCO_ICH9R, /* ICH9R */ - TCO_ICH9DH, /* ICH9DH */ - TCO_ICH9DO, /* ICH9DO */ - TCO_ICH9M, /* ICH9M */ - TCO_ICH9ME, /* ICH9M-E */ - TCO_ICH10, /* ICH10 */ - TCO_ICH10R, /* ICH10R */ - TCO_ICH10D, /* ICH10D */ - TCO_ICH10DO, /* ICH10DO */ - TCO_PCH, /* PCH Desktop Full Featured */ - TCO_PCHM, /* PCH Mobile Full Featured */ - TCO_P55, /* P55 */ - TCO_PM55, /* PM55 */ - TCO_H55, /* H55 */ - TCO_QM57, /* QM57 */ - TCO_H57, /* H57 */ - TCO_HM55, /* HM55 */ - TCO_Q57, /* Q57 */ - TCO_HM57, /* HM57 */ - TCO_PCHMSFF, /* PCH Mobile SFF Full Featured */ - TCO_QS57, /* QS57 */ - TCO_3400, /* 3400 */ - TCO_3420, /* 3420 */ - TCO_3450, /* 3450 */ - TCO_EP80579, /* EP80579 */ - TCO_CPT, /* Cougar Point */ - TCO_CPTD, /* Cougar Point Desktop */ - TCO_CPTM, /* Cougar Point Mobile */ - TCO_PBG, /* Patsburg */ - TCO_DH89XXCC, /* DH89xxCC */ - TCO_PPT, /* Panther Point */ - TCO_LPT, /* Lynx Point */ -}; - -static struct { - char *name; - unsigned int iTCO_version; -} iTCO_chipset_info[] __devinitdata = { - {"ICH", 1}, - {"ICH0", 1}, - {"ICH2", 1}, - {"ICH2-M", 1}, - {"ICH3-S", 1}, - {"ICH3-M", 1}, - {"ICH4", 1}, - {"ICH4-M", 1}, - {"C-ICH", 1}, - {"ICH5 or ICH5R", 1}, - {"6300ESB", 1}, - {"ICH6 or ICH6R", 2}, - {"ICH6-M", 2}, - {"ICH6W or ICH6RW", 2}, - {"631xESB/632xESB", 2}, - {"ICH7 or ICH7R", 2}, - {"ICH7DH", 2}, - {"ICH7-M or ICH7-U", 2}, - {"ICH7-M DH", 2}, - {"NM10", 2}, - {"ICH8 or ICH8R", 2}, - {"ICH8DH", 2}, - {"ICH8DO", 2}, - {"ICH8M", 2}, - {"ICH8M-E", 2}, - {"ICH9", 2}, - {"ICH9R", 2}, - {"ICH9DH", 2}, - {"ICH9DO", 2}, - {"ICH9M", 2}, - {"ICH9M-E", 2}, - {"ICH10", 2}, - {"ICH10R", 2}, - {"ICH10D", 2}, - {"ICH10DO", 2}, - {"PCH Desktop Full Featured", 2}, - {"PCH Mobile Full Featured", 2}, - {"P55", 2}, - {"PM55", 2}, - {"H55", 2}, - {"QM57", 2}, - {"H57", 2}, - {"HM55", 2}, - {"Q57", 2}, - {"HM57", 2}, - {"PCH Mobile SFF Full Featured", 2}, - {"QS57", 2}, - {"3400", 2}, - {"3420", 2}, - {"3450", 2}, - {"EP80579", 2}, - {"Cougar Point", 2}, - {"Cougar Point Desktop", 2}, - {"Cougar Point Mobile", 2}, - {"Patsburg", 2}, - {"DH89xxCC", 2}, - {"Panther Point", 2}, - {"Lynx Point", 2}, - {NULL, 0} -}; - -/* - * This data only exists for exporting the supported PCI ids - * via MODULE_DEVICE_TABLE. We do not actually register a - * pci_driver, because the I/O Controller Hub has also other - * functions that probably will be registered by other drivers. - */ -static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { - { PCI_VDEVICE(INTEL, 0x2410), TCO_ICH}, - { PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0}, - { PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2}, - { PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M}, - { PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3}, - { PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M}, - { PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4}, - { PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M}, - { PCI_VDEVICE(INTEL, 0x2450), TCO_CICH}, - { PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5}, - { PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB}, - { PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6}, - { PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M}, - { PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W}, - { PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7}, - { PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH}, - { PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M}, - { PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH}, - { PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10}, - { PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8}, - { PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH}, - { PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO}, - { PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M}, - { PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME}, - { PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9}, - { PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R}, - { PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH}, - { PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO}, - { PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M}, - { PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME}, - { PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10}, - { PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R}, - { PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D}, - { PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO}, - { PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH}, - { PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM}, - { PCI_VDEVICE(INTEL, 0x3b02), TCO_P55}, - { PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55}, - { PCI_VDEVICE(INTEL, 0x3b06), TCO_H55}, - { PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57}, - { PCI_VDEVICE(INTEL, 0x3b08), TCO_H57}, - { PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55}, - { PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57}, - { PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57}, - { PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF}, - { PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57}, - { PCI_VDEVICE(INTEL, 0x3b12), TCO_3400}, - { PCI_VDEVICE(INTEL, 0x3b14), TCO_3420}, - { PCI_VDEVICE(INTEL, 0x3b16), TCO_3450}, - { PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579}, - { PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD}, - { PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM}, - { PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG}, - { PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG}, - { PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC}, - { PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT}, - { 0, }, /* End of list */ -}; -MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); - /* Address definitions for the TCO */ /* TCO base address */ -#define TCOBASE (iTCO_wdt_private.ACPIBASE + 0x60) +#define TCOBASE (iTCO_wdt_private.tco_res->start) /* SMI Control and Enable Register */ -#define SMI_EN (iTCO_wdt_private.ACPIBASE + 0x30) +#define SMI_EN (iTCO_wdt_private.smi_res->start) #define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */ #define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */ @@ -393,19 +93,18 @@ static char expect_release; static struct { /* this is private data for the iTCO_wdt device */ /* TCO version/generation */ unsigned int iTCO_version; - /* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ - unsigned long ACPIBASE; + struct resource *tco_res; + struct resource *smi_res; + struct resource *gcs_res; /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ unsigned long __iomem *gcs; /* the lock for io operations */ spinlock_t io_lock; + struct platform_device *dev; /* the PCI-device */ struct pci_dev *pdev; } iTCO_wdt_private; -/* the watchdog platform device */ -static struct platform_device *iTCO_wdt_platform_device; - /* module parameters */ #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ @@ -485,7 +184,7 @@ static int iTCO_wdt_start(void) spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat); + iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, heartbeat); /* disable chipset's NO_REBOOT bit */ if (iTCO_wdt_unset_NO_REBOOT_bit()) { @@ -519,7 +218,7 @@ static int iTCO_wdt_stop(void) spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE); + iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res); /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ val = inw(TCO1_CNT); @@ -541,7 +240,7 @@ static int iTCO_wdt_keepalive(void) { spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat); + iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, heartbeat); /* Reload the timer by writing to the TCO Timer Counter register */ if (iTCO_wdt_private.iTCO_version == 2) @@ -786,83 +485,120 @@ static struct miscdevice iTCO_wdt_miscdev = { * Init & exit routines */ -static int __devinit iTCO_wdt_init(struct pci_dev *pdev, - const struct pci_device_id *ent, struct platform_device *dev) +static void __devexit iTCO_wdt_cleanup(void) +{ + /* Stop the timer before we leave */ + if (!nowayout) + iTCO_wdt_stop(); + + /* Deregister */ + misc_deregister(&iTCO_wdt_miscdev); + + /* release resources */ + release_region(iTCO_wdt_private.tco_res->start, + resource_size(iTCO_wdt_private.tco_res)); + release_region(iTCO_wdt_private.smi_res->start, + resource_size(iTCO_wdt_private.smi_res)); + if (iTCO_wdt_private.iTCO_version == 2) { + iounmap(iTCO_wdt_private.gcs); + release_mem_region(iTCO_wdt_private.gcs_res->start, + resource_size(iTCO_wdt_private.gcs_res)); + } + + iTCO_wdt_private.tco_res = NULL; + iTCO_wdt_private.smi_res = NULL; + iTCO_wdt_private.gcs_res = NULL; + iTCO_wdt_private.gcs = NULL; +} + +static int __devinit iTCO_wdt_probe(struct platform_device *dev) { - int ret; - u32 base_address; - unsigned long RCBA; + int ret = -ENODEV; unsigned long val32; + struct lpc_ich_info *ich_info = dev->dev.platform_data; + + if (!ich_info) + goto out; + + spin_lock_init(&iTCO_wdt_private.io_lock); + + iTCO_wdt_private.tco_res = + platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO); + if (!iTCO_wdt_private.tco_res) + goto out; + + iTCO_wdt_private.smi_res = + platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI); + if (!iTCO_wdt_private.smi_res) + goto out; + + iTCO_wdt_private.iTCO_version = ich_info->iTCO_version; + iTCO_wdt_private.dev = dev; + iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); /* - * Find the ACPI/PM base I/O address which is the base - * for the TCO registers (TCOBASE=ACPIBASE + 0x60) - * ACPIBASE is bits [15:7] from 0x40-0x43 + * Get the Memory-Mapped GCS register, we need it for the + * NO_REBOOT flag (TCO v2). */ - pci_read_config_dword(pdev, 0x40, &base_address); - base_address &= 0x0000ff80; - if (base_address == 0x00000000) { - /* Something's wrong here, ACPIBASE has to be set */ - pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n"); - return -ENODEV; - } - iTCO_wdt_private.iTCO_version = - iTCO_chipset_info[ent->driver_data].iTCO_version; - iTCO_wdt_private.ACPIBASE = base_address; - iTCO_wdt_private.pdev = pdev; - - /* Get the Memory-Mapped GCS register, we need it for the - NO_REBOOT flag (TCO v2). To get access to it you have to - read RCBA from PCI Config space 0xf0 and use it as base. - GCS = RCBA + ICH6_GCS(0x3410). */ if (iTCO_wdt_private.iTCO_version == 2) { - pci_read_config_dword(pdev, 0xf0, &base_address); - if ((base_address & 1) == 0) { - pr_err("RCBA is disabled by hardware/BIOS, device disabled\n"); - ret = -ENODEV; + iTCO_wdt_private.gcs_res = platform_get_resource(dev, + IORESOURCE_MEM, + ICH_RES_MEM_GCS); + + if (!iTCO_wdt_private.gcs_res) + goto out; + + if (!request_mem_region(iTCO_wdt_private.gcs_res->start, + resource_size(iTCO_wdt_private.gcs_res), dev->name)) { + ret = -EBUSY; goto out; } - RCBA = base_address & 0xffffc000; - iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4); + iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start, + resource_size(iTCO_wdt_private.gcs_res)); + if (!iTCO_wdt_private.gcs) { + ret = -EIO; + goto unreg_gcs; + } } /* Check chipset's NO_REBOOT bit */ if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ - goto out_unmap; + goto unmap_gcs; } /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ iTCO_wdt_set_NO_REBOOT_bit(); /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ - if (!request_region(SMI_EN, 4, "iTCO_wdt")) { - pr_err("I/O address 0x%04lx already in use, device disabled\n", + if (!request_region(iTCO_wdt_private.smi_res->start, + resource_size(iTCO_wdt_private.smi_res), dev->name)) { + pr_err("I/O address 0x%04llx already in use, device disabled\n", SMI_EN); - ret = -EIO; - goto out_unmap; + ret = -EBUSY; + goto unmap_gcs; } if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) { - /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ + /* + * Bit 13: TCO_EN -> 0 + * Disables TCO logic generating an SMI# + */ val32 = inl(SMI_EN); val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ outl(val32, SMI_EN); } - /* The TCO I/O registers reside in a 32-byte range pointed to - by the TCOBASE value */ - if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) { - pr_err("I/O address 0x%04lx already in use, device disabled\n", + if (!request_region(iTCO_wdt_private.tco_res->start, + resource_size(iTCO_wdt_private.tco_res), dev->name)) { + pr_err("I/O address 0x%04llx already in use, device disabled\n", TCOBASE); - ret = -EIO; - goto unreg_smi_en; + ret = -EBUSY; + goto unreg_smi; } - pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n", - iTCO_chipset_info[ent->driver_data].name, - iTCO_chipset_info[ent->driver_data].iTCO_version, - TCOBASE); + pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", + ich_info->name, ich_info->iTCO_version, TCOBASE); /* Clear out the (probably old) status */ outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ @@ -883,7 +619,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, if (ret != 0) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); - goto unreg_region; + goto unreg_tco; } pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", @@ -891,62 +627,31 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, return 0; -unreg_region: - release_region(TCOBASE, 0x20); -unreg_smi_en: - release_region(SMI_EN, 4); -out_unmap: +unreg_tco: + release_region(iTCO_wdt_private.tco_res->start, + resource_size(iTCO_wdt_private.tco_res)); +unreg_smi: + release_region(iTCO_wdt_private.smi_res->start, + resource_size(iTCO_wdt_private.smi_res)); +unmap_gcs: if (iTCO_wdt_private.iTCO_version == 2) iounmap(iTCO_wdt_private.gcs); -out: - iTCO_wdt_private.ACPIBASE = 0; - return ret; -} - -static void __devexit iTCO_wdt_cleanup(void) -{ - /* Stop the timer before we leave */ - if (!nowayout) - iTCO_wdt_stop(); - - /* Deregister */ - misc_deregister(&iTCO_wdt_miscdev); - release_region(TCOBASE, 0x20); - release_region(SMI_EN, 4); +unreg_gcs: if (iTCO_wdt_private.iTCO_version == 2) - iounmap(iTCO_wdt_private.gcs); - pci_dev_put(iTCO_wdt_private.pdev); - iTCO_wdt_private.ACPIBASE = 0; -} - -static int __devinit iTCO_wdt_probe(struct platform_device *dev) -{ - int ret = -ENODEV; - int found = 0; - struct pci_dev *pdev = NULL; - const struct pci_device_id *ent; - - spin_lock_init(&iTCO_wdt_private.io_lock); - - for_each_pci_dev(pdev) { - ent = pci_match_id(iTCO_wdt_pci_tbl, pdev); - if (ent) { - found++; - ret = iTCO_wdt_init(pdev, ent, dev); - if (!ret) - break; - } - } - - if (!found) - pr_info("No device detected\n"); + release_mem_region(iTCO_wdt_private.gcs_res->start, + resource_size(iTCO_wdt_private.gcs_res)); +out: + iTCO_wdt_private.tco_res = NULL; + iTCO_wdt_private.smi_res = NULL; + iTCO_wdt_private.gcs_res = NULL; + iTCO_wdt_private.gcs = NULL; return ret; } static int __devexit iTCO_wdt_remove(struct platform_device *dev) { - if (iTCO_wdt_private.ACPIBASE) + if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res) iTCO_wdt_cleanup(); return 0; @@ -977,23 +682,11 @@ static int __init iTCO_wdt_init_module(void) if (err) return err; - iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME, - -1, NULL, 0); - if (IS_ERR(iTCO_wdt_platform_device)) { - err = PTR_ERR(iTCO_wdt_platform_device); - goto unreg_platform_driver; - } - return 0; - -unreg_platform_driver: - platform_driver_unregister(&iTCO_wdt_driver); - return err; } static void __exit iTCO_wdt_cleanup_module(void) { - platform_device_unregister(iTCO_wdt_platform_device); platform_driver_unregister(&iTCO_wdt_driver); pr_info("Watchdog Module Unloaded\n"); } diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index fccc3002f27..91dd3ef63e9 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -7,6 +7,7 @@ #ifndef MFD_AB8500_H #define MFD_AB8500_H +#include <linux/atomic.h> #include <linux/mutex.h> struct device; @@ -194,6 +195,14 @@ enum ab8500_version { #define AB9540_INT_GPIO52F 123 #define AB9540_INT_GPIO53F 124 #define AB9540_INT_GPIO54F 125 /* not 8505 */ +/* ab8500_irq_regoffset[16] -> IT[Source|Latch|Mask]25 */ +#define AB8505_INT_KEYSTUCK 128 +#define AB8505_INT_IKR 129 +#define AB8505_INT_IKP 130 +#define AB8505_INT_KP 131 +#define AB8505_INT_KEYDEGLITCH 132 +#define AB8505_INT_MODPWRSTATUSF 134 +#define AB8505_INT_MODPWRSTATUSR 135 /* * AB8500_AB9540_NR_IRQS is used when configuring the IRQ numbers for the @@ -203,8 +212,8 @@ enum ab8500_version { * which is larger. */ #define AB8500_NR_IRQS 112 -#define AB8505_NR_IRQS 128 -#define AB9540_NR_IRQS 128 +#define AB8505_NR_IRQS 136 +#define AB9540_NR_IRQS 136 /* This is set to the roof of any AB8500 chip variant IRQ counts */ #define AB8500_MAX_NR_IRQS AB9540_NR_IRQS @@ -216,6 +225,7 @@ enum ab8500_version { * @dev: parent device * @lock: read/write operations lock * @irq_lock: genirq bus lock + * @transfer_ongoing: 0 if no transfer ongoing * @irq: irq line * @version: chip version id (e.g. ab8500 or ab9540) * @chip_id: chip revision id @@ -234,7 +244,7 @@ struct ab8500 { struct device *dev; struct mutex lock; struct mutex irq_lock; - + atomic_t transfer_ongoing; int irq_base; int irq; enum ab8500_version version; @@ -280,6 +290,8 @@ extern int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version); extern int __devexit ab8500_exit(struct ab8500 *ab8500); +extern int ab8500_suspend(struct ab8500 *ab8500); + static inline int is_ab8500(struct ab8500 *ab) { return ab->version == AB8500_VERSION_AB8500; diff --git a/include/linux/mfd/anatop.h b/include/linux/mfd/anatop.h index 22c1007d3ec..7f92acf03d9 100644 --- a/include/linux/mfd/anatop.h +++ b/include/linux/mfd/anatop.h @@ -34,7 +34,7 @@ struct anatop { spinlock_t reglock; }; -extern u32 anatop_get_bits(struct anatop *, u32, int, int); -extern void anatop_set_bits(struct anatop *, u32, int, int, u32); +extern u32 anatop_read_reg(struct anatop *, u32); +extern void anatop_write_reg(struct anatop *, u32, u32, u32); #endif /* __LINUX_MFD_ANATOP_H */ diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index ef6faa5cee4..e1148d037e7 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -31,6 +31,8 @@ struct asic3_platform_data { unsigned int gpio_base; + unsigned int clock_rate; + struct asic3_led *leds; }; diff --git a/include/linux/mfd/da9052/da9052.h b/include/linux/mfd/da9052/da9052.h index 8313cd9658e..0507c4c21a7 100644 --- a/include/linux/mfd/da9052/da9052.h +++ b/include/linux/mfd/da9052/da9052.h @@ -33,6 +33,18 @@ #include <linux/mfd/da9052/reg.h> +/* Common - HWMON Channel Definations */ +#define DA9052_ADC_VDDOUT 0 +#define DA9052_ADC_ICH 1 +#define DA9052_ADC_TBAT 2 +#define DA9052_ADC_VBAT 3 +#define DA9052_ADC_IN4 4 +#define DA9052_ADC_IN5 5 +#define DA9052_ADC_IN6 6 +#define DA9052_ADC_TSI 7 +#define DA9052_ADC_TJUNC 8 +#define DA9052_ADC_VBBAT 9 + #define DA9052_IRQ_DCIN 0 #define DA9052_IRQ_VBUS 1 #define DA9052_IRQ_DCINREM 2 @@ -79,6 +91,9 @@ struct da9052 { struct device *dev; struct regmap *regmap; + struct mutex auxadc_lock; + struct completion done; + int irq_base; struct regmap_irq_chip_data *irq_data; u8 chip_id; @@ -86,6 +101,10 @@ struct da9052 { int chip_irq; }; +/* ADC API */ +int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel); +int da9052_adc_read_temp(struct da9052 *da9052); + /* Device I/O API */ static inline int da9052_reg_read(struct da9052 *da9052, unsigned char reg) { diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h new file mode 100644 index 00000000000..594bc591f25 --- /dev/null +++ b/include/linux/mfd/lm3533.h @@ -0,0 +1,104 @@ +/* + * lm3533.h -- LM3533 interface + * + * Copyright (C) 2011-2012 Texas Instruments + * + * Author: Johan Hovold <jhovold@gmail.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. + */ + +#ifndef __LINUX_MFD_LM3533_H +#define __LINUX_MFD_LM3533_H + +#define LM3533_ATTR_RO(_name) \ + DEVICE_ATTR(_name, S_IRUGO, show_##_name, NULL) +#define LM3533_ATTR_RW(_name) \ + DEVICE_ATTR(_name, S_IRUGO | S_IWUSR , show_##_name, store_##_name) + +struct device; +struct regmap; + +struct lm3533 { + struct device *dev; + + struct regmap *regmap; + + int gpio_hwen; + int irq; + + unsigned have_als:1; + unsigned have_backlights:1; + unsigned have_leds:1; +}; + +struct lm3533_ctrlbank { + struct lm3533 *lm3533; + struct device *dev; + int id; +}; + +struct lm3533_als_platform_data { + unsigned pwm_mode:1; /* PWM input mode (default analog) */ + u8 r_select; /* 1 - 127 (ignored in PWM-mode) */ +}; + +struct lm3533_bl_platform_data { + char *name; + u16 max_current; /* 5000 - 29800 uA (800 uA step) */ + u8 default_brightness; /* 0 - 255 */ + u8 pwm; /* 0 - 0x3f */ +}; + +struct lm3533_led_platform_data { + char *name; + const char *default_trigger; + u16 max_current; /* 5000 - 29800 uA (800 uA step) */ + u8 pwm; /* 0 - 0x3f */ +}; + +enum lm3533_boost_freq { + LM3533_BOOST_FREQ_500KHZ, + LM3533_BOOST_FREQ_1000KHZ, +}; + +enum lm3533_boost_ovp { + LM3533_BOOST_OVP_16V, + LM3533_BOOST_OVP_24V, + LM3533_BOOST_OVP_32V, + LM3533_BOOST_OVP_40V, +}; + +struct lm3533_platform_data { + int gpio_hwen; + + enum lm3533_boost_ovp boost_ovp; + enum lm3533_boost_freq boost_freq; + + struct lm3533_als_platform_data *als; + + struct lm3533_bl_platform_data *backlights; + int num_backlights; + + struct lm3533_led_platform_data *leds; + int num_leds; +}; + +extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb); +extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb); + +extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val); +extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val); +extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, + u16 imax); +extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val); +extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val); + +extern int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val); +extern int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val); +extern int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask); + +#endif /* __LINUX_MFD_LM3533_H */ diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h new file mode 100644 index 00000000000..fec5256c3f5 --- /dev/null +++ b/include/linux/mfd/lpc_ich.h @@ -0,0 +1,48 @@ +/* + * linux/drivers/mfd/lpc_ich.h + * + * Copyright (c) 2012 Extreme Engineering Solution, Inc. + * Author: Aaron Sierra <asierra@xes-inc.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef LPC_ICH_H +#define LPC_ICH_H + +/* Watchdog resources */ +#define ICH_RES_IO_TCO 0 +#define ICH_RES_IO_SMI 1 +#define ICH_RES_MEM_OFF 2 +#define ICH_RES_MEM_GCS 0 + +/* GPIO resources */ +#define ICH_RES_GPIO 0 +#define ICH_RES_GPE0 1 + +/* GPIO compatibility */ +#define ICH_I3100_GPIO 0x401 +#define ICH_V5_GPIO 0x501 +#define ICH_V6_GPIO 0x601 +#define ICH_V7_GPIO 0x701 +#define ICH_V9_GPIO 0x801 +#define ICH_V10CORP_GPIO 0xa01 +#define ICH_V10CONS_GPIO 0xa11 + +struct lpc_ich_info { + char name[32]; + unsigned int iTCO_version; + unsigned int gpio_version; +}; + +#endif diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h new file mode 100644 index 00000000000..68263c5fa53 --- /dev/null +++ b/include/linux/mfd/max77693-private.h @@ -0,0 +1,227 @@ +/* + * max77693-private.h - Voltage regulator driver for the Maxim 77693 + * + * Copyright (C) 2012 Samsung Electrnoics + * SangYoung Son <hello.son@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * 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 + */ + +#ifndef __LINUX_MFD_MAX77693_PRIV_H +#define __LINUX_MFD_MAX77693_PRIV_H + +#include <linux/i2c.h> + +#define MAX77693_NUM_IRQ_MUIC_REGS 3 +#define MAX77693_REG_INVALID (0xff) + +/* Slave addr = 0xCC: PMIC, Charger, Flash LED */ +enum max77693_pmic_reg { + MAX77693_LED_REG_IFLASH1 = 0x00, + MAX77693_LED_REG_IFLASH2 = 0x01, + MAX77693_LED_REG_ITORCH = 0x02, + MAX77693_LED_REG_ITORCHTIMER = 0x03, + MAX77693_LED_REG_FLASH_TIMER = 0x04, + MAX77693_LED_REG_FLASH_EN = 0x05, + MAX77693_LED_REG_MAX_FLASH1 = 0x06, + MAX77693_LED_REG_MAX_FLASH2 = 0x07, + MAX77693_LED_REG_MAX_FLASH3 = 0x08, + MAX77693_LED_REG_MAX_FLASH4 = 0x09, + MAX77693_LED_REG_VOUT_CNTL = 0x0A, + MAX77693_LED_REG_VOUT_FLASH1 = 0x0B, + MAX77693_LED_REG_VOUT_FLASH2 = 0x0C, + MAX77693_LED_REG_FLASH_INT = 0x0E, + MAX77693_LED_REG_FLASH_INT_MASK = 0x0F, + MAX77693_LED_REG_FLASH_INT_STATUS = 0x10, + + MAX77693_PMIC_REG_PMIC_ID1 = 0x20, + MAX77693_PMIC_REG_PMIC_ID2 = 0x21, + MAX77693_PMIC_REG_INTSRC = 0x22, + MAX77693_PMIC_REG_INTSRC_MASK = 0x23, + MAX77693_PMIC_REG_TOPSYS_INT = 0x24, + MAX77693_PMIC_REG_TOPSYS_INT_MASK = 0x26, + MAX77693_PMIC_REG_TOPSYS_STAT = 0x28, + MAX77693_PMIC_REG_MAINCTRL1 = 0x2A, + MAX77693_PMIC_REG_LSCNFG = 0x2B, + + MAX77693_CHG_REG_CHG_INT = 0xB0, + MAX77693_CHG_REG_CHG_INT_MASK = 0xB1, + MAX77693_CHG_REG_CHG_INT_OK = 0xB2, + MAX77693_CHG_REG_CHG_DETAILS_00 = 0xB3, + MAX77693_CHG_REG_CHG_DETAILS_01 = 0xB4, + MAX77693_CHG_REG_CHG_DETAILS_02 = 0xB5, + MAX77693_CHG_REG_CHG_DETAILS_03 = 0xB6, + MAX77693_CHG_REG_CHG_CNFG_00 = 0xB7, + MAX77693_CHG_REG_CHG_CNFG_01 = 0xB8, + MAX77693_CHG_REG_CHG_CNFG_02 = 0xB9, + MAX77693_CHG_REG_CHG_CNFG_03 = 0xBA, + MAX77693_CHG_REG_CHG_CNFG_04 = 0xBB, + MAX77693_CHG_REG_CHG_CNFG_05 = 0xBC, + MAX77693_CHG_REG_CHG_CNFG_06 = 0xBD, + MAX77693_CHG_REG_CHG_CNFG_07 = 0xBE, + MAX77693_CHG_REG_CHG_CNFG_08 = 0xBF, + MAX77693_CHG_REG_CHG_CNFG_09 = 0xC0, + MAX77693_CHG_REG_CHG_CNFG_10 = 0xC1, + MAX77693_CHG_REG_CHG_CNFG_11 = 0xC2, + MAX77693_CHG_REG_CHG_CNFG_12 = 0xC3, + MAX77693_CHG_REG_CHG_CNFG_13 = 0xC4, + MAX77693_CHG_REG_CHG_CNFG_14 = 0xC5, + MAX77693_CHG_REG_SAFEOUT_CTRL = 0xC6, + + MAX77693_PMIC_REG_END, +}; + +/* Slave addr = 0x4A: MUIC */ +enum max77693_muic_reg { + MAX77693_MUIC_REG_ID = 0x00, + MAX77693_MUIC_REG_INT1 = 0x01, + MAX77693_MUIC_REG_INT2 = 0x02, + MAX77693_MUIC_REG_INT3 = 0x03, + MAX77693_MUIC_REG_STATUS1 = 0x04, + MAX77693_MUIC_REG_STATUS2 = 0x05, + MAX77693_MUIC_REG_STATUS3 = 0x06, + MAX77693_MUIC_REG_INTMASK1 = 0x07, + MAX77693_MUIC_REG_INTMASK2 = 0x08, + MAX77693_MUIC_REG_INTMASK3 = 0x09, + MAX77693_MUIC_REG_CDETCTRL1 = 0x0A, + MAX77693_MUIC_REG_CDETCTRL2 = 0x0B, + MAX77693_MUIC_REG_CTRL1 = 0x0C, + MAX77693_MUIC_REG_CTRL2 = 0x0D, + MAX77693_MUIC_REG_CTRL3 = 0x0E, + + MAX77693_MUIC_REG_END, +}; + +/* Slave addr = 0x90: Haptic */ +enum max77693_haptic_reg { + MAX77693_HAPTIC_REG_STATUS = 0x00, + MAX77693_HAPTIC_REG_CONFIG1 = 0x01, + MAX77693_HAPTIC_REG_CONFIG2 = 0x02, + MAX77693_HAPTIC_REG_CONFIG_CHNL = 0x03, + MAX77693_HAPTIC_REG_CONFG_CYC1 = 0x04, + MAX77693_HAPTIC_REG_CONFG_CYC2 = 0x05, + MAX77693_HAPTIC_REG_CONFIG_PER1 = 0x06, + MAX77693_HAPTIC_REG_CONFIG_PER2 = 0x07, + MAX77693_HAPTIC_REG_CONFIG_PER3 = 0x08, + MAX77693_HAPTIC_REG_CONFIG_PER4 = 0x09, + MAX77693_HAPTIC_REG_CONFIG_DUTY1 = 0x0A, + MAX77693_HAPTIC_REG_CONFIG_DUTY2 = 0x0B, + MAX77693_HAPTIC_REG_CONFIG_PWM1 = 0x0C, + MAX77693_HAPTIC_REG_CONFIG_PWM2 = 0x0D, + MAX77693_HAPTIC_REG_CONFIG_PWM3 = 0x0E, + MAX77693_HAPTIC_REG_CONFIG_PWM4 = 0x0F, + MAX77693_HAPTIC_REG_REV = 0x10, + + MAX77693_HAPTIC_REG_END, +}; + +enum max77693_irq_source { + LED_INT = 0, + TOPSYS_INT, + CHG_INT, + MUIC_INT1, + MUIC_INT2, + MUIC_INT3, + + MAX77693_IRQ_GROUP_NR, +}; + +enum max77693_irq { + /* PMIC - FLASH */ + MAX77693_LED_IRQ_FLED2_OPEN, + MAX77693_LED_IRQ_FLED2_SHORT, + MAX77693_LED_IRQ_FLED1_OPEN, + MAX77693_LED_IRQ_FLED1_SHORT, + MAX77693_LED_IRQ_MAX_FLASH, + + /* PMIC - TOPSYS */ + MAX77693_TOPSYS_IRQ_T120C_INT, + MAX77693_TOPSYS_IRQ_T140C_INT, + MAX77693_TOPSYS_IRQ_LOWSYS_INT, + + /* PMIC - Charger */ + MAX77693_CHG_IRQ_BYP_I, + MAX77693_CHG_IRQ_THM_I, + MAX77693_CHG_IRQ_BAT_I, + MAX77693_CHG_IRQ_CHG_I, + MAX77693_CHG_IRQ_CHGIN_I, + + /* MUIC INT1 */ + MAX77693_MUIC_IRQ_INT1_ADC, + MAX77693_MUIC_IRQ_INT1_ADC_LOW, + MAX77693_MUIC_IRQ_INT1_ADC_ERR, + MAX77693_MUIC_IRQ_INT1_ADC1K, + + /* MUIC INT2 */ + MAX77693_MUIC_IRQ_INT2_CHGTYP, + MAX77693_MUIC_IRQ_INT2_CHGDETREUN, + MAX77693_MUIC_IRQ_INT2_DCDTMR, + MAX77693_MUIC_IRQ_INT2_DXOVP, + MAX77693_MUIC_IRQ_INT2_VBVOLT, + MAX77693_MUIC_IRQ_INT2_VIDRM, + + /* MUIC INT3 */ + MAX77693_MUIC_IRQ_INT3_EOC, + MAX77693_MUIC_IRQ_INT3_CGMBC, + MAX77693_MUIC_IRQ_INT3_OVP, + MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR, + MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, + MAX77693_MUIC_IRQ_INT3_BAT_DET, + + MAX77693_IRQ_NR, +}; + +struct max77693_dev { + struct device *dev; + struct i2c_client *i2c; /* 0xCC , PMIC, Charger, Flash LED */ + struct i2c_client *muic; /* 0x4A , MUIC */ + struct i2c_client *haptic; /* 0x90 , Haptic */ + struct mutex iolock; + + int type; + + struct regmap *regmap; + struct regmap *regmap_muic; + struct regmap *regmap_haptic; + + struct irq_domain *irq_domain; + + int irq; + int irq_gpio; + bool wakeup; + struct mutex irqlock; + int irq_masks_cur[MAX77693_IRQ_GROUP_NR]; + int irq_masks_cache[MAX77693_IRQ_GROUP_NR]; +}; + +enum max77693_types { + TYPE_MAX77693, +}; + +extern int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest); +extern int max77693_bulk_read(struct regmap *map, u8 reg, int count, + u8 *buf); +extern int max77693_write_reg(struct regmap *map, u8 reg, u8 value); +extern int max77693_bulk_write(struct regmap *map, u8 reg, int count, + u8 *buf); +extern int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask); + +extern int max77693_irq_init(struct max77693_dev *max77686); +extern void max77693_irq_exit(struct max77693_dev *max77686); +extern int max77693_irq_resume(struct max77693_dev *max77686); + +#endif /* __LINUX_MFD_MAX77693_PRIV_H */ diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h new file mode 100644 index 00000000000..1d28ae90384 --- /dev/null +++ b/include/linux/mfd/max77693.h @@ -0,0 +1,36 @@ +/* + * max77693.h - Driver for the Maxim 77693 + * + * Copyright (C) 2012 Samsung Electrnoics + * SangYoung Son <hello.son@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * 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 + * + * This driver is based on max8997.h + * + * MAX77693 has PMIC, Charger, Flash LED, Haptic, MUIC devices. + * The devices share the same I2C bus and included in + * this mfd driver. + */ + +#ifndef __LINUX_MFD_MAX77693_H +#define __LINUX_MFD_MAX77693_H + +struct max77693_platform_data { + int wakeup; +}; +#endif /* __LINUX_MFD_MAX77693_H */ diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h new file mode 100644 index 00000000000..d179227e866 --- /dev/null +++ b/include/linux/mfd/sta2x11-mfd.h @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2009-2011 Wind River Systems, Inc. + * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * The STMicroelectronics ConneXt (STA2X11) chip has several unrelated + * functions in one PCI endpoint functions. This driver simply + * registers the platform devices in this iomemregion and exports a few + * functions to access common registers + */ + +#ifndef __STA2X11_MFD_H +#define __STA2X11_MFD_H +#include <linux/types.h> +#include <linux/pci.h> + +/* + * The MFD PCI block includes the GPIO peripherals and other register blocks. + * For GPIO, we have 32*4 bits (I use "gsta" for "gpio sta2x11".) + */ +#define GSTA_GPIO_PER_BLOCK 32 +#define GSTA_NR_BLOCKS 4 +#define GSTA_NR_GPIO (GSTA_GPIO_PER_BLOCK * GSTA_NR_BLOCKS) + +/* Pinconfig is set by the board definition: altfunc, pull-up, pull-down */ +struct sta2x11_gpio_pdata { + unsigned pinconfig[GSTA_NR_GPIO]; +}; + +/* Macros below lifted from sh_pfc.h, with minor differences */ +#define PINMUX_TYPE_NONE 0 +#define PINMUX_TYPE_FUNCTION 1 +#define PINMUX_TYPE_OUTPUT_LOW 2 +#define PINMUX_TYPE_OUTPUT_HIGH 3 +#define PINMUX_TYPE_INPUT 4 +#define PINMUX_TYPE_INPUT_PULLUP 5 +#define PINMUX_TYPE_INPUT_PULLDOWN 6 + +/* Give names to GPIO pins, like PXA does, taken from the manual */ +#define STA2X11_GPIO0 0 +#define STA2X11_GPIO1 1 +#define STA2X11_GPIO2 2 +#define STA2X11_GPIO3 3 +#define STA2X11_GPIO4 4 +#define STA2X11_GPIO5 5 +#define STA2X11_GPIO6 6 +#define STA2X11_GPIO7 7 +#define STA2X11_GPIO8_RGBOUT_RED7 8 +#define STA2X11_GPIO9_RGBOUT_RED6 9 +#define STA2X11_GPIO10_RGBOUT_RED5 10 +#define STA2X11_GPIO11_RGBOUT_RED4 11 +#define STA2X11_GPIO12_RGBOUT_RED3 12 +#define STA2X11_GPIO13_RGBOUT_RED2 13 +#define STA2X11_GPIO14_RGBOUT_RED1 14 +#define STA2X11_GPIO15_RGBOUT_RED0 15 +#define STA2X11_GPIO16_RGBOUT_GREEN7 16 +#define STA2X11_GPIO17_RGBOUT_GREEN6 17 +#define STA2X11_GPIO18_RGBOUT_GREEN5 18 +#define STA2X11_GPIO19_RGBOUT_GREEN4 19 +#define STA2X11_GPIO20_RGBOUT_GREEN3 20 +#define STA2X11_GPIO21_RGBOUT_GREEN2 21 +#define STA2X11_GPIO22_RGBOUT_GREEN1 22 +#define STA2X11_GPIO23_RGBOUT_GREEN0 23 +#define STA2X11_GPIO24_RGBOUT_BLUE7 24 +#define STA2X11_GPIO25_RGBOUT_BLUE6 25 +#define STA2X11_GPIO26_RGBOUT_BLUE5 26 +#define STA2X11_GPIO27_RGBOUT_BLUE4 27 +#define STA2X11_GPIO28_RGBOUT_BLUE3 28 +#define STA2X11_GPIO29_RGBOUT_BLUE2 29 +#define STA2X11_GPIO30_RGBOUT_BLUE1 30 +#define STA2X11_GPIO31_RGBOUT_BLUE0 31 +#define STA2X11_GPIO32_RGBOUT_VSYNCH 32 +#define STA2X11_GPIO33_RGBOUT_HSYNCH 33 +#define STA2X11_GPIO34_RGBOUT_DEN 34 +#define STA2X11_GPIO35_ETH_CRS_DV 35 +#define STA2X11_GPIO36_ETH_TXD1 36 +#define STA2X11_GPIO37_ETH_TXD0 37 +#define STA2X11_GPIO38_ETH_TX_EN 38 +#define STA2X11_GPIO39_MDIO 39 +#define STA2X11_GPIO40_ETH_REF_CLK 40 +#define STA2X11_GPIO41_ETH_RXD1 41 +#define STA2X11_GPIO42_ETH_RXD0 42 +#define STA2X11_GPIO43_MDC 43 +#define STA2X11_GPIO44_CAN_TX 44 +#define STA2X11_GPIO45_CAN_RX 45 +#define STA2X11_GPIO46_MLB_DAT 46 +#define STA2X11_GPIO47_MLB_SIG 47 +#define STA2X11_GPIO48_SPI0_CLK 48 +#define STA2X11_GPIO49_SPI0_TXD 49 +#define STA2X11_GPIO50_SPI0_RXD 50 +#define STA2X11_GPIO51_SPI0_FRM 51 +#define STA2X11_GPIO52_SPI1_CLK 52 +#define STA2X11_GPIO53_SPI1_TXD 53 +#define STA2X11_GPIO54_SPI1_RXD 54 +#define STA2X11_GPIO55_SPI1_FRM 55 +#define STA2X11_GPIO56_SPI2_CLK 56 +#define STA2X11_GPIO57_SPI2_TXD 57 +#define STA2X11_GPIO58_SPI2_RXD 58 +#define STA2X11_GPIO59_SPI2_FRM 59 +#define STA2X11_GPIO60_I2C0_SCL 60 +#define STA2X11_GPIO61_I2C0_SDA 61 +#define STA2X11_GPIO62_I2C1_SCL 62 +#define STA2X11_GPIO63_I2C1_SDA 63 +#define STA2X11_GPIO64_I2C2_SCL 64 +#define STA2X11_GPIO65_I2C2_SDA 65 +#define STA2X11_GPIO66_I2C3_SCL 66 +#define STA2X11_GPIO67_I2C3_SDA 67 +#define STA2X11_GPIO68_MSP0_RCK 68 +#define STA2X11_GPIO69_MSP0_RXD 69 +#define STA2X11_GPIO70_MSP0_RFS 70 +#define STA2X11_GPIO71_MSP0_TCK 71 +#define STA2X11_GPIO72_MSP0_TXD 72 +#define STA2X11_GPIO73_MSP0_TFS 73 +#define STA2X11_GPIO74_MSP0_SCK 74 +#define STA2X11_GPIO75_MSP1_CK 75 +#define STA2X11_GPIO76_MSP1_RXD 76 +#define STA2X11_GPIO77_MSP1_FS 77 +#define STA2X11_GPIO78_MSP1_TXD 78 +#define STA2X11_GPIO79_MSP2_CK 79 +#define STA2X11_GPIO80_MSP2_RXD 80 +#define STA2X11_GPIO81_MSP2_FS 81 +#define STA2X11_GPIO82_MSP2_TXD 82 +#define STA2X11_GPIO83_MSP3_CK 83 +#define STA2X11_GPIO84_MSP3_RXD 84 +#define STA2X11_GPIO85_MSP3_FS 85 +#define STA2X11_GPIO86_MSP3_TXD 86 +#define STA2X11_GPIO87_MSP4_CK 87 +#define STA2X11_GPIO88_MSP4_RXD 88 +#define STA2X11_GPIO89_MSP4_FS 89 +#define STA2X11_GPIO90_MSP4_TXD 90 +#define STA2X11_GPIO91_MSP5_CK 91 +#define STA2X11_GPIO92_MSP5_RXD 92 +#define STA2X11_GPIO93_MSP5_FS 93 +#define STA2X11_GPIO94_MSP5_TXD 94 +#define STA2X11_GPIO95_SDIO3_DAT3 95 +#define STA2X11_GPIO96_SDIO3_DAT2 96 +#define STA2X11_GPIO97_SDIO3_DAT1 97 +#define STA2X11_GPIO98_SDIO3_DAT0 98 +#define STA2X11_GPIO99_SDIO3_CLK 99 +#define STA2X11_GPIO100_SDIO3_CMD 100 +#define STA2X11_GPIO101 101 +#define STA2X11_GPIO102 102 +#define STA2X11_GPIO103 103 +#define STA2X11_GPIO104 104 +#define STA2X11_GPIO105_SDIO2_DAT3 105 +#define STA2X11_GPIO106_SDIO2_DAT2 106 +#define STA2X11_GPIO107_SDIO2_DAT1 107 +#define STA2X11_GPIO108_SDIO2_DAT0 108 +#define STA2X11_GPIO109_SDIO2_CLK 109 +#define STA2X11_GPIO110_SDIO2_CMD 110 +#define STA2X11_GPIO111 111 +#define STA2X11_GPIO112 112 +#define STA2X11_GPIO113 113 +#define STA2X11_GPIO114 114 +#define STA2X11_GPIO115_SDIO1_DAT3 115 +#define STA2X11_GPIO116_SDIO1_DAT2 116 +#define STA2X11_GPIO117_SDIO1_DAT1 117 +#define STA2X11_GPIO118_SDIO1_DAT0 118 +#define STA2X11_GPIO119_SDIO1_CLK 119 +#define STA2X11_GPIO120_SDIO1_CMD 120 +#define STA2X11_GPIO121 121 +#define STA2X11_GPIO122 122 +#define STA2X11_GPIO123 123 +#define STA2X11_GPIO124 124 +#define STA2X11_GPIO125_UART2_TXD 125 +#define STA2X11_GPIO126_UART2_RXD 126 +#define STA2X11_GPIO127_UART3_TXD 127 + +/* + * The APB bridge has its own registers, needed by our users as well. + * They are accessed with the following read/mask/write function. + */ +u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val); + +/* CAN and MLB */ +#define APBREG_BSR 0x00 /* Bridge Status Reg */ +#define APBREG_PAER 0x08 /* Peripherals Address Error Reg */ +#define APBREG_PWAC 0x20 /* Peripheral Write Access Control reg */ +#define APBREG_PRAC 0x40 /* Peripheral Read Access Control reg */ +#define APBREG_PCG 0x60 /* Peripheral Clock Gating Reg */ +#define APBREG_PUR 0x80 /* Peripheral Under Reset Reg */ +#define APBREG_EMU_PCG 0xA0 /* Emulator Peripheral Clock Gating Reg */ + +#define APBREG_CAN (1 << 1) +#define APBREG_MLB (1 << 3) + +/* SARAC */ +#define APBREG_BSR_SARAC 0x100 /* Bridge Status Reg */ +#define APBREG_PAER_SARAC 0x108 /* Peripherals Address Error Reg */ +#define APBREG_PWAC_SARAC 0x120 /* Peripheral Write Access Control reg */ +#define APBREG_PRAC_SARAC 0x140 /* Peripheral Read Access Control reg */ +#define APBREG_PCG_SARAC 0x160 /* Peripheral Clock Gating Reg */ +#define APBREG_PUR_SARAC 0x180 /* Peripheral Under Reset Reg */ +#define APBREG_EMU_PCG_SARAC 0x1A0 /* Emulator Peripheral Clock Gating Reg */ + +#define APBREG_SARAC (1 << 2) + +/* + * The system controller has its own registers. Some of these are accessed + * by out users as well, using the following read/mask/write/function + */ +u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val); + +#define SCTL_SCCTL 0x00 /* System controller control register */ +#define SCTL_ARMCFG 0x04 /* ARM configuration register */ +#define SCTL_SCPLLCTL 0x08 /* PLL control status register */ +#define SCTL_SCPLLFCTRL 0x0c /* PLL frequency control register */ +#define SCTL_SCRESFRACT 0x10 /* PLL fractional input register */ +#define SCTL_SCRESCTRL1 0x14 /* Peripheral reset control 1 */ +#define SCTL_SCRESXTRL2 0x18 /* Peripheral reset control 2 */ +#define SCTL_SCPEREN0 0x1c /* Peripheral clock enable register 0 */ +#define SCTL_SCPEREN1 0x20 /* Peripheral clock enable register 1 */ +#define SCTL_SCPEREN2 0x24 /* Peripheral clock enable register 2 */ +#define SCTL_SCGRST 0x28 /* Peripheral global reset */ +#define SCTL_SCPCIPMCR1 0x30 /* PCI power management control 1 */ +#define SCTL_SCPCIPMCR2 0x34 /* PCI power management control 2 */ +#define SCTL_SCPCIPMSR1 0x38 /* PCI power management status 1 */ +#define SCTL_SCPCIPMSR2 0x3c /* PCI power management status 2 */ +#define SCTL_SCPCIPMSR3 0x40 /* PCI power management status 3 */ +#define SCTL_SCINTREN 0x44 /* Interrupt enable */ +#define SCTL_SCRISR 0x48 /* RAW interrupt status */ +#define SCTL_SCCLKSTAT0 0x4c /* Peripheral clocks status 0 */ +#define SCTL_SCCLKSTAT1 0x50 /* Peripheral clocks status 1 */ +#define SCTL_SCCLKSTAT2 0x54 /* Peripheral clocks status 2 */ +#define SCTL_SCRSTSTA 0x58 /* Reset status register */ + +#define SCTL_SCRESCTRL1_USB_PHY_POR (1 << 0) +#define SCTL_SCRESCTRL1_USB_OTG (1 << 1) +#define SCTL_SCRESCTRL1_USB_HRST (1 << 2) +#define SCTL_SCRESCTRL1_USB_PHY_HOST (1 << 3) +#define SCTL_SCRESCTRL1_SATAII (1 << 4) +#define SCTL_SCRESCTRL1_VIP (1 << 5) +#define SCTL_SCRESCTRL1_PER_MMC0 (1 << 6) +#define SCTL_SCRESCTRL1_PER_MMC1 (1 << 7) +#define SCTL_SCRESCTRL1_PER_GPIO0 (1 << 8) +#define SCTL_SCRESCTRL1_PER_GPIO1 (1 << 9) +#define SCTL_SCRESCTRL1_PER_GPIO2 (1 << 10) +#define SCTL_SCRESCTRL1_PER_GPIO3 (1 << 11) +#define SCTL_SCRESCTRL1_PER_MTU0 (1 << 12) +#define SCTL_SCRESCTRL1_KER_SPI0 (1 << 13) +#define SCTL_SCRESCTRL1_KER_SPI1 (1 << 14) +#define SCTL_SCRESCTRL1_KER_SPI2 (1 << 15) +#define SCTL_SCRESCTRL1_KER_MCI0 (1 << 16) +#define SCTL_SCRESCTRL1_KER_MCI1 (1 << 17) +#define SCTL_SCRESCTRL1_PRE_HSI2C0 (1 << 18) +#define SCTL_SCRESCTRL1_PER_HSI2C1 (1 << 19) +#define SCTL_SCRESCTRL1_PER_HSI2C2 (1 << 20) +#define SCTL_SCRESCTRL1_PER_HSI2C3 (1 << 21) +#define SCTL_SCRESCTRL1_PER_MSP0 (1 << 22) +#define SCTL_SCRESCTRL1_PER_MSP1 (1 << 23) +#define SCTL_SCRESCTRL1_PER_MSP2 (1 << 24) +#define SCTL_SCRESCTRL1_PER_MSP3 (1 << 25) +#define SCTL_SCRESCTRL1_PER_MSP4 (1 << 26) +#define SCTL_SCRESCTRL1_PER_MSP5 (1 << 27) +#define SCTL_SCRESCTRL1_PER_MMC (1 << 28) +#define SCTL_SCRESCTRL1_KER_MSP0 (1 << 29) +#define SCTL_SCRESCTRL1_KER_MSP1 (1 << 30) +#define SCTL_SCRESCTRL1_KER_MSP2 (1 << 31) + +#define SCTL_SCPEREN0_UART0 (1 << 0) +#define SCTL_SCPEREN0_UART1 (1 << 1) +#define SCTL_SCPEREN0_UART2 (1 << 2) +#define SCTL_SCPEREN0_UART3 (1 << 3) +#define SCTL_SCPEREN0_MSP0 (1 << 4) +#define SCTL_SCPEREN0_MSP1 (1 << 5) +#define SCTL_SCPEREN0_MSP2 (1 << 6) +#define SCTL_SCPEREN0_MSP3 (1 << 7) +#define SCTL_SCPEREN0_MSP4 (1 << 8) +#define SCTL_SCPEREN0_MSP5 (1 << 9) +#define SCTL_SCPEREN0_SPI0 (1 << 10) +#define SCTL_SCPEREN0_SPI1 (1 << 11) +#define SCTL_SCPEREN0_SPI2 (1 << 12) +#define SCTL_SCPEREN0_I2C0 (1 << 13) +#define SCTL_SCPEREN0_I2C1 (1 << 14) +#define SCTL_SCPEREN0_I2C2 (1 << 15) +#define SCTL_SCPEREN0_I2C3 (1 << 16) +#define SCTL_SCPEREN0_SVDO_LVDS (1 << 17) +#define SCTL_SCPEREN0_USB_HOST (1 << 18) +#define SCTL_SCPEREN0_USB_OTG (1 << 19) +#define SCTL_SCPEREN0_MCI0 (1 << 20) +#define SCTL_SCPEREN0_MCI1 (1 << 21) +#define SCTL_SCPEREN0_MCI2 (1 << 22) +#define SCTL_SCPEREN0_MCI3 (1 << 23) +#define SCTL_SCPEREN0_SATA (1 << 24) +#define SCTL_SCPEREN0_ETHERNET (1 << 25) +#define SCTL_SCPEREN0_VIC (1 << 26) +#define SCTL_SCPEREN0_DMA_AUDIO (1 << 27) +#define SCTL_SCPEREN0_DMA_SOC (1 << 28) +#define SCTL_SCPEREN0_RAM (1 << 29) +#define SCTL_SCPEREN0_VIP (1 << 30) +#define SCTL_SCPEREN0_ARM (1 << 31) + +#define SCTL_SCPEREN1_UART0 (1 << 0) +#define SCTL_SCPEREN1_UART1 (1 << 1) +#define SCTL_SCPEREN1_UART2 (1 << 2) +#define SCTL_SCPEREN1_UART3 (1 << 3) +#define SCTL_SCPEREN1_MSP0 (1 << 4) +#define SCTL_SCPEREN1_MSP1 (1 << 5) +#define SCTL_SCPEREN1_MSP2 (1 << 6) +#define SCTL_SCPEREN1_MSP3 (1 << 7) +#define SCTL_SCPEREN1_MSP4 (1 << 8) +#define SCTL_SCPEREN1_MSP5 (1 << 9) +#define SCTL_SCPEREN1_SPI0 (1 << 10) +#define SCTL_SCPEREN1_SPI1 (1 << 11) +#define SCTL_SCPEREN1_SPI2 (1 << 12) +#define SCTL_SCPEREN1_I2C0 (1 << 13) +#define SCTL_SCPEREN1_I2C1 (1 << 14) +#define SCTL_SCPEREN1_I2C2 (1 << 15) +#define SCTL_SCPEREN1_I2C3 (1 << 16) +#define SCTL_SCPEREN1_USB_PHY (1 << 17) + +#endif /* __STA2X11_MFD_H */ diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 8516fd1eaab..f8d5b4d5843 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -117,7 +117,7 @@ struct matrix_keymap_data; * @no_autorepeat: disable key autorepeat */ struct stmpe_keypad_platform_data { - struct matrix_keymap_data *keymap_data; + const struct matrix_keymap_data *keymap_data; unsigned int debounce_ms; unsigned int scan_count; bool no_autorepeat; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 1c6c2860d1a..dd8dc0a6c46 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -18,6 +18,7 @@ #define __LINUX_MFD_TPS65910_H #include <linux/gpio.h> +#include <linux/regmap.h> /* TPS chip id list */ #define TPS65910 0 @@ -783,6 +784,18 @@ #define TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3 0x4 #define TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP 0x8 +/* + * Sleep keepon data: Maintains the state in sleep mode + * @therm_keepon: Keep on the thermal monitoring in sleep state. + * @clkout32k_keepon: Keep on the 32KHz clock output in sleep state. + * @i2chs_keepon: Keep on high speed internal clock in sleep state. + */ +struct tps65910_sleep_keepon_data { + unsigned therm_keepon:1; + unsigned clkout32k_keepon:1; + unsigned i2chs_keepon:1; +}; + /** * struct tps65910_board * Board platform data may be used to initialize regulators. @@ -794,6 +807,8 @@ struct tps65910_board { int irq_base; int vmbch_threshold; int vmbch2_threshold; + bool en_dev_slp; + struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; unsigned long regulator_ext_sleep_control[TPS65910_NUM_REGS]; struct regulator_init_data *tps65910_pmic_init_data[TPS65910_NUM_REGS]; @@ -809,16 +824,14 @@ struct tps65910 { struct regmap *regmap; struct mutex io_mutex; unsigned int id; - int (*read)(struct tps65910 *tps65910, u8 reg, int size, void *dest); - int (*write)(struct tps65910 *tps65910, u8 reg, int size, void *src); /* Client devices */ struct tps65910_pmic *pmic; struct tps65910_rtc *rtc; struct tps65910_power *power; - /* GPIO Handling */ - struct gpio_chip gpio; + /* Device node parsed board data */ + struct tps65910_board *of_plat_data; /* IRQ Handling */ struct mutex irq_lock; @@ -826,6 +839,7 @@ struct tps65910 { int irq_base; int irq_num; u32 irq_mask; + struct irq_domain *domain; }; struct tps65910_platform_data { @@ -833,9 +847,6 @@ struct tps65910_platform_data { int irq_base; }; -int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask); -int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask); -void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base); int tps65910_irq_init(struct tps65910 *tps65910, int irq, struct tps65910_platform_data *pdata); int tps65910_irq_exit(struct tps65910 *tps65910); @@ -845,4 +856,28 @@ static inline int tps65910_chip_id(struct tps65910 *tps65910) return tps65910->id; } +static inline int tps65910_reg_read(struct tps65910 *tps65910, u8 reg, + unsigned int *val) +{ + return regmap_read(tps65910->regmap, reg, val); +} + +static inline int tps65910_reg_write(struct tps65910 *tps65910, u8 reg, + unsigned int val) +{ + return regmap_write(tps65910->regmap, reg, val); +} + +static inline int tps65910_reg_set_bits(struct tps65910 *tps65910, u8 reg, + u8 mask) +{ + return regmap_update_bits(tps65910->regmap, reg, mask, mask); +} + +static inline int tps65910_reg_clear_bits(struct tps65910 *tps65910, u8 reg, + u8 mask) +{ + return regmap_update_bits(tps65910->regmap, reg, mask, 0); +} + #endif /* __LINUX_MFD_TPS65910_H */ diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index b15b5f03f5c..6659487c31e 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -27,6 +27,7 @@ #include <linux/interrupt.h> #include <linux/mfd/core.h> +#include <linux/regulator/consumer.h> #define TWL6040_REG_ASICID 0x01 #define TWL6040_REG_ASICREV 0x02 @@ -203,6 +204,7 @@ struct regmap; struct twl6040 { struct device *dev; struct regmap *regmap; + struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */ struct mutex mutex; struct mutex io_mutex; struct mutex irq_mutex; diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 4b1211859f7..4a3b83a7761 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -17,6 +17,7 @@ #include <linux/completion.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/list.h> #include <linux/regmap.h> @@ -338,6 +339,7 @@ #define WM831X_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */ struct regulator_dev; +struct irq_domain; #define WM831X_NUM_IRQ_REGS 5 #define WM831X_NUM_GPIO_REGS 16 @@ -367,7 +369,7 @@ struct wm831x { int irq; /* Our chip IRQ */ struct mutex irq_lock; - int irq_base; + struct irq_domain *irq_domain; int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */ int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ @@ -382,7 +384,8 @@ struct wm831x { /* Used by the interrupt controller code to post writes */ int gpio_update[WM831X_NUM_GPIO_REGS]; - bool gpio_level[WM831X_NUM_GPIO_REGS]; + bool gpio_level_high[WM831X_NUM_GPIO_REGS]; + bool gpio_level_low[WM831X_NUM_GPIO_REGS]; struct mutex auxadc_lock; struct list_head auxadc_pending; @@ -417,6 +420,11 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq); void wm831x_irq_exit(struct wm831x *wm831x); void wm831x_auxadc_init(struct wm831x *wm831x); +static inline int wm831x_irq(struct wm831x *wm831x, int irq) +{ + return irq_create_mapping(wm831x->irq_domain, irq); +} + extern struct regmap_config wm831x_regmap_config; #endif diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 98fcc977e82..9192b6404a7 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -602,6 +602,7 @@ extern const u16 wm8352_mode2_defaults[]; extern const u16 wm8352_mode3_defaults[]; struct wm8350; +struct regmap; struct wm8350_hwmon { struct platform_device *pdev; @@ -612,13 +613,7 @@ struct wm8350 { struct device *dev; /* device IO */ - union { - struct i2c_client *i2c_client; - struct spi_device *spi_device; - }; - int (*read_dev)(struct wm8350 *wm8350, char reg, int size, void *dest); - int (*write_dev)(struct wm8350 *wm8350, char reg, int size, - void *src); + struct regmap *regmap; u16 *reg_cache; struct mutex auxadc_mutex; diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h index 0147b696851..2de565b94d0 100644 --- a/include/linux/mfd/wm8400-private.h +++ b/include/linux/mfd/wm8400-private.h @@ -24,19 +24,14 @@ #include <linux/mfd/wm8400.h> #include <linux/mutex.h> #include <linux/platform_device.h> - -struct regmap; +#include <linux/regmap.h> #define WM8400_REGISTER_COUNT 0x55 struct wm8400 { struct device *dev; - - struct mutex io_lock; struct regmap *regmap; - u16 reg_cache[WM8400_REGISTER_COUNT]; - struct platform_device regulators[6]; }; @@ -930,6 +925,11 @@ struct wm8400 { u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg); int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data); -int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val); + +static inline int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, + u16 mask, u16 val) +{ + return regmap_update_bits(wm8400->regmap, reg, mask, val); +} #endif diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index 6695c3ec451..1f173306bf0 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -57,6 +57,7 @@ struct wm8994 { enum wm8994_type type; int revision; + int cust_id; struct device *dev; struct regmap *regmap; diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h index 86e6a032a07..053548961c1 100644 --- a/include/linux/mfd/wm8994/registers.h +++ b/include/linux/mfd/wm8994/registers.h @@ -2212,6 +2212,9 @@ /* * R256 (0x100) - Chip Revision */ +#define WM8994_CUST_ID_MASK 0xFF00 /* CUST_ID - [15:8] */ +#define WM8994_CUST_ID_SHIFT 8 /* CUST_ID - [15:8] */ +#define WM8994_CUST_ID_WIDTH 8 /* CUST_ID - [15:8] */ #define WM8994_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */ #define WM8994_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */ #define WM8994_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3329965ed63..ab741b0d007 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2506,6 +2506,7 @@ #define PCI_DEVICE_ID_INTEL_MRST_SD2 0x084F #define PCI_DEVICE_ID_INTEL_I960 0x0960 #define PCI_DEVICE_ID_INTEL_I960RM 0x0962 +#define PCI_DEVICE_ID_INTEL_CENTERTON_ILB 0x0c60 #define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062 #define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085 #define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F |