diff options
88 files changed, 8956 insertions, 1317 deletions
diff --git a/Documentation/devicetree/bindings/input/cros-ec-keyb.txt b/Documentation/devicetree/bindings/input/cros-ec-keyb.txt new file mode 100644 index 00000000000..0f6355ce39b --- /dev/null +++ b/Documentation/devicetree/bindings/input/cros-ec-keyb.txt @@ -0,0 +1,72 @@ +ChromeOS EC Keyboard + +Google's ChromeOS EC Keyboard is a simple matrix keyboard implemented on +a separate EC (Embedded Controller) device. It provides a message for reading +key scans from the EC. These are then converted into keycodes for processing +by the kernel. + +This binding is based on matrix-keymap.txt and extends/modifies it as follows: + +Required properties: +- compatible: "google,cros-ec-keyb" + +Optional properties: +- google,needs-ghost-filter: True to enable a ghost filter for the matrix +keyboard. This is recommended if the EC does not have its own logic or +hardware for this. + + +Example: + +cros-ec-keyb { +	compatible = "google,cros-ec-keyb"; +	keypad,num-rows = <8>; +	keypad,num-columns = <13>; +	google,needs-ghost-filter; +	/* +	 * Keymap entries take the form of 0xRRCCKKKK where +	 * RR=Row CC=Column KKKK=Key Code +	 * The values below are for a US keyboard layout and +	 * are taken from the Linux driver. Note that the +	 * 102ND key is not used for US keyboards. +	 */ +	linux,keymap = < +		/* CAPSLCK F1         B          F10     */ +		0x0001003a 0x0002003b 0x00030030 0x00040044 +		/* N       =          R_ALT      ESC     */ +		0x00060031 0x0008000d 0x000a0064 0x01010001 +		/* F4      G          F7         H       */ +		0x0102003e 0x01030022 0x01040041 0x01060023 +		/* '       F9         BKSPACE    L_CTRL  */ +		0x01080028 0x01090043 0x010b000e 0x0200001d +		/* TAB     F3         T          F6      */ +		0x0201000f 0x0202003d 0x02030014 0x02040040 +		/* ]       Y          102ND      [       */ +		0x0205001b 0x02060015 0x02070056 0x0208001a +		/* F8      GRAVE      F2         5       */ +		0x02090042 0x03010029 0x0302003c 0x03030006 +		/* F5      6          -          \       */ +		0x0304003f 0x03060007 0x0308000c 0x030b002b +		/* R_CTRL  A          D          F       */ +		0x04000061 0x0401001e 0x04020020 0x04030021 +		/* S       K          J          ;       */ +		0x0404001f 0x04050025 0x04060024 0x04080027 +		/* L       ENTER      Z          C       */ +		0x04090026 0x040b001c 0x0501002c 0x0502002e +		/* V       X          ,          M       */ +		0x0503002f 0x0504002d 0x05050033 0x05060032 +		/* L_SHIFT /          .          SPACE   */ +		0x0507002a 0x05080035 0x05090034 0x050B0039 +		/* 1       3          4          2       */ +		0x06010002 0x06020004 0x06030005 0x06040003 +		/* 8       7          0          9       */ +		0x06050009 0x06060008 0x0608000b 0x0609000a +		/* L_ALT   DOWN       RIGHT      Q       */ +		0x060a0038 0x060b006c 0x060c006a 0x07010010 +		/* E       R          W          I       */ +		0x07020012 0x07030013 0x07040011 0x07050017 +		/* U       R_SHIFT    P          O       */ +		0x07060016 0x07070036 0x07080019 0x07090018 +		/* UP      LEFT    */ +		0x070b0067 0x070c0069>; +}; diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt new file mode 100644 index 00000000000..d98cf18c721 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/as3711.txt @@ -0,0 +1,73 @@ +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power +supplies, a battery charger and an RTC. So far only bindings for the two stepup +DCDC converters are defined. Other DCDC and LDO supplies are configured, using +standard regulator properties, they must belong to a sub-node, called +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter +configuration should be placed in a subnode, called "backlight." + +Compulsory properties: +- compatible		: must be "ams,as3711" +- reg			: specifies the I2C address + +To use the SU1 converter as a backlight source the following two properties must +be provided: +- su1-dev		: framebuffer phandle +- su1-max-uA		: maximum current + +To use the SU2 converter as a backlight source the following two properties must +be provided: +- su2-dev		: framebuffer phandle +- su1-max-uA		: maximum current + +Additionally one of these properties must be provided to select the type of +feedback used: +- su2-feedback-voltage	: voltage feedback is used +- su2-feedback-curr1	: CURR1 input used for current feedback +- su2-feedback-curr2	: CURR2 input used for current feedback +- su2-feedback-curr3	: CURR3 input used for current feedback +- su2-feedback-curr-auto: automatic current feedback selection + +and one of these to select the over-voltage protection pin +- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection +- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection +- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection +- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection + +If "su2-feedback-curr-auto" is selected, one or more of the following properties +have to be specified: +- su2-auto-curr1	: use CURR1 input for current feedback +- su2-auto-curr2	: use CURR2 input for current feedback +- su2-auto-curr3	: use CURR3 input for current feedback + +Example: + +as3711@40 { +	compatible = "ams,as3711"; +	reg = <0x40>; + +	regulators { +		sd4 { +			regulator-name = "1.215V"; +			regulator-min-microvolt = <1215000>; +			regulator-max-microvolt = <1235000>; +		}; +		ldo2 { +			regulator-name = "2.8V CPU"; +			regulator-min-microvolt = <2800000>; +			regulator-max-microvolt = <2800000>; +			regulator-always-on; +			regulator-boot-on; +		}; +	}; + +	backlight { +		compatible = "ams,as3711-bl"; +		su2-dev = <&lcdc>; +		su2-max-uA = <36000>; +		su2-feedback-curr-auto; +		su2-fbprot-gpio4; +		su2-auto-curr1; +		su2-auto-curr2; +		su2-auto-curr3; +	}; +}; diff --git a/Documentation/devicetree/bindings/mfd/cros-ec.txt b/Documentation/devicetree/bindings/mfd/cros-ec.txt new file mode 100644 index 00000000000..e0e59c58a1f --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/cros-ec.txt @@ -0,0 +1,56 @@ +ChromeOS Embedded Controller + +Google's ChromeOS EC is a Cortex-M device which talks to the AP and +implements various function such as keyboard and battery charging. + +The EC can be connect through various means (I2C, SPI, LPC) and the +compatible string used depends on the inteface. Each connection method has +its own driver which connects to the top level interface-agnostic EC driver. +Other Linux driver (such as cros-ec-keyb for the matrix keyboard) connect to +the top-level driver. + +Required properties (I2C): +- compatible: "google,cros-ec-i2c" +- reg: I2C slave address + +Required properties (SPI): +- compatible: "google,cros-ec-spi" +- reg: SPI chip select + +Required properties (LPC): +- compatible: "google,cros-ec-lpc" +- reg: List of (IO address, size) pairs defining the interface uses + + +Example for I2C: + +i2c@12CA0000 { +	cros-ec@1e { +		reg = <0x1e>; +		compatible = "google,cros-ec-i2c"; +		interrupts = <14 0>; +		interrupt-parent = <&wakeup_eint>; +		wakeup-source; +	}; + + +Example for SPI: + +spi@131b0000 { +	ec@0 { +		compatible = "google,cros-ec-spi"; +		reg = <0x0>; +		interrupts = <14 0>; +		interrupt-parent = <&wakeup_eint>; +		wakeup-source; +		spi-max-frequency = <5000000>; +		controller-data { +		cs-gpio = <&gpf0 3 4 3 0>; +		samsung,spi-cs; +		samsung,spi-feedback-delay = <2>; +		}; +	}; +}; + + +Example for LPC is not supplied as it is not yet implemented. diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt new file mode 100644 index 00000000000..b381fa696bf --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt @@ -0,0 +1,80 @@ +OMAP HS USB Host + +Required properties: + +- compatible: should be "ti,usbhs-host" +- reg: should contain one register range i.e. start and length +- ti,hwmods: must contain "usb_host_hs" + +Optional properties: + +- num-ports: number of USB ports. Usually this is automatically detected +  from the IP's revision register but can be overridden by specifying +  this property. A maximum of 3 ports are supported at the moment. + +- portN-mode: String specifying the port mode for port N, where N can be +  from 1 to 3. If the port mode is not specified, that port is treated +  as unused. When specified, it must be one of the following. +	"ehci-phy", +        "ehci-tll", +        "ehci-hsic", +        "ohci-phy-6pin-datse0", +        "ohci-phy-6pin-dpdm", +        "ohci-phy-3pin-datse0", +        "ohci-phy-4pin-dpdm", +        "ohci-tll-6pin-datse0", +        "ohci-tll-6pin-dpdm", +        "ohci-tll-3pin-datse0", +        "ohci-tll-4pin-dpdm", +        "ohci-tll-2pin-datse0", +        "ohci-tll-2pin-dpdm", + +- single-ulpi-bypass: Must be present if the controller contains a single +  ULPI bypass control bit. e.g. OMAP3 silicon <= ES2.1 + +Required properties if child node exists: + +- #address-cells: Must be 1 +- #size-cells: Must be 1 +- ranges: must be present + +Properties for children: + +The OMAP HS USB Host subsystem contains EHCI and OHCI controllers. +See Documentation/devicetree/bindings/usb/omap-ehci.txt and +omap3-ohci.txt + +Example for OMAP4: + +usbhshost: usbhshost@4a064000 { +	compatible = "ti,usbhs-host"; +	reg = <0x4a064000 0x800>; +	ti,hwmods = "usb_host_hs"; +	#address-cells = <1>; +	#size-cells = <1>; +	ranges; + +	usbhsohci: ohci@4a064800 { +		compatible = "ti,ohci-omap3", "usb-ohci"; +		reg = <0x4a064800 0x400>; +		interrupt-parent = <&gic>; +		interrupts = <0 76 0x4>; +	}; + +	usbhsehci: ehci@4a064c00 { +		compatible = "ti,ehci-omap", "usb-ehci"; +		reg = <0x4a064c00 0x400>; +		interrupt-parent = <&gic>; +		interrupts = <0 77 0x4>; +	}; +}; + +&usbhshost { +	port1-mode = "ehci-phy"; +	port2-mode = "ehci-tll"; +	port3-mode = "ehci-phy"; +}; + +&usbhsehci { +	phys = <&hsusb1_phy 0 &hsusb3_phy>; +}; diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt new file mode 100644 index 00000000000..62fe69724e3 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt @@ -0,0 +1,17 @@ +OMAP HS USB Host TLL (Transceiver-Less Interface) + +Required properties: + +- compatible : should be "ti,usbhs-tll" +- reg : should contain one register range i.e. start and length +- interrupts : should contain the TLL module's interrupt +- ti,hwmod : must contain "usb_tll_hs" + +Example: + +	usbhstll: usbhstll@4a062000 { +		compatible = "ti,usbhs-tll"; +		reg = <0x4a062000 0x1000>; +		interrupts = <78>; +		ti,hwmods = "usb_tll_hs"; +	  }; diff --git a/Documentation/devicetree/bindings/sound/wm8994.txt b/Documentation/devicetree/bindings/sound/wm8994.txt index 7a7eb1e7bda..f2f3e80934d 100644 --- a/Documentation/devicetree/bindings/sound/wm8994.txt +++ b/Documentation/devicetree/bindings/sound/wm8994.txt @@ -5,14 +5,70 @@ on the board).  Required properties: -  - compatible : "wlf,wm1811", "wlf,wm8994", "wlf,wm8958" +  - compatible : One of "wlf,wm1811", "wlf,wm8994" or "wlf,wm8958".    - reg : the I2C address of the device for I2C, the chip select            number for SPI. +  - gpio-controller : Indicates this device is a GPIO controller. +  - #gpio-cells : Must be 2. The first cell is the pin number and the +    second cell is used to specify optional parameters (currently unused). + +  - AVDD2-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply, +    SPKVDD1-supply, SPKVDD2-supply : power supplies for the device, as covered +    in Documentation/devicetree/bindings/regulator/regulator.txt + +Optional properties: + +  - interrupts : The interrupt line the IRQ signal for the device is +    connected to.  This is optional, if it is not connected then none +    of the interrupt related properties should be specified. +  - interrupt-controller : These devices contain interrupt controllers +    and may provide interrupt services to other devices if they have an +    interrupt line connected. +  - interrupt-parent : The parent interrupt 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 + +  - wlf,gpio-cfg : A list of GPIO configuration register values. If absent, +    no configuration of these registers is performed. If any value is +    over 0xffff then the register will be left as default. If present 11 +    values must be supplied. + +  - wlf,micbias-cfg : Two MICBIAS register values for WM1811 or +    WM8958.  If absent the register defaults will be used. + +  - wlf,ldo1ena : GPIO specifier for control of LDO1ENA input to device. +  - wlf,ldo2ena : GPIO specifier for control of LDO2ENA input to device. + +  - wlf,lineout1-se : If present LINEOUT1 is in single ended mode. +  - wlf,lineout2-se : If present LINEOUT2 is in single ended mode. + +  - wlf,lineout1-feedback : If present LINEOUT1 has common mode feedback +    connected. +  - wlf,lineout2-feedback : If present LINEOUT2 has common mode feedback +    connected. + +  - wlf,ldoena-always-driven : If present LDOENA is always driven. +  Example:  codec: wm8994@1a {  	compatible = "wlf,wm8994";  	reg = <0x1a>; + +	gpio-controller; +	#gpio-cells = <2>; + +	lineout1-se; + +	AVDD2-supply = <®ulator>; +	CPVDD-supply = <®ulator>; +	DBVDD1-supply = <®ulator>; +	DBVDD2-supply = <®ulator>; +	DBVDD3-supply = <®ulator>; +	SPKVDD1-supply = <®ulator>; +	SPKVDD2-supply = <®ulator>;  }; diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index d58ad4ff8d3..2ebc97e16b9 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -466,8 +466,6 @@ config MACH_MX31ADS_WM1133_EV1  	depends on MACH_MX31ADS  	depends on MFD_WM8350_I2C  	depends on REGULATOR_WM8350 = y -	select MFD_WM8350_CONFIG_MODE_0 -	select MFD_WM8352_CONFIG_MODE_0  	help  	  Include support for the Wolfson Microelectronics 1133-EV1 PMU  	  and audio module for the MX31ADS platform. diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig index 283cb77d472..20578536aec 100644 --- a/arch/arm/mach-s3c64xx/Kconfig +++ b/arch/arm/mach-s3c64xx/Kconfig @@ -200,10 +200,7 @@ endchoice  config SMDK6410_WM1190_EV1  	bool "Support Wolfson Microelectronics 1190-EV1 PMIC card"  	depends on MACH_SMDK6410 -	select MFD_WM8350_CONFIG_MODE_0 -	select MFD_WM8350_CONFIG_MODE_3  	select MFD_WM8350_I2C -	select MFD_WM8352_CONFIG_MODE_0  	select REGULATOR  	select REGULATOR_WM8350  	select SAMSUNG_GPIO_EXTRA64 diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c index a946b759fab..7ccfef227c7 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -208,7 +208,7 @@ static const struct i2c_board_info wm1277_devs[] = {  static struct arizona_pdata wm5102_reva_pdata = {  	.ldoena = S3C64XX_GPN(7),  	.gpio_base = CODEC_GPIO_BASE, -	.irq_active_high = true, +	.irq_flags = IRQF_TRIGGER_HIGH,  	.micd_pol_gpio = CODEC_GPIO_BASE + 4,  	.micd_rate = 6,  	.gpio_defaults = { @@ -238,7 +238,7 @@ static struct spi_board_info wm5102_reva_spi_devs[] = {  static struct arizona_pdata wm5102_pdata = {  	.ldoena = S3C64XX_GPN(7),  	.gpio_base = CODEC_GPIO_BASE, -	.irq_active_high = true, +	.irq_flags = IRQF_TRIGGER_HIGH,  	.micd_pol_gpio = CODEC_GPIO_BASE + 2,  	.gpio_defaults = {  		[2] = 0x10000, /* AIF3TXLRCLK */ diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c index 26405efe0f9..6d0feb234d3 100644 --- a/drivers/gpio/gpio-ucb1400.c +++ b/drivers/gpio/gpio-ucb1400.c @@ -12,8 +12,6 @@  #include <linux/module.h>  #include <linux/ucb1400.h> -struct ucb1400_gpio_data *ucbdata; -  static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off)  {  	struct ucb1400_gpio *gpio; @@ -50,7 +48,7 @@ static int ucb1400_gpio_probe(struct platform_device *dev)  	struct ucb1400_gpio *ucb = dev->dev.platform_data;  	int err = 0; -	if (!(ucbdata && ucbdata->gpio_offset)) { +	if (!(ucb && ucb->gpio_offset)) {  		err = -EINVAL;  		goto err;  	} @@ -58,7 +56,7 @@ static int ucb1400_gpio_probe(struct platform_device *dev)  	platform_set_drvdata(dev, ucb);  	ucb->gc.label = "ucb1400_gpio"; -	ucb->gc.base = ucbdata->gpio_offset; +	ucb->gc.base = ucb->gpio_offset;  	ucb->gc.ngpio = 10;  	ucb->gc.owner = THIS_MODULE; @@ -72,8 +70,8 @@ static int ucb1400_gpio_probe(struct platform_device *dev)  	if (err)  		goto err; -	if (ucbdata && ucbdata->gpio_setup) -		err = ucbdata->gpio_setup(&dev->dev, ucb->gc.ngpio); +	if (ucb && ucb->gpio_setup) +		err = ucb->gpio_setup(&dev->dev, ucb->gc.ngpio);  err:  	return err; @@ -85,8 +83,8 @@ static int ucb1400_gpio_remove(struct platform_device *dev)  	int err = 0;  	struct ucb1400_gpio *ucb = platform_get_drvdata(dev); -	if (ucbdata && ucbdata->gpio_teardown) { -		err = ucbdata->gpio_teardown(&dev->dev, ucb->gc.ngpio); +	if (ucb && ucb->gpio_teardown) { +		err = ucb->gpio_teardown(&dev->dev, ucb->gc.ngpio);  		if (err)  			return err;  	} @@ -103,11 +101,6 @@ static struct platform_driver ucb1400_gpio_driver = {  	},  }; -void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) -{ -	ucbdata = data; -} -  module_platform_driver(ucb1400_gpio_driver);  MODULE_DESCRIPTION("Philips UCB1400 GPIO driver"); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ac050066700..6a195d5e90f 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -628,4 +628,16 @@ config KEYBOARD_W90P910  	  To compile this driver as a module, choose M here: the  	  module will be called w90p910_keypad. +config KEYBOARD_CROS_EC +	tristate "ChromeOS EC keyboard" +	select INPUT_MATRIXKMAP +	depends on MFD_CROS_EC +	help +	  Say Y here to enable the matrix keyboard used by ChromeOS devices +	  and implemented on the ChromeOS EC. You must enable one bus option +	  (MFD_CROS_EC_I2C or MFD_CROS_EC_SPI) to use this. + +	  To compile this driver as a module, choose M here: the +	  module will be called cros_ec_keyb. +  endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 49b16453d00..0c43e8cf8d0 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA)		+= amikbd.o  obj-$(CONFIG_KEYBOARD_ATARI)		+= atakbd.o  obj-$(CONFIG_KEYBOARD_ATKBD)		+= atkbd.o  obj-$(CONFIG_KEYBOARD_BFIN)		+= bf54x-keys.o +obj-$(CONFIG_KEYBOARD_CROS_EC)		+= cros_ec_keyb.o  obj-$(CONFIG_KEYBOARD_DAVINCI)		+= davinci_keyscan.o  obj-$(CONFIG_KEYBOARD_EP93XX)		+= ep93xx_keypad.o  obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS)	+= goldfish_events.o diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c new file mode 100644 index 00000000000..49557f27bfa --- /dev/null +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -0,0 +1,334 @@ +/* + * ChromeOS EC keyboard driver + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * This driver uses the Chrome OS EC byte-level message-based protocol for + * communicating the keyboard state (which keys are pressed) from a keyboard EC + * to the AP over some bus (such as i2c, lpc, spi).  The EC does debouncing, + * but everything else (including deghosting) is done here.  The main + * motivation for this is to keep the EC firmware as simple as possible, since + * it cannot be easily upgraded and EC flash/IRAM space is relatively + * expensive. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/input/matrix_keypad.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> + +/* + * @rows: Number of rows in the keypad + * @cols: Number of columns in the keypad + * @row_shift: log2 or number of rows, rounded up + * @keymap_data: Matrix keymap data used to convert to keyscan values + * @ghost_filter: true to enable the matrix key-ghosting filter + * @dev: Device pointer + * @idev: Input device + * @ec: Top level ChromeOS device to use to talk to EC + * @event_notifier: interrupt event notifier for transport devices + */ +struct cros_ec_keyb { +	unsigned int rows; +	unsigned int cols; +	int row_shift; +	const struct matrix_keymap_data *keymap_data; +	bool ghost_filter; + +	struct device *dev; +	struct input_dev *idev; +	struct cros_ec_device *ec; +	struct notifier_block notifier; +}; + + +static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev, +					  uint8_t *buf, int row) +{ +	int pressed_in_row = 0; +	int row_has_teeth = 0; +	int col, mask; + +	mask = 1 << row; +	for (col = 0; col < ckdev->cols; col++) { +		if (buf[col] & mask) { +			pressed_in_row++; +			row_has_teeth |= buf[col] & ~mask; +			if (pressed_in_row > 1 && row_has_teeth) { +				/* ghosting */ +				dev_dbg(ckdev->dev, +					"ghost found at: r%d c%d, pressed %d, teeth 0x%x\n", +					row, col, pressed_in_row, +					row_has_teeth); +				return true; +			} +		} +	} + +	return false; +} + +/* + * Returns true when there is at least one combination of pressed keys that + * results in ghosting. + */ +static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) +{ +	int row; + +	/* +	 * Ghosting happens if for any pressed key X there are other keys +	 * pressed both in the same row and column of X as, for instance, +	 * in the following diagram: +	 * +	 * . . Y . g . +	 * . . . . . . +	 * . . . . . . +	 * . . X . Z . +	 * +	 * In this case only X, Y, and Z are pressed, but g appears to be +	 * pressed too (see Wikipedia). +	 * +	 * We can detect ghosting in a single pass (*) over the keyboard state +	 * by maintaining two arrays.  pressed_in_row counts how many pressed +	 * keys we have found in a row.  row_has_teeth is true if any of the +	 * pressed keys for this row has other pressed keys in its column.  If +	 * at any point of the scan we find that a row has multiple pressed +	 * keys, and at least one of them is at the intersection with a column +	 * with multiple pressed keys, we're sure there is ghosting. +	 * Conversely, if there is ghosting, we will detect such situation for +	 * at least one key during the pass. +	 * +	 * (*) This looks linear in the number of keys, but it's not.  We can +	 * cheat because the number of rows is small. +	 */ +	for (row = 0; row < ckdev->rows; row++) +		if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row)) +			return true; + +	return false; +} + +/* + * Compares the new keyboard state to the old one and produces key + * press/release events accordingly.  The keyboard state is 13 bytes (one byte + * per column) + */ +static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, +			 uint8_t *kb_state, int len) +{ +	struct input_dev *idev = ckdev->idev; +	int col, row; +	int new_state; +	int num_cols; + +	num_cols = len; + +	if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { +		/* +		 * Simple-minded solution: ignore this state. The obvious +		 * improvement is to only ignore changes to keys involved in +		 * the ghosting, but process the other changes. +		 */ +		dev_dbg(ckdev->dev, "ghosting found\n"); +		return; +	} + +	for (col = 0; col < ckdev->cols; col++) { +		for (row = 0; row < ckdev->rows; row++) { +			int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); +			const unsigned short *keycodes = idev->keycode; +			int code; + +			code = keycodes[pos]; +			new_state = kb_state[col] & (1 << row); +			if (!!new_state != test_bit(code, idev->key)) { +				dev_dbg(ckdev->dev, +					"changed: [r%d c%d]: byte %02x\n", +					row, col, new_state); + +				input_report_key(idev, code, new_state); +			} +		} +	} +	input_sync(ckdev->idev); +} + +static int cros_ec_keyb_open(struct input_dev *dev) +{ +	struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + +	return blocking_notifier_chain_register(&ckdev->ec->event_notifier, +						&ckdev->notifier); +} + +static void cros_ec_keyb_close(struct input_dev *dev) +{ +	struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + +	blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, +					   &ckdev->notifier); +} + +static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) +{ +	return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE, +					  kb_state, ckdev->cols); +} + +static int cros_ec_keyb_work(struct notifier_block *nb, +		     unsigned long state, void *_notify) +{ +	int ret; +	struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, +						    notifier); +	uint8_t kb_state[ckdev->cols]; + +	ret = cros_ec_keyb_get_state(ckdev, kb_state); +	if (ret >= 0) +		cros_ec_keyb_process(ckdev, kb_state, ret); + +	return NOTIFY_DONE; +} + +/* Clear any keys in the buffer */ +static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev) +{ +	uint8_t old_state[ckdev->cols]; +	uint8_t new_state[ckdev->cols]; +	unsigned long duration; +	int i, ret; + +	/* +	 * Keep reading until we see that the scan state does not change. +	 * That indicates that we are done. +	 * +	 * Assume that the EC keyscan buffer is at most 32 deep. +	 */ +	duration = jiffies; +	ret = cros_ec_keyb_get_state(ckdev, new_state); +	for (i = 1; !ret && i < 32; i++) { +		memcpy(old_state, new_state, sizeof(old_state)); +		ret = cros_ec_keyb_get_state(ckdev, new_state); +		if (0 == memcmp(old_state, new_state, sizeof(old_state))) +			break; +	} +	duration = jiffies - duration; +	dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i, +		jiffies_to_usecs(duration)); +} + +static int cros_ec_keyb_probe(struct platform_device *pdev) +{ +	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); +	struct device *dev = ec->dev; +	struct cros_ec_keyb *ckdev; +	struct input_dev *idev; +	struct device_node *np; +	int err; + +	np = pdev->dev.of_node; +	if (!np) +		return -ENODEV; + +	ckdev = devm_kzalloc(&pdev->dev, sizeof(*ckdev), GFP_KERNEL); +	if (!ckdev) +		return -ENOMEM; +	err = matrix_keypad_parse_of_params(&pdev->dev, &ckdev->rows, +					    &ckdev->cols); +	if (err) +		return err; + +	idev = devm_input_allocate_device(&pdev->dev); +	if (!idev) +		return -ENOMEM; + +	ckdev->ec = ec; +	ckdev->notifier.notifier_call = cros_ec_keyb_work; +	ckdev->dev = dev; +	dev_set_drvdata(&pdev->dev, ckdev); + +	idev->name = ec->ec_name; +	idev->phys = ec->phys_name; +	__set_bit(EV_REP, idev->evbit); + +	idev->id.bustype = BUS_VIRTUAL; +	idev->id.version = 1; +	idev->id.product = 0; +	idev->dev.parent = &pdev->dev; +	idev->open = cros_ec_keyb_open; +	idev->close = cros_ec_keyb_close; + +	ckdev->ghost_filter = of_property_read_bool(np, +					"google,needs-ghost-filter"); + +	err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, +					 NULL, idev); +	if (err) { +		dev_err(dev, "cannot build key matrix\n"); +		return err; +	} + +	ckdev->row_shift = get_count_order(ckdev->cols); + +	input_set_capability(idev, EV_MSC, MSC_SCAN); +	input_set_drvdata(idev, ckdev); +	ckdev->idev = idev; +	err = input_register_device(ckdev->idev); +	if (err) { +		dev_err(dev, "cannot register input device\n"); +		return err; +	} + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_keyb_resume(struct device *dev) +{ +	struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + +	/* +	 * When the EC is not a wake source, then it could not have caused the +	 * resume, so we clear the EC's key scan buffer. If the EC was a +	 * wake source (e.g. the lid is open and the user might press a key to +	 * wake) then the key scan buffer should be preserved. +	 */ +	if (ckdev->ec->was_wake_device) +		cros_ec_keyb_clear_keyboard(ckdev); + +	return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); + +static struct platform_driver cros_ec_keyb_driver = { +	.probe = cros_ec_keyb_probe, +	.driver = { +		.name = "cros-ec-keyb", +		.pm	= &cros_ec_keyb_pm_ops, +	}, +}; + +module_platform_driver(cros_ec_keyb_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC keyboard driver"); +MODULE_ALIAS("platform:cros-ec-keyb"); diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 1b8add6cfb9..42181435fe6 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -144,12 +144,13 @@ static int lpc32xx_parse_dt(struct device *dev,  {  	struct device_node *np = dev->of_node;  	u32 rows = 0, columns = 0; +	int err; -	of_property_read_u32(np, "keypad,num-rows", &rows); -	of_property_read_u32(np, "keypad,num-columns", &columns); -	if (!rows || rows != columns) { -		dev_err(dev, -			"rows and columns must be specified and be equal!\n"); +	err = matrix_keypad_parse_of_params(dev, &rows, &columns); +	if (err) +		return err; +	if (rows != columns) { +		dev_err(dev, "rows and columns must be equal!\n");  		return -EINVAL;  	} diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index e25b022692c..1b289092f4e 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -215,18 +215,12 @@ static int omap4_keypad_parse_dt(struct device *dev,  				 struct omap4_keypad *keypad_data)  {  	struct device_node *np = dev->of_node; +	int err; -	if (!np) { -		dev_err(dev, "missing DT data"); -		return -EINVAL; -	} - -	of_property_read_u32(np, "keypad,num-rows", &keypad_data->rows); -	of_property_read_u32(np, "keypad,num-columns", &keypad_data->cols); -	if (!keypad_data->rows || !keypad_data->cols) { -		dev_err(dev, "number of keypad rows/columns not specified\n"); -		return -EINVAL; -	} +	err = matrix_keypad_parse_of_params(dev, &keypad_data->rows, +					    &keypad_data->cols); +	if (err) +		return err;  	if (of_get_property(np, "linux,input-no-autorepeat", NULL))  		keypad_data->no_autorepeat = true; diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index a34cc6714e5..55c15304ddb 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -288,8 +288,11 @@ static int tca8418_keypad_probe(struct i2c_client *client,  		irq_is_gpio = pdata->irq_is_gpio;  	} else {  		struct device_node *np = dev->of_node; -		of_property_read_u32(np, "keypad,num-rows", &rows); -		of_property_read_u32(np, "keypad,num-columns", &cols); +		int err; + +		err = matrix_keypad_parse_of_params(dev, &rows, &cols); +		if (err) +			return err;  		rep = of_property_read_bool(np, "keypad,autorepeat");  	} diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index 3ae496ea5fe..08b61f506db 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -50,6 +50,26 @@ static bool matrix_keypad_map_key(struct input_dev *input_dev,  }  #ifdef CONFIG_OF +int matrix_keypad_parse_of_params(struct device *dev, +				  unsigned int *rows, unsigned int *cols) +{ +	struct device_node *np = dev->of_node; + +	if (!np) { +		dev_err(dev, "missing DT data"); +		return -EINVAL; +	} +	of_property_read_u32(np, "keypad,num-rows", rows); +	of_property_read_u32(np, "keypad,num-columns", cols); +	if (!*rows || !*cols) { +		dev_err(dev, "number of keypad rows/columns not specified\n"); +		return -EINVAL; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(matrix_keypad_parse_of_params); +  static int matrix_keypad_parse_of_keymap(const char *propname,  					 unsigned int rows, unsigned int cols,  					 struct input_dev *input_dev) diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 893fc1ba6ea..31ca55548ef 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -1144,17 +1144,15 @@ static int pm860x_probe(struct i2c_client *client,  			return -ENOMEM;  		ret = pm860x_dt_init(node, &client->dev, pdata);  		if (ret) -			goto err; +			return ret;  	} else if (!pdata) {  		pr_info("No platform data in %s!\n", __func__);  		return -EINVAL;  	}  	chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL); -	if (chip == NULL) { -		ret = -ENOMEM; -		goto err; -	} +	if (chip == NULL) +		return -ENOMEM;  	chip->id = verify_addr(client);  	chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config); @@ -1194,10 +1192,6 @@ static int pm860x_probe(struct i2c_client *client,  	pm860x_device_init(chip, pdata);  	return 0; -err: -	if (node) -		devm_kfree(&client->dev, pdata); -	return ret;  }  static int pm860x_remove(struct i2c_client *client) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ca86581d02c..d9aed1593e5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -10,19 +10,240 @@ config MFD_CORE  	select IRQ_DOMAIN  	default n -config MFD_88PM860X -	bool "Support Marvell 88PM8606/88PM8607" +config MFD_CS5535 +	tristate "AMD CS5535 and CS5536 southbridge core functions" +	select MFD_CORE +	depends on PCI && X86 +	---help--- +	  This is the core driver for CS5535/CS5536 MFD functions.  This is +          necessary for using the board's GPIO and MFGPT functionality. + +config MFD_AS3711 +	bool "AMS AS3711" +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ  	depends on I2C=y && GENERIC_HARDIRQS +	help +	  Support for the AS3711 PMIC from AMS + +config PMIC_ADP5520 +	bool "Analog Devices ADP5520/01 MFD PMIC Core Support" +	depends on I2C=y +	help +	  Say yes here to add support for Analog Devices AD5520 and ADP5501, +	  Multifunction Power Management IC. This includes +	  the I2C driver and the core APIs _only_, you have to select +	  individual components like LCD backlight, LEDs, GPIOs and Kepad +	  under the corresponding menus. + +config MFD_AAT2870_CORE +	bool "AnalogicTech AAT2870" +	select MFD_CORE +	depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS +	help +	  If you say yes here you get support for the AAT2870. +	  This driver provides common support for accessing the device, +	  additional drivers must be enabled in order to use the +	  functionality of the device. + +config MFD_CROS_EC +	tristate "ChromeOS Embedded Controller" +	select MFD_CORE +	help +	  If you say Y here you get support for the ChromeOS Embedded +	  Controller (EC) providing keyboard, battery and power services. +	  You also ned to enable the driver for the bus you are using. The +	  protocol for talking to the EC is defined by the bus driver. + +config MFD_CROS_EC_I2C +	tristate "ChromeOS Embedded Controller (I2C)" +	depends on MFD_CROS_EC && I2C + +	help +	  If you say Y here, you get support for talking to the ChromeOS +	  EC through an I2C bus. This uses a simple byte-level protocol with +	  a checksum. Failing accesses will be retried three times to +	  improve reliability. + +config MFD_CROS_EC_SPI +	tristate "ChromeOS Embedded Controller (SPI)" +	depends on MFD_CROS_EC && SPI + +	---help--- +	  If you say Y here, you get support for talking to the ChromeOS EC +	  through a SPI bus, using a byte-level protocol. Since the EC's +	  response time cannot be guaranteed, we support ignoring +	  'pre-amble' bytes before the response actually starts. + +config MFD_ASIC3 +	bool "Compaq ASIC3" +	depends on GENERIC_HARDIRQS && GPIOLIB && ARM +	select MFD_CORE +	 ---help--- +	  This driver supports the ASIC3 multifunction chip found on many +	  PDAs (mainly iPAQ and HTC based ones) + +config PMIC_DA903X +	bool "Dialog Semiconductor DA9030/DA9034 PMIC Support" +	depends on I2C=y +	help +	  Say yes here to support for Dialog Semiconductor DA9030 (a.k.a +	  ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC +	  usually found on PXA processors-based platforms. This includes +	  the I2C driver and the core APIs _only_, you have to select +	  individual components like LCD backlight, voltage regulators, +	  LEDs and battery-charger under the corresponding menus. + +config PMIC_DA9052 +	bool +	select MFD_CORE + +config MFD_DA9052_SPI +	bool "Dialog Semiconductor DA9052/53 PMIC variants with SPI" +	select REGMAP_SPI +	select REGMAP_IRQ +	select PMIC_DA9052 +	depends on SPI_MASTER=y && GENERIC_HARDIRQS +	help +	  Support for the Dialog Semiconductor DA9052 PMIC +	  when controlled using SPI. This driver provides common support +	  for accessing the device, additional drivers must be enabled in +	  order to use the functionality of the device. + +config MFD_DA9052_I2C +	bool "Dialog Semiconductor DA9052/53 PMIC variants with I2C"  	select REGMAP_I2C +	select REGMAP_IRQ +	select PMIC_DA9052 +	depends on I2C=y && GENERIC_HARDIRQS +	help +	  Support for the Dialog Semiconductor DA9052 PMIC +	  when controlled using I2C. This driver provides common support +	  for accessing the device, additional drivers must be enabled in +	  order to use the functionality of the device. + +config MFD_DA9055 +	bool "Dialog Semiconductor DA9055 PMIC Support" +	select REGMAP_I2C +	select REGMAP_IRQ  	select MFD_CORE +	depends on I2C=y && GENERIC_HARDIRQS  	help -	  This supports for Marvell 88PM8606/88PM8607 Power Management IC. -	  This includes the I2C driver and the core APIs _only_, you have to -	  select individual components like voltage regulators, RTC and -	  battery-charger under the corresponding menus. +	  Say yes here for support of Dialog Semiconductor DA9055. This is +	  a Power Management IC. This driver provides common support for +	  accessing the device as well as the I2C interface to the chip itself. +	  Additional drivers must be enabled in order to use the functionality +	  of the device. + +	  This driver can be built as a module. If built as a module it will be +	  called "da9055" + +config MFD_MC13783 +	tristate + +config MFD_MC13XXX +	tristate +	depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS +	select MFD_CORE +	select MFD_MC13783 +	help +	  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 && GENERIC_HARDIRQS +	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 && GENERIC_HARDIRQS +	select REGMAP_I2C +	select MFD_MC13XXX +	help +	  Select this if your MC13xxx is connected via an I2C bus. + +config HTC_EGPIO +	bool "HTC EGPIO support" +	depends on GENERIC_HARDIRQS && GPIOLIB && ARM +	help +	    This driver supports the CPLD egpio chip present on +	    several HTC phones.  It provides basic support for input +	    pins, output pins, and irqs. + +config HTC_PASIC3 +	tristate "HTC PASIC3 LED/DS1WM chip support" +	select MFD_CORE +	depends on GENERIC_HARDIRQS +	help +	  This core driver provides register access for the LED/DS1WM +	  chips labeled "AIC2" and "AIC3", found on HTC Blueangel and +	  HTC Magician devices, respectively. Actual functionality is +	  handled by the leds-pasic3 and ds1wm drivers. + +config HTC_I2CPLD +	bool "HTC I2C PLD chip support" +	depends on I2C=y && GPIOLIB +	help +	  If you say yes here you get support for the supposed CPLD +	  found on omap850 HTC devices like the HTC Wizard and HTC Herald. +	  This device provides input and output GPIOs through an I2C +	  interface to one or more sub-chips. + +config LPC_ICH +	tristate "Intel ICH LPC" +	depends on PCI && GENERIC_HARDIRQS +	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 LPC_SCH +	tristate "Intel SCH LPC" +	depends on PCI && GENERIC_HARDIRQS +	select MFD_CORE +	help +	  LPC bridge function of the Intel SCH provides support for +	  System Management Bus and General Purpose I/O. + +config MFD_INTEL_MSIC +	bool "Intel MSIC" +	depends on INTEL_SCU_IPC +	select MFD_CORE +	help +	  Select this option to enable access to Intel MSIC (Avatele +	  Passage) chip. This chip embeds audio, battery, GPIO, etc. +	  devices used in Intel Medfield platforms. + +config MFD_JANZ_CMODIO +	tristate "Janz CMOD-IO PCI MODULbus Carrier Board" +	select MFD_CORE +	depends on PCI && GENERIC_HARDIRQS +	help +	  This is the core driver for the Janz CMOD-IO PCI MODULbus +	  carrier board. This device is a PCI to MODULbus bridge which may +	  host many different types of MODULbus daughterboards, including +	  CAN and GPIO controllers. + +config MFD_JZ4740_ADC +	bool "Janz JZ4740 ADC core" +	select MFD_CORE +	select GENERIC_IRQ_CHIP +	depends on MACH_JZ4740 +	help +	  Say yes here if you want support for the ADC unit in the JZ4740 SoC. +	  This driver is necessary for jz4740-battery and jz4740-hwmon driver.  config MFD_88PM800 -	tristate "Support Marvell 88PM800" +	tristate "Marvell 88PM800"  	depends on I2C=y && GENERIC_HARDIRQS  	select REGMAP_I2C  	select REGMAP_IRQ @@ -34,7 +255,7 @@ config MFD_88PM800  	  battery-charger under the corresponding menus.  config MFD_88PM805 -	tristate "Support Marvell 88PM805" +	tristate "Marvell 88PM805"  	depends on I2C=y && GENERIC_HARDIRQS  	select REGMAP_I2C  	select REGMAP_IRQ @@ -45,8 +266,242 @@ config MFD_88PM805  	  components like codec device, headset/Mic device under the  	  corresponding menus. +config MFD_88PM860X +	bool "Marvell 88PM8606/88PM8607" +	depends on I2C=y && GENERIC_HARDIRQS +	select REGMAP_I2C +	select MFD_CORE +	help +	  This supports for Marvell 88PM8606/88PM8607 Power Management IC. +	  This includes the I2C driver and the core APIs _only_, you have to +	  select individual components like voltage regulators, RTC and +	  battery-charger under the corresponding menus. + +config MFD_MAX77686 +	bool "Maxim Semiconductor MAX77686 PMIC Support" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select REGMAP_I2C +	select IRQ_DOMAIN +	help +	  Say yes here to support for Maxim Semiconductor MAX77686. +	  This is a Power Management IC with RTC 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_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_MAX8907 +	tristate "Maxim Semiconductor MAX8907 PMIC Support" +	select MFD_CORE +	depends on I2C=y && GENERIC_HARDIRQS +	select REGMAP_I2C +	select REGMAP_IRQ +	help +	  Say yes here to support for Maxim Semiconductor MAX8907. This is +	  a Power Management IC. 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 +	select MFD_CORE +	help +	  Say yes here to support for Maxim Semiconductor MAX8925. This is +	  a Power Management IC. 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_MAX8997 +	bool "Maxim Semiconductor MAX8997/8966 PMIC Support" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select IRQ_DOMAIN +	help +	  Say yes here to support for Maxim Semiconductor MAX8997/8966. +	  This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, +	  MUIC 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_MAX8998 +	bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	help +	  Say yes here to support for Maxim Semiconductor MAX8998 and +	  National Semiconductor LP3974. This is a Power Management IC. +	  This driver provides common support for accessing the device, +	  additional drivers must be enabled in order to use the functionality +	  of the device. + +config EZX_PCAP +	bool "Motorola EZXPCAP Support" +	depends on GENERIC_HARDIRQS && SPI_MASTER +	help +	  This enables the PCAP ASIC present on EZX Phones. This is +	  needed for MMC, TouchScreen, Sound, USB, etc.. + +config MFD_VIPERBOARD +        tristate "Nano River Technologies Viperboard" +	select MFD_CORE +	depends on USB && GENERIC_HARDIRQS +	default n +	help +	  Say yes here if you want support for Nano River Technologies +	  Viperboard. +	  There are mfd cell drivers available for i2c master, adc and +	  both gpios found on the board. The spi part does not yet +	  have a driver. +	  You need to select the mfd cell drivers separately. +	  The drivers do not support all features the board exposes. + +config MFD_RETU +	tristate "Nokia Retu and Tahvo multi-function device" +	select MFD_CORE +	depends on I2C && GENERIC_HARDIRQS +	select REGMAP_IRQ +	help +	  Retu and Tahvo are a multi-function devices found on Nokia +	  Internet Tablets (770, N800 and N810). + +config MFD_PCF50633 +	tristate "NXP PCF50633" +	depends on I2C +	select REGMAP_I2C +	help +	  Say yes here if you have NXP PCF50633 chip on your board. +	  This core driver provides register access and IRQ handling +	  facilities, and registers devices for the various functions +	  so that function-specific drivers can bind to them. + +config PCF50633_ADC +	tristate "NXP PCF50633 ADC" +	depends on MFD_PCF50633 +	help +	 Say yes here if you want to include support for ADC in the +	 NXP PCF50633 chip. + +config PCF50633_GPIO +	tristate "NXP PCF50633 GPIO" +	depends on MFD_PCF50633 +	help +	 Say yes here if you want to include support GPIO for pins on +	 the PCF50633 chip. + +config UCB1400_CORE +	tristate "Philips UCB1400 Core driver" +	depends on AC97_BUS +	depends on GPIOLIB +	help +	  This enables support for the Philips UCB1400 core functions. +	  The UCB1400 is an AC97 audio codec. + +	  To compile this driver as a module, choose M here: the +	  module will be called ucb1400_core. + +config MFD_PM8XXX +	tristate + +config MFD_PM8921_CORE +	tristate "Qualcomm PM8921 PMIC chip" +	depends on SSBI && BROKEN +	select MFD_CORE +	select MFD_PM8XXX +	help +	  If you say yes to this option, support will be included for the +	  built-in PM8921 PMIC chip. + +	  This is required if your board has a PM8921 and uses its features, +	  such as: MPPs, GPIOs, regulators, interrupts, and PWM. + +	  Say M here if you want to include support for PM8921 chip as a module. +	  This will build a module called "pm8921-core". + +config MFD_PM8XXX_IRQ +	bool "Qualcomm PM8xxx IRQ features" +	depends on MFD_PM8XXX +	default y if MFD_PM8XXX +	help +	  This is the IRQ driver for Qualcomm PM 8xxx PMIC chips. + +	  This is required to use certain other PM 8xxx features, such as GPIO +	  and MPP. + +config MFD_RDC321X +	tristate "RDC R-321x southbridge" +	select MFD_CORE +	depends on PCI && GENERIC_HARDIRQS +	help +	  Say yes here if you want to have support for the RDC R-321x SoC +	  southbridge which provides access to GPIOs and Watchdog using the +	  southbridge PCI device configuration space. + +config MFD_RTSX_PCI +	tristate "Realtek PCI-E card reader" +	depends on PCI && GENERIC_HARDIRQS +	select MFD_CORE +	help +	  This supports for Realtek PCI-Express card reader including rts5209, +	  rts5229, rtl8411, etc. Realtek card reader supports access to many +	  types of memory cards, such as Memory Stick, Memory Stick Pro, +	  Secure Digital and MultiMediaCard. + +config MFD_RC5T583 +	bool "Ricoh RC5T583 Power Management system device" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select REGMAP_I2C +	help +	  Select this option to get support for the RICOH583 Power +	  Management system device. +	  This driver provides common support for accessing the device +	  through i2c interface. The device supports multiple sub-devices +	  like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey. +	  Additional drivers must be enabled in order to use the +	  different functionality of the device. + +config MFD_SEC_CORE +	bool "SAMSUNG Electronics PMIC Series Support" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ +	help +	 Support for the Samsung Electronics MFD series. +	 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_SI476X_CORE +	tristate "Silicon Laboratories 4761/64/68 AM/FM radio." +	depends on I2C +	select MFD_CORE +	select REGMAP_I2C +	help +	  This is the core driver for the SI476x series of AM/FM +	  radio. This MFD driver connects the radio-si476x V4L2 module +	  and the si476x audio codec. + +	  To compile this driver as a module, choose M here: the +	  module will be called si476x-core. +  config MFD_SM501 -	tristate "Support for Silicon Motion SM501" +	tristate "Silicon Motion SM501"  	 ---help---  	  This is the core driver for the Silicon Motion SM501 multimedia  	  companion chip. This device is a multifunction device which may @@ -63,46 +518,147 @@ config MFD_SM501_GPIO  	 lines on the SM501. The platform data is used to supply the  	 base number for the first GPIO line to register. -config MFD_RTSX_PCI -	tristate "Support for Realtek PCI-E card reader" -	depends on PCI && GENERIC_HARDIRQS +config MFD_SMSC +       bool "SMSC ECE1099 series chips" +       depends on I2C=y && GENERIC_HARDIRQS +       select MFD_CORE +       select REGMAP_I2C +       help +        If you say yes here you get support for the +        ece1099 chips from SMSC. + +        To compile this driver as a module, choose M here: the +        module will be called smsc. + +config ABX500_CORE +	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" +	default y if ARCH_U300 || ARCH_U8500 +	help +	  Say yes here if you have the ABX500 Mixed Signal IC family +	  chips. This core driver expose register access functions. +	  Functionality specific drivers using these functions can +	  remain unchanged when IC changes. Binding of the functions to +	  actual register access is done by the IC core driver. + +config AB3100_CORE +	bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" +	depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS  	select MFD_CORE +	default y if ARCH_U300  	help -	  This supports for Realtek PCI-Express card reader including rts5209, -	  rts5229, rtl8411, etc. Realtek card reader supports access to many -	  types of memory cards, such as Memory Stick, Memory Stick Pro, -	  Secure Digital and MultiMediaCard. +	  Select this to enable the AB3100 Mixed Signal IC core +	  functionality. This connects to a AB3100 on the I2C bus +	  and expose a number of symbols needed for dependent devices +	  to read and write registers and subscribe to events from +	  this multi-functional IC. This is needed to use other features +	  of the AB3100 such as battery-backed RTC, charging control, +	  LEDs, vibrator, system power and temperature, power management +	  and ALSA sound. -config MFD_ASIC3 -	bool "Support for Compaq ASIC3" -	depends on GENERIC_HARDIRQS && GPIOLIB && ARM +config AB3100_OTP +	tristate "ST-Ericsson AB3100 OTP functions" +	depends on AB3100_CORE +	default y if AB3100_CORE +	help +	  Select this to enable the AB3100 Mixed Signal IC OTP (one-time +	  programmable memory) support. This exposes a sysfs file to read +	  out OTP values. + +config AB8500_CORE +	bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" +	depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU +	select POWER_SUPPLY  	select MFD_CORE -	 ---help--- -	  This driver supports the ASIC3 multifunction chip found on many -	  PDAs (mainly iPAQ and HTC based ones) +	select IRQ_DOMAIN +	help +	  Select this option to enable access to AB8500 power management +	  chip. This connects to U8500 either on the SSP/SPI bus (deprecated +	  since hardware version v1.0) or the I2C bus via PRCMU. It also adds +	  the irq_chip parts for handling the Mixed Signal chip events. +	  This chip embeds various other multimedia funtionalities as well. -config MFD_DAVINCI_VOICECODEC -	tristate +config AB8500_DEBUG +       bool "Enable debug info via debugfs" +       depends on AB8500_CORE && DEBUG_FS +       default y if DEBUG_FS +       help +         Select this option if you want debug information using the debug +         filesystem, debugfs. + +config AB8500_GPADC +	bool "ST-Ericsson AB8500 GPADC driver" +	depends on AB8500_CORE && REGULATOR_AB8500 +	default y +	help +	  AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage + +config MFD_DB8500_PRCMU +	bool "ST-Ericsson DB8500 Power Reset Control Management Unit" +	depends on UX500_SOC_DB8500  	select MFD_CORE +	help +	  Select this option to enable support for the DB8500 Power Reset +	  and Control Management Unit. This is basically an autonomous +	  system controller running an XP70 microprocessor, which is accessed +	  through a register map. -config MFD_DM355EVM_MSP -	bool "DaVinci DM355 EVM microcontroller" -	depends on I2C=y && MACH_DAVINCI_DM355_EVM +config MFD_STMPE +	bool "STMicroelectronics STMPE" +	depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS +	select MFD_CORE  	help -	  This driver supports the MSP430 microcontroller used on these -	  boards.  MSP430 firmware manages resets and power sequencing, -	  inputs from buttons and the IR remote, LEDs, an RTC, and more. +	  Support for the STMPE family of I/O Expanders from +	  STMicroelectronics. -config MFD_TI_SSP -	tristate "TI Sequencer Serial Port support" -	depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS +	  Currently supported devices are: + +		STMPE811: GPIO, Touchscreen +		STMPE1601: GPIO, Keypad +		STMPE1801: GPIO, Keypad +		STMPE2401: GPIO, Keypad +		STMPE2403: GPIO, Keypad + +	  This driver provides common support for accessing the device, +	  additional drivers must be enabled in order to use the functionality +	  of the device.  Currently available sub drivers are: + +		GPIO: stmpe-gpio +		Keypad: stmpe-keypad +		Touchscreen: stmpe-ts + +menu "STMicroelectronics STMPE Interface Drivers" +depends on MFD_STMPE + +config STMPE_I2C +	bool "STMicroelectronics STMPE I2C Inteface" +	depends on I2C=y +	default y +	help +	  This is used to enable I2C interface of STMPE + +config STMPE_SPI +	bool "STMicroelectronics STMPE SPI Inteface" +	depends on SPI_MASTER +	help +	  This is used to enable SPI interface of STMPE +endmenu + +config MFD_STA2X11 +	bool "STMicroelectronics STA2X11" +	depends on STA2X11 && GENERIC_HARDIRQS  	select MFD_CORE -	---help--- -	  Say Y here if you want support for the Sequencer Serial Port -	  in a Texas Instruments TNETV107X SoC. +	select REGMAP_MMIO -	  To compile this driver as a module, choose M here: the -	  module will be called ti-ssp. +config MFD_SYSCON +	bool "System Controller Register R/W Based on Regmap" +	select REGMAP_MMIO +	help +	  Select this option to enable accessing system control registers +	  via regmap. + +config MFD_DAVINCI_VOICECODEC +	tristate +	select MFD_CORE  config MFD_TI_AM335X_TSCADC  	tristate "TI ADC / Touch Screen chip support" @@ -116,60 +672,56 @@ config MFD_TI_AM335X_TSCADC  	  To compile this driver as a module, choose M here: the  	  module will be called ti_am335x_tscadc. -config HTC_EGPIO -	bool "HTC EGPIO support" -	depends on GENERIC_HARDIRQS && GPIOLIB && ARM +config MFD_DM355EVM_MSP +	bool "TI DaVinci DM355 EVM microcontroller" +	depends on I2C=y && MACH_DAVINCI_DM355_EVM  	help -	    This driver supports the CPLD egpio chip present on -	    several HTC phones.  It provides basic support for input -	    pins, output pins, and irqs. +	  This driver supports the MSP430 microcontroller used on these +	  boards.  MSP430 firmware manages resets and power sequencing, +	  inputs from buttons and the IR remote, LEDs, an RTC, and more. -config HTC_PASIC3 -	tristate "HTC PASIC3 LED/DS1WM chip support" +config MFD_LP8788 +	bool "TI LP8788 Power Management Unit Driver" +	depends on I2C=y && GENERIC_HARDIRQS  	select MFD_CORE -	depends on GENERIC_HARDIRQS -	help -	  This core driver provides register access for the LED/DS1WM -	  chips labeled "AIC2" and "AIC3", found on HTC Blueangel and -	  HTC Magician devices, respectively. Actual functionality is -	  handled by the leds-pasic3 and ds1wm drivers. - -config HTC_I2CPLD -	bool "HTC I2C PLD chip support" -	depends on I2C=y && GPIOLIB +	select REGMAP_I2C +	select IRQ_DOMAIN  	help -	  If you say yes here you get support for the supposed CPLD -	  found on omap850 HTC devices like the HTC Wizard and HTC Herald. -	  This device provides input and output GPIOs through an I2C -	  interface to one or more sub-chips. +	  TI LP8788 PMU supports regulators, battery charger, RTC, +	  ADC, backlight driver and current sinks. -config UCB1400_CORE -	tristate "Philips UCB1400 Core driver" -	depends on AC97_BUS -	depends on GPIOLIB +config MFD_OMAP_USB_HOST +	bool "TI OMAP USBHS core and TLL driver" +	depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 +	default y  	help -	  This enables support for the Philips UCB1400 core functions. -	  The UCB1400 is an AC97 audio codec. - -	  To compile this driver as a module, choose M here: the -	  module will be called ucb1400_core. +	  This is the core driver for the OAMP EHCI and OHCI drivers. +	  This MFD driver does the required setup functionalities for +	  OMAP USB Host drivers. -config MFD_LM3533 -	tristate "LM3533 Lighting Power chip" -	depends on I2C +config MFD_PALMAS +	bool "TI Palmas series chips"  	select MFD_CORE  	select REGMAP_I2C -	depends on GENERIC_HARDIRQS +	select REGMAP_IRQ +	depends on I2C=y && GENERIC_HARDIRQS  	help -	  Say yes here to enable support for National Semiconductor / TI -	  LM3533 Lighting Power chips. +	  If you say yes here you get support for the Palmas +	  series of PMIC chips from Texas Instruments. -	  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 MFD_TI_SSP +	tristate "TI Sequencer Serial Port support" +	depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS +	select MFD_CORE +	---help--- +	  Say Y here if you want support for the Sequencer Serial Port +	  in a Texas Instruments TNETV107X SoC. + +	  To compile this driver as a module, choose M here: the +	  module will be called ti-ssp.  config TPS6105X -	tristate "TPS61050/61052 Boost Converters" +	tristate "TI TPS61050/61052 Boost Converters"  	depends on I2C  	select REGULATOR  	select MFD_CORE @@ -182,7 +734,7 @@ config TPS6105X  	  also contains a GPIO pin.  config TPS65010 -	tristate "TPS6501x Power Management chips" +	tristate "TI TPS6501x Power Management chips"  	depends on I2C && GPIOLIB  	default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK  	help @@ -195,7 +747,7 @@ config TPS65010  	  will be called tps65010.  config TPS6507X -	tristate "TPS6507x Power Management / Touch Screen chips" +	tristate "TI TPS6507x Power Management / Touch Screen chips"  	select MFD_CORE  	depends on I2C && GENERIC_HARDIRQS  	help @@ -206,8 +758,24 @@ config TPS6507X  	  This driver can also be built as a module.  If so, the module  	  will be called tps6507x. +config TPS65911_COMPARATOR +	tristate + +config MFD_TPS65090 +	bool "TI TPS65090 Power Management chips" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ +	help +	  If you say yes here you get support for the TPS65090 series of +	  Power Management chips. +	  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_TPS65217 -	tristate "TPS65217 Power Management / White LED chips" +	tristate "TI TPS65217 Power Management / White LED chips"  	depends on I2C && GENERIC_HARDIRQS  	select MFD_CORE  	select REGMAP_I2C @@ -222,7 +790,7 @@ config MFD_TPS65217  	  will be called tps65217.  config MFD_TPS6586X -	bool "TPS6586x Power Management chips" +	bool "TI TPS6586x Power Management chips"  	depends on I2C=y && GENERIC_HARDIRQS  	select MFD_CORE  	select REGMAP_I2C @@ -237,7 +805,7 @@ config MFD_TPS6586X  	  will be called tps6586x.  config MFD_TPS65910 -	bool "TPS65910 Power Management chip" +	bool "TI TPS65910 Power Management chip"  	depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS  	select MFD_CORE  	select REGMAP_I2C @@ -248,11 +816,14 @@ config MFD_TPS65910  	  Power Management chips.  config MFD_TPS65912 -	bool +	bool "TI TPS65912 Power Management chip"  	depends on GPIOLIB +	help +	  If you say yes here you get support for the TPS65912 series of +	  PM chips.  config MFD_TPS65912_I2C -	bool "TPS65912 Power Management chip with I2C" +	bool "TI TPS65912 Power Management chip with I2C"  	select MFD_CORE  	select MFD_TPS65912  	depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS @@ -261,7 +832,7 @@ config MFD_TPS65912_I2C  	  PM chips with I2C interface.  config MFD_TPS65912_SPI -	bool "TPS65912 Power Management chip with SPI" +	bool "TI TPS65912 Power Management chip with SPI"  	select MFD_CORE  	select MFD_TPS65912  	depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS @@ -283,18 +854,8 @@ config MFD_TPS80031  	  ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with  	  Power Path from USB, 32K clock generator. -config MENELAUS -	bool "Texas Instruments TWL92330/Menelaus PM chip" -	depends on I2C=y && ARCH_OMAP2 -	help -	  If you say yes here you get support for the Texas Instruments -	  TWL92330/Menelaus Power Management chip. This include voltage -	  regulators, Dual slot memory card transceivers, real-time clock -	  and other features that are often used in portable devices like -	  cell phones and PDAs. -  config TWL4030_CORE -	bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" +	bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support"  	depends on I2C=y && GENERIC_HARDIRQS  	select IRQ_DOMAIN  	select REGMAP_I2C @@ -310,7 +871,7 @@ config TWL4030_CORE  	  versions) and many other features.  config TWL4030_MADC -	tristate "Texas Instruments TWL4030 MADC" +	tristate "TI TWL4030 MADC"  	depends on TWL4030_CORE  	help  	This driver provides support for triton TWL4030-MADC. The @@ -320,7 +881,7 @@ config TWL4030_MADC  	named twl4030-madc  config TWL4030_POWER -	bool "Support power resources on TWL4030 family chips" +	bool "TI TWL4030 power resources"  	depends on TWL4030_CORE && ARM  	help  	  Say yes here if you want to use the power resources on the @@ -333,13 +894,13 @@ config TWL4030_POWER  	  or reset when a sleep, wakeup or warm reset event occurs.  config MFD_TWL4030_AUDIO -	bool +	bool "TI TWL4030 Audio"  	depends on TWL4030_CORE && GENERIC_HARDIRQS  	select MFD_CORE  	default n  config TWL6040_CORE -	bool "Support for TWL6040 audio codec" +	bool "TI TWL6040 audio codec"  	depends on I2C=y && GENERIC_HARDIRQS  	select MFD_CORE  	select REGMAP_I2C @@ -352,48 +913,53 @@ config TWL6040_CORE  	  additional drivers must be enabled in order to use the  	  functionality of the device (audio, vibra). -config MFD_STMPE -	bool "Support STMicroelectronics STMPE" -	depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS -	select MFD_CORE +config MENELAUS +	bool "TI TWL92330/Menelaus PM chip" +	depends on I2C=y && ARCH_OMAP2  	help -	  Support for the STMPE family of I/O Expanders from -	  STMicroelectronics. - -	  Currently supported devices are: - -		STMPE811: GPIO, Touchscreen -		STMPE1601: GPIO, Keypad -		STMPE2401: GPIO, Keypad -		STMPE2403: GPIO, Keypad +	  If you say yes here you get support for the Texas Instruments +	  TWL92330/Menelaus Power Management chip. This include voltage +	  regulators, Dual slot memory card transceivers, real-time clock +	  and other features that are often used in portable devices like +	  cell phones and PDAs. -	  This driver provides common support for accessing the device, -	  additional drivers must be enabled in order to use the functionality -	  of the device.  Currently available sub drivers are: +config MFD_WL1273_CORE +	tristate "TI WL1273 FM radio" +	depends on I2C && GENERIC_HARDIRQS +	select MFD_CORE +	default n +	help +	  This is the core driver for the TI WL1273 FM radio. This MFD +	  driver connects the radio-wl1273 V4L2 module and the wl1273 +	  audio codec. -		GPIO: stmpe-gpio -		Keypad: stmpe-keypad -		Touchscreen: stmpe-ts +config MFD_LM3533 +	tristate "TI/National Semiconductor LM3533 Lighting Power chip" +	depends on I2C +	select MFD_CORE +	select REGMAP_I2C +	depends on GENERIC_HARDIRQS +	help +	  Say yes here to enable support for National Semiconductor / TI +	  LM3533 Lighting Power chips. -menu "STMPE Interface Drivers" -depends on MFD_STMPE +	  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 STMPE_I2C -	bool "STMPE I2C Inteface" -	depends on I2C=y -	default y -	help -	  This is used to enable I2C interface of STMPE +config MFD_TIMBERDALE +	tristate "Timberdale FPGA" +	select MFD_CORE +	depends on PCI && GPIOLIB +	---help--- +	This is the core driver for the timberdale FPGA. This device is a +	multifunction device which exposes numerous platform devices. -config STMPE_SPI -	bool "STMPE SPI Inteface" -	depends on SPI_MASTER -	help -	  This is used to enable SPI interface of STMPE -endmenu +	The timberdale FPGA can be found on the Intel Atom development board +	for in-vehicle infontainment, called Russellville.  config MFD_TC3589X -	bool "Support Toshiba TC35892 and variants" +	bool "Toshiba TC35892 and variants"  	depends on I2C=y && GENERIC_HARDIRQS  	select MFD_CORE  	help @@ -408,27 +974,15 @@ config MFD_TMIO  	default n  config MFD_T7L66XB -	bool "Support Toshiba T7L66XB" +	bool "Toshiba T7L66XB"  	depends on ARM && HAVE_CLK && GENERIC_HARDIRQS  	select MFD_CORE  	select MFD_TMIO  	help  	  Support for Toshiba Mobile IO Controller T7L66XB -config MFD_SMSC -       bool "Support for the SMSC ECE1099 series chips" -       depends on I2C=y && GENERIC_HARDIRQS -       select MFD_CORE -       select REGMAP_I2C -       help -        If you say yes here you get support for the -        ece1099 chips from SMSC. - -        To compile this driver as a module, choose M here: the -        module will be called smsc. -  config MFD_TC6387XB -	bool "Support Toshiba TC6387XB" +	bool "Toshiba TC6387XB"  	depends on ARM && HAVE_CLK  	select MFD_CORE  	select MFD_TMIO @@ -436,7 +990,7 @@ config MFD_TC6387XB  	  Support for Toshiba Mobile IO Controller TC6387XB  config MFD_TC6393XB -	bool "Support Toshiba TC6393XB" +	bool "Toshiba TC6393XB"  	depends on ARM && HAVE_CLK  	select GPIOLIB  	select MFD_CORE @@ -444,165 +998,14 @@ config MFD_TC6393XB  	help  	  Support for Toshiba Mobile IO Controller TC6393XB -config PMIC_DA903X -	bool "Dialog Semiconductor DA9030/DA9034 PMIC Support" -	depends on I2C=y -	help -	  Say yes here to support for Dialog Semiconductor DA9030 (a.k.a -	  ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC -	  usually found on PXA processors-based platforms. This includes -	  the I2C driver and the core APIs _only_, you have to select -	  individual components like LCD backlight, voltage regulators, -	  LEDs and battery-charger under the corresponding menus. - -config PMIC_DA9052 -	bool -	select MFD_CORE - -config MFD_DA9052_SPI -	bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI" -	select REGMAP_SPI -	select REGMAP_IRQ -	select PMIC_DA9052 -	depends on SPI_MASTER=y && GENERIC_HARDIRQS -	help -	  Support for the Dialog Semiconductor DA9052 PMIC -	  when controlled using SPI. This driver provides common support -	  for accessing the device, additional drivers must be enabled in -	  order to use the functionality of the device. - -config MFD_DA9052_I2C -	bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C" -	select REGMAP_I2C -	select REGMAP_IRQ -	select PMIC_DA9052 -	depends on I2C=y && GENERIC_HARDIRQS -	help -	  Support for the Dialog Semiconductor DA9052 PMIC -	  when controlled using I2C. This driver provides common support -	  for accessing the device, additional drivers must be enabled in -	  order to use the functionality of the device. - -config MFD_DA9055 -	bool "Dialog Semiconductor DA9055 PMIC Support" -	select REGMAP_I2C -	select REGMAP_IRQ -	select PMIC_DA9055 -	select MFD_CORE -	depends on I2C=y && GENERIC_HARDIRQS -	help -	  Say yes here for support of Dialog Semiconductor DA9055. This is -	  a Power Management IC. This driver provides common support for -	  accessing the device as well as the I2C interface to the chip itself. -	  Additional drivers must be enabled in order to use the functionality -	  of the device. - -	  This driver can be built as a module. If built as a module it will be -	  called "da9055" - -config PMIC_ADP5520 -	bool "Analog Devices ADP5520/01 MFD PMIC Core Support" -	depends on I2C=y -	help -	  Say yes here to add support for Analog Devices AD5520 and ADP5501, -	  Multifunction Power Management IC. This includes -	  the I2C driver and the core APIs _only_, you have to select -	  individual components like LCD backlight, LEDs, GPIOs and Kepad -	  under the corresponding menus. - -config MFD_LP8788 -	bool "Texas Instruments LP8788 Power Management Unit Driver" -	depends on I2C=y && GENERIC_HARDIRQS -	select MFD_CORE -	select REGMAP_I2C -	select IRQ_DOMAIN -	help -	  TI LP8788 PMU supports regulators, battery charger, RTC, -	  ADC, backlight driver and current sinks. - -config MFD_MAX77686 -	bool "Maxim Semiconductor MAX77686 PMIC Support" -	depends on I2C=y && GENERIC_HARDIRQS -	select MFD_CORE -	select REGMAP_I2C -	select IRQ_DOMAIN -	help -	  Say yes here to support for Maxim Semiconductor MAX77686. -	  This is a Power Management IC with RTC 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_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_MAX8907 -	tristate "Maxim Semiconductor MAX8907 PMIC Support" -	select MFD_CORE -	depends on I2C=y && GENERIC_HARDIRQS -	select REGMAP_I2C -	select REGMAP_IRQ -	help -	  Say yes here to support for Maxim Semiconductor MAX8907. This is -	  a Power Management IC. 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 -	select MFD_CORE -	help -	  Say yes here to support for Maxim Semiconductor MAX8925. This is -	  a Power Management IC. 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_MAX8997 -	bool "Maxim Semiconductor MAX8997/8966 PMIC Support" -	depends on I2C=y && GENERIC_HARDIRQS -	select MFD_CORE -	select IRQ_DOMAIN -	help -	  Say yes here to support for Maxim Semiconductor MAX8997/8966. -	  This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, -	  MUIC 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_MAX8998 -	bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" -	depends on I2C=y && GENERIC_HARDIRQS -	select MFD_CORE -	help -	  Say yes here to support for Maxim Semiconductor MAX8998 and -	  National Semiconductor LP3974. This is a Power Management IC. -	  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_SEC_CORE -	bool "SAMSUNG Electronics PMIC Series Support" -	depends on I2C=y && GENERIC_HARDIRQS +config MFD_VX855 +	tristate "VIA VX855/VX875 integrated south bridge" +	depends on PCI && GENERIC_HARDIRQS  	select MFD_CORE -	select REGMAP_I2C -	select REGMAP_IRQ  	help -	 Support for the Samsung Electronics MFD series. -	 This driver provides common support for accessing the device, -	 additional drivers must be enabled in order to use the functionality -	 of the device +	  Say yes here to enable support for various functions of the +	  VIA VX855/VX875 south bridge. You will need to enable the vx855_spi +	  and/or vx855_gpio drivers for this to do anything useful.  config MFD_ARIZONA  	select REGMAP @@ -611,7 +1014,7 @@ config MFD_ARIZONA  	bool  config MFD_ARIZONA_I2C -	tristate "Support Wolfson Microelectronics Arizona platform with I2C" +	tristate "Wolfson Microelectronics Arizona platform with I2C"  	select MFD_ARIZONA  	select MFD_CORE  	select REGMAP_I2C @@ -621,7 +1024,7 @@ config MFD_ARIZONA_I2C  	  core functionality controlled via I2C.  config MFD_ARIZONA_SPI -	tristate "Support Wolfson Microelectronics Arizona platform with SPI" +	tristate "Wolfson Microelectronics Arizona platform with SPI"  	select MFD_ARIZONA  	select MFD_CORE  	select REGMAP_SPI @@ -631,19 +1034,19 @@ config MFD_ARIZONA_SPI  	  core functionality controlled via I2C.  config MFD_WM5102 -	bool "Support Wolfson Microelectronics WM5102" +	bool "Wolfson Microelectronics WM5102"  	depends on MFD_ARIZONA  	help  	  Support for Wolfson Microelectronics WM5102 low power audio SoC  config MFD_WM5110 -	bool "Support Wolfson Microelectronics WM5110" +	bool "Wolfson Microelectronics WM5110"  	depends on MFD_ARIZONA  	help  	  Support for Wolfson Microelectronics WM5110 low power audio SoC  config MFD_WM8400 -	bool "Support Wolfson Microelectronics WM8400" +	bool "Wolfson Microelectronics WM8400"  	select MFD_CORE  	depends on I2C=y && GENERIC_HARDIRQS  	select REGMAP_I2C @@ -658,7 +1061,7 @@ config MFD_WM831X  	depends on GENERIC_HARDIRQS  config MFD_WM831X_I2C -	bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C" +	bool "Wolfson Microelectronics WM831x/2x PMICs with I2C"  	select MFD_CORE  	select MFD_WM831X  	select REGMAP_I2C @@ -671,7 +1074,7 @@ config MFD_WM831X_I2C  	  order to use the functionality of the device.  config MFD_WM831X_SPI -	bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI" +	bool "Wolfson Microelectronics WM831x/2x PMICs with SPI"  	select MFD_CORE  	select MFD_WM831X  	select REGMAP_SPI @@ -687,56 +1090,8 @@ config MFD_WM8350  	bool  	depends on GENERIC_HARDIRQS -config MFD_WM8350_CONFIG_MODE_0 -	bool -	depends on MFD_WM8350 - -config MFD_WM8350_CONFIG_MODE_1 -	bool -	depends on MFD_WM8350 - -config MFD_WM8350_CONFIG_MODE_2 -	bool -	depends on MFD_WM8350 - -config MFD_WM8350_CONFIG_MODE_3 -	bool -	depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_0 -	bool -	depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_1 -	bool -	depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_2 -	bool -	depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_3 -	bool -	depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_0 -	bool -	depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_1 -	bool -	depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_2 -	bool -	depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_3 -	bool -	depends on MFD_WM8350 -  config MFD_WM8350_I2C -	bool "Support Wolfson Microelectronics WM8350 with I2C" +	bool "Wolfson Microelectronics WM8350 with I2C"  	select MFD_WM8350  	depends on I2C=y && GENERIC_HARDIRQS  	help @@ -747,7 +1102,7 @@ config MFD_WM8350_I2C  	  selected to enable support for the functionality of the chip.  config MFD_WM8994 -	bool "Support Wolfson Microelectronics WM8994" +	bool "Wolfson Microelectronics WM8994"  	select MFD_CORE  	select REGMAP_I2C  	select REGMAP_IRQ @@ -760,365 +1115,6 @@ config MFD_WM8994  	  core support for the WM8994, in order to use the actual  	  functionaltiy of the device other drivers must be enabled. -config MFD_PCF50633 -	tristate "Support for NXP PCF50633" -	depends on I2C -	select REGMAP_I2C -	help -	  Say yes here if you have NXP PCF50633 chip on your board. -	  This core driver provides register access and IRQ handling -	  facilities, and registers devices for the various functions -	  so that function-specific drivers can bind to them. - -config PCF50633_ADC -	tristate "Support for NXP PCF50633 ADC" -	depends on MFD_PCF50633 -	help -	 Say yes here if you want to include support for ADC in the -	 NXP PCF50633 chip. - -config PCF50633_GPIO -	tristate "Support for NXP PCF50633 GPIO" -	depends on MFD_PCF50633 -	help -	 Say yes here if you want to include support GPIO for pins on -	 the PCF50633 chip. - -config MFD_MC13783 -	tristate - -config MFD_MC13XXX -	tristate -	depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS -	select MFD_CORE -	select MFD_MC13783 -	help -	  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 && GENERIC_HARDIRQS -	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 && GENERIC_HARDIRQS -	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 -	help -	  Say yes here if you have the ABX500 Mixed Signal IC family -	  chips. This core driver expose register access functions. -	  Functionality specific drivers using these functions can -	  remain unchanged when IC changes. Binding of the functions to -	  actual register access is done by the IC core driver. - -config AB3100_CORE -	bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" -	depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS -	select MFD_CORE -	default y if ARCH_U300 -	help -	  Select this to enable the AB3100 Mixed Signal IC core -	  functionality. This connects to a AB3100 on the I2C bus -	  and expose a number of symbols needed for dependent devices -	  to read and write registers and subscribe to events from -	  this multi-functional IC. This is needed to use other features -	  of the AB3100 such as battery-backed RTC, charging control, -	  LEDs, vibrator, system power and temperature, power management -	  and ALSA sound. - -config AB3100_OTP -	tristate "ST-Ericsson AB3100 OTP functions" -	depends on AB3100_CORE -	default y if AB3100_CORE -	help -	  Select this to enable the AB3100 Mixed Signal IC OTP (one-time -	  programmable memory) support. This exposes a sysfs file to read -	  out OTP values. - -config EZX_PCAP -	bool "PCAP Support" -	depends on GENERIC_HARDIRQS && SPI_MASTER -	help -	  This enables the PCAP ASIC present on EZX Phones. This is -	  needed for MMC, TouchScreen, Sound, USB, etc.. - -config AB8500_CORE -	bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" -	depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU -	select POWER_SUPPLY -	select MFD_CORE -	select IRQ_DOMAIN -	help -	  Select this option to enable access to AB8500 power management -	  chip. This connects to U8500 either on the SSP/SPI bus (deprecated -	  since hardware version v1.0) or the I2C bus via PRCMU. It also adds -	  the irq_chip parts for handling the Mixed Signal chip events. -	  This chip embeds various other multimedia funtionalities as well. - -config AB8500_DEBUG -       bool "Enable debug info via debugfs" -       depends on AB8500_CORE && DEBUG_FS -       default y if DEBUG_FS -       help -         Select this option if you want debug information using the debug -         filesystem, debugfs. - -config AB8500_GPADC -	bool "AB8500 GPADC driver" -	depends on AB8500_CORE && REGULATOR_AB8500 -	default y -	help -	  AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage - -config MFD_DB8500_PRCMU -	bool "ST-Ericsson DB8500 Power Reset Control Management Unit" -	depends on UX500_SOC_DB8500 -	select MFD_CORE -	help -	  Select this option to enable support for the DB8500 Power Reset -	  and Control Management Unit. This is basically an autonomous -	  system controller running an XP70 microprocessor, which is accessed -	  through a register map. - -config MFD_CS5535 -	tristate "Support for CS5535 and CS5536 southbridge core functions" -	select MFD_CORE -	depends on PCI && X86 -	---help--- -	  This is the core driver for CS5535/CS5536 MFD functions.  This is -          necessary for using the board's GPIO and MFGPT functionality. - -config MFD_TIMBERDALE -	tristate "Support for the Timberdale FPGA" -	select MFD_CORE -	depends on PCI && GPIOLIB -	---help--- -	This is the core driver for the timberdale FPGA. This device is a -	multifunction device which exposes numerous platform devices. - -	The timberdale FPGA can be found on the Intel Atom development board -	for in-vehicle infontainment, called Russellville. - -config LPC_SCH -	tristate "Intel SCH LPC" -	depends on PCI && GENERIC_HARDIRQS -	select MFD_CORE -	help -	  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 && GENERIC_HARDIRQS -	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 -	depends on PCI && GENERIC_HARDIRQS -	help -	  Say yes here if you want to have support for the RDC R-321x SoC -	  southbridge which provides access to GPIOs and Watchdog using the -	  southbridge PCI device configuration space. - -config MFD_JANZ_CMODIO -	tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" -	select MFD_CORE -	depends on PCI && GENERIC_HARDIRQS -	help -	  This is the core driver for the Janz CMOD-IO PCI MODULbus -	  carrier board. This device is a PCI to MODULbus bridge which may -	  host many different types of MODULbus daughterboards, including -	  CAN and GPIO controllers. - -config MFD_JZ4740_ADC -	bool "Support for the JZ4740 SoC ADC core" -	select MFD_CORE -	select GENERIC_IRQ_CHIP -	depends on MACH_JZ4740 -	help -	  Say yes here if you want support for the ADC unit in the JZ4740 SoC. -	  This driver is necessary for jz4740-battery and jz4740-hwmon driver. - -config MFD_VX855 -	tristate "Support for VIA VX855/VX875 integrated south bridge" -	depends on PCI && GENERIC_HARDIRQS -	select MFD_CORE -	help -	  Say yes here to enable support for various functions of the -	  VIA VX855/VX875 south bridge. You will need to enable the vx855_spi -	  and/or vx855_gpio drivers for this to do anything useful. - -config MFD_WL1273_CORE -	tristate "Support for TI WL1273 FM radio." -	depends on I2C && GENERIC_HARDIRQS -	select MFD_CORE -	default n -	help -	  This is the core driver for the TI WL1273 FM radio. This MFD -	  driver connects the radio-wl1273 V4L2 module and the wl1273 -	  audio codec. - -config MFD_OMAP_USB_HOST -	bool "Support OMAP USBHS core and TLL driver" -	depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 -	default y -	help -	  This is the core driver for the OAMP EHCI and OHCI drivers. -	  This MFD driver does the required setup functionalities for -	  OMAP USB Host drivers. - -config MFD_PM8XXX -	tristate - -config MFD_PM8921_CORE -	tristate "Qualcomm PM8921 PMIC chip" -	depends on SSBI && BROKEN -	select MFD_CORE -	select MFD_PM8XXX -	help -	  If you say yes to this option, support will be included for the -	  built-in PM8921 PMIC chip. - -	  This is required if your board has a PM8921 and uses its features, -	  such as: MPPs, GPIOs, regulators, interrupts, and PWM. - -	  Say M here if you want to include support for PM8921 chip as a module. -	  This will build a module called "pm8921-core". - -config MFD_PM8XXX_IRQ -	bool "Support for Qualcomm PM8xxx IRQ features" -	depends on MFD_PM8XXX -	default y if MFD_PM8XXX -	help -	  This is the IRQ driver for Qualcomm PM 8xxx PMIC chips. - -	  This is required to use certain other PM 8xxx features, such as GPIO -	  and MPP. - -config TPS65911_COMPARATOR -	tristate - -config MFD_TPS65090 -	bool "TPS65090 Power Management chips" -	depends on I2C=y && GENERIC_HARDIRQS -	select MFD_CORE -	select REGMAP_I2C -	select REGMAP_IRQ -	help -	  If you say yes here you get support for the TPS65090 series of -	  Power Management chips. -	  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_AAT2870_CORE -	bool "Support for the AnalogicTech AAT2870" -	select MFD_CORE -	depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS -	help -	  If you say yes here you get support for the AAT2870. -	  This driver provides common support for accessing the device, -	  additional drivers must be enabled in order to use the -	  functionality of the device. - -config MFD_INTEL_MSIC -	bool "Support for Intel MSIC" -	depends on INTEL_SCU_IPC -	select MFD_CORE -	help -	  Select this option to enable access to Intel MSIC (Avatele -	  Passage) chip. This chip embeds audio, battery, GPIO, etc. -	  devices used in Intel Medfield platforms. - -config MFD_RC5T583 -	bool "Ricoh RC5T583 Power Management system device" -	depends on I2C=y && GENERIC_HARDIRQS -	select MFD_CORE -	select REGMAP_I2C -	help -	  Select this option to get support for the RICOH583 Power -	  Management system device. -	  This driver provides common support for accessing the device -	  through i2c interface. The device supports multiple sub-devices -	  like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey. -	  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 && GENERIC_HARDIRQS -	select MFD_CORE -	select REGMAP_MMIO - -config MFD_SYSCON -	bool "System Controller Register R/W Based on Regmap" -	depends on OF -	select REGMAP_MMIO -	help -	  Select this option to enable accessing system control registers -	  via regmap. - -config MFD_PALMAS -	bool "Support for the TI Palmas series chips" -	select MFD_CORE -	select REGMAP_I2C -	select REGMAP_IRQ -	depends on I2C=y && GENERIC_HARDIRQS -	help -	  If you say yes here you get support for the Palmas -	  series of PMIC chips from Texas Instruments. - -config MFD_VIPERBOARD -        tristate "Support for Nano River Technologies Viperboard" -	select MFD_CORE -	depends on USB && GENERIC_HARDIRQS -	default n -	help -	  Say yes here if you want support for Nano River Technologies -	  Viperboard. -	  There are mfd cell drivers available for i2c master, adc and -	  both gpios found on the board. The spi part does not yet -	  have a driver. -	  You need to select the mfd cell drivers separately. -	  The drivers do not support all features the board exposes. - -config MFD_RETU -	tristate "Support for Retu multi-function device" -	select MFD_CORE -	depends on I2C && GENERIC_HARDIRQS -	select REGMAP_IRQ -	help -	  Retu is a multi-function device found on Nokia Internet Tablets -	  (770, N800 and N810). - -config MFD_AS3711 -	bool "Support for AS3711" -	select MFD_CORE -	select REGMAP_I2C -	select REGMAP_IRQ -	depends on I2C=y && GENERIC_HARDIRQS -	help -	  Support for the AS3711 PMIC from AMS -  endmenu  endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b90409c2366..718e94a2a9a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,8 +8,11 @@ obj-$(CONFIG_MFD_88PM800)	+= 88pm800.o 88pm80x.o  obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o  obj-$(CONFIG_MFD_SM501)		+= sm501.o  obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o +obj-$(CONFIG_MFD_CROS_EC)	+= cros_ec.o +obj-$(CONFIG_MFD_CROS_EC_I2C)	+= cros_ec_i2c.o +obj-$(CONFIG_MFD_CROS_EC_SPI)	+= cros_ec_spi.o -rtsx_pci-objs			:= rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o +rtsx_pci-objs			:= rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o  obj-$(CONFIG_MFD_RTSX_PCI)	+= rtsx_pci.o  obj-$(CONFIG_HTC_EGPIO)		+= htc-egpio.o @@ -131,6 +134,10 @@ obj-$(CONFIG_MFD_JZ4740_ADC)	+= jz4740-adc.o  obj-$(CONFIG_MFD_TPS6586X)	+= tps6586x.o  obj-$(CONFIG_MFD_VX855)		+= vx855.o  obj-$(CONFIG_MFD_WL1273_CORE)	+= wl1273-core.o + +si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o +obj-$(CONFIG_MFD_SI476X_CORE)	+= si476x-core.o +  obj-$(CONFIG_MFD_CS5535)	+= cs5535-mfd.o  obj-$(CONFIG_MFD_OMAP_USB_HOST)	+= omap-usb-host.o omap-usb-tll.o  obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index f1beb4971f8..dfdb0a2b683 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -367,12 +367,12 @@ static int aat2870_i2c_probe(struct i2c_client *client,  	int i, j;  	int ret = 0; -	aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL); +	aat2870 = devm_kzalloc(&client->dev, sizeof(struct aat2870_data), +				GFP_KERNEL);  	if (!aat2870) {  		dev_err(&client->dev,  			"Failed to allocate memory for aat2870\n"); -		ret = -ENOMEM; -		goto out; +		return -ENOMEM;  	}  	aat2870->dev = &client->dev; @@ -400,12 +400,12 @@ static int aat2870_i2c_probe(struct i2c_client *client,  		aat2870->init(aat2870);  	if (aat2870->en_pin >= 0) { -		ret = gpio_request_one(aat2870->en_pin, GPIOF_OUT_INIT_HIGH, -				       "aat2870-en"); +		ret = devm_gpio_request_one(&client->dev, aat2870->en_pin, +					GPIOF_OUT_INIT_HIGH, "aat2870-en");  		if (ret < 0) {  			dev_err(&client->dev,  				"Failed to request GPIO %d\n", aat2870->en_pin); -			goto out_kfree; +			return ret;  		}  	} @@ -436,11 +436,6 @@ static int aat2870_i2c_probe(struct i2c_client *client,  out_disable:  	aat2870_disable(aat2870); -	if (aat2870->en_pin >= 0) -		gpio_free(aat2870->en_pin); -out_kfree: -	kfree(aat2870); -out:  	return ret;  } @@ -452,11 +447,8 @@ static int aat2870_i2c_remove(struct i2c_client *client)  	mfd_remove_devices(aat2870->dev);  	aat2870_disable(aat2870); -	if (aat2870->en_pin >= 0) -		gpio_free(aat2870->en_pin);  	if (aat2870->uninit)  		aat2870->uninit(aat2870); -	kfree(aat2870);  	return 0;  } diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 8440010eb2b..d7ce016029f 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -248,19 +248,7 @@ static struct platform_driver ab3100_otp_driver = {  	.remove	 = __exit_p(ab3100_otp_remove),  }; -static int __init ab3100_otp_init(void) -{ -	return platform_driver_probe(&ab3100_otp_driver, -				     ab3100_otp_probe); -} - -static void __exit ab3100_otp_exit(void) -{ -	platform_driver_unregister(&ab3100_otp_driver); -} - -module_init(ab3100_otp_init); -module_exit(ab3100_otp_exit); +module_platform_driver_probe(ab3100_otp_driver, ab3100_otp_probe);  MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");  MODULE_DESCRIPTION("AB3100 OTP Readout Driver"); diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f276352cc9e..8e8a016effe 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -458,22 +458,23 @@ static void update_latch_offset(u8 *offset, int i)  static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,  					int latch_offset, u8 latch_val)  { -	int int_bit = __ffs(latch_val); -	int line, i; +	int int_bit, 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; -		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; +	} -		if (i >= ab8500->mask_size) { -			dev_err(ab8500->dev, "Register offset 0x%2x not declared\n", -					latch_offset); -			return -ENXIO; -		} +	/* ignore masked out interrupts */ +	latch_val &= ~ab8500->mask[i]; +	while (latch_val) { +		int_bit = __ffs(latch_val);  		line = (i << 3) + int_bit;  		latch_val &= ~(1 << int_bit); @@ -491,7 +492,7 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,  			line += 1;  		handle_nested_irq(ab8500->irq_base + line); -	} while (latch_val); +	}  	return 0;  } @@ -1107,6 +1108,7 @@ static struct mfd_cell ab8500_devs[] = {  	},  	{  		.name = "ab8500-usb", +		.of_compatible = "stericsson,ab8500-usb",  		.num_resources = ARRAY_SIZE(ab8500_usb_resources),  		.resources = ab8500_usb_resources,  	}, diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 65f72284185..5e65b28a5d0 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -332,7 +332,7 @@ if (ad_value < 0) {  	return voltage;  } -EXPORT_SYMBOL(ab8500_gpadc_convert); +EXPORT_SYMBOL(ab8500_gpadc_sw_hw_convert);  /**   * ab8500_gpadc_read_raw() - gpadc read diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 272479cdb10..fbca1ced49f 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -242,7 +242,7 @@ static int __init ab8500_sysctrl_init(void)  {  	return platform_driver_register(&ab8500_sysctrl_driver);  } -subsys_initcall(ab8500_sysctrl_init); +arch_initcall(ab8500_sysctrl_init);  MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com");  MODULE_DESCRIPTION("AB8500 system control driver"); diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 210dd038bb5..0d2eba02343 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -36,6 +36,7 @@ struct adp5520_chip {  	struct blocking_notifier_head notifier_list;  	int irq;  	unsigned long id; +	uint8_t mode;  };  static int __adp5520_read(struct i2c_client *client, @@ -326,7 +327,10 @@ static int adp5520_suspend(struct device *dev)  	struct i2c_client *client = to_i2c_client(dev);  	struct adp5520_chip *chip = dev_get_drvdata(&client->dev); -	adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); +	adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode); +	/* All other bits are W1C */ +	chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY; +	adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);  	return 0;  } @@ -335,7 +339,7 @@ static int adp5520_resume(struct device *dev)  	struct i2c_client *client = to_i2c_client(dev);  	struct adp5520_chip *chip = dev_get_drvdata(&client->dev); -	adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); +	adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode);  	return 0;  }  #endif @@ -360,17 +364,7 @@ static struct i2c_driver adp5520_driver = {  	.id_table 	= adp5520_id,  }; -static int __init adp5520_init(void) -{ -	return i2c_add_driver(&adp5520_driver); -} -module_init(adp5520_init); - -static void __exit adp5520_exit(void) -{ -	i2c_del_driver(&adp5520_driver); -} -module_exit(adp5520_exit); +module_i2c_driver(adp5520_driver);  MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");  MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver"); diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index b562c7bf8a4..6ab03043fd6 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -39,11 +39,21 @@ int arizona_clk32k_enable(struct arizona *arizona)  	arizona->clk32k_ref++; -	if (arizona->clk32k_ref == 1) +	if (arizona->clk32k_ref == 1) { +		switch (arizona->pdata.clk32k_src) { +		case ARIZONA_32KZ_MCLK1: +			ret = pm_runtime_get_sync(arizona->dev); +			if (ret != 0) +				goto out; +			break; +		} +  		ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,  					 ARIZONA_CLK_32K_ENA,  					 ARIZONA_CLK_32K_ENA); +	} +out:  	if (ret != 0)  		arizona->clk32k_ref--; @@ -63,10 +73,17 @@ int arizona_clk32k_disable(struct arizona *arizona)  	arizona->clk32k_ref--; -	if (arizona->clk32k_ref == 0) +	if (arizona->clk32k_ref == 0) {  		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,  				   ARIZONA_CLK_32K_ENA, 0); +		switch (arizona->pdata.clk32k_src) { +		case ARIZONA_32KZ_MCLK1: +			pm_runtime_put_sync(arizona->dev); +			break; +		} +	} +  	mutex_unlock(&arizona->clk_lock);  	return ret; @@ -179,42 +196,134 @@ static irqreturn_t arizona_overclocked(int irq, void *data)  	return IRQ_HANDLED;  } -static int arizona_wait_for_boot(struct arizona *arizona) +static int arizona_poll_reg(struct arizona *arizona, +			    int timeout, unsigned int reg, +			    unsigned int mask, unsigned int target)  { -	unsigned int reg; +	unsigned int val = 0;  	int ret, i; +	for (i = 0; i < timeout; i++) { +		ret = regmap_read(arizona->regmap, reg, &val); +		if (ret != 0) { +			dev_err(arizona->dev, "Failed to read reg %u: %d\n", +				reg, ret); +			continue; +		} + +		if ((val & mask) == target) +			return 0; + +		msleep(1); +	} + +	dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val); +	return -ETIMEDOUT; +} + +static int arizona_wait_for_boot(struct arizona *arizona) +{ +	int ret; +  	/*  	 * We can't use an interrupt as we need to runtime resume to do so,  	 * we won't race with the interrupt handler as it'll be blocked on  	 * runtime resume.  	 */ -	for (i = 0; i < 5; i++) { -		msleep(1); +	ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5, +			       ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS); -		ret = regmap_read(arizona->regmap, -				  ARIZONA_INTERRUPT_RAW_STATUS_5, ®); -		if (ret != 0) { -			dev_err(arizona->dev, "Failed to read boot state: %d\n", -				ret); -			continue; -		} +	if (!ret) +		regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, +			     ARIZONA_BOOT_DONE_STS); -		if (reg & ARIZONA_BOOT_DONE_STS) -			break; +	pm_runtime_mark_last_busy(arizona->dev); + +	return ret; +} + +static int arizona_apply_hardware_patch(struct arizona* arizona) +{ +	unsigned int fll, sysclk; +	int ret, err; + +	regcache_cache_bypass(arizona->regmap, true); + +	/* Cache existing FLL and SYSCLK settings */ +	ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll); +	if (ret != 0) { +		dev_err(arizona->dev, "Failed to cache FLL settings: %d\n", +			ret); +		return ret; +	} +	ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk); +	if (ret != 0) { +		dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n", +			ret); +		return ret;  	} -	if (reg & ARIZONA_BOOT_DONE_STS) { -		regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, -			     ARIZONA_BOOT_DONE_STS); -	} else { -		dev_err(arizona->dev, "Device boot timed out: %x\n", reg); -		return -ETIMEDOUT; +	/* Start up SYSCLK using the FLL in free running mode */ +	ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, +			ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN); +	if (ret != 0) { +		dev_err(arizona->dev, +			"Failed to start FLL in freerunning mode: %d\n", +			ret); +		return ret; +	} +	ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5, +			       ARIZONA_FLL1_CLOCK_OK_STS, +			       ARIZONA_FLL1_CLOCK_OK_STS); +	if (ret != 0) { +		ret = -ETIMEDOUT; +		goto err_fll;  	} -	pm_runtime_mark_last_busy(arizona->dev); +	ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144); +	if (ret != 0) { +		dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret); +		goto err_fll; +	} -	return 0; +	/* Start the write sequencer and wait for it to finish */ +	ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, +			ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160); +	if (ret != 0) { +		dev_err(arizona->dev, "Failed to start write sequencer: %d\n", +			ret); +		goto err_sysclk; +	} +	ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1, +			       ARIZONA_WSEQ_BUSY, 0); +	if (ret != 0) { +		regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, +				ARIZONA_WSEQ_ABORT); +		ret = -ETIMEDOUT; +	} + +err_sysclk: +	err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk); +	if (err != 0) { +		dev_err(arizona->dev, +			"Failed to re-apply old SYSCLK settings: %d\n", +			err); +	} + +err_fll: +	err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll); +	if (err != 0) { +		dev_err(arizona->dev, +			"Failed to re-apply old FLL settings: %d\n", +			err); +	} + +	regcache_cache_bypass(arizona->regmap, false); + +	if (ret != 0) +		return ret; +	else +		return err;  }  #ifdef CONFIG_PM_RUNTIME @@ -233,20 +342,44 @@ static int arizona_runtime_resume(struct device *dev)  	regcache_cache_only(arizona->regmap, false); -	ret = arizona_wait_for_boot(arizona); -	if (ret != 0) { -		regulator_disable(arizona->dcvdd); -		return ret; +	switch (arizona->type) { +	case WM5102: +		ret = wm5102_patch(arizona); +		if (ret != 0) { +			dev_err(arizona->dev, "Failed to apply patch: %d\n", +				ret); +			goto err; +		} + +		ret = arizona_apply_hardware_patch(arizona); +		if (ret != 0) { +			dev_err(arizona->dev, +				"Failed to apply hardware patch: %d\n", +				ret); +			goto err; +		} +		break; +	default: +		ret = arizona_wait_for_boot(arizona); +		if (ret != 0) { +			goto err; +		} + +		break;  	}  	ret = regcache_sync(arizona->regmap);  	if (ret != 0) {  		dev_err(arizona->dev, "Failed to restore register cache\n"); -		regulator_disable(arizona->dcvdd); -		return ret; +		goto err;  	}  	return 0; + +err: +	regcache_cache_only(arizona->regmap, true); +	regulator_disable(arizona->dcvdd); +	return ret;  }  static int arizona_runtime_suspend(struct device *dev) @@ -371,6 +504,17 @@ int arizona_dev_init(struct arizona *arizona)  		goto err_early;  	} +	if (arizona->pdata.reset) { +		/* Start out with /RESET low to put the chip into reset */ +		ret = gpio_request_one(arizona->pdata.reset, +				       GPIOF_DIR_OUT | GPIOF_INIT_LOW, +				       "arizona /RESET"); +		if (ret != 0) { +			dev_err(dev, "Failed to request /RESET: %d\n", ret); +			goto err_early; +		} +	} +  	ret = regulator_bulk_enable(arizona->num_core_supplies,  				    arizona->core_supplies);  	if (ret != 0) { @@ -386,16 +530,8 @@ int arizona_dev_init(struct arizona *arizona)  	}  	if (arizona->pdata.reset) { -		/* Start out with /RESET low to put the chip into reset */ -		ret = gpio_request_one(arizona->pdata.reset, -				       GPIOF_DIR_OUT | GPIOF_INIT_LOW, -				       "arizona /RESET"); -		if (ret != 0) { -			dev_err(dev, "Failed to request /RESET: %d\n", ret); -			goto err_dcvdd; -		} -  		gpio_set_value_cansleep(arizona->pdata.reset, 1); +		msleep(1);  	}  	regcache_cache_only(arizona->regmap, false); @@ -424,6 +560,7 @@ int arizona_dev_init(struct arizona *arizona)  			arizona->type = WM5102;  		}  		apply_patch = wm5102_patch; +		arizona->rev &= 0x7;  		break;  #endif  #ifdef CONFIG_MFD_WM5110 @@ -454,6 +591,8 @@ int arizona_dev_init(struct arizona *arizona)  			goto err_reset;  		} +		msleep(1); +  		ret = regcache_sync(arizona->regmap);  		if (ret != 0) {  			dev_err(dev, "Failed to sync device: %d\n", ret); @@ -461,10 +600,24 @@ int arizona_dev_init(struct arizona *arizona)  		}  	} -	ret = arizona_wait_for_boot(arizona); -	if (ret != 0) { -		dev_err(arizona->dev, "Device failed initial boot: %d\n", ret); -		goto err_reset; +	switch (arizona->type) { +	case WM5102: +		ret = regmap_read(arizona->regmap, 0x19, &val); +		if (ret != 0) +			dev_err(dev, +				"Failed to check write sequencer state: %d\n", +				ret); +		else if (val & 0x01) +			break; +		/* Fall through */ +	default: +		ret = arizona_wait_for_boot(arizona); +		if (ret != 0) { +			dev_err(arizona->dev, +				"Device failed initial boot: %d\n", ret); +			goto err_reset; +		} +		break;  	}  	if (apply_patch) { @@ -474,6 +627,20 @@ int arizona_dev_init(struct arizona *arizona)  				ret);  			goto err_reset;  		} + +		switch (arizona->type) { +		case WM5102: +			ret = arizona_apply_hardware_patch(arizona); +			if (ret != 0) { +				dev_err(arizona->dev, +					"Failed to apply hardware patch: %d\n", +					ret); +				goto err_reset; +			} +			break; +		default: +			break; +		}  	}  	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { @@ -498,6 +665,7 @@ int arizona_dev_init(struct arizona *arizona)  		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,  				   ARIZONA_CLK_32K_SRC_MASK,  				   arizona->pdata.clk32k_src - 1); +		arizona_clk32k_enable(arizona);  		break;  	case ARIZONA_32KZ_NONE:  		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, @@ -511,10 +679,16 @@ int arizona_dev_init(struct arizona *arizona)  	}  	for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) { -		if (!arizona->pdata.micbias[i].mV) +		if (!arizona->pdata.micbias[i].mV && +		    !arizona->pdata.micbias[i].bypass)  			continue; +		/* Apply default for bypass mode */ +		if (!arizona->pdata.micbias[i].mV) +			arizona->pdata.micbias[i].mV = 2800; +  		val = (arizona->pdata.micbias[i].mV - 1500) / 100; +  		val <<= ARIZONA_MICB1_LVL_SHIFT;  		if (arizona->pdata.micbias[i].ext_cap) @@ -526,10 +700,14 @@ int arizona_dev_init(struct arizona *arizona)  		if (arizona->pdata.micbias[i].fast_start)  			val |= ARIZONA_MICB1_RATE; +		if (arizona->pdata.micbias[i].bypass) +			val |= ARIZONA_MICB1_BYPASS; +  		regmap_update_bits(arizona->regmap,  				   ARIZONA_MIC_BIAS_CTRL_1 + i,  				   ARIZONA_MICB1_LVL_MASK |  				   ARIZONA_MICB1_DISCH | +				   ARIZONA_MICB1_BYPASS |  				   ARIZONA_MICB1_RATE, val);  	} @@ -610,10 +788,9 @@ err_irq:  	arizona_irq_exit(arizona);  err_reset:  	if (arizona->pdata.reset) { -		gpio_set_value_cansleep(arizona->pdata.reset, 1); +		gpio_set_value_cansleep(arizona->pdata.reset, 0);  		gpio_free(arizona->pdata.reset);  	} -err_dcvdd:  	regulator_disable(arizona->dcvdd);  err_enable:  	regulator_bulk_disable(arizona->num_core_supplies, diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 2bec5f0db3e..64cd9b6dac9 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -94,6 +94,7 @@ static irqreturn_t arizona_ctrlif_err(int irq, void *data)  static irqreturn_t arizona_irq_thread(int irq, void *data)  {  	struct arizona *arizona = data; +	bool poll;  	unsigned int val;  	int ret; @@ -103,20 +104,39 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)  		return IRQ_NONE;  	} -	/* Always handle the AoD domain */ -	handle_nested_irq(irq_find_mapping(arizona->virq, 0)); +	do { +		poll = false; -	/* -	 * Check if one of the main interrupts is asserted and only -	 * check that domain if it is. -	 */ -	ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, &val); -	if (ret == 0 && val & ARIZONA_IRQ1_STS) { -		handle_nested_irq(irq_find_mapping(arizona->virq, 1)); -	} else if (ret != 0) { -		dev_err(arizona->dev, "Failed to read main IRQ status: %d\n", -			ret); -	} +		/* Always handle the AoD domain */ +		handle_nested_irq(irq_find_mapping(arizona->virq, 0)); + +		/* +		 * Check if one of the main interrupts is asserted and only +		 * check that domain if it is. +		 */ +		ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, +				  &val); +		if (ret == 0 && val & ARIZONA_IRQ1_STS) { +			handle_nested_irq(irq_find_mapping(arizona->virq, 1)); +		} else if (ret != 0) { +			dev_err(arizona->dev, +				"Failed to read main IRQ status: %d\n", ret); +		} + +		/* +		 * Poll the IRQ pin status to see if we're really done +		 * if the interrupt controller can't do it for us. +		 */ +		if (!arizona->pdata.irq_gpio) { +			break; +		} else if (arizona->pdata.irq_flags & IRQF_TRIGGER_RISING && +			   gpio_get_value_cansleep(arizona->pdata.irq_gpio)) { +			poll = true; +		} else if (arizona->pdata.irq_flags & IRQF_TRIGGER_FALLING && +			   !gpio_get_value_cansleep(arizona->pdata.irq_gpio)) { +			poll = true; +		} +	} while (poll);  	pm_runtime_mark_last_busy(arizona->dev);  	pm_runtime_put_autosuspend(arizona->dev); @@ -169,6 +189,7 @@ int arizona_irq_init(struct arizona *arizona)  	int ret, i;  	const struct regmap_irq_chip *aod, *irq;  	bool ctrlif_error = true; +	struct irq_data *irq_data;  	switch (arizona->type) {  #ifdef CONFIG_MFD_WM5102 @@ -192,7 +213,36 @@ int arizona_irq_init(struct arizona *arizona)  		return -EINVAL;  	} -	if (arizona->pdata.irq_active_high) { +	/* Disable all wake sources by default */ +	regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0); + +	/* Read the flags from the interrupt controller if not specified */ +	if (!arizona->pdata.irq_flags) { +		irq_data = irq_get_irq_data(arizona->irq); +		if (!irq_data) { +			dev_err(arizona->dev, "Invalid IRQ: %d\n", +				arizona->irq); +			return -EINVAL; +		} + +		arizona->pdata.irq_flags = irqd_get_trigger_type(irq_data); +		switch (arizona->pdata.irq_flags) { +		case IRQF_TRIGGER_LOW: +		case IRQF_TRIGGER_HIGH: +		case IRQF_TRIGGER_RISING: +		case IRQF_TRIGGER_FALLING: +			break; + +		case IRQ_TYPE_NONE: +		default: +			/* Device default */ +			arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; +			break; +		} +	} + +	if (arizona->pdata.irq_flags & (IRQF_TRIGGER_HIGH | +					IRQF_TRIGGER_RISING)) {  		ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,  					 ARIZONA_IRQ_POL, 0);  		if (ret != 0) { @@ -200,12 +250,10 @@ int arizona_irq_init(struct arizona *arizona)  				ret);  			goto err;  		} - -		flags |= IRQF_TRIGGER_HIGH; -	} else { -		flags |= IRQF_TRIGGER_LOW;  	} +	flags |= arizona->pdata.irq_flags; +  	/* Allocate a virtual IRQ domain to distribute to the regmap domains */  	arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,  					      arizona); @@ -257,11 +305,31 @@ int arizona_irq_init(struct arizona *arizona)  		}  	} +	/* Used to emulate edge trigger and to work around broken pinmux */ +	if (arizona->pdata.irq_gpio) { +		if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) { +			dev_warn(arizona->dev, "IRQ %d is not GPIO %d (%d)\n", +				 arizona->irq, arizona->pdata.irq_gpio, +				 gpio_to_irq(arizona->pdata.irq_gpio)); +			arizona->irq = gpio_to_irq(arizona->pdata.irq_gpio); +		} + +		ret = devm_gpio_request_one(arizona->dev, +					    arizona->pdata.irq_gpio, +					    GPIOF_IN, "arizona IRQ"); +		if (ret != 0) { +			dev_err(arizona->dev, +				"Failed to request IRQ GPIO %d:: %d\n", +				arizona->pdata.irq_gpio, ret); +			arizona->pdata.irq_gpio = 0; +		} +	} +  	ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,  				   flags, "arizona", arizona);  	if (ret != 0) { -		dev_err(arizona->dev, "Failed to request IRQ %d: %d\n", +		dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n",  			arizona->irq, ret);  		goto err_main_irq;  	} diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 1b9fdd698b0..b57e642d2b4 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -67,7 +67,7 @@ static int arizona_spi_probe(struct spi_device *spi)  static int arizona_spi_remove(struct spi_device *spi)  { -	struct arizona *arizona = dev_get_drvdata(&spi->dev); +	struct arizona *arizona = spi_get_drvdata(spi);  	arizona_dev_exit(arizona);  	return 0;  } diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index e994c969112..01e41416270 100644 --- a/drivers/mfd/as3711.c +++ b/drivers/mfd/as3711.c @@ -112,16 +112,34 @@ static const struct regmap_config as3711_regmap_config = {  	.cache_type = REGCACHE_RBTREE,  }; +#ifdef CONFIG_OF +static struct of_device_id as3711_of_match[] = { +	{.compatible = "ams,as3711",}, +	{} +}; +MODULE_DEVICE_TABLE(of, as3711_of_match); +#endif +  static int as3711_i2c_probe(struct i2c_client *client,  			    const struct i2c_device_id *id)  {  	struct as3711 *as3711; -	struct as3711_platform_data *pdata = client->dev.platform_data; +	struct as3711_platform_data *pdata;  	unsigned int id1, id2;  	int ret; -	if (!pdata) -		dev_dbg(&client->dev, "Platform data not found\n"); +	if (!client->dev.of_node) { +		pdata = client->dev.platform_data; +		if (!pdata) +			dev_dbg(&client->dev, "Platform data not found\n"); +	} else { +		pdata = devm_kzalloc(&client->dev, +				     sizeof(*pdata), GFP_KERNEL); +		if (!pdata) { +			dev_err(&client->dev, "Failed to allocate pdata\n"); +			return -ENOMEM; +		} +	}  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);  	if (!as3711) { @@ -193,7 +211,8 @@ static struct i2c_driver as3711_i2c_driver = {  	.driver = {  		   .name = "as3711",  		   .owner = THIS_MODULE, -		   }, +		   .of_match_table = of_match_ptr(as3711_of_match), +	},  	.probe = as3711_i2c_probe,  	.remove = as3711_i2c_remove,  	.id_table = as3711_i2c_id, diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c new file mode 100644 index 00000000000..10cd14e35eb --- /dev/null +++ b/drivers/mfd/cros_ec.c @@ -0,0 +1,196 @@ +/* + * ChromeOS EC multi-function device + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * The ChromeOS EC multi function device is used to mux all the requests + * to the EC device for its multiple features: keyboard controller, + * battery charging and regulator control, firmware update. + */ + +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mfd/core.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> + +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, +		       struct cros_ec_msg *msg) +{ +	uint8_t *out; +	int csum, i; + +	BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE); +	out = ec_dev->dout; +	out[0] = EC_CMD_VERSION0 + msg->version; +	out[1] = msg->cmd; +	out[2] = msg->out_len; +	csum = out[0] + out[1] + out[2]; +	for (i = 0; i < msg->out_len; i++) +		csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i]; +	out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff); + +	return EC_MSG_TX_PROTO_BYTES + msg->out_len; +} +EXPORT_SYMBOL(cros_ec_prepare_tx); + +static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, +		uint16_t cmd, void *out_buf, int out_len, +		void *in_buf, int in_len) +{ +	struct cros_ec_msg msg; + +	msg.version = cmd >> 8; +	msg.cmd = cmd & 0xff; +	msg.out_buf = out_buf; +	msg.out_len = out_len; +	msg.in_buf = in_buf; +	msg.in_len = in_len; + +	return ec_dev->command_xfer(ec_dev, &msg); +} + +static int cros_ec_command_recv(struct cros_ec_device *ec_dev, +		uint16_t cmd, void *buf, int buf_len) +{ +	return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len); +} + +static int cros_ec_command_send(struct cros_ec_device *ec_dev, +		uint16_t cmd, void *buf, int buf_len) +{ +	return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0); +} + +static irqreturn_t ec_irq_thread(int irq, void *data) +{ +	struct cros_ec_device *ec_dev = data; + +	if (device_may_wakeup(ec_dev->dev)) +		pm_wakeup_event(ec_dev->dev, 0); + +	blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev); + +	return IRQ_HANDLED; +} + +static struct mfd_cell cros_devs[] = { +	{ +		.name = "cros-ec-keyb", +		.id = 1, +		.of_compatible = "google,cros-ec-keyb", +	}, +}; + +int cros_ec_register(struct cros_ec_device *ec_dev) +{ +	struct device *dev = ec_dev->dev; +	int err = 0; + +	BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); + +	ec_dev->command_send = cros_ec_command_send; +	ec_dev->command_recv = cros_ec_command_recv; +	ec_dev->command_sendrecv = cros_ec_command_sendrecv; + +	if (ec_dev->din_size) { +		ec_dev->din = kmalloc(ec_dev->din_size, GFP_KERNEL); +		if (!ec_dev->din) { +			err = -ENOMEM; +			goto fail_din; +		} +	} +	if (ec_dev->dout_size) { +		ec_dev->dout = kmalloc(ec_dev->dout_size, GFP_KERNEL); +		if (!ec_dev->dout) { +			err = -ENOMEM; +			goto fail_dout; +		} +	} + +	if (!ec_dev->irq) { +		dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); +		goto fail_irq; +	} + +	err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, +				   IRQF_TRIGGER_LOW | IRQF_ONESHOT, +				   "chromeos-ec", ec_dev); +	if (err) { +		dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); +		goto fail_irq; +	} + +	err = mfd_add_devices(dev, 0, cros_devs, +			      ARRAY_SIZE(cros_devs), +			      NULL, ec_dev->irq, NULL); +	if (err) { +		dev_err(dev, "failed to add mfd devices\n"); +		goto fail_mfd; +	} + +	dev_info(dev, "Chrome EC (%s)\n", ec_dev->name); + +	return 0; + +fail_mfd: +	free_irq(ec_dev->irq, ec_dev); +fail_irq: +	kfree(ec_dev->dout); +fail_dout: +	kfree(ec_dev->din); +fail_din: +	return err; +} +EXPORT_SYMBOL(cros_ec_register); + +int cros_ec_remove(struct cros_ec_device *ec_dev) +{ +	mfd_remove_devices(ec_dev->dev); +	free_irq(ec_dev->irq, ec_dev); +	kfree(ec_dev->dout); +	kfree(ec_dev->din); + +	return 0; +} +EXPORT_SYMBOL(cros_ec_remove); + +#ifdef CONFIG_PM_SLEEP +int cros_ec_suspend(struct cros_ec_device *ec_dev) +{ +	struct device *dev = ec_dev->dev; + +	if (device_may_wakeup(dev)) +		ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); + +	disable_irq(ec_dev->irq); +	ec_dev->was_wake_device = ec_dev->wake_enabled; + +	return 0; +} +EXPORT_SYMBOL(cros_ec_suspend); + +int cros_ec_resume(struct cros_ec_device *ec_dev) +{ +	enable_irq(ec_dev->irq); + +	if (ec_dev->wake_enabled) { +		disable_irq_wake(ec_dev->irq); +		ec_dev->wake_enabled = 0; +	} + +	return 0; +} +EXPORT_SYMBOL(cros_ec_resume); + +#endif diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c new file mode 100644 index 00000000000..123044608b6 --- /dev/null +++ b/drivers/mfd/cros_ec_i2c.c @@ -0,0 +1,201 @@ +/* + * ChromeOS EC multi-function device (I2C) + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +static inline struct cros_ec_device *to_ec_dev(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return i2c_get_clientdata(client); +} + +static int cros_ec_command_xfer(struct cros_ec_device *ec_dev, +				struct cros_ec_msg *msg) +{ +	struct i2c_client *client = ec_dev->priv; +	int ret = -ENOMEM; +	int i; +	int packet_len; +	u8 *out_buf = NULL; +	u8 *in_buf = NULL; +	u8 sum; +	struct i2c_msg i2c_msg[2]; + +	i2c_msg[0].addr = client->addr; +	i2c_msg[0].flags = 0; +	i2c_msg[1].addr = client->addr; +	i2c_msg[1].flags = I2C_M_RD; + +	/* +	 * allocate larger packet (one byte for checksum, one byte for +	 * length, and one for result code) +	 */ +	packet_len = msg->in_len + 3; +	in_buf = kzalloc(packet_len, GFP_KERNEL); +	if (!in_buf) +		goto done; +	i2c_msg[1].len = packet_len; +	i2c_msg[1].buf = (char *)in_buf; + +	/* +	 * allocate larger packet (one byte for checksum, one for +	 * command code, one for length, and one for command version) +	 */ +	packet_len = msg->out_len + 4; +	out_buf = kzalloc(packet_len, GFP_KERNEL); +	if (!out_buf) +		goto done; +	i2c_msg[0].len = packet_len; +	i2c_msg[0].buf = (char *)out_buf; + +	out_buf[0] = EC_CMD_VERSION0 + msg->version; +	out_buf[1] = msg->cmd; +	out_buf[2] = msg->out_len; + +	/* copy message payload and compute checksum */ +	sum = out_buf[0] + out_buf[1] + out_buf[2]; +	for (i = 0; i < msg->out_len; i++) { +		out_buf[3 + i] = msg->out_buf[i]; +		sum += out_buf[3 + i]; +	} +	out_buf[3 + msg->out_len] = sum; + +	/* send command to EC and read answer */ +	ret = i2c_transfer(client->adapter, i2c_msg, 2); +	if (ret < 0) { +		dev_err(ec_dev->dev, "i2c transfer failed: %d\n", ret); +		goto done; +	} else if (ret != 2) { +		dev_err(ec_dev->dev, "failed to get response: %d\n", ret); +		ret = -EIO; +		goto done; +	} + +	/* check response error code */ +	if (i2c_msg[1].buf[0]) { +		dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", +			 msg->cmd, i2c_msg[1].buf[0]); +		ret = -EINVAL; +		goto done; +	} + +	/* copy response packet payload and compute checksum */ +	sum = in_buf[0] + in_buf[1]; +	for (i = 0; i < msg->in_len; i++) { +		msg->in_buf[i] = in_buf[2 + i]; +		sum += in_buf[2 + i]; +	} +	dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n", +		i2c_msg[1].len, in_buf, sum); +	if (sum != in_buf[2 + msg->in_len]) { +		dev_err(ec_dev->dev, "bad packet checksum\n"); +		ret = -EBADMSG; +		goto done; +	} + +	ret = 0; + done: +	kfree(in_buf); +	kfree(out_buf); +	return ret; +} + +static int cros_ec_probe_i2c(struct i2c_client *client, +			     const struct i2c_device_id *dev_id) +{ +	struct device *dev = &client->dev; +	struct cros_ec_device *ec_dev = NULL; +	int err; + + 	ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); +	if (!ec_dev) +		return -ENOMEM; + +	i2c_set_clientdata(client, ec_dev); +	ec_dev->name = "I2C"; +	ec_dev->dev = dev; +	ec_dev->priv = client; +	ec_dev->irq = client->irq; +	ec_dev->command_xfer = cros_ec_command_xfer; +	ec_dev->ec_name = client->name; +	ec_dev->phys_name = client->adapter->name; +	ec_dev->parent = &client->dev; + +	err = cros_ec_register(ec_dev); +	if (err) { +		dev_err(dev, "cannot register EC\n"); +		return err; +	} + +	return 0; +} + +static int cros_ec_remove_i2c(struct i2c_client *client) +{ +	struct cros_ec_device *ec_dev = i2c_get_clientdata(client); + +	cros_ec_remove(ec_dev); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_i2c_suspend(struct device *dev) +{ +	struct cros_ec_device *ec_dev = to_ec_dev(dev); + +	return cros_ec_suspend(ec_dev); +} + +static int cros_ec_i2c_resume(struct device *dev) +{ +	struct cros_ec_device *ec_dev = to_ec_dev(dev); + +	return cros_ec_resume(ec_dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend, +			  cros_ec_i2c_resume); + +static const struct i2c_device_id cros_ec_i2c_id[] = { +	{ "cros-ec-i2c", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id); + +static struct i2c_driver cros_ec_driver = { +	.driver	= { +		.name	= "cros-ec-i2c", +		.owner	= THIS_MODULE, +		.pm	= &cros_ec_i2c_pm_ops, +	}, +	.probe		= cros_ec_probe_i2c, +	.remove		= cros_ec_remove_i2c, +	.id_table	= cros_ec_i2c_id, +}; + +module_i2c_driver(cros_ec_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC multi function device"); diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c new file mode 100644 index 00000000000..19193cf1e7a --- /dev/null +++ b/drivers/mfd/cros_ec_spi.c @@ -0,0 +1,375 @@ +/* + * ChromeOS EC multi-function device (SPI) + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + + +/* The header byte, which follows the preamble */ +#define EC_MSG_HEADER			0xec + +/* + * Number of EC preamble bytes we read at a time. Since it takes + * about 400-500us for the EC to respond there is not a lot of + * point in tuning this. If the EC could respond faster then + * we could increase this so that might expect the preamble and + * message to occur in a single transaction. However, the maximum + * SPI transfer size is 256 bytes, so at 5MHz we need a response + * time of perhaps <320us (200 bytes / 1600 bits). + */ +#define EC_MSG_PREAMBLE_COUNT		32 + +/* +  * We must get a response from the EC in 5ms. This is a very long +  * time, but the flash write command can take 2-3ms. The EC command +  * processing is currently not very fast (about 500us). We could +  * look at speeding this up and making the flash write command a +  * 'slow' command, requiring a GET_STATUS wait loop, like flash +  * erase. +  */ +#define EC_MSG_DEADLINE_MS		5 + +/* +  * Time between raising the SPI chip select (for the end of a +  * transaction) and dropping it again (for the next transaction). +  * If we go too fast, the EC will miss the transaction. It seems +  * that 50us is enough with the 16MHz STM32 EC. +  */ +#define EC_SPI_RECOVERY_TIME_NS	(50 * 1000) + +/** + * struct cros_ec_spi - information about a SPI-connected EC + * + * @spi: SPI device we are connected to + * @last_transfer_ns: time that we last finished a transfer, or 0 if there + *	if no record + */ +struct cros_ec_spi { +	struct spi_device *spi; +	s64 last_transfer_ns; +}; + +static void debug_packet(struct device *dev, const char *name, u8 *ptr, +			  int len) +{ +#ifdef DEBUG +	int i; + +	dev_dbg(dev, "%s: ", name); +	for (i = 0; i < len; i++) +		dev_cont(dev, " %02x", ptr[i]); +#endif +} + +/** + * cros_ec_spi_receive_response - Receive a response from the EC. + * + * This function has two phases: reading the preamble bytes (since if we read + * data from the EC before it is ready to send, we just get preamble) and + * reading the actual message. + * + * The received data is placed into ec_dev->din. + * + * @ec_dev: ChromeOS EC device + * @need_len: Number of message bytes we need to read + */ +static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, +					int need_len) +{ +	struct cros_ec_spi *ec_spi = ec_dev->priv; +	struct spi_transfer trans; +	struct spi_message msg; +	u8 *ptr, *end; +	int ret; +	unsigned long deadline; +	int todo; + +	/* Receive data until we see the header byte */ +	deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); +	do { +		memset(&trans, '\0', sizeof(trans)); +		trans.cs_change = 1; +		trans.rx_buf = ptr = ec_dev->din; +		trans.len = EC_MSG_PREAMBLE_COUNT; + +		spi_message_init(&msg); +		spi_message_add_tail(&trans, &msg); +		ret = spi_sync(ec_spi->spi, &msg); +		if (ret < 0) { +			dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); +			return ret; +		} + +		for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { +			if (*ptr == EC_MSG_HEADER) { +				dev_dbg(ec_dev->dev, "msg found at %ld\n", +					ptr - ec_dev->din); +				break; +			} +		} + +		if (time_after(jiffies, deadline)) { +			dev_warn(ec_dev->dev, "EC failed to respond in time\n"); +			return -ETIMEDOUT; +		} +	} while (ptr == end); + +	/* +	 * ptr now points to the header byte. Copy any valid data to the +	 * start of our buffer +	 */ +	todo = end - ++ptr; +	BUG_ON(todo < 0 || todo > ec_dev->din_size); +	todo = min(todo, need_len); +	memmove(ec_dev->din, ptr, todo); +	ptr = ec_dev->din + todo; +	dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n", +		 need_len, todo); +	need_len -= todo; + +	/* Receive data until we have it all */ +	while (need_len > 0) { +		/* +		 * We can't support transfers larger than the SPI FIFO size +		 * unless we have DMA. We don't have DMA on the ISP SPI ports +		 * for Exynos. We need a way of asking SPI driver for +		 * maximum-supported transfer size. +		 */ +		todo = min(need_len, 256); +		dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%ld\n", +			todo, need_len, ptr - ec_dev->din); + +		memset(&trans, '\0', sizeof(trans)); +		trans.cs_change = 1; +		trans.rx_buf = ptr; +		trans.len = todo; +		spi_message_init(&msg); +		spi_message_add_tail(&trans, &msg); + +		/* send command to EC and read answer */ +		BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo > +				ec_dev->din_size); +		ret = spi_sync(ec_spi->spi, &msg); +		if (ret < 0) { +			dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); +			return ret; +		} + +		debug_packet(ec_dev->dev, "interim", ptr, todo); +		ptr += todo; +		need_len -= todo; +	} + +	dev_dbg(ec_dev->dev, "loop done, ptr=%ld\n", ptr - ec_dev->din); + +	return 0; +} + +/** + * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply + * + * @ec_dev: ChromeOS EC device + * @ec_msg: Message to transfer + */ +static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, +				    struct cros_ec_msg *ec_msg) +{ +	struct cros_ec_spi *ec_spi = ec_dev->priv; +	struct spi_transfer trans; +	struct spi_message msg; +	int i, len; +	u8 *ptr; +	int sum; +	int ret = 0, final_ret; +	struct timespec ts; + +	len = cros_ec_prepare_tx(ec_dev, ec_msg); +	dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); + +	/* If it's too soon to do another transaction, wait */ +	if (ec_spi->last_transfer_ns) { +		struct timespec ts; +		unsigned long delay;	/* The delay completed so far */ + +		ktime_get_ts(&ts); +		delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns; +		if (delay < EC_SPI_RECOVERY_TIME_NS) +			ndelay(delay); +	} + +	/* Transmit phase - send our message */ +	debug_packet(ec_dev->dev, "out", ec_dev->dout, len); +	memset(&trans, '\0', sizeof(trans)); +	trans.tx_buf = ec_dev->dout; +	trans.len = len; +	trans.cs_change = 1; +	spi_message_init(&msg); +	spi_message_add_tail(&trans, &msg); +	ret = spi_sync(ec_spi->spi, &msg); + +	/* Get the response */ +	if (!ret) { +		ret = cros_ec_spi_receive_response(ec_dev, +				ec_msg->in_len + EC_MSG_TX_PROTO_BYTES); +	} else { +		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); +	} + +	/* turn off CS */ +	spi_message_init(&msg); +	final_ret = spi_sync(ec_spi->spi, &msg); +	ktime_get_ts(&ts); +	ec_spi->last_transfer_ns = timespec_to_ns(&ts); +	if (!ret) +		ret = final_ret; +	if (ret < 0) { +		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); +		return ret; +	} + +	/* check response error code */ +	ptr = ec_dev->din; +	if (ptr[0]) { +		dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", +			 ec_msg->cmd, ptr[0]); +		debug_packet(ec_dev->dev, "in_err", ptr, len); +		return -EINVAL; +	} +	len = ptr[1]; +	sum = ptr[0] + ptr[1]; +	if (len > ec_msg->in_len) { +		dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", +			len, ec_msg->in_len); +		return -ENOSPC; +	} + +	/* copy response packet payload and compute checksum */ +	for (i = 0; i < len; i++) { +		sum += ptr[i + 2]; +		if (ec_msg->in_len) +			ec_msg->in_buf[i] = ptr[i + 2]; +	} +	sum &= 0xff; + +	debug_packet(ec_dev->dev, "in", ptr, len + 3); + +	if (sum != ptr[len + 2]) { +		dev_err(ec_dev->dev, +			"bad packet checksum, expected %02x, got %02x\n", +			sum, ptr[len + 2]); +		return -EBADMSG; +	} + +	return 0; +} + +static int cros_ec_probe_spi(struct spi_device *spi) +{ +	struct device *dev = &spi->dev; +	struct cros_ec_device *ec_dev; +	struct cros_ec_spi *ec_spi; +	int err; + +	spi->bits_per_word = 8; +	spi->mode = SPI_MODE_0; +	err = spi_setup(spi); +	if (err < 0) +		return err; + +	ec_spi = devm_kzalloc(dev, sizeof(*ec_spi), GFP_KERNEL); +	if (ec_spi == NULL) +		return -ENOMEM; +	ec_spi->spi = spi; +	ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); +	if (!ec_dev) +		return -ENOMEM; + +	spi_set_drvdata(spi, ec_dev); +	ec_dev->name = "SPI"; +	ec_dev->dev = dev; +	ec_dev->priv = ec_spi; +	ec_dev->irq = spi->irq; +	ec_dev->command_xfer = cros_ec_command_spi_xfer; +	ec_dev->ec_name = ec_spi->spi->modalias; +	ec_dev->phys_name = dev_name(&ec_spi->spi->dev); +	ec_dev->parent = &ec_spi->spi->dev; +	ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT; +	ec_dev->dout_size = EC_MSG_BYTES; + +	err = cros_ec_register(ec_dev); +	if (err) { +		dev_err(dev, "cannot register EC\n"); +		return err; +	} + +	return 0; +} + +static int cros_ec_remove_spi(struct spi_device *spi) +{ +	struct cros_ec_device *ec_dev; + +	ec_dev = spi_get_drvdata(spi); +	cros_ec_remove(ec_dev); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_spi_suspend(struct device *dev) +{ +	struct cros_ec_device *ec_dev = dev_get_drvdata(dev); + +	return cros_ec_suspend(ec_dev); +} + +static int cros_ec_spi_resume(struct device *dev) +{ +	struct cros_ec_device *ec_dev = dev_get_drvdata(dev); + +	return cros_ec_resume(ec_dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_spi_pm_ops, cros_ec_spi_suspend, +			 cros_ec_spi_resume); + +static const struct spi_device_id cros_ec_spi_id[] = { +	{ "cros-ec-spi", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(spi, cros_ec_spi_id); + +static struct spi_driver cros_ec_driver_spi = { +	.driver	= { +		.name	= "cros-ec-spi", +		.owner	= THIS_MODULE, +		.pm	= &cros_ec_spi_pm_ops, +	}, +	.probe		= cros_ec_probe_spi, +	.remove		= cros_ec_remove_spi, +	.id_table	= cros_ec_spi_id, +}; + +module_spi_driver(cros_ec_driver_spi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)"); diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index 05176cd2862..f1a316e0d6a 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -499,7 +499,8 @@ static int da903x_probe(struct i2c_client *client,  	unsigned int tmp;  	int ret; -	chip = kzalloc(sizeof(struct da903x_chip), GFP_KERNEL); +	chip = devm_kzalloc(&client->dev, sizeof(struct da903x_chip), +				GFP_KERNEL);  	if (chip == NULL)  		return -ENOMEM; @@ -515,33 +516,27 @@ static int da903x_probe(struct i2c_client *client,  	ret = chip->ops->init_chip(chip);  	if (ret) -		goto out_free_chip; +		return ret;  	/* mask and clear all IRQs */  	chip->events_mask = 0xffffffff;  	chip->ops->mask_events(chip, chip->events_mask);  	chip->ops->read_events(chip, &tmp); -	ret = request_irq(client->irq, da903x_irq_handler, +	ret = devm_request_irq(&client->dev, client->irq, da903x_irq_handler,  			IRQF_TRIGGER_FALLING,  			"da903x", chip);  	if (ret) {  		dev_err(&client->dev, "failed to request irq %d\n",  				client->irq); -		goto out_free_chip; +		return ret;  	}  	ret = da903x_add_subdevs(chip, pdata);  	if (ret) -		goto out_free_irq; +		return ret;  	return 0; - -out_free_irq: -	free_irq(client->irq, chip); -out_free_chip: -	kfree(chip); -	return ret;  }  static int da903x_remove(struct i2c_client *client) @@ -549,8 +544,6 @@ static int da903x_remove(struct i2c_client *client)  	struct da903x_chip *chip = i2c_get_clientdata(client);  	da903x_remove_subdevs(chip); -	free_irq(client->irq, chip); -	kfree(chip);  	return 0;  } diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 61d63b93576..0680bcbc53d 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -38,7 +38,7 @@ static int da9052_spi_probe(struct spi_device *spi)  	da9052->dev = &spi->dev;  	da9052->chip_irq = spi->irq; -	dev_set_drvdata(&spi->dev, da9052); +	spi_set_drvdata(spi, da9052);  	da9052_regmap_config.read_flag_mask = 1;  	da9052_regmap_config.write_flag_mask = 0; @@ -60,7 +60,7 @@ static int da9052_spi_probe(struct spi_device *spi)  static int da9052_spi_remove(struct spi_device *spi)  { -	struct da9052 *da9052 = dev_get_drvdata(&spi->dev); +	struct da9052 *da9052 = spi_get_drvdata(spi);  	da9052_device_exit(da9052);  	return 0; diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c index f56a1a9f777..49cb23d3746 100644 --- a/drivers/mfd/da9055-core.c +++ b/drivers/mfd/da9055-core.c @@ -391,7 +391,7 @@ int da9055_device_init(struct da9055 *da9055)  		da9055->irq_base = pdata->irq_base;  	ret = regmap_add_irq_chip(da9055->regmap, da9055->chip_irq, -				  IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +				  IRQF_TRIGGER_LOW | IRQF_ONESHOT,  				  da9055->irq_base, &da9055_regmap_irq_chip,  				  &da9055->irq_data);  	if (ret < 0) diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index c0bcc872af4..c60ab0c3c4d 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -177,17 +177,7 @@ static struct platform_driver davinci_vc_driver = {  	.remove	= davinci_vc_remove,  }; -static int __init davinci_vc_init(void) -{ -	return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe); -} -module_init(davinci_vc_init); - -static void __exit davinci_vc_exit(void) -{ -	platform_driver_unregister(&davinci_vc_driver); -} -module_exit(davinci_vc_exit); +module_platform_driver_probe(davinci_vc_driver, davinci_vc_probe);  MODULE_AUTHOR("Miguel Aguilar");  MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface"); diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 21434beb420..319b8abe742 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -24,6 +24,7 @@  #include <linux/jiffies.h>  #include <linux/bitops.h>  #include <linux/fs.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/uaccess.h>  #include <linux/mfd/core.h> @@ -2704,6 +2705,7 @@ static void dbx500_fw_version_init(struct platform_device *pdev,  {  	struct resource *res;  	void __iomem *tcpm_base; +	u32 version;  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  					   "prcmu-tcpm"); @@ -2713,26 +2715,27 @@ static void dbx500_fw_version_init(struct platform_device *pdev,  		return;  	}  	tcpm_base = ioremap(res->start, resource_size(res)); -	if (tcpm_base != NULL) { -		u32 version; - -		version = readl(tcpm_base + version_offset); -		fw_info.version.project = (version & 0xFF); -		fw_info.version.api_version = (version >> 8) & 0xFF; -		fw_info.version.func_version = (version >> 16) & 0xFF; -		fw_info.version.errata = (version >> 24) & 0xFF; -		strncpy(fw_info.version.project_name, -			fw_project_name(fw_info.version.project), -			PRCMU_FW_PROJECT_NAME_LEN); -		fw_info.valid = true; -		pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n", -			fw_info.version.project_name, -			fw_info.version.project, -			fw_info.version.api_version, -			fw_info.version.func_version, -			fw_info.version.errata); -		iounmap(tcpm_base); +	if (!tcpm_base) { +		dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n"); +		return;  	} + +	version = readl(tcpm_base + version_offset); +	fw_info.version.project = (version & 0xFF); +	fw_info.version.api_version = (version >> 8) & 0xFF; +	fw_info.version.func_version = (version >> 16) & 0xFF; +	fw_info.version.errata = (version >> 24) & 0xFF; +	strncpy(fw_info.version.project_name, +		fw_project_name(fw_info.version.project), +		PRCMU_FW_PROJECT_NAME_LEN); +	fw_info.valid = true; +	pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n", +		fw_info.version.project_name, +		fw_info.version.project, +		fw_info.version.api_version, +		fw_info.version.func_version, +		fw_info.version.errata); +	iounmap(tcpm_base);  }  void __init db8500_prcmu_early_init(u32 phy_base, u32 size) @@ -3065,6 +3068,15 @@ static struct db8500_thsens_platform_data db8500_thsens_data = {  	.num_trips = 4,  }; +static struct mfd_cell common_prcmu_devs[] = { +	{ +		.name = "ux500_wdt", +		.platform_data = &db8500_wdt_pdata, +		.pdata_size = sizeof(db8500_wdt_pdata), +		.id = -1, +	}, +}; +  static struct mfd_cell db8500_prcmu_devs[] = {  	{  		.name = "db8500-prcmu-regulators", @@ -3079,12 +3091,6 @@ static struct mfd_cell db8500_prcmu_devs[] = {  		.pdata_size = sizeof(db8500_cpufreq_table),  	},  	{ -		.name = "ux500_wdt", -		.platform_data = &db8500_wdt_pdata, -		.pdata_size = sizeof(db8500_wdt_pdata), -		.id = -1, -	}, -	{  		.name = "db8500-thermal",  		.num_resources = ARRAY_SIZE(db8500_thsens_resources),  		.resources = db8500_thsens_resources, @@ -3173,13 +3179,25 @@ static int db8500_prcmu_probe(struct platform_device *pdev)  	db8500_prcmu_update_cpufreq(); -	err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, -			      ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, db8500_irq_domain); +	err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs, +			      ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain);  	if (err) {  		pr_err("prcmu: Failed to add subdevices\n");  		return err;  	} +	/* TODO: Remove restriction when clk definitions are available. */ +	if (!of_machine_is_compatible("st-ericsson,u8540")) { +		err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, +				      ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, +				      db8500_irq_domain); +		if (err) { +			mfd_remove_devices(&pdev->dev); +			pr_err("prcmu: Failed to add subdevices\n"); +			goto no_irq_return; +		} +	} +  	err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata,  					   pdata->ab_irq);  	if (err) { diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index b7a61f0f27a..5502106ad51 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -393,7 +393,7 @@ static int pcap_add_subdev(struct pcap_chip *pcap,  static int ezx_pcap_remove(struct spi_device *spi)  { -	struct pcap_chip *pcap = dev_get_drvdata(&spi->dev); +	struct pcap_chip *pcap = spi_get_drvdata(spi);  	struct pcap_platform_data *pdata = spi->dev.platform_data;  	int i, adc_irq; @@ -403,7 +403,7 @@ static int ezx_pcap_remove(struct spi_device *spi)  	/* cleanup ADC */  	adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?  				PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); -	free_irq(adc_irq, pcap); +	devm_free_irq(&spi->dev, adc_irq, pcap);  	mutex_lock(&pcap->adc_mutex);  	for (i = 0; i < PCAP_ADC_MAXQ; i++)  		kfree(pcap->adc_queue[i]); @@ -415,8 +415,6 @@ static int ezx_pcap_remove(struct spi_device *spi)  	destroy_workqueue(pcap->workqueue); -	kfree(pcap); -  	return 0;  } @@ -431,7 +429,7 @@ static int ezx_pcap_probe(struct spi_device *spi)  	if (!pdata)  		goto ret; -	pcap = kzalloc(sizeof(*pcap), GFP_KERNEL); +	pcap = devm_kzalloc(&spi->dev, sizeof(*pcap), GFP_KERNEL);  	if (!pcap) {  		ret = -ENOMEM;  		goto ret; @@ -441,14 +439,14 @@ static int ezx_pcap_probe(struct spi_device *spi)  	mutex_init(&pcap->adc_mutex);  	INIT_WORK(&pcap->isr_work, pcap_isr_work);  	INIT_WORK(&pcap->msr_work, pcap_msr_work); -	dev_set_drvdata(&spi->dev, pcap); +	spi_set_drvdata(spi, pcap);  	/* setup spi */  	spi->bits_per_word = 32;  	spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0);  	ret = spi_setup(spi);  	if (ret) -		goto free_pcap; +		goto ret;  	pcap->spi = spi; @@ -458,7 +456,7 @@ static int ezx_pcap_probe(struct spi_device *spi)  	if (!pcap->workqueue) {  		ret = -ENOMEM;  		dev_err(&spi->dev, "can't create pcap thread\n"); -		goto free_pcap; +		goto ret;  	}  	/* redirect interrupts to AP, except adcdone2 */ @@ -491,7 +489,8 @@ static int ezx_pcap_probe(struct spi_device *spi)  	adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?  					PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); -	ret = request_irq(adc_irq, pcap_adc_irq, 0, "ADC", pcap); +	ret = devm_request_irq(&spi->dev, adc_irq, pcap_adc_irq, 0, "ADC", +				pcap);  	if (ret)  		goto free_irqchip; @@ -511,14 +510,12 @@ static int ezx_pcap_probe(struct spi_device *spi)  remove_subdevs:  	device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);  /* free_adc: */ -	free_irq(adc_irq, pcap); +	devm_free_irq(&spi->dev, adc_irq, pcap);  free_irqchip:  	for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)  		irq_set_chip_and_handler(i, NULL, NULL);  /* destroy_workqueue: */  	destroy_workqueue(pcap->workqueue); -free_pcap: -	kfree(pcap);  ret:  	return ret;  } diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 9e5453d21a6..0285fceb99a 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -208,18 +208,7 @@ static struct platform_driver pasic3_driver = {  	.remove		= pasic3_remove,  }; -static int __init pasic3_base_init(void) -{ -	return platform_driver_probe(&pasic3_driver, pasic3_probe); -} - -static void __exit pasic3_base_exit(void) -{ -	platform_driver_unregister(&pasic3_driver); -} - -module_init(pasic3_base_init); -module_exit(pasic3_base_exit); +module_platform_driver_probe(pasic3_driver, pasic3_probe);  MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");  MODULE_DESCRIPTION("Core driver for HTC PASIC3"); diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index 1804331bd52..5be3b5e1385 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -323,7 +323,8 @@ static int intel_msic_init_devices(struct intel_msic *msic)  	if (pdata->ocd) {  		unsigned gpio = pdata->ocd->gpio; -		ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio"); +		ret = devm_gpio_request_one(&pdev->dev, gpio, +					GPIOF_IN, "ocd_gpio");  		if (ret) {  			dev_err(&pdev->dev, "failed to register OCD GPIO\n");  			return ret; @@ -332,7 +333,6 @@ static int intel_msic_init_devices(struct intel_msic *msic)  		ret = gpio_to_irq(gpio);  		if (ret < 0) {  			dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n"); -			gpio_free(gpio);  			return ret;  		} @@ -359,8 +359,6 @@ static int intel_msic_init_devices(struct intel_msic *msic)  fail:  	mfd_remove_devices(&pdev->dev); -	if (pdata->ocd) -		gpio_free(pdata->ocd->gpio);  	return ret;  } @@ -368,12 +366,8 @@ fail:  static void intel_msic_remove_devices(struct intel_msic *msic)  {  	struct platform_device *pdev = msic->pdev; -	struct intel_msic_platform_data *pdata = pdev->dev.platform_data;  	mfd_remove_devices(&pdev->dev); - -	if (pdata->ocd) -		gpio_free(pdata->ocd->gpio);  }  static int intel_msic_probe(struct platform_device *pdev) diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index ceebf2c1ea9..4b7e6dac1de 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -496,8 +496,8 @@ static int lm3533_device_init(struct lm3533 *lm3533)  	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"); +		ret = devm_gpio_request_one(lm3533->dev, lm3533->gpio_hwen, +					GPIOF_OUT_INIT_LOW, "lm3533-hwen");  		if (ret < 0) {  			dev_err(lm3533->dev,  				"failed to request HWEN GPIO %d\n", @@ -528,8 +528,6 @@ 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;  } @@ -542,8 +540,6 @@ static void lm3533_device_exit(struct lm3533 *lm3533)  	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) diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 4d73963cd8f..1cbb17609c8 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -46,7 +46,7 @@ static struct regmap_config max77686_regmap_config = {  #ifdef CONFIG_OF  static struct of_device_id max77686_pmic_dt_match[] = { -	{.compatible = "maxim,max77686",        .data = 0}, +	{.compatible = "maxim,max77686", .data = NULL},  	{},  }; diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 3032bae20b6..77189daadf1 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -131,7 +131,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi)  	if (!mc13xxx)  		return -ENOMEM; -	dev_set_drvdata(&spi->dev, mc13xxx); +	spi_set_drvdata(spi, mc13xxx);  	spi->mode = SPI_MODE_0 | SPI_CS_HIGH;  	mc13xxx->dev = &spi->dev; @@ -144,7 +144,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi)  		ret = PTR_ERR(mc13xxx->regmap);  		dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",  				ret); -		dev_set_drvdata(&spi->dev, NULL); +		spi_set_drvdata(spi, NULL);  		return ret;  	} @@ -164,7 +164,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi)  static int mc13xxx_spi_remove(struct spi_device *spi)  { -	struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); +	struct mc13xxx *mc13xxx = spi_get_drvdata(spi);  	mc13xxx_common_cleanup(mc13xxx); diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 4febc5c7fde..759fae3ca7f 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -1,8 +1,9 @@  /**   * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI   * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://www.ti.com   * Author: Keshava Munegowda <keshava_mgowda@ti.com> + * Author: Roger Quadros <rogerq@ti.com>   *   * This program is free software: you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2  of @@ -27,6 +28,9 @@  #include <linux/platform_device.h>  #include <linux/platform_data/usb-omap.h>  #include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/err.h>  #include "omap-usb.h" @@ -137,6 +141,49 @@ static inline u8 usbhs_readb(void __iomem *base, u8 reg)  /*-------------------------------------------------------------------------*/ +/** + * Map 'enum usbhs_omap_port_mode' found in <linux/platform_data/usb-omap.h> + * to the device tree binding portN-mode found in + * 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt' + */ +static const char * const port_modes[] = { +	[OMAP_USBHS_PORT_MODE_UNUSED]	= "", +	[OMAP_EHCI_PORT_MODE_PHY]	= "ehci-phy", +	[OMAP_EHCI_PORT_MODE_TLL]	= "ehci-tll", +	[OMAP_EHCI_PORT_MODE_HSIC]	= "ehci-hsic", +	[OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0]	= "ohci-phy-6pin-datse0", +	[OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM]	= "ohci-phy-6pin-dpdm", +	[OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0]	= "ohci-phy-3pin-datse0", +	[OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM]	= "ohci-phy-4pin-dpdm", +	[OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0]	= "ohci-tll-6pin-datse0", +	[OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM]	= "ohci-tll-6pin-dpdm", +	[OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0]	= "ohci-tll-3pin-datse0", +	[OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM]	= "ohci-tll-4pin-dpdm", +	[OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0]	= "ohci-tll-2pin-datse0", +	[OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM]	= "ohci-tll-2pin-dpdm", +}; + +/** + * omap_usbhs_get_dt_port_mode - Get the 'enum usbhs_omap_port_mode' + * from the port mode string. + * @mode: The port mode string, usually obtained from device tree. + * + * The function returns the 'enum usbhs_omap_port_mode' that matches the + * provided port mode string as per the port_modes table. + * If no match is found it returns -ENODEV + */ +static const int omap_usbhs_get_dt_port_mode(const char *mode) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(port_modes); i++) { +		if (!strcmp(mode, port_modes[i])) +			return i; +	} + +	return -ENODEV; +} +  static struct platform_device *omap_usbhs_alloc_child(const char *name,  			struct resource	*res, int num_resources, void *pdata,  			size_t pdata_size, struct device *dev) @@ -278,7 +325,7 @@ static int usbhs_runtime_resume(struct device *dev)  	dev_dbg(dev, "usbhs_runtime_resume\n"); -	omap_tll_enable(); +	omap_tll_enable(pdata);  	if (!IS_ERR(omap->ehci_logic_fck))  		clk_enable(omap->ehci_logic_fck); @@ -353,7 +400,7 @@ static int usbhs_runtime_suspend(struct device *dev)  	if (!IS_ERR(omap->ehci_logic_fck))  		clk_disable(omap->ehci_logic_fck); -	omap_tll_disable(); +	omap_tll_disable(pdata);  	return 0;  } @@ -430,24 +477,10 @@ static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap,  static void omap_usbhs_init(struct device *dev)  {  	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev); -	struct usbhs_omap_platform_data	*pdata = omap->pdata;  	unsigned			reg;  	dev_dbg(dev, "starting TI HSUSB Controller\n"); -	if (pdata->phy_reset) { -		if (gpio_is_valid(pdata->reset_gpio_port[0])) -			gpio_request_one(pdata->reset_gpio_port[0], -					 GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - -		if (gpio_is_valid(pdata->reset_gpio_port[1])) -			gpio_request_one(pdata->reset_gpio_port[1], -					 GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); - -		/* Hold the PHY in RESET for enough time till DIR is high */ -		udelay(10); -	} -  	pm_runtime_get_sync(dev);  	reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); @@ -476,36 +509,59 @@ static void omap_usbhs_init(struct device *dev)  	dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);  	pm_runtime_put_sync(dev); -	if (pdata->phy_reset) { -		/* Hold the PHY in RESET for enough time till -		 * PHY is settled and ready -		 */ -		udelay(10); +} + +static int usbhs_omap_get_dt_pdata(struct device *dev, +					struct usbhs_omap_platform_data *pdata) +{ +	int ret, i; +	struct device_node *node = dev->of_node; -		if (gpio_is_valid(pdata->reset_gpio_port[0])) -			gpio_set_value_cansleep -				(pdata->reset_gpio_port[0], 1); +	ret = of_property_read_u32(node, "num-ports", &pdata->nports); +	if (ret) +		pdata->nports = 0; -		if (gpio_is_valid(pdata->reset_gpio_port[1])) -			gpio_set_value_cansleep -				(pdata->reset_gpio_port[1], 1); +	if (pdata->nports > OMAP3_HS_USB_PORTS) { +		dev_warn(dev, "Too many num_ports <%d> in device tree. Max %d\n", +				pdata->nports, OMAP3_HS_USB_PORTS); +		return -ENODEV;  	} -} -static void omap_usbhs_deinit(struct device *dev) -{ -	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev); -	struct usbhs_omap_platform_data	*pdata = omap->pdata; +	/* get port modes */ +	for (i = 0; i < OMAP3_HS_USB_PORTS; i++) { +		char prop[11]; +		const char *mode; -	if (pdata->phy_reset) { -		if (gpio_is_valid(pdata->reset_gpio_port[0])) -			gpio_free(pdata->reset_gpio_port[0]); +		pdata->port_mode[i] = OMAP_USBHS_PORT_MODE_UNUSED; + +		snprintf(prop, sizeof(prop), "port%d-mode", i + 1); +		ret = of_property_read_string(node, prop, &mode); +		if (ret < 0) +			continue; + +		ret = omap_usbhs_get_dt_port_mode(mode); +		if (ret < 0) { +			dev_warn(dev, "Invalid port%d-mode \"%s\" in device tree\n", +					i, mode); +			return -ENODEV; +		} -		if (gpio_is_valid(pdata->reset_gpio_port[1])) -			gpio_free(pdata->reset_gpio_port[1]); +		dev_dbg(dev, "port%d-mode: %s -> %d\n", i, mode, ret); +		pdata->port_mode[i] = ret;  	} + +	/* get flags */ +	pdata->single_ulpi_bypass = of_property_read_bool(node, +						"single-ulpi-bypass"); + +	return 0;  } +static struct of_device_id usbhs_child_match_table[] = { +	{ .compatible = "ti,omap-ehci", }, +	{ .compatible = "ti,omap-ohci", }, +	{ } +};  /**   * usbhs_omap_probe - initialize TI-based HCDs @@ -522,26 +578,46 @@ static int usbhs_omap_probe(struct platform_device *pdev)  	int				i;  	bool				need_logic_fck; +	if (dev->of_node) { +		/* For DT boot we populate platform data from OF node */ +		pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +		if (!pdata) +			return -ENOMEM; + +		ret = usbhs_omap_get_dt_pdata(dev, pdata); +		if (ret) +			return ret; + +		dev->platform_data = pdata; +	} +  	if (!pdata) {  		dev_err(dev, "Missing platform data\n");  		return -ENODEV;  	} +	if (pdata->nports > OMAP3_HS_USB_PORTS) { +		dev_info(dev, "Too many num_ports <%d> in platform_data. Max %d\n", +				pdata->nports, OMAP3_HS_USB_PORTS); +		return -ENODEV; +	} +  	omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);  	if (!omap) {  		dev_err(dev, "Memory allocation failed\n");  		return -ENOMEM;  	} -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); -	omap->uhh_base = devm_request_and_ioremap(dev, res); -	if (!omap->uhh_base) { -		dev_err(dev, "Resource request/ioremap failed\n"); -		return -EADDRNOTAVAIL; -	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	omap->uhh_base = devm_ioremap_resource(dev, res); +	if (IS_ERR(omap->uhh_base)) +		return PTR_ERR(omap->uhh_base);  	omap->pdata = pdata; +	/* Initialize the TLL subsystem */ +	omap_tll_init(pdata); +  	pm_runtime_enable(dev);  	platform_set_drvdata(pdev, omap); @@ -575,6 +651,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)  			 omap->usbhs_rev, omap->nports);  			break;  		} +		pdata->nports = omap->nports;  	}  	i = sizeof(struct clk *) * omap->nports; @@ -700,17 +777,28 @@ static int usbhs_omap_probe(struct platform_device *pdev)  	}  	omap_usbhs_init(dev); -	ret = omap_usbhs_alloc_children(pdev); -	if (ret) { -		dev_err(dev, "omap_usbhs_alloc_children failed\n"); -		goto err_alloc; + +	if (dev->of_node) { +		ret = of_platform_populate(dev->of_node, +				usbhs_child_match_table, NULL, dev); + +		if (ret) { +			dev_err(dev, "Failed to create DT children: %d\n", ret); +			goto err_alloc; +		} + +	} else { +		ret = omap_usbhs_alloc_children(pdev); +		if (ret) { +			dev_err(dev, "omap_usbhs_alloc_children failed: %d\n", +						ret); +			goto err_alloc; +		}  	}  	return 0;  err_alloc: -	omap_usbhs_deinit(&pdev->dev); -  	for (i = 0; i < omap->nports; i++) {  		if (!IS_ERR(omap->utmi_clk[i]))  			clk_put(omap->utmi_clk[i]); @@ -744,6 +832,13 @@ err_mem:  	return ret;  } +static int usbhs_omap_remove_child(struct device *dev, void *data) +{ +	dev_info(dev, "unregistering\n"); +	platform_device_unregister(to_platform_device(dev)); +	return 0; +} +  /**   * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs   * @pdev: USB Host Controller being removed @@ -755,8 +850,6 @@ static int usbhs_omap_remove(struct platform_device *pdev)  	struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);  	int i; -	omap_usbhs_deinit(&pdev->dev); -  	for (i = 0; i < omap->nports; i++) {  		if (!IS_ERR(omap->utmi_clk[i]))  			clk_put(omap->utmi_clk[i]); @@ -777,6 +870,8 @@ static int usbhs_omap_remove(struct platform_device *pdev)  	pm_runtime_disable(&pdev->dev); +	/* remove children */ +	device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child);  	return 0;  } @@ -785,16 +880,26 @@ static const struct dev_pm_ops usbhsomap_dev_pm_ops = {  	.runtime_resume		= usbhs_runtime_resume,  }; +static const struct of_device_id usbhs_omap_dt_ids[] = { +	{ .compatible = "ti,usbhs-host" }, +	{ } +}; + +MODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids); + +  static struct platform_driver usbhs_omap_driver = {  	.driver = {  		.name		= (char *)usbhs_driver_name,  		.owner		= THIS_MODULE,  		.pm		= &usbhsomap_dev_pm_ops, +		.of_match_table = of_match_ptr(usbhs_omap_dt_ids),  	},  	.remove		= usbhs_omap_remove,  };  MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); +MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");  MODULE_ALIAS("platform:" USBHS_DRIVER_NAME);  MODULE_LICENSE("GPL v2");  MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI"); diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 0aef1a76888..e59ac4cbac9 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -1,8 +1,9 @@  /**   * omap-usb-tll.c - The USB TLL driver for OMAP EHCI & OHCI   * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2012-2013 Texas Instruments Incorporated - http://www.ti.com   * Author: Keshava Munegowda <keshava_mgowda@ti.com> + * Author: Roger Quadros <rogerq@ti.com>   *   * This program is free software: you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2  of @@ -27,6 +28,7 @@  #include <linux/err.h>  #include <linux/pm_runtime.h>  #include <linux/platform_data/usb-omap.h> +#include <linux/of.h>  #define USBTLL_DRIVER_NAME	"usbhs_tll" @@ -105,8 +107,8 @@  struct usbtll_omap {  	int					nch;	/* num. of channels */ -	struct usbhs_omap_platform_data		*pdata;  	struct clk				**ch_clk; +	void __iomem				*base;  };  /*-------------------------------------------------------------------------*/ @@ -210,14 +212,10 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)  static int usbtll_omap_probe(struct platform_device *pdev)  {  	struct device				*dev =  &pdev->dev; -	struct usbhs_omap_platform_data		*pdata = dev->platform_data; -	void __iomem				*base;  	struct resource				*res;  	struct usbtll_omap			*tll; -	unsigned				reg;  	int					ret = 0;  	int					i, ver; -	bool needs_tll;  	dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); @@ -227,26 +225,16 @@ static int usbtll_omap_probe(struct platform_device *pdev)  		return -ENOMEM;  	} -	if (!pdata) { -		dev_err(dev, "Platform data missing\n"); -		return -ENODEV; -	} - -	tll->pdata = pdata; -  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	base = devm_request_and_ioremap(dev, res); -	if (!base) { -		ret = -EADDRNOTAVAIL; -		dev_err(dev, "Resource request/ioremap failed:%d\n", ret); -		return ret; -	} +	tll->base = devm_ioremap_resource(dev, res); +	if (IS_ERR(tll->base)) +		return PTR_ERR(tll->base);  	platform_set_drvdata(pdev, tll);  	pm_runtime_enable(dev);  	pm_runtime_get_sync(dev); -	ver =  usbtll_read(base, OMAP_USBTLL_REVISION); +	ver =  usbtll_read(tll->base, OMAP_USBTLL_REVISION);  	switch (ver) {  	case OMAP_USBTLL_REV1:  	case OMAP_USBTLL_REV4: @@ -283,11 +271,85 @@ static int usbtll_omap_probe(struct platform_device *pdev)  			dev_dbg(dev, "can't get clock : %s\n", clkname);  	} +	pm_runtime_put_sync(dev); +	/* only after this can omap_tll_enable/disable work */ +	spin_lock(&tll_lock); +	tll_dev = dev; +	spin_unlock(&tll_lock); + +	return 0; + +err_clk_alloc: +	pm_runtime_put_sync(dev); +	pm_runtime_disable(dev); + +	return ret; +} + +/** + * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usbtll_omap_probe(). + */ +static int usbtll_omap_remove(struct platform_device *pdev) +{ +	struct usbtll_omap *tll = platform_get_drvdata(pdev); +	int i; + +	spin_lock(&tll_lock); +	tll_dev = NULL; +	spin_unlock(&tll_lock); + +	for (i = 0; i < tll->nch; i++) +		if (!IS_ERR(tll->ch_clk[i])) +			clk_put(tll->ch_clk[i]); + +	pm_runtime_disable(&pdev->dev); +	return 0; +} + +static const struct of_device_id usbtll_omap_dt_ids[] = { +	{ .compatible = "ti,usbhs-tll" }, +	{ } +}; + +MODULE_DEVICE_TABLE(of, usbtll_omap_dt_ids); + +static struct platform_driver usbtll_omap_driver = { +	.driver = { +		.name		= (char *)usbtll_driver_name, +		.owner		= THIS_MODULE, +		.of_match_table = of_match_ptr(usbtll_omap_dt_ids), +	}, +	.probe		= usbtll_omap_probe, +	.remove		= usbtll_omap_remove, +}; + +int omap_tll_init(struct usbhs_omap_platform_data *pdata) +{ +	int i; +	bool needs_tll; +	unsigned reg; +	struct usbtll_omap *tll; + +	spin_lock(&tll_lock); + +	if (!tll_dev) { +		spin_unlock(&tll_lock); +		return -ENODEV; +	} + +	tll = dev_get_drvdata(tll_dev); +  	needs_tll = false;  	for (i = 0; i < tll->nch; i++)  		needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); +	pm_runtime_get_sync(tll_dev); +  	if (needs_tll) { +		void __iomem *base = tll->base;  		/* Program Common TLL register */  		reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); @@ -336,51 +398,29 @@ static int usbtll_omap_probe(struct platform_device *pdev)  		}  	} -	pm_runtime_put_sync(dev); -	/* only after this can omap_tll_enable/disable work */ -	spin_lock(&tll_lock); -	tll_dev = dev; +	pm_runtime_put_sync(tll_dev); +  	spin_unlock(&tll_lock);  	return 0; - -err_clk_alloc: -	pm_runtime_put_sync(dev); -	pm_runtime_disable(dev); - -	return ret;  } +EXPORT_SYMBOL_GPL(omap_tll_init); -/** - * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs - * @pdev: USB Host Controller being removed - * - * Reverses the effect of usbtll_omap_probe(). - */ -static int usbtll_omap_remove(struct platform_device *pdev) +int omap_tll_enable(struct usbhs_omap_platform_data *pdata)  { -	struct usbtll_omap *tll = platform_get_drvdata(pdev);  	int i; +	struct usbtll_omap *tll;  	spin_lock(&tll_lock); -	tll_dev = NULL; -	spin_unlock(&tll_lock); -	for (i = 0; i < tll->nch; i++) -		if (!IS_ERR(tll->ch_clk[i])) -			clk_put(tll->ch_clk[i]); - -	pm_runtime_disable(&pdev->dev); -	return 0; -} +	if (!tll_dev) { +		spin_unlock(&tll_lock); +		return -ENODEV; +	} -static int usbtll_runtime_resume(struct device *dev) -{ -	struct usbtll_omap			*tll = dev_get_drvdata(dev); -	struct usbhs_omap_platform_data		*pdata = tll->pdata; -	int i; +	tll = dev_get_drvdata(tll_dev); -	dev_dbg(dev, "usbtll_runtime_resume\n"); +	pm_runtime_get_sync(tll_dev);  	for (i = 0; i < tll->nch; i++) {  		if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { @@ -391,22 +431,31 @@ static int usbtll_runtime_resume(struct device *dev)  			r = clk_enable(tll->ch_clk[i]);  			if (r) { -				dev_err(dev, +				dev_err(tll_dev,  				 "Error enabling ch %d clock: %d\n", i, r);  			}  		}  	} +	spin_unlock(&tll_lock); +  	return 0;  } +EXPORT_SYMBOL_GPL(omap_tll_enable); -static int usbtll_runtime_suspend(struct device *dev) +int omap_tll_disable(struct usbhs_omap_platform_data *pdata)  { -	struct usbtll_omap			*tll = dev_get_drvdata(dev); -	struct usbhs_omap_platform_data		*pdata = tll->pdata;  	int i; +	struct usbtll_omap *tll; -	dev_dbg(dev, "usbtll_runtime_suspend\n"); +	spin_lock(&tll_lock); + +	if (!tll_dev) { +		spin_unlock(&tll_lock); +		return -ENODEV; +	} + +	tll = dev_get_drvdata(tll_dev);  	for (i = 0; i < tll->nch; i++) {  		if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { @@ -415,64 +464,16 @@ static int usbtll_runtime_suspend(struct device *dev)  		}  	} -	return 0; -} - -static const struct dev_pm_ops usbtllomap_dev_pm_ops = { -	SET_RUNTIME_PM_OPS(usbtll_runtime_suspend, -			   usbtll_runtime_resume, -			   NULL) -}; - -static struct platform_driver usbtll_omap_driver = { -	.driver = { -		.name		= (char *)usbtll_driver_name, -		.owner		= THIS_MODULE, -		.pm		= &usbtllomap_dev_pm_ops, -	}, -	.probe		= usbtll_omap_probe, -	.remove		= usbtll_omap_remove, -}; - -int omap_tll_enable(void) -{ -	int ret; - -	spin_lock(&tll_lock); - -	if (!tll_dev) { -		pr_err("%s: OMAP USB TLL not initialized\n", __func__); -		ret = -ENODEV; -	} else { -		ret = pm_runtime_get_sync(tll_dev); -	} - -	spin_unlock(&tll_lock); - -	return ret; -} -EXPORT_SYMBOL_GPL(omap_tll_enable); - -int omap_tll_disable(void) -{ -	int ret; - -	spin_lock(&tll_lock); - -	if (!tll_dev) { -		pr_err("%s: OMAP USB TLL not initialized\n", __func__); -		ret = -ENODEV; -	} else { -		ret = pm_runtime_put_sync(tll_dev); -	} +	pm_runtime_put_sync(tll_dev);  	spin_unlock(&tll_lock); -	return ret; +	return 0;  }  EXPORT_SYMBOL_GPL(omap_tll_disable);  MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); +MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");  MODULE_ALIAS("platform:" USBHS_DRIVER_NAME);  MODULE_LICENSE("GPL v2");  MODULE_DESCRIPTION("usb tll driver for TI OMAP EHCI and OHCI controllers"); diff --git a/drivers/mfd/omap-usb.h b/drivers/mfd/omap-usb.h index 972aa961b06..2a508b6aeac 100644 --- a/drivers/mfd/omap-usb.h +++ b/drivers/mfd/omap-usb.h @@ -1,2 +1,3 @@ -extern int omap_tll_enable(void); -extern int omap_tll_disable(void); +extern int omap_tll_init(struct usbhs_omap_platform_data *pdata); +extern int omap_tll_enable(struct usbhs_omap_platform_data *pdata); +extern int omap_tll_disable(struct usbhs_omap_platform_data *pdata); diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 73bf76df104..53e9fe638d3 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -278,20 +278,20 @@ static void palmas_dt_to_pdata(struct i2c_client *i2c,  	int ret;  	u32 prop; -	ret = of_property_read_u32(node, "ti,mux_pad1", &prop); +	ret = of_property_read_u32(node, "ti,mux-pad1", &prop);  	if (!ret) {  		pdata->mux_from_pdata = 1;  		pdata->pad1 = prop;  	} -	ret = of_property_read_u32(node, "ti,mux_pad2", &prop); +	ret = of_property_read_u32(node, "ti,mux-pad2", &prop);  	if (!ret) {  		pdata->mux_from_pdata = 1;  		pdata->pad2 = prop;  	}  	/* The default for this register is all masked */ -	ret = of_property_read_u32(node, "ti,power_ctrl", &prop); +	ret = of_property_read_u32(node, "ti,power-ctrl", &prop);  	if (!ret)  		pdata->power_ctrl = prop;  	else @@ -349,6 +349,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  				ret = -ENOMEM;  				goto err;  			} +			palmas->i2c_clients[i]->dev.of_node = of_node_get(node);  		}  		palmas->regmap[i] = devm_regmap_init_i2c(palmas->i2c_clients[i],  				&palmas_regmap_config[i]); diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index 3ba048655bf..a1830986eeb 100644 --- a/drivers/mfd/retu-mfd.c +++ b/drivers/mfd/retu-mfd.c @@ -1,5 +1,5 @@  /* - * Retu MFD driver + * Retu/Tahvo MFD driver   *   * Copyright (C) 2004, 2005 Nokia Corporation   * @@ -33,7 +33,8 @@  #define RETU_REG_ASICR		0x00		/* ASIC ID and revision */  #define RETU_REG_ASICR_VILMA	(1 << 7)	/* Bit indicating Vilma */  #define RETU_REG_IDR		0x01		/* Interrupt ID */ -#define RETU_REG_IMR		0x02		/* Interrupt mask */ +#define RETU_REG_IMR		0x02		/* Interrupt mask (Retu) */ +#define TAHVO_REG_IMR		0x03		/* Interrupt mask (Tahvo) */  /* Interrupt sources */  #define RETU_INT_PWR		0		/* Power button */ @@ -84,6 +85,62 @@ static struct regmap_irq_chip retu_irq_chip = {  /* Retu device registered for the power off. */  static struct retu_dev *retu_pm_power_off; +static struct resource tahvo_usb_res[] = { +	{ +		.name	= "tahvo-usb", +		.start	= TAHVO_INT_VBUS, +		.end	= TAHVO_INT_VBUS, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct mfd_cell tahvo_devs[] = { +	{ +		.name		= "tahvo-usb", +		.resources	= tahvo_usb_res, +		.num_resources	= ARRAY_SIZE(tahvo_usb_res), +	}, +}; + +static struct regmap_irq tahvo_irqs[] = { +	[TAHVO_INT_VBUS] = { +		.mask = 1 << TAHVO_INT_VBUS, +	} +}; + +static struct regmap_irq_chip tahvo_irq_chip = { +	.name		= "TAHVO", +	.irqs		= tahvo_irqs, +	.num_irqs	= ARRAY_SIZE(tahvo_irqs), +	.num_regs	= 1, +	.status_base	= RETU_REG_IDR, +	.mask_base	= TAHVO_REG_IMR, +	.ack_base	= RETU_REG_IDR, +}; + +static const struct retu_data { +	char			*chip_name; +	char			*companion_name; +	struct regmap_irq_chip	*irq_chip; +	struct mfd_cell		*children; +	int			nchildren; +} retu_data[] = { +	[0] = { +		.chip_name	= "Retu", +		.companion_name	= "Vilma", +		.irq_chip	= &retu_irq_chip, +		.children	= retu_devs, +		.nchildren	= ARRAY_SIZE(retu_devs), +	}, +	[1] = { +		.chip_name	= "Tahvo", +		.companion_name	= "Betty", +		.irq_chip	= &tahvo_irq_chip, +		.children	= tahvo_devs, +		.nchildren	= ARRAY_SIZE(tahvo_devs), +	} +}; +  int retu_read(struct retu_dev *rdev, u8 reg)  {  	int ret; @@ -173,9 +230,14 @@ static struct regmap_config retu_config = {  static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id)  { +	struct retu_data const *rdat;  	struct retu_dev *rdev;  	int ret; +	if (i2c->addr > ARRAY_SIZE(retu_data)) +		return -ENODEV; +	rdat = &retu_data[i2c->addr - 1]; +  	rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL);  	if (rdev == NULL)  		return -ENOMEM; @@ -190,25 +252,27 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id)  	ret = retu_read(rdev, RETU_REG_ASICR);  	if (ret < 0) { -		dev_err(rdev->dev, "could not read Retu revision: %d\n", ret); +		dev_err(rdev->dev, "could not read %s revision: %d\n", +			rdat->chip_name, ret);  		return ret;  	} -	dev_info(rdev->dev, "Retu%s v%d.%d found\n", -		 (ret & RETU_REG_ASICR_VILMA) ? " & Vilma" : "", +	dev_info(rdev->dev, "%s%s%s v%d.%d found\n", rdat->chip_name, +		 (ret & RETU_REG_ASICR_VILMA) ? " & " : "", +		 (ret & RETU_REG_ASICR_VILMA) ? rdat->companion_name : "",  		 (ret >> 4) & 0x7, ret & 0xf); -	/* Mask all RETU interrupts. */ -	ret = retu_write(rdev, RETU_REG_IMR, 0xffff); +	/* Mask all interrupts. */ +	ret = retu_write(rdev, rdat->irq_chip->mask_base, 0xffff);  	if (ret < 0)  		return ret;  	ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1, -				  &retu_irq_chip, &rdev->irq_data); +				  rdat->irq_chip, &rdev->irq_data);  	if (ret < 0)  		return ret; -	ret = mfd_add_devices(rdev->dev, -1, retu_devs, ARRAY_SIZE(retu_devs), +	ret = mfd_add_devices(rdev->dev, -1, rdat->children, rdat->nchildren,  			      NULL, regmap_irq_chip_get_base(rdev->irq_data),  			      NULL);  	if (ret < 0) { @@ -216,7 +280,7 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id)  		return ret;  	} -	if (!pm_power_off) { +	if (i2c->addr == 1 && !pm_power_off) {  		retu_pm_power_off = rdev;  		pm_power_off	  = retu_power_off;  	} @@ -240,6 +304,7 @@ static int retu_remove(struct i2c_client *i2c)  static const struct i2c_device_id retu_id[] = {  	{ "retu-mfd", 0 }, +	{ "tahvo-mfd", 0 },  	{ }  };  MODULE_DEVICE_TABLE(i2c, retu_id); diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c new file mode 100644 index 00000000000..15dc848bc08 --- /dev/null +++ b/drivers/mfd/rts5249.c @@ -0,0 +1,241 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: + *   Wei WANG <wei_wang@realsil.com.cn> + *   No. 128, West Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/mfd/rtsx_pci.h> + +#include "rtsx_pcr.h" + +static u8 rts5249_get_ic_version(struct rtsx_pcr *pcr) +{ +	u8 val; + +	rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); +	return val & 0x0F; +} + +static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) +{ +	rtsx_pci_init_cmd(pcr); + +	/* Configure GPIO as output */ +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); +	/* Switch LDO3318 source from DV33 to card_3v3 */ +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); +	/* LED shine disabled, set initial shine cycle period */ +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); +	/* Correct driving */ +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, +			SD30_CLK_DRIVE_SEL, 0xFF, 0x99); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, +			SD30_CMD_DRIVE_SEL, 0xFF, 0x99); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, +			SD30_DAT_DRIVE_SEL, 0xFF, 0x92); + +	return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5249_optimize_phy(struct rtsx_pcr *pcr) +{ +	int err; + +	err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, 0xFE46); +	if (err < 0) +		return err; + +	msleep(1); + +	return rtsx_pci_write_phy_register(pcr, PHY_BPCR, 0x05C0); +} + +static int rts5249_turn_on_led(struct rtsx_pcr *pcr) +{ +	return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); +} + +static int rts5249_turn_off_led(struct rtsx_pcr *pcr) +{ +	return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); +} + +static int rts5249_enable_auto_blink(struct rtsx_pcr *pcr) +{ +	return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); +} + +static int rts5249_disable_auto_blink(struct rtsx_pcr *pcr) +{ +	return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); +} + +static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card) +{ +	int err; + +	rtsx_pci_init_cmd(pcr); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, +			SD_POWER_MASK, SD_VCC_PARTIAL_POWER_ON); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, +			LDO3318_PWR_MASK, 0x02); +	err = rtsx_pci_send_cmd(pcr, 100); +	if (err < 0) +		return err; + +	msleep(5); + +	rtsx_pci_init_cmd(pcr); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, +			SD_POWER_MASK, SD_VCC_POWER_ON); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, +			LDO3318_PWR_MASK, 0x06); +	err = rtsx_pci_send_cmd(pcr, 100); +	if (err < 0) +		return err; + +	return 0; +} + +static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) +{ +	rtsx_pci_init_cmd(pcr); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, +			SD_POWER_MASK, SD_POWER_OFF); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, +			LDO3318_PWR_MASK, 0x00); +	return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ +	int err; +	u8 clk_drive, cmd_drive, dat_drive; + +	if (voltage == OUTPUT_3V3) { +		err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4FC0 | 0x24); +		if (err < 0) +			return err; +		clk_drive = 0x99; +		cmd_drive = 0x99; +		dat_drive = 0x92; +	} else if (voltage == OUTPUT_1V8) { +		err = rtsx_pci_write_phy_register(pcr, PHY_BACR, 0x3C02); +		if (err < 0) +			return err; +		err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4C40 | 0x24); +		if (err < 0) +			return err; +		clk_drive = 0xb3; +		cmd_drive = 0xb3; +		dat_drive = 0xb3; +	} else { +		return -EINVAL; +	} + +	/* set pad drive */ +	rtsx_pci_init_cmd(pcr); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, +			0xFF, clk_drive); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, +			0xFF, cmd_drive); +	rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, +			0xFF, dat_drive); +	return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5249_pcr_ops = { +	.extra_init_hw = rts5249_extra_init_hw, +	.optimize_phy = rts5249_optimize_phy, +	.turn_on_led = rts5249_turn_on_led, +	.turn_off_led = rts5249_turn_off_led, +	.enable_auto_blink = rts5249_enable_auto_blink, +	.disable_auto_blink = rts5249_disable_auto_blink, +	.card_power_on = rts5249_card_power_on, +	.card_power_off = rts5249_card_power_off, +	.switch_output_voltage = rts5249_switch_output_voltage, +}; + +/* SD Pull Control Enable: + *     SD_DAT[3:0] ==> pull up + *     SD_CD       ==> pull up + *     SD_WP       ==> pull up + *     SD_CMD      ==> pull up + *     SD_CLK      ==> pull down + */ +static const u32 rts5249_sd_pull_ctl_enable_tbl[] = { +	RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), +	RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), +	RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), +	RTSX_REG_PAIR(CARD_PULL_CTL4, 0xAA), +	0, +}; + +/* SD Pull Control Disable: + *     SD_DAT[3:0] ==> pull down + *     SD_CD       ==> pull up + *     SD_WP       ==> pull down + *     SD_CMD      ==> pull down + *     SD_CLK      ==> pull down + */ +static const u32 rts5249_sd_pull_ctl_disable_tbl[] = { +	RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), +	RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), +	RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), +	RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), +	0, +}; + +/* MS Pull Control Enable: + *     MS CD       ==> pull up + *     others      ==> pull down + */ +static const u32 rts5249_ms_pull_ctl_enable_tbl[] = { +	RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), +	RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), +	RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), +	0, +}; + +/* MS Pull Control Disable: + *     MS CD       ==> pull up + *     others      ==> pull down + */ +static const u32 rts5249_ms_pull_ctl_disable_tbl[] = { +	RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), +	RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), +	RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), +	0, +}; + +void rts5249_init_params(struct rtsx_pcr *pcr) +{ +	pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; +	pcr->num_slots = 2; +	pcr->ops = &rts5249_pcr_ops; + +	pcr->ic_version = rts5249_get_ic_version(pcr); +	pcr->sd_pull_ctl_enable_tbl = rts5249_sd_pull_ctl_enable_tbl; +	pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl; +	pcr->ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl; +	pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 2f12cc13489..e968c01ca2a 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -56,6 +56,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = {  	{ PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 },  	{ PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 },  	{ PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, +	{ PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 },  	{ 0, }  }; @@ -1033,6 +1034,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)  	case 0x5227:  		rts5227_init_params(pcr);  		break; + +	case 0x5249: +		rts5249_init_params(pcr); +		break;  	}  	dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", @@ -1138,7 +1143,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,  	ret = rtsx_pci_acquire_irq(pcr);  	if (ret < 0) -		goto free_dma; +		goto disable_msi;  	pci_set_master(pcidev);  	synchronize_irq(pcr->irq); @@ -1162,7 +1167,9 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,  disable_irq:  	free_irq(pcr->irq, (void *)pcr); -free_dma: +disable_msi: +	if (pcr->msi_en) +		pci_disable_msi(pcr->pci);  	dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN,  			pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);  unmap: diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 2b3ab8a0482..55fcfc25c4e 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -32,5 +32,6 @@ void rts5209_init_params(struct rtsx_pcr *pcr);  void rts5229_init_params(struct rtsx_pcr *pcr);  void rtl8411_init_params(struct rtsx_pcr *pcr);  void rts5227_init_params(struct rtsx_pcr *pcr); +void rts5249_init_params(struct rtsx_pcr *pcr);  #endif diff --git a/drivers/mfd/si476x-cmd.c b/drivers/mfd/si476x-cmd.c new file mode 100644 index 00000000000..de48b4e8845 --- /dev/null +++ b/drivers/mfd/si476x-cmd.c @@ -0,0 +1,1553 @@ +/* + * drivers/mfd/si476x-cmd.c -- Subroutines implementing command + * protocol of si476x series of chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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; version 2 of the License. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/atomic.h> +#include <linux/i2c.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/videodev2.h> + +#include <linux/mfd/si476x-core.h> + +#define msb(x)                  ((u8)((u16) x >> 8)) +#define lsb(x)                  ((u8)((u16) x &  0x00FF)) + + + +#define CMD_POWER_UP				0x01 +#define CMD_POWER_UP_A10_NRESP			1 +#define CMD_POWER_UP_A10_NARGS			5 + +#define CMD_POWER_UP_A20_NRESP			1 +#define CMD_POWER_UP_A20_NARGS			5 + +#define POWER_UP_DELAY_MS			110 + +#define CMD_POWER_DOWN				0x11 +#define CMD_POWER_DOWN_A10_NRESP		1 + +#define CMD_POWER_DOWN_A20_NRESP		1 +#define CMD_POWER_DOWN_A20_NARGS		1 + +#define CMD_FUNC_INFO				0x12 +#define CMD_FUNC_INFO_NRESP			7 + +#define CMD_SET_PROPERTY			0x13 +#define CMD_SET_PROPERTY_NARGS			5 +#define CMD_SET_PROPERTY_NRESP			1 + +#define CMD_GET_PROPERTY			0x14 +#define CMD_GET_PROPERTY_NARGS			3 +#define CMD_GET_PROPERTY_NRESP			4 + +#define CMD_AGC_STATUS				0x17 +#define CMD_AGC_STATUS_NRESP_A10		2 +#define CMD_AGC_STATUS_NRESP_A20                6 + +#define PIN_CFG_BYTE(x) (0x7F & (x)) +#define CMD_DIG_AUDIO_PIN_CFG			0x18 +#define CMD_DIG_AUDIO_PIN_CFG_NARGS		4 +#define CMD_DIG_AUDIO_PIN_CFG_NRESP		5 + +#define CMD_ZIF_PIN_CFG				0x19 +#define CMD_ZIF_PIN_CFG_NARGS			4 +#define CMD_ZIF_PIN_CFG_NRESP			5 + +#define CMD_IC_LINK_GPO_CTL_PIN_CFG		0x1A +#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS	4 +#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP	5 + +#define CMD_ANA_AUDIO_PIN_CFG			0x1B +#define CMD_ANA_AUDIO_PIN_CFG_NARGS		1 +#define CMD_ANA_AUDIO_PIN_CFG_NRESP		2 + +#define CMD_INTB_PIN_CFG			0x1C +#define CMD_INTB_PIN_CFG_NARGS			2 +#define CMD_INTB_PIN_CFG_A10_NRESP		6 +#define CMD_INTB_PIN_CFG_A20_NRESP		3 + +#define CMD_FM_TUNE_FREQ			0x30 +#define CMD_FM_TUNE_FREQ_A10_NARGS		5 +#define CMD_FM_TUNE_FREQ_A20_NARGS		3 +#define CMD_FM_TUNE_FREQ_NRESP			1 + +#define CMD_FM_RSQ_STATUS			0x32 + +#define CMD_FM_RSQ_STATUS_A10_NARGS		1 +#define CMD_FM_RSQ_STATUS_A10_NRESP		17 +#define CMD_FM_RSQ_STATUS_A30_NARGS		1 +#define CMD_FM_RSQ_STATUS_A30_NRESP		23 + + +#define CMD_FM_SEEK_START			0x31 +#define CMD_FM_SEEK_START_NARGS			1 +#define CMD_FM_SEEK_START_NRESP			1 + +#define CMD_FM_RDS_STATUS			0x36 +#define CMD_FM_RDS_STATUS_NARGS			1 +#define CMD_FM_RDS_STATUS_NRESP			16 + +#define CMD_FM_RDS_BLOCKCOUNT			0x37 +#define CMD_FM_RDS_BLOCKCOUNT_NARGS		1 +#define CMD_FM_RDS_BLOCKCOUNT_NRESP		8 + +#define CMD_FM_PHASE_DIVERSITY			0x38 +#define CMD_FM_PHASE_DIVERSITY_NARGS		1 +#define CMD_FM_PHASE_DIVERSITY_NRESP		1 + +#define CMD_FM_PHASE_DIV_STATUS			0x39 +#define CMD_FM_PHASE_DIV_STATUS_NRESP		2 + +#define CMD_AM_TUNE_FREQ			0x40 +#define CMD_AM_TUNE_FREQ_NARGS			3 +#define CMD_AM_TUNE_FREQ_NRESP			1 + +#define CMD_AM_RSQ_STATUS			0x42 +#define CMD_AM_RSQ_STATUS_NARGS			1 +#define CMD_AM_RSQ_STATUS_NRESP			13 + +#define CMD_AM_SEEK_START			0x41 +#define CMD_AM_SEEK_START_NARGS			1 +#define CMD_AM_SEEK_START_NRESP			1 + + +#define CMD_AM_ACF_STATUS			0x45 +#define CMD_AM_ACF_STATUS_NRESP			6 +#define CMD_AM_ACF_STATUS_NARGS			1 + +#define CMD_FM_ACF_STATUS			0x35 +#define CMD_FM_ACF_STATUS_NRESP			8 +#define CMD_FM_ACF_STATUS_NARGS			1 + +#define CMD_MAX_ARGS_COUNT			(10) + + +enum si476x_acf_status_report_bits { +	SI476X_ACF_BLEND_INT	= (1 << 4), +	SI476X_ACF_HIBLEND_INT	= (1 << 3), +	SI476X_ACF_HICUT_INT	= (1 << 2), +	SI476X_ACF_CHBW_INT	= (1 << 1), +	SI476X_ACF_SOFTMUTE_INT	= (1 << 0), + +	SI476X_ACF_SMUTE	= (1 << 0), +	SI476X_ACF_SMATTN	= 0b11111, +	SI476X_ACF_PILOT	= (1 << 7), +	SI476X_ACF_STBLEND	= ~SI476X_ACF_PILOT, +}; + +enum si476x_agc_status_report_bits { +	SI476X_AGC_MXHI		= (1 << 5), +	SI476X_AGC_MXLO		= (1 << 4), +	SI476X_AGC_LNAHI	= (1 << 3), +	SI476X_AGC_LNALO	= (1 << 2), +}; + +enum si476x_errors { +	SI476X_ERR_BAD_COMMAND		= 0x10, +	SI476X_ERR_BAD_ARG1		= 0x11, +	SI476X_ERR_BAD_ARG2		= 0x12, +	SI476X_ERR_BAD_ARG3		= 0x13, +	SI476X_ERR_BAD_ARG4		= 0x14, +	SI476X_ERR_BUSY			= 0x18, +	SI476X_ERR_BAD_INTERNAL_MEMORY  = 0x20, +	SI476X_ERR_BAD_PATCH		= 0x30, +	SI476X_ERR_BAD_BOOT_MODE	= 0x31, +	SI476X_ERR_BAD_PROPERTY		= 0x40, +}; + +static int si476x_core_parse_and_nag_about_error(struct si476x_core *core) +{ +	int err; +	char *cause; +	u8 buffer[2]; + +	if (core->revision != SI476X_REVISION_A10) { +		err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, +					   buffer, sizeof(buffer)); +		if (err == sizeof(buffer)) { +			switch (buffer[1]) { +			case SI476X_ERR_BAD_COMMAND: +				cause = "Bad command"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BAD_ARG1: +				cause = "Bad argument #1"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BAD_ARG2: +				cause = "Bad argument #2"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BAD_ARG3: +				cause = "Bad argument #3"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BAD_ARG4: +				cause = "Bad argument #4"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BUSY: +				cause = "Chip is busy"; +				err = -EBUSY; +				break; +			case SI476X_ERR_BAD_INTERNAL_MEMORY: +				cause = "Bad internal memory"; +				err = -EIO; +				break; +			case SI476X_ERR_BAD_PATCH: +				cause = "Bad patch"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BAD_BOOT_MODE: +				cause = "Bad boot mode"; +				err = -EINVAL; +				break; +			case SI476X_ERR_BAD_PROPERTY: +				cause = "Bad property"; +				err = -EINVAL; +				break; +			default: +				cause = "Unknown"; +				err = -EIO; +			} + +			dev_err(&core->client->dev, +				"[Chip error status]: %s\n", cause); +		} else { +			dev_err(&core->client->dev, +				"Failed to fetch error code\n"); +			err = (err >= 0) ? -EIO : err; +		} +	} else { +		err = -EIO; +	} + +	return err; +} + +/** + * si476x_core_send_command() - sends a command to si476x and waits its + * response + * @core:    si476x_device structure for the device we are + *            communicating with + * @command:  command id + * @args:     command arguments we are sending + * @argn:     actual size of @args + * @response: buffer to place the expected response from the device + * @respn:    actual size of @response + * @usecs:    amount of time to wait before reading the response (in + *            usecs) + * + * Function returns 0 on succsess and negative error code on + * failure + */ +static int si476x_core_send_command(struct si476x_core *core, +				    const u8 command, +				    const u8 args[], +				    const int argn, +				    u8 resp[], +				    const int respn, +				    const int usecs) +{ +	struct i2c_client *client = core->client; +	int err; +	u8  data[CMD_MAX_ARGS_COUNT + 1]; + +	if (argn > CMD_MAX_ARGS_COUNT) { +		err = -ENOMEM; +		goto exit; +	} + +	if (!client->adapter) { +		err = -ENODEV; +		goto exit; +	} + +	/* First send the command and its arguments */ +	data[0] = command; +	memcpy(&data[1], args, argn); +	dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data); + +	err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND, +				   (char *) data, argn + 1); +	if (err != argn + 1) { +		dev_err(&core->client->dev, +			"Error while sending command 0x%02x\n", +			command); +		err = (err >= 0) ? -EIO : err; +		goto exit; +	} +	/* Set CTS to zero only after the command is send to avoid +	 * possible racing conditions when working in polling mode */ +	atomic_set(&core->cts, 0); + +	/* if (unlikely(command == CMD_POWER_DOWN) */ +	if (!wait_event_timeout(core->command, +				atomic_read(&core->cts), +				usecs_to_jiffies(usecs) + 1)) +		dev_warn(&core->client->dev, +			 "(%s) [CMD 0x%02x] Answer timeout.\n", +			 __func__, command); + +	/* +	  When working in polling mode, for some reason the tuner will +	  report CTS bit as being set in the first status byte read, +	  but all the consequtive ones will return zeros until the +	  tuner is actually completed the POWER_UP command. To +	  workaround that we wait for second CTS to be reported +	 */ +	if (unlikely(!core->client->irq && command == CMD_POWER_UP)) { +		if (!wait_event_timeout(core->command, +					atomic_read(&core->cts), +					usecs_to_jiffies(usecs) + 1)) +			dev_warn(&core->client->dev, +				 "(%s) Power up took too much time.\n", +				 __func__); +	} + +	/* Then get the response */ +	err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn); +	if (err != respn) { +		dev_err(&core->client->dev, +			"Error while reading response for command 0x%02x\n", +			command); +		err = (err >= 0) ? -EIO : err; +		goto exit; +	} +	dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp); + +	err = 0; + +	if (resp[0] & SI476X_ERR) { +		dev_err(&core->client->dev, +			"[CMD 0x%02x] Chip set error flag\n", command); +		err = si476x_core_parse_and_nag_about_error(core); +		goto exit; +	} + +	if (!(resp[0] & SI476X_CTS)) +		err = -EBUSY; +exit: +	return err; +} + +static int si476x_cmd_clear_stc(struct si476x_core *core) +{ +	int err; +	struct si476x_rsq_status_args args = { +		.primary	= false, +		.rsqack		= false, +		.attune		= false, +		.cancel		= false, +		.stcack		= true, +	}; + +	switch (core->power_up_parameters.func) { +	case SI476X_FUNC_FM_RECEIVER: +		err = si476x_core_cmd_fm_rsq_status(core, &args, NULL); +		break; +	case SI476X_FUNC_AM_RECEIVER: +		err = si476x_core_cmd_am_rsq_status(core, &args, NULL); +		break; +	default: +		err = -EINVAL; +	} + +	return err; +} + +static int si476x_cmd_tune_seek_freq(struct si476x_core *core, +				     uint8_t cmd, +				     const uint8_t args[], size_t argn, +				     uint8_t *resp, size_t respn) +{ +	int err; + + +	atomic_set(&core->stc, 0); +	err = si476x_core_send_command(core, cmd, args, argn, resp, respn, +				       SI476X_TIMEOUT_TUNE); +	if (!err) { +		wait_event_killable(core->tuning, +				    atomic_read(&core->stc)); +		si476x_cmd_clear_stc(core); +	} + +	return err; +} + +/** + * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device + * @core: device to send the command to + * @info:  struct si476x_func_info to fill all the information + *         returned by the command + * + * The command requests the firmware and patch version for currently + * loaded firmware (dependent on the function of the device FM/AM/WB) + * + * Function returns 0 on succsess and negative error code on + * failure + */ +int si476x_core_cmd_func_info(struct si476x_core *core, +			      struct si476x_func_info *info) +{ +	int err; +	u8  resp[CMD_FUNC_INFO_NRESP]; + +	err = si476x_core_send_command(core, CMD_FUNC_INFO, +				       NULL, 0, +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); + +	info->firmware.major    = resp[1]; +	info->firmware.minor[0] = resp[2]; +	info->firmware.minor[1] = resp[3]; + +	info->patch_id = ((u16) resp[4] << 8) | resp[5]; +	info->func     = resp[6]; + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info); + +/** + * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device + * @core:    device to send the command to + * @property: property address + * @value:    property value + * + * Function returns 0 on succsess and negative error code on + * failure + */ +int si476x_core_cmd_set_property(struct si476x_core *core, +				 u16 property, u16 value) +{ +	u8       resp[CMD_SET_PROPERTY_NRESP]; +	const u8 args[CMD_SET_PROPERTY_NARGS] = { +		0x00, +		msb(property), +		lsb(property), +		msb(value), +		lsb(value), +	}; + +	return si476x_core_send_command(core, CMD_SET_PROPERTY, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property); + +/** + * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device + * @core:    device to send the command to + * @property: property address + * + * Function return the value of property as u16 on success or a + * negative error on failure + */ +int si476x_core_cmd_get_property(struct si476x_core *core, u16 property) +{ +	int err; +	u8       resp[CMD_GET_PROPERTY_NRESP]; +	const u8 args[CMD_GET_PROPERTY_NARGS] = { +		0x00, +		msb(property), +		lsb(property), +	}; + +	err = si476x_core_send_command(core, CMD_GET_PROPERTY, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	if (err < 0) +		return err; +	else +		return be16_to_cpup((__be16 *)(resp + 2)); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property); + +/** + * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to + * the device + * @core: device to send the command to + * @dclk:  DCLK pin function configuration: + *	   #SI476X_DCLK_NOOP     - do not modify the behaviour + *         #SI476X_DCLK_TRISTATE - put the pin in tristate condition, + *                                 enable 1MOhm pulldown + *         #SI476X_DCLK_DAUDIO   - set the pin to be a part of digital + *                                 audio interface + * @dfs:   DFS pin function configuration: + *         #SI476X_DFS_NOOP      - do not modify the behaviour + *         #SI476X_DFS_TRISTATE  - put the pin in tristate condition, + *                             enable 1MOhm pulldown + *      SI476X_DFS_DAUDIO    - set the pin to be a part of digital + *                             audio interface + * @dout - DOUT pin function configuration: + *      SI476X_DOUT_NOOP       - do not modify the behaviour + *      SI476X_DOUT_TRISTATE   - put the pin in tristate condition, + *                               enable 1MOhm pulldown + *      SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S + *                               port 1 + *      SI476X_DOUT_I2S_INPUT  - set this pin to be digital in on I2S + *                               port 1 + * @xout - XOUT pin function configuration: + *	SI476X_XOUT_NOOP        - do not modify the behaviour + *      SI476X_XOUT_TRISTATE    - put the pin in tristate condition, + *                                enable 1MOhm pulldown + *      SI476X_XOUT_I2S_INPUT   - set this pin to be digital in on I2S + *                                port 1 + *      SI476X_XOUT_MODE_SELECT - set this pin to be the input that + *                                selects the mode of the I2S audio + *                                combiner (analog or HD) + *                                [SI4761/63/65/67 Only] + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_dig_audio_pin_cfg(struct  si476x_core *core, +				      enum si476x_dclk_config dclk, +				      enum si476x_dfs_config  dfs, +				      enum si476x_dout_config dout, +				      enum si476x_xout_config xout) +{ +	u8       resp[CMD_DIG_AUDIO_PIN_CFG_NRESP]; +	const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = { +		PIN_CFG_BYTE(dclk), +		PIN_CFG_BYTE(dfs), +		PIN_CFG_BYTE(dout), +		PIN_CFG_BYTE(xout), +	}; + +	return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg); + +/** + * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND' + * @core - device to send the command to + * @iqclk - IQCL pin function configuration: + *       SI476X_IQCLK_NOOP     - do not modify the behaviour + *       SI476X_IQCLK_TRISTATE - put the pin in tristate condition, + *                               enable 1MOhm pulldown + *       SI476X_IQCLK_IQ       - set pin to be a part of I/Q interace + *                               in master mode + * @iqfs - IQFS pin function configuration: + *       SI476X_IQFS_NOOP     - do not modify the behaviour + *       SI476X_IQFS_TRISTATE - put the pin in tristate condition, + *                              enable 1MOhm pulldown + *       SI476X_IQFS_IQ       - set pin to be a part of I/Q interace + *                              in master mode + * @iout - IOUT pin function configuration: + *       SI476X_IOUT_NOOP     - do not modify the behaviour + *       SI476X_IOUT_TRISTATE - put the pin in tristate condition, + *                              enable 1MOhm pulldown + *       SI476X_IOUT_OUTPUT   - set pin to be I out + * @qout - QOUT pin function configuration: + *       SI476X_QOUT_NOOP     - do not modify the behaviour + *       SI476X_QOUT_TRISTATE - put the pin in tristate condition, + *                              enable 1MOhm pulldown + *       SI476X_QOUT_OUTPUT   - set pin to be Q out + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core, +				enum si476x_iqclk_config iqclk, +				enum si476x_iqfs_config iqfs, +				enum si476x_iout_config iout, +				enum si476x_qout_config qout) +{ +	u8       resp[CMD_ZIF_PIN_CFG_NRESP]; +	const u8 args[CMD_ZIF_PIN_CFG_NARGS] = { +		PIN_CFG_BYTE(iqclk), +		PIN_CFG_BYTE(iqfs), +		PIN_CFG_BYTE(iout), +		PIN_CFG_BYTE(qout), +	}; + +	return si476x_core_send_command(core, CMD_ZIF_PIN_CFG, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg); + +/** + * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send + * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device + * @core - device to send the command to + * @icin - ICIN pin function configuration: + *      SI476X_ICIN_NOOP      - do not modify the behaviour + *      SI476X_ICIN_TRISTATE  - put the pin in tristate condition, + *                              enable 1MOhm pulldown + *      SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high + *      SI476X_ICIN_GPO1_LOW  - set pin to be an output, drive it low + *      SI476X_ICIN_IC_LINK   - set the pin to be a part of Inter-Chip link + * @icip - ICIP pin function configuration: + *      SI476X_ICIP_NOOP      - do not modify the behaviour + *      SI476X_ICIP_TRISTATE  - put the pin in tristate condition, + *                              enable 1MOhm pulldown + *      SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high + *      SI476X_ICIP_GPO1_LOW  - set pin to be an output, drive it low + *      SI476X_ICIP_IC_LINK   - set the pin to be a part of Inter-Chip link + * @icon - ICON pin function configuration: + *      SI476X_ICON_NOOP     - do not modify the behaviour + *      SI476X_ICON_TRISTATE - put the pin in tristate condition, + *                             enable 1MOhm pulldown + *      SI476X_ICON_I2S      - set the pin to be a part of audio + *                             interface in slave mode (DCLK) + *      SI476X_ICON_IC_LINK  - set the pin to be a part of Inter-Chip link + * @icop - ICOP pin function configuration: + *      SI476X_ICOP_NOOP     - do not modify the behaviour + *      SI476X_ICOP_TRISTATE - put the pin in tristate condition, + *                             enable 1MOhm pulldown + *      SI476X_ICOP_I2S      - set the pin to be a part of audio + *                             interface in slave mode (DOUT) + *                             [Si4761/63/65/67 Only] + *      SI476X_ICOP_IC_LINK  - set the pin to be a part of Inter-Chip link + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core, +					    enum si476x_icin_config icin, +					    enum si476x_icip_config icip, +					    enum si476x_icon_config icon, +					    enum si476x_icop_config icop) +{ +	u8       resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP]; +	const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = { +		PIN_CFG_BYTE(icin), +		PIN_CFG_BYTE(icip), +		PIN_CFG_BYTE(icon), +		PIN_CFG_BYTE(icop), +	}; + +	return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg); + +/** + * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the + * device + * @core - device to send the command to + * @lrout - LROUT pin function configuration: + *       SI476X_LROUT_NOOP     - do not modify the behaviour + *       SI476X_LROUT_TRISTATE - put the pin in tristate condition, + *                               enable 1MOhm pulldown + *       SI476X_LROUT_AUDIO    - set pin to be audio output + *       SI476X_LROUT_MPX      - set pin to be MPX output + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core, +				      enum si476x_lrout_config lrout) +{ +	u8       resp[CMD_ANA_AUDIO_PIN_CFG_NRESP]; +	const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = { +		PIN_CFG_BYTE(lrout), +	}; + +	return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg); + + +/** + * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device + * @core - device to send the command to + * @intb - INTB pin function configuration: + *      SI476X_INTB_NOOP     - do not modify the behaviour + *      SI476X_INTB_TRISTATE - put the pin in tristate condition, + *                             enable 1MOhm pulldown + *      SI476X_INTB_DAUDIO   - set pin to be a part of digital + *                             audio interface in slave mode + *      SI476X_INTB_IRQ      - set pin to be an interrupt request line + * @a1 - A1 pin function configuration: + *      SI476X_A1_NOOP     - do not modify the behaviour + *      SI476X_A1_TRISTATE - put the pin in tristate condition, + *                           enable 1MOhm pulldown + *      SI476X_A1_IRQ      - set pin to be an interrupt request line + * + * Function returns 0 on success and negative error code on failure + */ +static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core, +					    enum si476x_intb_config intb, +					    enum si476x_a1_config a1) +{ +	u8       resp[CMD_INTB_PIN_CFG_A10_NRESP]; +	const u8 args[CMD_INTB_PIN_CFG_NARGS] = { +		PIN_CFG_BYTE(intb), +		PIN_CFG_BYTE(a1), +	}; + +	return si476x_core_send_command(core, CMD_INTB_PIN_CFG, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} + +static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core, +					    enum si476x_intb_config intb, +					    enum si476x_a1_config a1) +{ +	u8       resp[CMD_INTB_PIN_CFG_A20_NRESP]; +	const u8 args[CMD_INTB_PIN_CFG_NARGS] = { +		PIN_CFG_BYTE(intb), +		PIN_CFG_BYTE(a1), +	}; + +	return si476x_core_send_command(core, CMD_INTB_PIN_CFG, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} + + + +/** + * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the + * device + * @core  - device to send the command to + * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT, + *           RSSSILINT, BLENDINT, MULTHINT and MULTLINT + * @attune - when set the values in the status report are the values + *           that were calculated at tune + * @cancel - abort ongoing seek/tune opertation + * @stcack - clear the STCINT bin in status register + * @report - all signal quality information retured by the command + *           (if NULL then the output of the command is ignored) + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_am_rsq_status(struct si476x_core *core, +				  struct si476x_rsq_status_args *rsqargs, +				  struct si476x_rsq_status_report *report) +{ +	int err; +	u8       resp[CMD_AM_RSQ_STATUS_NRESP]; +	const u8 args[CMD_AM_RSQ_STATUS_NARGS] = { +		rsqargs->rsqack << 3 | rsqargs->attune << 2 | +		rsqargs->cancel << 1 | rsqargs->stcack, +	}; + +	err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	/* +	 * Besides getting received signal quality information this +	 * command can be used to just acknowledge different interrupt +	 * flags in those cases it is useless to copy and parse +	 * received data so user can pass NULL, and thus avoid +	 * unnecessary copying. +	 */ +	if (!report) +		return err; + +	report->snrhint		= 0b00001000 & resp[1]; +	report->snrlint		= 0b00000100 & resp[1]; +	report->rssihint	= 0b00000010 & resp[1]; +	report->rssilint	= 0b00000001 & resp[1]; + +	report->bltf		= 0b10000000 & resp[2]; +	report->snr_ready	= 0b00100000 & resp[2]; +	report->rssiready	= 0b00001000 & resp[2]; +	report->afcrl		= 0b00000010 & resp[2]; +	report->valid		= 0b00000001 & resp[2]; + +	report->readfreq	= be16_to_cpup((__be16 *)(resp + 3)); +	report->freqoff		= resp[5]; +	report->rssi		= resp[6]; +	report->snr		= resp[7]; +	report->lassi		= resp[9]; +	report->hassi		= resp[10]; +	report->mult		= resp[11]; +	report->dev		= resp[12]; + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status); + +int si476x_core_cmd_fm_acf_status(struct si476x_core *core, +			     struct si476x_acf_status_report *report) +{ +	int err; +	u8       resp[CMD_FM_ACF_STATUS_NRESP]; +	const u8 args[CMD_FM_ACF_STATUS_NARGS] = { +		0x0, +	}; + +	if (!report) +		return -EINVAL; + +	err = si476x_core_send_command(core, CMD_FM_ACF_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	if (err < 0) +		return err; + +	report->blend_int	= resp[1] & SI476X_ACF_BLEND_INT; +	report->hblend_int	= resp[1] & SI476X_ACF_HIBLEND_INT; +	report->hicut_int	= resp[1] & SI476X_ACF_HICUT_INT; +	report->chbw_int	= resp[1] & SI476X_ACF_CHBW_INT; +	report->softmute_int	= resp[1] & SI476X_ACF_SOFTMUTE_INT; +	report->smute		= resp[2] & SI476X_ACF_SMUTE; +	report->smattn		= resp[3] & SI476X_ACF_SMATTN; +	report->chbw		= resp[4]; +	report->hicut		= resp[5]; +	report->hiblend		= resp[6]; +	report->pilot		= resp[7] & SI476X_ACF_PILOT; +	report->stblend		= resp[7] & SI476X_ACF_STBLEND; + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status); + +int si476x_core_cmd_am_acf_status(struct si476x_core *core, +				  struct si476x_acf_status_report *report) +{ +	int err; +	u8       resp[CMD_AM_ACF_STATUS_NRESP]; +	const u8 args[CMD_AM_ACF_STATUS_NARGS] = { +		0x0, +	}; + +	if (!report) +		return -EINVAL; + +	err = si476x_core_send_command(core, CMD_AM_ACF_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	if (err < 0) +		return err; + +	report->blend_int	= resp[1] & SI476X_ACF_BLEND_INT; +	report->hblend_int	= resp[1] & SI476X_ACF_HIBLEND_INT; +	report->hicut_int	= resp[1] & SI476X_ACF_HICUT_INT; +	report->chbw_int	= resp[1] & SI476X_ACF_CHBW_INT; +	report->softmute_int	= resp[1] & SI476X_ACF_SOFTMUTE_INT; +	report->smute		= resp[2] & SI476X_ACF_SMUTE; +	report->smattn		= resp[3] & SI476X_ACF_SMATTN; +	report->chbw		= resp[4]; +	report->hicut		= resp[5]; + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status); + + +/** + * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the + * device + * @core  - device to send the command to + * @seekup - if set the direction of the search is 'up' + * @wrap   - if set seek wraps when hitting band limit + * + * This function begins search for a valid station. The station is + * considered valid when 'FM_VALID_SNR_THRESHOLD' and + * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria + * are met. +} * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_fm_seek_start(struct si476x_core *core, +				  bool seekup, bool wrap) +{ +	u8       resp[CMD_FM_SEEK_START_NRESP]; +	const u8 args[CMD_FM_SEEK_START_NARGS] = { +		seekup << 3 | wrap << 2, +	}; + +	return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START, +					 args, sizeof(args), +					 resp, sizeof(resp)); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start); + +/** + * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the + * device + * @core - device to send the command to + * @status_only - if set the data is not removed from RDSFIFO, + *                RDSFIFOUSED is not decremented and data in all the + *                rest RDS data contains the last valid info received + * @mtfifo if set the command clears RDS receive FIFO + * @intack if set the command clards the RDSINT bit. + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_fm_rds_status(struct si476x_core *core, +				  bool status_only, +				  bool mtfifo, +				  bool intack, +				  struct si476x_rds_status_report *report) +{ +	int err; +	u8       resp[CMD_FM_RDS_STATUS_NRESP]; +	const u8 args[CMD_FM_RDS_STATUS_NARGS] = { +		status_only << 2 | mtfifo << 1 | intack, +	}; + +	err = si476x_core_send_command(core, CMD_FM_RDS_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	/* +	 * Besides getting RDS status information this command can be +	 * used to just acknowledge different interrupt flags in those +	 * cases it is useless to copy and parse received data so user +	 * can pass NULL, and thus avoid unnecessary copying. +	 */ +	if (err < 0 || report == NULL) +		return err; + +	report->rdstpptyint	= 0b00010000 & resp[1]; +	report->rdspiint	= 0b00001000 & resp[1]; +	report->rdssyncint	= 0b00000010 & resp[1]; +	report->rdsfifoint	= 0b00000001 & resp[1]; + +	report->tpptyvalid	= 0b00010000 & resp[2]; +	report->pivalid		= 0b00001000 & resp[2]; +	report->rdssync		= 0b00000010 & resp[2]; +	report->rdsfifolost	= 0b00000001 & resp[2]; + +	report->tp		= 0b00100000 & resp[3]; +	report->pty		= 0b00011111 & resp[3]; + +	report->pi		= be16_to_cpup((__be16 *)(resp + 4)); +	report->rdsfifoused	= resp[6]; + +	report->ble[V4L2_RDS_BLOCK_A]	= 0b11000000 & resp[7]; +	report->ble[V4L2_RDS_BLOCK_B]	= 0b00110000 & resp[7]; +	report->ble[V4L2_RDS_BLOCK_C]	= 0b00001100 & resp[7]; +	report->ble[V4L2_RDS_BLOCK_D]	= 0b00000011 & resp[7]; + +	report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A; +	report->rds[V4L2_RDS_BLOCK_A].msb = resp[8]; +	report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9]; + +	report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B; +	report->rds[V4L2_RDS_BLOCK_B].msb = resp[10]; +	report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11]; + +	report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C; +	report->rds[V4L2_RDS_BLOCK_C].msb = resp[12]; +	report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13]; + +	report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D; +	report->rds[V4L2_RDS_BLOCK_D].msb = resp[14]; +	report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15]; + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status); + +int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core, +				bool clear, +				struct si476x_rds_blockcount_report *report) +{ +	int err; +	u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP]; +	const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = { +		clear, +	}; + +	if (!report) +		return -EINVAL; + +	err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); + +	if (!err) { +		report->expected	= be16_to_cpup((__be16 *)(resp + 2)); +		report->received	= be16_to_cpup((__be16 *)(resp + 4)); +		report->uncorrectable	= be16_to_cpup((__be16 *)(resp + 6)); +	} + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount); + +int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core, +				       enum si476x_phase_diversity_mode mode) +{ +	u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP]; +	const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = { +		mode & 0b111, +	}; + +	return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity); +/** + * si476x_core_cmd_fm_phase_div_status() - get the phase diversity + * status + * + * @core: si476x device + * + * NOTE caller must hold core lock + * + * Function returns the value of the status bit in case of success and + * negative error code in case of failre. + */ +int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core) +{ +	int err; +	u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP]; + +	err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS, +				       NULL, 0, +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); + +	return (err < 0) ? err : resp[1]; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status); + + +/** + * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the + * device + * @core  - device to send the command to + * @seekup - if set the direction of the search is 'up' + * @wrap   - if set seek wraps when hitting band limit + * + * This function begins search for a valid station. The station is + * considered valid when 'FM_VALID_SNR_THRESHOLD' and + * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria + * are met. + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_am_seek_start(struct si476x_core *core, +				  bool seekup, bool wrap) +{ +	u8       resp[CMD_AM_SEEK_START_NRESP]; +	const u8 args[CMD_AM_SEEK_START_NARGS] = { +		seekup << 3 | wrap << 2, +	}; + +	return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START, +					 args, sizeof(args), +					 resp, sizeof(resp)); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start); + + + +static int si476x_core_cmd_power_up_a10(struct si476x_core *core, +					struct si476x_power_up_args *puargs) +{ +	u8       resp[CMD_POWER_UP_A10_NRESP]; +	const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); +	const bool ctsen  = (core->client->irq != 0); +	const u8 args[CMD_POWER_UP_A10_NARGS] = { +		0xF7,		/* Reserved, always 0xF7 */ +		0x3F & puargs->xcload,	/* First two bits are reserved to be +				 * zeros */ +		ctsen << 7 | intsel << 6 | 0x07, /* Last five bits +						   * are reserved to +						   * be written as 0x7 */ +		puargs->func << 4 | puargs->freq, +		0x11,		/* Reserved, always 0x11 */ +	}; + +	return si476x_core_send_command(core, CMD_POWER_UP, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_TIMEOUT_POWER_UP); +} + +static int si476x_core_cmd_power_up_a20(struct si476x_core *core, +				 struct si476x_power_up_args *puargs) +{ +	u8       resp[CMD_POWER_UP_A20_NRESP]; +	const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); +	const bool ctsen  = (core->client->irq != 0); +	const u8 args[CMD_POWER_UP_A20_NARGS] = { +		puargs->ibias6x << 7 | puargs->xstart, +		0x3F & puargs->xcload,	/* First two bits are reserved to be +					 * zeros */ +		ctsen << 7 | intsel << 6 | puargs->fastboot << 5 | +		puargs->xbiashc << 3 | puargs->xbias, +		puargs->func << 4 | puargs->freq, +		0x10 | puargs->xmode, +	}; + +	return si476x_core_send_command(core, CMD_POWER_UP, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_TIMEOUT_POWER_UP); +} + +static int si476x_core_cmd_power_down_a10(struct si476x_core *core, +					  struct si476x_power_down_args *pdargs) +{ +	u8 resp[CMD_POWER_DOWN_A10_NRESP]; + +	return si476x_core_send_command(core, CMD_POWER_DOWN, +					NULL, 0, +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} + +static int si476x_core_cmd_power_down_a20(struct si476x_core *core, +					  struct si476x_power_down_args *pdargs) +{ +	u8 resp[CMD_POWER_DOWN_A20_NRESP]; +	const u8 args[CMD_POWER_DOWN_A20_NARGS] = { +		pdargs->xosc, +	}; +	return si476x_core_send_command(core, CMD_POWER_DOWN, +					args, ARRAY_SIZE(args), +					resp, ARRAY_SIZE(resp), +					SI476X_DEFAULT_TIMEOUT); +} + +static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core, +					struct si476x_tune_freq_args *tuneargs) +{ + +	const int am_freq = tuneargs->freq; +	u8       resp[CMD_AM_TUNE_FREQ_NRESP]; +	const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { +		(tuneargs->hd << 6), +		msb(am_freq), +		lsb(am_freq), +	}; + +	return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args, +					 sizeof(args), +					 resp, sizeof(resp)); +} + +static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core, +					struct si476x_tune_freq_args *tuneargs) +{ +	const int am_freq = tuneargs->freq; +	u8       resp[CMD_AM_TUNE_FREQ_NRESP]; +	const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { +		(tuneargs->zifsr << 6) | (tuneargs->injside & 0b11), +		msb(am_freq), +		lsb(am_freq), +	}; + +	return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, +					 args, sizeof(args), +					 resp, sizeof(resp)); +} + +static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core, +					struct si476x_rsq_status_args *rsqargs, +					struct si476x_rsq_status_report *report) +{ +	int err; +	u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP]; +	const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = { +		rsqargs->rsqack << 3 | rsqargs->attune << 2 | +		rsqargs->cancel << 1 | rsqargs->stcack, +	}; + +	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	/* +	 * Besides getting received signal quality information this +	 * command can be used to just acknowledge different interrupt +	 * flags in those cases it is useless to copy and parse +	 * received data so user can pass NULL, and thus avoid +	 * unnecessary copying. +	 */ +	if (err < 0 || report == NULL) +		return err; + +	report->multhint	= 0b10000000 & resp[1]; +	report->multlint	= 0b01000000 & resp[1]; +	report->snrhint		= 0b00001000 & resp[1]; +	report->snrlint		= 0b00000100 & resp[1]; +	report->rssihint	= 0b00000010 & resp[1]; +	report->rssilint	= 0b00000001 & resp[1]; + +	report->bltf		= 0b10000000 & resp[2]; +	report->snr_ready	= 0b00100000 & resp[2]; +	report->rssiready	= 0b00001000 & resp[2]; +	report->afcrl		= 0b00000010 & resp[2]; +	report->valid		= 0b00000001 & resp[2]; + +	report->readfreq	= be16_to_cpup((__be16 *)(resp + 3)); +	report->freqoff		= resp[5]; +	report->rssi		= resp[6]; +	report->snr		= resp[7]; +	report->lassi		= resp[9]; +	report->hassi		= resp[10]; +	report->mult		= resp[11]; +	report->dev		= resp[12]; +	report->readantcap	= be16_to_cpup((__be16 *)(resp + 13)); +	report->assi		= resp[15]; +	report->usn		= resp[16]; + +	return err; +} + +static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core, +					     struct si476x_rsq_status_args *rsqargs, +					     struct si476x_rsq_status_report *report) +{ +	int err; +	u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP]; +	const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { +		rsqargs->primary << 4 | rsqargs->rsqack << 3 | +		rsqargs->attune  << 2 | rsqargs->cancel << 1 | +		rsqargs->stcack, +	}; + +	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	/* +	 * Besides getting received signal quality information this +	 * command can be used to just acknowledge different interrupt +	 * flags in those cases it is useless to copy and parse +	 * received data so user can pass NULL, and thus avoid +	 * unnecessary copying. +	 */ +	if (err < 0 || report == NULL) +		return err; + +	report->multhint	= 0b10000000 & resp[1]; +	report->multlint	= 0b01000000 & resp[1]; +	report->snrhint		= 0b00001000 & resp[1]; +	report->snrlint		= 0b00000100 & resp[1]; +	report->rssihint	= 0b00000010 & resp[1]; +	report->rssilint	= 0b00000001 & resp[1]; + +	report->bltf		= 0b10000000 & resp[2]; +	report->snr_ready	= 0b00100000 & resp[2]; +	report->rssiready	= 0b00001000 & resp[2]; +	report->afcrl		= 0b00000010 & resp[2]; +	report->valid		= 0b00000001 & resp[2]; + +	report->readfreq	= be16_to_cpup((__be16 *)(resp + 3)); +	report->freqoff		= resp[5]; +	report->rssi		= resp[6]; +	report->snr		= resp[7]; +	report->lassi		= resp[9]; +	report->hassi		= resp[10]; +	report->mult		= resp[11]; +	report->dev		= resp[12]; +	report->readantcap	= be16_to_cpup((__be16 *)(resp + 13)); +	report->assi		= resp[15]; +	report->usn		= resp[16]; + +	return err; +} + + +static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core, +					struct si476x_rsq_status_args *rsqargs, +					struct si476x_rsq_status_report *report) +{ +	int err; +	u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP]; +	const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { +		rsqargs->primary << 4 | rsqargs->rsqack << 3 | +		rsqargs->attune << 2 | rsqargs->cancel << 1 | +		rsqargs->stcack, +	}; + +	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, +				       args, ARRAY_SIZE(args), +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	/* +	 * Besides getting received signal quality information this +	 * command can be used to just acknowledge different interrupt +	 * flags in those cases it is useless to copy and parse +	 * received data so user can pass NULL, and thus avoid +	 * unnecessary copying. +	 */ +	if (err < 0 || report == NULL) +		return err; + +	report->multhint	= 0b10000000 & resp[1]; +	report->multlint	= 0b01000000 & resp[1]; +	report->snrhint		= 0b00001000 & resp[1]; +	report->snrlint		= 0b00000100 & resp[1]; +	report->rssihint	= 0b00000010 & resp[1]; +	report->rssilint	= 0b00000001 & resp[1]; + +	report->bltf		= 0b10000000 & resp[2]; +	report->snr_ready	= 0b00100000 & resp[2]; +	report->rssiready	= 0b00001000 & resp[2]; +	report->injside         = 0b00000100 & resp[2]; +	report->afcrl		= 0b00000010 & resp[2]; +	report->valid		= 0b00000001 & resp[2]; + +	report->readfreq	= be16_to_cpup((__be16 *)(resp + 3)); +	report->freqoff		= resp[5]; +	report->rssi		= resp[6]; +	report->snr		= resp[7]; +	report->issi		= resp[8]; +	report->lassi		= resp[9]; +	report->hassi		= resp[10]; +	report->mult		= resp[11]; +	report->dev		= resp[12]; +	report->readantcap	= be16_to_cpup((__be16 *)(resp + 13)); +	report->assi		= resp[15]; +	report->usn		= resp[16]; + +	report->pilotdev	= resp[17]; +	report->rdsdev		= resp[18]; +	report->assidev		= resp[19]; +	report->strongdev	= resp[20]; +	report->rdspi		= be16_to_cpup((__be16 *)(resp + 21)); + +	return err; +} + +static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core, +					struct si476x_tune_freq_args *tuneargs) +{ +	u8       resp[CMD_FM_TUNE_FREQ_NRESP]; +	const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = { +		(tuneargs->hd << 6) | (tuneargs->tunemode << 4) +		| (tuneargs->smoothmetrics << 2), +		msb(tuneargs->freq), +		lsb(tuneargs->freq), +		msb(tuneargs->antcap), +		lsb(tuneargs->antcap) +	}; + +	return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, +					 args, sizeof(args), +					 resp, sizeof(resp)); +} + +static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core, +					struct si476x_tune_freq_args *tuneargs) +{ +	u8       resp[CMD_FM_TUNE_FREQ_NRESP]; +	const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = { +		(tuneargs->hd << 6) | (tuneargs->tunemode << 4) +		|  (tuneargs->smoothmetrics << 2) | (tuneargs->injside), +		msb(tuneargs->freq), +		lsb(tuneargs->freq), +	}; + +	return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, +					 args, sizeof(args), +					 resp, sizeof(resp)); +} + +static int si476x_core_cmd_agc_status_a20(struct si476x_core *core, +					struct si476x_agc_status_report *report) +{ +	int err; +	u8 resp[CMD_AGC_STATUS_NRESP_A20]; + +	if (!report) +		return -EINVAL; + +	err = si476x_core_send_command(core, CMD_AGC_STATUS, +				       NULL, 0, +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	if (err < 0) +		return err; + +	report->mxhi		= resp[1] & SI476X_AGC_MXHI; +	report->mxlo		= resp[1] & SI476X_AGC_MXLO; +	report->lnahi		= resp[1] & SI476X_AGC_LNAHI; +	report->lnalo		= resp[1] & SI476X_AGC_LNALO; +	report->fmagc1		= resp[2]; +	report->fmagc2		= resp[3]; +	report->pgagain		= resp[4]; +	report->fmwblang	= resp[5]; + +	return err; +} + +static int si476x_core_cmd_agc_status_a10(struct si476x_core *core, +					struct si476x_agc_status_report *report) +{ +	int err; +	u8 resp[CMD_AGC_STATUS_NRESP_A10]; + +	if (!report) +		return -EINVAL; + +	err = si476x_core_send_command(core, CMD_AGC_STATUS, +				       NULL, 0, +				       resp, ARRAY_SIZE(resp), +				       SI476X_DEFAULT_TIMEOUT); +	if (err < 0) +		return err; + +	report->mxhi	= resp[1] & SI476X_AGC_MXHI; +	report->mxlo	= resp[1] & SI476X_AGC_MXLO; +	report->lnahi	= resp[1] & SI476X_AGC_LNAHI; +	report->lnalo	= resp[1] & SI476X_AGC_LNALO; + +	return err; +} + +typedef int (*tune_freq_func_t) (struct si476x_core *core, +				 struct si476x_tune_freq_args *tuneargs); + +static struct { +	int (*power_up) (struct si476x_core *, +			 struct si476x_power_up_args *); +	int (*power_down) (struct si476x_core *, +			   struct si476x_power_down_args *); + +	tune_freq_func_t fm_tune_freq; +	tune_freq_func_t am_tune_freq; + +	int (*fm_rsq_status)(struct si476x_core *, +			     struct si476x_rsq_status_args *, +			     struct si476x_rsq_status_report *); + +	int (*agc_status)(struct si476x_core *, +			  struct si476x_agc_status_report *); +	int (*intb_pin_cfg)(struct si476x_core *core, +			    enum si476x_intb_config intb, +			    enum si476x_a1_config a1); +} si476x_cmds_vtable[] = { +	[SI476X_REVISION_A10] = { +		.power_up	= si476x_core_cmd_power_up_a10, +		.power_down	= si476x_core_cmd_power_down_a10, +		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a10, +		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a10, +		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a10, +		.agc_status	= si476x_core_cmd_agc_status_a10, +		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10, +	}, +	[SI476X_REVISION_A20] = { +		.power_up	= si476x_core_cmd_power_up_a20, +		.power_down	= si476x_core_cmd_power_down_a20, +		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a20, +		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a20, +		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a20, +		.agc_status	= si476x_core_cmd_agc_status_a20, +		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20, +	}, +	[SI476X_REVISION_A30] = { +		.power_up	= si476x_core_cmd_power_up_a20, +		.power_down	= si476x_core_cmd_power_down_a20, +		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a20, +		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a20, +		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a30, +		.agc_status	= si476x_core_cmd_agc_status_a20, +		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20, +	}, +}; + +int si476x_core_cmd_power_up(struct si476x_core *core, +			     struct si476x_power_up_args *args) +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return si476x_cmds_vtable[core->revision].power_up(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up); + +int si476x_core_cmd_power_down(struct si476x_core *core, +			       struct si476x_power_down_args *args) +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return si476x_cmds_vtable[core->revision].power_down(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down); + +int si476x_core_cmd_fm_tune_freq(struct si476x_core *core, +				 struct si476x_tune_freq_args *args) +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq); + +int si476x_core_cmd_am_tune_freq(struct si476x_core *core, +				 struct si476x_tune_freq_args *args) +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return si476x_cmds_vtable[core->revision].am_tune_freq(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq); + +int si476x_core_cmd_fm_rsq_status(struct si476x_core *core, +				  struct si476x_rsq_status_args *args, +				  struct si476x_rsq_status_report *report) + +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args, +								report); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status); + +int si476x_core_cmd_agc_status(struct si476x_core *core, +				  struct si476x_agc_status_report *report) + +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return si476x_cmds_vtable[core->revision].agc_status(core, report); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status); + +int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core, +			    enum si476x_intb_config intb, +			    enum si476x_a1_config a1) +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); + +	return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); +MODULE_DESCRIPTION("API for command exchange for si476x"); diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c new file mode 100644 index 00000000000..f5bc8e4bd4b --- /dev/null +++ b/drivers/mfd/si476x-i2c.c @@ -0,0 +1,886 @@ +/* + * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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; version 2 of the License. + * + * 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. + * + */ +#include <linux/module.h> + +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/i2c.h> +#include <linux/err.h> + +#include <linux/mfd/si476x-core.h> + +#define SI476X_MAX_IO_ERRORS		10 +#define SI476X_DRIVER_RDS_FIFO_DEPTH	128 + +/** + * si476x_core_config_pinmux() - pin function configuration function + * + * @core: Core device structure + * + * Configure the functions of the pins of the radio chip. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +static int si476x_core_config_pinmux(struct si476x_core *core) +{ +	int err; +	dev_dbg(&core->client->dev, "Configuring pinmux\n"); +	err = si476x_core_cmd_dig_audio_pin_cfg(core, +						core->pinmux.dclk, +						core->pinmux.dfs, +						core->pinmux.dout, +						core->pinmux.xout); +	if (err < 0) { +		dev_err(&core->client->dev, +			"Failed to configure digital audio pins(err = %d)\n", +			err); +		return err; +	} + +	err = si476x_core_cmd_zif_pin_cfg(core, +					  core->pinmux.iqclk, +					  core->pinmux.iqfs, +					  core->pinmux.iout, +					  core->pinmux.qout); +	if (err < 0) { +		dev_err(&core->client->dev, +			"Failed to configure ZIF pins(err = %d)\n", +			err); +		return err; +	} + +	err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core, +						      core->pinmux.icin, +						      core->pinmux.icip, +						      core->pinmux.icon, +						      core->pinmux.icop); +	if (err < 0) { +		dev_err(&core->client->dev, +			"Failed to configure IC-Link/GPO pins(err = %d)\n", +			err); +		return err; +	} + +	err = si476x_core_cmd_ana_audio_pin_cfg(core, +						core->pinmux.lrout); +	if (err < 0) { +		dev_err(&core->client->dev, +			"Failed to configure analog audio pins(err = %d)\n", +			err); +		return err; +	} + +	err = si476x_core_cmd_intb_pin_cfg(core, +					   core->pinmux.intb, +					   core->pinmux.a1); +	if (err < 0) { +		dev_err(&core->client->dev, +			"Failed to configure interrupt pins(err = %d)\n", +			err); +		return err; +	} + +	return 0; +} + +static inline void si476x_core_schedule_polling_work(struct si476x_core *core) +{ +	schedule_delayed_work(&core->status_monitor, +			      usecs_to_jiffies(SI476X_STATUS_POLL_US)); +} + +/** + * si476x_core_start() - early chip startup function + * @core: Core device structure + * @soft: When set, this flag forces "soft" startup, where "soft" + * power down is the one done by sending appropriate command instead + * of using reset pin of the tuner + * + * Perform required startup sequence to correctly power + * up the chip and perform initial configuration. It does the + * following sequence of actions: + *       1. Claims and enables the power supplies VD and VIO1 required + *          for I2C interface of the chip operation. + *       2. Waits for 100us, pulls the reset line up, enables irq, + *          waits for another 100us as it is specified by the + *          datasheet. + *       3. Sends 'POWER_UP' command to the device with all provided + *          information about power-up parameters. + *       4. Configures, pin multiplexor, disables digital audio and + *          configures interrupt sources. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +int si476x_core_start(struct si476x_core *core, bool soft) +{ +	struct i2c_client *client = core->client; +	int err; + +	if (!soft) { +		if (gpio_is_valid(core->gpio_reset)) +			gpio_set_value_cansleep(core->gpio_reset, 1); + +		if (client->irq) +			enable_irq(client->irq); + +		udelay(100); + +		if (!client->irq) { +			atomic_set(&core->is_alive, 1); +			si476x_core_schedule_polling_work(core); +		} +	} else { +		if (client->irq) +			enable_irq(client->irq); +		else { +			atomic_set(&core->is_alive, 1); +			si476x_core_schedule_polling_work(core); +		} +	} + +	err = si476x_core_cmd_power_up(core, +				       &core->power_up_parameters); + +	if (err < 0) { +		dev_err(&core->client->dev, +			"Power up failure(err = %d)\n", +			err); +		goto disable_irq; +	} + +	if (client->irq) +		atomic_set(&core->is_alive, 1); + +	err = si476x_core_config_pinmux(core); +	if (err < 0) { +		dev_err(&core->client->dev, +			"Failed to configure pinmux(err = %d)\n", +			err); +		goto disable_irq; +	} + +	if (client->irq) { +		err = regmap_write(core->regmap, +				   SI476X_PROP_INT_CTL_ENABLE, +				   SI476X_RDSIEN | +				   SI476X_STCIEN | +				   SI476X_CTSIEN); +		if (err < 0) { +			dev_err(&core->client->dev, +				"Failed to configure interrupt sources" +				"(err = %d)\n", err); +			goto disable_irq; +		} +	} + +	return 0; + +disable_irq: +	if (err == -ENODEV) +		atomic_set(&core->is_alive, 0); + +	if (client->irq) +		disable_irq(client->irq); +	else +		cancel_delayed_work_sync(&core->status_monitor); + +	if (gpio_is_valid(core->gpio_reset)) +		gpio_set_value_cansleep(core->gpio_reset, 0); + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_start); + +/** + * si476x_core_stop() - chip power-down function + * @core: Core device structure + * @soft: When set, function sends a POWER_DOWN command instead of + * bringing reset line low + * + * Power down the chip by performing following actions: + * 1. Disable IRQ or stop the polling worker + * 2. Send the POWER_DOWN command if the power down is soft or bring + *    reset line low if not. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +int si476x_core_stop(struct si476x_core *core, bool soft) +{ +	int err = 0; +	atomic_set(&core->is_alive, 0); + +	if (soft) { +		/* TODO: This probably shoud be a configurable option, +		 * so it is possible to have the chips keep their +		 * oscillators running +		 */ +		struct si476x_power_down_args args = { +			.xosc = false, +		}; +		err = si476x_core_cmd_power_down(core, &args); +	} + +	/* We couldn't disable those before +	 * 'si476x_core_cmd_power_down' since we expect to get CTS +	 * interrupt */ +	if (core->client->irq) +		disable_irq(core->client->irq); +	else +		cancel_delayed_work_sync(&core->status_monitor); + +	if (!soft) { +		if (gpio_is_valid(core->gpio_reset)) +			gpio_set_value_cansleep(core->gpio_reset, 0); +	} +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_stop); + +/** + * si476x_core_set_power_state() - set the level at which the power is + * supplied for the chip. + * @core: Core device structure + * @next_state: enum si476x_power_state describing power state to + *              switch to. + * + * Switch on all the required power supplies + * + * This function returns 0 in case of suvccess and negative error code + * otherwise. + */ +int si476x_core_set_power_state(struct si476x_core *core, +				enum si476x_power_state next_state) +{ +	/* +	   It is not clear form the datasheet if it is possible to +	   work with device if not all power domains are operational. +	   So for now the power-up policy is "power-up all the things!" +	 */ +	int err = 0; + +	if (core->power_state == SI476X_POWER_INCONSISTENT) { +		dev_err(&core->client->dev, +			"The device in inconsistent power state\n"); +		return -EINVAL; +	} + +	if (next_state != core->power_state) { +		switch (next_state) { +		case SI476X_POWER_UP_FULL: +			err = regulator_bulk_enable(ARRAY_SIZE(core->supplies), +						    core->supplies); +			if (err < 0) { +				core->power_state = SI476X_POWER_INCONSISTENT; +				break; +			} +			/* +			 * Startup timing diagram recommends to have a +			 * 100 us delay between enabling of the power +			 * supplies and turning the tuner on. +			 */ +			udelay(100); + +			err = si476x_core_start(core, false); +			if (err < 0) +				goto disable_regulators; + +			core->power_state = next_state; +			break; + +		case SI476X_POWER_DOWN: +			core->power_state = next_state; +			err = si476x_core_stop(core, false); +			if (err < 0) +				core->power_state = SI476X_POWER_INCONSISTENT; +disable_regulators: +			err = regulator_bulk_disable(ARRAY_SIZE(core->supplies), +						     core->supplies); +			if (err < 0) +				core->power_state = SI476X_POWER_INCONSISTENT; +			break; +		default: +			BUG(); +		} +	} + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_set_power_state); + +/** + * si476x_core_report_drainer_stop() - mark the completion of the RDS + * buffer drain porcess by the worker. + * + * @core: Core device structure + */ +static inline void si476x_core_report_drainer_stop(struct si476x_core *core) +{ +	mutex_lock(&core->rds_drainer_status_lock); +	core->rds_drainer_is_working = false; +	mutex_unlock(&core->rds_drainer_status_lock); +} + +/** + * si476x_core_start_rds_drainer_once() - start RDS drainer worker if + * ther is none working, do nothing otherwise + * + * @core: Datastructure corresponding to the chip. + */ +static inline void si476x_core_start_rds_drainer_once(struct si476x_core *core) +{ +	mutex_lock(&core->rds_drainer_status_lock); +	if (!core->rds_drainer_is_working) { +		core->rds_drainer_is_working = true; +		schedule_work(&core->rds_fifo_drainer); +	} +	mutex_unlock(&core->rds_drainer_status_lock); +} +/** + * si476x_drain_rds_fifo() - RDS buffer drainer. + * @work: struct work_struct being ppassed to the function by the + * kernel. + * + * Drain the contents of the RDS FIFO of + */ +static void si476x_core_drain_rds_fifo(struct work_struct *work) +{ +	int err; + +	struct si476x_core *core = container_of(work, struct si476x_core, +						rds_fifo_drainer); + +	struct si476x_rds_status_report report; + +	si476x_core_lock(core); +	err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report); +	if (!err) { +		int i = report.rdsfifoused; +		dev_dbg(&core->client->dev, +			"%d elements in RDS FIFO. Draining.\n", i); +		for (; i > 0; --i) { +			err = si476x_core_cmd_fm_rds_status(core, false, false, +							    (i == 1), &report); +			if (err < 0) +				goto unlock; + +			kfifo_in(&core->rds_fifo, report.rds, +				 sizeof(report.rds)); +			dev_dbg(&core->client->dev, "RDS data:\n %*ph\n", +				(int)sizeof(report.rds), report.rds); +		} +		dev_dbg(&core->client->dev, "Drrrrained!\n"); +		wake_up_interruptible(&core->rds_read_queue); +	} + +unlock: +	si476x_core_unlock(core); +	si476x_core_report_drainer_stop(core); +} + +/** + * si476x_core_pronounce_dead() + * + * @core: Core device structure + * + * Mark the device as being dead and wake up all potentially waiting + * threads of execution. + * + */ +static void si476x_core_pronounce_dead(struct si476x_core *core) +{ +	dev_info(&core->client->dev, "Core device is dead.\n"); + +	atomic_set(&core->is_alive, 0); + +	/* Wake up al possible waiting processes */ +	wake_up_interruptible(&core->rds_read_queue); + +	atomic_set(&core->cts, 1); +	wake_up(&core->command); + +	atomic_set(&core->stc, 1); +	wake_up(&core->tuning); +} + +/** + * si476x_core_i2c_xfer() + * + * @core: Core device structure + * @type: Transfer type + * @buf: Transfer buffer for/with data + * @count: Transfer buffer size + * + * Perfrom and I2C transfer(either read or write) and keep a counter + * of I/O errors. If the error counter rises above the threshold + * pronounce device dead. + * + * The function returns zero on succes or negative error code on + * failure. + */ +int si476x_core_i2c_xfer(struct si476x_core *core, +		    enum si476x_i2c_type type, +		    char *buf, int count) +{ +	static int io_errors_count; +	int err; +	if (type == SI476X_I2C_SEND) +		err = i2c_master_send(core->client, buf, count); +	else +		err = i2c_master_recv(core->client, buf, count); + +	if (err < 0) { +		if (io_errors_count++ > SI476X_MAX_IO_ERRORS) +			si476x_core_pronounce_dead(core); +	} else { +		io_errors_count = 0; +	} + +	return err; +} +EXPORT_SYMBOL_GPL(si476x_core_i2c_xfer); + +/** + * si476x_get_status() + * @core: Core device structure + * + * Get the status byte of the core device by berforming one byte I2C + * read. + * + * The function returns a status value or a negative error code on + * error. + */ +static int si476x_core_get_status(struct si476x_core *core) +{ +	u8 response; +	int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, +				  &response, sizeof(response)); + +	return (err < 0) ? err : response; +} + +/** + * si476x_get_and_signal_status() - IRQ dispatcher + * @core: Core device structure + * + * Dispatch the arrived interrupt request based on the value of the + * status byte reported by the tuner. + * + */ +static void si476x_core_get_and_signal_status(struct si476x_core *core) +{ +	int status = si476x_core_get_status(core); +	if (status < 0) { +		dev_err(&core->client->dev, "Failed to get status\n"); +		return; +	} + +	if (status & SI476X_CTS) { +		/* Unfortunately completions could not be used for +		 * signalling CTS since this flag cannot be cleared +		 * in status byte, and therefore once it becomes true +		 * multiple calls to 'complete' would cause the +		 * commands following the current one to be completed +		 * before they actually are */ +		dev_dbg(&core->client->dev, "[interrupt] CTSINT\n"); +		atomic_set(&core->cts, 1); +		wake_up(&core->command); +	} + +	if (status & SI476X_FM_RDS_INT) { +		dev_dbg(&core->client->dev, "[interrupt] RDSINT\n"); +		si476x_core_start_rds_drainer_once(core); +	} + +	if (status & SI476X_STC_INT) { +		dev_dbg(&core->client->dev, "[interrupt] STCINT\n"); +		atomic_set(&core->stc, 1); +		wake_up(&core->tuning); +	} +} + +static void si476x_core_poll_loop(struct work_struct *work) +{ +	struct si476x_core *core = SI476X_WORK_TO_CORE(work); + +	si476x_core_get_and_signal_status(core); + +	if (atomic_read(&core->is_alive)) +		si476x_core_schedule_polling_work(core); +} + +static irqreturn_t si476x_core_interrupt(int irq, void *dev) +{ +	struct si476x_core *core = dev; + +	si476x_core_get_and_signal_status(core); + +	return IRQ_HANDLED; +} + +/** + * si476x_firmware_version_to_revision() + * @core: Core device structure + * @major:  Firmware major number + * @minor1: Firmware first minor number + * @minor2: Firmware second minor number + * + * Convert a chip's firmware version number into an offset that later + * will be used to as offset in "vtable" of tuner functions + * + * This function returns a positive offset in case of success and a -1 + * in case of failure. + */ +static int si476x_core_fwver_to_revision(struct si476x_core *core, +					 int func, int major, +					 int minor1, int minor2) +{ +	switch (func) { +	case SI476X_FUNC_FM_RECEIVER: +		switch (major) { +		case 5: +			return SI476X_REVISION_A10; +		case 8: +			return SI476X_REVISION_A20; +		case 10: +			return SI476X_REVISION_A30; +		default: +			goto unknown_revision; +		} +	case SI476X_FUNC_AM_RECEIVER: +		switch (major) { +		case 5: +			return SI476X_REVISION_A10; +		case 7: +			return SI476X_REVISION_A20; +		case 9: +			return SI476X_REVISION_A30; +		default: +			goto unknown_revision; +		} +	case SI476X_FUNC_WB_RECEIVER: +		switch (major) { +		case 3: +			return SI476X_REVISION_A10; +		case 5: +			return SI476X_REVISION_A20; +		case 7: +			return SI476X_REVISION_A30; +		default: +			goto unknown_revision; +		} +	case SI476X_FUNC_BOOTLOADER: +	default:		/* FALLTHROUG */ +		BUG(); +		return -1; +	} + +unknown_revision: +	dev_err(&core->client->dev, +		"Unsupported version of the firmware: %d.%d.%d, " +		"reverting to A10 comptible functions\n", +		major, minor1, minor2); + +	return SI476X_REVISION_A10; +} + +/** + * si476x_get_revision_info() + * @core: Core device structure + * + * Get the firmware version number of the device. It is done in + * following three steps: + *    1. Power-up the device + *    2. Send the 'FUNC_INFO' command + *    3. Powering the device down. + * + * The function return zero on success and a negative error code on + * failure. + */ +static int si476x_core_get_revision_info(struct si476x_core *core) +{ +	int rval; +	struct si476x_func_info info; + +	si476x_core_lock(core); +	rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL); +	if (rval < 0) +		goto exit; + +	rval = si476x_core_cmd_func_info(core, &info); +	if (rval < 0) +		goto power_down; + +	core->revision = si476x_core_fwver_to_revision(core, info.func, +						       info.firmware.major, +						       info.firmware.minor[0], +						       info.firmware.minor[1]); +power_down: +	si476x_core_set_power_state(core, SI476X_POWER_DOWN); +exit: +	si476x_core_unlock(core); + +	return rval; +} + +bool si476x_core_has_am(struct si476x_core *core) +{ +	return core->chip_id == SI476X_CHIP_SI4761 || +		core->chip_id == SI476X_CHIP_SI4764; +} +EXPORT_SYMBOL_GPL(si476x_core_has_am); + +bool si476x_core_has_diversity(struct si476x_core *core) +{ +	return core->chip_id == SI476X_CHIP_SI4764; +} +EXPORT_SYMBOL_GPL(si476x_core_has_diversity); + +bool si476x_core_is_a_secondary_tuner(struct si476x_core *core) +{ +	return si476x_core_has_diversity(core) && +		(core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA || +		 core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING); +} +EXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner); + +bool si476x_core_is_a_primary_tuner(struct si476x_core *core) +{ +	return si476x_core_has_diversity(core) && +		(core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA || +		 core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING); +} +EXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner); + +bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core) +{ +	return si476x_core_has_am(core) && +		(core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER); +} +EXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode); + +bool si476x_core_is_powered_up(struct si476x_core *core) +{ +	return core->power_state == SI476X_POWER_UP_FULL; +} +EXPORT_SYMBOL_GPL(si476x_core_is_powered_up); + +static int si476x_core_probe(struct i2c_client *client, +			     const struct i2c_device_id *id) +{ +	int rval; +	struct si476x_core          *core; +	struct si476x_platform_data *pdata; +	struct mfd_cell *cell; +	int              cell_num; + +	core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL); +	if (!core) { +		dev_err(&client->dev, +			"failed to allocate 'struct si476x_core'\n"); +		return -ENOMEM; +	} +	core->client = client; + +	core->regmap = devm_regmap_init_si476x(core); +	if (IS_ERR(core->regmap)) { +		rval = PTR_ERR(core->regmap); +		dev_err(&client->dev, +			"Failed to allocate register map: %d\n", +			rval); +		return rval; +	} + +	i2c_set_clientdata(client, core); + +	atomic_set(&core->is_alive, 0); +	core->power_state = SI476X_POWER_DOWN; + +	pdata = client->dev.platform_data; +	if (pdata) { +		memcpy(&core->power_up_parameters, +		       &pdata->power_up_parameters, +		       sizeof(core->power_up_parameters)); + +		core->gpio_reset = -1; +		if (gpio_is_valid(pdata->gpio_reset)) { +			rval = gpio_request(pdata->gpio_reset, "si476x reset"); +			if (rval) { +				dev_err(&client->dev, +					"Failed to request gpio: %d\n", rval); +				return rval; +			} +			core->gpio_reset = pdata->gpio_reset; +			gpio_direction_output(core->gpio_reset, 0); +		} + +		core->diversity_mode = pdata->diversity_mode; +		memcpy(&core->pinmux, &pdata->pinmux, +		       sizeof(struct si476x_pinmux)); +	} else { +		dev_err(&client->dev, "No platform data provided\n"); +		return -EINVAL; +	} + +	core->supplies[0].supply = "vd"; +	core->supplies[1].supply = "va"; +	core->supplies[2].supply = "vio1"; +	core->supplies[3].supply = "vio2"; + +	rval = devm_regulator_bulk_get(&client->dev, +				       ARRAY_SIZE(core->supplies), +				       core->supplies); +	if (rval) { +		dev_err(&client->dev, "Failet to gett all of the regulators\n"); +		goto free_gpio; +	} + +	mutex_init(&core->cmd_lock); +	init_waitqueue_head(&core->command); +	init_waitqueue_head(&core->tuning); + +	rval = kfifo_alloc(&core->rds_fifo, +			   SI476X_DRIVER_RDS_FIFO_DEPTH * +			   sizeof(struct v4l2_rds_data), +			   GFP_KERNEL); +	if (rval) { +		dev_err(&client->dev, "Could not alloate the FIFO\n"); +		goto free_gpio; +	} +	mutex_init(&core->rds_drainer_status_lock); +	init_waitqueue_head(&core->rds_read_queue); +	INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo); + +	if (client->irq) { +		rval = devm_request_threaded_irq(&client->dev, +						 client->irq, NULL, +						 si476x_core_interrupt, +						 IRQF_TRIGGER_FALLING, +						 client->name, core); +		if (rval < 0) { +			dev_err(&client->dev, "Could not request IRQ %d\n", +				client->irq); +			goto free_kfifo; +		} +		disable_irq(client->irq); +		dev_dbg(&client->dev, "IRQ requested.\n"); + +		core->rds_fifo_depth = 20; +	} else { +		INIT_DELAYED_WORK(&core->status_monitor, +				  si476x_core_poll_loop); +		dev_info(&client->dev, +			 "No IRQ number specified, will use polling\n"); + +		core->rds_fifo_depth = 5; +	} + +	core->chip_id = id->driver_data; + +	rval = si476x_core_get_revision_info(core); +	if (rval < 0) { +		rval = -ENODEV; +		goto free_kfifo; +	} + +	cell_num = 0; + +	cell = &core->cells[SI476X_RADIO_CELL]; +	cell->name = "si476x-radio"; +	cell_num++; + +#ifdef CONFIG_SND_SOC_SI476X +	if ((core->chip_id == SI476X_CHIP_SI4761 || +	     core->chip_id == SI476X_CHIP_SI4764)	&& +	    core->pinmux.dclk == SI476X_DCLK_DAUDIO     && +	    core->pinmux.dfs  == SI476X_DFS_DAUDIO      && +	    core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT && +	    core->pinmux.xout == SI476X_XOUT_TRISTATE) { +		cell = &core->cells[SI476X_CODEC_CELL]; +		cell->name          = "si476x-codec"; +		cell_num++; +	} +#endif +	rval = mfd_add_devices(&client->dev, +			       (client->adapter->nr << 8) + client->addr, +			       core->cells, cell_num, +			       NULL, 0, NULL); +	if (!rval) +		return 0; + +free_kfifo: +	kfifo_free(&core->rds_fifo); + +free_gpio: +	if (gpio_is_valid(core->gpio_reset)) +		gpio_free(core->gpio_reset); + +	return rval; +} + +static int si476x_core_remove(struct i2c_client *client) +{ +	struct si476x_core *core = i2c_get_clientdata(client); + +	si476x_core_pronounce_dead(core); +	mfd_remove_devices(&client->dev); + +	if (client->irq) +		disable_irq(client->irq); +	else +		cancel_delayed_work_sync(&core->status_monitor); + +	kfifo_free(&core->rds_fifo); + +	if (gpio_is_valid(core->gpio_reset)) +		gpio_free(core->gpio_reset); + +	return 0; +} + + +static const struct i2c_device_id si476x_id[] = { +	{ "si4761", SI476X_CHIP_SI4761 }, +	{ "si4764", SI476X_CHIP_SI4764 }, +	{ "si4768", SI476X_CHIP_SI4768 }, +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, si476x_id); + +static struct i2c_driver si476x_core_driver = { +	.driver		= { +		.name	= "si476x-core", +		.owner  = THIS_MODULE, +	}, +	.probe		= si476x_core_probe, +	.remove         = si476x_core_remove, +	.id_table       = si476x_id, +}; +module_i2c_driver(si476x_core_driver); + + +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); +MODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/si476x-prop.c b/drivers/mfd/si476x-prop.c new file mode 100644 index 00000000000..cfeffa6e15d --- /dev/null +++ b/drivers/mfd/si476x-prop.c @@ -0,0 +1,241 @@ +/* + * drivers/mfd/si476x-prop.c -- Subroutines to access + * properties of si476x chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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; version 2 of the License. + * + * 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. + */ +#include <linux/module.h> + +#include <linux/mfd/si476x-core.h> + +struct si476x_property_range { +	u16 low, high; +}; + +static bool si476x_core_element_is_in_array(u16 element, +					    const u16 array[], +					    size_t size) +{ +	int i; + +	for (i = 0; i < size; i++) +		if (element == array[i]) +			return true; + +	return false; +} + +static bool si476x_core_element_is_in_range(u16 element, +					    const struct si476x_property_range range[], +					    size_t size) +{ +	int i; + +	for (i = 0; i < size; i++) +		if (element <= range[i].high && element >= range[i].low) +			return true; + +	return false; +} + +static bool si476x_core_is_valid_property_a10(struct si476x_core *core, +					      u16 property) +{ +	static const u16 valid_properties[] = { +		0x0000, +		0x0500, 0x0501, +		0x0600, +		0x0709, 0x070C, 0x070D, 0x70E, 0x710, +		0x0718, +		0x1207, 0x1208, +		0x2007, +		0x2300, +	}; + +	static const struct si476x_property_range valid_ranges[] = { +		{ 0x0200, 0x0203 }, +		{ 0x0300, 0x0303 }, +		{ 0x0400, 0x0404 }, +		{ 0x0700, 0x0707 }, +		{ 0x1100, 0x1102 }, +		{ 0x1200, 0x1204 }, +		{ 0x1300, 0x1306 }, +		{ 0x2000, 0x2005 }, +		{ 0x2100, 0x2104 }, +		{ 0x2106, 0x2106 }, +		{ 0x2200, 0x220E }, +		{ 0x3100, 0x3104 }, +		{ 0x3207, 0x320F }, +		{ 0x3300, 0x3304 }, +		{ 0x3500, 0x3517 }, +		{ 0x3600, 0x3617 }, +		{ 0x3700, 0x3717 }, +		{ 0x4000, 0x4003 }, +	}; + +	return	si476x_core_element_is_in_range(property, valid_ranges, +						ARRAY_SIZE(valid_ranges)) || +		si476x_core_element_is_in_array(property, valid_properties, +						ARRAY_SIZE(valid_properties)); +} + +static bool si476x_core_is_valid_property_a20(struct si476x_core *core, +					      u16 property) +{ +	static const u16 valid_properties[] = { +		0x071B, +		0x1006, +		0x2210, +		0x3401, +	}; + +	static const struct si476x_property_range valid_ranges[] = { +		{ 0x2215, 0x2219 }, +	}; + +	return	si476x_core_is_valid_property_a10(core, property) || +		si476x_core_element_is_in_range(property, valid_ranges, +						ARRAY_SIZE(valid_ranges))  || +		si476x_core_element_is_in_array(property, valid_properties, +						ARRAY_SIZE(valid_properties)); +} + +static bool si476x_core_is_valid_property_a30(struct si476x_core *core, +					      u16 property) +{ +	static const u16 valid_properties[] = { +		0x071C, 0x071D, +		0x1007, 0x1008, +		0x220F, 0x2214, +		0x2301, +		0x3105, 0x3106, +		0x3402, +	}; + +	static const struct si476x_property_range valid_ranges[] = { +		{ 0x0405, 0x0411 }, +		{ 0x2008, 0x200B }, +		{ 0x2220, 0x2223 }, +		{ 0x3100, 0x3106 }, +	}; + +	return	si476x_core_is_valid_property_a20(core, property) || +		si476x_core_element_is_in_range(property, valid_ranges, +						ARRAY_SIZE(valid_ranges)) || +		si476x_core_element_is_in_array(property, valid_properties, +						ARRAY_SIZE(valid_properties)); +} + +typedef bool (*valid_property_pred_t) (struct si476x_core *, u16); + +static bool si476x_core_is_valid_property(struct si476x_core *core, +					  u16 property) +{ +	static const valid_property_pred_t is_valid_property[] = { +		[SI476X_REVISION_A10] = si476x_core_is_valid_property_a10, +		[SI476X_REVISION_A20] = si476x_core_is_valid_property_a20, +		[SI476X_REVISION_A30] = si476x_core_is_valid_property_a30, +	}; + +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); +	return is_valid_property[core->revision](core, property); +} + + +static bool si476x_core_is_readonly_property(struct si476x_core *core, +					     u16 property) +{ +	BUG_ON(core->revision > SI476X_REVISION_A30 || +	       core->revision == -1); + +	switch (core->revision) { +	case SI476X_REVISION_A10: +		return (property == 0x3200); +	case SI476X_REVISION_A20: +		return (property == 0x1006 || +			property == 0x2210 || +			property == 0x3200); +	case SI476X_REVISION_A30: +		return false; +	} + +	return false; +} + +static bool si476x_core_regmap_readable_register(struct device *dev, +						 unsigned int reg) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct si476x_core *core = i2c_get_clientdata(client); + +	return si476x_core_is_valid_property(core, (u16) reg); + +} + +static bool si476x_core_regmap_writable_register(struct device *dev, +						 unsigned int reg) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct si476x_core *core = i2c_get_clientdata(client); + +	return si476x_core_is_valid_property(core, (u16) reg) && +		!si476x_core_is_readonly_property(core, (u16) reg); +} + + +static int si476x_core_regmap_write(void *context, unsigned int reg, +				    unsigned int val) +{ +	return si476x_core_cmd_set_property(context, reg, val); +} + +static int si476x_core_regmap_read(void *context, unsigned int reg, +				   unsigned *val) +{ +	struct si476x_core *core = context; +	int err; + +	err = si476x_core_cmd_get_property(core, reg); +	if (err < 0) +		return err; + +	*val = err; + +	return 0; +} + + +static const struct regmap_config si476x_regmap_config = { +	.reg_bits = 16, +	.val_bits = 16, + +	.max_register = 0x4003, + +	.writeable_reg = si476x_core_regmap_writable_register, +	.readable_reg = si476x_core_regmap_readable_register, + +	.reg_read = si476x_core_regmap_read, +	.reg_write = si476x_core_regmap_write, + +	.cache_type = REGCACHE_RBTREE, +}; + +struct regmap *devm_regmap_init_si476x(struct si476x_core *core) +{ +	return devm_regmap_init(&core->client->dev, NULL, +				core, &si476x_regmap_config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_si476x); diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 9bd33169a11..d70a343078f 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -98,17 +98,6 @@ static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)  	return 0;  } -static int 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; -} -  /* This function is exported and is not expected to fail */  u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val,  		       enum sta2x11_mfd_plat_dev index) diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index fd5fcb63068..0da02e11d58 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -75,6 +75,7 @@ static const struct i2c_device_id stmpe_i2c_id[] = {  	{ "stmpe801", STMPE801 },  	{ "stmpe811", STMPE811 },  	{ "stmpe1601", STMPE1601 }, +	{ "stmpe1801", STMPE1801 },  	{ "stmpe2401", STMPE2401 },  	{ "stmpe2403", STMPE2403 },  	{ } diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c index 973659f8abd..a81badbaa91 100644 --- a/drivers/mfd/stmpe-spi.c +++ b/drivers/mfd/stmpe-spi.c @@ -103,7 +103,7 @@ stmpe_spi_probe(struct spi_device *spi)  static int stmpe_spi_remove(struct spi_device *spi)  { -	struct stmpe *stmpe = dev_get_drvdata(&spi->dev); +	struct stmpe *stmpe = spi_get_drvdata(spi);  	return stmpe_remove(stmpe);  } diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 4b11202061b..bbccd514d3e 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -19,6 +19,7 @@  #include <linux/pm.h>  #include <linux/slab.h>  #include <linux/mfd/core.h> +#include <linux/delay.h>  #include "stmpe.h"  static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks) @@ -643,6 +644,88 @@ static struct stmpe_variant_info stmpe1601 = {  };  /* + * STMPE1801 + */ +static const u8 stmpe1801_regs[] = { +	[STMPE_IDX_CHIP_ID]	= STMPE1801_REG_CHIP_ID, +	[STMPE_IDX_ICR_LSB]	= STMPE1801_REG_INT_CTRL_LOW, +	[STMPE_IDX_IER_LSB]	= STMPE1801_REG_INT_EN_MASK_LOW, +	[STMPE_IDX_ISR_LSB]	= STMPE1801_REG_INT_STA_LOW, +	[STMPE_IDX_GPMR_LSB]	= STMPE1801_REG_GPIO_MP_LOW, +	[STMPE_IDX_GPSR_LSB]	= STMPE1801_REG_GPIO_SET_LOW, +	[STMPE_IDX_GPCR_LSB]	= STMPE1801_REG_GPIO_CLR_LOW, +	[STMPE_IDX_GPDR_LSB]	= STMPE1801_REG_GPIO_SET_DIR_LOW, +	[STMPE_IDX_GPRER_LSB]	= STMPE1801_REG_GPIO_RE_LOW, +	[STMPE_IDX_GPFER_LSB]	= STMPE1801_REG_GPIO_FE_LOW, +	[STMPE_IDX_IEGPIOR_LSB]	= STMPE1801_REG_INT_EN_GPIO_MASK_LOW, +	[STMPE_IDX_ISGPIOR_LSB]	= STMPE1801_REG_INT_STA_GPIO_LOW, +}; + +static struct stmpe_variant_block stmpe1801_blocks[] = { +	{ +		.cell	= &stmpe_gpio_cell, +		.irq	= STMPE1801_IRQ_GPIOC, +		.block	= STMPE_BLOCK_GPIO, +	}, +	{ +		.cell	= &stmpe_keypad_cell, +		.irq	= STMPE1801_IRQ_KEYPAD, +		.block	= STMPE_BLOCK_KEYPAD, +	}, +}; + +static int stmpe1801_enable(struct stmpe *stmpe, unsigned int blocks, +			    bool enable) +{ +	unsigned int mask = 0; +	if (blocks & STMPE_BLOCK_GPIO) +		mask |= STMPE1801_MSK_INT_EN_GPIO; + +	if (blocks & STMPE_BLOCK_KEYPAD) +		mask |= STMPE1801_MSK_INT_EN_KPC; + +	return __stmpe_set_bits(stmpe, STMPE1801_REG_INT_EN_MASK_LOW, mask, +				enable ? mask : 0); +} + +static int stmpe1801_reset(struct stmpe *stmpe) +{ +	unsigned long timeout; +	int ret = 0; + +	ret = __stmpe_set_bits(stmpe, STMPE1801_REG_SYS_CTRL, +		STMPE1801_MSK_SYS_CTRL_RESET, STMPE1801_MSK_SYS_CTRL_RESET); +	if (ret < 0) +		return ret; + +	timeout = jiffies + msecs_to_jiffies(100); +	while (time_before(jiffies, timeout)) { +		ret = __stmpe_reg_read(stmpe, STMPE1801_REG_SYS_CTRL); +		if (ret < 0) +			return ret; +		if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET)) +			return 0; +		usleep_range(100, 200); +	}; +	return -EIO; +} + +static struct stmpe_variant_info stmpe1801 = { +	.name		= "stmpe1801", +	.id_val		= STMPE1801_ID, +	.id_mask	= 0xfff0, +	.num_gpios	= 18, +	.af_bits	= 0, +	.regs		= stmpe1801_regs, +	.blocks		= stmpe1801_blocks, +	.num_blocks	= ARRAY_SIZE(stmpe1801_blocks), +	.num_irqs	= STMPE1801_NR_INTERNAL_IRQS, +	.enable		= stmpe1801_enable, +	/* stmpe1801 do not have any gpio alternate function */ +	.get_altfunc	= NULL, +}; + +/*   * STMPE24XX   */ @@ -740,6 +823,7 @@ static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {  	[STMPE801]	= &stmpe801,  	[STMPE811]	= &stmpe811,  	[STMPE1601]	= &stmpe1601, +	[STMPE1801]	= &stmpe1801,  	[STMPE2401]	= &stmpe2401,  	[STMPE2403]	= &stmpe2403,  }; @@ -759,7 +843,7 @@ static irqreturn_t stmpe_irq(int irq, void *data)  	struct stmpe *stmpe = data;  	struct stmpe_variant_info *variant = stmpe->variant;  	int num = DIV_ROUND_UP(variant->num_irqs, 8); -	u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB]; +	u8 israddr;  	u8 isr[num];  	int ret;  	int i; @@ -771,6 +855,11 @@ static irqreturn_t stmpe_irq(int irq, void *data)  		return IRQ_HANDLED;  	} +	if (variant->id_val == STMPE1801_ID) +		israddr = stmpe->regs[STMPE_IDX_ISR_LSB]; +	else +		israddr = stmpe->regs[STMPE_IDX_ISR_MSB]; +  	ret = stmpe_block_read(stmpe, israddr, num, isr);  	if (ret < 0)  		return IRQ_NONE; @@ -938,6 +1027,12 @@ static int stmpe_chip_init(struct stmpe *stmpe)  	if (ret)  		return ret; +	if (id == STMPE1801_ID)	{ +		ret =  stmpe1801_reset(stmpe); +		if (ret < 0) +			return ret; +	} +  	if (stmpe->irq >= 0) {  		if (id == STMPE801_ID)  			icr = STMPE801_REG_SYS_CTRL_INT_EN; @@ -1015,7 +1110,10 @@ void stmpe_of_probe(struct stmpe_platform_data *pdata, struct device_node *np)  {  	struct device_node *child; -	pdata->id = -1; +	pdata->id = of_alias_get_id(np, "stmpe-i2c"); +	if (pdata->id < 0) +		pdata->id = -1; +  	pdata->irq_trigger = IRQF_TRIGGER_NONE;  	of_property_read_u32(np, "st,autosleep-timeout", @@ -1057,6 +1155,9 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)  			return -ENOMEM;  		stmpe_of_probe(pdata, np); + +		if (of_find_property(np, "interrupts", NULL) == NULL) +			ci->irq = -1;  	}  	stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL); diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 7b8e13f5b76..ff2b09ba879 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -199,6 +199,55 @@ int stmpe_remove(struct stmpe *stmpe);  #define STPME1601_AUTOSLEEP_ENABLE		(1 << 3)  /* + * STMPE1801 + */ +#define STMPE1801_ID			0xc110 +#define STMPE1801_NR_INTERNAL_IRQS	5 +#define STMPE1801_IRQ_KEYPAD_COMBI	4 +#define STMPE1801_IRQ_GPIOC		3 +#define STMPE1801_IRQ_KEYPAD_OVER	2 +#define STMPE1801_IRQ_KEYPAD		1 +#define STMPE1801_IRQ_WAKEUP		0 + +#define STMPE1801_REG_CHIP_ID			0x00 +#define STMPE1801_REG_SYS_CTRL			0x02 +#define STMPE1801_REG_INT_CTRL_LOW		0x04 +#define STMPE1801_REG_INT_EN_MASK_LOW		0x06 +#define STMPE1801_REG_INT_STA_LOW		0x08 +#define STMPE1801_REG_INT_EN_GPIO_MASK_LOW	0x0A +#define STMPE1801_REG_INT_EN_GPIO_MASK_MID	0x0B +#define STMPE1801_REG_INT_EN_GPIO_MASK_HIGH	0x0C +#define STMPE1801_REG_INT_STA_GPIO_LOW		0x0D +#define STMPE1801_REG_INT_STA_GPIO_MID		0x0E +#define STMPE1801_REG_INT_STA_GPIO_HIGH		0x0F +#define STMPE1801_REG_GPIO_SET_LOW		0x10 +#define STMPE1801_REG_GPIO_SET_MID		0x11 +#define STMPE1801_REG_GPIO_SET_HIGH		0x12 +#define STMPE1801_REG_GPIO_CLR_LOW		0x13 +#define STMPE1801_REG_GPIO_CLR_MID		0x14 +#define STMPE1801_REG_GPIO_CLR_HIGH		0x15 +#define STMPE1801_REG_GPIO_MP_LOW		0x16 +#define STMPE1801_REG_GPIO_MP_MID		0x17 +#define STMPE1801_REG_GPIO_MP_HIGH		0x18 +#define STMPE1801_REG_GPIO_SET_DIR_LOW		0x19 +#define STMPE1801_REG_GPIO_SET_DIR_MID		0x1A +#define STMPE1801_REG_GPIO_SET_DIR_HIGH		0x1B +#define STMPE1801_REG_GPIO_RE_LOW		0x1C +#define STMPE1801_REG_GPIO_RE_MID		0x1D +#define STMPE1801_REG_GPIO_RE_HIGH		0x1E +#define STMPE1801_REG_GPIO_FE_LOW		0x1F +#define STMPE1801_REG_GPIO_FE_MID		0x20 +#define STMPE1801_REG_GPIO_FE_HIGH		0x21 +#define STMPE1801_REG_GPIO_PULL_UP_LOW		0x22 +#define STMPE1801_REG_GPIO_PULL_UP_MID		0x23 +#define STMPE1801_REG_GPIO_PULL_UP_HIGH		0x24 + +#define STMPE1801_MSK_SYS_CTRL_RESET		(1 << 7) + +#define STMPE1801_MSK_INT_EN_KPC		(1 << 1) +#define STMPE1801_MSK_INT_EN_GPIO		(1 << 3) + +/*   * STMPE24xx   */ diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 61aea6381cd..962a6e17a01 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -25,17 +25,15 @@  static struct platform_driver syscon_driver;  struct syscon { -	struct device *dev;  	void __iomem *base;  	struct regmap *regmap;  }; -static int syscon_match(struct device *dev, void *data) +static int syscon_match_node(struct device *dev, void *data)  { -	struct syscon *syscon = dev_get_drvdata(dev);  	struct device_node *dn = data; -	return (syscon->dev->of_node == dn) ? 1 : 0; +	return (dev->of_node == dn) ? 1 : 0;  }  struct regmap *syscon_node_to_regmap(struct device_node *np) @@ -44,7 +42,7 @@ struct regmap *syscon_node_to_regmap(struct device_node *np)  	struct device *dev;  	dev = driver_find_device(&syscon_driver.driver, NULL, np, -				 syscon_match); +				 syscon_match_node);  	if (!dev)  		return ERR_PTR(-EPROBE_DEFER); @@ -70,6 +68,34 @@ struct regmap *syscon_regmap_lookup_by_compatible(const char *s)  }  EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); +static int syscon_match_pdevname(struct device *dev, void *data) +{ +	struct platform_device *pdev = to_platform_device(dev); +	const struct platform_device_id *id = platform_get_device_id(pdev); + +	if (id) +		if (!strcmp(id->name, (const char *)data)) +			return 1; + +	return !strcmp(dev_name(dev), (const char *)data); +} + +struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) +{ +	struct device *dev; +	struct syscon *syscon; + +	dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, +				 syscon_match_pdevname); +	if (!dev) +		return ERR_PTR(-EPROBE_DEFER); + +	syscon = dev_get_drvdata(dev); + +	return syscon->regmap; +} +EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); +  struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,  					const char *property)  { @@ -101,28 +127,22 @@ static struct regmap_config syscon_regmap_config = {  static int syscon_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; -	struct device_node *np = dev->of_node;  	struct syscon *syscon; -	struct resource res; -	int ret; - -	if (!np) -		return -ENOENT; +	struct resource *res; -	syscon = devm_kzalloc(dev, sizeof(struct syscon), -			    GFP_KERNEL); +	syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);  	if (!syscon)  		return -ENOMEM; -	syscon->base = of_iomap(np, 0); -	if (!syscon->base) -		return -EADDRNOTAVAIL; +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -ENOENT; -	ret = of_address_to_resource(np, 0, &res); -	if (ret) -		return ret; +	syscon->base = devm_ioremap(dev, res->start, resource_size(res)); +	if (!syscon->base) +		return -ENOMEM; -	syscon_regmap_config.max_register = res.end - res.start - 3; +	syscon_regmap_config.max_register = res->end - res->start - 3;  	syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,  					&syscon_regmap_config);  	if (IS_ERR(syscon->regmap)) { @@ -130,25 +150,17 @@ static int syscon_probe(struct platform_device *pdev)  		return PTR_ERR(syscon->regmap);  	} -	syscon->dev = dev;  	platform_set_drvdata(pdev, syscon); -	dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n", -		res.start, res.end); +	dev_info(dev, "regmap %pR registered\n", res);  	return 0;  } -static int syscon_remove(struct platform_device *pdev) -{ -	struct syscon *syscon; - -	syscon = platform_get_drvdata(pdev); -	iounmap(syscon->base); -	platform_set_drvdata(pdev, NULL); - -	return 0; -} +static const struct platform_device_id syscon_ids[] = { +	{ "syscon", }, +	{ } +};  static struct platform_driver syscon_driver = {  	.driver = { @@ -157,7 +169,7 @@ static struct platform_driver syscon_driver = {  		.of_match_table = of_syscon_match,  	},  	.probe		= syscon_probe, -	.remove		= syscon_remove, +	.id_table	= syscon_ids,  };  static int __init syscon_init(void) diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index ecc092c7f74..4cb92bb2aea 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -350,7 +350,8 @@ static int tc3589x_probe(struct i2c_client *i2c,  				     | I2C_FUNC_SMBUS_I2C_BLOCK))  		return -EIO; -	tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL); +	tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x), +				GFP_KERNEL);  	if (!tc3589x)  		return -ENOMEM; @@ -366,33 +367,27 @@ static int tc3589x_probe(struct i2c_client *i2c,  	ret = tc3589x_chip_init(tc3589x);  	if (ret) -		goto out_free; +		return ret;  	ret = tc3589x_irq_init(tc3589x, np);  	if (ret) -		goto out_free; +		return ret;  	ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,  				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,  				   "tc3589x", tc3589x);  	if (ret) {  		dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); -		goto out_free; +		return ret;  	}  	ret = tc3589x_device_init(tc3589x);  	if (ret) {  		dev_err(tc3589x->dev, "failed to add child devices\n"); -		goto out_freeirq; +		return ret;  	}  	return 0; - -out_freeirq: -	free_irq(tc3589x->i2c->irq, tc3589x); -out_free: -	kfree(tc3589x); -	return ret;  }  static int tc3589x_remove(struct i2c_client *client) @@ -401,10 +396,6 @@ static int tc3589x_remove(struct i2c_client *client)  	mfd_remove_devices(tc3589x->dev); -	free_irq(tc3589x->i2c->irq, tc3589x); - -	kfree(tc3589x); -  	return 0;  } diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 98edb5be85c..fbd6ee67b5a 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -56,12 +56,23 @@  #define TPS65090_INT2_MASK_OVERLOAD_FET6		6  #define TPS65090_INT2_MASK_OVERLOAD_FET7		7 +static struct resource charger_resources[] = { +	{ +		.start  = TPS65090_IRQ_VAC_STATUS_CHANGE, +		.end    = TPS65090_IRQ_VAC_STATUS_CHANGE, +		.flags  = IORESOURCE_IRQ, +	} +}; +  static struct mfd_cell tps65090s[] = {  	{  		.name = "tps65090-pmic",  	},  	{  		.name = "tps65090-charger", +		.num_resources = ARRAY_SIZE(charger_resources), +		.resources = &charger_resources[0], +		.of_compatible = "ti,tps65090-charger",  	},  }; diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 942b666a2a0..42bd3ea5df3 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -211,12 +211,14 @@ static int twl4030battery_current(int raw_volt)   * @reg_base - Base address of the first channel   * @Channels - 16 bit bitmap. If the bit is set, channel value is read   * @buf - The channel values are stored here. if read fails error + * @raw - Return raw values without conversion   * value is stored   * Returns the number of successfully read channels.   */  static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,  				      u8 reg_base, unsigned -						long channels, int *buf) +				      long channels, int *buf, +				      bool raw)  {  	int count = 0, count_req = 0, i;  	u8 reg; @@ -230,6 +232,10 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,  			count_req++;  			continue;  		} +		if (raw) { +			count++; +			continue; +		}  		switch (i) {  		case 10:  			buf[i] = twl4030battery_current(buf[i]); @@ -371,7 +377,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)  		method = &twl4030_conversion_methods[r->method];  		/* Read results */  		len = twl4030_madc_read_channels(madc, method->rbase, -						 r->channels, r->rbuf); +						 r->channels, r->rbuf, r->raw);  		/* Return results to caller */  		if (r->func_cb != NULL) {  			r->func_cb(len, r->channels, r->rbuf); @@ -397,7 +403,7 @@ err_i2c:  		method = &twl4030_conversion_methods[r->method];  		/* Read results */  		len = twl4030_madc_read_channels(madc, method->rbase, -						 r->channels, r->rbuf); +						 r->channels, r->rbuf, r->raw);  		/* Return results to caller */  		if (r->func_cb != NULL) {  			r->func_cb(len, r->channels, r->rbuf); @@ -585,7 +591,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)  		goto out;  	}  	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, -					 req->channels, req->rbuf); +					 req->channels, req->rbuf, req->raw);  	twl4030_madc->requests[req->method].active = 0;  out: diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index f361bf38a0a..492ee2cd340 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -554,7 +554,7 @@ static int twl6040_probe(struct i2c_client *client,  	twl6040->supplies[0].supply = "vio";  	twl6040->supplies[1].supply = "v2v1"; -	ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, +	ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,  				 twl6040->supplies);  	if (ret != 0) {  		dev_err(&client->dev, "Failed to get supplies: %d\n", ret); @@ -564,7 +564,7 @@ static int twl6040_probe(struct i2c_client *client,  	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; +		goto regulator_get_err;  	}  	twl6040->dev = &client->dev; @@ -586,8 +586,8 @@ static int twl6040_probe(struct i2c_client *client,  		twl6040->audpwron = -EINVAL;  	if (gpio_is_valid(twl6040->audpwron)) { -		ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW, -				       "audpwron"); +		ret = devm_gpio_request_one(&client->dev, twl6040->audpwron, +					GPIOF_OUT_INIT_LOW, "audpwron");  		if (ret)  			goto gpio_err;  	} @@ -596,14 +596,14 @@ static int twl6040_probe(struct i2c_client *client,  			IRQF_ONESHOT, 0, &twl6040_irq_chip,  			&twl6040->irq_data);  	if (ret < 0) -		goto irq_init_err; +		goto gpio_err;  	twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data,  					       TWL6040_IRQ_READY);  	twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data,  					       TWL6040_IRQ_TH); -	ret = request_threaded_irq(twl6040->irq_ready, NULL, +	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL,  				   twl6040_readyint_handler, IRQF_ONESHOT,  				   "twl6040_irq_ready", twl6040);  	if (ret) { @@ -611,7 +611,7 @@ static int twl6040_probe(struct i2c_client *client,  		goto readyirq_err;  	} -	ret = request_threaded_irq(twl6040->irq_th, NULL, +	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL,  				   twl6040_thint_handler, IRQF_ONESHOT,  				   "twl6040_irq_th", twl6040);  	if (ret) { @@ -681,18 +681,13 @@ static int twl6040_probe(struct i2c_client *client,  	return 0;  mfd_err: -	free_irq(twl6040->irq_th, twl6040); +	devm_free_irq(&client->dev, twl6040->irq_th, twl6040);  thirq_err: -	free_irq(twl6040->irq_ready, twl6040); +	devm_free_irq(&client->dev, twl6040->irq_ready, twl6040);  readyirq_err:  	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); -irq_init_err: -	if (gpio_is_valid(twl6040->audpwron)) -		gpio_free(twl6040->audpwron);  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);  err: @@ -706,18 +701,14 @@ static int twl6040_remove(struct i2c_client *client)  	if (twl6040->power_count)  		twl6040_power(twl6040, 0); -	if (gpio_is_valid(twl6040->audpwron)) -		gpio_free(twl6040->audpwron); - -	free_irq(twl6040->irq_ready, twl6040); -	free_irq(twl6040->irq_th, twl6040); +	devm_free_irq(&client->dev, twl6040->irq_ready, twl6040); +	devm_free_irq(&client->dev, twl6040->irq_th, twl6040);  	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);  	mfd_remove_devices(&client->dev);  	i2c_set_clientdata(client, NULL);  	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); -	regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);  	return 0;  } diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index daf69527ed8..e9031fa9d53 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -75,6 +75,11 @@ static int ucb1400_core_probe(struct device *dev)  	/* GPIO */  	ucb_gpio.ac97 = ac97; +	if (pdata) { +		ucb_gpio.gpio_setup = pdata->gpio_setup; +		ucb_gpio.gpio_teardown = pdata->gpio_teardown; +		ucb_gpio.gpio_offset = pdata->gpio_offset; +	}  	ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1);  	if (!ucb->ucb1400_gpio) {  		err = -ENOMEM; diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c index 3c1723aa622..84ce6b9daa3 100644 --- a/drivers/mfd/vexpress-config.c +++ b/drivers/mfd/vexpress-config.c @@ -184,13 +184,14 @@ static int vexpress_config_schedule(struct vexpress_config_trans *trans)  	spin_lock_irqsave(&bridge->transactions_lock, flags); -	vexpress_config_dump_trans("Executing", trans); - -	if (list_empty(&bridge->transactions)) +	if (list_empty(&bridge->transactions)) { +		vexpress_config_dump_trans("Executing", trans);  		status = bridge->info->func_exec(trans->func->func,  				trans->offset, trans->write, trans->data); -	else +	} else { +		vexpress_config_dump_trans("Queuing", trans);  		status = VEXPRESS_CONFIG_STATUS_WAIT; +	}  	switch (status) {  	case VEXPRESS_CONFIG_STATUS_DONE: @@ -212,25 +213,31 @@ void vexpress_config_complete(struct vexpress_config_bridge *bridge,  {  	struct vexpress_config_trans *trans;  	unsigned long flags; +	const char *message = "Completed";  	spin_lock_irqsave(&bridge->transactions_lock, flags);  	trans = list_first_entry(&bridge->transactions,  			struct vexpress_config_trans, list); -	vexpress_config_dump_trans("Completed", trans); -  	trans->status = status; -	list_del(&trans->list); -	if (!list_empty(&bridge->transactions)) { -		vexpress_config_dump_trans("Pending", trans); +	do { +		vexpress_config_dump_trans(message, trans); +		list_del(&trans->list); +		complete(&trans->completion); -		bridge->info->func_exec(trans->func->func, trans->offset, -				trans->write, trans->data); -	} -	spin_unlock_irqrestore(&bridge->transactions_lock, flags); +		if (list_empty(&bridge->transactions)) +			break; + +		trans = list_first_entry(&bridge->transactions, +				struct vexpress_config_trans, list); +		vexpress_config_dump_trans("Executing pending", trans); +		trans->status = bridge->info->func_exec(trans->func->func, +				trans->offset, trans->write, trans->data); +		message = "Finished pending"; +	} while (trans->status == VEXPRESS_CONFIG_STATUS_DONE); -	complete(&trans->completion); +	spin_unlock_irqrestore(&bridge->transactions_lock, flags);  }  EXPORT_SYMBOL(vexpress_config_complete); diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index bf75e967a1f..96a020b1dcd 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -490,12 +490,12 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)  		return err;  	} +	vexpress_sysreg_dev = &pdev->dev; +  	platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",  			PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,  			sizeof(vexpress_sysreg_leds_pdata)); -	vexpress_sysreg_dev = &pdev->dev; -  	device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);  	return 0; diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index f70c4956ff9..155c4a1a6a9 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -10,6 +10,7 @@   * published by the Free Software Foundation.   */ +#include <linux/device.h>  #include <linux/module.h>  #include <linux/mfd/arizona/core.h> @@ -57,31 +58,54 @@ static const struct reg_default wm5102_reva_patch[] = {  };  static const struct reg_default wm5102_revb_patch[] = { +	{ 0x19, 0x0001 },  	{ 0x80, 0x0003 },  	{ 0x081, 0xE022 }, -	{ 0x410, 0x4080 }, -	{ 0x418, 0x4080 }, -	{ 0x420, 0x4080 }, -	{ 0x428, 0xC000 }, +	{ 0x410, 0x6080 }, +	{ 0x418, 0xa080 }, +	{ 0x420, 0xa080 }, +	{ 0x428, 0xe000 }, +	{ 0x443, 0xDC1A },  	{ 0x4B0, 0x0066 },  	{ 0x458, 0x000b },  	{ 0x212, 0x0000 }, +	{ 0x171, 0x0000 }, +	{ 0x35E, 0x000C }, +	{ 0x2D4, 0x0000 },  	{ 0x80, 0x0000 },  };  /* We use a function so we can use ARRAY_SIZE() */  int wm5102_patch(struct arizona *arizona)  { +	const struct reg_default *wm5102_patch; +	int ret = 0; +	int i, patch_size; +  	switch (arizona->rev) {  	case 0: -		return regmap_register_patch(arizona->regmap, -					     wm5102_reva_patch, -					     ARRAY_SIZE(wm5102_reva_patch)); +		wm5102_patch = wm5102_reva_patch; +		patch_size = ARRAY_SIZE(wm5102_reva_patch);  	default: -		return regmap_register_patch(arizona->regmap, -					     wm5102_revb_patch, -					     ARRAY_SIZE(wm5102_revb_patch)); +		wm5102_patch = wm5102_revb_patch; +		patch_size = ARRAY_SIZE(wm5102_revb_patch); +	} + +	regcache_cache_bypass(arizona->regmap, true); + +	for (i = 0; i < patch_size; i++) { +		ret = regmap_write(arizona->regmap, wm5102_patch[i].reg, +				   wm5102_patch[i].def); +		if (ret != 0) { +			dev_err(arizona->dev, "Failed to write %x = %x: %d\n", +				wm5102_patch[i].reg, wm5102_patch[i].def, ret); +			goto out; +		}  	} + +out: +	regcache_cache_bypass(arizona->regmap, false); +	return ret;  }  static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = { @@ -282,7 +306,7 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x00000155, 0x0000 },   /* R341   - Rate Estimator 4 */   	{ 0x00000156, 0x0000 },   /* R342   - Rate Estimator 5 */   	{ 0x00000161, 0x0000 },   /* R353   - Dynamic Frequency Scaling 1 */  -	{ 0x00000171, 0x0002 },   /* R369   - FLL1 Control 1 */ +	{ 0x00000171, 0x0000 },   /* R369   - FLL1 Control 1 */  	{ 0x00000172, 0x0008 },   /* R370   - FLL1 Control 2 */   	{ 0x00000173, 0x0018 },   /* R371   - FLL1 Control 3 */   	{ 0x00000174, 0x007D },   /* R372   - FLL1 Control 4 */  @@ -366,7 +390,7 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x00000400, 0x0000 },   /* R1024  - Output Enables 1 */   	{ 0x00000408, 0x0000 },   /* R1032  - Output Rate 1 */   	{ 0x00000409, 0x0022 },   /* R1033  - Output Volume Ramp */  -	{ 0x00000410, 0x4080 },   /* R1040  - Output Path Config 1L */ +	{ 0x00000410, 0x6080 },   /* R1040  - Output Path Config 1L */  	{ 0x00000411, 0x0180 },   /* R1041  - DAC Digital Volume 1L */   	{ 0x00000412, 0x0081 },   /* R1042  - DAC Volume Limit 1L */  	{ 0x00000413, 0x0001 },   /* R1043  - Noise Gate Select 1L */  @@ -374,7 +398,7 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x00000415, 0x0180 },   /* R1045  - DAC Digital Volume 1R */   	{ 0x00000416, 0x0081 },   /* R1046  - DAC Volume Limit 1R */  	{ 0x00000417, 0x0002 },   /* R1047  - Noise Gate Select 1R */  -	{ 0x00000418, 0x4080 },   /* R1048  - Output Path Config 2L */ +	{ 0x00000418, 0xA080 },   /* R1048  - Output Path Config 2L */  	{ 0x00000419, 0x0180 },   /* R1049  - DAC Digital Volume 2L */   	{ 0x0000041A, 0x0081 },   /* R1050  - DAC Volume Limit 2L */  	{ 0x0000041B, 0x0004 },   /* R1051  - Noise Gate Select 2L */  @@ -382,11 +406,11 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x0000041D, 0x0180 },   /* R1053  - DAC Digital Volume 2R */   	{ 0x0000041E, 0x0081 },   /* R1054  - DAC Volume Limit 2R */  	{ 0x0000041F, 0x0008 },   /* R1055  - Noise Gate Select 2R */  -	{ 0x00000420, 0x4080 },   /* R1056  - Output Path Config 3L */ +	{ 0x00000420, 0xA080 },   /* R1056  - Output Path Config 3L */  	{ 0x00000421, 0x0180 },   /* R1057  - DAC Digital Volume 3L */   	{ 0x00000422, 0x0081 },   /* R1058  - DAC Volume Limit 3L */  	{ 0x00000423, 0x0010 },   /* R1059  - Noise Gate Select 3L */  -	{ 0x00000428, 0xC000 },   /* R1064  - Output Path Config 4L */ +	{ 0x00000428, 0xE000 },   /* R1064  - Output Path Config 4L */  	{ 0x00000429, 0x0180 },   /* R1065  - DAC Digital Volume 4L */   	{ 0x0000042A, 0x0081 },   /* R1066  - Out Volume 4L */  	{ 0x0000042B, 0x0040 },   /* R1067  - Noise Gate Select 4L */  @@ -401,7 +425,7 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x00000436, 0x0081 },   /* R1078  - DAC Volume Limit 5R */  	{ 0x00000437, 0x0200 },   /* R1079  - Noise Gate Select 5R */  	{ 0x00000450, 0x0000 },   /* R1104  - DAC AEC Control 1 */  -	{ 0x00000458, 0x0001 },   /* R1112  - Noise Gate Control */  +	{ 0x00000458, 0x000B },   /* R1112  - Noise Gate Control */  	{ 0x00000490, 0x0069 },   /* R1168  - PDM SPK1 CTRL 1 */   	{ 0x00000491, 0x0000 },   /* R1169  - PDM SPK1 CTRL 2 */   	{ 0x00000500, 0x000C },   /* R1280  - AIF1 BCLK Ctrl */  diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 4e70e157a90..e7ed14f661d 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -37,7 +37,7 @@ static int wm831x_spi_probe(struct spi_device *spi)  	spi->bits_per_word = 16;  	spi->mode = SPI_MODE_0; -	dev_set_drvdata(&spi->dev, wm831x); +	spi_set_drvdata(spi, wm831x);  	wm831x->dev = &spi->dev;  	wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config); @@ -53,7 +53,7 @@ static int wm831x_spi_probe(struct spi_device *spi)  static int wm831x_spi_remove(struct spi_device *spi)  { -	struct wm831x *wm831x = dev_get_drvdata(&spi->dev); +	struct wm831x *wm831x = spi_get_drvdata(spi);  	wm831x_device_exit(wm831x); @@ -69,7 +69,7 @@ static int wm831x_spi_suspend(struct device *dev)  static void wm831x_spi_shutdown(struct spi_device *spi)  { -	struct wm831x *wm831x = dev_get_drvdata(&spi->dev); +	struct wm831x *wm831x = spi_get_drvdata(spi);  	wm831x_device_shutdown(wm831x);  } diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 803e93fae56..00e4fe2f3c7 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -19,6 +19,9 @@  #include <linux/err.h>  #include <linux/delay.h>  #include <linux/mfd/core.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h>  #include <linux/pm_runtime.h>  #include <linux/regmap.h>  #include <linux/regulator/consumer.h> @@ -191,7 +194,7 @@ static const char *wm8958_main_supplies[] = {  	"SPKVDD2",  }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_RUNTIME  static int wm8994_suspend(struct device *dev)  {  	struct wm8994 *wm8994 = dev_get_drvdata(dev); @@ -396,6 +399,60 @@ static const struct reg_default wm1811_reva_patch[] = {  	{ 0x102, 0x0 },  }; +#ifdef CONFIG_OF +static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) +{ +	struct device_node *np = wm8994->dev->of_node; +	struct wm8994_pdata *pdata = &wm8994->pdata; +	int i; + +	if (!np) +		return 0; + +	if (of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_defaults, +				       ARRAY_SIZE(pdata->gpio_defaults)) >= 0) { +		for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) { +			if (wm8994->pdata.gpio_defaults[i] == 0) +				pdata->gpio_defaults[i] +					= WM8994_CONFIGURE_GPIO; +		} +	} + +	of_property_read_u32_array(np, "wlf,micbias-cfg", pdata->micbias, +				   ARRAY_SIZE(pdata->micbias)); + +	pdata->lineout1_diff = true; +	pdata->lineout2_diff = true; +	if (of_find_property(np, "wlf,lineout1-se", NULL)) +		pdata->lineout1_diff = false; +	if (of_find_property(np, "wlf,lineout2-se", NULL)) +		pdata->lineout2_diff = false; + +	if (of_find_property(np, "wlf,lineout1-feedback", NULL)) +		pdata->lineout1fb = true; +	if (of_find_property(np, "wlf,lineout2-feedback", NULL)) +		pdata->lineout2fb = true; + +	if (of_find_property(np, "wlf,ldoena-always-driven", NULL)) +		pdata->lineout2fb = true; + +	pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0); +	if (pdata->ldo[0].enable < 0) +		pdata->ldo[0].enable = 0; + +	pdata->ldo[1].enable = of_get_named_gpio(np, "wlf,ldo2ena", 0); +	if (pdata->ldo[1].enable < 0) +		pdata->ldo[1].enable = 0; + +	return 0; +} +#else +static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) +{ +	return 0; +} +#endif +  /*   * Instantiate the generic non-control parts of the device.   */ @@ -405,7 +462,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)  	struct regmap_config *regmap_config;  	const struct reg_default *regmap_patch = NULL;  	const char *devname; -	int ret, i, patch_regs; +	int ret, i, patch_regs = 0;  	int pulls = 0;  	if (dev_get_platdata(wm8994->dev)) { @@ -414,6 +471,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)  	}  	pdata = &wm8994->pdata; +	ret = wm8994_set_pdata_from_of(wm8994); +	if (ret != 0) +		return ret; +  	dev_set_drvdata(wm8994->dev, wm8994);  	/* Add the on-chip regulators first for bootstrapping */ @@ -673,9 +734,9 @@ static void wm8994_device_exit(struct wm8994 *wm8994)  }  static const struct of_device_id wm8994_of_match[] = { -	{ .compatible = "wlf,wm1811", }, -	{ .compatible = "wlf,wm8994", }, -	{ .compatible = "wlf,wm8958", }, +	{ .compatible = "wlf,wm1811", .data = (void *)WM1811 }, +	{ .compatible = "wlf,wm8994", .data = (void *)WM8994 }, +	{ .compatible = "wlf,wm8958", .data = (void *)WM8958 },  	{ }  };  MODULE_DEVICE_TABLE(of, wm8994_of_match); @@ -683,6 +744,7 @@ MODULE_DEVICE_TABLE(of, wm8994_of_match);  static int wm8994_i2c_probe(struct i2c_client *i2c,  				      const struct i2c_device_id *id)  { +	const struct of_device_id *of_id;  	struct wm8994 *wm8994;  	int ret; @@ -693,7 +755,14 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,  	i2c_set_clientdata(i2c, wm8994);  	wm8994->dev = &i2c->dev;  	wm8994->irq = i2c->irq; -	wm8994->type = id->driver_data; + +	if (i2c->dev.of_node) { +		of_id = of_match_device(wm8994_of_match, &i2c->dev); +		if (of_id) +			wm8994->type = (int)of_id->data; +	} else { +		wm8994->type = id->driver_data; +	}  	wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config);  	if (IS_ERR(wm8994->regmap)) { @@ -724,15 +793,16 @@ static const struct i2c_device_id wm8994_i2c_id[] = {  };  MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id); -static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume, -			    NULL); +static const struct dev_pm_ops wm8994_pm_ops = { +	SET_RUNTIME_PM_OPS(wm8994_suspend, wm8994_resume, NULL) +};  static struct i2c_driver wm8994_i2c_driver = {  	.driver = {  		.name = "wm8994",  		.owner = THIS_MODULE,  		.pm = &wm8994_pm_ops, -		.of_match_table = wm8994_of_match, +		.of_match_table = of_match_ptr(wm8994_of_match),  	},  	.probe = wm8994_i2c_probe,  	.remove = wm8994_i2c_remove, diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index 1a1dcb831a1..cbde1d6d322 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c @@ -42,6 +42,7 @@ static int rx51_battery_read_adc(int channel)  	req.method = TWL4030_MADC_SW1;  	req.func_cb = NULL;  	req.type = TWL4030_MADC_WAIT; +	req.raw = true;  	if (twl4030_madc_conversion(&req) <= 0)  		return -ENODATA; diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h index 530e11ba073..01f59510704 100644 --- a/include/linux/i2c/twl4030-madc.h +++ b/include/linux/i2c/twl4030-madc.h @@ -39,6 +39,7 @@ struct twl4030_madc_conversion_method {   * @do_avgP:	sample the input channel for 4 consecutive cycles   * @method:	RT, SW1, SW2   * @type:	Polling or interrupt based method + * @raw:	Return raw value, do not convert it   */  struct twl4030_madc_request { @@ -48,6 +49,7 @@ struct twl4030_madc_request {  	u16 type;  	bool active;  	bool result_pending; +	bool raw;  	int rbuf[TWL4030_MADC_MAX_CHANNELS];  	void (*func_cb)(int len, int channels, int *buf);  }; diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index 5f3aa6b11bf..27e06acc509 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -81,4 +81,23 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,  			       unsigned short *keymap,  			       struct input_dev *input_dev); +#ifdef CONFIG_OF +/** + * matrix_keypad_parse_of_params() - Read parameters from matrix-keypad node + * + * @dev: Device containing of_node + * @rows: Returns number of matrix rows + * @cols: Returns number of matrix columns + * @return 0 if OK, <0 on error + */ +int matrix_keypad_parse_of_params(struct device *dev, +				  unsigned int *rows, unsigned int *cols); +#else +static inline int matrix_keypad_parse_of_params(struct device *dev, +				  unsigned int *rows, unsigned int *cols) +{ +	return -ENOSYS; +} +#endif /* CONFIG_OF */ +  #endif /* _MATRIX_KEYPAD_H */ diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index a0f940987a3..80dead1f710 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -78,6 +78,7 @@ struct arizona_micbias {  	unsigned int ext_cap:1;    /** External capacitor fitted */  	unsigned int discharge:1;  /** Actively discharge */  	unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ +	unsigned int bypass:1;     /** Use bypass mode */  };  struct arizona_micd_config { @@ -104,7 +105,8 @@ struct arizona_pdata {  	/** If a direct 32kHz clock is provided on an MCLK specify it here */  	int clk32k_src; -	bool irq_active_high; /** IRQ polarity */ +	/** Mode for primary IRQ (defaults to active low) */ +	unsigned int irq_flags;  	/* Base GPIO */  	int gpio_base; @@ -183,6 +185,9 @@ struct arizona_pdata {  	/** Haptic actuator type */  	unsigned int hap_act; + +	/** GPIO for primary IRQ (used for edge triggered emulation) */ +	int irq_gpio;  };  #endif diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h new file mode 100644 index 00000000000..032af7fc5b2 --- /dev/null +++ b/include/linux/mfd/cros_ec.h @@ -0,0 +1,170 @@ +/* + * ChromeOS EC multi-function device + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef __LINUX_MFD_CROS_EC_H +#define __LINUX_MFD_CROS_EC_H + +#include <linux/mfd/cros_ec_commands.h> + +/* + * Command interface between EC and AP, for LPC, I2C and SPI interfaces. + */ +enum { +	EC_MSG_TX_HEADER_BYTES	= 3, +	EC_MSG_TX_TRAILER_BYTES	= 1, +	EC_MSG_TX_PROTO_BYTES	= EC_MSG_TX_HEADER_BYTES + +					EC_MSG_TX_TRAILER_BYTES, +	EC_MSG_RX_PROTO_BYTES	= 3, + +	/* Max length of messages */ +	EC_MSG_BYTES		= EC_HOST_PARAM_SIZE + EC_MSG_TX_PROTO_BYTES, + +}; + +/** + * struct cros_ec_msg - A message sent to the EC, and its reply + * + * @version: Command version number (often 0) + * @cmd: Command to send (EC_CMD_...) + * @out_buf: Outgoing payload (to EC) + * @outlen: Outgoing length + * @in_buf: Incoming payload (from EC) + * @in_len: Incoming length + */ +struct cros_ec_msg { +	u8 version; +	u8 cmd; +	uint8_t *out_buf; +	int out_len; +	uint8_t *in_buf; +	int in_len; +}; + +/** + * struct cros_ec_device - Information about a ChromeOS EC device + * + * @name: Name of this EC interface + * @priv: Private data + * @irq: Interrupt to use + * @din: input buffer (from EC) + * @dout: output buffer (to EC) + * \note + * These two buffers will always be dword-aligned and include enough + * space for up to 7 word-alignment bytes also, so we can ensure that + * the body of the message is always dword-aligned (64-bit). + * + * We use this alignment to keep ARM and x86 happy. Probably word + * alignment would be OK, there might be a small performance advantage + * to using dword. + * @din_size: size of din buffer + * @dout_size: size of dout buffer + * @command_send: send a command + * @command_recv: receive a command + * @ec_name: name of EC device (e.g. 'chromeos-ec') + * @phys_name: name of physical comms layer (e.g. 'i2c-4') + * @parent: pointer to parent device (e.g. i2c or spi device) + * @dev: Device pointer + * dev_lock: Lock to prevent concurrent access + * @wake_enabled: true if this device can wake the system from sleep + * @was_wake_device: true if this device was set to wake the system from + * sleep at the last suspend + * @event_notifier: interrupt event notifier for transport devices + */ +struct cros_ec_device { +	const char *name; +	void *priv; +	int irq; +	uint8_t *din; +	uint8_t *dout; +	int din_size; +	int dout_size; +	int (*command_send)(struct cros_ec_device *ec, +			uint16_t cmd, void *out_buf, int out_len); +	int (*command_recv)(struct cros_ec_device *ec, +			uint16_t cmd, void *in_buf, int in_len); +	int (*command_sendrecv)(struct cros_ec_device *ec, +			uint16_t cmd, void *out_buf, int out_len, +			void *in_buf, int in_len); +	int (*command_xfer)(struct cros_ec_device *ec, +			struct cros_ec_msg *msg); + +	const char *ec_name; +	const char *phys_name; +	struct device *parent; + +	/* These are --private-- fields - do not assign */ +	struct device *dev; +	struct mutex dev_lock; +	bool wake_enabled; +	bool was_wake_device; +	struct blocking_notifier_head event_notifier; +}; + +/** + * cros_ec_suspend - Handle a suspend operation for the ChromeOS EC device + * + * This can be called by drivers to handle a suspend event. + * + * ec_dev: Device to suspend + * @return 0 if ok, -ve on error + */ +int cros_ec_suspend(struct cros_ec_device *ec_dev); + +/** + * cros_ec_resume - Handle a resume operation for the ChromeOS EC device + * + * This can be called by drivers to handle a resume event. + * + * @ec_dev: Device to resume + * @return 0 if ok, -ve on error + */ +int cros_ec_resume(struct cros_ec_device *ec_dev); + +/** + * cros_ec_prepare_tx - Prepare an outgoing message in the output buffer + * + * This is intended to be used by all ChromeOS EC drivers, but at present + * only SPI uses it. Once LPC uses the same protocol it can start using it. + * I2C could use it now, with a refactor of the existing code. + * + * @ec_dev: Device to register + * @msg: Message to write + */ +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, +		       struct cros_ec_msg *msg); + +/** + * cros_ec_remove - Remove a ChromeOS EC + * + * Call this to deregister a ChromeOS EC. After this you should call + * cros_ec_free(). + * + * @ec_dev: Device to register + * @return 0 if ok, -ve on error + */ +int cros_ec_remove(struct cros_ec_device *ec_dev); + +/** + * cros_ec_register - Register a new ChromeOS EC, using the provided info + * + * Before calling this, allocate a pointer to a new device and then fill + * in all the fields up to the --private-- marker. + * + * @ec_dev: Device to register + * @return 0 if ok, -ve on error + */ +int cros_ec_register(struct cros_ec_device *ec_dev); + +#endif /* __LINUX_MFD_CROS_EC_H */ diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h new file mode 100644 index 00000000000..86fd06953bc --- /dev/null +++ b/include/linux/mfd/cros_ec_commands.h @@ -0,0 +1,1369 @@ +/* + * Host communication command constants for ChromeOS EC + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * The ChromeOS EC multi function device is used to mux all the requests + * to the EC device for its multiple features: keyboard controller, + * battery charging and regulator control, firmware update. + * + * NOTE: This file is copied verbatim from the ChromeOS EC Open Source + * project in an attempt to make future updates easy to make. + */ + +#ifndef __CROS_EC_COMMANDS_H +#define __CROS_EC_COMMANDS_H + +/* + * Protocol overview + * + * request:  CMD [ P0 P1 P2 ... Pn S ] + * response: ERR [ P0 P1 P2 ... Pn S ] + * + * where the bytes are defined as follow : + *      - CMD is the command code. (defined by EC_CMD_ constants) + *      - ERR is the error code. (defined by EC_RES_ constants) + *      - Px is the optional payload. + *        it is not sent if the error code is not success. + *        (defined by ec_params_ and ec_response_ structures) + *      - S is the checksum which is the sum of all payload bytes. + * + * On LPC, CMD and ERR are sent/received at EC_LPC_ADDR_KERNEL|USER_CMD + * and the payloads are sent/received at EC_LPC_ADDR_KERNEL|USER_PARAM. + * On I2C, all bytes are sent serially in the same message. + */ + +/* Current version of this protocol */ +#define EC_PROTO_VERSION          0x00000002 + +/* Command version mask */ +#define EC_VER_MASK(version) (1UL << (version)) + +/* I/O addresses for ACPI commands */ +#define EC_LPC_ADDR_ACPI_DATA  0x62 +#define EC_LPC_ADDR_ACPI_CMD   0x66 + +/* I/O addresses for host command */ +#define EC_LPC_ADDR_HOST_DATA  0x200 +#define EC_LPC_ADDR_HOST_CMD   0x204 + +/* I/O addresses for host command args and params */ +#define EC_LPC_ADDR_HOST_ARGS  0x800 +#define EC_LPC_ADDR_HOST_PARAM 0x804 +#define EC_HOST_PARAM_SIZE     0x0fc  /* Size of param area in bytes */ + +/* I/O addresses for host command params, old interface */ +#define EC_LPC_ADDR_OLD_PARAM  0x880 +#define EC_OLD_PARAM_SIZE      0x080  /* Size of param area in bytes */ + +/* EC command register bit functions */ +#define EC_LPC_CMDR_DATA	(1 << 0)  /* Data ready for host to read */ +#define EC_LPC_CMDR_PENDING	(1 << 1)  /* Write pending to EC */ +#define EC_LPC_CMDR_BUSY	(1 << 2)  /* EC is busy processing a command */ +#define EC_LPC_CMDR_CMD		(1 << 3)  /* Last host write was a command */ +#define EC_LPC_CMDR_ACPI_BRST	(1 << 4)  /* Burst mode (not used) */ +#define EC_LPC_CMDR_SCI		(1 << 5)  /* SCI event is pending */ +#define EC_LPC_CMDR_SMI		(1 << 6)  /* SMI event is pending */ + +#define EC_LPC_ADDR_MEMMAP       0x900 +#define EC_MEMMAP_SIZE         255 /* ACPI IO buffer max is 255 bytes */ +#define EC_MEMMAP_TEXT_MAX     8   /* Size of a string in the memory map */ + +/* The offset address of each type of data in mapped memory. */ +#define EC_MEMMAP_TEMP_SENSOR      0x00 /* Temp sensors */ +#define EC_MEMMAP_FAN              0x10 /* Fan speeds */ +#define EC_MEMMAP_TEMP_SENSOR_B    0x18 /* Temp sensors (second set) */ +#define EC_MEMMAP_ID               0x20 /* 'E' 'C' */ +#define EC_MEMMAP_ID_VERSION       0x22 /* Version of data in 0x20 - 0x2f */ +#define EC_MEMMAP_THERMAL_VERSION  0x23 /* Version of data in 0x00 - 0x1f */ +#define EC_MEMMAP_BATTERY_VERSION  0x24 /* Version of data in 0x40 - 0x7f */ +#define EC_MEMMAP_SWITCHES_VERSION 0x25 /* Version of data in 0x30 - 0x33 */ +#define EC_MEMMAP_EVENTS_VERSION   0x26 /* Version of data in 0x34 - 0x3f */ +#define EC_MEMMAP_HOST_CMD_FLAGS   0x27 /* Host command interface flags */ +#define EC_MEMMAP_SWITCHES         0x30 +#define EC_MEMMAP_HOST_EVENTS      0x34 +#define EC_MEMMAP_BATT_VOLT        0x40 /* Battery Present Voltage */ +#define EC_MEMMAP_BATT_RATE        0x44 /* Battery Present Rate */ +#define EC_MEMMAP_BATT_CAP         0x48 /* Battery Remaining Capacity */ +#define EC_MEMMAP_BATT_FLAG        0x4c /* Battery State, defined below */ +#define EC_MEMMAP_BATT_DCAP        0x50 /* Battery Design Capacity */ +#define EC_MEMMAP_BATT_DVLT        0x54 /* Battery Design Voltage */ +#define EC_MEMMAP_BATT_LFCC        0x58 /* Battery Last Full Charge Capacity */ +#define EC_MEMMAP_BATT_CCNT        0x5c /* Battery Cycle Count */ +#define EC_MEMMAP_BATT_MFGR        0x60 /* Battery Manufacturer String */ +#define EC_MEMMAP_BATT_MODEL       0x68 /* Battery Model Number String */ +#define EC_MEMMAP_BATT_SERIAL      0x70 /* Battery Serial Number String */ +#define EC_MEMMAP_BATT_TYPE        0x78 /* Battery Type String */ + +/* Number of temp sensors at EC_MEMMAP_TEMP_SENSOR */ +#define EC_TEMP_SENSOR_ENTRIES     16 +/* + * Number of temp sensors at EC_MEMMAP_TEMP_SENSOR_B. + * + * Valid only if EC_MEMMAP_THERMAL_VERSION returns >= 2. + */ +#define EC_TEMP_SENSOR_B_ENTRIES      8 +#define EC_TEMP_SENSOR_NOT_PRESENT    0xff +#define EC_TEMP_SENSOR_ERROR          0xfe +#define EC_TEMP_SENSOR_NOT_POWERED    0xfd +#define EC_TEMP_SENSOR_NOT_CALIBRATED 0xfc +/* + * The offset of temperature value stored in mapped memory.  This allows + * reporting a temperature range of 200K to 454K = -73C to 181C. + */ +#define EC_TEMP_SENSOR_OFFSET      200 + +#define EC_FAN_SPEED_ENTRIES       4       /* Number of fans at EC_MEMMAP_FAN */ +#define EC_FAN_SPEED_NOT_PRESENT   0xffff  /* Entry not present */ +#define EC_FAN_SPEED_STALLED       0xfffe  /* Fan stalled */ + +/* Battery bit flags at EC_MEMMAP_BATT_FLAG. */ +#define EC_BATT_FLAG_AC_PRESENT   0x01 +#define EC_BATT_FLAG_BATT_PRESENT 0x02 +#define EC_BATT_FLAG_DISCHARGING  0x04 +#define EC_BATT_FLAG_CHARGING     0x08 +#define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 + +/* Switch flags at EC_MEMMAP_SWITCHES */ +#define EC_SWITCH_LID_OPEN               0x01 +#define EC_SWITCH_POWER_BUTTON_PRESSED   0x02 +#define EC_SWITCH_WRITE_PROTECT_DISABLED 0x04 +/* Recovery requested via keyboard */ +#define EC_SWITCH_KEYBOARD_RECOVERY      0x08 +/* Recovery requested via dedicated signal (from servo board) */ +#define EC_SWITCH_DEDICATED_RECOVERY     0x10 +/* Was fake developer mode switch; now unused.  Remove in next refactor. */ +#define EC_SWITCH_IGNORE0                0x20 + +/* Host command interface flags */ +/* Host command interface supports LPC args (LPC interface only) */ +#define EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED  0x01 + +/* Wireless switch flags */ +#define EC_WIRELESS_SWITCH_WLAN      0x01 +#define EC_WIRELESS_SWITCH_BLUETOOTH 0x02 + +/* + * This header file is used in coreboot both in C and ACPI code.  The ACPI code + * is pre-processed to handle constants but the ASL compiler is unable to + * handle actual C code so keep it separate. + */ +#ifndef __ACPI__ + +/* LPC command status byte masks */ +/* EC has written a byte in the data register and host hasn't read it yet */ +#define EC_LPC_STATUS_TO_HOST     0x01 +/* Host has written a command/data byte and the EC hasn't read it yet */ +#define EC_LPC_STATUS_FROM_HOST   0x02 +/* EC is processing a command */ +#define EC_LPC_STATUS_PROCESSING  0x04 +/* Last write to EC was a command, not data */ +#define EC_LPC_STATUS_LAST_CMD    0x08 +/* EC is in burst mode.  Unsupported by Chrome EC, so this bit is never set */ +#define EC_LPC_STATUS_BURST_MODE  0x10 +/* SCI event is pending (requesting SCI query) */ +#define EC_LPC_STATUS_SCI_PENDING 0x20 +/* SMI event is pending (requesting SMI query) */ +#define EC_LPC_STATUS_SMI_PENDING 0x40 +/* (reserved) */ +#define EC_LPC_STATUS_RESERVED    0x80 + +/* + * EC is busy.  This covers both the EC processing a command, and the host has + * written a new command but the EC hasn't picked it up yet. + */ +#define EC_LPC_STATUS_BUSY_MASK \ +	(EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) + +/* Host command response codes */ +enum ec_status { +	EC_RES_SUCCESS = 0, +	EC_RES_INVALID_COMMAND = 1, +	EC_RES_ERROR = 2, +	EC_RES_INVALID_PARAM = 3, +	EC_RES_ACCESS_DENIED = 4, +	EC_RES_INVALID_RESPONSE = 5, +	EC_RES_INVALID_VERSION = 6, +	EC_RES_INVALID_CHECKSUM = 7, +	EC_RES_IN_PROGRESS = 8,		/* Accepted, command in progress */ +	EC_RES_UNAVAILABLE = 9,		/* No response available */ +	EC_RES_TIMEOUT = 10,		/* We got a timeout */ +	EC_RES_OVERFLOW = 11,		/* Table / data overflow */ +}; + +/* + * Host event codes.  Note these are 1-based, not 0-based, because ACPI query + * EC command uses code 0 to mean "no event pending".  We explicitly specify + * each value in the enum listing so they won't change if we delete/insert an + * item or rearrange the list (it needs to be stable across platforms, not + * just within a single compiled instance). + */ +enum host_event_code { +	EC_HOST_EVENT_LID_CLOSED = 1, +	EC_HOST_EVENT_LID_OPEN = 2, +	EC_HOST_EVENT_POWER_BUTTON = 3, +	EC_HOST_EVENT_AC_CONNECTED = 4, +	EC_HOST_EVENT_AC_DISCONNECTED = 5, +	EC_HOST_EVENT_BATTERY_LOW = 6, +	EC_HOST_EVENT_BATTERY_CRITICAL = 7, +	EC_HOST_EVENT_BATTERY = 8, +	EC_HOST_EVENT_THERMAL_THRESHOLD = 9, +	EC_HOST_EVENT_THERMAL_OVERLOAD = 10, +	EC_HOST_EVENT_THERMAL = 11, +	EC_HOST_EVENT_USB_CHARGER = 12, +	EC_HOST_EVENT_KEY_PRESSED = 13, +	/* +	 * EC has finished initializing the host interface.  The host can check +	 * for this event following sending a EC_CMD_REBOOT_EC command to +	 * determine when the EC is ready to accept subsequent commands. +	 */ +	EC_HOST_EVENT_INTERFACE_READY = 14, +	/* Keyboard recovery combo has been pressed */ +	EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, + +	/* Shutdown due to thermal overload */ +	EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, +	/* Shutdown due to battery level too low */ +	EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, + +	/* +	 * The high bit of the event mask is not used as a host event code.  If +	 * it reads back as set, then the entire event mask should be +	 * considered invalid by the host.  This can happen when reading the +	 * raw event status via EC_MEMMAP_HOST_EVENTS but the LPC interface is +	 * not initialized on the EC, or improperly configured on the host. +	 */ +	EC_HOST_EVENT_INVALID = 32 +}; +/* Host event mask */ +#define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) + +/* Arguments at EC_LPC_ADDR_HOST_ARGS */ +struct ec_lpc_host_args { +	uint8_t flags; +	uint8_t command_version; +	uint8_t data_size; +	/* +	 * Checksum; sum of command + flags + command_version + data_size + +	 * all params/response data bytes. +	 */ +	uint8_t checksum; +} __packed; + +/* Flags for ec_lpc_host_args.flags */ +/* + * Args are from host.  Data area at EC_LPC_ADDR_HOST_PARAM contains command + * params. + * + * If EC gets a command and this flag is not set, this is an old-style command. + * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with + * unknown length.  EC must respond with an old-style response (that is, + * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). + */ +#define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 +/* + * Args are from EC.  Data area at EC_LPC_ADDR_HOST_PARAM contains response. + * + * If EC responds to a command and this flag is not set, this is an old-style + * response.  Command version is 0 and response data from EC is at + * EC_LPC_ADDR_OLD_PARAM with unknown length. + */ +#define EC_HOST_ARGS_FLAG_TO_HOST   0x02 + +/* + * Notes on commands: + * + * Each command is an 8-byte command value.  Commands which take params or + * return response data specify structs for that data.  If no struct is + * specified, the command does not input or output data, respectively. + * Parameter/response length is implicit in the structs.  Some underlying + * communication protocols (I2C, SPI) may add length or checksum headers, but + * those are implementation-dependent and not defined here. + */ + +/*****************************************************************************/ +/* General / test commands */ + +/* + * Get protocol version, used to deal with non-backward compatible protocol + * changes. + */ +#define EC_CMD_PROTO_VERSION 0x00 + +struct ec_response_proto_version { +	uint32_t version; +} __packed; + +/* + * Hello.  This is a simple command to test the EC is responsive to + * commands. + */ +#define EC_CMD_HELLO 0x01 + +struct ec_params_hello { +	uint32_t in_data;  /* Pass anything here */ +} __packed; + +struct ec_response_hello { +	uint32_t out_data;  /* Output will be in_data + 0x01020304 */ +} __packed; + +/* Get version number */ +#define EC_CMD_GET_VERSION 0x02 + +enum ec_current_image { +	EC_IMAGE_UNKNOWN = 0, +	EC_IMAGE_RO, +	EC_IMAGE_RW +}; + +struct ec_response_get_version { +	/* Null-terminated version strings for RO, RW */ +	char version_string_ro[32]; +	char version_string_rw[32]; +	char reserved[32];       /* Was previously RW-B string */ +	uint32_t current_image;  /* One of ec_current_image */ +} __packed; + +/* Read test */ +#define EC_CMD_READ_TEST 0x03 + +struct ec_params_read_test { +	uint32_t offset;   /* Starting value for read buffer */ +	uint32_t size;     /* Size to read in bytes */ +} __packed; + +struct ec_response_read_test { +	uint32_t data[32]; +} __packed; + +/* + * Get build information + * + * Response is null-terminated string. + */ +#define EC_CMD_GET_BUILD_INFO 0x04 + +/* Get chip info */ +#define EC_CMD_GET_CHIP_INFO 0x05 + +struct ec_response_get_chip_info { +	/* Null-terminated strings */ +	char vendor[32]; +	char name[32]; +	char revision[32];  /* Mask version */ +} __packed; + +/* Get board HW version */ +#define EC_CMD_GET_BOARD_VERSION 0x06 + +struct ec_response_board_version { +	uint16_t board_version;  /* A monotonously incrementing number. */ +} __packed; + +/* + * Read memory-mapped data. + * + * This is an alternate interface to memory-mapped data for bus protocols + * which don't support direct-mapped memory - I2C, SPI, etc. + * + * Response is params.size bytes of data. + */ +#define EC_CMD_READ_MEMMAP 0x07 + +struct ec_params_read_memmap { +	uint8_t offset;   /* Offset in memmap (EC_MEMMAP_*) */ +	uint8_t size;     /* Size to read in bytes */ +} __packed; + +/* Read versions supported for a command */ +#define EC_CMD_GET_CMD_VERSIONS 0x08 + +struct ec_params_get_cmd_versions { +	uint8_t cmd;      /* Command to check */ +} __packed; + +struct ec_response_get_cmd_versions { +	/* +	 * Mask of supported versions; use EC_VER_MASK() to compare with a +	 * desired version. +	 */ +	uint32_t version_mask; +} __packed; + +/* + * Check EC communcations status (busy). This is needed on i2c/spi but not + * on lpc since it has its own out-of-band busy indicator. + * + * lpc must read the status from the command register. Attempting this on + * lpc will overwrite the args/parameter space and corrupt its data. + */ +#define EC_CMD_GET_COMMS_STATUS		0x09 + +/* Avoid using ec_status which is for return values */ +enum ec_comms_status { +	EC_COMMS_STATUS_PROCESSING	= 1 << 0,	/* Processing cmd */ +}; + +struct ec_response_get_comms_status { +	uint32_t flags;		/* Mask of enum ec_comms_status */ +} __packed; + + +/*****************************************************************************/ +/* Flash commands */ + +/* Get flash info */ +#define EC_CMD_FLASH_INFO 0x10 + +struct ec_response_flash_info { +	/* Usable flash size, in bytes */ +	uint32_t flash_size; +	/* +	 * Write block size.  Write offset and size must be a multiple +	 * of this. +	 */ +	uint32_t write_block_size; +	/* +	 * Erase block size.  Erase offset and size must be a multiple +	 * of this. +	 */ +	uint32_t erase_block_size; +	/* +	 * Protection block size.  Protection offset and size must be a +	 * multiple of this. +	 */ +	uint32_t protect_block_size; +} __packed; + +/* + * Read flash + * + * Response is params.size bytes of data. + */ +#define EC_CMD_FLASH_READ 0x11 + +struct ec_params_flash_read { +	uint32_t offset;   /* Byte offset to read */ +	uint32_t size;     /* Size to read in bytes */ +} __packed; + +/* Write flash */ +#define EC_CMD_FLASH_WRITE 0x12 + +struct ec_params_flash_write { +	uint32_t offset;   /* Byte offset to write */ +	uint32_t size;     /* Size to write in bytes */ +	/* +	 * Data to write.  Could really use EC_PARAM_SIZE - 8, but tidiest to +	 * use a power of 2 so writes stay aligned. +	 */ +	uint8_t data[64]; +} __packed; + +/* Erase flash */ +#define EC_CMD_FLASH_ERASE 0x13 + +struct ec_params_flash_erase { +	uint32_t offset;   /* Byte offset to erase */ +	uint32_t size;     /* Size to erase in bytes */ +} __packed; + +/* + * Get/set flash protection. + * + * If mask!=0, sets/clear the requested bits of flags.  Depending on the + * firmware write protect GPIO, not all flags will take effect immediately; + * some flags require a subsequent hard reset to take effect.  Check the + * returned flags bits to see what actually happened. + * + * If mask=0, simply returns the current flags state. + */ +#define EC_CMD_FLASH_PROTECT 0x15 +#define EC_VER_FLASH_PROTECT 1  /* Command version 1 */ + +/* Flags for flash protection */ +/* RO flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_RO_AT_BOOT         (1 << 0) +/* + * RO flash code protected now.  If this bit is set, at-boot status cannot + * be changed. + */ +#define EC_FLASH_PROTECT_RO_NOW             (1 << 1) +/* Entire flash code protected now, until reboot. */ +#define EC_FLASH_PROTECT_ALL_NOW            (1 << 2) +/* Flash write protect GPIO is asserted now */ +#define EC_FLASH_PROTECT_GPIO_ASSERTED      (1 << 3) +/* Error - at least one bank of flash is stuck locked, and cannot be unlocked */ +#define EC_FLASH_PROTECT_ERROR_STUCK        (1 << 4) +/* + * Error - flash protection is in inconsistent state.  At least one bank of + * flash which should be protected is not protected.  Usually fixed by + * re-requesting the desired flags, or by a hard reset if that fails. + */ +#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5) +/* Entile flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_ALL_AT_BOOT        (1 << 6) + +struct ec_params_flash_protect { +	uint32_t mask;   /* Bits in flags to apply */ +	uint32_t flags;  /* New flags to apply */ +} __packed; + +struct ec_response_flash_protect { +	/* Current value of flash protect flags */ +	uint32_t flags; +	/* +	 * Flags which are valid on this platform.  This allows the caller +	 * to distinguish between flags which aren't set vs. flags which can't +	 * be set on this platform. +	 */ +	uint32_t valid_flags; +	/* Flags which can be changed given the current protection state */ +	uint32_t writable_flags; +} __packed; + +/* + * Note: commands 0x14 - 0x19 version 0 were old commands to get/set flash + * write protect.  These commands may be reused with version > 0. + */ + +/* Get the region offset/size */ +#define EC_CMD_FLASH_REGION_INFO 0x16 +#define EC_VER_FLASH_REGION_INFO 1 + +enum ec_flash_region { +	/* Region which holds read-only EC image */ +	EC_FLASH_REGION_RO, +	/* Region which holds rewritable EC image */ +	EC_FLASH_REGION_RW, +	/* +	 * Region which should be write-protected in the factory (a superset of +	 * EC_FLASH_REGION_RO) +	 */ +	EC_FLASH_REGION_WP_RO, +}; + +struct ec_params_flash_region_info { +	uint32_t region;  /* enum ec_flash_region */ +} __packed; + +struct ec_response_flash_region_info { +	uint32_t offset; +	uint32_t size; +} __packed; + +/* Read/write VbNvContext */ +#define EC_CMD_VBNV_CONTEXT 0x17 +#define EC_VER_VBNV_CONTEXT 1 +#define EC_VBNV_BLOCK_SIZE 16 + +enum ec_vbnvcontext_op { +	EC_VBNV_CONTEXT_OP_READ, +	EC_VBNV_CONTEXT_OP_WRITE, +}; + +struct ec_params_vbnvcontext { +	uint32_t op; +	uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +struct ec_response_vbnvcontext { +	uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +/*****************************************************************************/ +/* PWM commands */ + +/* Get fan target RPM */ +#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 + +struct ec_response_pwm_get_fan_rpm { +	uint32_t rpm; +} __packed; + +/* Set target fan RPM */ +#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 + +struct ec_params_pwm_set_fan_target_rpm { +	uint32_t rpm; +} __packed; + +/* Get keyboard backlight */ +#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 + +struct ec_response_pwm_get_keyboard_backlight { +	uint8_t percent; +	uint8_t enabled; +} __packed; + +/* Set keyboard backlight */ +#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 + +struct ec_params_pwm_set_keyboard_backlight { +	uint8_t percent; +} __packed; + +/* Set target fan PWM duty cycle */ +#define EC_CMD_PWM_SET_FAN_DUTY 0x24 + +struct ec_params_pwm_set_fan_duty { +	uint32_t percent; +} __packed; + +/*****************************************************************************/ +/* + * Lightbar commands. This looks worse than it is. Since we only use one HOST + * command to say "talk to the lightbar", we put the "and tell it to do X" part + * into a subcommand. We'll make separate structs for subcommands with + * different input args, so that we know how much to expect. + */ +#define EC_CMD_LIGHTBAR_CMD 0x28 + +struct rgb_s { +	uint8_t r, g, b; +}; + +#define LB_BATTERY_LEVELS 4 +/* List of tweakable parameters. NOTE: It's __packed so it can be sent in a + * host command, but the alignment is the same regardless. Keep it that way. + */ +struct lightbar_params { +	/* Timing */ +	int google_ramp_up; +	int google_ramp_down; +	int s3s0_ramp_up; +	int s0_tick_delay[2];			/* AC=0/1 */ +	int s0a_tick_delay[2];			/* AC=0/1 */ +	int s0s3_ramp_down; +	int s3_sleep_for; +	int s3_ramp_up; +	int s3_ramp_down; + +	/* Oscillation */ +	uint8_t new_s0; +	uint8_t osc_min[2];			/* AC=0/1 */ +	uint8_t osc_max[2];			/* AC=0/1 */ +	uint8_t w_ofs[2];			/* AC=0/1 */ + +	/* Brightness limits based on the backlight and AC. */ +	uint8_t bright_bl_off_fixed[2];		/* AC=0/1 */ +	uint8_t bright_bl_on_min[2];		/* AC=0/1 */ +	uint8_t bright_bl_on_max[2];		/* AC=0/1 */ + +	/* Battery level thresholds */ +	uint8_t battery_threshold[LB_BATTERY_LEVELS - 1]; + +	/* Map [AC][battery_level] to color index */ +	uint8_t s0_idx[2][LB_BATTERY_LEVELS];	/* AP is running */ +	uint8_t s3_idx[2][LB_BATTERY_LEVELS];	/* AP is sleeping */ + +	/* Color palette */ +	struct rgb_s color[8];			/* 0-3 are Google colors */ +} __packed; + +struct ec_params_lightbar { +	uint8_t cmd;		      /* Command (see enum lightbar_command) */ +	union { +		struct { +			/* no args */ +		} dump, off, on, init, get_seq, get_params; + +		struct num { +			uint8_t num; +		} brightness, seq, demo; + +		struct reg { +			uint8_t ctrl, reg, value; +		} reg; + +		struct rgb { +			uint8_t led, red, green, blue; +		} rgb; + +		struct lightbar_params set_params; +	}; +} __packed; + +struct ec_response_lightbar { +	union { +		struct dump { +			struct { +				uint8_t reg; +				uint8_t ic0; +				uint8_t ic1; +			} vals[23]; +		} dump; + +		struct get_seq { +			uint8_t num; +		} get_seq; + +		struct lightbar_params get_params; + +		struct { +			/* no return params */ +		} off, on, init, brightness, seq, reg, rgb, demo, set_params; +	}; +} __packed; + +/* Lightbar commands */ +enum lightbar_command { +	LIGHTBAR_CMD_DUMP = 0, +	LIGHTBAR_CMD_OFF = 1, +	LIGHTBAR_CMD_ON = 2, +	LIGHTBAR_CMD_INIT = 3, +	LIGHTBAR_CMD_BRIGHTNESS = 4, +	LIGHTBAR_CMD_SEQ = 5, +	LIGHTBAR_CMD_REG = 6, +	LIGHTBAR_CMD_RGB = 7, +	LIGHTBAR_CMD_GET_SEQ = 8, +	LIGHTBAR_CMD_DEMO = 9, +	LIGHTBAR_CMD_GET_PARAMS = 10, +	LIGHTBAR_CMD_SET_PARAMS = 11, +	LIGHTBAR_NUM_CMDS +}; + +/*****************************************************************************/ +/* Verified boot commands */ + +/* + * Note: command code 0x29 version 0 was VBOOT_CMD in Link EVT; it may be + * reused for other purposes with version > 0. + */ + +/* Verified boot hash command */ +#define EC_CMD_VBOOT_HASH 0x2A + +struct ec_params_vboot_hash { +	uint8_t cmd;             /* enum ec_vboot_hash_cmd */ +	uint8_t hash_type;       /* enum ec_vboot_hash_type */ +	uint8_t nonce_size;      /* Nonce size; may be 0 */ +	uint8_t reserved0;       /* Reserved; set 0 */ +	uint32_t offset;         /* Offset in flash to hash */ +	uint32_t size;           /* Number of bytes to hash */ +	uint8_t nonce_data[64];  /* Nonce data; ignored if nonce_size=0 */ +} __packed; + +struct ec_response_vboot_hash { +	uint8_t status;          /* enum ec_vboot_hash_status */ +	uint8_t hash_type;       /* enum ec_vboot_hash_type */ +	uint8_t digest_size;     /* Size of hash digest in bytes */ +	uint8_t reserved0;       /* Ignore; will be 0 */ +	uint32_t offset;         /* Offset in flash which was hashed */ +	uint32_t size;           /* Number of bytes hashed */ +	uint8_t hash_digest[64]; /* Hash digest data */ +} __packed; + +enum ec_vboot_hash_cmd { +	EC_VBOOT_HASH_GET = 0,       /* Get current hash status */ +	EC_VBOOT_HASH_ABORT = 1,     /* Abort calculating current hash */ +	EC_VBOOT_HASH_START = 2,     /* Start computing a new hash */ +	EC_VBOOT_HASH_RECALC = 3,    /* Synchronously compute a new hash */ +}; + +enum ec_vboot_hash_type { +	EC_VBOOT_HASH_TYPE_SHA256 = 0, /* SHA-256 */ +}; + +enum ec_vboot_hash_status { +	EC_VBOOT_HASH_STATUS_NONE = 0, /* No hash (not started, or aborted) */ +	EC_VBOOT_HASH_STATUS_DONE = 1, /* Finished computing a hash */ +	EC_VBOOT_HASH_STATUS_BUSY = 2, /* Busy computing a hash */ +}; + +/* + * Special values for offset for EC_VBOOT_HASH_START and EC_VBOOT_HASH_RECALC. + * If one of these is specified, the EC will automatically update offset and + * size to the correct values for the specified image (RO or RW). + */ +#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe +#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd + +/*****************************************************************************/ +/* USB charging control commands */ + +/* Set USB port charging mode */ +#define EC_CMD_USB_CHARGE_SET_MODE 0x30 + +struct ec_params_usb_charge_set_mode { +	uint8_t usb_port_id; +	uint8_t mode; +} __packed; + +/*****************************************************************************/ +/* Persistent storage for host */ + +/* Maximum bytes that can be read/written in a single command */ +#define EC_PSTORE_SIZE_MAX 64 + +/* Get persistent storage info */ +#define EC_CMD_PSTORE_INFO 0x40 + +struct ec_response_pstore_info { +	/* Persistent storage size, in bytes */ +	uint32_t pstore_size; +	/* Access size; read/write offset and size must be a multiple of this */ +	uint32_t access_size; +} __packed; + +/* + * Read persistent storage + * + * Response is params.size bytes of data. + */ +#define EC_CMD_PSTORE_READ 0x41 + +struct ec_params_pstore_read { +	uint32_t offset;   /* Byte offset to read */ +	uint32_t size;     /* Size to read in bytes */ +} __packed; + +/* Write persistent storage */ +#define EC_CMD_PSTORE_WRITE 0x42 + +struct ec_params_pstore_write { +	uint32_t offset;   /* Byte offset to write */ +	uint32_t size;     /* Size to write in bytes */ +	uint8_t data[EC_PSTORE_SIZE_MAX]; +} __packed; + +/*****************************************************************************/ +/* Real-time clock */ + +/* RTC params and response structures */ +struct ec_params_rtc { +	uint32_t time; +} __packed; + +struct ec_response_rtc { +	uint32_t time; +} __packed; + +/* These use ec_response_rtc */ +#define EC_CMD_RTC_GET_VALUE 0x44 +#define EC_CMD_RTC_GET_ALARM 0x45 + +/* These all use ec_params_rtc */ +#define EC_CMD_RTC_SET_VALUE 0x46 +#define EC_CMD_RTC_SET_ALARM 0x47 + +/*****************************************************************************/ +/* Port80 log access */ + +/* Get last port80 code from previous boot */ +#define EC_CMD_PORT80_LAST_BOOT 0x48 + +struct ec_response_port80_last_boot { +	uint16_t code; +} __packed; + +/*****************************************************************************/ +/* Thermal engine commands */ + +/* Set thershold value */ +#define EC_CMD_THERMAL_SET_THRESHOLD 0x50 + +struct ec_params_thermal_set_threshold { +	uint8_t sensor_type; +	uint8_t threshold_id; +	uint16_t value; +} __packed; + +/* Get threshold value */ +#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 + +struct ec_params_thermal_get_threshold { +	uint8_t sensor_type; +	uint8_t threshold_id; +} __packed; + +struct ec_response_thermal_get_threshold { +	uint16_t value; +} __packed; + +/* Toggle automatic fan control */ +#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 + +/* Get TMP006 calibration data */ +#define EC_CMD_TMP006_GET_CALIBRATION 0x53 + +struct ec_params_tmp006_get_calibration { +	uint8_t index; +} __packed; + +struct ec_response_tmp006_get_calibration { +	float s0; +	float b0; +	float b1; +	float b2; +} __packed; + +/* Set TMP006 calibration data */ +#define EC_CMD_TMP006_SET_CALIBRATION 0x54 + +struct ec_params_tmp006_set_calibration { +	uint8_t index; +	uint8_t reserved[3];  /* Reserved; set 0 */ +	float s0; +	float b0; +	float b1; +	float b2; +} __packed; + +/*****************************************************************************/ +/* MKBP - Matrix KeyBoard Protocol */ + +/* + * Read key state + * + * Returns raw data for keyboard cols; see ec_response_mkbp_info.cols for + * expected response size. + */ +#define EC_CMD_MKBP_STATE 0x60 + +/* Provide information about the matrix : number of rows and columns */ +#define EC_CMD_MKBP_INFO 0x61 + +struct ec_response_mkbp_info { +	uint32_t rows; +	uint32_t cols; +	uint8_t switches; +} __packed; + +/* Simulate key press */ +#define EC_CMD_MKBP_SIMULATE_KEY 0x62 + +struct ec_params_mkbp_simulate_key { +	uint8_t col; +	uint8_t row; +	uint8_t pressed; +} __packed; + +/* Configure keyboard scanning */ +#define EC_CMD_MKBP_SET_CONFIG 0x64 +#define EC_CMD_MKBP_GET_CONFIG 0x65 + +/* flags */ +enum mkbp_config_flags { +	EC_MKBP_FLAGS_ENABLE = 1,	/* Enable keyboard scanning */ +}; + +enum mkbp_config_valid { +	EC_MKBP_VALID_SCAN_PERIOD		= 1 << 0, +	EC_MKBP_VALID_POLL_TIMEOUT		= 1 << 1, +	EC_MKBP_VALID_MIN_POST_SCAN_DELAY	= 1 << 3, +	EC_MKBP_VALID_OUTPUT_SETTLE		= 1 << 4, +	EC_MKBP_VALID_DEBOUNCE_DOWN		= 1 << 5, +	EC_MKBP_VALID_DEBOUNCE_UP		= 1 << 6, +	EC_MKBP_VALID_FIFO_MAX_DEPTH		= 1 << 7, +}; + +/* Configuration for our key scanning algorithm */ +struct ec_mkbp_config { +	uint32_t valid_mask;		/* valid fields */ +	uint8_t flags;		/* some flags (enum mkbp_config_flags) */ +	uint8_t valid_flags;		/* which flags are valid */ +	uint16_t scan_period_us;	/* period between start of scans */ +	/* revert to interrupt mode after no activity for this long */ +	uint32_t poll_timeout_us; +	/* +	 * minimum post-scan relax time. Once we finish a scan we check +	 * the time until we are due to start the next one. If this time is +	 * shorter this field, we use this instead. +	 */ +	uint16_t min_post_scan_delay_us; +	/* delay between setting up output and waiting for it to settle */ +	uint16_t output_settle_us; +	uint16_t debounce_down_us;	/* time for debounce on key down */ +	uint16_t debounce_up_us;	/* time for debounce on key up */ +	/* maximum depth to allow for fifo (0 = no keyscan output) */ +	uint8_t fifo_max_depth; +} __packed; + +struct ec_params_mkbp_set_config { +	struct ec_mkbp_config config; +} __packed; + +struct ec_response_mkbp_get_config { +	struct ec_mkbp_config config; +} __packed; + +/* Run the key scan emulation */ +#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66 + +enum ec_keyscan_seq_cmd { +	EC_KEYSCAN_SEQ_STATUS = 0,	/* Get status information */ +	EC_KEYSCAN_SEQ_CLEAR = 1,	/* Clear sequence */ +	EC_KEYSCAN_SEQ_ADD = 2,		/* Add item to sequence */ +	EC_KEYSCAN_SEQ_START = 3,	/* Start running sequence */ +	EC_KEYSCAN_SEQ_COLLECT = 4,	/* Collect sequence summary data */ +}; + +enum ec_collect_flags { +	/* +	 * Indicates this scan was processed by the EC. Due to timing, some +	 * scans may be skipped. +	 */ +	EC_KEYSCAN_SEQ_FLAG_DONE	= 1 << 0, +}; + +struct ec_collect_item { +	uint8_t flags;		/* some flags (enum ec_collect_flags) */ +}; + +struct ec_params_keyscan_seq_ctrl { +	uint8_t cmd;	/* Command to send (enum ec_keyscan_seq_cmd) */ +	union { +		struct { +			uint8_t active;		/* still active */ +			uint8_t num_items;	/* number of items */ +			/* Current item being presented */ +			uint8_t cur_item; +		} status; +		struct { +			/* +			 * Absolute time for this scan, measured from the +			 * start of the sequence. +			 */ +			uint32_t time_us; +			uint8_t scan[0];	/* keyscan data */ +		} add; +		struct { +			uint8_t start_item;	/* First item to return */ +			uint8_t num_items;	/* Number of items to return */ +		} collect; +	}; +} __packed; + +struct ec_result_keyscan_seq_ctrl { +	union { +		struct { +			uint8_t num_items;	/* Number of items */ +			/* Data for each item */ +			struct ec_collect_item item[0]; +		} collect; +	}; +} __packed; + +/*****************************************************************************/ +/* Temperature sensor commands */ + +/* Read temperature sensor info */ +#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70 + +struct ec_params_temp_sensor_get_info { +	uint8_t id; +} __packed; + +struct ec_response_temp_sensor_get_info { +	char sensor_name[32]; +	uint8_t sensor_type; +} __packed; + +/*****************************************************************************/ + +/* + * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI + * commands accidentally sent to the wrong interface.  See the ACPI section + * below. + */ + +/*****************************************************************************/ +/* Host event commands */ + +/* + * Host event mask params and response structures, shared by all of the host + * event commands below. + */ +struct ec_params_host_event_mask { +	uint32_t mask; +} __packed; + +struct ec_response_host_event_mask { +	uint32_t mask; +} __packed; + +/* These all use ec_response_host_event_mask */ +#define EC_CMD_HOST_EVENT_GET_B         0x87 +#define EC_CMD_HOST_EVENT_GET_SMI_MASK  0x88 +#define EC_CMD_HOST_EVENT_GET_SCI_MASK  0x89 +#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d + +/* These all use ec_params_host_event_mask */ +#define EC_CMD_HOST_EVENT_SET_SMI_MASK  0x8a +#define EC_CMD_HOST_EVENT_SET_SCI_MASK  0x8b +#define EC_CMD_HOST_EVENT_CLEAR         0x8c +#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e +#define EC_CMD_HOST_EVENT_CLEAR_B       0x8f + +/*****************************************************************************/ +/* Switch commands */ + +/* Enable/disable LCD backlight */ +#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90 + +struct ec_params_switch_enable_backlight { +	uint8_t enabled; +} __packed; + +/* Enable/disable WLAN/Bluetooth */ +#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91 + +struct ec_params_switch_enable_wireless { +	uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* GPIO commands. Only available on EC if write protect has been disabled. */ + +/* Set GPIO output value */ +#define EC_CMD_GPIO_SET 0x92 + +struct ec_params_gpio_set { +	char name[32]; +	uint8_t val; +} __packed; + +/* Get GPIO value */ +#define EC_CMD_GPIO_GET 0x93 + +struct ec_params_gpio_get { +	char name[32]; +} __packed; +struct ec_response_gpio_get { +	uint8_t val; +} __packed; + +/*****************************************************************************/ +/* I2C commands. Only available when flash write protect is unlocked. */ + +/* Read I2C bus */ +#define EC_CMD_I2C_READ 0x94 + +struct ec_params_i2c_read { +	uint16_t addr; +	uint8_t read_size; /* Either 8 or 16. */ +	uint8_t port; +	uint8_t offset; +} __packed; +struct ec_response_i2c_read { +	uint16_t data; +} __packed; + +/* Write I2C bus */ +#define EC_CMD_I2C_WRITE 0x95 + +struct ec_params_i2c_write { +	uint16_t data; +	uint16_t addr; +	uint8_t write_size; /* Either 8 or 16. */ +	uint8_t port; +	uint8_t offset; +} __packed; + +/*****************************************************************************/ +/* Charge state commands. Only available when flash write protect unlocked. */ + +/* Force charge state machine to stop in idle mode */ +#define EC_CMD_CHARGE_FORCE_IDLE 0x96 + +struct ec_params_force_idle { +	uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* Console commands. Only available when flash write protect is unlocked. */ + +/* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */ +#define EC_CMD_CONSOLE_SNAPSHOT 0x97 + +/* + * Read next chunk of data from saved snapshot. + * + * Response is null-terminated string.  Empty string, if there is no more + * remaining output. + */ +#define EC_CMD_CONSOLE_READ 0x98 + +/*****************************************************************************/ + +/* + * Cut off battery power output if the battery supports. + * + * For unsupported battery, just don't implement this command and lets EC + * return EC_RES_INVALID_COMMAND. + */ +#define EC_CMD_BATTERY_CUT_OFF 0x99 + +/*****************************************************************************/ +/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */ + +/* + * Dump charge state machine context. + * + * Response is a binary dump of charge state machine context. + */ +#define EC_CMD_CHARGE_DUMP 0xa0 + +/* + * Set maximum battery charging current. + */ +#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1 + +struct ec_params_current_limit { +	uint32_t limit; +} __packed; + +/*****************************************************************************/ +/* System commands */ + +/* + * TODO: this is a confusing name, since it doesn't necessarily reboot the EC. + * Rename to "set image" or something similar. + */ +#define EC_CMD_REBOOT_EC 0xd2 + +/* Command */ +enum ec_reboot_cmd { +	EC_REBOOT_CANCEL = 0,        /* Cancel a pending reboot */ +	EC_REBOOT_JUMP_RO = 1,       /* Jump to RO without rebooting */ +	EC_REBOOT_JUMP_RW = 2,       /* Jump to RW without rebooting */ +	/* (command 3 was jump to RW-B) */ +	EC_REBOOT_COLD = 4,          /* Cold-reboot */ +	EC_REBOOT_DISABLE_JUMP = 5,  /* Disable jump until next reboot */ +	EC_REBOOT_HIBERNATE = 6      /* Hibernate EC */ +}; + +/* Flags for ec_params_reboot_ec.reboot_flags */ +#define EC_REBOOT_FLAG_RESERVED0      (1 << 0)  /* Was recovery request */ +#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1)  /* Reboot after AP shutdown */ + +struct ec_params_reboot_ec { +	uint8_t cmd;           /* enum ec_reboot_cmd */ +	uint8_t flags;         /* See EC_REBOOT_FLAG_* */ +} __packed; + +/* + * Get information on last EC panic. + * + * Returns variable-length platform-dependent panic information.  See panic.h + * for details. + */ +#define EC_CMD_GET_PANIC_INFO 0xd3 + +/*****************************************************************************/ +/* + * ACPI commands + * + * These are valid ONLY on the ACPI command/data port. + */ + +/* + * ACPI Read Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + *    - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD + *    - Wait for EC_LPC_CMDR_PENDING bit to clear + *    - Write address to EC_LPC_ADDR_ACPI_DATA + *    - Wait for EC_LPC_CMDR_DATA bit to set + *    - Read value from EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_READ 0x80 + +/* + * ACPI Write Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + *    - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD + *    - Wait for EC_LPC_CMDR_PENDING bit to clear + *    - Write address to EC_LPC_ADDR_ACPI_DATA + *    - Wait for EC_LPC_CMDR_PENDING bit to clear + *    - Write value to EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_WRITE 0x81 + +/* + * ACPI Query Embedded Controller + * + * This clears the lowest-order bit in the currently pending host events, and + * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, + * event 0x80000000 = 32), or 0 if no event was pending. + */ +#define EC_CMD_ACPI_QUERY_EVENT 0x84 + +/* Valid addresses in ACPI memory space, for read/write commands */ +/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ +#define EC_ACPI_MEM_VERSION            0x00 +/* + * Test location; writing value here updates test compliment byte to (0xff - + * value). + */ +#define EC_ACPI_MEM_TEST               0x01 +/* Test compliment; writes here are ignored. */ +#define EC_ACPI_MEM_TEST_COMPLIMENT    0x02 +/* Keyboard backlight brightness percent (0 - 100) */ +#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 + +/* Current version of ACPI memory address space */ +#define EC_ACPI_MEM_VERSION_CURRENT 1 + + +/*****************************************************************************/ +/* + * Special commands + * + * These do not follow the normal rules for commands.  See each command for + * details. + */ + +/* + * Reboot NOW + * + * This command will work even when the EC LPC interface is busy, because the + * reboot command is processed at interrupt level.  Note that when the EC + * reboots, the host will reboot too, so there is no response to this command. + * + * Use EC_CMD_REBOOT_EC to reboot the EC more politely. + */ +#define EC_CMD_REBOOT 0xd1  /* Think "die" */ + +/* + * Resend last response (not supported on LPC). + * + * Returns EC_RES_UNAVAILABLE if there is no response available - for example, + * there was no previous command, or the previous command's response was too + * big to save. + */ +#define EC_CMD_RESEND_RESPONSE 0xdb + +/* + * This header byte on a command indicate version 0. Any header byte less + * than this means that we are talking to an old EC which doesn't support + * versioning. In that case, we assume version 0. + * + * Header bytes greater than this indicate a later version. For example, + * EC_CMD_VERSION0 + 1 means we are using version 1. + * + * The old EC interface must not use commands 0dc or higher. + */ +#define EC_CMD_VERSION0 0xdc + +#endif  /* !__ACPI__ */ + +#endif  /* __CROS_EC_COMMANDS_H */ diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index ecddc5173c7..8f21daf62fb 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -1,9 +1,10 @@  /*   * TI Palmas   * - * Copyright 2011 Texas Instruments Inc. + * Copyright 2011-2013 Texas Instruments Inc.   *   * Author: Graeme Gregory <gg@slimlogic.co.uk> + * Author: Ian Lartey <ian@slimlogic.co.uk>   *   *  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 @@ -22,6 +23,15 @@  #define PALMAS_NUM_CLIENTS		3 +/* The ID_REVISION NUMBERS */ +#define PALMAS_CHIP_OLD_ID		0x0000 +#define PALMAS_CHIP_ID			0xC035 +#define PALMAS_CHIP_CHARGER_ID		0xC036 + +#define is_palmas(a)	(((a) == PALMAS_CHIP_OLD_ID) || \ +			((a) == PALMAS_CHIP_ID)) +#define is_palmas_charger(a) ((a) == PALMAS_CHIP_CHARGER_ID) +  struct palmas_pmic;  struct palmas_gpadc;  struct palmas_resource; diff --git a/include/linux/mfd/retu.h b/include/linux/mfd/retu.h index 1e2715d5b83..65471c4a392 100644 --- a/include/linux/mfd/retu.h +++ b/include/linux/mfd/retu.h @@ -1,5 +1,5 @@  /* - * Retu MFD driver interface + * Retu/Tahvo MFD driver interface   *   * This file is subject to the terms and conditions of the GNU General   * Public License. See the file "COPYING" in the main directory of this @@ -19,4 +19,10 @@ int retu_write(struct retu_dev *, u8, u16);  #define RETU_REG_CC1		0x0d		/* Common control register 1 */  #define RETU_REG_STATUS		0x16		/* Status register */ +/* Interrupt sources */ +#define TAHVO_INT_VBUS		0		/* VBUS state */ + +/* Interrupt status */ +#define TAHVO_STAT_VBUS		(1 << TAHVO_INT_VBUS) +  #endif /* __LINUX_MFD_RETU_H */ diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 26ea7f1b7ca..86bc635f838 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -500,6 +500,8 @@  #define BPP_POWER_15_PERCENT_ON		0x08  #define BPP_POWER_ON			0x00  #define BPP_POWER_MASK			0x0F +#define SD_VCC_PARTIAL_POWER_ON		0x02 +#define SD_VCC_POWER_ON			0x00  /* PWR_GATE_CTRL */  #define PWR_GATE_EN			0x01 @@ -689,6 +691,40 @@  #define IMAGE_FLAG_ADDR0		0xCE80  #define IMAGE_FLAG_ADDR1		0xCE81 +/* Phy register */ +#define PHY_PCR				0x00 +#define PHY_RCR0			0x01 +#define PHY_RCR1			0x02 +#define PHY_RCR2			0x03 +#define PHY_RTCR			0x04 +#define PHY_RDR				0x05 +#define PHY_TCR0			0x06 +#define PHY_TCR1			0x07 +#define PHY_TUNE			0x08 +#define PHY_IMR				0x09 +#define PHY_BPCR			0x0A +#define PHY_BIST			0x0B +#define PHY_RAW_L			0x0C +#define PHY_RAW_H			0x0D +#define PHY_RAW_DATA			0x0E +#define PHY_HOST_CLK_CTRL		0x0F +#define PHY_DMR				0x10 +#define PHY_BACR			0x11 +#define PHY_IER				0x12 +#define PHY_BCSR			0x13 +#define PHY_BPR				0x14 +#define PHY_BPNR2			0x15 +#define PHY_BPNR			0x16 +#define PHY_BRNR2			0x17 +#define PHY_BENR			0x18 +#define PHY_REG_REV			0x19 +#define PHY_FLD0			0x1A +#define PHY_FLD1			0x1B +#define PHY_FLD2			0x1C +#define PHY_FLD3			0x1D +#define PHY_FLD4			0x1E +#define PHY_DUM_REG			0x1F +  #define rtsx_pci_init_cmd(pcr)		((pcr)->ci = 0)  struct rtsx_pcr; diff --git a/include/linux/mfd/si476x-core.h b/include/linux/mfd/si476x-core.h new file mode 100644 index 00000000000..ba89b94e4a5 --- /dev/null +++ b/include/linux/mfd/si476x-core.h @@ -0,0 +1,533 @@ +/* + * include/media/si476x-core.h -- Common definitions for si476x core + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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; version 2 of the License. + * + * 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. + * + */ + +#ifndef SI476X_CORE_H +#define SI476X_CORE_H + +#include <linux/kfifo.h> +#include <linux/atomic.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/videodev2.h> +#include <linux/regulator/consumer.h> + +#include <linux/mfd/si476x-platform.h> +#include <linux/mfd/si476x-reports.h> + +/* Command Timeouts */ +#define SI476X_DEFAULT_TIMEOUT	100000 +#define SI476X_TIMEOUT_TUNE	700000 +#define SI476X_TIMEOUT_POWER_UP	330000 +#define SI476X_STATUS_POLL_US	0 + +/* -------------------- si476x-i2c.c ----------------------- */ + +enum si476x_freq_supported_chips { +	SI476X_CHIP_SI4761 = 1, +	SI476X_CHIP_SI4764, +	SI476X_CHIP_SI4768, +}; + +enum si476x_part_revisions { +	SI476X_REVISION_A10 = 0, +	SI476X_REVISION_A20 = 1, +	SI476X_REVISION_A30 = 2, +}; + +enum si476x_mfd_cells { +	SI476X_RADIO_CELL = 0, +	SI476X_CODEC_CELL, +	SI476X_MFD_CELLS, +}; + +/** + * enum si476x_power_state - possible power state of the si476x + * device. + * + * @SI476X_POWER_DOWN: In this state all regulators are turned off + * and the reset line is pulled low. The device is completely + * inactive. + * @SI476X_POWER_UP_FULL: In this state all the power regualtors are + * turned on, reset line pulled high, IRQ line is enabled(polling is + * active for polling use scenario) and device is turned on with + * POWER_UP command. The device is ready to be used. + * @SI476X_POWER_INCONSISTENT: This state indicates that previous + * power down was inconsistent, meaning some of the regulators were + * not turned down and thus use of the device, without power-cycling + * is impossible. + */ +enum si476x_power_state { +	SI476X_POWER_DOWN		= 0, +	SI476X_POWER_UP_FULL		= 1, +	SI476X_POWER_INCONSISTENT	= 2, +}; + +/** + * struct si476x_core - internal data structure representing the + * underlying "core" device which all the MFD cell-devices use. + * + * @client: Actual I2C client used to transfer commands to the chip. + * @chip_id: Last digit of the chip model(E.g. "1" for SI4761) + * @cells: MFD cell devices created by this driver. + * @cmd_lock: Mutex used to serialize all the requests to the core + * device. This filed should not be used directly. Instead + * si476x_core_lock()/si476x_core_unlock() should be used to get + * exclusive access to the "core" device. + * @users: Active users counter(Used by the radio cell) + * @rds_read_queue: Wait queue used to wait for RDS data. + * @rds_fifo: FIFO in which all the RDS data received from the chip is + * placed. + * @rds_fifo_drainer: Worker that drains on-chip RDS FIFO. + * @rds_drainer_is_working: Flag used for launching only one instance + * of the @rds_fifo_drainer. + * @rds_drainer_status_lock: Lock used to guard access to the + * @rds_drainer_is_working variable. + * @command: Wait queue for wainting on the command comapletion. + * @cts: Clear To Send flag set upon receiving first status with CTS + * set. + * @tuning: Wait queue used for wainting for tune/seek comand + * completion. + * @stc: Similar to @cts, but for the STC bit of the status value. + * @power_up_parameters: Parameters used as argument for POWER_UP + * command when the device is started. + * @state: Current power state of the device. + * @supplues: Structure containing handles to all power supplies used + * by the device (NULL ones are ignored). + * @gpio_reset: GPIO pin connectet to the RSTB pin of the chip. + * @pinmux: Chip's configurable pins configuration. + * @diversity_mode: Chips role when functioning in diversity mode. + * @status_monitor: Polling worker used in polling use case scenarion + * (when IRQ is not avalible). + * @revision: Chip's running firmware revision number(Used for correct + * command set support). + */ + +struct si476x_core { +	struct i2c_client *client; +	struct regmap *regmap; +	int chip_id; +	struct mfd_cell cells[SI476X_MFD_CELLS]; + +	struct mutex cmd_lock; /* for serializing fm radio operations */ +	atomic_t users; + +	wait_queue_head_t  rds_read_queue; +	struct kfifo       rds_fifo; +	struct work_struct rds_fifo_drainer; +	bool               rds_drainer_is_working; +	struct mutex       rds_drainer_status_lock; + +	wait_queue_head_t command; +	atomic_t          cts; + +	wait_queue_head_t tuning; +	atomic_t          stc; + +	struct si476x_power_up_args power_up_parameters; + +	enum si476x_power_state power_state; + +	struct regulator_bulk_data supplies[4]; + +	int gpio_reset; + +	struct si476x_pinmux pinmux; +	enum si476x_phase_diversity_mode diversity_mode; + +	atomic_t is_alive; + +	struct delayed_work status_monitor; +#define SI476X_WORK_TO_CORE(w) container_of(to_delayed_work(w),	\ +					    struct si476x_core,	\ +					    status_monitor) + +	int revision; + +	int rds_fifo_depth; +}; + +static inline struct si476x_core *i2c_mfd_cell_to_core(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev->parent); +	return i2c_get_clientdata(client); +} + + +/** + * si476x_core_lock() - lock the core device to get an exclusive access + * to it. + */ +static inline void si476x_core_lock(struct si476x_core *core) +{ +	mutex_lock(&core->cmd_lock); +} + +/** + * si476x_core_unlock() - unlock the core device to relinquish an + * exclusive access to it. + */ +static inline void si476x_core_unlock(struct si476x_core *core) +{ +	mutex_unlock(&core->cmd_lock); +} + +/* *_TUNE_FREQ family of commands accept frequency in multiples of +    10kHz */ +static inline u16 hz_to_si476x(struct si476x_core *core, int freq) +{ +	u16 result; + +	switch (core->power_up_parameters.func) { +	default: +	case SI476X_FUNC_FM_RECEIVER: +		result = freq / 10000; +		break; +	case SI476X_FUNC_AM_RECEIVER: +		result = freq / 1000; +		break; +	} + +	return result; +} + +static inline int si476x_to_hz(struct si476x_core *core, u16 freq) +{ +	int result; + +	switch (core->power_up_parameters.func) { +	default: +	case SI476X_FUNC_FM_RECEIVER: +		result = freq * 10000; +		break; +	case SI476X_FUNC_AM_RECEIVER: +		result = freq * 1000; +		break; +	} + +	return result; +} + +/* Since the V4L2_TUNER_CAP_LOW flag is supplied, V4L2 subsystem + * mesures frequency in 62.5 Hz units */ + +static inline int hz_to_v4l2(int freq) +{ +	return (freq * 10) / 625; +} + +static inline int v4l2_to_hz(int freq) +{ +	return (freq * 625) / 10; +} + +static inline u16 v4l2_to_si476x(struct si476x_core *core, int freq) +{ +	return hz_to_si476x(core, v4l2_to_hz(freq)); +} + +static inline int si476x_to_v4l2(struct si476x_core *core, u16 freq) +{ +	return hz_to_v4l2(si476x_to_hz(core, freq)); +} + + + +/** + * struct si476x_func_info - structure containing result of the + * FUNC_INFO command. + * + * @firmware.major: Firmware major number. + * @firmware.minor[...]: Firmware minor numbers. + * @patch_id: + * @func: Mode tuner is working in. + */ +struct si476x_func_info { +	struct { +		u8 major, minor[2]; +	} firmware; +	u16 patch_id; +	enum si476x_func func; +}; + +/** + * struct si476x_power_down_args - structure used to pass parameters + * to POWER_DOWN command + * + * @xosc: true - Power down, but leav oscillator running. + *        false - Full power down. + */ +struct si476x_power_down_args { +	bool xosc; +}; + +/** + * enum si476x_tunemode - enum representing possible tune modes for + * the chip. + * @SI476X_TM_VALIDATED_NORMAL_TUNE: Unconditionally stay on the new + * channel after tune, tune status is valid. + * @SI476X_TM_INVALIDATED_FAST_TUNE: Unconditionally stay in the new + * channel after tune, tune status invalid. + * @SI476X_TM_VALIDATED_AF_TUNE: Jump back to previous channel if + * metric thresholds are not met. + * @SI476X_TM_VALIDATED_AF_CHECK: Unconditionally jump back to the + * previous channel. + */ +enum si476x_tunemode { +	SI476X_TM_VALIDATED_NORMAL_TUNE = 0, +	SI476X_TM_INVALIDATED_FAST_TUNE = 1, +	SI476X_TM_VALIDATED_AF_TUNE     = 2, +	SI476X_TM_VALIDATED_AF_CHECK    = 3, +}; + +/** + * enum si476x_smoothmetrics - enum containing the possible setting fo + * audio transitioning of the chip + * @SI476X_SM_INITIALIZE_AUDIO: Initialize audio state to match this + * new channel + * @SI476X_SM_TRANSITION_AUDIO: Transition audio state from previous + * channel values to the new values + */ +enum si476x_smoothmetrics { +	SI476X_SM_INITIALIZE_AUDIO = 0, +	SI476X_SM_TRANSITION_AUDIO = 1, +}; + +/** + * struct si476x_rds_status_report - the structure representing the + * response to 'FM_RD_STATUS' command + * @rdstpptyint: Traffic program flag(TP) and/or program type(PTY) + * code has changed. + * @rdspiint: Program indentifiaction(PI) code has changed. + * @rdssyncint: RDS synchronization has changed. + * @rdsfifoint: RDS was received and the RDS FIFO has at least + * 'FM_RDS_INTERRUPT_FIFO_COUNT' elements in it. + * @tpptyvalid: TP flag and PTY code are valid falg. + * @pivalid: PI code is valid flag. + * @rdssync: RDS is currently synchronized. + * @rdsfifolost: On or more RDS groups have been lost/discarded flag. + * @tp: Current channel's TP flag. + * @pty: Current channel's PTY code. + * @pi: Current channel's PI code. + * @rdsfifoused: Number of blocks remaining in the RDS FIFO (0 if + * empty). + */ +struct si476x_rds_status_report { +	bool rdstpptyint, rdspiint, rdssyncint, rdsfifoint; +	bool tpptyvalid, pivalid, rdssync, rdsfifolost; +	bool tp; + +	u8 pty; +	u16 pi; + +	u8 rdsfifoused; +	u8 ble[4]; + +	struct v4l2_rds_data rds[4]; +}; + +struct si476x_rsq_status_args { +	bool primary; +	bool rsqack; +	bool attune; +	bool cancel; +	bool stcack; +}; + +enum si476x_injside { +	SI476X_INJSIDE_AUTO	= 0, +	SI476X_INJSIDE_LOW	= 1, +	SI476X_INJSIDE_HIGH	= 2, +}; + +struct si476x_tune_freq_args { +	bool zifsr; +	bool hd; +	enum si476x_injside injside; +	int freq; +	enum si476x_tunemode tunemode; +	enum si476x_smoothmetrics smoothmetrics; +	int antcap; +}; + +int  si476x_core_stop(struct si476x_core *, bool); +int  si476x_core_start(struct si476x_core *, bool); +int  si476x_core_set_power_state(struct si476x_core *, enum si476x_power_state); +bool si476x_core_has_am(struct si476x_core *); +bool si476x_core_has_diversity(struct si476x_core *); +bool si476x_core_is_a_secondary_tuner(struct si476x_core *); +bool si476x_core_is_a_primary_tuner(struct si476x_core *); +bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core); +bool si476x_core_is_powered_up(struct si476x_core *core); + +enum si476x_i2c_type { +	SI476X_I2C_SEND, +	SI476X_I2C_RECV +}; + +int si476x_core_i2c_xfer(struct si476x_core *, +			 enum si476x_i2c_type, +			 char *, int); + + +/* -------------------- si476x-cmd.c ----------------------- */ + +int si476x_core_cmd_func_info(struct si476x_core *, struct si476x_func_info *); +int si476x_core_cmd_set_property(struct si476x_core *, u16, u16); +int si476x_core_cmd_get_property(struct si476x_core *, u16); +int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *, +				      enum si476x_dclk_config, +				      enum si476x_dfs_config, +				      enum si476x_dout_config, +				      enum si476x_xout_config); +int si476x_core_cmd_zif_pin_cfg(struct si476x_core *, +				enum si476x_iqclk_config, +				enum si476x_iqfs_config, +				enum si476x_iout_config, +				enum si476x_qout_config); +int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *, +					    enum si476x_icin_config, +					    enum si476x_icip_config, +					    enum si476x_icon_config, +					    enum si476x_icop_config); +int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *, +				      enum si476x_lrout_config); +int si476x_core_cmd_intb_pin_cfg(struct si476x_core *, enum si476x_intb_config, +				 enum si476x_a1_config); +int si476x_core_cmd_fm_seek_start(struct si476x_core *, bool, bool); +int si476x_core_cmd_am_seek_start(struct si476x_core *, bool, bool); +int si476x_core_cmd_fm_rds_status(struct si476x_core *, bool, bool, bool, +				  struct si476x_rds_status_report *); +int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *, bool, +				      struct si476x_rds_blockcount_report *); +int si476x_core_cmd_fm_tune_freq(struct si476x_core *, +				 struct si476x_tune_freq_args *); +int si476x_core_cmd_am_tune_freq(struct si476x_core *, +				 struct si476x_tune_freq_args *); +int si476x_core_cmd_am_rsq_status(struct si476x_core *, +				  struct si476x_rsq_status_args *, +				  struct si476x_rsq_status_report *); +int si476x_core_cmd_fm_rsq_status(struct si476x_core *, +				  struct si476x_rsq_status_args *, +				  struct si476x_rsq_status_report *); +int si476x_core_cmd_power_up(struct si476x_core *, +			     struct si476x_power_up_args *); +int si476x_core_cmd_power_down(struct si476x_core *, +			       struct si476x_power_down_args *); +int si476x_core_cmd_fm_phase_div_status(struct si476x_core *); +int si476x_core_cmd_fm_phase_diversity(struct si476x_core *, +				       enum si476x_phase_diversity_mode); + +int si476x_core_cmd_fm_acf_status(struct si476x_core *, +				  struct si476x_acf_status_report *); +int si476x_core_cmd_am_acf_status(struct si476x_core *, +				  struct si476x_acf_status_report *); +int si476x_core_cmd_agc_status(struct si476x_core *, +			       struct si476x_agc_status_report *); + +enum si476x_power_grid_type { +	SI476X_POWER_GRID_50HZ = 0, +	SI476X_POWER_GRID_60HZ, +}; + +/* Properties  */ + +enum si476x_interrupt_flags { +	SI476X_STCIEN = (1 << 0), +	SI476X_ACFIEN = (1 << 1), +	SI476X_RDSIEN = (1 << 2), +	SI476X_RSQIEN = (1 << 3), + +	SI476X_ERRIEN = (1 << 6), +	SI476X_CTSIEN = (1 << 7), + +	SI476X_STCREP = (1 << 8), +	SI476X_ACFREP = (1 << 9), +	SI476X_RDSREP = (1 << 10), +	SI476X_RSQREP = (1 << 11), +}; + +enum si476x_rdsint_sources { +	SI476X_RDSTPPTY = (1 << 4), +	SI476X_RDSPI    = (1 << 3), +	SI476X_RDSSYNC	= (1 << 1), +	SI476X_RDSRECV	= (1 << 0), +}; + +enum si476x_status_response_bits { +	SI476X_CTS	  = (1 << 7), +	SI476X_ERR	  = (1 << 6), +	/* Status response for WB receiver */ +	SI476X_WB_ASQ_INT = (1 << 4), +	SI476X_RSQ_INT    = (1 << 3), +	/* Status response for FM receiver */ +	SI476X_FM_RDS_INT = (1 << 2), +	SI476X_ACF_INT    = (1 << 1), +	SI476X_STC_INT    = (1 << 0), +}; + +/* -------------------- si476x-prop.c ----------------------- */ + +enum si476x_common_receiver_properties { +	SI476X_PROP_INT_CTL_ENABLE			= 0x0000, +	SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE	= 0x0200, +	SI476X_PROP_DIGITAL_IO_INPUT_FORMAT		= 0x0201, +	SI476X_PROP_DIGITAL_IO_OUTPUT_SAMPLE_RATE	= 0x0202, +	SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT		= 0x0203, + +	SI476X_PROP_SEEK_BAND_BOTTOM			= 0x1100, +	SI476X_PROP_SEEK_BAND_TOP			= 0x1101, +	SI476X_PROP_SEEK_FREQUENCY_SPACING		= 0x1102, + +	SI476X_PROP_VALID_MAX_TUNE_ERROR		= 0x2000, +	SI476X_PROP_VALID_SNR_THRESHOLD			= 0x2003, +	SI476X_PROP_VALID_RSSI_THRESHOLD		= 0x2004, +}; + +enum si476x_am_receiver_properties { +	SI476X_PROP_AUDIO_PWR_LINE_FILTER		= 0x0303, +}; + +enum si476x_fm_receiver_properties { +	SI476X_PROP_AUDIO_DEEMPHASIS			= 0x0302, + +	SI476X_PROP_FM_RDS_INTERRUPT_SOURCE		= 0x4000, +	SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT		= 0x4001, +	SI476X_PROP_FM_RDS_CONFIG			= 0x4002, +}; + +enum si476x_prop_audio_pwr_line_filter_bits { +	SI476X_PROP_PWR_HARMONICS_MASK	= 0x001f, +	SI476X_PROP_PWR_GRID_MASK	= 0x0100, +	SI476X_PROP_PWR_ENABLE_MASK	= 0x0200, +	SI476X_PROP_PWR_GRID_50HZ	= 0x0000, +	SI476X_PROP_PWR_GRID_60HZ	= 0x0100, +}; + +enum si476x_prop_fm_rds_config_bits { +	SI476X_PROP_RDSEN_MASK	= 0x1, +	SI476X_PROP_RDSEN	= 0x1, +}; + + +struct regmap *devm_regmap_init_si476x(struct si476x_core *); + +#endif	/* SI476X_CORE_H */ diff --git a/include/linux/mfd/si476x-platform.h b/include/linux/mfd/si476x-platform.h new file mode 100644 index 00000000000..88bb93b7a9d --- /dev/null +++ b/include/linux/mfd/si476x-platform.h @@ -0,0 +1,267 @@ +/* + * include/media/si476x-platform.h -- Platform data specific definitions + * + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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; version 2 of the License. + * + * 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. + * + */ + +#ifndef __SI476X_PLATFORM_H__ +#define __SI476X_PLATFORM_H__ + +/* It is possible to select one of the four adresses using pins A0 + * and A1 on SI476x */ +#define SI476X_I2C_ADDR_1	0x60 +#define SI476X_I2C_ADDR_2	0x61 +#define SI476X_I2C_ADDR_3	0x62 +#define SI476X_I2C_ADDR_4	0x63 + +enum si476x_iqclk_config { +	SI476X_IQCLK_NOOP = 0, +	SI476X_IQCLK_TRISTATE = 1, +	SI476X_IQCLK_IQ = 21, +}; +enum si476x_iqfs_config { +	SI476X_IQFS_NOOP = 0, +	SI476X_IQFS_TRISTATE = 1, +	SI476X_IQFS_IQ = 21, +}; +enum si476x_iout_config { +	SI476X_IOUT_NOOP = 0, +	SI476X_IOUT_TRISTATE = 1, +	SI476X_IOUT_OUTPUT = 22, +}; +enum si476x_qout_config { +	SI476X_QOUT_NOOP = 0, +	SI476X_QOUT_TRISTATE = 1, +	SI476X_QOUT_OUTPUT = 22, +}; + +enum si476x_dclk_config { +	SI476X_DCLK_NOOP      = 0, +	SI476X_DCLK_TRISTATE  = 1, +	SI476X_DCLK_DAUDIO    = 10, +}; + +enum si476x_dfs_config { +	SI476X_DFS_NOOP      = 0, +	SI476X_DFS_TRISTATE  = 1, +	SI476X_DFS_DAUDIO    = 10, +}; + +enum si476x_dout_config { +	SI476X_DOUT_NOOP       = 0, +	SI476X_DOUT_TRISTATE   = 1, +	SI476X_DOUT_I2S_OUTPUT = 12, +	SI476X_DOUT_I2S_INPUT  = 13, +}; + +enum si476x_xout_config { +	SI476X_XOUT_NOOP        = 0, +	SI476X_XOUT_TRISTATE    = 1, +	SI476X_XOUT_I2S_INPUT   = 13, +	SI476X_XOUT_MODE_SELECT = 23, +}; + +enum si476x_icin_config { +	SI476X_ICIN_NOOP	= 0, +	SI476X_ICIN_TRISTATE	= 1, +	SI476X_ICIN_GPO1_HIGH	= 2, +	SI476X_ICIN_GPO1_LOW	= 3, +	SI476X_ICIN_IC_LINK	= 30, +}; + +enum si476x_icip_config { +	SI476X_ICIP_NOOP	= 0, +	SI476X_ICIP_TRISTATE	= 1, +	SI476X_ICIP_GPO2_HIGH	= 2, +	SI476X_ICIP_GPO2_LOW	= 3, +	SI476X_ICIP_IC_LINK	= 30, +}; + +enum si476x_icon_config { +	SI476X_ICON_NOOP	= 0, +	SI476X_ICON_TRISTATE	= 1, +	SI476X_ICON_I2S		= 10, +	SI476X_ICON_IC_LINK	= 30, +}; + +enum si476x_icop_config { +	SI476X_ICOP_NOOP	= 0, +	SI476X_ICOP_TRISTATE	= 1, +	SI476X_ICOP_I2S		= 10, +	SI476X_ICOP_IC_LINK	= 30, +}; + + +enum si476x_lrout_config { +	SI476X_LROUT_NOOP	= 0, +	SI476X_LROUT_TRISTATE	= 1, +	SI476X_LROUT_AUDIO	= 2, +	SI476X_LROUT_MPX	= 3, +}; + + +enum si476x_intb_config { +	SI476X_INTB_NOOP     = 0, +	SI476X_INTB_TRISTATE = 1, +	SI476X_INTB_DAUDIO   = 10, +	SI476X_INTB_IRQ      = 40, +}; + +enum si476x_a1_config { +	SI476X_A1_NOOP     = 0, +	SI476X_A1_TRISTATE = 1, +	SI476X_A1_IRQ      = 40, +}; + + +struct si476x_pinmux { +	enum si476x_dclk_config  dclk; +	enum si476x_dfs_config   dfs; +	enum si476x_dout_config  dout; +	enum si476x_xout_config  xout; + +	enum si476x_iqclk_config iqclk; +	enum si476x_iqfs_config  iqfs; +	enum si476x_iout_config  iout; +	enum si476x_qout_config  qout; + +	enum si476x_icin_config  icin; +	enum si476x_icip_config  icip; +	enum si476x_icon_config  icon; +	enum si476x_icop_config  icop; + +	enum si476x_lrout_config lrout; + +	enum si476x_intb_config  intb; +	enum si476x_a1_config    a1; +}; + +enum si476x_ibias6x { +	SI476X_IBIAS6X_OTHER			= 0, +	SI476X_IBIAS6X_RCVR1_NON_4MHZ_CLK	= 1, +}; + +enum si476x_xstart { +	SI476X_XSTART_MULTIPLE_TUNER	= 0x11, +	SI476X_XSTART_NORMAL		= 0x77, +}; + +enum si476x_freq { +	SI476X_FREQ_4_MHZ		= 0, +	SI476X_FREQ_37P209375_MHZ	= 1, +	SI476X_FREQ_36P4_MHZ		= 2, +	SI476X_FREQ_37P8_MHZ		=  3, +}; + +enum si476x_xmode { +	SI476X_XMODE_CRYSTAL_RCVR1	= 1, +	SI476X_XMODE_EXT_CLOCK		= 2, +	SI476X_XMODE_CRYSTAL_RCVR2_3	= 3, +}; + +enum si476x_xbiashc { +	SI476X_XBIASHC_SINGLE_RECEIVER = 0, +	SI476X_XBIASHC_MULTIPLE_RECEIVER = 1, +}; + +enum si476x_xbias { +	SI476X_XBIAS_RCVR2_3	= 0, +	SI476X_XBIAS_4MHZ_RCVR1 = 3, +	SI476X_XBIAS_RCVR1	= 7, +}; + +enum si476x_func { +	SI476X_FUNC_BOOTLOADER	= 0, +	SI476X_FUNC_FM_RECEIVER = 1, +	SI476X_FUNC_AM_RECEIVER = 2, +	SI476X_FUNC_WB_RECEIVER = 3, +}; + + +/** + * @xcload: Selects the amount of additional on-chip capacitance to + *          be connected between XTAL1 and gnd and between XTAL2 and + *          GND. One half of the capacitance value shown here is the + *          additional load capacitance presented to the xtal. The + *          minimum step size is 0.277 pF. Recommended value is 0x28 + *          but it will be layout dependent. Range is 0–0x3F i.e. + *          (0–16.33 pF) + * @ctsien: enable CTSINT(interrupt request when CTS condition + *          arises) when set + * @intsel: when set A1 pin becomes the interrupt pin; otherwise, + *          INTB is the interrupt pin + * @func:   selects the boot function of the device. I.e. + *          SI476X_BOOTLOADER  - Boot loader + *          SI476X_FM_RECEIVER - FM receiver + *          SI476X_AM_RECEIVER - AM receiver + *          SI476X_WB_RECEIVER - Weatherband receiver + * @freq:   oscillator's crystal frequency: + *          SI476X_XTAL_37P209375_MHZ - 37.209375 Mhz + *          SI476X_XTAL_36P4_MHZ      - 36.4 Mhz + *          SI476X_XTAL_37P8_MHZ      - 37.8 Mhz + */ +struct si476x_power_up_args { +	enum si476x_ibias6x ibias6x; +	enum si476x_xstart  xstart; +	u8   xcload; +	bool fastboot; +	enum si476x_xbiashc xbiashc; +	enum si476x_xbias   xbias; +	enum si476x_func    func; +	enum si476x_freq    freq; +	enum si476x_xmode   xmode; +}; + + +/** + * enum si476x_phase_diversity_mode - possbile phase diversity modes + * for SI4764/5/6/7 chips. + * + * @SI476X_PHDIV_DISABLED:		Phase diversity feature is + *					disabled. + * @SI476X_PHDIV_PRIMARY_COMBINING:	Tuner works as a primary tuner + *					in combination with a + *					secondary one. + * @SI476X_PHDIV_PRIMARY_ANTENNA:	Tuner works as a primary tuner + *					using only its own antenna. + * @SI476X_PHDIV_SECONDARY_ANTENNA:	Tuner works as a primary tuner + *					usning seconary tuner's antenna. + * @SI476X_PHDIV_SECONDARY_COMBINING:	Tuner works as a secondary + *					tuner in combination with the + *					primary one. + */ +enum si476x_phase_diversity_mode { +	SI476X_PHDIV_DISABLED			= 0, +	SI476X_PHDIV_PRIMARY_COMBINING		= 1, +	SI476X_PHDIV_PRIMARY_ANTENNA		= 2, +	SI476X_PHDIV_SECONDARY_ANTENNA		= 3, +	SI476X_PHDIV_SECONDARY_COMBINING	= 5, +}; + + +/* + * Platform dependent definition + */ +struct si476x_platform_data { +	int gpio_reset; /* < 0 if not used */ + +	struct si476x_power_up_args power_up_parameters; +	enum si476x_phase_diversity_mode diversity_mode; + +	struct si476x_pinmux pinmux; +}; + + +#endif /* __SI476X_PLATFORM_H__ */ diff --git a/include/linux/mfd/si476x-reports.h b/include/linux/mfd/si476x-reports.h new file mode 100644 index 00000000000..e0b9455a79c --- /dev/null +++ b/include/linux/mfd/si476x-reports.h @@ -0,0 +1,163 @@ +/* + * include/media/si476x-platform.h -- Definitions of the data formats + * returned by debugfs hooks + * + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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; version 2 of the License. + * + * 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. + * + */ + +#ifndef __SI476X_REPORTS_H__ +#define __SI476X_REPORTS_H__ + +/** + * struct si476x_rsq_status - structure containing received signal + * quality + * @multhint:   Multipath Detect High. + *              true  - Indicatedes that the value is below + *                      FM_RSQ_MULTIPATH_HIGH_THRESHOLD + *              false - Indicatedes that the value is above + *                      FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * @multlint:   Multipath Detect Low. + *              true  - Indicatedes that the value is below + *                      FM_RSQ_MULTIPATH_LOW_THRESHOLD + *              false - Indicatedes that the value is above + *                      FM_RSQ_MULTIPATH_LOW_THRESHOLD + * @snrhint:    SNR Detect High. + *              true  - Indicatedes that the value is below + *                      FM_RSQ_SNR_HIGH_THRESHOLD + *              false - Indicatedes that the value is above + *                      FM_RSQ_SNR_HIGH_THRESHOLD + * @snrlint:    SNR Detect Low. + *              true  - Indicatedes that the value is below + *                      FM_RSQ_SNR_LOW_THRESHOLD + *              false - Indicatedes that the value is above + *                      FM_RSQ_SNR_LOW_THRESHOLD + * @rssihint:   RSSI Detect High. + *              true  - Indicatedes that the value is below + *                      FM_RSQ_RSSI_HIGH_THRESHOLD + *              false - Indicatedes that the value is above + *                      FM_RSQ_RSSI_HIGH_THRESHOLD + * @rssilint:   RSSI Detect Low. + *              true  - Indicatedes that the value is below + *                      FM_RSQ_RSSI_LOW_THRESHOLD + *              false - Indicatedes that the value is above + *                      FM_RSQ_RSSI_LOW_THRESHOLD + * @bltf:       Band Limit. + *              Set if seek command hits the band limit or wrapped to + *              the original frequency. + * @snr_ready:  SNR measurement in progress. + * @rssiready:  RSSI measurement in progress. + * @afcrl:      Set if FREQOFF >= MAX_TUNE_ERROR + * @valid:      Set if the channel is valid + *               rssi < FM_VALID_RSSI_THRESHOLD + *               snr  < FM_VALID_SNR_THRESHOLD + *               tune_error < FM_VALID_MAX_TUNE_ERROR + * @readfreq:   Current tuned frequency. + * @freqoff:    Signed frequency offset. + * @rssi:       Received Signal Strength Indicator(dBuV). + * @snr:        RF SNR Indicator(dB). + * @lassi: + * @hassi:      Low/High side Adjacent(100 kHz) Channel Strength Indicator + * @mult:       Multipath indicator + * @dev:        Who knows? But values may vary. + * @readantcap: Antenna tuning capacity value. + * @assi:       Adjacent Channel(+/- 200kHz) Strength Indicator + * @usn:        Ultrasonic Noise Inticator in -DBFS + */ +struct si476x_rsq_status_report { +	__u8 multhint, multlint; +	__u8 snrhint,  snrlint; +	__u8 rssihint, rssilint; +	__u8 bltf; +	__u8 snr_ready; +	__u8 rssiready; +	__u8 injside; +	__u8 afcrl; +	__u8 valid; + +	__u16 readfreq; +	__s8  freqoff; +	__s8  rssi; +	__s8  snr; +	__s8  issi; +	__s8  lassi, hassi; +	__s8  mult; +	__u8  dev; +	__u16 readantcap; +	__s8  assi; +	__s8  usn; + +	__u8 pilotdev; +	__u8 rdsdev; +	__u8 assidev; +	__u8 strongdev; +	__u16 rdspi; +} __packed; + +/** + * si476x_acf_status_report - ACF report results + * + * @blend_int: If set, indicates that stereo separation has crossed + * below the blend threshold as set by FM_ACF_BLEND_THRESHOLD + * @hblend_int: If set, indicates that HiBlend cutoff frequency is + * lower than threshold as set by FM_ACF_HBLEND_THRESHOLD + * @hicut_int:  If set, indicates that HiCut cutoff frequency is lower + * than the threshold set by ACF_ + + */ +struct si476x_acf_status_report { +	__u8 blend_int; +	__u8 hblend_int; +	__u8 hicut_int; +	__u8 chbw_int; +	__u8 softmute_int; +	__u8 smute; +	__u8 smattn; +	__u8 chbw; +	__u8 hicut; +	__u8 hiblend; +	__u8 pilot; +	__u8 stblend; +} __packed; + +enum si476x_fmagc { +	SI476X_FMAGC_10K_OHM	= 0, +	SI476X_FMAGC_800_OHM	= 1, +	SI476X_FMAGC_400_OHM	= 2, +	SI476X_FMAGC_200_OHM	= 4, +	SI476X_FMAGC_100_OHM	= 8, +	SI476X_FMAGC_50_OHM	= 16, +	SI476X_FMAGC_25_OHM	= 32, +	SI476X_FMAGC_12P5_OHM	= 64, +	SI476X_FMAGC_6P25_OHM	= 128, +}; + +struct si476x_agc_status_report { +	__u8 mxhi; +	__u8 mxlo; +	__u8 lnahi; +	__u8 lnalo; +	__u8 fmagc1; +	__u8 fmagc2; +	__u8 pgagain; +	__u8 fmwblang; +} __packed; + +struct si476x_rds_blockcount_report { +	__u16 expected; +	__u16 received; +	__u16 uncorrectable; +} __packed; + +#endif  /* __SI476X_REPORTS_H__ */ diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 383ac1512a3..48395a69a7e 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -26,6 +26,7 @@ enum stmpe_partnum {  	STMPE801,  	STMPE811,  	STMPE1601, +	STMPE1801,  	STMPE2401,  	STMPE2403,  	STMPE_NBR_PARTS @@ -39,6 +40,7 @@ enum {  	STMPE_IDX_CHIP_ID,  	STMPE_IDX_ICR_LSB,  	STMPE_IDX_IER_LSB, +	STMPE_IDX_ISR_LSB,  	STMPE_IDX_ISR_MSB,  	STMPE_IDX_GPMR_LSB,  	STMPE_IDX_GPSR_LSB, @@ -49,6 +51,7 @@ enum {  	STMPE_IDX_GPFER_LSB,  	STMPE_IDX_GPAFR_U_MSB,  	STMPE_IDX_IEGPIOR_LSB, +	STMPE_IDX_ISGPIOR_LSB,  	STMPE_IDX_ISGPIOR_MSB,  	STMPE_IDX_MAX,  }; diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index 6aeb6b8da64..b473577f36d 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h @@ -15,8 +15,11 @@  #ifndef __LINUX_MFD_SYSCON_H__  #define __LINUX_MFD_SYSCON_H__ +struct device_node; +  extern struct regmap *syscon_node_to_regmap(struct device_node *np);  extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); +extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s);  extern struct regmap *syscon_regmap_lookup_by_phandle(  					struct device_node *np,  					const char *property); diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 998628a2b08..3f43069413e 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -27,6 +27,7 @@  /* TPS65090 IRQs */  enum { +	TPS65090_IRQ_INTERRUPT,  	TPS65090_IRQ_VAC_STATUS_CHANGE,  	TPS65090_IRQ_VSYS_STATUS_CHANGE,  	TPS65090_IRQ_BAT_STATUS_CHANGE, diff --git a/include/linux/of.h b/include/linux/of.h index fb2002f3c7d..1b671c3809b 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -356,6 +356,11 @@ static inline struct device_node *of_find_node_by_name(struct device_node *from,  	return NULL;  } +static inline struct device_node *of_get_parent(const struct device_node *node) +{ +	return NULL; +} +  static inline bool of_have_populated_dt(void)  {  	return false; diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h index d21b33c4c6c..2e9ee4d1c67 100644 --- a/include/linux/ucb1400.h +++ b/include/linux/ucb1400.h @@ -83,15 +83,12 @@  #define UCB_ID			0x7e  #define UCB_ID_1400             0x4304 -struct ucb1400_gpio_data { -	int gpio_offset; -	int (*gpio_setup)(struct device *dev, int ngpio); -	int (*gpio_teardown)(struct device *dev, int ngpio); -}; -  struct ucb1400_gpio {  	struct gpio_chip	gc;  	struct snd_ac97		*ac97; +	int			gpio_offset; +	int			(*gpio_setup)(struct device *dev, int ngpio); +	int			(*gpio_teardown)(struct device *dev, int ngpio);  };  struct ucb1400_ts { @@ -110,6 +107,9 @@ struct ucb1400 {  struct ucb1400_pdata {  	int	irq; +	int	gpio_offset; +	int	(*gpio_setup)(struct device *dev, int ngpio); +	int	(*gpio_teardown)(struct device *dev, int ngpio);  };  static inline u16 ucb1400_reg_read(struct snd_ac97 *ac97, u16 reg) @@ -162,10 +162,4 @@ static inline void ucb1400_adc_disable(struct snd_ac97 *ac97)  unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel,  			      int adcsync); -#ifdef CONFIG_GPIO_UCB1400 -void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data); -#else -static inline void ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) {} -#endif -  #endif  |