diff options
Diffstat (limited to 'drivers/mfd')
121 files changed, 6709 insertions, 3818 deletions
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index a65447d6560..841717a2842 100644 --- a/drivers/mfd/88pm800.c +++ b/drivers/mfd/88pm800.c @@ -148,7 +148,7 @@ static struct resource onkey_resources[] = {  	 },  }; -static struct mfd_cell onkey_devs[] = { +static const struct mfd_cell onkey_devs[] = {  	{  	 .name = "88pm80x-onkey",  	 .num_resources = 1, @@ -157,7 +157,7 @@ static struct mfd_cell onkey_devs[] = {  	 },  }; -static struct mfd_cell regulator_devs[] = { +static const struct mfd_cell regulator_devs[] = {  	{  	 .name = "88pm80x-regulator",  	 .id = -1, @@ -571,7 +571,7 @@ static int pm800_probe(struct i2c_client *client,  	ret = pm800_pages_init(chip);  	if (ret) {  		dev_err(&client->dev, "pm800_pages_init failed!\n"); -		goto err_page_init; +		goto err_device_init;  	}  	ret = device_800_init(chip, pdata); @@ -587,7 +587,6 @@ static int pm800_probe(struct i2c_client *client,  err_device_init:  	pm800_pages_exit(chip); -err_page_init:  err_subchip_alloc:  	pm80x_deinit();  out_init: diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c index 8a5b6ffb5af..64751c2a1ac 100644 --- a/drivers/mfd/88pm805.c +++ b/drivers/mfd/88pm805.c @@ -77,7 +77,7 @@ static struct resource codec_resources[] = {  	 },  }; -static struct mfd_cell codec_devs[] = { +static const struct mfd_cell codec_devs[] = {  	{  	 .name = "88pm80x-codec",  	 .num_resources = ARRAY_SIZE(codec_resources), diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 7ebe9ef1eba..bcfc9e85b4a 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -1179,12 +1179,18 @@ static int pm860x_probe(struct i2c_client *client,  		chip->companion_addr = pdata->companion_addr;  		chip->companion = i2c_new_dummy(chip->client->adapter,  						chip->companion_addr); +		if (!chip->companion) { +			dev_err(&client->dev, +				"Failed to allocate I2C companion device\n"); +			return -ENODEV; +		}  		chip->regmap_companion = regmap_init_i2c(chip->companion,  							&pm860x_regmap_config);  		if (IS_ERR(chip->regmap_companion)) {  			ret = PTR_ERR(chip->regmap_companion);  			dev_err(&chip->companion->dev,  				"Failed to allocate register map: %d\n", ret); +			i2c_unregister_device(chip->companion);  			return ret;  		}  		i2c_set_clientdata(chip->companion, chip); @@ -1247,7 +1253,7 @@ static struct i2c_driver pm860x_driver = {  		.name	= "88PM860x",  		.owner	= THIS_MODULE,  		.pm     = &pm860x_pm_ops, -		.of_match_table	= of_match_ptr(pm860x_dt_ids), +		.of_match_table	= pm860x_dt_ids,  	},  	.probe		= pm860x_probe,  	.remove		= pm860x_remove, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 914c3d142f7..6cc4b6acc22 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -27,6 +27,18 @@ config MFD_AS3711  	help  	  Support for the AS3711 PMIC from AMS +config MFD_AS3722 +	bool "ams AS3722 Power Management IC" +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ +	depends on I2C=y && OF +	help +	  The ams AS3722 is a compact system PMU suitable for mobile phones, +	  tablets etc. It has 4 DC/DC step-down regulators, 3 DC/DC step-down +	  controllers, 11 LDOs, RTC, automatic battery, temperature and +	  over current monitoring, GPIOs, ADC and a watchdog. +  config PMIC_ADP5520  	bool "Analog Devices ADP5520/01 MFD PMIC Core Support"  	depends on I2C=y @@ -47,6 +59,26 @@ config MFD_AAT2870_CORE  	  additional drivers must be enabled in order to use the  	  functionality of the device. +config MFD_BCM590XX +	tristate "Broadcom BCM590xx PMUs" +	select MFD_CORE +	select REGMAP_I2C +	depends on I2C +	help +	  Support for the BCM590xx PMUs from Broadcom + +config MFD_AXP20X +	bool "X-Powers AXP20X" +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ +	depends on I2C=y +	help +	  If you say Y here you get support for the X-Powers AXP202 and AXP209. +	  This driver include only the core APIs. You have to select individual +	  components like regulators or the PEK (Power Enable Key) under the +	  corresponding menus. +  config MFD_CROS_EC  	tristate "ChromeOS Embedded Controller"  	select MFD_CORE @@ -68,7 +100,7 @@ config MFD_CROS_EC_I2C  config MFD_CROS_EC_SPI  	tristate "ChromeOS Embedded Controller (SPI)" -	depends on MFD_CROS_EC && SPI +	depends on MFD_CROS_EC && SPI && OF  	---help---  	  If you say Y here, you get support for talking to the ChromeOS EC @@ -88,7 +120,7 @@ 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 +	  Say yes here to add 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 @@ -151,14 +183,10 @@ config MFD_DA9063  	  Additional drivers must be enabled in order to use the functionality  	  of the device. -config MFD_MC13783 -	tristate -  config MFD_MC13XXX  	tristate  	depends on (SPI_MASTER || I2C)  	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, @@ -234,6 +262,16 @@ config MFD_INTEL_MSIC  	  Passage) chip. This chip embeds audio, battery, GPIO, etc.  	  devices used in Intel Medfield platforms. +config MFD_IPAQ_MICRO +	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" +	depends on SA1100_H3100 || SA1100_H3600 +	select MFD_CORE +	help +	  Select this to get support for the Microcontroller found in +	  the Compaq iPAQ handheld computers. This is an Atmel +	  AT90LS8535 microcontroller flashed with a special iPAQ +	  firmware using the custom protocol implemented in this driver. +  config MFD_JANZ_CMODIO  	tristate "Janz CMOD-IO PCI MODULbus Carrier Board"  	select MFD_CORE @@ -262,13 +300,18 @@ config MFD_KEMPLD  	  device may provide functions like watchdog, GPIO, UART and I2C bus.  	  The following modules are supported: +		* COMe-bHL6  		* COMe-bIP#  		* COMe-bPC2 (ETXexpress-PC)  		* COMe-bSC# (ETXexpress-SC T#) +		* COMe-cBT6  		* COMe-cCT6  		* COMe-cDC2 (microETXexpress-DC) +		* COMe-cHL6  		* COMe-cPC2 (microETXexpress-PC) +		* COMe-mBT10  		* COMe-mCT10 +		* COMe-mTT10 (nanoETXexpress-TT)  		* ETX-OH  	  This driver can also be built as a module. If so, the module @@ -309,6 +352,20 @@ config MFD_88PM860X  	  select individual components like voltage regulators, RTC and  	  battery-charger under the corresponding menus. +config MFD_MAX14577 +	bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support" +	depends on I2C=y +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ +	select IRQ_DOMAIN +	help +	  Say yes here to add support for Maxim Semiconductor MAX14577 and +	  MAX77836 Micro-USB ICs with battery charger. +	  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_MAX77686  	bool "Maxim Semiconductor MAX77686 PMIC Support"  	depends on I2C=y @@ -316,7 +373,7 @@ config MFD_MAX77686  	select REGMAP_I2C  	select IRQ_DOMAIN  	help -	  Say yes here to support for Maxim Semiconductor MAX77686. +	  Say yes here to add 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 @@ -328,7 +385,7 @@ config MFD_MAX77693  	select MFD_CORE  	select REGMAP_I2C  	help -	  Say yes here to support for Maxim Semiconductor MAX77693. +	  Say yes here to add 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; @@ -342,7 +399,7 @@ config MFD_MAX8907  	select REGMAP_I2C  	select REGMAP_IRQ  	help -	  Say yes here to support for Maxim Semiconductor MAX8907. This is +	  Say yes here to add 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. @@ -352,7 +409,7 @@ config MFD_MAX8925  	depends on I2C=y  	select MFD_CORE  	help -	  Say yes here to support for Maxim Semiconductor MAX8925. This is +	  Say yes here to add 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. @@ -363,7 +420,7 @@ config MFD_MAX8997  	select MFD_CORE  	select IRQ_DOMAIN  	help -	  Say yes here to support for Maxim Semiconductor MAX8997/8966. +	  Say yes here to add 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; @@ -376,7 +433,7 @@ config MFD_MAX8998  	select MFD_CORE  	select IRQ_DOMAIN  	help -	  Say yes here to support for Maxim Semiconductor MAX8998 and +	  Say yes here to add 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 @@ -452,10 +509,11 @@ config MFD_PM8XXX  config MFD_PM8921_CORE  	tristate "Qualcomm PM8921 PMIC chip" -	depends on (ARCH_MSM || HEXAGON) -	depends on BROKEN +	depends on (ARM || HEXAGON) +	select IRQ_DOMAIN  	select MFD_CORE  	select MFD_PM8XXX +	select REGMAP  	help  	  If you say yes to this option, support will be included for the  	  built-in PM8921 PMIC chip. @@ -466,16 +524,6 @@ config MFD_PM8921_CORE  	  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 @@ -495,6 +543,16 @@ config MFD_RTSX_PCI  	  types of memory cards, such as Memory Stick, Memory Stick Pro,  	  Secure Digital and MultiMediaCard. +config MFD_RTSX_USB +	tristate "Realtek USB card reader" +	depends on USB +	select MFD_CORE +	help +	  Select this option to get support for Realtek USB 2.0 card readers +	  including RTS5129, RTS5139, RTS5179 and RTS5170. +	  Realtek card reader supports access to many types of memory cards, +	  such as Memory Stick Pro, Secure Digital and MultiMediaCard. +  config MFD_RC5T583  	bool "Ricoh RC5T583 Power Management system device"  	depends on I2C=y @@ -639,6 +697,7 @@ config MFD_DB8500_PRCMU  config MFD_STMPE  	bool "STMicroelectronics STMPE"  	depends on (I2C=y || SPI_MASTER=y) +	depends on OF  	select MFD_CORE  	help  	  Support for the STMPE family of I/O Expanders from @@ -664,14 +723,14 @@ menu "STMicroelectronics STMPE Interface Drivers"  depends on MFD_STMPE  config STMPE_I2C -	bool "STMicroelectronics STMPE I2C Inteface" +	bool "STMicroelectronics STMPE I2C Interface"  	depends on I2C=y  	default y  	help  	  This is used to enable I2C interface of STMPE  config STMPE_SPI -	bool "STMicroelectronics STMPE SPI Inteface" +	bool "STMicroelectronics STMPE SPI Interface"  	depends on SPI_MASTER  	help  	  This is used to enable SPI interface of STMPE @@ -683,6 +742,14 @@ config MFD_STA2X11  	select MFD_CORE  	select REGMAP_MMIO +config MFD_SUN6I_PRCM +	bool "Allwinner A31 PRCM controller" +	depends on ARCH_SUNXI +	select MFD_CORE +	help +	  Support for the PRCM (Power/Reset/Clock Management) unit available +	  in A31 SoC. +  config MFD_SYSCON  	bool "System Controller Register R/W Based on Regmap"  	select REGMAP_MMIO @@ -693,6 +760,7 @@ config MFD_SYSCON  config MFD_DAVINCI_VOICECODEC  	tristate  	select MFD_CORE +	select REGMAP_MMIO  config MFD_TI_AM335X_TSCADC  	tristate "TI ADC / Touch Screen chip support" @@ -713,6 +781,17 @@ config MFD_DM355EVM_MSP  	  boards.  MSP430 firmware manages resets and power sequencing,  	  inputs from buttons and the IR remote, LEDs, an RTC, and more. +config MFD_LP3943 +	tristate "TI/National Semiconductor LP3943 MFD Driver" +	depends on I2C +	select MFD_CORE +	select REGMAP_I2C +	help +	  Support for the TI/National Semiconductor LP3943. +	  This driver consists of GPIO and PWM drivers. +	  With these functionalities, it can be used for LED string control or +	  general usage such like a GPIO controller and a PWM controller. +  config MFD_LP8788  	bool "TI LP8788 Power Management Unit Driver"  	depends on I2C=y @@ -742,17 +821,6 @@ config MFD_PALMAS  	  If you say yes here you get support for the Palmas  	  series of PMIC chips from Texas Instruments. -config MFD_TI_SSP -	tristate "TI Sequencer Serial Port support" -	depends on ARCH_DAVINCI_TNETV107X -	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 "TI TPS61050/61052 Boost Converters"  	depends on I2C @@ -821,6 +889,22 @@ config MFD_TPS65217  	  This driver can also be built as a module.  If so, the module  	  will be called tps65217. +config MFD_TPS65218 +	tristate "TI TPS65218 Power Management chips" +	depends on I2C +	select MFD_CORE +	select REGMAP_I2C +	select REGMAP_IRQ +	help +	  If you say yes here you get support for the TPS65218 series of +	  Power Management chips. +	  These include voltage regulators, gpio and other features +	  that are often used in portable devices. Only regulator +	  component is currently supported. + +	  This driver can also be built as a module.  If so, the module +	  will be called tps65218. +  config MFD_TPS6586X  	bool "TI TPS6586x Power Management chips"  	depends on I2C=y @@ -903,16 +987,6 @@ config TWL4030_CORE  	  high speed USB OTG transceiver, an audio codec (on most  	  versions) and many other features. -config TWL4030_MADC -	tristate "TI TWL4030 MADC" -	depends on TWL4030_CORE -	help -	This driver provides support for triton TWL4030-MADC. The -	driver supports both RT and SW conversion methods. - -	This driver can be built as a module. If so it will be -	named twl4030-madc -  config TWL4030_POWER  	bool "TI TWL4030 power resources"  	depends on TWL4030_CORE && ARM @@ -1151,8 +1225,15 @@ config MFD_WM8994  	  core support for the WM8994, in order to use the actual  	  functionaltiy of the device other drivers must be enabled. -endmenu -endif +config MFD_STW481X +	tristate "Support for ST Microelectronics STw481x" +	depends on I2C && ARCH_NOMADIK +	select REGMAP_I2C +	select MFD_CORE +	help +	  Select this option to enable the STw481x chip driver used +	  in various ST Microelectronics and ST-Ericsson embedded +	  Nomadik series.  menu "Multimedia Capabilities Port drivers"  	depends on ARCH_SA1100 @@ -1168,7 +1249,7 @@ config MCP_SA11X0  # Chip drivers  config MCP_UCB1200 -	bool "Support for UCB1200 / UCB1300" +	tristate "Support for UCB1200 / UCB1300"  	depends on MCP_SA11X0  	select MCP @@ -1178,9 +1259,17 @@ config MCP_UCB1200_TS  endmenu -config VEXPRESS_CONFIG -	bool "ARM Versatile Express platform infrastructure" -	depends on ARM || ARM64 +config MFD_VEXPRESS_SYSREG +	bool "Versatile Express System Registers" +	depends on VEXPRESS_CONFIG && GPIOLIB +	default y +	select CLKSRC_MMIO +	select GPIO_GENERIC_PLATFORM +	select MFD_CORE +	select MFD_SYSCON  	help -	  Platform configuration infrastructure for the ARM Ltd. -	  Versatile Express. +	  System Registers are the platform configuration block +	  on the ARM Ltd. Versatile Express board. + +endmenu +endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 15b905c6553..8afedba535c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,12 +8,14 @@ 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_BCM590XX)	+= bcm590xx.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 rts5249.o  obj-$(CONFIG_MFD_RTSX_PCI)	+= rtsx_pci.o +obj-$(CONFIG_MFD_RTSX_USB)	+= rtsx_usb.o  obj-$(CONFIG_HTC_EGPIO)		+= htc-egpio.o  obj-$(CONFIG_HTC_PASIC3)	+= htc-pasic3.o @@ -21,13 +23,13 @@ obj-$(CONFIG_HTC_I2CPLD)	+= htc-i2cpld.o  obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)	+= davinci_voicecodec.o  obj-$(CONFIG_MFD_DM355EVM_MSP)	+= dm355evm_msp.o -obj-$(CONFIG_MFD_TI_SSP)	+= ti-ssp.o  obj-$(CONFIG_MFD_TI_AM335X_TSCADC)	+= ti_am335x_tscadc.o  obj-$(CONFIG_MFD_STA2X11)	+= sta2x11-mfd.o  obj-$(CONFIG_MFD_STMPE)		+= stmpe.o  obj-$(CONFIG_STMPE_I2C)		+= stmpe-i2c.o  obj-$(CONFIG_STMPE_SPI)		+= stmpe-spi.o +obj-$(CONFIG_MFD_SUN6I_PRCM)	+= sun6i-prcm.o  obj-$(CONFIG_MFD_TC3589X)	+= tc3589x.o  obj-$(CONFIG_MFD_T7L66XB)	+= t7l66xb.o tmio_core.o  obj-$(CONFIG_MFD_TC6387XB)	+= tc6387xb.o tmio_core.o @@ -62,6 +64,7 @@ obj-$(CONFIG_TPS6105X)		+= tps6105x.o  obj-$(CONFIG_TPS65010)		+= tps65010.o  obj-$(CONFIG_TPS6507X)		+= tps6507x.o  obj-$(CONFIG_MFD_TPS65217)	+= tps65217.o +obj-$(CONFIG_MFD_TPS65218)	+= tps65218.o  obj-$(CONFIG_MFD_TPS65910)	+= tps65910.o  tps65912-objs                   := tps65912-core.o tps65912-irq.o  obj-$(CONFIG_MFD_TPS65912)	+= tps65912.o @@ -71,7 +74,6 @@ obj-$(CONFIG_MFD_TPS80031)	+= tps80031.o  obj-$(CONFIG_MENELAUS)		+= menelaus.o  obj-$(CONFIG_TWL4030_CORE)	+= twl-core.o twl4030-irq.o twl6030-irq.o -obj-$(CONFIG_TWL4030_MADC)      += twl4030-madc.o  obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o @@ -101,7 +103,9 @@ obj-$(CONFIG_PMIC_DA9052)	+= da9052-irq.o  obj-$(CONFIG_PMIC_DA9052)	+= da9052-core.o  obj-$(CONFIG_MFD_DA9052_SPI)	+= da9052-spi.o  obj-$(CONFIG_MFD_DA9052_I2C)	+= da9052-i2c.o +obj-$(CONFIG_MFD_AXP20X)	+= axp20x.o +obj-$(CONFIG_MFD_LP3943)	+= lp3943.o  obj-$(CONFIG_MFD_LP8788)	+= lp8788.o lp8788-irq.o  da9055-objs			:= da9055-core.o da9055-i2c.o @@ -110,6 +114,7 @@ obj-$(CONFIG_MFD_DA9055)	+= da9055.o  da9063-objs			:= da9063-core.o da9063-irq.o da9063-i2c.o  obj-$(CONFIG_MFD_DA9063)	+= da9063.o +obj-$(CONFIG_MFD_MAX14577)	+= max14577.o  obj-$(CONFIG_MFD_MAX77686)	+= max77686.o max77686-irq.o  obj-$(CONFIG_MFD_MAX77693)	+= max77693.o max77693-irq.o  obj-$(CONFIG_MFD_MAX8907)	+= max8907.o @@ -148,7 +153,6 @@ 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 ssbi.o -obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o  obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o  obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o  obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o @@ -159,6 +163,9 @@ obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o  obj-$(CONFIG_MFD_SEC_CORE)	+= sec-core.o sec-irq.o  obj-$(CONFIG_MFD_SYSCON)	+= syscon.o  obj-$(CONFIG_MFD_LM3533)	+= lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG)	+= vexpress-config.o vexpress-sysreg.o +obj-$(CONFIG_MFD_VEXPRESS_SYSREG)	+= vexpress-sysreg.o  obj-$(CONFIG_MFD_RETU)		+= retu-mfd.o  obj-$(CONFIG_MFD_AS3711)	+= as3711.o +obj-$(CONFIG_MFD_AS3722)	+= as3722.o +obj-$(CONFIG_MFD_STW481X)	+= stw481x.o +obj-$(CONFIG_MFD_IPAQ_MICRO)	+= ipaq-micro.o diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index 6f68472e0ca..14d9542a4ee 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -293,7 +293,7 @@ static ssize_t aat2870_reg_write_file(struct file *file,  	unsigned long addr, val;  	int ret; -	buf_size = min(count, (sizeof(buf)-1)); +	buf_size = min(count, (size_t)(sizeof(buf)-1));  	if (copy_from_user(buf, user_buf, buf_size)) {  		dev_err(aat2870->dev, "Failed to copy from user\n");  		return -EFAULT; diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index b6c2cdc7609..cf2e6a198c6 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -491,7 +491,7 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,  		if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F)  			line += 1; -		handle_nested_irq(ab8500->irq_base + line); +		handle_nested_irq(irq_create_mapping(ab8500->domain, line));  	}  	return 0; @@ -591,8 +591,8 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)  		num_irqs = AB8500_NR_IRQS;  	/* If ->irq_base is zero this will give a linear mapping */ -	ab8500->domain = irq_domain_add_simple(NULL, -			num_irqs, ab8500->irq_base, +	ab8500->domain = irq_domain_add_simple(ab8500->dev->of_node, +			num_irqs, 0,  			&ab8500_irq_ops, ab8500);  	if (!ab8500->domain) { @@ -1017,7 +1017,7 @@ static struct resource ab8500_temp_resources[] = {  	},  }; -static struct mfd_cell ab8500_bm_devs[] = { +static const struct mfd_cell ab8500_bm_devs[] = {  	{  		.name = "ab8500-charger",  		.of_compatible = "stericsson,ab8500-charger", @@ -1052,7 +1052,7 @@ static struct mfd_cell ab8500_bm_devs[] = {  	},  }; -static struct mfd_cell ab8500_devs[] = { +static const struct mfd_cell ab8500_devs[] = {  #ifdef CONFIG_DEBUG_FS  	{  		.name = "ab8500-debug", @@ -1143,7 +1143,7 @@ static struct mfd_cell ab8500_devs[] = {  	},  }; -static struct mfd_cell ab9540_devs[] = { +static const struct mfd_cell ab9540_devs[] = {  #ifdef CONFIG_DEBUG_FS  	{  		.name = "ab8500-debug", @@ -1214,7 +1214,7 @@ static struct mfd_cell ab9540_devs[] = {  };  /* Device list for ab8505  */ -static struct mfd_cell ab8505_devs[] = { +static const struct mfd_cell ab8505_devs[] = {  #ifdef CONFIG_DEBUG_FS  	{  		.name = "ab8500-debug", @@ -1275,7 +1275,7 @@ static struct mfd_cell ab8505_devs[] = {  	},  }; -static struct mfd_cell ab8540_devs[] = { +static const struct mfd_cell ab8540_devs[] = {  #ifdef CONFIG_DEBUG_FS  	{  		.name = "ab8500-debug", @@ -1339,7 +1339,7 @@ static struct mfd_cell ab8540_devs[] = {  	},  }; -static struct mfd_cell ab8540_cut1_devs[] = { +static const struct mfd_cell ab8540_cut1_devs[] = {  	{  		.name = "ab8500-rtc",  		.of_compatible = "stericsson,ab8500-rtc", @@ -1348,7 +1348,7 @@ static struct mfd_cell ab8540_cut1_devs[] = {  	},  }; -static struct mfd_cell ab8540_cut2_devs[] = { +static const struct mfd_cell ab8540_cut2_devs[] = {  	{  		.name = "ab8540-rtc",  		.of_compatible = "stericsson,ab8540-rtc", @@ -1583,14 +1583,13 @@ static int ab8500_probe(struct platform_device *pdev)  	if (!ab8500)  		return -ENOMEM; -	if (plat) -		ab8500->irq_base = plat->irq_base; -  	ab8500->dev = &pdev->dev;  	resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -	if (!resource) +	if (!resource) { +		dev_err(&pdev->dev, "no IRQ resource\n");  		return -ENODEV; +	}  	ab8500->irq = resource->start; @@ -1612,8 +1611,10 @@ static int ab8500_probe(struct platform_device *pdev)  	else {  		ret = get_register_interruptible(ab8500, AB8500_MISC,  			AB8500_IC_NAME_REG, &value); -		if (ret < 0) +		if (ret < 0) { +			dev_err(&pdev->dev, "could not probe HW\n");  			return ret; +		}  		ab8500->version = value;  	} @@ -1759,30 +1760,30 @@ static int ab8500_probe(struct platform_device *pdev)  	if (is_ab9540(ab8500))  		ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,  				ARRAY_SIZE(ab9540_devs), NULL, -				ab8500->irq_base, ab8500->domain); +				0, ab8500->domain);  	else if (is_ab8540(ab8500)) {  		ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,  			      ARRAY_SIZE(ab8540_devs), NULL, -			      ab8500->irq_base, NULL); +			      0, ab8500->domain);  		if (ret)  			return ret;  		if (is_ab8540_1p2_or_earlier(ab8500))  			ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut1_devs,  			      ARRAY_SIZE(ab8540_cut1_devs), NULL, -			      ab8500->irq_base, NULL); +			      0, ab8500->domain);  		else /* ab8540 >= cut2 */  			ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut2_devs,  			      ARRAY_SIZE(ab8540_cut2_devs), NULL, -			      ab8500->irq_base, NULL); +			      0, ab8500->domain);  	} else if (is_ab8505(ab8500))  		ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,  			      ARRAY_SIZE(ab8505_devs), NULL, -			      ab8500->irq_base, ab8500->domain); +			      0, ab8500->domain);  	else  		ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,  				ARRAY_SIZE(ab8500_devs), NULL, -				ab8500->irq_base, ab8500->domain); +				0, ab8500->domain);  	if (ret)  		return ret; @@ -1790,7 +1791,7 @@ static int ab8500_probe(struct platform_device *pdev)  		/* Add battery management devices */  		ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,  				      ARRAY_SIZE(ab8500_bm_devs), NULL, -				      ab8500->irq_base, ab8500->domain); +				      0, ab8500->domain);  		if (ret)  			dev_err(ab8500->dev, "error adding bm devices\n");  	} diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index e33e385af0a..d1a22aae2df 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -1600,7 +1600,6 @@ static int ab8500_interrupts_print(struct seq_file *s, void *p)  	for (line = 0; line < num_interrupt_lines; line++) {  		struct irq_desc *desc = irq_to_desc(line + irq_first); -		struct irqaction *action = desc->action;  		seq_printf(s, "%3i:  %6i %4i", line,  			   num_interrupts[line], @@ -1608,7 +1607,9 @@ static int ab8500_interrupts_print(struct seq_file *s, void *p)  		if (desc && desc->name)  			seq_printf(s, "-%-8s", desc->name); -		if (action) { +		if (desc && desc->action) { +			struct irqaction *action = desc->action; +  			seq_printf(s, "  %s", action->name);  			while ((action = action->next) != NULL)  				seq_printf(s, ", %s", action->name); diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c index f3a15aa54d7..fe418995108 100644 --- a/drivers/mfd/abx500-core.c +++ b/drivers/mfd/abx500-core.c @@ -151,22 +151,6 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)  }  EXPORT_SYMBOL(abx500_startup_irq_enabled); -void abx500_dump_all_banks(void) -{ -	struct abx500_ops *ops; -	struct device dummy_child = {NULL}; -	struct abx500_device_entry *dev_entry; - -	list_for_each_entry(dev_entry, &abx500_list, list) { -		dummy_child.parent = dev_entry->dev; -		ops = &dev_entry->ops; - -		if ((ops != NULL) && (ops->dump_all_banks != NULL)) -			ops->dump_all_banks(&dummy_child); -	} -} -EXPORT_SYMBOL(abx500_dump_all_banks); -  MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");  MODULE_DESCRIPTION("ABX500 core driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 62501553d63..f495b8b57dd 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -20,7 +20,6 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/platform_device.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/interrupt.h>  #include <linux/irq.h> diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 5ac3aa48473..cfc191abae4 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -251,8 +251,6 @@ 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) { @@ -322,8 +320,6 @@ err_fll:  			err);  	} -	regcache_cache_bypass(arizona->regmap, false); -  	if (ret != 0)  		return ret;  	else @@ -512,19 +508,31 @@ int arizona_of_get_type(struct device *dev)  }  EXPORT_SYMBOL_GPL(arizona_of_get_type); +int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, +			      bool mandatory) +{ +	int gpio; + +	gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0); +	if (gpio < 0) { +		if (mandatory) +			dev_err(arizona->dev, +				"Mandatory DT gpio %s missing/malformed: %d\n", +				prop, gpio); + +		gpio = 0; +	} + +	return gpio; +} +EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio); +  static int arizona_of_get_core_pdata(struct arizona *arizona)  { +	struct arizona_pdata *pdata = &arizona->pdata;  	int ret, i; -	arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node, -						 "wlf,reset", 0); -	if (arizona->pdata.reset < 0) -		arizona->pdata.reset = 0; - -	arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node, -						  "wlf,ldoena", 0); -	if (arizona->pdata.ldoena < 0) -		arizona->pdata.ldoena = 0; +	pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true);  	ret = of_property_read_u32_array(arizona->dev->of_node,  					 "wlf,gpio-defaults", @@ -540,7 +548,7 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)  		for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {  			if (arizona->pdata.gpio_defaults[i] > 0xffff)  				arizona->pdata.gpio_defaults[i] = 0; -			if (arizona->pdata.gpio_defaults[i] == 0) +			else if (arizona->pdata.gpio_defaults[i] == 0)  				arizona->pdata.gpio_defaults[i] = 0x10000;  		}  	} else { @@ -565,35 +573,62 @@ static inline int arizona_of_get_core_pdata(struct arizona *arizona)  }  #endif -static struct mfd_cell early_devs[] = { +static const struct mfd_cell early_devs[] = {  	{ .name = "arizona-ldo1" },  }; -static struct mfd_cell wm5102_devs[] = { +static const char *wm5102_supplies[] = { +	"DBVDD2", +	"DBVDD3", +	"CPVDD", +	"SPKVDDL", +	"SPKVDDR", +	"MICVDD", +}; + +static const struct mfd_cell wm5102_devs[] = {  	{ .name = "arizona-micsupp" },  	{ .name = "arizona-extcon" },  	{ .name = "arizona-gpio" },  	{ .name = "arizona-haptics" },  	{ .name = "arizona-pwm" }, -	{ .name = "wm5102-codec" }, +	{ +		.name = "wm5102-codec", +		.parent_supplies = wm5102_supplies, +		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies), +	},  }; -static struct mfd_cell wm5110_devs[] = { +static const struct mfd_cell wm5110_devs[] = {  	{ .name = "arizona-micsupp" },  	{ .name = "arizona-extcon" },  	{ .name = "arizona-gpio" },  	{ .name = "arizona-haptics" },  	{ .name = "arizona-pwm" }, -	{ .name = "wm5110-codec" }, +	{ +		.name = "wm5110-codec", +		.parent_supplies = wm5102_supplies, +		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies), +	},  }; -static struct mfd_cell wm8997_devs[] = { +static const char *wm8997_supplies[] = { +	"DBVDD2", +	"CPVDD", +	"SPKVDD", +}; + +static const struct mfd_cell wm8997_devs[] = {  	{ .name = "arizona-micsupp" },  	{ .name = "arizona-extcon" },  	{ .name = "arizona-gpio" },  	{ .name = "arizona-haptics" },  	{ .name = "arizona-pwm" }, -	{ .name = "wm8997-codec" }, +	{ +		.name = "wm8997-codec", +		.parent_supplies = wm8997_supplies, +		.num_parent_supplies = ARRAY_SIZE(wm8997_supplies), +	},  };  int arizona_dev_init(struct arizona *arizona) @@ -607,11 +642,11 @@ int arizona_dev_init(struct arizona *arizona)  	dev_set_drvdata(arizona->dev, arizona);  	mutex_init(&arizona->clk_lock); -	arizona_of_get_core_pdata(arizona); -  	if (dev_get_platdata(arizona->dev))  		memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),  		       sizeof(arizona->pdata)); +	else +		arizona_of_get_core_pdata(arizona);  	regcache_cache_only(arizona->regmap, true); @@ -630,6 +665,9 @@ int arizona_dev_init(struct arizona *arizona)  		return -EINVAL;  	} +	/* Mark DCVDD as external, LDO1 driver will clear if internal */ +	arizona->external_dcvdd = true; +  	ret = mfd_add_devices(arizona->dev, -1, early_devs,  			      ARRAY_SIZE(early_devs), NULL, 0, NULL);  	if (ret != 0) { @@ -829,14 +867,6 @@ int arizona_dev_init(struct arizona *arizona)  			     arizona->pdata.gpio_defaults[i]);  	} -	/* -	 * LDO1 can only be used to supply DCVDD so if it has no -	 * consumers then DCVDD is supplied externally. -	 */ -	if (arizona->pdata.ldo1 && -	    arizona->pdata.ldo1->num_consumer_supplies == 0) -		arizona->external_dcvdd = true; -  	pm_runtime_set_autosuspend_delay(arizona->dev, 100);  	pm_runtime_use_autosuspend(arizona->dev);  	pm_runtime_enable(arizona->dev); diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 51dbabf7c02..beccb790c9b 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -17,6 +17,7 @@  #include <linux/regmap.h>  #include <linux/regulator/consumer.h>  #include <linux/slab.h> +#include <linux/of.h>  #include <linux/mfd/arizona/core.h> diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 88758ab9402..17102f58910 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -285,7 +285,7 @@ int arizona_irq_init(struct arizona *arizona)  				  IRQF_ONESHOT, -1, irq,  				  &arizona->irq_chip);  	if (ret != 0) { -		dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret); +		dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret);  		goto err_aod;  	} diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 47be7b35b5c..1ca554b18be 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -17,6 +17,7 @@  #include <linux/regulator/consumer.h>  #include <linux/slab.h>  #include <linux/spi/spi.h> +#include <linux/of.h>  #include <linux/mfd/arizona/core.h> diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index abd3ab7c090..d9706ede8d3 100644 --- a/drivers/mfd/as3711.c +++ b/drivers/mfd/as3711.c @@ -17,6 +17,7 @@  #include <linux/mfd/as3711.h>  #include <linux/mfd/core.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/regmap.h>  #include <linux/slab.h> @@ -113,7 +114,7 @@ static const struct regmap_config as3711_regmap_config = {  };  #ifdef CONFIG_OF -static struct of_device_id as3711_of_match[] = { +static const struct of_device_id as3711_of_match[] = {  	{.compatible = "ams,as3711",},  	{}  }; diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c new file mode 100644 index 00000000000..39fa554f13b --- /dev/null +++ b/drivers/mfd/as3722.c @@ -0,0 +1,453 @@ +/* + * Core driver for ams AS3722 PMICs + * + * Copyright (C) 2013 AMS AG + * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. + * + * Author: Florian Lobmaier <florian.lobmaier@ams.com> + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/core.h> +#include <linux/mfd/as3722.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define AS3722_DEVICE_ID	0x0C + +static const struct resource as3722_rtc_resource[] = { +	{ +		.name = "as3722-rtc-alarm", +		.start = AS3722_IRQ_RTC_ALARM, +		.end = AS3722_IRQ_RTC_ALARM, +		.flags = IORESOURCE_IRQ, +	}, +}; + +static const struct resource as3722_adc_resource[] = { +	{ +		.name = "as3722-adc", +		.start = AS3722_IRQ_ADC, +		.end = AS3722_IRQ_ADC, +		.flags = IORESOURCE_IRQ, +	}, +}; + +static const struct mfd_cell as3722_devs[] = { +	{ +		.name = "as3722-pinctrl", +	}, +	{ +		.name = "as3722-regulator", +	}, +	{ +		.name = "as3722-rtc", +		.num_resources = ARRAY_SIZE(as3722_rtc_resource), +		.resources = as3722_rtc_resource, +	}, +	{ +		.name = "as3722-adc", +		.num_resources = ARRAY_SIZE(as3722_adc_resource), +		.resources = as3722_adc_resource, +	}, +	{ +		.name = "as3722-power-off", +	}, +	{ +		.name = "as3722-wdt", +	}, +}; + +static const struct regmap_irq as3722_irqs[] = { +	/* INT1 IRQs */ +	[AS3722_IRQ_LID] = { +		.mask = AS3722_INTERRUPT_MASK1_LID, +	}, +	[AS3722_IRQ_ACOK] = { +		.mask = AS3722_INTERRUPT_MASK1_ACOK, +	}, +	[AS3722_IRQ_ENABLE1] = { +		.mask = AS3722_INTERRUPT_MASK1_ENABLE1, +	}, +	[AS3722_IRQ_OCCUR_ALARM_SD0] = { +		.mask = AS3722_INTERRUPT_MASK1_OCURR_ALARM_SD0, +	}, +	[AS3722_IRQ_ONKEY_LONG_PRESS] = { +		.mask = AS3722_INTERRUPT_MASK1_ONKEY_LONG, +	}, +	[AS3722_IRQ_ONKEY] = { +		.mask = AS3722_INTERRUPT_MASK1_ONKEY, +	}, +	[AS3722_IRQ_OVTMP] = { +		.mask = AS3722_INTERRUPT_MASK1_OVTMP, +	}, +	[AS3722_IRQ_LOWBAT] = { +		.mask = AS3722_INTERRUPT_MASK1_LOWBAT, +	}, + +	/* INT2 IRQs */ +	[AS3722_IRQ_SD0_LV] = { +		.mask = AS3722_INTERRUPT_MASK2_SD0_LV, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_SD1_LV] = { +		.mask = AS3722_INTERRUPT_MASK2_SD1_LV, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_SD2_LV] = { +		.mask = AS3722_INTERRUPT_MASK2_SD2345_LV, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_PWM1_OV_PROT] = { +		.mask = AS3722_INTERRUPT_MASK2_PWM1_OV_PROT, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_PWM2_OV_PROT] = { +		.mask = AS3722_INTERRUPT_MASK2_PWM2_OV_PROT, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_ENABLE2] = { +		.mask = AS3722_INTERRUPT_MASK2_ENABLE2, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_SD6_LV] = { +		.mask = AS3722_INTERRUPT_MASK2_SD6_LV, +		.reg_offset = 1, +	}, +	[AS3722_IRQ_RTC_REP] = { +		.mask = AS3722_INTERRUPT_MASK2_RTC_REP, +		.reg_offset = 1, +	}, + +	/* INT3 IRQs */ +	[AS3722_IRQ_RTC_ALARM] = { +		.mask = AS3722_INTERRUPT_MASK3_RTC_ALARM, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_GPIO1] = { +		.mask = AS3722_INTERRUPT_MASK3_GPIO1, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_GPIO2] = { +		.mask = AS3722_INTERRUPT_MASK3_GPIO2, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_GPIO3] = { +		.mask = AS3722_INTERRUPT_MASK3_GPIO3, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_GPIO4] = { +		.mask = AS3722_INTERRUPT_MASK3_GPIO4, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_GPIO5] = { +		.mask = AS3722_INTERRUPT_MASK3_GPIO5, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_WATCHDOG] = { +		.mask = AS3722_INTERRUPT_MASK3_WATCHDOG, +		.reg_offset = 2, +	}, +	[AS3722_IRQ_ENABLE3] = { +		.mask = AS3722_INTERRUPT_MASK3_ENABLE3, +		.reg_offset = 2, +	}, + +	/* INT4 IRQs */ +	[AS3722_IRQ_TEMP_SD0_SHUTDOWN] = { +		.mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_SHUTDOWN, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_TEMP_SD1_SHUTDOWN] = { +		.mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_SHUTDOWN, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_TEMP_SD2_SHUTDOWN] = { +		.mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_SHUTDOWN, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_TEMP_SD0_ALARM] = { +		.mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_ALARM, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_TEMP_SD1_ALARM] = { +		.mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_ALARM, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_TEMP_SD6_ALARM] = { +		.mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_ALARM, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_OCCUR_ALARM_SD6] = { +		.mask = AS3722_INTERRUPT_MASK4_OCCUR_ALARM_SD6, +		.reg_offset = 3, +	}, +	[AS3722_IRQ_ADC] = { +		.mask = AS3722_INTERRUPT_MASK4_ADC, +		.reg_offset = 3, +	}, +}; + +static const struct regmap_irq_chip as3722_irq_chip = { +	.name = "as3722", +	.irqs = as3722_irqs, +	.num_irqs = ARRAY_SIZE(as3722_irqs), +	.num_regs = 4, +	.status_base = AS3722_INTERRUPT_STATUS1_REG, +	.mask_base = AS3722_INTERRUPT_MASK1_REG, +}; + +static int as3722_check_device_id(struct as3722 *as3722) +{ +	u32 val; +	int ret; + +	/* Check that this is actually a AS3722 */ +	ret = as3722_read(as3722, AS3722_ASIC_ID1_REG, &val); +	if (ret < 0) { +		dev_err(as3722->dev, "ASIC_ID1 read failed: %d\n", ret); +		return ret; +	} + +	if (val != AS3722_DEVICE_ID) { +		dev_err(as3722->dev, "Device is not AS3722, ID is 0x%x\n", val); +		return -ENODEV; +	} + +	ret = as3722_read(as3722, AS3722_ASIC_ID2_REG, &val); +	if (ret < 0) { +		dev_err(as3722->dev, "ASIC_ID2 read failed: %d\n", ret); +		return ret; +	} + +	dev_info(as3722->dev, "AS3722 with revision 0x%x found\n", val); +	return 0; +} + +static int as3722_configure_pullups(struct as3722 *as3722) +{ +	int ret; +	u32 val = 0; + +	if (as3722->en_intern_int_pullup) +		val |= AS3722_INT_PULL_UP; +	if (as3722->en_intern_i2c_pullup) +		val |= AS3722_I2C_PULL_UP; + +	ret = as3722_update_bits(as3722, AS3722_IOVOLTAGE_REG, +			AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, val); +	if (ret < 0) +		dev_err(as3722->dev, "IOVOLTAGE_REG update failed: %d\n", ret); +	return ret; +} + +static const struct regmap_range as3722_readable_ranges[] = { +	regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG), +	regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG), +	regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_REG_SEQU_MOD3_REG), +	regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG), +	regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG), +	regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG, +					AS3722_BATTERY_VOLTAGE_MONITOR2_REG), +	regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG), +	regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG), +	regmap_reg_range(AS3722_RTC_ACCESS_REG, AS3722_RTC_ACCESS_REG), +	regmap_reg_range(AS3722_RTC_STATUS_REG, AS3722_TEMP_STATUS_REG), +	regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG), +	regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG), +	regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG), +	regmap_reg_range(AS3722_FUSE7_REG, AS3722_FUSE7_REG), +}; + +static const struct regmap_access_table as3722_readable_table = { +	.yes_ranges = as3722_readable_ranges, +	.n_yes_ranges = ARRAY_SIZE(as3722_readable_ranges), +}; + +static const struct regmap_range as3722_writable_ranges[] = { +	regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG), +	regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG), +	regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_GPIO_SIGNAL_OUT_REG), +	regmap_reg_range(AS3722_REG_SEQU_MOD1_REG, AS3722_REG_SEQU_MOD3_REG), +	regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG), +	regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG), +	regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG, +					AS3722_BATTERY_VOLTAGE_MONITOR2_REG), +	regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG), +	regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG), +	regmap_reg_range(AS3722_INTERRUPT_MASK1_REG, AS3722_TEMP_STATUS_REG), +	regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC1_CONTROL_REG), +	regmap_reg_range(AS3722_ADC1_THRESHOLD_HI_MSB_REG, +					AS3722_ADC_CONFIGURATION_REG), +	regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG), +}; + +static const struct regmap_access_table as3722_writable_table = { +	.yes_ranges = as3722_writable_ranges, +	.n_yes_ranges = ARRAY_SIZE(as3722_writable_ranges), +}; + +static const struct regmap_range as3722_cacheable_ranges[] = { +	regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_LDO11_VOLTAGE_REG), +	regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_LDOCONTROL1_REG), +}; + +static const struct regmap_access_table as3722_volatile_table = { +	.no_ranges = as3722_cacheable_ranges, +	.n_no_ranges = ARRAY_SIZE(as3722_cacheable_ranges), +}; + +static const struct regmap_config as3722_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.max_register = AS3722_MAX_REGISTER, +	.cache_type = REGCACHE_RBTREE, +	.rd_table = &as3722_readable_table, +	.wr_table = &as3722_writable_table, +	.volatile_table = &as3722_volatile_table, +}; + +static int as3722_i2c_of_probe(struct i2c_client *i2c, +			struct as3722 *as3722) +{ +	struct device_node *np = i2c->dev.of_node; +	struct irq_data *irq_data; + +	if (!np) { +		dev_err(&i2c->dev, "Device Tree not found\n"); +		return -EINVAL; +	} + +	irq_data = irq_get_irq_data(i2c->irq); +	if (!irq_data) { +		dev_err(&i2c->dev, "Invalid IRQ: %d\n", i2c->irq); +		return -EINVAL; +	} + +	as3722->en_intern_int_pullup = of_property_read_bool(np, +					"ams,enable-internal-int-pullup"); +	as3722->en_intern_i2c_pullup = of_property_read_bool(np, +					"ams,enable-internal-i2c-pullup"); +	as3722->irq_flags = irqd_get_trigger_type(irq_data); +	dev_dbg(&i2c->dev, "IRQ flags are 0x%08lx\n", as3722->irq_flags); +	return 0; +} + +static int as3722_i2c_probe(struct i2c_client *i2c, +			const struct i2c_device_id *id) +{ +	struct as3722 *as3722; +	unsigned long irq_flags; +	int ret; + +	as3722 = devm_kzalloc(&i2c->dev, sizeof(struct as3722), GFP_KERNEL); +	if (!as3722) +		return -ENOMEM; + +	as3722->dev = &i2c->dev; +	as3722->chip_irq = i2c->irq; +	i2c_set_clientdata(i2c, as3722); + +	ret = as3722_i2c_of_probe(i2c, as3722); +	if (ret < 0) +		return ret; + +	as3722->regmap = devm_regmap_init_i2c(i2c, &as3722_regmap_config); +	if (IS_ERR(as3722->regmap)) { +		ret = PTR_ERR(as3722->regmap); +		dev_err(&i2c->dev, "regmap init failed: %d\n", ret); +		return ret; +	} + +	ret = as3722_check_device_id(as3722); +	if (ret < 0) +		return ret; + +	irq_flags = as3722->irq_flags | IRQF_ONESHOT; +	ret = regmap_add_irq_chip(as3722->regmap, as3722->chip_irq, +			irq_flags, -1, &as3722_irq_chip, +			&as3722->irq_data); +	if (ret < 0) { +		dev_err(as3722->dev, "Failed to add regmap irq: %d\n", ret); +		return ret; +	} + +	ret = as3722_configure_pullups(as3722); +	if (ret < 0) +		goto scrub; + +	ret = mfd_add_devices(&i2c->dev, -1, as3722_devs, +			ARRAY_SIZE(as3722_devs), NULL, 0, +			regmap_irq_get_domain(as3722->irq_data)); +	if (ret) { +		dev_err(as3722->dev, "Failed to add MFD devices: %d\n", ret); +		goto scrub; +	} + +	dev_dbg(as3722->dev, "AS3722 core driver initialized successfully\n"); +	return 0; + +scrub: +	regmap_del_irq_chip(as3722->chip_irq, as3722->irq_data); +	return ret; +} + +static int as3722_i2c_remove(struct i2c_client *i2c) +{ +	struct as3722 *as3722 = i2c_get_clientdata(i2c); + +	mfd_remove_devices(as3722->dev); +	regmap_del_irq_chip(as3722->chip_irq, as3722->irq_data); +	return 0; +} + +static const struct of_device_id as3722_of_match[] = { +	{ .compatible = "ams,as3722", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, as3722_of_match); + +static const struct i2c_device_id as3722_i2c_id[] = { +	{ "as3722", 0 }, +	{}, +}; +MODULE_DEVICE_TABLE(i2c, as3722_i2c_id); + +static struct i2c_driver as3722_i2c_driver = { +	.driver = { +		.name = "as3722", +		.owner = THIS_MODULE, +		.of_match_table = as3722_of_match, +	}, +	.probe = as3722_i2c_probe, +	.remove = as3722_i2c_remove, +	.id_table = as3722_i2c_id, +}; + +module_i2c_driver(as3722_i2c_driver); + +MODULE_DESCRIPTION("I2C support for AS3722 PMICs"); +MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index fa22154c84e..9f6294f2a07 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -695,7 +695,7 @@ static int ds1wm_disable(struct platform_device *pdev)  	return 0;  } -static struct mfd_cell asic3_cell_ds1wm = { +static const struct mfd_cell asic3_cell_ds1wm = {  	.name          = "ds1wm",  	.enable        = ds1wm_enable,  	.disable       = ds1wm_disable, @@ -797,7 +797,7 @@ static int asic3_mmc_disable(struct platform_device *pdev)  	return 0;  } -static struct mfd_cell asic3_cell_mmc = { +static const struct mfd_cell asic3_cell_mmc = {  	.name          = "tmio-mmc",  	.enable        = asic3_mmc_enable,  	.disable       = asic3_mmc_disable, diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c new file mode 100644 index 00000000000..dee653989e3 --- /dev/null +++ b/drivers/mfd/axp20x.c @@ -0,0 +1,258 @@ +/* + * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209 + * + * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC + * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature + * as well as 4 configurable GPIOs. + * + * Author: Carlo Caione <carlo@caione.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/mfd/axp20x.h> +#include <linux/mfd/core.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> + +#define AXP20X_OFF	0x80 + +static const struct regmap_range axp20x_writeable_ranges[] = { +	regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), +	regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), +}; + +static const struct regmap_range axp20x_volatile_ranges[] = { +	regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE), +}; + +static const struct regmap_access_table axp20x_writeable_table = { +	.yes_ranges	= axp20x_writeable_ranges, +	.n_yes_ranges	= ARRAY_SIZE(axp20x_writeable_ranges), +}; + +static const struct regmap_access_table axp20x_volatile_table = { +	.yes_ranges	= axp20x_volatile_ranges, +	.n_yes_ranges	= ARRAY_SIZE(axp20x_volatile_ranges), +}; + +static struct resource axp20x_pek_resources[] = { +	{ +		.name	= "PEK_DBR", +		.start	= AXP20X_IRQ_PEK_RIS_EDGE, +		.end	= AXP20X_IRQ_PEK_RIS_EDGE, +		.flags	= IORESOURCE_IRQ, +	}, { +		.name	= "PEK_DBF", +		.start	= AXP20X_IRQ_PEK_FAL_EDGE, +		.end	= AXP20X_IRQ_PEK_FAL_EDGE, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static const struct regmap_config axp20x_regmap_config = { +	.reg_bits	= 8, +	.val_bits	= 8, +	.wr_table	= &axp20x_writeable_table, +	.volatile_table	= &axp20x_volatile_table, +	.max_register	= AXP20X_FG_RES, +	.cache_type	= REGCACHE_RBTREE, +}; + +#define AXP20X_IRQ(_irq, _off, _mask) \ +	[AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } + +static const struct regmap_irq axp20x_regmap_irqs[] = { +	AXP20X_IRQ(ACIN_OVER_V,		0, 7), +	AXP20X_IRQ(ACIN_PLUGIN,		0, 6), +	AXP20X_IRQ(ACIN_REMOVAL,	0, 5), +	AXP20X_IRQ(VBUS_OVER_V,		0, 4), +	AXP20X_IRQ(VBUS_PLUGIN,		0, 3), +	AXP20X_IRQ(VBUS_REMOVAL,	0, 2), +	AXP20X_IRQ(VBUS_V_LOW,		0, 1), +	AXP20X_IRQ(BATT_PLUGIN,		1, 7), +	AXP20X_IRQ(BATT_REMOVAL,	1, 6), +	AXP20X_IRQ(BATT_ENT_ACT_MODE,	1, 5), +	AXP20X_IRQ(BATT_EXIT_ACT_MODE,	1, 4), +	AXP20X_IRQ(CHARG,		1, 3), +	AXP20X_IRQ(CHARG_DONE,		1, 2), +	AXP20X_IRQ(BATT_TEMP_HIGH,	1, 1), +	AXP20X_IRQ(BATT_TEMP_LOW,	1, 0), +	AXP20X_IRQ(DIE_TEMP_HIGH,	2, 7), +	AXP20X_IRQ(CHARG_I_LOW,		2, 6), +	AXP20X_IRQ(DCDC1_V_LONG,	2, 5), +	AXP20X_IRQ(DCDC2_V_LONG,	2, 4), +	AXP20X_IRQ(DCDC3_V_LONG,	2, 3), +	AXP20X_IRQ(PEK_SHORT,		2, 1), +	AXP20X_IRQ(PEK_LONG,		2, 0), +	AXP20X_IRQ(N_OE_PWR_ON,		3, 7), +	AXP20X_IRQ(N_OE_PWR_OFF,	3, 6), +	AXP20X_IRQ(VBUS_VALID,		3, 5), +	AXP20X_IRQ(VBUS_NOT_VALID,	3, 4), +	AXP20X_IRQ(VBUS_SESS_VALID,	3, 3), +	AXP20X_IRQ(VBUS_SESS_END,	3, 2), +	AXP20X_IRQ(LOW_PWR_LVL1,	3, 1), +	AXP20X_IRQ(LOW_PWR_LVL2,	3, 0), +	AXP20X_IRQ(TIMER,		4, 7), +	AXP20X_IRQ(PEK_RIS_EDGE,	4, 6), +	AXP20X_IRQ(PEK_FAL_EDGE,	4, 5), +	AXP20X_IRQ(GPIO3_INPUT,		4, 3), +	AXP20X_IRQ(GPIO2_INPUT,		4, 2), +	AXP20X_IRQ(GPIO1_INPUT,		4, 1), +	AXP20X_IRQ(GPIO0_INPUT,		4, 0), +}; + +static const struct of_device_id axp20x_of_match[] = { +	{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID }, +	{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, axp20x_of_match); + +/* + * This is useless for OF-enabled devices, but it is needed by I2C subsystem + */ +static const struct i2c_device_id axp20x_i2c_id[] = { +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); + +static const struct regmap_irq_chip axp20x_regmap_irq_chip = { +	.name			= "axp20x_irq_chip", +	.status_base		= AXP20X_IRQ1_STATE, +	.ack_base		= AXP20X_IRQ1_STATE, +	.mask_base		= AXP20X_IRQ1_EN, +	.num_regs		= 5, +	.irqs			= axp20x_regmap_irqs, +	.num_irqs		= ARRAY_SIZE(axp20x_regmap_irqs), +	.mask_invert		= true, +	.init_ack_masked	= true, +}; + +static const char * const axp20x_supplies[] = { +	"acin", +	"vin2", +	"vin3", +	"ldo24in", +	"ldo3in", +	"ldo5in", +}; + +static struct mfd_cell axp20x_cells[] = { +	{ +		.name			= "axp20x-pek", +		.num_resources		= ARRAY_SIZE(axp20x_pek_resources), +		.resources		= axp20x_pek_resources, +	}, { +		.name			= "axp20x-regulator", +		.parent_supplies	= axp20x_supplies, +		.num_parent_supplies	= ARRAY_SIZE(axp20x_supplies), +	}, +}; + +static struct axp20x_dev *axp20x_pm_power_off; +static void axp20x_power_off(void) +{ +	regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, +		     AXP20X_OFF); +} + +static int axp20x_i2c_probe(struct i2c_client *i2c, +			 const struct i2c_device_id *id) +{ +	struct axp20x_dev *axp20x; +	const struct of_device_id *of_id; +	int ret; + +	axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); +	if (!axp20x) +		return -ENOMEM; + +	of_id = of_match_device(axp20x_of_match, &i2c->dev); +	if (!of_id) { +		dev_err(&i2c->dev, "Unable to setup AXP20X data\n"); +		return -ENODEV; +	} +	axp20x->variant = (long) of_id->data; + +	axp20x->i2c_client = i2c; +	axp20x->dev = &i2c->dev; +	dev_set_drvdata(axp20x->dev, axp20x); + +	axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config); +	if (IS_ERR(axp20x->regmap)) { +		ret = PTR_ERR(axp20x->regmap); +		dev_err(&i2c->dev, "regmap init failed: %d\n", ret); +		return ret; +	} + +	ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, +				  IRQF_ONESHOT | IRQF_SHARED, -1, +				  &axp20x_regmap_irq_chip, +				  &axp20x->regmap_irqc); +	if (ret) { +		dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); +		return ret; +	} + +	ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells, +			      ARRAY_SIZE(axp20x_cells), NULL, 0, NULL); + +	if (ret) { +		dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); +		regmap_del_irq_chip(i2c->irq, axp20x->regmap_irqc); +		return ret; +	} + +	if (!pm_power_off) { +		axp20x_pm_power_off = axp20x; +		pm_power_off = axp20x_power_off; +	} + +	dev_info(&i2c->dev, "AXP20X driver loaded\n"); + +	return 0; +} + +static int axp20x_i2c_remove(struct i2c_client *i2c) +{ +	struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); + +	if (axp20x == axp20x_pm_power_off) { +		axp20x_pm_power_off = NULL; +		pm_power_off = NULL; +	} + +	mfd_remove_devices(axp20x->dev); +	regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc); + +	return 0; +} + +static struct i2c_driver axp20x_i2c_driver = { +	.driver = { +		.name	= "axp20x", +		.owner	= THIS_MODULE, +		.of_match_table	= of_match_ptr(axp20x_of_match), +	}, +	.probe		= axp20x_i2c_probe, +	.remove		= axp20x_i2c_remove, +	.id_table	= axp20x_i2c_id, +}; + +module_i2c_driver(axp20x_i2c_driver); + +MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X"); +MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c new file mode 100644 index 00000000000..e334de000e8 --- /dev/null +++ b/drivers/mfd/bcm590xx.c @@ -0,0 +1,132 @@ +/* + * Broadcom BCM590xx PMU + * + * Copyright 2014 Linaro Limited + * Author: Matt Porter <mporter@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under  the terms of the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/mfd/bcm590xx.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +static const struct mfd_cell bcm590xx_devs[] = { +	{ +		.name = "bcm590xx-vregs", +	}, +}; + +static const struct regmap_config bcm590xx_regmap_config_pri = { +	.reg_bits	= 8, +	.val_bits	= 8, +	.max_register	= BCM590XX_MAX_REGISTER_PRI, +	.cache_type	= REGCACHE_RBTREE, +}; + +static const struct regmap_config bcm590xx_regmap_config_sec = { +	.reg_bits	= 8, +	.val_bits	= 8, +	.max_register	= BCM590XX_MAX_REGISTER_SEC, +	.cache_type	= REGCACHE_RBTREE, +}; + +static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri, +			      const struct i2c_device_id *id) +{ +	struct bcm590xx *bcm590xx; +	int ret; + +	bcm590xx = devm_kzalloc(&i2c_pri->dev, sizeof(*bcm590xx), GFP_KERNEL); +	if (!bcm590xx) +		return -ENOMEM; + +	i2c_set_clientdata(i2c_pri, bcm590xx); +	bcm590xx->dev = &i2c_pri->dev; +	bcm590xx->i2c_pri = i2c_pri; + +	bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri, +						 &bcm590xx_regmap_config_pri); +	if (IS_ERR(bcm590xx->regmap_pri)) { +		ret = PTR_ERR(bcm590xx->regmap_pri); +		dev_err(&i2c_pri->dev, "primary regmap init failed: %d\n", ret); +		return ret; +	} + +	/* Secondary I2C slave address is the base address with A(2) asserted */ +	bcm590xx->i2c_sec = i2c_new_dummy(i2c_pri->adapter, +					  i2c_pri->addr | BIT(2)); +	if (IS_ERR_OR_NULL(bcm590xx->i2c_sec)) { +		dev_err(&i2c_pri->dev, "failed to add secondary I2C device\n"); +		return -ENODEV; +	} +	i2c_set_clientdata(bcm590xx->i2c_sec, bcm590xx); + +	bcm590xx->regmap_sec = devm_regmap_init_i2c(bcm590xx->i2c_sec, +						&bcm590xx_regmap_config_sec); +	if (IS_ERR(bcm590xx->regmap_sec)) { +		ret = PTR_ERR(bcm590xx->regmap_sec); +		dev_err(&bcm590xx->i2c_sec->dev, +			"secondary regmap init failed: %d\n", ret); +		goto err; +	} + +	ret = mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs, +			      ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL); +	if (ret < 0) { +		dev_err(&i2c_pri->dev, "failed to add sub-devices: %d\n", ret); +		goto err; +	} + +	return 0; + +err: +	i2c_unregister_device(bcm590xx->i2c_sec); +	return ret; +} + +static int bcm590xx_i2c_remove(struct i2c_client *i2c) +{ +	mfd_remove_devices(&i2c->dev); +	return 0; +} + +static const struct of_device_id bcm590xx_of_match[] = { +	{ .compatible = "brcm,bcm59056" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, bcm590xx_of_match); + +static const struct i2c_device_id bcm590xx_i2c_id[] = { +	{ "bcm59056" }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, bcm590xx_i2c_id); + +static struct i2c_driver bcm590xx_i2c_driver = { +	.driver = { +		   .name = "bcm590xx", +		   .owner = THIS_MODULE, +		   .of_match_table = of_match_ptr(bcm590xx_of_match), +	}, +	.probe = bcm590xx_i2c_probe, +	.remove = bcm590xx_i2c_remove, +	.id_table = bcm590xx_i2c_id, +}; +module_i2c_driver(bcm590xx_i2c_driver); + +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>"); +MODULE_DESCRIPTION("BCM590xx multi-function driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("i2c:bcm590xx"); diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 1f36885d674..38fe9bf0d16 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -30,7 +30,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,  	uint8_t *out;  	int csum, i; -	BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE); +	BUG_ON(msg->out_len > EC_PROTO2_MAX_PARAM_SIZE);  	out = ec_dev->dout;  	out[0] = EC_CMD_VERSION0 + msg->version;  	out[1] = msg->cmd; @@ -84,12 +84,17 @@ static irqreturn_t ec_irq_thread(int irq, void *data)  	return IRQ_HANDLED;  } -static struct mfd_cell cros_devs[] = { +static const struct mfd_cell cros_devs[] = {  	{  		.name = "cros-ec-keyb",  		.id = 1,  		.of_compatible = "google,cros-ec-keyb",  	}, +	{ +		.name = "cros-ec-i2c-tunnel", +		.id = 2, +		.of_compatible = "google,cros-ec-i2c-tunnel", +	},  };  int cros_ec_register(struct cros_ec_device *ec_dev) @@ -184,3 +189,6 @@ int cros_ec_resume(struct cros_ec_device *ec_dev)  EXPORT_SYMBOL(cros_ec_resume);  #endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC core driver"); diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 123044608b6..4f71be99a18 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -120,7 +120,7 @@ static int cros_ec_command_xfer(struct cros_ec_device *ec_dev,  	return ret;  } -static int cros_ec_probe_i2c(struct i2c_client *client, +static int cros_ec_i2c_probe(struct i2c_client *client,  			     const struct i2c_device_id *dev_id)  {  	struct device *dev = &client->dev; @@ -150,7 +150,7 @@ static int cros_ec_probe_i2c(struct i2c_client *client,  	return 0;  } -static int cros_ec_remove_i2c(struct i2c_client *client) +static int cros_ec_i2c_remove(struct i2c_client *client)  {  	struct cros_ec_device *ec_dev = i2c_get_clientdata(client); @@ -190,8 +190,8 @@ static struct i2c_driver cros_ec_driver = {  		.owner	= THIS_MODULE,  		.pm	= &cros_ec_i2c_pm_ops,  	}, -	.probe		= cros_ec_probe_i2c, -	.remove		= cros_ec_remove_i2c, +	.probe		= cros_ec_i2c_probe, +	.remove		= cros_ec_i2c_remove,  	.id_table	= cros_ec_i2c_id,  }; diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 367ccb58ecb..0b8d3282916 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -18,6 +18,7 @@  #include <linux/module.h>  #include <linux/mfd/cros_ec.h>  #include <linux/mfd/cros_ec_commands.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/slab.h>  #include <linux/spi/spi.h> @@ -38,22 +39,31 @@  #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 + * Allow for a long time for the EC to respond.  We support i2c + * tunneling and support fairly long messages for the tunnel (249 + * bytes long at the moment).  If we're talking to a 100 kHz device + * on the other end and need to transfer ~256 bytes, then we need: + *  10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms + * + * We'll wait 4 times that to handle clock stretching and other + * paranoia. + * + * It's pretty unlikely that we'll really see a 249 byte tunnel in + * anything other than testing.  If this was more common we might + * consider having slow commands like this require a GET_STATUS + * wait loop.  The 'flash write' command would be another candidate + * for this, clocking in at 2-3ms. + */ +#define EC_MSG_DEADLINE_MS		100  /*    * 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. +  * If we go too fast, the EC will miss the transaction. We know that we +  * need at least 70 us with the 16 MHz STM32 EC, so go with 200 us to be +  * safe.    */ -#define EC_SPI_RECOVERY_TIME_NS	(50 * 1000) +#define EC_SPI_RECOVERY_TIME_NS	(200 * 1000)  /**   * struct cros_ec_spi - information about a SPI-connected EC @@ -61,10 +71,15 @@   * @spi: SPI device we are connected to   * @last_transfer_ns: time that we last finished a transfer, or 0 if there   *	if no record + * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that + *      is sent when we want to turn off CS at the end of a transaction. + * @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time   */  struct cros_ec_spi {  	struct spi_device *spi;  	s64 last_transfer_ns; +	unsigned int end_of_msg_delay; +	struct mutex lock;  };  static void debug_packet(struct device *dev, const char *name, u8 *ptr, @@ -75,7 +90,9 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,  	dev_dbg(dev, "%s: ", name);  	for (i = 0; i < len; i++) -		dev_cont(dev, " %02x", ptr[i]); +		pr_cont(" %02x", ptr[i]); + +	pr_cont("\n");  #endif  } @@ -104,8 +121,10 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,  	/* Receive data until we see the header byte */  	deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); -	do { -		memset(&trans, '\0', sizeof(trans)); +	while (true) { +		unsigned long start_jiffies = jiffies; + +		memset(&trans, 0, sizeof(trans));  		trans.cs_change = 1;  		trans.rx_buf = ptr = ec_dev->din;  		trans.len = EC_MSG_PREAMBLE_COUNT; @@ -125,12 +144,19 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,  				break;  			}  		} +		if (ptr != end) +			break; -		if (time_after(jiffies, deadline)) { +		/* +		 * Use the time at the start of the loop as a timeout.  This +		 * gives us one last shot at getting the transfer and is useful +		 * in case we got context switched out for a while. +		 */ +		if (time_after(start_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 @@ -157,7 +183,7 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,  		dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",  			todo, need_len, ptr - ec_dev->din); -		memset(&trans, '\0', sizeof(trans)); +		memset(&trans, 0, sizeof(trans));  		trans.cs_change = 1;  		trans.rx_buf = ptr;  		trans.len = todo; @@ -201,6 +227,13 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,  	int ret = 0, final_ret;  	struct timespec ts; +	/* +	 * We have the shared ec_dev buffer plus we do lots of separate spi_sync +	 * calls, so we need to make sure only one person is using this at a +	 * time. +	 */ +	mutex_lock(&ec_spi->lock); +  	len = cros_ec_prepare_tx(ec_dev, ec_msg);  	dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); @@ -212,12 +245,12 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,  		ktime_get_ts(&ts);  		delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns;  		if (delay < EC_SPI_RECOVERY_TIME_NS) -			ndelay(delay); +			ndelay(EC_SPI_RECOVERY_TIME_NS - delay);  	}  	/* Transmit phase - send our message */  	debug_packet(ec_dev->dev, "out", ec_dev->dout, len); -	memset(&trans, '\0', sizeof(trans)); +	memset(&trans, 0, sizeof(trans));  	trans.tx_buf = ec_dev->dout;  	trans.len = len;  	trans.cs_change = 1; @@ -235,6 +268,17 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,  	/* turn off CS */  	spi_message_init(&msg); + +	if (ec_spi->end_of_msg_delay) { +		/* +		 * Add delay for last transaction, to ensure the rising edge +		 * doesn't come too soon after the end of the data. +		 */ +		memset(&trans, 0, sizeof(trans)); +		trans.delay_usecs = ec_spi->end_of_msg_delay; +		spi_message_add_tail(&trans, &msg); +	} +  	final_ret = spi_sync(ec_spi->spi, &msg);  	ktime_get_ts(&ts);  	ec_spi->last_transfer_ns = timespec_to_ns(&ts); @@ -242,7 +286,7 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,  		ret = final_ret;  	if (ret < 0) {  		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); -		return ret; +		goto exit;  	}  	/* check response error code */ @@ -251,14 +295,16 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,  		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; +		ret = -EINVAL; +		goto exit;  	}  	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; +		ret = -ENOSPC; +		goto exit;  	}  	/* copy response packet payload and compute checksum */ @@ -275,13 +321,28 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,  		dev_err(ec_dev->dev,  			"bad packet checksum, expected %02x, got %02x\n",  			sum, ptr[len + 2]); -		return -EBADMSG; +		ret = -EBADMSG; +		goto exit;  	} -	return 0; +	ret = 0; +exit: +	mutex_unlock(&ec_spi->lock); +	return ret;  } -static int cros_ec_probe_spi(struct spi_device *spi) +static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) +{ +	struct device_node *np = dev->of_node; +	u32 val; +	int ret; + +	ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val); +	if (!ret) +		ec_spi->end_of_msg_delay = val; +} + +static int cros_ec_spi_probe(struct spi_device *spi)  {  	struct device *dev = &spi->dev;  	struct cros_ec_device *ec_dev; @@ -298,10 +359,14 @@ static int cros_ec_probe_spi(struct spi_device *spi)  	if (ec_spi == NULL)  		return -ENOMEM;  	ec_spi->spi = spi; +	mutex_init(&ec_spi->lock);  	ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);  	if (!ec_dev)  		return -ENOMEM; +	/* Check for any DT properties */ +	cros_ec_spi_dt_probe(ec_spi, dev); +  	spi_set_drvdata(spi, ec_dev);  	ec_dev->name = "SPI";  	ec_dev->dev = dev; @@ -323,7 +388,7 @@ static int cros_ec_probe_spi(struct spi_device *spi)  	return 0;  } -static int cros_ec_remove_spi(struct spi_device *spi) +static int cros_ec_spi_remove(struct spi_device *spi)  {  	struct cros_ec_device *ec_dev; @@ -364,12 +429,12 @@ static struct spi_driver cros_ec_driver_spi = {  		.owner	= THIS_MODULE,  		.pm	= &cros_ec_spi_pm_ops,  	}, -	.probe		= cros_ec_probe_spi, -	.remove		= cros_ec_remove_spi, +	.probe		= cros_ec_spi_probe, +	.remove		= cros_ec_spi_remove,  	.id_table	= cros_ec_spi_id,  };  module_spi_driver(cros_ec_driver_spi); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2");  MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)"); diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 2e4752a9220..be91cb5d6e7 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -23,7 +23,6 @@   */  #include <linux/kernel.h> -#include <linux/init.h>  #include <linux/mfd/core.h>  #include <linux/module.h>  #include <linux/pci.h> @@ -172,7 +171,7 @@ static void cs5535_mfd_remove(struct pci_dev *pdev)  	pci_disable_device(pdev);  } -static DEFINE_PCI_DEVICE_TABLE(cs5535_mfd_pci_tbl) = { +static const struct pci_device_id cs5535_mfd_pci_tbl[] = {  	{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },  	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },  	{ 0, } diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index ea28a33576e..e8af816d73a 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -279,6 +279,9 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg)  	case DA9052_EVENT_B_REG:  	case DA9052_EVENT_C_REG:  	case DA9052_EVENT_D_REG: +	case DA9052_CONTROL_B_REG: +	case DA9052_CONTROL_D_REG: +	case DA9052_SUPPLY_REG:  	case DA9052_FAULTLOG_REG:  	case DA9052_CHG_TIME_REG:  	case DA9052_ADC_RES_L_REG: @@ -427,7 +430,7 @@ int da9052_adc_read_temp(struct da9052 *da9052)  }  EXPORT_SYMBOL_GPL(da9052_adc_read_temp); -static struct mfd_cell da9052_subdev_info[] = { +static const struct mfd_cell da9052_subdev_info[] = {  	{  		.name = "da9052-regulator",  		.id = 1, diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 6a9fec40d01..6da8ec8ff80 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -75,6 +75,7 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)  					   DA9052_PARK_REGISTER,  					   &val);  		break; +	case DA9053_BC:  	default:  		/*  		 * For other chips parking of I2C register @@ -86,7 +87,11 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)  	return 0;  } -static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) +/* + * According to errata item 24, multiwrite mode should be avoided + * in order to prevent register data corruption after power-down. + */ +static int da9052_i2c_disable_multiwrite(struct da9052 *da9052)  {  	int reg_val, ret; @@ -94,8 +99,8 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)  	if (ret < 0)  		return ret; -	if (reg_val & DA9052_CONTROL_B_WRITEMODE) { -		reg_val &= ~DA9052_CONTROL_B_WRITEMODE; +	if (!(reg_val & DA9052_CONTROL_B_WRITEMODE)) { +		reg_val |= DA9052_CONTROL_B_WRITEMODE;  		ret = regmap_write(da9052->regmap, DA9052_CONTROL_B_REG,  				   reg_val);  		if (ret < 0) @@ -110,6 +115,7 @@ static const struct i2c_device_id da9052_i2c_id[] = {  	{"da9053-aa", DA9053_AA},  	{"da9053-ba", DA9053_BA},  	{"da9053-bb", DA9053_BB}, +	{"da9053-bc", DA9053_BC},  	{}  }; @@ -117,8 +123,9 @@ static const struct i2c_device_id da9052_i2c_id[] = {  static const struct of_device_id dialog_dt_ids[] = {  	{ .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },  	{ .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] }, -	{ .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] }, +	{ .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] },  	{ .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] }, +	{ .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] },  	{ /* sentinel */ }  };  #endif @@ -154,7 +161,7 @@ static int da9052_i2c_probe(struct i2c_client *client,  		return ret;  	} -	ret = da9052_i2c_enable_multiwrite(da9052); +	ret = da9052_i2c_disable_multiwrite(da9052);  	if (ret < 0)  		return ret; diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 0680bcbc53d..17666b40b70 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -71,6 +71,7 @@ static struct spi_device_id da9052_spi_id[] = {  	{"da9053-aa", DA9053_AA},  	{"da9053-ba", DA9053_BA},  	{"da9053-bb", DA9053_BB}, +	{"da9053-bc", DA9053_BC},  	{}  }; diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c index d3670cd3c3c..caf8dcffd0a 100644 --- a/drivers/mfd/da9055-core.c +++ b/drivers/mfd/da9055-core.c @@ -294,7 +294,7 @@ static struct resource da9055_ld05_6_resource = {  	.flags = IORESOURCE_IRQ,  }; -static struct mfd_cell da9055_devs[] = { +static const struct mfd_cell da9055_devs[] = {  	{  		.of_compatible = "dialog,da9055-gpio",  		.name = "da9055-gpio", diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c index 13af7e50021..d4d4c165eb9 100644 --- a/drivers/mfd/da9055-i2c.c +++ b/drivers/mfd/da9055-i2c.c @@ -15,6 +15,8 @@  #include <linux/device.h>  #include <linux/i2c.h>  #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h>  #include <linux/mfd/da9055/core.h> @@ -53,8 +55,21 @@ static int da9055_i2c_remove(struct i2c_client *i2c)  	return 0;  } +/* + * DO NOT change the device Ids. The naming is intentionally specific as both + * the PMIC and CODEC parts of this chip are instantiated separately as I2C + * devices (both have configurable I2C addresses, and are to all intents and + * purposes separate). As a result there are specific DA9055 ids for PMIC + * and CODEC, which must be different to operate together. + */  static struct i2c_device_id da9055_i2c_id[] = { -	{"da9055", 0}, +	{"da9055-pmic", 0}, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); + +static const struct of_device_id da9055_of_match[] = { +	{ .compatible = "dlg,da9055-pmic", },  	{ }  }; @@ -63,8 +78,9 @@ static struct i2c_driver da9055_i2c_driver = {  	.remove = da9055_i2c_remove,  	.id_table = da9055_i2c_id,  	.driver = { -		.name = "da9055", +		.name = "da9055-pmic",  		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(da9055_of_match),  	},  }; diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index c9cf8d98840..e70ae315abc 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -75,7 +75,7 @@ static struct resource da9063_hwmon_resources[] = {  }; -static struct mfd_cell da9063_devs[] = { +static const struct mfd_cell da9063_devs[] = {  	{  		.name		= DA9063_DRVNAME_REGULATORS,  		.num_resources	= ARRAY_SIZE(da9063_regulators_resources), @@ -110,7 +110,7 @@ static struct mfd_cell da9063_devs[] = {  int da9063_device_init(struct da9063 *da9063, unsigned int irq)  {  	struct da9063_pdata *pdata = da9063->dev->platform_data; -	int model, revision; +	int model, variant_id, variant_code;  	int ret;  	if (pdata) { @@ -141,23 +141,26 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)  		return -ENODEV;  	} -	ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision); +	ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id);  	if (ret < 0) { -		dev_err(da9063->dev, "Cannot read chip revision id.\n"); +		dev_err(da9063->dev, "Cannot read chip variant id.\n");  		return -EIO;  	} -	revision >>= DA9063_CHIP_VARIANT_SHIFT; -	if (revision != 3) { -		dev_err(da9063->dev, "Unknown chip revision: %d\n", revision); + +	variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT; + +	dev_info(da9063->dev, +		 "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n", +		 model, variant_id); + +	if (variant_code != PMIC_DA9063_BB) { +		dev_err(da9063->dev, "Unknown chip variant code: 0x%02X\n", +				variant_code);  		return -ENODEV;  	}  	da9063->model = model; -	da9063->revision = revision; - -	dev_info(da9063->dev, -		 "Device detected (model-ID: 0x%02X  rev-ID: 0x%02X)\n", -		 model, revision); +	da9063->variant_code = variant_code;  	ret = da9063_irq_init(da9063);  	if (ret) { diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 53f371dcbb6..193cf168ba8 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -25,6 +25,7 @@  #include <linux/bitops.h>  #include <linux/fs.h>  #include <linux/of.h> +#include <linux/of_irq.h>  #include <linux/platform_device.h>  #include <linux/uaccess.h>  #include <linux/mfd/core.h> @@ -480,7 +481,6 @@ static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {  	CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true),  	CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true),  	CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), -	CLK_MGT_ENTRY(BML8580CLK, PLL_DIV, true),  	CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true),  	CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true),  	CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), @@ -1734,18 +1734,17 @@ static struct cpufreq_frequency_table db8500_cpufreq_table[] = {  static long round_armss_rate(unsigned long rate)  { +	struct cpufreq_frequency_table *pos;  	long freq = 0; -	int i = 0;  	/* cpufreq table frequencies is in KHz. */  	rate = rate / 1000;  	/* Find the corresponding arm opp from the cpufreq table. */ -	while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) { -		freq = db8500_cpufreq_table[i].frequency; +	cpufreq_for_each_entry(pos, db8500_cpufreq_table) { +		freq = pos->frequency;  		if (freq == rate)  			break; -		i++;  	}  	/* Return the last valid value, even if a match was not found. */ @@ -1886,23 +1885,21 @@ static void set_clock_rate(u8 clock, unsigned long rate)  static int set_armss_rate(unsigned long rate)  { -	int i = 0; +	struct cpufreq_frequency_table *pos;  	/* cpufreq table frequencies is in KHz. */  	rate = rate / 1000;  	/* Find the corresponding arm opp from the cpufreq table. */ -	while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) { -		if (db8500_cpufreq_table[i].frequency == rate) +	cpufreq_for_each_entry(pos, db8500_cpufreq_table) +		if (pos->frequency == rate)  			break; -		i++; -	} -	if (db8500_cpufreq_table[i].frequency != rate) +	if (pos->frequency != rate)  		return -EINVAL;  	/* Set the new arm opp. */ -	return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data); +	return db8500_prcmu_set_arm_opp(pos->driver_data);  }  static int set_plldsi_rate(unsigned long rate) @@ -2303,9 +2300,6 @@ int prcmu_ac_wake_req(void)  	if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,  			msecs_to_jiffies(5000))) { -#if defined(CONFIG_DBX500_PRCMU_DEBUG) -		db8500_prcmu_debug_dump(__func__, true, true); -#endif  		pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",  			__func__);  		ret = -EFAULT; @@ -2679,16 +2673,12 @@ static struct irq_domain_ops db8500_irq_ops = {  	.xlate  = irq_domain_xlate_twocell,  }; -static int db8500_irq_init(struct device_node *np, int irq_base) +static int db8500_irq_init(struct device_node *np)  {  	int i; -	/* In the device tree case, just take some IRQs */ -	if (np) -		irq_base = 0; -  	db8500_irq_domain = irq_domain_add_simple( -		np, NUM_PRCMU_WAKEUPS, irq_base, +		np, NUM_PRCMU_WAKEUPS, 0,  		&db8500_irq_ops, NULL);  	if (!db8500_irq_domain) { @@ -3071,7 +3061,7 @@ static struct db8500_thsens_platform_data db8500_thsens_data = {  	.num_trips = 4,  }; -static struct mfd_cell common_prcmu_devs[] = { +static const struct mfd_cell common_prcmu_devs[] = {  	{  		.name = "ux500_wdt",  		.platform_data = &db8500_wdt_pdata, @@ -3080,7 +3070,7 @@ static struct mfd_cell common_prcmu_devs[] = {  	},  }; -static struct mfd_cell db8500_prcmu_devs[] = { +static const struct mfd_cell db8500_prcmu_devs[] = {  	{  		.name = "db8500-prcmu-regulators",  		.of_compatible = "stericsson,db8500-prcmu-regulator", @@ -3115,11 +3105,11 @@ static void db8500_prcmu_update_cpufreq(void)  }  static int db8500_prcmu_register_ab8500(struct device *parent, -					struct ab8500_platform_data *pdata, -					int irq) +					struct ab8500_platform_data *pdata)  { -	struct resource ab8500_resource = DEFINE_RES_IRQ(irq); -	struct mfd_cell ab8500_cell = { +	struct device_node *np; +	struct resource ab8500_resource; +	const struct mfd_cell ab8500_cell = {  		.name = "ab8500-core",  		.of_compatible = "stericsson,ab8500",  		.id = AB8500_VERSION_AB8500, @@ -3129,6 +3119,20 @@ static int db8500_prcmu_register_ab8500(struct device *parent,  		.num_resources = 1,  	}; +	if (!parent->of_node) +		return -ENODEV; + +	/* Look up the device node, sneak the IRQ out of it */ +	for_each_child_of_node(parent->of_node, np) { +		if (of_device_is_compatible(np, ab8500_cell.of_compatible)) +			break; +	} +	if (!np) { +		dev_info(parent, "could not find AB8500 node in the device tree\n"); +		return -ENODEV; +	} +	of_irq_to_resource_table(np, &ab8500_resource, 1); +  	return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL);  } @@ -3181,7 +3185,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)  		goto no_irq_return;  	} -	db8500_irq_init(np, pdata->irq_base); +	db8500_irq_init(np);  	prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); @@ -3206,8 +3210,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)  		}  	} -	err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata, -					   pdata->ab_irq); +	err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata);  	if (err) {  		mfd_remove_devices(&pdev->dev);  		pr_err("prcmu: Failed to add ab8500 subdevice\n"); diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 4f6f0fa5d3b..7cc32a8ff01 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -32,7 +32,6 @@  #define PRCM_PER7CLK_MGT	(0x040)  #define PRCM_LCDCLK_MGT		(0x044)  #define PRCM_BMLCLK_MGT		(0x04C) -#define PRCM_BML8580CLK_MGT	(0x108)  #define PRCM_HSITXCLK_MGT	(0x050)  #define PRCM_HSIRXCLK_MGT	(0x054)  #define PRCM_HDMICLK_MGT	(0x058) diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index 7245b0c5b79..2ed774e7d34 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -394,16 +394,12 @@ static int pcap_add_subdev(struct pcap_chip *pcap,  static int ezx_pcap_remove(struct spi_device *spi)  {  	struct pcap_chip *pcap = spi_get_drvdata(spi); -	struct pcap_platform_data *pdata = dev_get_platdata(&spi->dev); -	int i, adc_irq; +	int i;  	/* remove all registered subdevs */  	device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);  	/* cleanup ADC */ -	adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? -				PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); -	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]); @@ -509,8 +505,6 @@ static int ezx_pcap_probe(struct spi_device *spi)  remove_subdevs:  	device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); -/* free_adc: */ -	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); diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 6bf92a507b9..e88d4f6fef4 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -114,7 +114,7 @@ static struct resource ds1wm_resources[] __initdata = {  	},  }; -static struct mfd_cell ds1wm_cell __initdata = { +static const struct mfd_cell ds1wm_cell __initconst = {  	.name          = "ds1wm",  	.enable        = ds1wm_enable,  	.disable       = ds1wm_disable, diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index 9203d47cdbb..049fd23af54 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -178,7 +178,7 @@ static struct mfd_cell msic_devs[] = {   * These devices appear only after the MSIC driver itself is initialized so   * we can guarantee that the SCU IPC interface is ready.   */ -static struct mfd_cell msic_other_devs[] = { +static const struct mfd_cell msic_other_devs[] = {  	/* Audio codec in the MSIC */  	{  		.id			= -1, diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c new file mode 100644 index 00000000000..7e50fe0118e --- /dev/null +++ b/drivers/mfd/ipaq-micro.c @@ -0,0 +1,482 @@ +/* + * Compaq iPAQ h3xxx Atmel microcontroller companion support + * + * This is an Atmel AT90LS8535 with a special flashed-in firmware that + * implements the special protocol used by this driver. + * + * based on previous kernel 2.4 version by Andrew Christian + * Author : Alessandro Gardich <gremlin@gremlin.it> + * Author : Dmitry Artamonow <mad_soft@inbox.ru> + * Author : Linus Walleij <linus.walleij@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/mfd/core.h> +#include <linux/mfd/ipaq-micro.h> +#include <linux/string.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/list.h> + +#include <mach/hardware.h> + +static void ipaq_micro_trigger_tx(struct ipaq_micro *micro) +{ +	struct ipaq_micro_txdev *tx = µ->tx; +	struct ipaq_micro_msg *msg = micro->msg; +	int i, bp; +	u8 checksum; +	u32 val; + +	bp = 0; +	tx->buf[bp++] = CHAR_SOF; + +	checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f); +	tx->buf[bp++] = checksum; + +	for (i = 0; i < msg->tx_len; i++) { +		tx->buf[bp++] = msg->tx_data[i]; +		checksum += msg->tx_data[i]; +	} + +	tx->buf[bp++] = checksum; +	tx->len = bp; +	tx->index = 0; +	print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, +		       tx->buf, tx->len, true); + +	/* Enable interrupt */ +	val = readl(micro->base + UTCR3); +	val |= UTCR3_TIE; +	writel(val, micro->base + UTCR3); +} + +int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg) +{ +	unsigned long flags; + +	dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len); + +	spin_lock_irqsave(µ->lock, flags); +	if (micro->msg) { +		list_add_tail(&msg->node, µ->queue); +		spin_unlock_irqrestore(µ->lock, flags); +		return 0; +	} +	micro->msg = msg; +	ipaq_micro_trigger_tx(micro); +	spin_unlock_irqrestore(µ->lock, flags); +	return 0; +} +EXPORT_SYMBOL(ipaq_micro_tx_msg); + +static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data) +{ +	int i; + +	dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len); + +	spin_lock(µ->lock); +	switch (id) { +	case MSG_VERSION: +	case MSG_EEPROM_READ: +	case MSG_EEPROM_WRITE: +	case MSG_BACKLIGHT: +	case MSG_NOTIFY_LED: +	case MSG_THERMAL_SENSOR: +	case MSG_BATTERY: +		/* Handle synchronous messages */ +		if (micro->msg && micro->msg->id == id) { +			struct ipaq_micro_msg *msg = micro->msg; + +			memcpy(msg->rx_data, data, len); +			msg->rx_len = len; +			complete(µ->msg->ack); +			if (!list_empty(µ->queue)) { +				micro->msg = list_entry(micro->queue.next, +							struct ipaq_micro_msg, +							node); +				list_del_init(µ->msg->node); +				ipaq_micro_trigger_tx(micro); +			} else +				micro->msg = NULL; +			dev_dbg(micro->dev, "OK RX message 0x%02x\n", id); +		} else { +			dev_err(micro->dev, +				"out of band RX message 0x%02x\n", id); +			if(!micro->msg) +				dev_info(micro->dev, "no message queued\n"); +			else +				dev_info(micro->dev, "expected message %02x\n", +					 micro->msg->id); +		} +		break; +	case MSG_KEYBOARD: +		if (micro->key) +			micro->key(micro->key_data, len, data); +		else +			dev_dbg(micro->dev, "key message ignored, no handle \n"); +		break; +	case MSG_TOUCHSCREEN: +		if (micro->ts) +			micro->ts(micro->ts_data, len, data); +		else +			dev_dbg(micro->dev, "touchscreen message ignored, no handle \n"); +		break; +	default: +		dev_err(micro->dev, +			"unknown msg %d [%d] ", id, len); +		for (i = 0; i < len; ++i) +			pr_cont("0x%02x ", data[i]); +		pr_cont("\n"); +	} +	spin_unlock(µ->lock); +} + +static void micro_process_char(struct ipaq_micro *micro, u8 ch) +{ +	struct ipaq_micro_rxdev *rx = µ->rx; + +	switch (rx->state) { +	case STATE_SOF:	/* Looking for SOF */ +		if (ch == CHAR_SOF) +			rx->state = STATE_ID; /* Next byte is the id and len */ +		break; +	case STATE_ID: /* Looking for id and len byte */ +		rx->id = (ch & 0xf0) >> 4 ; +		rx->len = (ch & 0x0f); +		rx->index = 0; +		rx->chksum = ch; +		rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM; +		break; +	case STATE_DATA: /* Looking for 'len' data bytes */ +		rx->chksum += ch; +		rx->buf[rx->index] = ch; +		if (++rx->index == rx->len) +			rx->state = STATE_CHKSUM; +		break; +	case STATE_CHKSUM: /* Looking for the checksum */ +		if (ch == rx->chksum) +			micro_rx_msg(micro, rx->id, rx->len, rx->buf); +		rx->state = STATE_SOF; +		break; +	} +} + +static void micro_rx_chars(struct ipaq_micro *micro) +{ +	u32 status, ch; + +	while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) { +		ch = readl(micro->base + UTDR); +		if (status & UTSR1_PRE) +			dev_err(micro->dev, "rx: parity error\n"); +		else if (status & UTSR1_FRE) +			dev_err(micro->dev, "rx: framing error\n"); +		else if (status & UTSR1_ROR) +			dev_err(micro->dev, "rx: overrun error\n"); +		micro_process_char(micro, ch); +	} +} + +static void ipaq_micro_get_version(struct ipaq_micro *micro) +{ +	struct ipaq_micro_msg msg = { +		.id = MSG_VERSION, +	}; + +	ipaq_micro_tx_msg_sync(micro, &msg); +	if (msg.rx_len == 4) { +		memcpy(micro->version, msg.rx_data, 4); +		micro->version[4] = '\0'; +	} else if (msg.rx_len == 9) { +		memcpy(micro->version, msg.rx_data, 4); +		micro->version[4] = '\0'; +		/* Bytes 4-7 are "pack", byte 8 is "boot type" */ +	} else { +		dev_err(micro->dev, +			"illegal version message %d bytes\n", msg.rx_len); +	} +} + +static void ipaq_micro_eeprom_read(struct ipaq_micro *micro, +				   u8 address, u8 len, u8 *data) +{ +	struct ipaq_micro_msg msg = { +		.id = MSG_EEPROM_READ, +	}; +	u8 i; + +	for (i = 0; i < len; i++) { +		msg.tx_data[0] = address + i; +		msg.tx_data[1] = 1; +		msg.tx_len = 2; +		ipaq_micro_tx_msg_sync(micro, &msg); +		memcpy(data + (i * 2), msg.rx_data, 2); +	} +} + +static char *ipaq_micro_str(u8 *wchar, u8 len) +{ +	char retstr[256]; +	u8 i; + +	for (i = 0; i < len / 2; i++) +		retstr[i] = wchar[i * 2]; +	return kstrdup(retstr, GFP_KERNEL); +} + +static u16 ipaq_micro_to_u16(u8 *data) +{ +	return data[1] << 8 | data[0]; +} + +static void ipaq_micro_eeprom_dump(struct ipaq_micro *micro) +{ +	u8 dump[256]; +	char *str; + +	ipaq_micro_eeprom_read(micro, 0, 128, dump); +	str = ipaq_micro_str(dump, 10); +	if (str) { +		dev_info(micro->dev, "HM version %s\n", str); +		kfree(str); +	} +	str = ipaq_micro_str(dump+10, 40); +	if (str) { +		dev_info(micro->dev, "serial number: %s\n", str); +		/* Feed the random pool with this */ +		add_device_randomness(str, strlen(str)); +		kfree(str); +	} +	str = ipaq_micro_str(dump+50, 20); +	if (str) { +		dev_info(micro->dev, "module ID: %s\n", str); +		kfree(str); +	} +	str = ipaq_micro_str(dump+70, 10); +	if (str) { +		dev_info(micro->dev, "product revision: %s\n", str); +		kfree(str); +	} +	dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80)); +	dev_info(micro->dev, "frame rate: %u fps\n", +		 ipaq_micro_to_u16(dump+82)); +	dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84)); +	dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86)); +	dev_info(micro->dev, "color display: %s\n", +		 ipaq_micro_to_u16(dump+88) ? "yes" : "no"); +	dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90)); +	dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92)); +	dev_info(micro->dev, "screen: %u x %u\n", +		 ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96)); +	print_hex_dump(KERN_DEBUG, "eeprom: ", DUMP_PREFIX_OFFSET, 16, 1, +		       dump, 256, true); + +} + +static void micro_tx_chars(struct ipaq_micro *micro) +{ +	struct ipaq_micro_txdev *tx = µ->tx; +	u32 val; + +	while ((tx->index < tx->len) && +	       (readl(micro->base + UTSR1) & UTSR1_TNF)) { +		writel(tx->buf[tx->index], micro->base + UTDR); +		tx->index++; +	} + +	/* Stop interrupts */ +	val = readl(micro->base + UTCR3); +	val &= ~UTCR3_TIE; +	writel(val, micro->base + UTCR3); +} + +static void micro_reset_comm(struct ipaq_micro *micro) +{ +	struct ipaq_micro_rxdev *rx = µ->rx; +	u32 val; + +	if (micro->msg) +		complete(µ->msg->ack); + +	/* Initialize Serial channel protocol frame */ +	rx->state = STATE_SOF;  /* Reset the state machine */ + +	/* Set up interrupts */ +	writel(0x01, micro->sdlc + 0x0); /* Select UART mode */ + +	/* Clean up CR3 */ +	writel(0x0, micro->base + UTCR3); + +	/* Format: 8N1 */ +	writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0); + +	/* Baud rate: 115200 */ +	writel(0x0, micro->base + UTCR1); +	writel(0x1, micro->base + UTCR2); + +	/* Clear SR0 */ +	writel(0xff, micro->base + UTSR0); + +	/* Enable RX int, disable TX int */ +	writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3); +	val = readl(micro->base + UTCR3); +	val &= ~UTCR3_TIE; +	writel(val, micro->base + UTCR3); +} + +static irqreturn_t micro_serial_isr(int irq, void *dev_id) +{ +	struct ipaq_micro *micro = dev_id; +	struct ipaq_micro_txdev *tx = µ->tx; +	u32 status; + +	status = readl(micro->base + UTSR0); +	do { +		if (status & (UTSR0_RID | UTSR0_RFS)) { +			if (status & UTSR0_RID) +				/* Clear the Receiver IDLE bit */ +				writel(UTSR0_RID, micro->base + UTSR0); +			micro_rx_chars(micro); +		} + +		/* Clear break bits */ +		if (status & (UTSR0_RBB | UTSR0_REB)) +			writel(status & (UTSR0_RBB | UTSR0_REB), +			       micro->base + UTSR0); + +		if (status & UTSR0_TFS) +			micro_tx_chars(micro); + +		status = readl(micro->base + UTSR0); + +	} while (((tx->index < tx->len) && (status & UTSR0_TFS)) || +		 (status & (UTSR0_RFS | UTSR0_RID))); + +	return IRQ_HANDLED; +} + +static const struct mfd_cell micro_cells[] = { +	{ .name = "ipaq-micro-backlight", }, +	{ .name = "ipaq-micro-battery", }, +	{ .name = "ipaq-micro-keys", }, +	{ .name = "ipaq-micro-ts", }, +	{ .name = "ipaq-micro-leds", }, +}; + +static int micro_resume(struct device *dev) +{ +	struct ipaq_micro *micro = dev_get_drvdata(dev); + +	micro_reset_comm(micro); +	mdelay(10); + +	return 0; +} + +static int micro_probe(struct platform_device *pdev) +{ +	struct ipaq_micro *micro; +	struct resource *res; +	int ret; +	int irq; + +	micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL); +	if (!micro) +		return -ENOMEM; + +	micro->dev = &pdev->dev; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -EINVAL; + +	micro->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(micro->base)) +		return PTR_ERR(micro->base); + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +	if (!res) +		return -EINVAL; + +	micro->sdlc = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(micro->sdlc)) +		return PTR_ERR(micro->sdlc); + +	micro_reset_comm(micro); + +	irq = platform_get_irq(pdev, 0); +	if (!irq) +		return -EINVAL; +	ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr, +			       IRQF_SHARED, "ipaq-micro", +			       micro); +	if (ret) { +		dev_err(&pdev->dev, "unable to grab serial port IRQ\n"); +		return ret; +	} else +		dev_info(&pdev->dev, "grabbed serial port IRQ\n"); + +	spin_lock_init(µ->lock); +	INIT_LIST_HEAD(µ->queue); +	platform_set_drvdata(pdev, micro); + +	ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells, +			      ARRAY_SIZE(micro_cells), NULL, 0, NULL); +	if (ret) { +		dev_err(&pdev->dev, "error adding MFD cells"); +		return ret; +	} + +	/* Check version */ +	ipaq_micro_get_version(micro); +	dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version); +	ipaq_micro_eeprom_dump(micro); + +	return 0; +} + +static int micro_remove(struct platform_device *pdev) +{ +	struct ipaq_micro *micro = platform_get_drvdata(pdev); +	u32 val; + +	mfd_remove_devices(&pdev->dev); + +	val = readl(micro->base + UTCR3); +	val &= ~(UTCR3_RXE | UTCR3_RIE); /* disable receive interrupt */ +	val &= ~(UTCR3_TXE | UTCR3_TIE); /* disable transmit interrupt */ +	writel(val, micro->base + UTCR3); + +	return 0; +} + +static const struct dev_pm_ops micro_dev_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume) +}; + +static struct platform_driver micro_device_driver = { +	.driver   = { +		.name	= "ipaq-h3xxx-micro", +		.pm	= µ_dev_pm_ops, +	}, +	.probe    = micro_probe, +	.remove   = micro_remove, +	/* .shutdown = micro_suspend, // FIXME */ +}; +module_platform_driver(micro_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro core and backlight"); diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index fcbb2e9dfd3..433f823037d 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -13,7 +13,6 @@  #include <linux/kernel.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/pci.h>  #include <linux/interrupt.h>  #include <linux/delay.h> @@ -265,7 +264,7 @@ static void cmodio_pci_remove(struct pci_dev *dev)  #define PCI_VENDOR_ID_JANZ		0x13c3  /* The list of devices that this module will support */ -static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = { +static const struct pci_device_id cmodio_pci_ids[] = {  	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 },  	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 },  	{ 0, } diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 3c0e8cf6916..7a51c0d0d4f 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -181,7 +181,7 @@ static struct resource jz4740_battery_resources[] = {  	},  }; -static struct mfd_cell jz4740_adc_cells[] = { +static const struct mfd_cell jz4740_adc_cells[] = {  	{  		.id = 0,  		.name = "jz4740-hwmon", diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index d3e23278d29..f7ff0188603 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -86,7 +86,7 @@ enum kempld_cells {  	KEMPLD_UART,  }; -static struct mfd_cell kempld_devs[] = { +static const struct mfd_cell kempld_devs[] = {  	[KEMPLD_I2C] = {  		.name = "kempld-i2c",  	}, @@ -288,9 +288,38 @@ EXPORT_SYMBOL_GPL(kempld_release_mutex);   */  static int kempld_get_info(struct kempld_device_data *pld)  { +	int ret;  	struct kempld_platform_data *pdata = dev_get_platdata(pld->dev); +	char major, minor; + +	ret = pdata->get_info(pld); +	if (ret) +		return ret; + +	/* The Kontron PLD firmware version string has the following format: +	 * Pwxy.zzzz +	 *   P:    Fixed +	 *   w:    PLD number    - 1 hex digit +	 *   x:    Major version - 1 alphanumerical digit (0-9A-V) +	 *   y:    Minor version - 1 alphanumerical digit (0-9A-V) +	 *   zzzz: Build number  - 4 zero padded hex digits */ -	return pdata->get_info(pld); +	if (pld->info.major < 10) +		major = pld->info.major + '0'; +	else +		major = (pld->info.major - 10) + 'A'; +	if (pld->info.minor < 10) +		minor = pld->info.minor + '0'; +	else +		minor = (pld->info.minor - 10) + 'A'; + +	ret = scnprintf(pld->info.version, sizeof(pld->info.version), +			"P%X%c%c.%04X", pld->info.number, major, minor, +			pld->info.buildnr); +	if (ret < 0) +		return ret; + +	return 0;  }  /* @@ -307,9 +336,71 @@ static int kempld_register_cells(struct kempld_device_data *pld)  	return pdata->register_cells(pld);  } +static const char *kempld_get_type_string(struct kempld_device_data *pld) +{ +	const char *version_type; + +	switch (pld->info.type) { +	case 0: +		version_type = "release"; +		break; +	case 1: +		version_type = "debug"; +		break; +	case 2: +		version_type = "custom"; +		break; +	default: +		version_type = "unspecified"; +		break; +	} + +	return version_type; +} + +static ssize_t kempld_version_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct kempld_device_data *pld = dev_get_drvdata(dev); + +	return scnprintf(buf, PAGE_SIZE, "%s\n", pld->info.version); +} + +static ssize_t kempld_specification_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct kempld_device_data *pld = dev_get_drvdata(dev); + +	return scnprintf(buf, PAGE_SIZE, "%d.%d\n", pld->info.spec_major, +		       pld->info.spec_minor); +} + +static ssize_t kempld_type_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct kempld_device_data *pld = dev_get_drvdata(dev); + +	return scnprintf(buf, PAGE_SIZE, "%s\n", kempld_get_type_string(pld)); +} + +static DEVICE_ATTR(pld_version, S_IRUGO, kempld_version_show, NULL); +static DEVICE_ATTR(pld_specification, S_IRUGO, kempld_specification_show, +		   NULL); +static DEVICE_ATTR(pld_type, S_IRUGO, kempld_type_show, NULL); + +static struct attribute *pld_attributes[] = { +	&dev_attr_pld_version.attr, +	&dev_attr_pld_specification.attr, +	&dev_attr_pld_type.attr, +	NULL +}; + +static const struct attribute_group pld_attr_group = { +	.attrs = pld_attributes, +}; +  static int kempld_detect_device(struct kempld_device_data *pld)  { -	char *version_type;  	u8 index_reg;  	int ret; @@ -322,9 +413,12 @@ static int kempld_detect_device(struct kempld_device_data *pld)  		return -ENODEV;  	} -	/* Release hardware mutex if aquired */ -	if (!(index_reg & KEMPLD_MUTEX_KEY)) +	/* Release hardware mutex if acquired */ +	if (!(index_reg & KEMPLD_MUTEX_KEY)) { +		iowrite8(KEMPLD_MUTEX_KEY, pld->io_index); +		/* PXT and COMe-cPC2 boards may require a second release */  		iowrite8(KEMPLD_MUTEX_KEY, pld->io_index); +	}  	mutex_unlock(&pld->lock); @@ -332,27 +426,19 @@ static int kempld_detect_device(struct kempld_device_data *pld)  	if (ret)  		return ret; -	switch (pld->info.type) { -	case 0: -		version_type = "release"; -		break; -	case 1: -		version_type = "debug"; -		break; -	case 2: -		version_type = "custom"; -		break; -	default: -		version_type = "unspecified"; -	} +	dev_info(pld->dev, "Found Kontron PLD - %s (%s), spec %d.%d\n", +		 pld->info.version, kempld_get_type_string(pld), +		 pld->info.spec_major, pld->info.spec_minor); -	dev_info(pld->dev, "Found Kontron PLD %d\n", pld->info.number); -	dev_info(pld->dev, "%s version %d.%d build %d, specification %d.%d\n", -		 version_type, pld->info.major, pld->info.minor, -		 pld->info.buildnr, pld->info.spec_major, -		 pld->info.spec_minor); +	ret = sysfs_create_group(&pld->dev->kobj, &pld_attr_group); +	if (ret) +		return ret; -	return kempld_register_cells(pld); +	ret = kempld_register_cells(pld); +	if (ret) +		sysfs_remove_group(&pld->dev->kobj, &pld_attr_group); + +	return ret;  }  static int kempld_probe(struct platform_device *pdev) @@ -396,6 +482,8 @@ static int kempld_remove(struct platform_device *pdev)  	struct kempld_device_data *pld = platform_get_drvdata(pdev);  	struct kempld_platform_data *pdata = dev_get_platdata(pld->dev); +	sysfs_remove_group(&pld->dev->kobj, &pld_attr_group); +  	mfd_remove_devices(&pdev->dev);  	pdata->release_hardware_mutex(pld); @@ -438,6 +526,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {  		.driver_data = (void *)&kempld_platform_data_generic,  		.callback = kempld_create_platform_device,  	}, { +		.ident = "CHL6", +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), +			DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"), +		}, +		.driver_data = (void *)&kempld_platform_data_generic, +		.callback = kempld_create_platform_device, +	}, {  		.ident = "CHR2",  		.matches = {  			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), @@ -510,6 +606,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {  		.driver_data = (void *)&kempld_platform_data_generic,  		.callback = kempld_create_platform_device,  	}, { +		.ident = "CVV6", +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), +			DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"), +		}, +		.driver_data = (void *)&kempld_platform_data_generic, +		.callback = kempld_create_platform_device, +	}, {  		.ident = "FRI2",  		.matches = {  			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), @@ -533,6 +637,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {  		.driver_data = (void *)&kempld_platform_data_generic,  		.callback = kempld_create_platform_device,  	}, { +		.ident = "MVV1", +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), +			DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"), +		}, +		.driver_data = (void *)&kempld_platform_data_generic, +		.callback = kempld_create_platform_device, +	}, {  		.ident = "NTC1",  		.matches = {  			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), diff --git a/drivers/mfd/lp3943.c b/drivers/mfd/lp3943.c new file mode 100644 index 00000000000..335b930112b --- /dev/null +++ b/drivers/mfd/lp3943.c @@ -0,0 +1,167 @@ +/* + * TI/National Semiconductor LP3943 MFD Core Driver + * + * Copyright 2013 Texas Instruments + * + * Author: Milo Kim <milo.kim@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 as + * published by the Free Software Foundation. + * + * Driver structure: + *   LP3943 is an integrated device capable of driving 16 output channels. + *   It can be used for a GPIO expander and PWM generators. + * + *                                   LED control    General usage for a device + *                                   ___________   ____________________________ + * + *   LP3943 MFD ---- GPIO expander    leds-gpio        eg) HW enable pin + *               | + *               --- PWM generator    leds-pwm         eg) PWM input + * + *   Internal two PWM channels are used for LED dimming effect. + *   And each output pin can be used as a GPIO as well. + *   The LED functionality can work with GPIOs or PWMs. + *   LEDs can be controlled with legacy leds-gpio(static brightness) or + *   leds-pwm drivers(dynamic brightness control). + *   Alternatively, it can be used for generic GPIO and PWM controller. + *   For example, a GPIO is HW enable pin of a device. + *   A PWM is input pin of a backlight device. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/lp3943.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> + +#define LP3943_MAX_REGISTERS		0x09 + +/* Register configuration for pin MUX */ +static const struct lp3943_reg_cfg lp3943_mux_cfg[] = { +	/* address, mask, shift */ +	{ LP3943_REG_MUX0, 0x03, 0 }, +	{ LP3943_REG_MUX0, 0x0C, 2 }, +	{ LP3943_REG_MUX0, 0x30, 4 }, +	{ LP3943_REG_MUX0, 0xC0, 6 }, +	{ LP3943_REG_MUX1, 0x03, 0 }, +	{ LP3943_REG_MUX1, 0x0C, 2 }, +	{ LP3943_REG_MUX1, 0x30, 4 }, +	{ LP3943_REG_MUX1, 0xC0, 6 }, +	{ LP3943_REG_MUX2, 0x03, 0 }, +	{ LP3943_REG_MUX2, 0x0C, 2 }, +	{ LP3943_REG_MUX2, 0x30, 4 }, +	{ LP3943_REG_MUX2, 0xC0, 6 }, +	{ LP3943_REG_MUX3, 0x03, 0 }, +	{ LP3943_REG_MUX3, 0x0C, 2 }, +	{ LP3943_REG_MUX3, 0x30, 4 }, +	{ LP3943_REG_MUX3, 0xC0, 6 }, +}; + +static const struct mfd_cell lp3943_devs[] = { +	{ +		.name = "lp3943-pwm", +		.of_compatible = "ti,lp3943-pwm", +	}, +	{ +		.name = "lp3943-gpio", +		.of_compatible = "ti,lp3943-gpio", +	}, +}; + +int lp3943_read_byte(struct lp3943 *lp3943, u8 reg, u8 *read) +{ +	int ret; +	unsigned int val; + +	ret = regmap_read(lp3943->regmap, reg, &val); +	if (ret < 0) +		return ret; + +	*read = (u8)val; +	return 0; +} +EXPORT_SYMBOL_GPL(lp3943_read_byte); + +int lp3943_write_byte(struct lp3943 *lp3943, u8 reg, u8 data) +{ +	return regmap_write(lp3943->regmap, reg, data); +} +EXPORT_SYMBOL_GPL(lp3943_write_byte); + +int lp3943_update_bits(struct lp3943 *lp3943, u8 reg, u8 mask, u8 data) +{ +	return regmap_update_bits(lp3943->regmap, reg, mask, data); +} +EXPORT_SYMBOL_GPL(lp3943_update_bits); + +static const struct regmap_config lp3943_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.max_register = LP3943_MAX_REGISTERS, +}; + +static int lp3943_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ +	struct lp3943 *lp3943; +	struct device *dev = &cl->dev; + +	lp3943 = devm_kzalloc(dev, sizeof(*lp3943), GFP_KERNEL); +	if (!lp3943) +		return -ENOMEM; + +	lp3943->regmap = devm_regmap_init_i2c(cl, &lp3943_regmap_config); +	if (IS_ERR(lp3943->regmap)) +		return PTR_ERR(lp3943->regmap); + +	lp3943->pdata = dev_get_platdata(dev); +	lp3943->dev = dev; +	lp3943->mux_cfg = lp3943_mux_cfg; +	i2c_set_clientdata(cl, lp3943); + +	return mfd_add_devices(dev, -1, lp3943_devs, ARRAY_SIZE(lp3943_devs), +			       NULL, 0, NULL); +} + +static int lp3943_remove(struct i2c_client *cl) +{ +	struct lp3943 *lp3943 = i2c_get_clientdata(cl); + +	mfd_remove_devices(lp3943->dev); +	return 0; +} + +static const struct i2c_device_id lp3943_ids[] = { +	{ "lp3943", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, lp3943_ids); + +#ifdef CONFIG_OF +static const struct of_device_id lp3943_of_match[] = { +	{ .compatible = "ti,lp3943", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, lp3943_of_match); +#endif + +static struct i2c_driver lp3943_driver = { +	.probe = lp3943_probe, +	.remove = lp3943_remove, +	.driver = { +		.name = "lp3943", +		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(lp3943_of_match), +	}, +	.id_table = lp3943_ids, +}; + +module_i2c_driver(lp3943_driver); + +MODULE_DESCRIPTION("LP3943 MFD Core Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c index 0f122191101..a30bc15fe5b 100644 --- a/drivers/mfd/lp8788.c +++ b/drivers/mfd/lp8788.c @@ -71,7 +71,7 @@ static struct resource rtc_irqs[] = {  	},  }; -static struct mfd_cell lp8788_devs[] = { +static const struct mfd_cell lp8788_devs[] = {  	/* 4 bucks */  	MFD_DEV_WITH_ID(BUCK, 1),  	MFD_DEV_WITH_ID(BUCK, 2), diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 9483bc8472a..7d8482ff586 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -53,11 +53,11 @@   *	document number TBD : Wellsburg   *	document number TBD : Avoton SoC   *	document number TBD : Coleto Creek + *	document number TBD : Wildcat Point-LP   */  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/init.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/errno.h> @@ -71,9 +71,11 @@  #define ACPIBASE_GPE_END	0x2f  #define ACPIBASE_SMI_OFF	0x30  #define ACPIBASE_SMI_END	0x33 +#define ACPIBASE_PMC_OFF	0x08 +#define ACPIBASE_PMC_END	0x0c  #define ACPIBASE_TCO_OFF	0x60  #define ACPIBASE_TCO_END	0x7f -#define ACPICTRL		0x44 +#define ACPICTRL_PMCBASE	0x44  #define ACPIBASE_GCS_OFF	0x3410  #define ACPIBASE_GCS_END	0x3414 @@ -89,16 +91,17 @@  #define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)  #define wdt_res(b, i) (&wdt_ich_res[(b) + (i)]) -struct lpc_ich_cfg { -	int base; -	int ctrl; -	int save; -}; -  struct lpc_ich_priv {  	int chipset; -	struct lpc_ich_cfg acpi; -	struct lpc_ich_cfg gpio; + +	int abase;		/* ACPI base */ +	int actrl_pbase;	/* ACPI control or PMC base */ +	int gbase;		/* GPIO base */ +	int gctrl;		/* GPIO control */ + +	int abase_save;		/* Cached ACPI base value */ +	int actrl_pbase_save;		/* Cached ACPI control or PMC base value */ +	int gctrl_save;		/* Cached GPIO control value */  };  static struct resource wdt_ich_res[] = { @@ -110,7 +113,7 @@ static struct resource wdt_ich_res[] = {  	{  		.flags = IORESOURCE_IO,  	}, -	/* GCS */ +	/* GCS or PMC */  	{  		.flags = IORESOURCE_MEM,  	}, @@ -210,7 +213,9 @@ enum lpc_chipsets {  	LPC_LPT_LP,	/* Lynx Point-LP */  	LPC_WBG,	/* Wellsburg */  	LPC_AVN,	/* Avoton SoC */ +	LPC_BAYTRAIL,   /* Bay Trail SoC */  	LPC_COLETO,	/* Coleto Creek */ +	LPC_WPT_LP,	/* Wildcat Point-LP */  };  static struct lpc_ich_info lpc_chipset_info[] = { @@ -301,6 +306,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {  	[LPC_NM10] = {  		.name = "NM10",  		.iTCO_version = 2, +		.gpio_version = ICH_V7_GPIO,  	},  	[LPC_ICH8] = {  		.name = "ICH8 or ICH8R", @@ -482,6 +488,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {  	[LPC_PPT] = {  		.name = "Panther Point",  		.iTCO_version = 2, +		.gpio_version = ICH_V5_GPIO,  	},  	[LPC_LPT] = {  		.name = "Lynx Point", @@ -497,12 +504,21 @@ static struct lpc_ich_info lpc_chipset_info[] = {  	},  	[LPC_AVN] = {  		.name = "Avoton SoC", -		.iTCO_version = 1, +		.iTCO_version = 3, +		.gpio_version = AVOTON_GPIO, +	}, +	[LPC_BAYTRAIL] = { +		.name = "Bay Trail SoC", +		.iTCO_version = 3,  	},  	[LPC_COLETO] = {  		.name = "Coleto Creek",  		.iTCO_version = 2,  	}, +	[LPC_WPT_LP] = { +		.name = "Wildcat Point_LP", +		.iTCO_version = 2, +	},  };  /* @@ -511,7 +527,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {   * pci_driver, because the I/O Controller Hub has also other   * functions that probably will be registered by other drivers.   */ -static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = { +static const struct pci_device_id lpc_ich_ids[] = {  	{ PCI_VDEVICE(INTEL, 0x2410), LPC_ICH},  	{ PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0},  	{ PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2}, @@ -720,7 +736,15 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {  	{ PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN},  	{ PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN},  	{ PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN}, +	{ PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL},  	{ PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO}, +	{ PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP}, +	{ PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP}, +	{ PCI_VDEVICE(INTEL, 0x9cc3), LPC_WPT_LP}, +	{ PCI_VDEVICE(INTEL, 0x9cc5), LPC_WPT_LP}, +	{ PCI_VDEVICE(INTEL, 0x9cc6), LPC_WPT_LP}, +	{ PCI_VDEVICE(INTEL, 0x9cc7), LPC_WPT_LP}, +	{ PCI_VDEVICE(INTEL, 0x9cc9), LPC_WPT_LP},  	{ 0, },			/* End of list */  };  MODULE_DEVICE_TABLE(pci, lpc_ich_ids); @@ -729,14 +753,20 @@ static void lpc_ich_restore_config_space(struct pci_dev *dev)  {  	struct lpc_ich_priv *priv = pci_get_drvdata(dev); -	if (priv->acpi.save >= 0) { -		pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save); -		priv->acpi.save = -1; +	if (priv->abase_save >= 0) { +		pci_write_config_byte(dev, priv->abase, priv->abase_save); +		priv->abase_save = -1; +	} + +	if (priv->actrl_pbase_save >= 0) { +		pci_write_config_byte(dev, priv->actrl_pbase, +			priv->actrl_pbase_save); +		priv->actrl_pbase_save = -1;  	} -	if (priv->gpio.save >= 0) { -		pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save); -		priv->gpio.save = -1; +	if (priv->gctrl_save >= 0) { +		pci_write_config_byte(dev, priv->gctrl, priv->gctrl_save); +		priv->gctrl_save = -1;  	}  } @@ -745,9 +775,26 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev)  	struct lpc_ich_priv *priv = pci_get_drvdata(dev);  	u8 reg_save; -	pci_read_config_byte(dev, priv->acpi.ctrl, ®_save); -	pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10); -	priv->acpi.save = reg_save; +	switch (lpc_chipset_info[priv->chipset].iTCO_version) { +	case 3: +		/* +		 * Some chipsets (eg Avoton) enable the ACPI space in the +		 * ACPI BASE register. +		 */ +		pci_read_config_byte(dev, priv->abase, ®_save); +		pci_write_config_byte(dev, priv->abase, reg_save | 0x2); +		priv->abase_save = reg_save; +		break; +	default: +		/* +		 * Most chipsets enable the ACPI space in the ACPI control +		 * register. +		 */ +		pci_read_config_byte(dev, priv->actrl_pbase, ®_save); +		pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x80); +		priv->actrl_pbase_save = reg_save; +		break; +	}  }  static void lpc_ich_enable_gpio_space(struct pci_dev *dev) @@ -755,9 +802,20 @@ static void lpc_ich_enable_gpio_space(struct pci_dev *dev)  	struct lpc_ich_priv *priv = pci_get_drvdata(dev);  	u8 reg_save; -	pci_read_config_byte(dev, priv->gpio.ctrl, ®_save); -	pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10); -	priv->gpio.save = reg_save; +	pci_read_config_byte(dev, priv->gctrl, ®_save); +	pci_write_config_byte(dev, priv->gctrl, reg_save | 0x10); +	priv->gctrl_save = reg_save; +} + +static void lpc_ich_enable_pmc_space(struct pci_dev *dev) +{ +	struct lpc_ich_priv *priv = pci_get_drvdata(dev); +	u8 reg_save; + +	pci_read_config_byte(dev, priv->actrl_pbase, ®_save); +	pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x2); + +	priv->actrl_pbase_save = reg_save;  }  static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell) @@ -802,7 +860,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)  	struct resource *res;  	/* Setup power management base register */ -	pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); +	pci_read_config_dword(dev, priv->abase, &base_addr_cfg);  	base_addr = base_addr_cfg & 0x0000ff80;  	if (!base_addr) {  		dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -828,7 +886,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)  gpe0_done:  	/* Setup GPIO base register */ -	pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg); +	pci_read_config_dword(dev, priv->gbase, &base_addr_cfg);  	base_addr = base_addr_cfg & 0x0000ff80;  	if (!base_addr) {  		dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n"); @@ -878,7 +936,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)  	struct resource *res;  	/* Setup power management base register */ -	pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); +	pci_read_config_dword(dev, priv->abase, &base_addr_cfg);  	base_addr = base_addr_cfg & 0x0000ff80;  	if (!base_addr) {  		dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -897,14 +955,20 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)  	lpc_ich_enable_acpi_space(dev);  	/* +	 * iTCO v2:  	 * Get the Memory-Mapped GCS register. To get access to it  	 * we have to read RCBA from PCI Config space 0xf0 and use  	 * it as base. GCS = RCBA + ICH6_GCS(0x3410). +	 * +	 * iTCO v3: +	 * Get the Power Management Configuration register.  To get access +	 * to it we have to read the PMC BASE from config space and address +	 * the register at offset 0x8.  	 */  	if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {  		/* Don't register iomem for TCO ver 1 */  		lpc_ich_cells[LPC_WDT].num_resources--; -	} else { +	} else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) {  		pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);  		base_addr = base_addr_cfg & 0xffffc000;  		if (!(base_addr_cfg & 1)) { @@ -913,9 +977,17 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)  			ret = -ENODEV;  			goto wdt_done;  		} -		res = wdt_mem_res(ICH_RES_MEM_GCS); +		res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);  		res->start = base_addr + ACPIBASE_GCS_OFF;  		res->end = base_addr + ACPIBASE_GCS_END; +	} else if (lpc_chipset_info[priv->chipset].iTCO_version == 3) { +		lpc_ich_enable_pmc_space(dev); +		pci_read_config_dword(dev, ACPICTRL_PMCBASE, &base_addr_cfg); +		base_addr = base_addr_cfg & 0xfffffe00; + +		res = wdt_mem_res(ICH_RES_MEM_GCS_PMC); +		res->start = base_addr + ACPIBASE_PMC_OFF; +		res->end = base_addr + ACPIBASE_PMC_END;  	}  	lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]); @@ -939,28 +1011,35 @@ static int lpc_ich_probe(struct pci_dev *dev,  		return -ENOMEM;  	priv->chipset = id->driver_data; -	priv->acpi.save = -1; -	priv->acpi.base = ACPIBASE; -	priv->acpi.ctrl = ACPICTRL; -	priv->gpio.save = -1; +	priv->actrl_pbase_save = -1; +	priv->abase_save = -1; + +	priv->abase = ACPIBASE; +	priv->actrl_pbase = ACPICTRL_PMCBASE; + +	priv->gctrl_save = -1;  	if (priv->chipset <= LPC_ICH5) { -		priv->gpio.base = GPIOBASE_ICH0; -		priv->gpio.ctrl = GPIOCTRL_ICH0; +		priv->gbase = GPIOBASE_ICH0; +		priv->gctrl = GPIOCTRL_ICH0;  	} else { -		priv->gpio.base = GPIOBASE_ICH6; -		priv->gpio.ctrl = GPIOCTRL_ICH6; +		priv->gbase = GPIOBASE_ICH6; +		priv->gctrl = GPIOCTRL_ICH6;  	}  	pci_set_drvdata(dev, priv); -	ret = lpc_ich_init_wdt(dev); -	if (!ret) -		cell_added = true; +	if (lpc_chipset_info[priv->chipset].iTCO_version) { +		ret = lpc_ich_init_wdt(dev); +		if (!ret) +			cell_added = true; +	} -	ret = lpc_ich_init_gpio(dev); -	if (!ret) -		cell_added = true; +	if (lpc_chipset_info[priv->chipset].gpio_version) { +		ret = lpc_ich_init_gpio(dev); +		if (!ret) +			cell_added = true; +	}  	/*  	 * We only care if at least one or none of the cells registered @@ -969,7 +1048,6 @@ static int lpc_ich_probe(struct pci_dev *dev,  	if (!cell_added) {  		dev_warn(&dev->dev, "No MFD cells added\n");  		lpc_ich_restore_config_space(dev); -		pci_set_drvdata(dev, NULL);  		return -ENODEV;  	} @@ -980,7 +1058,6 @@ static void lpc_ich_remove(struct pci_dev *dev)  {  	mfd_remove_devices(&dev->dev);  	lpc_ich_restore_config_space(dev); -	pci_set_drvdata(dev, NULL);  }  static struct pci_driver lpc_ich_driver = { diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 8cc6aac27cb..4ee755034f3 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -23,7 +23,6 @@   *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.   */ -#include <linux/init.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/errno.h> @@ -59,21 +58,24 @@ static struct mfd_cell isch_smbus_cell = {  	.name = "isch_smbus",  	.num_resources = 1,  	.resources = &smbus_sch_resource, +	.ignore_resource_conflicts = true,  };  static struct mfd_cell sch_gpio_cell = {  	.name = "sch_gpio",  	.num_resources = 1,  	.resources = &gpio_sch_resource, +	.ignore_resource_conflicts = true,  };  static struct mfd_cell wdt_sch_cell = {  	.name = "ie6xx_wdt",  	.num_resources = 1,  	.resources = &wdt_sch_resource, +	.ignore_resource_conflicts = true,  }; -static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = { +static const struct pci_device_id lpc_sch_ids[] = {  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) }, diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c new file mode 100644 index 00000000000..4a5e885383f --- /dev/null +++ b/drivers/mfd/max14577.c @@ -0,0 +1,478 @@ +/* + * max14577.c - mfd core driver for the Maxim 14577/77836 + * + * Copyright (C) 2014 Samsung Electrnoics + * Chanwoo Choi <cw00.choi@samsung.com> + * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 is based on max8997.c + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/of_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max14577.h> +#include <linux/mfd/max14577-private.h> + +static const struct mfd_cell max14577_devs[] = { +	{ +		.name = "max14577-muic", +		.of_compatible = "maxim,max14577-muic", +	}, +	{ +		.name = "max14577-regulator", +		.of_compatible = "maxim,max14577-regulator", +	}, +	{ .name = "max14577-charger", }, +}; + +static const struct mfd_cell max77836_devs[] = { +	{ +		.name = "max77836-muic", +		.of_compatible = "maxim,max77836-muic", +	}, +	{ +		.name = "max77836-regulator", +		.of_compatible = "maxim,max77836-regulator", +	}, +	{ +		.name = "max77836-charger", +		.of_compatible = "maxim,max77836-charger", +	}, +	{ +		.name = "max77836-battery", +		.of_compatible = "maxim,max77836-battery", +	}, +}; + +static const struct of_device_id max14577_dt_match[] = { +	{ +		.compatible = "maxim,max14577", +		.data = (void *)MAXIM_DEVICE_TYPE_MAX14577, +	}, +	{ +		.compatible = "maxim,max77836", +		.data = (void *)MAXIM_DEVICE_TYPE_MAX77836, +	}, +	{}, +}; + +static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3: +		return true; +	default: +		break; +	} +	return false; +} + +static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg) +{ +	/* Any max14577 volatile registers are also max77836 volatile. */ +	if (max14577_muic_volatile_reg(dev, reg)) +		return true; + +	switch (reg) { +	case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB: +	case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB: +	case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L: +	case MAX77836_PMIC_REG_INTSRC: +	case MAX77836_PMIC_REG_TOPSYS_INT: +	case MAX77836_PMIC_REG_TOPSYS_STAT: +		return true; +	default: +		break; +	} +	return false; +} + +static const struct regmap_config max14577_muic_regmap_config = { +	.reg_bits	= 8, +	.val_bits	= 8, +	.volatile_reg	= max14577_muic_volatile_reg, +	.max_register	= MAX14577_REG_END, +}; + +static const struct regmap_config max77836_pmic_regmap_config = { +	.reg_bits	= 8, +	.val_bits	= 8, +	.volatile_reg	= max77836_muic_volatile_reg, +	.max_register	= MAX77836_PMIC_REG_END, +}; + +static const struct regmap_irq max14577_irqs[] = { +	/* INT1 interrupts */ +	{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, }, +	{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, }, +	{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, }, +	/* INT2 interrupts */ +	{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, }, +	/* INT3 interrupts */ +	{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, }, +	{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, }, +	{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, }, +	{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, }, +}; + +static const struct regmap_irq_chip max14577_irq_chip = { +	.name			= "max14577", +	.status_base		= MAX14577_REG_INT1, +	.mask_base		= MAX14577_REG_INTMASK1, +	.mask_invert		= true, +	.num_regs		= 3, +	.irqs			= max14577_irqs, +	.num_irqs		= ARRAY_SIZE(max14577_irqs), +}; + +static const struct regmap_irq max77836_muic_irqs[] = { +	/* INT1 interrupts */ +	{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, }, +	{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, }, +	{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, }, +	{ .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, }, +	/* INT2 interrupts */ +	{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, }, +	{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, }, +	{ .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, }, +	/* INT3 interrupts */ +	{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, }, +	{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, }, +	{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, }, +	{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, }, +}; + +static const struct regmap_irq_chip max77836_muic_irq_chip = { +	.name			= "max77836-muic", +	.status_base		= MAX14577_REG_INT1, +	.mask_base		= MAX14577_REG_INTMASK1, +	.mask_invert		= true, +	.num_regs		= 3, +	.irqs			= max77836_muic_irqs, +	.num_irqs		= ARRAY_SIZE(max77836_muic_irqs), +}; + +static const struct regmap_irq max77836_pmic_irqs[] = { +	{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, }, +	{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, }, +}; + +static const struct regmap_irq_chip max77836_pmic_irq_chip = { +	.name			= "max77836-pmic", +	.status_base		= MAX77836_PMIC_REG_TOPSYS_INT, +	.mask_base		= MAX77836_PMIC_REG_TOPSYS_INT_MASK, +	.mask_invert		= false, +	.num_regs		= 1, +	.irqs			= max77836_pmic_irqs, +	.num_irqs		= ARRAY_SIZE(max77836_pmic_irqs), +}; + +static void max14577_print_dev_type(struct max14577 *max14577) +{ +	u8 reg_data, vendor_id, device_id; +	int ret; + +	ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID, +			®_data); +	if (ret) { +		dev_err(max14577->dev, +			"Failed to read DEVICEID register: %d\n", ret); +		return; +	} + +	vendor_id = ((reg_data & DEVID_VENDORID_MASK) >> +				DEVID_VENDORID_SHIFT); +	device_id = ((reg_data & DEVID_DEVICEID_MASK) >> +				DEVID_DEVICEID_SHIFT); + +	dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n", +			max14577->dev_type, device_id, vendor_id); +} + +/* + * Max77836 specific initialization code for driver probe. + * Adds new I2C dummy device, regmap and regmap IRQ chip. + * Unmasks Interrupt Source register. + * + * On success returns 0. + * On failure returns errno and reverts any changes done so far (e.g. remove + * I2C dummy device), except masking the INT SRC register. + */ +static int max77836_init(struct max14577 *max14577) +{ +	int ret; +	u8 intsrc_mask; + +	max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter, +			I2C_ADDR_PMIC); +	if (!max14577->i2c_pmic) { +		dev_err(max14577->dev, "Failed to register PMIC I2C device\n"); +		return -ENODEV; +	} +	i2c_set_clientdata(max14577->i2c_pmic, max14577); + +	max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic, +			&max77836_pmic_regmap_config); +	if (IS_ERR(max14577->regmap_pmic)) { +		ret = PTR_ERR(max14577->regmap_pmic); +		dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n", +				ret); +		goto err; +	} + +	/* Un-mask MAX77836 Interrupt Source register */ +	ret = max14577_read_reg(max14577->regmap_pmic, +			MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask); +	if (ret < 0) { +		dev_err(max14577->dev, "Failed to read PMIC register\n"); +		goto err; +	} + +	intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK); +	intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK); +	ret = max14577_write_reg(max14577->regmap_pmic, +			MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask); +	if (ret < 0) { +		dev_err(max14577->dev, "Failed to write PMIC register\n"); +		goto err; +	} + +	ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq, +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED, +			0, &max77836_pmic_irq_chip, +			&max14577->irq_data_pmic); +	if (ret != 0) { +		dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n", +				max14577->irq, ret); +		goto err; +	} + +	return 0; + +err: +	i2c_unregister_device(max14577->i2c_pmic); + +	return ret; +} + +/* + * Max77836 specific de-initialization code for driver remove. + */ +static void max77836_remove(struct max14577 *max14577) +{ +	regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic); +	i2c_unregister_device(max14577->i2c_pmic); +} + +static int max14577_i2c_probe(struct i2c_client *i2c, +			      const struct i2c_device_id *id) +{ +	struct max14577 *max14577; +	struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev); +	struct device_node *np = i2c->dev.of_node; +	int ret = 0; +	const struct regmap_irq_chip *irq_chip; +	const struct mfd_cell *mfd_devs; +	unsigned int mfd_devs_size; +	int irq_flags; + +	if (np) { +		pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); +		if (!pdata) +			return -ENOMEM; +		i2c->dev.platform_data = pdata; +	} + +	if (!pdata) { +		dev_err(&i2c->dev, "No platform data found.\n"); +		return -EINVAL; +	} + +	max14577 = devm_kzalloc(&i2c->dev, sizeof(*max14577), GFP_KERNEL); +	if (!max14577) +		return -ENOMEM; + +	i2c_set_clientdata(i2c, max14577); +	max14577->dev = &i2c->dev; +	max14577->i2c = i2c; +	max14577->irq = i2c->irq; + +	max14577->regmap = devm_regmap_init_i2c(i2c, +			&max14577_muic_regmap_config); +	if (IS_ERR(max14577->regmap)) { +		ret = PTR_ERR(max14577->regmap); +		dev_err(max14577->dev, "Failed to allocate register map: %d\n", +				ret); +		return ret; +	} + +	if (np) { +		const struct of_device_id *of_id; + +		of_id = of_match_device(max14577_dt_match, &i2c->dev); +		if (of_id) +			max14577->dev_type = +				(enum maxim_device_type)of_id->data; +	} else { +		max14577->dev_type = id->driver_data; +	} + +	max14577_print_dev_type(max14577); + +	switch (max14577->dev_type) { +	case MAXIM_DEVICE_TYPE_MAX77836: +		irq_chip = &max77836_muic_irq_chip; +		mfd_devs = max77836_devs; +		mfd_devs_size = ARRAY_SIZE(max77836_devs); +		irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; +		break; +	case MAXIM_DEVICE_TYPE_MAX14577: +	default: +		irq_chip = &max14577_irq_chip; +		mfd_devs = max14577_devs; +		mfd_devs_size = ARRAY_SIZE(max14577_devs); +		irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; +		break; +	} + +	ret = regmap_add_irq_chip(max14577->regmap, max14577->irq, +				  irq_flags, 0, irq_chip, +				  &max14577->irq_data); +	if (ret != 0) { +		dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", +				max14577->irq, ret); +		return ret; +	} + +	/* Max77836 specific initialization code (additional regmap) */ +	if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) { +		ret = max77836_init(max14577); +		if (ret < 0) +			goto err_max77836; +	} + +	ret = mfd_add_devices(max14577->dev, -1, mfd_devs, +			mfd_devs_size, NULL, 0, +			regmap_irq_get_domain(max14577->irq_data)); +	if (ret < 0) +		goto err_mfd; + +	device_init_wakeup(max14577->dev, 1); + +	return 0; + +err_mfd: +	if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) +		max77836_remove(max14577); +err_max77836: +	regmap_del_irq_chip(max14577->irq, max14577->irq_data); + +	return ret; +} + +static int max14577_i2c_remove(struct i2c_client *i2c) +{ +	struct max14577 *max14577 = i2c_get_clientdata(i2c); + +	mfd_remove_devices(max14577->dev); +	regmap_del_irq_chip(max14577->irq, max14577->irq_data); +	if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) +		max77836_remove(max14577); + +	return 0; +} + +static const struct i2c_device_id max14577_i2c_id[] = { +	{ "max14577", MAXIM_DEVICE_TYPE_MAX14577, }, +	{ "max77836", MAXIM_DEVICE_TYPE_MAX77836, }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, max14577_i2c_id); + +#ifdef CONFIG_PM_SLEEP +static int max14577_suspend(struct device *dev) +{ +	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); +	struct max14577 *max14577 = i2c_get_clientdata(i2c); + +	if (device_may_wakeup(dev)) +		enable_irq_wake(max14577->irq); +	/* +	 * MUIC IRQ must be disabled during suspend because if it happens +	 * while suspended it will be handled before resuming I2C. +	 * +	 * When device is woken up from suspend (e.g. by ADC change), +	 * an interrupt occurs before resuming I2C bus controller. +	 * Interrupt handler tries to read registers but this read +	 * will fail because I2C is still suspended. +	 */ +	disable_irq(max14577->irq); + +	return 0; +} + +static int max14577_resume(struct device *dev) +{ +	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); +	struct max14577 *max14577 = i2c_get_clientdata(i2c); + +	if (device_may_wakeup(dev)) +		disable_irq_wake(max14577->irq); +	enable_irq(max14577->irq); + +	return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume); + +static struct i2c_driver max14577_i2c_driver = { +	.driver = { +		.name = "max14577", +		.owner = THIS_MODULE, +		.pm = &max14577_pm, +		.of_match_table = max14577_dt_match, +	}, +	.probe = max14577_i2c_probe, +	.remove = max14577_i2c_remove, +	.id_table = max14577_i2c_id, +}; + +static int __init max14577_i2c_init(void) +{ +	BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM); +	BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM); + +	return i2c_add_driver(&max14577_i2c_driver); +} +subsys_initcall(max14577_i2c_init); + +static void __exit max14577_i2c_exit(void) +{ +	i2c_del_driver(&max14577_i2c_driver); +} +module_exit(max14577_i2c_exit); + +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 522be67b2e6..ce869acf27a 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -31,10 +31,11 @@  #include <linux/mfd/max77686.h>  #include <linux/mfd/max77686-private.h>  #include <linux/err.h> +#include <linux/of.h>  #define I2C_ADDR_RTC	(0x0C >> 1) -static struct mfd_cell max77686_devs[] = { +static const struct mfd_cell max77686_devs[] = {  	{ .name = "max77686-pmic", },  	{ .name = "max77686-rtc", },  	{ .name = "max77686-clk", }, @@ -46,7 +47,7 @@ static struct regmap_config max77686_regmap_config = {  };  #ifdef CONFIG_OF -static struct of_device_id max77686_pmic_dt_match[] = { +static const struct of_device_id max77686_pmic_dt_match[] = {  	{.compatible = "maxim,max77686", .data = NULL},  	{},  }; @@ -103,7 +104,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c,  	max77686->irq_gpio = pdata->irq_gpio;  	max77686->irq = i2c->irq; -	max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config); +	max77686->regmap = devm_regmap_init_i2c(i2c, &max77686_regmap_config);  	if (IS_ERR(max77686->regmap)) {  		ret = PTR_ERR(max77686->regmap);  		dev_err(max77686->dev, "Failed to allocate register map: %d\n", @@ -120,6 +121,10 @@ static int max77686_i2c_probe(struct i2c_client *i2c,  		dev_info(max77686->dev, "device found\n");  	max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); +	if (!max77686->rtc) { +		dev_err(max77686->dev, "Failed to allocate I2C device for RTC\n"); +		return -ENODEV; +	}  	i2c_set_clientdata(max77686->rtc, max77686);  	max77686_irq_init(max77686); diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c index 1029d018c73..66b58fe7709 100644 --- a/drivers/mfd/max77693-irq.c +++ b/drivers/mfd/max77693-irq.c @@ -128,7 +128,8 @@ static void max77693_irq_sync_unlock(struct irq_data *data)  static const inline struct max77693_irq_data *  irq_to_max77693_irq(struct max77693_dev *max77693, int irq)  { -	return &max77693_irqs[irq]; +	struct irq_data *data = irq_get_irq_data(irq); +	return &max77693_irqs[data->hwirq];  }  static void max77693_irq_mask(struct irq_data *data) diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index c04723efc70..7e05428c756 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -28,6 +28,7 @@  #include <linux/i2c.h>  #include <linux/err.h>  #include <linux/interrupt.h> +#include <linux/of.h>  #include <linux/pm_runtime.h>  #include <linux/mutex.h>  #include <linux/mfd/core.h> @@ -40,7 +41,7 @@  #define I2C_ADDR_MUIC	(0x4A >> 1)  #define I2C_ADDR_HAPTIC	(0x90 >> 1) -static struct mfd_cell max77693_devs[] = { +static const struct mfd_cell max77693_devs[] = {  	{ .name = "max77693-pmic", },  	{ .name = "max77693-charger", },  	{ .name = "max77693-flash", }, @@ -106,19 +107,19 @@ static const struct regmap_config max77693_regmap_config = {  	.max_register = MAX77693_PMIC_REG_END,  }; +static const struct regmap_config max77693_regmap_muic_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.max_register = MAX77693_MUIC_REG_END, +}; +  static int max77693_i2c_probe(struct i2c_client *i2c,  			      const struct i2c_device_id *id)  {  	struct max77693_dev *max77693; -	struct max77693_platform_data *pdata = dev_get_platdata(&i2c->dev);  	u8 reg_data;  	int ret = 0; -	if (!pdata) { -		dev_err(&i2c->dev, "No platform data found.\n"); -		return -EINVAL; -	} -  	max77693 = devm_kzalloc(&i2c->dev,  			sizeof(struct max77693_dev), GFP_KERNEL);  	if (max77693 == NULL) @@ -138,8 +139,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c,  		return ret;  	} -	max77693->wakeup = pdata->wakeup; -  	ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,  				®_data);  	if (ret < 0) { @@ -149,9 +148,18 @@ static int max77693_i2c_probe(struct i2c_client *i2c,  		dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);  	max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); +	if (!max77693->muic) { +		dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n"); +		return -ENODEV; +	}  	i2c_set_clientdata(max77693->muic, max77693);  	max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); +	if (!max77693->haptic) { +		dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n"); +		ret = -ENODEV; +		goto err_i2c_haptic; +	}  	i2c_set_clientdata(max77693->haptic, max77693);  	/* @@ -160,7 +168,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,  	 * before call max77693-muic probe() function.  	 */  	max77693->regmap_muic = devm_regmap_init_i2c(max77693->muic, -					 &max77693_regmap_config); +					 &max77693_regmap_muic_config);  	if (IS_ERR(max77693->regmap_muic)) {  		ret = PTR_ERR(max77693->regmap_muic);  		dev_err(max77693->dev, @@ -179,16 +187,15 @@ static int max77693_i2c_probe(struct i2c_client *i2c,  	if (ret < 0)  		goto err_mfd; -	device_init_wakeup(max77693->dev, pdata->wakeup); -  	return ret;  err_mfd:  	max77693_irq_exit(max77693);  err_irq:  err_regmap_muic: -	i2c_unregister_device(max77693->muic);  	i2c_unregister_device(max77693->haptic); +err_i2c_haptic: +	i2c_unregister_device(max77693->muic);  	return ret;  } @@ -235,11 +242,19 @@ static const struct dev_pm_ops max77693_pm = {  	.resume = max77693_resume,  }; +#ifdef CONFIG_OF +static const struct of_device_id max77693_dt_match[] = { +	{ .compatible = "maxim,max77693" }, +	{}, +}; +#endif +  static struct i2c_driver max77693_i2c_driver = {  	.driver = {  		   .name = "max77693",  		   .owner = THIS_MODULE,  		   .pm = &max77693_pm, +		   .of_match_table = of_match_ptr(max77693_dt_match),  	},  	.probe = max77693_i2c_probe,  	.remove = max77693_i2c_remove, diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c index e9b1c93a3ad..232749c8813 100644 --- a/drivers/mfd/max8907.c +++ b/drivers/mfd/max8907.c @@ -17,11 +17,12 @@  #include <linux/mfd/core.h>  #include <linux/mfd/max8907.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/of_device.h>  #include <linux/regmap.h>  #include <linux/slab.h> -static struct mfd_cell max8907_cells[] = { +static const struct mfd_cell max8907_cells[] = {  	{ .name = "max8907-regulator", },  	{ .name = "max8907-rtc", },  }; @@ -304,7 +305,7 @@ static int max8907_i2c_remove(struct i2c_client *i2c)  }  #ifdef CONFIG_OF -static struct of_device_id max8907_of_match[] = { +static const struct of_device_id max8907_of_match[] = {  	{ .compatible = "maxim,max8907" },  	{ },  }; diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index f0cc40296d8..f3faf0c45dd 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -45,7 +45,7 @@ static struct resource touch_resources[] = {  	},  }; -static struct mfd_cell touch_devs[] = { +static const struct mfd_cell touch_devs[] = {  	{  		.name		= "max8925-touch",  		.num_resources	= 1, @@ -63,7 +63,7 @@ static struct resource power_supply_resources[] = {  	},  }; -static struct mfd_cell power_devs[] = { +static const struct mfd_cell power_devs[] = {  	{  		.name		= "max8925-power",  		.num_resources	= 1, @@ -81,7 +81,7 @@ static struct resource rtc_resources[] = {  	},  }; -static struct mfd_cell rtc_devs[] = { +static const struct mfd_cell rtc_devs[] = {  	{  		.name		= "max8925-rtc",  		.num_resources	= 1, @@ -104,7 +104,7 @@ static struct resource onkey_resources[] = {  	},  }; -static struct mfd_cell onkey_devs[] = { +static const struct mfd_cell onkey_devs[] = {  	{  		.name		= "max8925-onkey",  		.num_resources	= 2, diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c index de7fb80a605..a83eed5c15c 100644 --- a/drivers/mfd/max8925-i2c.c +++ b/drivers/mfd/max8925-i2c.c @@ -181,9 +181,18 @@ static int max8925_probe(struct i2c_client *client,  	mutex_init(&chip->io_lock);  	chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR); +	if (!chip->rtc) { +		dev_err(chip->dev, "Failed to allocate I2C device for RTC\n"); +		return -ENODEV; +	}  	i2c_set_clientdata(chip->rtc, chip);  	chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR); +	if (!chip->adc) { +		dev_err(chip->dev, "Failed to allocate I2C device for ADC\n"); +		i2c_unregister_device(chip->rtc); +		return -ENODEV; +	}  	i2c_set_clientdata(chip->adc, chip);  	device_init_wakeup(&client->dev, 1); @@ -238,7 +247,7 @@ static struct i2c_driver max8925_driver = {  		.name	= "max8925",  		.owner	= THIS_MODULE,  		.pm     = &max8925_pm_ops, -		.of_match_table = of_match_ptr(max8925_dt_ids), +		.of_match_table = max8925_dt_ids,  	},  	.probe		= max8925_probe,  	.remove		= max8925_remove, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index cee098c0dae..595364ee178 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -24,6 +24,7 @@  #include <linux/err.h>  #include <linux/slab.h>  #include <linux/i2c.h> +#include <linux/of.h>  #include <linux/of_irq.h>  #include <linux/interrupt.h>  #include <linux/pm_runtime.h> @@ -39,7 +40,7 @@  #define I2C_ADDR_RTC	(0x0C >> 1)  #define I2C_ADDR_HAPTIC	(0x90 >> 1) -static struct mfd_cell max8997_devs[] = { +static const struct mfd_cell max8997_devs[] = {  	{ .name = "max8997-pmic", },  	{ .name = "max8997-rtc", },  	{ .name = "max8997-battery", }, @@ -50,7 +51,7 @@ static struct mfd_cell max8997_devs[] = {  };  #ifdef CONFIG_OF -static struct of_device_id max8997_pmic_dt_match[] = { +static const struct of_device_id max8997_pmic_dt_match[] = {  	{ .compatible = "maxim,max8997-pmic", .data = (void *)TYPE_MAX8997 },  	{},  }; @@ -132,7 +133,6 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)  }  EXPORT_SYMBOL_GPL(max8997_update_reg); -#ifdef CONFIG_OF  /*   * Only the common platform data elements for max8997 are parsed here from the   * device tree. Other sub-modules of max8997 such as pmic, rtc and others have @@ -163,25 +163,16 @@ static struct max8997_platform_data *max8997_i2c_parse_dt_pdata(  	return pd;  } -#else -static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( -					struct device *dev) -{ -	return 0; -} -#endif -static inline int max8997_i2c_get_driver_data(struct i2c_client *i2c, +static inline unsigned long max8997_i2c_get_driver_data(struct i2c_client *i2c,  						const struct i2c_device_id *id)  { -#ifdef CONFIG_OF -	if (i2c->dev.of_node) { +	if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) {  		const struct of_device_id *match;  		match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node); -		return (int)match->data; +		return (unsigned long)match->data;  	} -#endif -	return (int)id->driver_data; +	return id->driver_data;  }  static int max8997_i2c_probe(struct i2c_client *i2c, @@ -202,7 +193,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c,  	max8997->type = max8997_i2c_get_driver_data(i2c, id);  	max8997->irq = i2c->irq; -	if (max8997->dev->of_node) { +	if (IS_ENABLED(CONFIG_OF) && max8997->dev->of_node) {  		pdata = max8997_i2c_parse_dt_pdata(max8997->dev);  		if (IS_ERR(pdata))  			return PTR_ERR(pdata); @@ -217,28 +208,45 @@ static int max8997_i2c_probe(struct i2c_client *i2c,  	mutex_init(&max8997->iolock);  	max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); +	if (!max8997->rtc) { +		dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n"); +		return -ENODEV; +	}  	i2c_set_clientdata(max8997->rtc, max8997); +  	max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); +	if (!max8997->haptic) { +		dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n"); +		ret = -ENODEV; +		goto err_i2c_haptic; +	}  	i2c_set_clientdata(max8997->haptic, max8997); +  	max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); +	if (!max8997->muic) { +		dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n"); +		ret = -ENODEV; +		goto err_i2c_muic; +	}  	i2c_set_clientdata(max8997->muic, max8997);  	pm_runtime_set_active(max8997->dev);  	max8997_irq_init(max8997); -	mfd_add_devices(max8997->dev, -1, max8997_devs, +	ret = mfd_add_devices(max8997->dev, -1, max8997_devs,  			ARRAY_SIZE(max8997_devs),  			NULL, 0, NULL); +	if (ret < 0) { +		dev_err(max8997->dev, "failed to add MFD devices %d\n", ret); +		goto err_mfd; +	}  	/*  	 * TODO: enable others (flash, muic, rtc, battery, ...) and  	 * check the return value  	 */ -	if (ret < 0) -		goto err_mfd; -  	/* MAX8997 has a power button input. */  	device_init_wakeup(max8997->dev, pdata->wakeup); @@ -247,7 +255,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c,  err_mfd:  	mfd_remove_devices(max8997->dev);  	i2c_unregister_device(max8997->muic); +err_i2c_muic:  	i2c_unregister_device(max8997->haptic); +err_i2c_haptic:  	i2c_unregister_device(max8997->rtc);  	return ret;  } diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index fe6332dcabe..a37cb7444b6 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -37,7 +37,7 @@  #define RTC_I2C_ADDR		(0x0c >> 1) -static struct mfd_cell max8998_devs[] = { +static const struct mfd_cell max8998_devs[] = {  	{  		.name = "max8998-pmic",  	}, { @@ -47,7 +47,7 @@ static struct mfd_cell max8998_devs[] = {  	},  }; -static struct mfd_cell lp3974_devs[] = { +static const struct mfd_cell lp3974_devs[] = {  	{  		.name = "lp3974-pmic",  	}, { @@ -132,7 +132,7 @@ int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)  EXPORT_SYMBOL(max8998_update_reg);  #ifdef CONFIG_OF -static struct of_device_id max8998_dt_match[] = { +static const struct of_device_id max8998_dt_match[] = {  	{ .compatible = "maxim,max8998", .data = (void *)TYPE_MAX8998 },  	{ .compatible = "national,lp3974", .data = (void *)TYPE_LP3974 },  	{ .compatible = "ti,lp3974", .data = (void *)TYPE_LP3974 }, @@ -169,16 +169,16 @@ static struct max8998_platform_data *max8998_i2c_parse_dt_pdata(  	return pd;  } -static inline int max8998_i2c_get_driver_data(struct i2c_client *i2c, +static inline unsigned long max8998_i2c_get_driver_data(struct i2c_client *i2c,  						const struct i2c_device_id *id)  {  	if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) {  		const struct of_device_id *match;  		match = of_match_node(max8998_dt_match, i2c->dev.of_node); -		return (int)match->data; +		return (unsigned long)match->data;  	} -	return (int)id->driver_data; +	return id->driver_data;  }  static int max8998_i2c_probe(struct i2c_client *i2c, @@ -215,6 +215,10 @@ static int max8998_i2c_probe(struct i2c_client *i2c,  	mutex_init(&max8998->iolock);  	max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); +	if (!max8998->rtc) { +		dev_err(&i2c->dev, "Failed to allocate I2C device for RTC\n"); +		return -ENODEV; +	}  	i2c_set_clientdata(max8998->rtc, max8998);  	max8998_irq_init(max8998); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 2a9b100c482..acf5dd712eb 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -158,11 +158,6 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)  {  	int ret; -	BUG_ON(!mutex_is_locked(&mc13xxx->lock)); - -	if (offset > MC13XXX_NUMREGS) -		return -EINVAL; -  	ret = regmap_read(mc13xxx->regmap, offset, val);  	dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val); @@ -172,11 +167,9 @@ EXPORT_SYMBOL(mc13xxx_reg_read);  int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)  { -	BUG_ON(!mutex_is_locked(&mc13xxx->lock)); -  	dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val); -	if (offset > MC13XXX_NUMREGS || val > 0xffffff) +	if (val >= BIT(24))  		return -EINVAL;  	return regmap_write(mc13xxx->regmap, offset, val); @@ -186,7 +179,6 @@ EXPORT_SYMBOL(mc13xxx_reg_write);  int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,  		u32 mask, u32 val)  { -	BUG_ON(!mutex_is_locked(&mc13xxx->lock));  	BUG_ON(val & ~mask);  	dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n",  			offset, val, mask); @@ -644,42 +636,36 @@ static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)  }  #endif -int mc13xxx_common_init(struct mc13xxx *mc13xxx, -		struct mc13xxx_platform_data *pdata, int irq) +int mc13xxx_common_init(struct device *dev)  { +	struct mc13xxx_platform_data *pdata = dev_get_platdata(dev); +	struct mc13xxx *mc13xxx = dev_get_drvdata(dev);  	int ret;  	u32 revision; -	mc13xxx_lock(mc13xxx); +	mc13xxx->dev = dev;  	ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);  	if (ret) -		goto err_revision; +		return ret;  	mc13xxx->variant->print_revision(mc13xxx, revision);  	/* mask all irqs */  	ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);  	if (ret) -		goto err_mask; +		return ret;  	ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff);  	if (ret) -		goto err_mask; - -	ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread, -			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); - -	if (ret) { -err_mask: -err_revision: -		mc13xxx_unlock(mc13xxx);  		return ret; -	} -	mc13xxx->irq = irq; +	mutex_init(&mc13xxx->lock); -	mc13xxx_unlock(mc13xxx); +	ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread, +			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); +	if (ret) +		return ret;  	if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)  		mc13xxx->flags = pdata->flags; @@ -687,17 +673,9 @@ err_revision:  	if (mc13xxx->flags & MC13XXX_USE_ADC)  		mc13xxx_add_subdevice(mc13xxx, "%s-adc"); -	if (mc13xxx->flags & MC13XXX_USE_CODEC) -		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec", -					pdata->codec, sizeof(*pdata->codec)); -  	if (mc13xxx->flags & MC13XXX_USE_RTC)  		mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); -	if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN) -		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts", -				&pdata->touch, sizeof(pdata->touch)); -  	if (pdata) {  		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",  			&pdata->regulators, sizeof(pdata->regulators)); @@ -705,23 +683,37 @@ err_revision:  				pdata->leds, sizeof(*pdata->leds));  		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",  				pdata->buttons, sizeof(*pdata->buttons)); +		if (mc13xxx->flags & MC13XXX_USE_CODEC) +			mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec", +				pdata->codec, sizeof(*pdata->codec)); +		if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN) +			mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts", +				&pdata->touch, sizeof(pdata->touch));  	} else {  		mc13xxx_add_subdevice(mc13xxx, "%s-regulator");  		mc13xxx_add_subdevice(mc13xxx, "%s-led");  		mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton"); +		if (mc13xxx->flags & MC13XXX_USE_CODEC) +			mc13xxx_add_subdevice(mc13xxx, "%s-codec"); +		if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN) +			mc13xxx_add_subdevice(mc13xxx, "%s-ts");  	}  	return 0;  }  EXPORT_SYMBOL_GPL(mc13xxx_common_init); -void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx) +int mc13xxx_common_exit(struct device *dev)  { +	struct mc13xxx *mc13xxx = dev_get_drvdata(dev); +  	free_irq(mc13xxx->irq, mc13xxx); +	mfd_remove_devices(dev); +	mutex_destroy(&mc13xxx->lock); -	mfd_remove_devices(mc13xxx->dev); +	return 0;  } -EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup); +EXPORT_SYMBOL_GPL(mc13xxx_common_exit);  MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");  MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index f745e27ee87..ae3addb153a 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -10,7 +10,6 @@  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/platform_device.h> -#include <linux/mutex.h>  #include <linux/mfd/core.h>  #include <linux/mfd/mc13xxx.h>  #include <linux/of.h> @@ -60,7 +59,6 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,  		const struct i2c_device_id *id)  {  	struct mc13xxx *mc13xxx; -	struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);  	int ret;  	mc13xxx = devm_kzalloc(&client->dev, sizeof(*mc13xxx), GFP_KERNEL); @@ -69,16 +67,13 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,  	dev_set_drvdata(&client->dev, mc13xxx); -	mc13xxx->dev = &client->dev; -	mutex_init(&mc13xxx->lock); +	mc13xxx->irq = client->irq;  	mc13xxx->regmap = devm_regmap_init_i2c(client,  					       &mc13xxx_regmap_i2c_config);  	if (IS_ERR(mc13xxx->regmap)) {  		ret = PTR_ERR(mc13xxx->regmap); -		dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", -				ret); -		dev_set_drvdata(&client->dev, NULL); +		dev_err(&client->dev, "Failed to initialize regmap: %d\n", ret);  		return ret;  	} @@ -90,18 +85,12 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,  		mc13xxx->variant = (void *)id->driver_data;  	} -	ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); - -	return ret; +	return mc13xxx_common_init(&client->dev);  }  static int mc13xxx_i2c_remove(struct i2c_client *client)  { -	struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev); - -	mc13xxx_common_cleanup(mc13xxx); - -	return 0; +	return mc13xxx_common_exit(&client->dev);  }  static struct i2c_driver mc13xxx_i2c_driver = { diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 77189daadf1..702925e242c 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -13,7 +13,6 @@  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/platform_device.h> -#include <linux/mutex.h>  #include <linux/interrupt.h>  #include <linux/mfd/core.h>  #include <linux/mfd/mc13xxx.h> @@ -94,10 +93,15 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count)  {  	struct device *dev = context;  	struct spi_device *spi = to_spi_device(dev); +	const char *reg = data;  	if (count != 4)  		return -ENOTSUPP; +	/* include errata fix for spi audio problems */ +	if (*reg == MC13783_AUDIO_CODEC || *reg == MC13783_AUDIO_DAC) +		spi_write(spi, data, count); +  	return spi_write(spi, data, count);  } @@ -124,27 +128,29 @@ static struct regmap_bus regmap_mc13xxx_bus = {  static int mc13xxx_spi_probe(struct spi_device *spi)  {  	struct mc13xxx *mc13xxx; -	struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);  	int ret;  	mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL);  	if (!mc13xxx)  		return -ENOMEM; -	spi_set_drvdata(spi, mc13xxx); +	dev_set_drvdata(&spi->dev, mc13xxx); +  	spi->mode = SPI_MODE_0 | SPI_CS_HIGH; -	mc13xxx->dev = &spi->dev; -	mutex_init(&mc13xxx->lock); +	mc13xxx->irq = spi->irq; + +	spi->max_speed_hz = spi->max_speed_hz ? : 26000000; +	ret = spi_setup(spi); +	if (ret) +		return ret;  	mc13xxx->regmap = devm_regmap_init(&spi->dev, ®map_mc13xxx_bus,  					   &spi->dev,  					   &mc13xxx_regmap_spi_config);  	if (IS_ERR(mc13xxx->regmap)) {  		ret = PTR_ERR(mc13xxx->regmap); -		dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", -				ret); -		spi_set_drvdata(spi, NULL); +		dev_err(&spi->dev, "Failed to initialize regmap: %d\n", ret);  		return ret;  	} @@ -159,16 +165,12 @@ static int mc13xxx_spi_probe(struct spi_device *spi)  		mc13xxx->variant = (void *)id_entry->driver_data;  	} -	return mc13xxx_common_init(mc13xxx, pdata, spi->irq); +	return mc13xxx_common_init(&spi->dev);  }  static int mc13xxx_spi_remove(struct spi_device *spi)  { -	struct mc13xxx *mc13xxx = spi_get_drvdata(spi); - -	mc13xxx_common_cleanup(mc13xxx); - -	return 0; +	return mc13xxx_common_exit(&spi->dev);  }  static struct spi_driver mc13xxx_spi_driver = { diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h index 460ec5c7b18..ae7f1659f5d 100644 --- a/drivers/mfd/mc13xxx.h +++ b/drivers/mfd/mc13xxx.h @@ -43,9 +43,7 @@ struct mc13xxx {  	int adcflags;  }; -int mc13xxx_common_init(struct mc13xxx *mc13xxx, -		struct mc13xxx_platform_data *pdata, int irq); - -void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx); +int mc13xxx_common_init(struct device *dev); +int mc13xxx_common_exit(struct device *dev);  #endif /* __DRIVERS_MFD_MC13XXX_H */ diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 41c31b3ac94..29d76986b40 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -12,7 +12,6 @@   *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/io.h>  #include <linux/errno.h>  #include <linux/kernel.h> diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index ad25bfa3fb0..5e2667afe2b 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -1287,29 +1287,8 @@ static struct i2c_driver menelaus_i2c_driver = {  	.id_table	= menelaus_id,  }; -static int __init menelaus_init(void) -{ -	int res; - -	res = i2c_add_driver(&menelaus_i2c_driver); -	if (res < 0) { -		pr_err(DRIVER_NAME ": driver registration failed\n"); -		return res; -	} - -	return 0; -} - -static void __exit menelaus_exit(void) -{ -	i2c_del_driver(&menelaus_i2c_driver); - -	/* FIXME: Shutdown menelaus parts that can be shut down */ -} +module_i2c_driver(menelaus_i2c_driver);  MODULE_AUTHOR("Texas Instruments, Inc. (and others)");  MODULE_DESCRIPTION("I2C interface for Menelaus.");  MODULE_LICENSE("GPL"); - -module_init(menelaus_init); -module_exit(menelaus_exit); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f421586f29f..892d343193a 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -20,6 +20,7 @@  #include <linux/module.h>  #include <linux/irqdomain.h>  #include <linux/of.h> +#include <linux/regulator/consumer.h>  static struct device_type mfd_dev_type = {  	.name	= "mfd_device", @@ -63,7 +64,8 @@ int mfd_cell_disable(struct platform_device *pdev)  EXPORT_SYMBOL(mfd_cell_disable);  static int mfd_platform_add_cell(struct platform_device *pdev, -				 const struct mfd_cell *cell) +				 const struct mfd_cell *cell, +				 atomic_t *usage_count)  {  	if (!cell)  		return 0; @@ -72,11 +74,12 @@ static int mfd_platform_add_cell(struct platform_device *pdev,  	if (!pdev->mfd_cell)  		return -ENOMEM; +	pdev->mfd_cell->usage_count = usage_count;  	return 0;  }  static int mfd_add_device(struct device *parent, int id, -			  const struct mfd_cell *cell, +			  const struct mfd_cell *cell, atomic_t *usage_count,  			  struct resource *mem_base,  			  int irq_base, struct irq_domain *domain)  { @@ -99,6 +102,13 @@ static int mfd_add_device(struct device *parent, int id,  	pdev->dev.dma_mask = parent->dma_mask;  	pdev->dev.dma_parms = parent->dma_parms; +	ret = regulator_bulk_register_supply_alias( +			&pdev->dev, cell->parent_supplies, +			parent, cell->parent_supplies, +			cell->num_parent_supplies); +	if (ret < 0) +		goto fail_res; +  	if (parent->of_node && cell->of_compatible) {  		for_each_child_of_node(parent->of_node, np) {  			if (of_device_is_compatible(np, cell->of_compatible)) { @@ -112,12 +122,12 @@ static int mfd_add_device(struct device *parent, int id,  		ret = platform_device_add_data(pdev,  					cell->platform_data, cell->pdata_size);  		if (ret) -			goto fail_res; +			goto fail_alias;  	} -	ret = mfd_platform_add_cell(pdev, cell); +	ret = mfd_platform_add_cell(pdev, cell, usage_count);  	if (ret) -		goto fail_res; +		goto fail_alias;  	for (r = 0; r < cell->num_resources; r++) {  		res[r].name = cell->resources[r].name; @@ -152,17 +162,17 @@ static int mfd_add_device(struct device *parent, int id,  		if (!cell->ignore_resource_conflicts) {  			ret = acpi_check_resource_conflict(&res[r]);  			if (ret) -				goto fail_res; +				goto fail_alias;  		}  	}  	ret = platform_device_add_resources(pdev, res, cell->num_resources);  	if (ret) -		goto fail_res; +		goto fail_alias;  	ret = platform_device_add(pdev);  	if (ret) -		goto fail_res; +		goto fail_alias;  	if (cell->pm_runtime_no_callbacks)  		pm_runtime_no_callbacks(&pdev->dev); @@ -171,6 +181,10 @@ static int mfd_add_device(struct device *parent, int id,  	return 0; +fail_alias: +	regulator_bulk_unregister_supply_alias(&pdev->dev, +					       cell->parent_supplies, +					       cell->num_parent_supplies);  fail_res:  	kfree(res);  fail_device: @@ -180,12 +194,12 @@ fail_alloc:  }  int mfd_add_devices(struct device *parent, int id, -		    struct mfd_cell *cells, int n_devs, +		    const struct mfd_cell *cells, int n_devs,  		    struct resource *mem_base,  		    int irq_base, struct irq_domain *domain)  {  	int i; -	int ret = 0; +	int ret;  	atomic_t *cnts;  	/* initialize reference counting for all cells */ @@ -195,16 +209,19 @@ int mfd_add_devices(struct device *parent, int id,  	for (i = 0; i < n_devs; i++) {  		atomic_set(&cnts[i], 0); -		cells[i].usage_count = &cnts[i]; -		ret = mfd_add_device(parent, id, cells + i, mem_base, +		ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base,  				     irq_base, domain);  		if (ret) -			break; +			goto fail;  	} -	if (ret) -		mfd_remove_devices(parent); +	return 0; +fail: +	if (i) +		mfd_remove_devices(parent); +	else +		kfree(cnts);  	return ret;  }  EXPORT_SYMBOL(mfd_add_devices); @@ -221,6 +238,9 @@ static int mfd_remove_devices_fn(struct device *dev, void *c)  	pdev = to_platform_device(dev);  	cell = mfd_get_cell(pdev); +	regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies, +					       cell->num_parent_supplies); +  	/* find the base address of usage_count pointers (for freeing) */  	if (!*usage_count || (cell->usage_count < *usage_count))  		*usage_count = cell->usage_count; @@ -259,8 +279,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)  	for (i = 0; i < n_clones; i++) {  		cell_entry.name = clones[i];  		/* don't give up if a single call fails; just report error */ -		if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0, -				   NULL)) +		if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, +				   cell_entry.usage_count, NULL, 0, NULL))  			dev_err(dev, "failed to create platform device '%s'\n",  					clones[i]);  	} diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 29ee54d6851..b48d80c367f 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -121,22 +121,22 @@ static u64 usbhs_dmamask = DMA_BIT_MASK(32);  static inline void usbhs_write(void __iomem *base, u32 reg, u32 val)  { -	__raw_writel(val, base + reg); +	writel_relaxed(val, base + reg);  }  static inline u32 usbhs_read(void __iomem *base, u32 reg)  { -	return __raw_readl(base + reg); +	return readl_relaxed(base + reg);  }  static inline void usbhs_writeb(void __iomem *base, u8 reg, u8 val)  { -	__raw_writeb(val, base + reg); +	writeb_relaxed(val, base + reg);  }  static inline u8 usbhs_readb(void __iomem *base, u8 reg)  { -	return __raw_readb(base + reg); +	return readb_relaxed(base + reg);  }  /*-------------------------------------------------------------------------*/ @@ -328,13 +328,13 @@ static int usbhs_runtime_resume(struct device *dev)  	omap_tll_enable(pdata);  	if (!IS_ERR(omap->ehci_logic_fck)) -		clk_enable(omap->ehci_logic_fck); +		clk_prepare_enable(omap->ehci_logic_fck);  	for (i = 0; i < omap->nports; i++) {  		switch (pdata->port_mode[i]) {  		case OMAP_EHCI_PORT_MODE_HSIC:  			if (!IS_ERR(omap->hsic60m_clk[i])) { -				r = clk_enable(omap->hsic60m_clk[i]); +				r = clk_prepare_enable(omap->hsic60m_clk[i]);  				if (r) {  					dev_err(dev,  					 "Can't enable port %d hsic60m clk:%d\n", @@ -343,7 +343,7 @@ static int usbhs_runtime_resume(struct device *dev)  			}  			if (!IS_ERR(omap->hsic480m_clk[i])) { -				r = clk_enable(omap->hsic480m_clk[i]); +				r = clk_prepare_enable(omap->hsic480m_clk[i]);  				if (r) {  					dev_err(dev,  					 "Can't enable port %d hsic480m clk:%d\n", @@ -354,7 +354,7 @@ static int usbhs_runtime_resume(struct device *dev)  		case OMAP_EHCI_PORT_MODE_TLL:  			if (!IS_ERR(omap->utmi_clk[i])) { -				r = clk_enable(omap->utmi_clk[i]); +				r = clk_prepare_enable(omap->utmi_clk[i]);  				if (r) {  					dev_err(dev,  					 "Can't enable port %d clk : %d\n", @@ -382,15 +382,15 @@ static int usbhs_runtime_suspend(struct device *dev)  		switch (pdata->port_mode[i]) {  		case OMAP_EHCI_PORT_MODE_HSIC:  			if (!IS_ERR(omap->hsic60m_clk[i])) -				clk_disable(omap->hsic60m_clk[i]); +				clk_disable_unprepare(omap->hsic60m_clk[i]);  			if (!IS_ERR(omap->hsic480m_clk[i])) -				clk_disable(omap->hsic480m_clk[i]); +				clk_disable_unprepare(omap->hsic480m_clk[i]);  		/* Fall through as utmi_clks were used in HSIC mode */  		case OMAP_EHCI_PORT_MODE_TLL:  			if (!IS_ERR(omap->utmi_clk[i])) -				clk_disable(omap->utmi_clk[i]); +				clk_disable_unprepare(omap->utmi_clk[i]);  			break;  		default:  			break; @@ -398,7 +398,7 @@ static int usbhs_runtime_suspend(struct device *dev)  	}  	if (!IS_ERR(omap->ehci_logic_fck)) -		clk_disable(omap->ehci_logic_fck); +		clk_disable_unprepare(omap->ehci_logic_fck);  	omap_tll_disable(pdata); @@ -557,7 +557,7 @@ static int usbhs_omap_get_dt_pdata(struct device *dev,  	return 0;  } -static struct of_device_id usbhs_child_match_table[] = { +static const struct of_device_id usbhs_child_match_table[] = {  	{ .compatible = "ti,omap-ehci", },  	{ .compatible = "ti,omap-ohci", },  	{ } @@ -665,55 +665,78 @@ static int usbhs_omap_probe(struct platform_device *pdev)  		goto err_mem;  	} -	need_logic_fck = false; +	/* Set all clocks as invalid to begin with */ +	omap->ehci_logic_fck = ERR_PTR(-ENODEV); +	omap->init_60m_fclk = ERR_PTR(-ENODEV); +	omap->utmi_p1_gfclk = ERR_PTR(-ENODEV); +	omap->utmi_p2_gfclk = ERR_PTR(-ENODEV); +	omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV); +	omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV); +  	for (i = 0; i < omap->nports; i++) { -		if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || -			is_ehci_hsic_mode(i)) -				need_logic_fck |= true; +		omap->utmi_clk[i] = ERR_PTR(-ENODEV); +		omap->hsic480m_clk[i] = ERR_PTR(-ENODEV); +		omap->hsic60m_clk[i] = ERR_PTR(-ENODEV);  	} -	omap->ehci_logic_fck = ERR_PTR(-EINVAL); -	if (need_logic_fck) { -		omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); -		if (IS_ERR(omap->ehci_logic_fck)) { -			ret = PTR_ERR(omap->ehci_logic_fck); -			dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); +	/* for OMAP3 i.e. USBHS REV1 */ +	if (omap->usbhs_rev == OMAP_USBHS_REV1) { +		need_logic_fck = false; +		for (i = 0; i < omap->nports; i++) { +			if (is_ehci_phy_mode(pdata->port_mode[i]) || +			    is_ehci_tll_mode(pdata->port_mode[i]) || +			    is_ehci_hsic_mode(pdata->port_mode[i])) + +				need_logic_fck |= true;  		} + +		if (need_logic_fck) { +			omap->ehci_logic_fck = devm_clk_get(dev, +							    "usbhost_120m_fck"); +			if (IS_ERR(omap->ehci_logic_fck)) { +				ret = PTR_ERR(omap->ehci_logic_fck); +				dev_err(dev, "usbhost_120m_fck failed:%d\n", +					ret); +				goto err_mem; +			} +		} +		goto initialize;  	} -	omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk"); +	/* for OMAP4+ i.e. USBHS REV2+ */ +	omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk");  	if (IS_ERR(omap->utmi_p1_gfclk)) {  		ret = PTR_ERR(omap->utmi_p1_gfclk);  		dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); -		goto err_p1_gfclk; +		goto err_mem;  	} -	omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk"); +	omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk");  	if (IS_ERR(omap->utmi_p2_gfclk)) {  		ret = PTR_ERR(omap->utmi_p2_gfclk);  		dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); -		goto err_p2_gfclk; +		goto err_mem;  	} -	omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); +	omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1");  	if (IS_ERR(omap->xclk60mhsp1_ck)) {  		ret = PTR_ERR(omap->xclk60mhsp1_ck); -		dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); -		goto err_xclk60mhsp1; +		dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret); +		goto err_mem;  	} -	omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); +	omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2");  	if (IS_ERR(omap->xclk60mhsp2_ck)) {  		ret = PTR_ERR(omap->xclk60mhsp2_ck); -		dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); -		goto err_xclk60mhsp2; +		dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret); +		goto err_mem;  	} -	omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); +	omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int");  	if (IS_ERR(omap->init_60m_fclk)) {  		ret = PTR_ERR(omap->init_60m_fclk); -		dev_err(dev, "init_60m_fclk failed error:%d\n", ret); -		goto err_init60m; +		dev_err(dev, "refclk_60m_int failed error:%d\n", ret); +		goto err_mem;  	}  	for (i = 0; i < omap->nports; i++) { @@ -727,55 +750,72 @@ static int usbhs_omap_probe(struct platform_device *pdev)  		 * platforms have all clocks and we can function without  		 * them  		 */ -		omap->utmi_clk[i] = clk_get(dev, clkname); -		if (IS_ERR(omap->utmi_clk[i])) -			dev_dbg(dev, "Failed to get clock : %s : %ld\n", -				clkname, PTR_ERR(omap->utmi_clk[i])); +		omap->utmi_clk[i] = devm_clk_get(dev, clkname); +		if (IS_ERR(omap->utmi_clk[i])) { +			ret = PTR_ERR(omap->utmi_clk[i]); +			dev_err(dev, "Failed to get clock : %s : %d\n", +				clkname, ret); +			goto err_mem; +		}  		snprintf(clkname, sizeof(clkname),  				"usb_host_hs_hsic480m_p%d_clk", i + 1); -		omap->hsic480m_clk[i] = clk_get(dev, clkname); -		if (IS_ERR(omap->hsic480m_clk[i])) -			dev_dbg(dev, "Failed to get clock : %s : %ld\n", -				clkname, PTR_ERR(omap->hsic480m_clk[i])); +		omap->hsic480m_clk[i] = devm_clk_get(dev, clkname); +		if (IS_ERR(omap->hsic480m_clk[i])) { +			ret = PTR_ERR(omap->hsic480m_clk[i]); +			dev_err(dev, "Failed to get clock : %s : %d\n", +				clkname, ret); +			goto err_mem; +		}  		snprintf(clkname, sizeof(clkname),  				"usb_host_hs_hsic60m_p%d_clk", i + 1); -		omap->hsic60m_clk[i] = clk_get(dev, clkname); -		if (IS_ERR(omap->hsic60m_clk[i])) -			dev_dbg(dev, "Failed to get clock : %s : %ld\n", -				clkname, PTR_ERR(omap->hsic60m_clk[i])); +		omap->hsic60m_clk[i] = devm_clk_get(dev, clkname); +		if (IS_ERR(omap->hsic60m_clk[i])) { +			ret = PTR_ERR(omap->hsic60m_clk[i]); +			dev_err(dev, "Failed to get clock : %s : %d\n", +				clkname, ret); +			goto err_mem; +		}  	}  	if (is_ehci_phy_mode(pdata->port_mode[0])) { -		/* for OMAP3, clk_set_parent fails */  		ret = clk_set_parent(omap->utmi_p1_gfclk,  					omap->xclk60mhsp1_ck); -		if (ret != 0) -			dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n", -					ret); +		if (ret != 0) { +			dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n", +				ret); +			goto err_mem; +		}  	} else if (is_ehci_tll_mode(pdata->port_mode[0])) {  		ret = clk_set_parent(omap->utmi_p1_gfclk,  					omap->init_60m_fclk); -		if (ret != 0) -			dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n", -					ret); +		if (ret != 0) { +			dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n", +				ret); +			goto err_mem; +		}  	}  	if (is_ehci_phy_mode(pdata->port_mode[1])) {  		ret = clk_set_parent(omap->utmi_p2_gfclk,  					omap->xclk60mhsp2_ck); -		if (ret != 0) -			dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n", -					ret); +		if (ret != 0) { +			dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n", +				ret); +			goto err_mem; +		}  	} else if (is_ehci_tll_mode(pdata->port_mode[1])) {  		ret = clk_set_parent(omap->utmi_p2_gfclk,  						omap->init_60m_fclk); -		if (ret != 0) -			dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n", -					ret); +		if (ret != 0) { +			dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n", +				ret); +			goto err_mem; +		}  	} +initialize:  	omap_usbhs_init(dev);  	if (dev->of_node) { @@ -784,7 +824,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)  		if (ret) {  			dev_err(dev, "Failed to create DT children: %d\n", ret); -			goto err_alloc; +			goto err_mem;  		}  	} else { @@ -792,40 +832,12 @@ static int usbhs_omap_probe(struct platform_device *pdev)  		if (ret) {  			dev_err(dev, "omap_usbhs_alloc_children failed: %d\n",  						ret); -			goto err_alloc; +			goto err_mem;  		}  	}  	return 0; -err_alloc: -	for (i = 0; i < omap->nports; i++) { -		if (!IS_ERR(omap->utmi_clk[i])) -			clk_put(omap->utmi_clk[i]); -		if (!IS_ERR(omap->hsic60m_clk[i])) -			clk_put(omap->hsic60m_clk[i]); -		if (!IS_ERR(omap->hsic480m_clk[i])) -			clk_put(omap->hsic480m_clk[i]); -	} - -	clk_put(omap->init_60m_fclk); - -err_init60m: -	clk_put(omap->xclk60mhsp2_ck); - -err_xclk60mhsp2: -	clk_put(omap->xclk60mhsp1_ck); - -err_xclk60mhsp1: -	clk_put(omap->utmi_p2_gfclk); - -err_p2_gfclk: -	clk_put(omap->utmi_p1_gfclk); - -err_p1_gfclk: -	if (!IS_ERR(omap->ehci_logic_fck)) -		clk_put(omap->ehci_logic_fck); -  err_mem:  	pm_runtime_disable(dev); @@ -847,27 +859,6 @@ static int usbhs_omap_remove_child(struct device *dev, void *data)   */  static int usbhs_omap_remove(struct platform_device *pdev)  { -	struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); -	int i; - -	for (i = 0; i < omap->nports; i++) { -		if (!IS_ERR(omap->utmi_clk[i])) -			clk_put(omap->utmi_clk[i]); -		if (!IS_ERR(omap->hsic60m_clk[i])) -			clk_put(omap->hsic60m_clk[i]); -		if (!IS_ERR(omap->hsic480m_clk[i])) -			clk_put(omap->hsic480m_clk[i]); -	} - -	clk_put(omap->init_60m_fclk); -	clk_put(omap->utmi_p1_gfclk); -	clk_put(omap->utmi_p2_gfclk); -	clk_put(omap->xclk60mhsp2_ck); -	clk_put(omap->xclk60mhsp1_ck); - -	if (!IS_ERR(omap->ehci_logic_fck)) -		clk_put(omap->ehci_logic_fck); -  	pm_runtime_disable(&pdev->dev);  	/* remove children */ @@ -893,7 +884,7 @@ static struct platform_driver usbhs_omap_driver = {  		.name		= (char *)usbhs_driver_name,  		.owner		= THIS_MODULE,  		.pm		= &usbhsomap_dev_pm_ops, -		.of_match_table = of_match_ptr(usbhs_omap_dt_ids), +		.of_match_table = usbhs_omap_dt_ids,  	},  	.remove		= usbhs_omap_remove,  }; diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index e59ac4cbac9..532eacab6b4 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -121,22 +121,22 @@ static DEFINE_SPINLOCK(tll_lock);	/* serialize access to tll_dev */  static inline void usbtll_write(void __iomem *base, u32 reg, u32 val)  { -	__raw_writel(val, base + reg); +	writel_relaxed(val, base + reg);  }  static inline u32 usbtll_read(void __iomem *base, u32 reg)  { -	return __raw_readl(base + reg); +	return readl_relaxed(base + reg);  }  static inline void usbtll_writeb(void __iomem *base, u8 reg, u8 val)  { -	__raw_writeb(val, base + reg); +	writeb_relaxed(val, base + reg);  }  static inline u8 usbtll_readb(void __iomem *base, u8 reg)  { -	return __raw_readb(base + reg); +	return readb_relaxed(base + reg);  }  /*-------------------------------------------------------------------------*/ @@ -252,7 +252,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)  		break;  	} -	tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), +	tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch,  						GFP_KERNEL);  	if (!tll->ch_clk) {  		ret = -ENOMEM; @@ -320,7 +320,7 @@ 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), +		.of_match_table = usbtll_omap_dt_ids,  	},  	.probe		= usbtll_omap_probe,  	.remove		= usbtll_omap_remove, @@ -333,21 +333,17 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata)  	unsigned reg;  	struct usbtll_omap *tll; -	spin_lock(&tll_lock); - -	if (!tll_dev) { -		spin_unlock(&tll_lock); +	if (!tll_dev)  		return -ENODEV; -	} -	tll = dev_get_drvdata(tll_dev); +	pm_runtime_get_sync(tll_dev); +	spin_lock(&tll_lock); +	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; @@ -398,9 +394,8 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata)  		}  	} -	pm_runtime_put_sync(tll_dev); -  	spin_unlock(&tll_lock); +	pm_runtime_put_sync(tll_dev);  	return 0;  } @@ -411,17 +406,14 @@ int omap_tll_enable(struct usbhs_omap_platform_data *pdata)  	int i;  	struct usbtll_omap *tll; -	spin_lock(&tll_lock); - -	if (!tll_dev) { -		spin_unlock(&tll_lock); +	if (!tll_dev)  		return -ENODEV; -	} - -	tll = dev_get_drvdata(tll_dev);  	pm_runtime_get_sync(tll_dev); +	spin_lock(&tll_lock); +	tll = dev_get_drvdata(tll_dev); +  	for (i = 0; i < tll->nch; i++) {  		if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {  			int r; @@ -429,7 +421,7 @@ int omap_tll_enable(struct usbhs_omap_platform_data *pdata)  			if (IS_ERR(tll->ch_clk[i]))  				continue; -			r = clk_enable(tll->ch_clk[i]); +			r = clk_prepare_enable(tll->ch_clk[i]);  			if (r) {  				dev_err(tll_dev,  				 "Error enabling ch %d clock: %d\n", i, r); @@ -448,25 +440,21 @@ int omap_tll_disable(struct usbhs_omap_platform_data *pdata)  	int i;  	struct usbtll_omap *tll; -	spin_lock(&tll_lock); - -	if (!tll_dev) { -		spin_unlock(&tll_lock); +	if (!tll_dev)  		return -ENODEV; -	} +	spin_lock(&tll_lock);  	tll = dev_get_drvdata(tll_dev);  	for (i = 0; i < tll->nch; i++) {  		if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {  			if (!IS_ERR(tll->ch_clk[i])) -				clk_disable(tll->ch_clk[i]); +				clk_disable_unprepare(tll->ch_clk[i]);  		}  	} -	pm_runtime_put_sync(tll_dev); -  	spin_unlock(&tll_lock); +	pm_runtime_put_sync(tll_dev);  	return 0;  } diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 135afabe4ae..d280d789e55 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -368,6 +368,7 @@ static const struct of_device_id of_palmas_match_tbl[] = {  	},  	{ },  }; +MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);  static int palmas_i2c_probe(struct i2c_client *i2c,  			    const struct i2c_device_id *id) @@ -402,7 +403,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  	palmas->dev = &i2c->dev;  	palmas->irq = i2c->irq; -	match = of_match_device(of_match_ptr(of_palmas_match_tbl), &i2c->dev); +	match = of_match_device(of_palmas_match_tbl, &i2c->dev);  	if (!match)  		return -ENODATA; @@ -421,7 +422,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  				dev_err(palmas->dev,  					"can't attach client %d\n", i);  				ret = -ENOMEM; -				goto err; +				goto err_i2c;  			}  			palmas->i2c_clients[i]->dev.of_node = of_node_get(node);  		} @@ -432,7 +433,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  			dev_err(palmas->dev,  				"Failed to allocate regmap %d, err: %d\n",  				i, ret); -			goto err; +			goto err_i2c;  		}  	} @@ -451,7 +452,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  			reg);  	if (ret < 0) {  		dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret); -		goto err; +		goto err_i2c;  	}  	/* Change IRQ into clear on read mode for efficiency */ @@ -465,7 +466,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  			IRQF_ONESHOT | pdata->irq_flags, 0, &palmas_irq_chip,  			&palmas->irq_data);  	if (ret < 0) -		goto err; +		goto err_i2c;  no_irq:  	slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE); @@ -551,7 +552,6 @@ no_irq:  		} else if (pdata->pm_off && !pm_power_off) {  			palmas_dev = palmas;  			pm_power_off = palmas_power_off; -			return ret;  		}  	} @@ -559,17 +559,31 @@ no_irq:  err_irq:  	regmap_del_irq_chip(palmas->irq, palmas->irq_data); -err: +err_i2c: +	for (i = 1; i < PALMAS_NUM_CLIENTS; i++) { +		if (palmas->i2c_clients[i]) +			i2c_unregister_device(palmas->i2c_clients[i]); +	}  	return ret;  }  static int palmas_i2c_remove(struct i2c_client *i2c)  {  	struct palmas *palmas = i2c_get_clientdata(i2c); +	int i; -	mfd_remove_devices(palmas->dev);  	regmap_del_irq_chip(palmas->irq, palmas->irq_data); +	for (i = 1; i < PALMAS_NUM_CLIENTS; i++) { +		if (palmas->i2c_clients[i]) +			i2c_unregister_device(palmas->i2c_clients[i]); +	} + +	if (palmas == palmas_dev) { +		pm_power_off = NULL; +		palmas_dev = NULL; +	} +  	return 0;  } diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c index b8941a556d7..c1984b0d1b6 100644 --- a/drivers/mfd/pcf50633-adc.c +++ b/drivers/mfd/pcf50633-adc.c @@ -19,7 +19,6 @@  #include <linux/kernel.h>  #include <linux/slab.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/device.h>  #include <linux/platform_device.h>  #include <linux/completion.h> diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 6841d6805fd..41ab5e34d2a 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -245,7 +245,7 @@ static int pcf50633_probe(struct i2c_client *client,  	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {  		struct platform_device *pdev; -		pdev = platform_device_alloc("pcf50633-regltr", i); +		pdev = platform_device_alloc("pcf50633-regulator", i);  		if (!pdev) {  			dev_err(pcf->dev, "Cannot create regulator %d\n", i);  			continue; diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index a6841f77aa5..95951380354 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -14,118 +14,296 @@  #define pr_fmt(fmt) "%s: " fmt, __func__  #include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irq.h> +#include <linux/irqdomain.h>  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/slab.h>  #include <linux/err.h>  #include <linux/ssbi.h> +#include <linux/regmap.h> +#include <linux/of_platform.h>  #include <linux/mfd/core.h> -#include <linux/mfd/pm8xxx/pm8921.h> -#include <linux/mfd/pm8xxx/core.h> + +#define	SSBI_REG_ADDR_IRQ_BASE		0x1BB + +#define	SSBI_REG_ADDR_IRQ_ROOT		(SSBI_REG_ADDR_IRQ_BASE + 0) +#define	SSBI_REG_ADDR_IRQ_M_STATUS1	(SSBI_REG_ADDR_IRQ_BASE + 1) +#define	SSBI_REG_ADDR_IRQ_M_STATUS2	(SSBI_REG_ADDR_IRQ_BASE + 2) +#define	SSBI_REG_ADDR_IRQ_M_STATUS3	(SSBI_REG_ADDR_IRQ_BASE + 3) +#define	SSBI_REG_ADDR_IRQ_M_STATUS4	(SSBI_REG_ADDR_IRQ_BASE + 4) +#define	SSBI_REG_ADDR_IRQ_BLK_SEL	(SSBI_REG_ADDR_IRQ_BASE + 5) +#define	SSBI_REG_ADDR_IRQ_IT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 6) +#define	SSBI_REG_ADDR_IRQ_CONFIG	(SSBI_REG_ADDR_IRQ_BASE + 7) +#define	SSBI_REG_ADDR_IRQ_RT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 8) + +#define	PM_IRQF_LVL_SEL			0x01	/* level select */ +#define	PM_IRQF_MASK_FE			0x02	/* mask falling edge */ +#define	PM_IRQF_MASK_RE			0x04	/* mask rising edge */ +#define	PM_IRQF_CLR			0x08	/* clear interrupt */ +#define	PM_IRQF_BITS_MASK		0x70 +#define	PM_IRQF_BITS_SHIFT		4 +#define	PM_IRQF_WRITE			0x80 + +#define	PM_IRQF_MASK_ALL		(PM_IRQF_MASK_FE | \ +					PM_IRQF_MASK_RE)  #define REG_HWREV		0x002  /* PMIC4 revision */  #define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */ -struct pm8921 { -	struct device			*dev; -	struct pm_irq_chip		*irq_chip; +#define PM8921_NR_IRQS		256 + +struct pm_irq_chip { +	struct regmap		*regmap; +	spinlock_t		pm_irq_lock; +	struct irq_domain	*irqdomain; +	unsigned int		num_irqs; +	unsigned int		num_blocks; +	unsigned int		num_masters; +	u8			config[0];  }; -static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) +static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, +				 unsigned int *ip)  { -	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); -	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; +	int	rc; + +	spin_lock(&chip->pm_irq_lock); +	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); +	if (rc) { +		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); +		goto bail; +	} -	return ssbi_read(pmic->dev->parent, addr, val, 1); +	rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); +	if (rc) +		pr_err("Failed Reading Status rc=%d\n", rc); +bail: +	spin_unlock(&chip->pm_irq_lock); +	return rc;  } -static int pm8921_writeb(const struct device *dev, u16 addr, u8 val) +static int +pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)  { -	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); -	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; +	int	rc; + +	spin_lock(&chip->pm_irq_lock); +	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); +	if (rc) { +		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); +		goto bail; +	} -	return ssbi_write(pmic->dev->parent, addr, &val, 1); +	cp |= PM_IRQF_WRITE; +	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp); +	if (rc) +		pr_err("Failed Configuring IRQ rc=%d\n", rc); +bail: +	spin_unlock(&chip->pm_irq_lock); +	return rc;  } -static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf, -									int cnt) +static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)  { -	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); -	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; +	int pmirq, irq, i, ret = 0; +	unsigned int bits; + +	ret = pm8xxx_read_block_irq(chip, block, &bits); +	if (ret) { +		pr_err("Failed reading %d block ret=%d", block, ret); +		return ret; +	} +	if (!bits) { +		pr_err("block bit set in master but no irqs: %d", block); +		return 0; +	} -	return ssbi_read(pmic->dev->parent, addr, buf, cnt); +	/* Check IRQ bits */ +	for (i = 0; i < 8; i++) { +		if (bits & (1 << i)) { +			pmirq = block * 8 + i; +			irq = irq_find_mapping(chip->irqdomain, pmirq); +			generic_handle_irq(irq); +		} +	} +	return 0;  } -static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf, -									int cnt) +static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)  { -	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); -	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; +	unsigned int blockbits; +	int block_number, i, ret = 0; + +	ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master, +			  &blockbits); +	if (ret) { +		pr_err("Failed to read master %d ret=%d\n", master, ret); +		return ret; +	} +	if (!blockbits) { +		pr_err("master bit set in root but no blocks: %d", master); +		return 0; +	} -	return ssbi_write(pmic->dev->parent, addr, buf, cnt); +	for (i = 0; i < 8; i++) +		if (blockbits & (1 << i)) { +			block_number = master * 8 + i;	/* block # */ +			ret |= pm8xxx_irq_block_handler(chip, block_number); +		} +	return ret;  } -static int pm8921_read_irq_stat(const struct device *dev, int irq) +static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)  { -	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); -	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; +	struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); +	struct irq_chip *irq_chip = irq_desc_get_chip(desc); +	unsigned int root; +	int	i, ret, masters = 0; + +	chained_irq_enter(irq_chip, desc); + +	ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root); +	if (ret) { +		pr_err("Can't read root status ret=%d\n", ret); +		return; +	} + +	/* on pm8xxx series masters start from bit 1 of the root */ +	masters = root >> 1; -	return pm8xxx_get_irq_stat(pmic->irq_chip, irq); +	/* Read allowed masters for blocks. */ +	for (i = 0; i < chip->num_masters; i++) +		if (masters & (1 << i)) +			pm8xxx_irq_master_handler(chip, i); + +	chained_irq_exit(irq_chip, desc);  } -static struct pm8xxx_drvdata pm8921_drvdata = { -	.pmic_readb		= pm8921_readb, -	.pmic_writeb		= pm8921_writeb, -	.pmic_read_buf		= pm8921_read_buf, -	.pmic_write_buf		= pm8921_write_buf, -	.pmic_read_irq_stat	= pm8921_read_irq_stat, -}; +static void pm8xxx_irq_mask_ack(struct irq_data *d) +{ +	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int pmirq = irqd_to_hwirq(d); +	int	irq_bit; +	u8	block, config; + +	block = pmirq / 8; +	irq_bit = pmirq % 8; -static int pm8921_add_subdevices(const struct pm8921_platform_data -					   *pdata, -					   struct pm8921 *pmic, -					   u32 rev) +	config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; +	pm8xxx_config_irq(chip, block, config); +} + +static void pm8xxx_irq_unmask(struct irq_data *d)  { -	int ret = 0, irq_base = 0; -	struct pm_irq_chip *irq_chip; - -	if (pdata->irq_pdata) { -		pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS; -		pdata->irq_pdata->irq_cdata.rev = rev; -		irq_base = pdata->irq_pdata->irq_base; -		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata); - -		if (IS_ERR(irq_chip)) { -			pr_err("Failed to init interrupts ret=%ld\n", -					PTR_ERR(irq_chip)); -			return PTR_ERR(irq_chip); -		} -		pmic->irq_chip = irq_chip; +	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int pmirq = irqd_to_hwirq(d); +	int	irq_bit; +	u8	block, config; + +	block = pmirq / 8; +	irq_bit = pmirq % 8; + +	config = chip->config[pmirq]; +	pm8xxx_config_irq(chip, block, config); +} + +static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ +	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int pmirq = irqd_to_hwirq(d); +	int irq_bit; +	u8 block, config; + +	block = pmirq / 8; +	irq_bit  = pmirq % 8; + +	chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) +							| PM_IRQF_MASK_ALL; +	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { +		if (flow_type & IRQF_TRIGGER_RISING) +			chip->config[pmirq] &= ~PM_IRQF_MASK_RE; +		if (flow_type & IRQF_TRIGGER_FALLING) +			chip->config[pmirq] &= ~PM_IRQF_MASK_FE; +	} else { +		chip->config[pmirq] |= PM_IRQF_LVL_SEL; + +		if (flow_type & IRQF_TRIGGER_HIGH) +			chip->config[pmirq] &= ~PM_IRQF_MASK_RE; +		else +			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;  	} -	return ret; + +	config = chip->config[pmirq] | PM_IRQF_CLR; +	return pm8xxx_config_irq(chip, block, config); +} + +static struct irq_chip pm8xxx_irq_chip = { +	.name		= "pm8xxx", +	.irq_mask_ack	= pm8xxx_irq_mask_ack, +	.irq_unmask	= pm8xxx_irq_unmask, +	.irq_set_type	= pm8xxx_irq_set_type, +	.flags		= IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, +}; + +static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, +				   irq_hw_number_t hwirq) +{ +	struct pm_irq_chip *chip = d->host_data; + +	irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); +	irq_set_chip_data(irq, chip); +#ifdef CONFIG_ARM +	set_irq_flags(irq, IRQF_VALID); +#else +	irq_set_noprobe(irq); +#endif +	return 0;  } +static const struct irq_domain_ops pm8xxx_irq_domain_ops = { +	.xlate = irq_domain_xlate_twocell, +	.map = pm8xxx_irq_domain_map, +}; + +static const struct regmap_config ssbi_regmap_config = { +	.reg_bits = 16, +	.val_bits = 8, +	.max_register = 0x3ff, +	.fast_io = true, +	.reg_read = ssbi_reg_read, +	.reg_write = ssbi_reg_write +}; + +static const struct of_device_id pm8921_id_table[] = { +	{ .compatible = "qcom,pm8058", }, +	{ .compatible = "qcom,pm8921", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, pm8921_id_table); +  static int pm8921_probe(struct platform_device *pdev)  { -	const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev); -	struct pm8921 *pmic; -	int rc; -	u8 val; +	struct regmap *regmap; +	int irq, rc; +	unsigned int val;  	u32 rev; +	struct pm_irq_chip *chip; +	unsigned int nirqs = PM8921_NR_IRQS; -	if (!pdata) { -		pr_err("missing platform data\n"); -		return -EINVAL; -	} +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) +		return irq; -	pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL); -	if (!pmic) { -		pr_err("Cannot alloc pm8921 struct\n"); -		return -ENOMEM; -	} +	regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent, +				  &ssbi_regmap_config); +	if (IS_ERR(regmap)) +		return PTR_ERR(regmap);  	/* Read PMIC chip revision */ -	rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); +	rc = regmap_read(regmap, REG_HWREV, &val);  	if (rc) {  		pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);  		return rc; @@ -134,7 +312,7 @@ static int pm8921_probe(struct platform_device *pdev)  	rev = val;  	/* Read PMIC chip revision 2 */ -	rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val)); +	rc = regmap_read(regmap, REG_HWREV_2, &val);  	if (rc) {  		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",  			REG_HWREV_2, rc); @@ -143,40 +321,54 @@ static int pm8921_probe(struct platform_device *pdev)  	pr_info("PMIC revision 2: %02X\n", val);  	rev |= val << BITS_PER_BYTE; -	pmic->dev = &pdev->dev; -	pm8921_drvdata.pm_chip_data = pmic; -	platform_set_drvdata(pdev, &pm8921_drvdata); +	chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + +					sizeof(chip->config[0]) * nirqs, +					GFP_KERNEL); +	if (!chip) +		return -ENOMEM; + +	platform_set_drvdata(pdev, chip); +	chip->regmap = regmap; +	chip->num_irqs = nirqs; +	chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); +	chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); +	spin_lock_init(&chip->pm_irq_lock); + +	chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs, +						&pm8xxx_irq_domain_ops, +						chip); +	if (!chip->irqdomain) +		return -ENODEV; -	rc = pm8921_add_subdevices(pdata, pmic, rev); +	irq_set_handler_data(irq, chip); +	irq_set_chained_handler(irq, pm8xxx_irq_handler); +	irq_set_irq_wake(irq, 1); + +	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);  	if (rc) { -		pr_err("Cannot add subdevices rc=%d\n", rc); -		goto err; +		irq_set_chained_handler(irq, NULL); +		irq_set_handler_data(irq, NULL); +		irq_domain_remove(chip->irqdomain);  	} -	/* gpio might not work if no irq device is found */ -	WARN_ON(pmic->irq_chip == NULL); +	return rc; +} +static int pm8921_remove_child(struct device *dev, void *unused) +{ +	platform_device_unregister(to_platform_device(dev));  	return 0; - -err: -	mfd_remove_devices(pmic->dev); -	return rc;  }  static int pm8921_remove(struct platform_device *pdev)  { -	struct pm8xxx_drvdata *drvdata; -	struct pm8921 *pmic = NULL; - -	drvdata = platform_get_drvdata(pdev); -	if (drvdata) -		pmic = drvdata->pm_chip_data; -	if (pmic) -		mfd_remove_devices(pmic->dev); -	if (pmic->irq_chip) { -		pm8xxx_irq_exit(pmic->irq_chip); -		pmic->irq_chip = NULL; -	} +	int irq = platform_get_irq(pdev, 0); +	struct pm_irq_chip *chip = platform_get_drvdata(pdev); + +	device_for_each_child(&pdev->dev, NULL, pm8921_remove_child); +	irq_set_chained_handler(irq, NULL); +	irq_set_handler_data(irq, NULL); +	irq_domain_remove(chip->irqdomain);  	return 0;  } @@ -187,6 +379,7 @@ static struct platform_driver pm8921_driver = {  	.driver		= {  		.name	= "pm8921-core",  		.owner	= THIS_MODULE, +		.of_match_table = pm8921_id_table,  	},  }; diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c deleted file mode 100644 index 1360e20adf1..00000000000 --- a/drivers/mfd/pm8xxx-irq.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2011, Code Aurora Forum. 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 version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt)	"%s: " fmt, __func__ - -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/kernel.h> -#include <linux/mfd/pm8xxx/core.h> -#include <linux/mfd/pm8xxx/irq.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -/* PMIC8xxx IRQ */ - -#define	SSBI_REG_ADDR_IRQ_BASE		0x1BB - -#define	SSBI_REG_ADDR_IRQ_ROOT		(SSBI_REG_ADDR_IRQ_BASE + 0) -#define	SSBI_REG_ADDR_IRQ_M_STATUS1	(SSBI_REG_ADDR_IRQ_BASE + 1) -#define	SSBI_REG_ADDR_IRQ_M_STATUS2	(SSBI_REG_ADDR_IRQ_BASE + 2) -#define	SSBI_REG_ADDR_IRQ_M_STATUS3	(SSBI_REG_ADDR_IRQ_BASE + 3) -#define	SSBI_REG_ADDR_IRQ_M_STATUS4	(SSBI_REG_ADDR_IRQ_BASE + 4) -#define	SSBI_REG_ADDR_IRQ_BLK_SEL	(SSBI_REG_ADDR_IRQ_BASE + 5) -#define	SSBI_REG_ADDR_IRQ_IT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 6) -#define	SSBI_REG_ADDR_IRQ_CONFIG	(SSBI_REG_ADDR_IRQ_BASE + 7) -#define	SSBI_REG_ADDR_IRQ_RT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 8) - -#define	PM_IRQF_LVL_SEL			0x01	/* level select */ -#define	PM_IRQF_MASK_FE			0x02	/* mask falling edge */ -#define	PM_IRQF_MASK_RE			0x04	/* mask rising edge */ -#define	PM_IRQF_CLR			0x08	/* clear interrupt */ -#define	PM_IRQF_BITS_MASK		0x70 -#define	PM_IRQF_BITS_SHIFT		4 -#define	PM_IRQF_WRITE			0x80 - -#define	PM_IRQF_MASK_ALL		(PM_IRQF_MASK_FE | \ -					PM_IRQF_MASK_RE) - -struct pm_irq_chip { -	struct device		*dev; -	spinlock_t		pm_irq_lock; -	unsigned int		devirq; -	unsigned int		irq_base; -	unsigned int		num_irqs; -	unsigned int		num_blocks; -	unsigned int		num_masters; -	u8			config[0]; -}; - -static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp) -{ -	return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp); -} - -static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp) -{ -	return pm8xxx_readb(chip->dev, -			SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp); -} - -static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip) -{ -	int	rc; - -	spin_lock(&chip->pm_irq_lock); -	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); -	if (rc) { -		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); -		goto bail; -	} - -	rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); -	if (rc) -		pr_err("Failed Reading Status rc=%d\n", rc); -bail: -	spin_unlock(&chip->pm_irq_lock); -	return rc; -} - -static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp) -{ -	int	rc; - -	spin_lock(&chip->pm_irq_lock); -	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); -	if (rc) { -		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); -		goto bail; -	} - -	cp |= PM_IRQF_WRITE; -	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp); -	if (rc) -		pr_err("Failed Configuring IRQ rc=%d\n", rc); -bail: -	spin_unlock(&chip->pm_irq_lock); -	return rc; -} - -static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) -{ -	int pmirq, irq, i, ret = 0; -	u8 bits; - -	ret = pm8xxx_read_block_irq(chip, block, &bits); -	if (ret) { -		pr_err("Failed reading %d block ret=%d", block, ret); -		return ret; -	} -	if (!bits) { -		pr_err("block bit set in master but no irqs: %d", block); -		return 0; -	} - -	/* Check IRQ bits */ -	for (i = 0; i < 8; i++) { -		if (bits & (1 << i)) { -			pmirq = block * 8 + i; -			irq = pmirq + chip->irq_base; -			generic_handle_irq(irq); -		} -	} -	return 0; -} - -static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) -{ -	u8 blockbits; -	int block_number, i, ret = 0; - -	ret = pm8xxx_read_master_irq(chip, master, &blockbits); -	if (ret) { -		pr_err("Failed to read master %d ret=%d\n", master, ret); -		return ret; -	} -	if (!blockbits) { -		pr_err("master bit set in root but no blocks: %d", master); -		return 0; -	} - -	for (i = 0; i < 8; i++) -		if (blockbits & (1 << i)) { -			block_number = master * 8 + i;	/* block # */ -			ret |= pm8xxx_irq_block_handler(chip, block_number); -		} -	return ret; -} - -static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) -{ -	struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); -	struct irq_chip *irq_chip = irq_desc_get_chip(desc); -	u8	root; -	int	i, ret, masters = 0; - -	ret = pm8xxx_read_root_irq(chip, &root); -	if (ret) { -		pr_err("Can't read root status ret=%d\n", ret); -		return; -	} - -	/* on pm8xxx series masters start from bit 1 of the root */ -	masters = root >> 1; - -	/* Read allowed masters for blocks. */ -	for (i = 0; i < chip->num_masters; i++) -		if (masters & (1 << i)) -			pm8xxx_irq_master_handler(chip, i); - -	irq_chip->irq_ack(&desc->irq_data); -} - -static void pm8xxx_irq_mask_ack(struct irq_data *d) -{ -	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); -	unsigned int pmirq = d->irq - chip->irq_base; -	int	master, irq_bit; -	u8	block, config; - -	block = pmirq / 8; -	master = block / 8; -	irq_bit = pmirq % 8; - -	config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; -	pm8xxx_config_irq(chip, block, config); -} - -static void pm8xxx_irq_unmask(struct irq_data *d) -{ -	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); -	unsigned int pmirq = d->irq - chip->irq_base; -	int	master, irq_bit; -	u8	block, config; - -	block = pmirq / 8; -	master = block / 8; -	irq_bit = pmirq % 8; - -	config = chip->config[pmirq]; -	pm8xxx_config_irq(chip, block, config); -} - -static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ -	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); -	unsigned int pmirq = d->irq - chip->irq_base; -	int master, irq_bit; -	u8 block, config; - -	block = pmirq / 8; -	master = block / 8; -	irq_bit  = pmirq % 8; - -	chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) -							| PM_IRQF_MASK_ALL; -	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { -		if (flow_type & IRQF_TRIGGER_RISING) -			chip->config[pmirq] &= ~PM_IRQF_MASK_RE; -		if (flow_type & IRQF_TRIGGER_FALLING) -			chip->config[pmirq] &= ~PM_IRQF_MASK_FE; -	} else { -		chip->config[pmirq] |= PM_IRQF_LVL_SEL; - -		if (flow_type & IRQF_TRIGGER_HIGH) -			chip->config[pmirq] &= ~PM_IRQF_MASK_RE; -		else -			chip->config[pmirq] &= ~PM_IRQF_MASK_FE; -	} - -	config = chip->config[pmirq] | PM_IRQF_CLR; -	return pm8xxx_config_irq(chip, block, config); -} - -static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on) -{ -	return 0; -} - -static struct irq_chip pm8xxx_irq_chip = { -	.name		= "pm8xxx", -	.irq_mask_ack	= pm8xxx_irq_mask_ack, -	.irq_unmask	= pm8xxx_irq_unmask, -	.irq_set_type	= pm8xxx_irq_set_type, -	.irq_set_wake	= pm8xxx_irq_set_wake, -	.flags		= IRQCHIP_MASK_ON_SUSPEND, -}; - -/** - * pm8xxx_get_irq_stat - get the status of the irq line - * @chip: pointer to identify a pmic irq controller - * @irq: the irq number - * - * The pm8xxx gpio and mpp rely on the interrupt block to read - * the values on their pins. This function is to facilitate reading - * the status of a gpio or an mpp line. The caller has to convert the - * gpio number to irq number. - * - * RETURNS: - * an int indicating the value read on that line - */ -int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) -{ -	int pmirq, rc; -	u8  block, bits, bit; -	unsigned long flags; - -	if (chip == NULL || irq < chip->irq_base || -			irq >= chip->irq_base + chip->num_irqs) -		return -EINVAL; - -	pmirq = irq - chip->irq_base; - -	block = pmirq / 8; -	bit = pmirq % 8; - -	spin_lock_irqsave(&chip->pm_irq_lock, flags); - -	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block); -	if (rc) { -		pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n", -			irq, pmirq, block, rc); -		goto bail_out; -	} - -	rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits); -	if (rc) { -		pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n", -			irq, pmirq, block, rc); -		goto bail_out; -	} - -	rc = (bits & (1 << bit)) ? 1 : 0; - -bail_out: -	spin_unlock_irqrestore(&chip->pm_irq_lock, flags); - -	return rc; -} -EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat); - -struct pm_irq_chip *  pm8xxx_irq_init(struct device *dev, -				const struct pm8xxx_irq_platform_data *pdata) -{ -	struct pm_irq_chip  *chip; -	int devirq, rc; -	unsigned int pmirq; - -	if (!pdata) { -		pr_err("No platform data\n"); -		return ERR_PTR(-EINVAL); -	} - -	devirq = pdata->devirq; -	if (devirq < 0) { -		pr_err("missing devirq\n"); -		rc = devirq; -		return ERR_PTR(-EINVAL); -	} - -	chip = kzalloc(sizeof(struct pm_irq_chip) -			+ sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL); -	if (!chip) { -		pr_err("Cannot alloc pm_irq_chip struct\n"); -		return ERR_PTR(-EINVAL); -	} - -	chip->dev = dev; -	chip->devirq = devirq; -	chip->irq_base = pdata->irq_base; -	chip->num_irqs = pdata->irq_cdata.nirqs; -	chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); -	chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); -	spin_lock_init(&chip->pm_irq_lock); - -	for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) { -		irq_set_chip_and_handler(chip->irq_base + pmirq, -				&pm8xxx_irq_chip, -				handle_level_irq); -		irq_set_chip_data(chip->irq_base + pmirq, chip); -#ifdef CONFIG_ARM -		set_irq_flags(chip->irq_base + pmirq, IRQF_VALID); -#else -		irq_set_noprobe(chip->irq_base + pmirq); -#endif -	} - -	irq_set_irq_type(devirq, pdata->irq_trigger_flag); -	irq_set_handler_data(devirq, chip); -	irq_set_chained_handler(devirq, pm8xxx_irq_handler); -	set_irq_wake(devirq, 1); - -	return chip; -} - -int pm8xxx_irq_exit(struct pm_irq_chip *chip) -{ -	irq_set_chained_handler(chip->devirq, NULL); -	kfree(chip); -	return 0; -} diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c index b41db596870..bb850202027 100644 --- a/drivers/mfd/rc5t583-irq.c +++ b/drivers/mfd/rc5t583-irq.c @@ -22,7 +22,6 @@   */  #include <linux/interrupt.h>  #include <linux/irq.h> -#include <linux/init.h>  #include <linux/i2c.h>  #include <linux/mfd/rc5t583.h> diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index 346330176af..df276ad9f40 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -74,7 +74,7 @@ static struct deepsleep_control_data deepsleep_data[] = {  #define EXT_PWR_REQ		\  	(RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) -static struct mfd_cell rc5t583_subdevs[] = { +static const struct mfd_cell rc5t583_subdevs[] = {  	{.name = "rc5t583-gpio",},  	{.name = "rc5t583-regulator",},  	{.name = "rc5t583-rtc",      }, diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 21b7bef7350..6575585f1d1 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -19,7 +19,6 @@   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   *   */ -#include <linux/init.h>  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/platform_device.h> @@ -39,7 +38,7 @@ static struct resource rdc321x_wdt_resource[] = {  };  static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = { -	.max_gpios	= RDC321X_MAX_GPIO, +	.max_gpios	= RDC321X_NUM_GPIO,  };  static struct resource rdc321x_gpio_resources[] = { @@ -56,7 +55,7 @@ static struct resource rdc321x_gpio_resources[] = {  	}  }; -static struct mfd_cell rdc321x_sb_cells[] = { +static const struct mfd_cell rdc321x_sb_cells[] = {  	{  		.name		= "rdc321x-wdt",  		.resources	= rdc321x_wdt_resource, @@ -96,7 +95,7 @@ static void rdc321x_sb_remove(struct pci_dev *pdev)  	mfd_remove_devices(&pdev->dev);  } -static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = { +static const struct pci_device_id rdc321x_sb_table[] = {  	{ PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) },  	{}  }; diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index a1830986eeb..663f8a37aa6 100644 --- a/drivers/mfd/retu-mfd.c +++ b/drivers/mfd/retu-mfd.c @@ -19,7 +19,6 @@  #include <linux/err.h>  #include <linux/i2c.h>  #include <linux/irq.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/mutex.h>  #include <linux/module.h> @@ -55,7 +54,7 @@ static struct resource retu_pwrbutton_res[] = {  	},  }; -static struct mfd_cell retu_devs[] = { +static const struct mfd_cell retu_devs[] = {  	{  		.name		= "retu-wdt"  	}, @@ -94,7 +93,7 @@ static struct resource tahvo_usb_res[] = {  	},  }; -static struct mfd_cell tahvo_devs[] = { +static const struct mfd_cell tahvo_devs[] = {  	{  		.name		= "tahvo-usb",  		.resources	= tahvo_usb_res, @@ -122,7 +121,7 @@ static const struct retu_data {  	char			*chip_name;  	char			*companion_name;  	struct regmap_irq_chip	*irq_chip; -	struct mfd_cell		*children; +	const struct mfd_cell	*children;  	int			nchildren;  } retu_data[] = {  	[0] = { diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 52801351864..fdd34c883d8 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -49,8 +49,8 @@ static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr)  static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr)  { -	u32 reg1; -	u8 reg3; +	u32 reg1 = 0; +	u8 reg3 = 0;  	rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®1);  	dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1); @@ -71,7 +71,7 @@ static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr)  static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr)  { -	u32 reg; +	u32 reg = 0;  	rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®);  	dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); @@ -191,24 +191,25 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)  			BPP_LDO_POWB, BPP_LDO_SUSPEND);  } -static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +static int rtl8411_do_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage, +		int bpp_tuned18_shift, int bpp_asic_1v8)  {  	u8 mask, val;  	int err; -	mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK; +	mask = (BPP_REG_TUNED18 << bpp_tuned18_shift) | BPP_PAD_MASK;  	if (voltage == OUTPUT_3V3) {  		err = rtsx_pci_write_register(pcr,  				SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3);  		if (err < 0)  			return err; -		val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3; +		val = (BPP_ASIC_3V3 << bpp_tuned18_shift) | BPP_PAD_3V3;  	} else if (voltage == OUTPUT_1V8) {  		err = rtsx_pci_write_register(pcr,  				SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8);  		if (err < 0)  			return err; -		val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8; +		val = (bpp_asic_1v8 << bpp_tuned18_shift) | BPP_PAD_1V8;  	} else {  		return -EINVAL;  	} @@ -216,6 +217,18 @@ static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)  	return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);  } +static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ +	return rtl8411_do_switch_output_voltage(pcr, voltage, +			BPP_TUNED18_SHIFT_8411, BPP_ASIC_1V8); +} + +static int rtl8402_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ +	return rtl8411_do_switch_output_voltage(pcr, voltage, +			BPP_TUNED18_SHIFT_8402, BPP_ASIC_2V0); +} +  static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr)  {  	unsigned int card_exist; @@ -295,6 +308,22 @@ static const struct pcr_ops rtl8411_pcr_ops = {  	.force_power_down = rtl8411_force_power_down,  }; +static const struct pcr_ops rtl8402_pcr_ops = { +	.fetch_vendor_settings = rtl8411_fetch_vendor_settings, +	.extra_init_hw = rtl8411_extra_init_hw, +	.optimize_phy = NULL, +	.turn_on_led = rtl8411_turn_on_led, +	.turn_off_led = rtl8411_turn_off_led, +	.enable_auto_blink = rtl8411_enable_auto_blink, +	.disable_auto_blink = rtl8411_disable_auto_blink, +	.card_power_on = rtl8411_card_power_on, +	.card_power_off = rtl8411_card_power_off, +	.switch_output_voltage = rtl8402_switch_output_voltage, +	.cd_deglitch = rtl8411_cd_deglitch, +	.conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, +	.force_power_down = rtl8411_force_power_down, +}; +  static const struct pcr_ops rtl8411b_pcr_ops = {  	.fetch_vendor_settings = rtl8411b_fetch_vendor_settings,  	.extra_init_hw = rtl8411b_extra_init_hw, @@ -441,12 +470,10 @@ static const u32 rtl8411b_qfn48_ms_pull_ctl_disable_tbl[] = {  	0,  }; -void rtl8411_init_params(struct rtsx_pcr *pcr) +static void rtl8411_init_common_params(struct rtsx_pcr *pcr)  {  	pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;  	pcr->num_slots = 2; -	pcr->ops = &rtl8411_pcr_ops; -  	pcr->flags = 0;  	pcr->card_drive_sel = RTL8411_CARD_DRIVE_DEFAULT;  	pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; @@ -454,47 +481,29 @@ void rtl8411_init_params(struct rtsx_pcr *pcr)  	pcr->aspm_en = ASPM_L1_EN;  	pcr->tx_initial_phase = SET_CLOCK_PHASE(23, 7, 14);  	pcr->rx_initial_phase = SET_CLOCK_PHASE(4, 3, 10); -  	pcr->ic_version = rtl8411_get_ic_version(pcr); -	pcr->sd_pull_ctl_enable_tbl = rtl8411_sd_pull_ctl_enable_tbl; -	pcr->sd_pull_ctl_disable_tbl = rtl8411_sd_pull_ctl_disable_tbl; -	pcr->ms_pull_ctl_enable_tbl = rtl8411_ms_pull_ctl_enable_tbl; -	pcr->ms_pull_ctl_disable_tbl = rtl8411_ms_pull_ctl_disable_tbl; +} + +void rtl8411_init_params(struct rtsx_pcr *pcr) +{ +	rtl8411_init_common_params(pcr); +	pcr->ops = &rtl8411_pcr_ops; +	set_pull_ctrl_tables(pcr, rtl8411);  }  void rtl8411b_init_params(struct rtsx_pcr *pcr)  { -	pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; -	pcr->num_slots = 2; +	rtl8411_init_common_params(pcr);  	pcr->ops = &rtl8411b_pcr_ops; +	if (rtl8411b_is_qfn48(pcr)) +		set_pull_ctrl_tables(pcr, rtl8411b_qfn48); +	else +		set_pull_ctrl_tables(pcr, rtl8411b_qfn64); +} -	pcr->flags = 0; -	pcr->card_drive_sel = RTL8411_CARD_DRIVE_DEFAULT; -	pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; -	pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; -	pcr->aspm_en = ASPM_L1_EN; -	pcr->tx_initial_phase = SET_CLOCK_PHASE(23, 7, 14); -	pcr->rx_initial_phase = SET_CLOCK_PHASE(4, 3, 10); - -	pcr->ic_version = rtl8411_get_ic_version(pcr); - -	if (rtl8411b_is_qfn48(pcr)) { -		pcr->sd_pull_ctl_enable_tbl = -			rtl8411b_qfn48_sd_pull_ctl_enable_tbl; -		pcr->sd_pull_ctl_disable_tbl = -			rtl8411b_qfn48_sd_pull_ctl_disable_tbl; -		pcr->ms_pull_ctl_enable_tbl = -			rtl8411b_qfn48_ms_pull_ctl_enable_tbl; -		pcr->ms_pull_ctl_disable_tbl = -			rtl8411b_qfn48_ms_pull_ctl_disable_tbl; -	} else { -		pcr->sd_pull_ctl_enable_tbl = -			rtl8411b_qfn64_sd_pull_ctl_enable_tbl; -		pcr->sd_pull_ctl_disable_tbl = -			rtl8411b_qfn64_sd_pull_ctl_disable_tbl; -		pcr->ms_pull_ctl_enable_tbl = -			rtl8411b_qfn64_ms_pull_ctl_enable_tbl; -		pcr->ms_pull_ctl_disable_tbl = -			rtl8411b_qfn64_ms_pull_ctl_disable_tbl; -	} +void rtl8402_init_params(struct rtsx_pcr *pcr) +{ +	rtl8411_init_common_params(pcr); +	pcr->ops = &rtl8402_pcr_ops; +	set_pull_ctrl_tables(pcr, rtl8411);  } diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 3b835f593e3..573de7bfcce 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -130,13 +130,57 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr)  {  	int err; -	err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, 0xFE46); +	err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, +			PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED | +			PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN | +			PHY_REG_REV_RX_PWST | PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 | +			PHY_REG_REV_STOP_CLKRD | PHY_REG_REV_STOP_CLKWR);  	if (err < 0)  		return err;  	msleep(1); -	return rtsx_pci_write_phy_register(pcr, PHY_BPCR, 0x05C0); +	err = rtsx_pci_write_phy_register(pcr, PHY_BPCR, +			PHY_BPCR_IBRXSEL | PHY_BPCR_IBTXSEL | +			PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN); +	if (err < 0) +		return err; +	err = rtsx_pci_write_phy_register(pcr, PHY_PCR, +			PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | +			PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | +			PHY_PCR_RSSI_EN); +	if (err < 0) +		return err; +	err = rtsx_pci_write_phy_register(pcr, PHY_RCR2, +			PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR | +			PHY_RCR2_CDR_CP_10 | PHY_RCR2_CDR_SR_2 | +			PHY_RCR2_FREQSEL_12 | PHY_RCR2_CPADJEN | +			PHY_RCR2_CDR_SC_8 | PHY_RCR2_CALIB_LATE); +	if (err < 0) +		return err; +	err = rtsx_pci_write_phy_register(pcr, PHY_FLD4, +			PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF | +			PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA | +			PHY_FLD4_BER_COUNT | PHY_FLD4_BER_TIMER | +			PHY_FLD4_BER_CHK_EN); +	if (err < 0) +		return err; +	err = rtsx_pci_write_phy_register(pcr, PHY_RDR, PHY_RDR_RXDSEL_1_9); +	if (err < 0) +		return err; +	err = rtsx_pci_write_phy_register(pcr, PHY_RCR1, +			PHY_RCR1_ADP_TIME | PHY_RCR1_VCO_COARSE); +	if (err < 0) +		return err; +	err = rtsx_pci_write_phy_register(pcr, PHY_FLD3, +			PHY_FLD3_TIMER_4 | PHY_FLD3_TIMER_6 | +			PHY_FLD3_RXDELINK); +	if (err < 0) +		return err; +	return rtsx_pci_write_phy_register(pcr, PHY_TUNE, +			PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 | +			PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 | +			PHY_TUNE_TUNED12);  }  static int rts5249_turn_on_led(struct rtsx_pcr *pcr) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index e6ae7720f9e..1d15735f9ef 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -50,13 +50,14 @@ static struct mfd_cell rtsx_pcr_cells[] = {  	},  }; -static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { +static const struct pci_device_id rtsx_pci_ids[] = {  	{ PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 },  	{ 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 },  	{ PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, +	{ PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 },  	{ 0, }  }; @@ -1061,6 +1062,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)  	case 0x5287:  		rtl8411b_init_params(pcr);  		break; + +	case 0x5286: +		rtl8402_init_params(pcr); +		break;  	}  	dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", @@ -1149,7 +1154,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,  	pcr->remap_addr = ioremap_nocache(base, len);  	if (!pcr->remap_addr) {  		ret = -ENOMEM; -		goto free_host; +		goto free_handle;  	}  	pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev), @@ -1209,8 +1214,6 @@ disable_msi:  			pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);  unmap:  	iounmap(pcr->remap_addr); -free_host: -	dev_set_drvdata(&pcidev->dev, NULL);  free_handle:  	kfree(handle);  free_pcr: @@ -1230,8 +1233,14 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)  	pcr->remove_pci = true; -	cancel_delayed_work(&pcr->carddet_work); -	cancel_delayed_work(&pcr->idle_work); +	/* Disable interrupts at the pcr level */ +	spin_lock_irq(&pcr->lock); +	rtsx_pci_writel(pcr, RTSX_BIER, 0); +	pcr->bier = 0; +	spin_unlock_irq(&pcr->lock); + +	cancel_delayed_work_sync(&pcr->carddet_work); +	cancel_delayed_work_sync(&pcr->idle_work);  	mfd_remove_devices(&pcidev->dev); @@ -1242,7 +1251,6 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)  		pci_disable_msi(pcr->pci);  	iounmap(pcr->remap_addr); -	dev_set_drvdata(&pcidev->dev, NULL);  	pci_release_regions(pcidev);  	pci_disable_device(pcidev); diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 947e79b05ce..07e4c2ebf05 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -30,6 +30,7 @@  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 rtl8402_init_params(struct rtsx_pcr *pcr);  void rts5227_init_params(struct rtsx_pcr *pcr);  void rts5249_init_params(struct rtsx_pcr *pcr);  void rtl8411b_init_params(struct rtsx_pcr *pcr); @@ -63,4 +64,12 @@ static inline u8 map_sd_drive(int idx)  #define rtl8411_reg_to_sd30_drive_sel_3v3(reg)	(((reg) >> 5) & 0x07)  #define rtl8411b_reg_to_sd30_drive_sel_3v3(reg)	((reg) & 0x03) +#define set_pull_ctrl_tables(pcr, __device)				\ +do {									\ +	pcr->sd_pull_ctl_enable_tbl  = __device##_sd_pull_ctl_enable_tbl;  \ +	pcr->sd_pull_ctl_disable_tbl = __device##_sd_pull_ctl_disable_tbl; \ +	pcr->ms_pull_ctl_enable_tbl  = __device##_ms_pull_ctl_enable_tbl;  \ +	pcr->ms_pull_ctl_disable_tbl = __device##_ms_pull_ctl_disable_tbl; \ +} while (0) +  #endif diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c new file mode 100644 index 00000000000..6352bec8419 --- /dev/null +++ b/drivers/mfd/rtsx_usb.c @@ -0,0 +1,766 @@ +/* Driver for Realtek USB 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 version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: + *   Roger Tseng <rogerable@realtek.com> + */ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/usb.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rtsx_usb.h> + +static int polling_pipe = 1; +module_param(polling_pipe, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); + +static const struct mfd_cell rtsx_usb_cells[] = { +	[RTSX_USB_SD_CARD] = { +		.name = "rtsx_usb_sdmmc", +		.pdata_size = 0, +	}, +	[RTSX_USB_MS_CARD] = { +		.name = "rtsx_usb_ms", +		.pdata_size = 0, +	}, +}; + +static void rtsx_usb_sg_timed_out(unsigned long data) +{ +	struct rtsx_ucr *ucr = (struct rtsx_ucr *)data; + +	dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); +	usb_sg_cancel(&ucr->current_sg); + +	/* we know the cancellation is caused by time-out */ +	ucr->current_sg.status = -ETIMEDOUT; +} + +static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, +		unsigned int pipe, struct scatterlist *sg, int num_sg, +		unsigned int length, unsigned int *act_len, int timeout) +{ +	int ret; + +	dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", +			__func__, length, num_sg); +	ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, +			sg, num_sg, length, GFP_NOIO); +	if (ret) +		return ret; + +	ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); +	add_timer(&ucr->sg_timer); +	usb_sg_wait(&ucr->current_sg); +	del_timer_sync(&ucr->sg_timer); + +	if (act_len) +		*act_len = ucr->current_sg.bytes; + +	return ucr->current_sg.status; +} + +int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, +			      void *buf, unsigned int len, int num_sg, +			      unsigned int *act_len, int timeout) +{ +	if (timeout < 600) +		timeout = 600; + +	if (num_sg) +		return rtsx_usb_bulk_transfer_sglist(ucr, pipe, +				(struct scatterlist *)buf, num_sg, len, act_len, +				timeout); +	else +		return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, +				timeout); +} +EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); + +static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr, +		u16 addr, u16 len, u8 seq_type) +{ +	rtsx_usb_cmd_hdr_tag(ucr); + +	ucr->cmd_buf[PACKET_TYPE] = seq_type; +	ucr->cmd_buf[5] = (u8)(len >> 8); +	ucr->cmd_buf[6] = (u8)len; +	ucr->cmd_buf[8] = (u8)(addr >> 8); +	ucr->cmd_buf[9] = (u8)addr; + +	if (seq_type == SEQ_WRITE) +		ucr->cmd_buf[STAGE_FLAG] = 0; +	else +		ucr->cmd_buf[STAGE_FLAG] = STAGE_R; +} + +static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, +		u16 addr, u16 len, u8 *data) +{ +	u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4); + +	if (!data) +		return -EINVAL; + +	if (cmd_len > IOBUF_SIZE) +		return -EINVAL; + +	rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE); +	memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len); + +	return rtsx_usb_transfer_data(ucr, +			usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), +			ucr->cmd_buf, cmd_len, 0, NULL, 100); +} + +static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, +		u16 addr, u16 len, u8 *data) +{ +	int i, ret; +	u16 rsp_len = round_down(len, 4); +	u16 res_len = len - rsp_len; + +	if (!data) +		return -EINVAL; + +	/* 4-byte aligned part */ +	if (rsp_len) { +		rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ); +		ret = rtsx_usb_transfer_data(ucr, +				usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), +				ucr->cmd_buf, 12, 0, NULL, 100); +		if (ret) +			return ret; + +		ret = rtsx_usb_transfer_data(ucr, +				usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), +				data, rsp_len, 0, NULL, 100); +		if (ret) +			return ret; +	} + +	/* unaligned part */ +	for (i = 0; i < res_len; i++) { +		ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, +				data + rsp_len + i); +		if (ret) +			return ret; +	} + +	return 0; +} + +int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) +{ +	return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); +} +EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); + +int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) +{ +	return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); +} +EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); + +int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, +		u8 mask, u8 data) +{ +	u16 value, index; + +	addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT; +	value = swab16(addr); +	index = mask | data << 8; + +	return usb_control_msg(ucr->pusb_dev, +			usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +			value, index, NULL, 0, 100); +} +EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); + +int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) +{ +	u16 value; + +	if (!data) +		return -EINVAL; +	*data = 0; + +	addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; +	value = swab16(addr); + +	return usb_control_msg(ucr->pusb_dev, +			usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, +			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +			value, 0, data, 1, 100); +} +EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); + +void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr, +		u8 mask, u8 data) +{ +	int i; + +	if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) { +		i = CMD_OFFSET + ucr->cmd_idx * 4; + +		ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | +			(u8)((reg_addr >> 8) & 0x3F); +		ucr->cmd_buf[i++] = (u8)reg_addr; +		ucr->cmd_buf[i++] = mask; +		ucr->cmd_buf[i++] = data; + +		ucr->cmd_idx++; +	} +} +EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); + +int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) +{ +	int ret; + +	ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); +	ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); +	ucr->cmd_buf[STAGE_FLAG] = flag; + +	ret = rtsx_usb_transfer_data(ucr, +			usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), +			ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, +			0, NULL, timeout); +	if (ret) { +		rtsx_usb_clear_fsm_err(ucr); +		return ret; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); + +int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) +{ +	if (rsp_len <= 0) +		return -EINVAL; + +	rsp_len = ALIGN(rsp_len, 4); + +	return rtsx_usb_transfer_data(ucr, +			usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), +			ucr->rsp_buf, rsp_len, 0, NULL, timeout); +} +EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); + +static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) +{ +	int ret; + +	rtsx_usb_init_cmd(ucr); +	rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); +	rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); +	ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); +	if (ret) +		return ret; + +	ret = rtsx_usb_get_rsp(ucr, 2, 100); +	if (ret) +		return ret; + +	*status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | +		  ((ucr->rsp_buf[1] & 0x03) << 4); + +	return 0; +} + +int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) +{ +	int ret; + +	if (!status) +		return -EINVAL; + +	if (polling_pipe == 0) +		ret = usb_control_msg(ucr->pusb_dev, +				usb_rcvctrlpipe(ucr->pusb_dev, 0), +				RTSX_USB_REQ_POLL, +				USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +				0, 0, status, 2, 100); +	else +		ret = rtsx_usb_get_status_with_bulk(ucr, status); + +	/* usb_control_msg may return positive when success */ +	if (ret < 0) +		return ret; + +	return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); + +static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) +{ +	dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", +			val, addr); + +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, +			0xFF, (addr >> 4) & 0x0F); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); + +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) +{ +	rtsx_usb_init_cmd(ucr); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} +EXPORT_SYMBOL_GPL(rtsx_usb_write_register); + +int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) +{ +	int ret; + +	if (data != NULL) +		*data = 0; + +	rtsx_usb_init_cmd(ucr); +	rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); +	ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); +	if (ret) +		return ret; + +	ret = rtsx_usb_get_rsp(ucr, 1, 100); +	if (ret) +		return ret; + +	if (data != NULL) +		*data = ucr->rsp_buf[0]; + +	return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_read_register); + +static inline u8 double_ssc_depth(u8 depth) +{ +	return (depth > 1) ? (depth - 1) : depth; +} + +static u8 revise_ssc_depth(u8 ssc_depth, u8 div) +{ +	if (div > CLK_DIV_1) { +		if (ssc_depth > div - 1) +			ssc_depth -= (div - 1); +		else +			ssc_depth = SSC_DEPTH_2M; +	} + +	return ssc_depth; +} + +int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, +		u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) +{ +	int ret; +	u8 n, clk_divider, mcu_cnt, div; + +	if (!card_clock) { +		ucr->cur_clk = 0; +		return 0; +	} + +	if (initial_mode) { +		/* We use 250k(around) here, in initial stage */ +		clk_divider = SD_CLK_DIVIDE_128; +		card_clock = 30000000; +	} else { +		clk_divider = SD_CLK_DIVIDE_0; +	} + +	ret = rtsx_usb_write_register(ucr, SD_CFG1, +			SD_CLK_DIVIDE_MASK, clk_divider); +	if (ret < 0) +		return ret; + +	card_clock /= 1000000; +	dev_dbg(&ucr->pusb_intf->dev, +			"Switch card clock to %dMHz\n", card_clock); + +	if (!initial_mode && double_clk) +		card_clock *= 2; +	dev_dbg(&ucr->pusb_intf->dev, +			"Internal SSC clock: %dMHz (cur_clk = %d)\n", +			card_clock, ucr->cur_clk); + +	if (card_clock == ucr->cur_clk) +		return 0; + +	/* Converting clock value into internal settings: n and div */ +	n = card_clock - 2; +	if ((card_clock <= 2) || (n > MAX_DIV_N)) +		return -EINVAL; + +	mcu_cnt = 60/card_clock + 3; +	if (mcu_cnt > 15) +		mcu_cnt = 15; + +	/* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ + +	div = CLK_DIV_1; +	while (n < MIN_DIV_N && div < CLK_DIV_4) { +		n = (n + 2) * 2 - 2; +		div++; +	} +	dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); + +	if (double_clk) +		ssc_depth = double_ssc_depth(ssc_depth); + +	ssc_depth = revise_ssc_depth(ssc_depth, div); +	dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); + +	rtsx_usb_init_cmd(ucr); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, +			0x3F, (div << 4) | mcu_cnt); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, +			SSC_DEPTH_MASK, ssc_depth); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); +	if (vpclk) { +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, +				PHASE_NOT_RESET, 0); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, +				PHASE_NOT_RESET, PHASE_NOT_RESET); +	} + +	ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); +	if (ret < 0) +		return ret; + +	ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, +			SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); +	if (ret < 0) +		return ret; + +	/* Wait SSC clock stable */ +	usleep_range(100, 1000); + +	ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); +	if (ret < 0) +		return ret; + +	ucr->cur_clk = card_clock; + +	return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); + +int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) +{ +	int ret; +	u16 val; +	u16 cd_mask[] = { +		[RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), +		[RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) +	}; + +	ret = rtsx_usb_get_card_status(ucr, &val); +	/* +	 * If get status fails, return 0 (ok) for the exclusive check +	 * and let the flow fail at somewhere else. +	 */ +	if (ret) +		return 0; + +	if (val & cd_mask[card]) +		return -EIO; + +	return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); + +static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) +{ +	int ret; +	u8 val; + +	rtsx_usb_init_cmd(ucr); + +	if (CHECK_PKG(ucr, LQFP48)) { +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, +				LDO3318_PWR_MASK, LDO_SUSPEND); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, +				FORCE_LDO_POWERB, FORCE_LDO_POWERB); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, +				0x30, 0x10); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, +				0x03, 0x01); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, +				0x0C, 0x04); +	} + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +			CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, +			SD30_DRIVE_MASK, DRIVER_TYPE_D); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +			CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); + +	if (ucr->is_rts5179) +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				CARD_PULL_CTL5, 0x03, 0x01); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, +		       EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, +			XD_INT | MS_INT | SD_INT, +			XD_INT | MS_INT | SD_INT); + +	ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); +	if (ret) +		return ret; + +	/* config non-crystal mode */ +	rtsx_usb_read_register(ucr, CFG_MODE, &val); +	if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { +		ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); +		if (ret) +			return ret; +	} + +	return 0; +} + +static int rtsx_usb_init_chip(struct rtsx_ucr *ucr) +{ +	int ret; +	u8 val; + +	rtsx_usb_clear_fsm_err(ucr); + +	/* power on SSC */ +	ret = rtsx_usb_write_register(ucr, +			FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); +	if (ret) +		return ret; + +	usleep_range(100, 1000); +	ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); +	if (ret) +		return ret; + +	/* determine IC version */ +	ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); +	if (ret) +		return ret; + +	ucr->ic_version = val & HW_VER_MASK; + +	/* determine package */ +	ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); +	if (ret) +		return ret; + +	if (val & CARD_SHARE_LQFP_SEL) { +		ucr->package = LQFP48; +		dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); +	} else { +		ucr->package = QFN24; +		dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); +	} + +	/* determine IC variations */ +	rtsx_usb_read_register(ucr, CFG_MODE_1, &val); +	if (val & RTS5179) { +		ucr->is_rts5179 = true; +		dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); +	} else { +		ucr->is_rts5179 = false; +	} + +	return rtsx_usb_reset_chip(ucr); +} + +static int rtsx_usb_probe(struct usb_interface *intf, +			 const struct usb_device_id *id) +{ +	struct usb_device *usb_dev = interface_to_usbdev(intf); +	struct rtsx_ucr *ucr; +	int ret; + +	dev_dbg(&intf->dev, +		": Realtek USB Card Reader found at bus %03d address %03d\n", +		 usb_dev->bus->busnum, usb_dev->devnum); + +	ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL); +	if (!ucr) +		return -ENOMEM; + +	ucr->pusb_dev = usb_dev; + +	ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE, +			GFP_KERNEL, &ucr->iobuf_dma); +	if (!ucr->iobuf) +		return -ENOMEM; + +	usb_set_intfdata(intf, ucr); + +	ucr->vendor_id = id->idVendor; +	ucr->product_id = id->idProduct; +	ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf; + +	mutex_init(&ucr->dev_mutex); + +	ucr->pusb_intf = intf; + +	/* initialize */ +	ret = rtsx_usb_init_chip(ucr); +	if (ret) +		goto out_init_fail; + +	/* initialize USB SG transfer timer */ +	setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr); + +	ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells, +			ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL); +	if (ret) +		goto out_init_fail; + +#ifdef CONFIG_PM +	intf->needs_remote_wakeup = 1; +	usb_enable_autosuspend(usb_dev); +#endif + +	return 0; + +out_init_fail: +	usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, +			ucr->iobuf_dma); +	return ret; +} + +static void rtsx_usb_disconnect(struct usb_interface *intf) +{ +	struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); + +	dev_dbg(&intf->dev, "%s called\n", __func__); + +	mfd_remove_devices(&intf->dev); + +	usb_set_intfdata(ucr->pusb_intf, NULL); +	usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, +			ucr->iobuf_dma); +} + +#ifdef CONFIG_PM +static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct rtsx_ucr *ucr = +		(struct rtsx_ucr *)usb_get_intfdata(intf); + +	dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n", +			__func__, message.event); + +	/* +	 * Call to make sure LED is off during suspend to save more power. +	 * It is NOT a permanent state and could be turned on anytime later. +	 * Thus no need to call turn_on when resunming. +	 */ +	mutex_lock(&ucr->dev_mutex); +	rtsx_usb_turn_off_led(ucr); +	mutex_unlock(&ucr->dev_mutex); + +	return 0; +} + +static int rtsx_usb_resume(struct usb_interface *intf) +{ +	return 0; +} + +static int rtsx_usb_reset_resume(struct usb_interface *intf) +{ +	struct rtsx_ucr *ucr = +		(struct rtsx_ucr *)usb_get_intfdata(intf); + +	rtsx_usb_reset_chip(ucr); +	return 0; +} + +#else /* CONFIG_PM */ + +#define rtsx_usb_suspend NULL +#define rtsx_usb_resume NULL +#define rtsx_usb_reset_resume NULL + +#endif /* CONFIG_PM */ + + +static int rtsx_usb_pre_reset(struct usb_interface *intf) +{ +	struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); + +	mutex_lock(&ucr->dev_mutex); +	return 0; +} + +static int rtsx_usb_post_reset(struct usb_interface *intf) +{ +	struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); + +	mutex_unlock(&ucr->dev_mutex); +	return 0; +} + +static struct usb_device_id rtsx_usb_usb_ids[] = { +	{ USB_DEVICE(0x0BDA, 0x0129) }, +	{ USB_DEVICE(0x0BDA, 0x0139) }, +	{ USB_DEVICE(0x0BDA, 0x0140) }, +	{ } +}; + +static struct usb_driver rtsx_usb_driver = { +	.name			= "rtsx_usb", +	.probe			= rtsx_usb_probe, +	.disconnect		= rtsx_usb_disconnect, +	.suspend		= rtsx_usb_suspend, +	.resume			= rtsx_usb_resume, +	.reset_resume		= rtsx_usb_reset_resume, +	.pre_reset		= rtsx_usb_pre_reset, +	.post_reset		= rtsx_usb_post_reset, +	.id_table		= rtsx_usb_usb_ids, +	.supports_autosuspend	= 1, +	.soft_unbind		= 1, +}; + +module_usb_driver(rtsx_usb_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); +MODULE_DESCRIPTION("Realtek USB Card Reader Driver"); diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index f530e4b73f1..be06d0abbf1 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -17,6 +17,7 @@  #include <linux/err.h>  #include <linux/slab.h>  #include <linux/i2c.h> +#include <linux/of.h>  #include <linux/of_irq.h>  #include <linux/interrupt.h>  #include <linux/pm_runtime.h> @@ -24,13 +25,14 @@  #include <linux/mfd/core.h>  #include <linux/mfd/samsung/core.h>  #include <linux/mfd/samsung/irq.h> -#include <linux/mfd/samsung/rtc.h> +#include <linux/mfd/samsung/s2mpa01.h>  #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps14.h>  #include <linux/mfd/samsung/s5m8763.h>  #include <linux/mfd/samsung/s5m8767.h>  #include <linux/regmap.h> -static struct mfd_cell s5m8751_devs[] = { +static const struct mfd_cell s5m8751_devs[] = {  	{  		.name = "s5m8751-pmic",  	}, { @@ -40,7 +42,7 @@ static struct mfd_cell s5m8751_devs[] = {  	},  }; -static struct mfd_cell s5m8763_devs[] = { +static const struct mfd_cell s5m8763_devs[] = {  	{  		.name = "s5m8763-pmic",  	}, { @@ -50,63 +52,73 @@ static struct mfd_cell s5m8763_devs[] = {  	},  }; -static struct mfd_cell s5m8767_devs[] = { +static const struct mfd_cell s5m8767_devs[] = {  	{  		.name = "s5m8767-pmic",  	}, {  		.name = "s5m-rtc", -	}, +	}, { +		.name = "s5m8767-clk", +		.of_compatible = "samsung,s5m8767-clk", +	}  }; -static struct mfd_cell s2mps11_devs[] = { +static const struct mfd_cell s2mps11_devs[] = {  	{  		.name = "s2mps11-pmic",  	}, {  		.name = "s2mps11-clk", +		.of_compatible = "samsung,s2mps11-clk", +	} +}; + +static const struct mfd_cell s2mps14_devs[] = { +	{ +		.name = "s2mps14-pmic", +	}, { +		.name = "s2mps14-rtc", +	}, { +		.name = "s2mps14-clk", +		.of_compatible = "samsung,s2mps14-clk",  	}  }; +static const struct mfd_cell s2mpa01_devs[] = { +	{ +		.name = "s2mpa01-pmic", +	}, +}; +  #ifdef CONFIG_OF -static struct of_device_id sec_dt_match[] = { +static const struct of_device_id sec_dt_match[] = {  	{	.compatible = "samsung,s5m8767-pmic",  		.data = (void *)S5M8767X, -	}, -	{	.compatible = "samsung,s2mps11-pmic", +	}, { +		.compatible = "samsung,s2mps11-pmic",  		.data = (void *)S2MPS11X, +	}, { +		.compatible = "samsung,s2mps14-pmic", +		.data = (void *)S2MPS14X, +	}, { +		.compatible = "samsung,s2mpa01-pmic", +		.data = (void *)S2MPA01, +	}, { +		/* Sentinel */  	}, -	{},  };  #endif -int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest) +static bool s2mpa01_volatile(struct device *dev, unsigned int reg)  { -	return regmap_read(sec_pmic->regmap, reg, dest); -} -EXPORT_SYMBOL_GPL(sec_reg_read); - -int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf) -{ -	return regmap_bulk_read(sec_pmic->regmap, reg, buf, count); -} -EXPORT_SYMBOL_GPL(sec_bulk_read); - -int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value) -{ -	return regmap_write(sec_pmic->regmap, reg, value); -} -EXPORT_SYMBOL_GPL(sec_reg_write); - -int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf) -{ -	return regmap_raw_write(sec_pmic->regmap, reg, buf, count); -} -EXPORT_SYMBOL_GPL(sec_bulk_write); - -int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask) -{ -	return regmap_update_bits(sec_pmic->regmap, reg, mask, val); +	switch (reg) { +	case S2MPA01_REG_INT1M: +	case S2MPA01_REG_INT2M: +	case S2MPA01_REG_INT3M: +		return false; +	default: +		return true; +	}  } -EXPORT_SYMBOL_GPL(sec_reg_update);  static bool s2mps11_volatile(struct device *dev, unsigned int reg)  { @@ -133,12 +145,21 @@ static bool s5m8763_volatile(struct device *dev, unsigned int reg)  	}  } -static struct regmap_config sec_regmap_config = { +static const struct regmap_config sec_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +}; + +static const struct regmap_config s2mpa01_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, + +	.max_register = S2MPA01_REG_LDO_OVCB4, +	.volatile_reg = s2mpa01_volatile, +	.cache_type = REGCACHE_FLAT,  }; -static struct regmap_config s2mps11_regmap_config = { +static const struct regmap_config s2mps11_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, @@ -147,7 +168,16 @@ static struct regmap_config s2mps11_regmap_config = {  	.cache_type = REGCACHE_FLAT,  }; -static struct regmap_config s5m8763_regmap_config = { +static const struct regmap_config s2mps14_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, + +	.max_register = S2MPS14_REG_LDODSCH3, +	.volatile_reg = s2mps11_volatile, +	.cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config s5m8763_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, @@ -156,7 +186,7 @@ static struct regmap_config s5m8763_regmap_config = {  	.cache_type = REGCACHE_FLAT,  }; -static struct regmap_config s5m8767_regmap_config = { +static const struct regmap_config s5m8767_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, @@ -198,21 +228,21 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(  static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(  					struct device *dev)  { -	return 0; +	return NULL;  }  #endif -static inline int sec_i2c_get_driver_data(struct i2c_client *i2c, +static inline unsigned long sec_i2c_get_driver_data(struct i2c_client *i2c,  						const struct i2c_device_id *id)  {  #ifdef CONFIG_OF  	if (i2c->dev.of_node) {  		const struct of_device_id *match;  		match = of_match_node(sec_dt_match, i2c->dev.of_node); -		return (int)match->data; +		return (unsigned long)match->data;  	}  #endif -	return (int)id->driver_data; +	return id->driver_data;  }  static int sec_pmic_probe(struct i2c_client *i2c, @@ -221,6 +251,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,  	struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev);  	const struct regmap_config *regmap;  	struct sec_pmic_dev *sec_pmic; +	unsigned long device_type;  	int ret;  	sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev), @@ -232,7 +263,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,  	sec_pmic->dev = &i2c->dev;  	sec_pmic->i2c = i2c;  	sec_pmic->irq = i2c->irq; -	sec_pmic->type = sec_i2c_get_driver_data(i2c, id); +	device_type = sec_i2c_get_driver_data(i2c, id);  	if (sec_pmic->dev->of_node) {  		pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev); @@ -240,7 +271,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,  			ret = PTR_ERR(pdata);  			return ret;  		} -		pdata->device_type = sec_pmic->type; +		pdata->device_type = device_type;  	}  	if (pdata) {  		sec_pmic->device_type = pdata->device_type; @@ -251,9 +282,15 @@ static int sec_pmic_probe(struct i2c_client *i2c,  	}  	switch (sec_pmic->device_type) { +	case S2MPA01: +		regmap = &s2mpa01_regmap_config; +		break;  	case S2MPS11X:  		regmap = &s2mps11_regmap_config;  		break; +	case S2MPS14X: +		regmap = &s2mps14_regmap_config; +		break;  	case S5M8763X:  		regmap = &s5m8763_regmap_config;  		break; @@ -265,17 +302,14 @@ static int sec_pmic_probe(struct i2c_client *i2c,  		break;  	} -	sec_pmic->regmap = devm_regmap_init_i2c(i2c, regmap); -	if (IS_ERR(sec_pmic->regmap)) { -		ret = PTR_ERR(sec_pmic->regmap); +	sec_pmic->regmap_pmic = devm_regmap_init_i2c(i2c, regmap); +	if (IS_ERR(sec_pmic->regmap_pmic)) { +		ret = PTR_ERR(sec_pmic->regmap_pmic);  		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",  			ret);  		return ret;  	} -	sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); -	i2c_set_clientdata(sec_pmic->rtc, sec_pmic); -  	if (pdata && pdata->cfg_pmic_irq)  		pdata->cfg_pmic_irq(); @@ -296,23 +330,32 @@ static int sec_pmic_probe(struct i2c_client *i2c,  		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,  				      ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL);  		break; +	case S2MPA01: +		ret = mfd_add_devices(sec_pmic->dev, -1, s2mpa01_devs, +				      ARRAY_SIZE(s2mpa01_devs), NULL, 0, NULL); +		break;  	case S2MPS11X:  		ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,  				      ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL);  		break; +	case S2MPS14X: +		ret = mfd_add_devices(sec_pmic->dev, -1, s2mps14_devs, +				      ARRAY_SIZE(s2mps14_devs), NULL, 0, NULL); +		break;  	default:  		/* If this happens the probe function is problem */  		BUG();  	}  	if (ret) -		goto err; +		goto err_mfd; + +	device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);  	return ret; -err: +err_mfd:  	sec_irq_exit(sec_pmic); -	i2c_unregister_device(sec_pmic->rtc);  	return ret;  } @@ -322,10 +365,46 @@ static int sec_pmic_remove(struct i2c_client *i2c)  	mfd_remove_devices(sec_pmic->dev);  	sec_irq_exit(sec_pmic); -	i2c_unregister_device(sec_pmic->rtc);  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int sec_pmic_suspend(struct device *dev) +{ +	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); +	struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); + +	if (device_may_wakeup(dev)) +		enable_irq_wake(sec_pmic->irq); +	/* +	 * PMIC IRQ must be disabled during suspend for RTC alarm +	 * to work properly. +	 * When device is woken up from suspend, an +	 * interrupt occurs before resuming I2C bus controller. +	 * The interrupt is handled by regmap_irq_thread which tries +	 * to read RTC registers. This read fails (I2C is still +	 * suspended) and RTC Alarm interrupt is disabled. +	 */ +	disable_irq(sec_pmic->irq); + +	return 0; +} + +static int sec_pmic_resume(struct device *dev) +{ +	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); +	struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); + +	if (device_may_wakeup(dev)) +		disable_irq_wake(sec_pmic->irq); +	enable_irq(sec_pmic->irq); + +	return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume); +  static const struct i2c_device_id sec_pmic_id[] = {  	{ "sec_pmic", 0 },  	{ } @@ -336,6 +415,7 @@ static struct i2c_driver sec_pmic_driver = {  	.driver = {  		   .name = "sec_pmic",  		   .owner = THIS_MODULE, +		   .pm = &sec_pmic_pm_ops,  		   .of_match_table = of_match_ptr(sec_dt_match),  	},  	.probe = sec_pmic_probe, diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index 0dd84e99081..654e2c1dbf7 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -1,7 +1,7 @@  /*   * sec-irq.c   * - * Copyright (c) 2011 Samsung Electronics Co., Ltd + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd   *              http://www.samsung.com   *   *  This program is free software; you can redistribute  it and/or modify it @@ -19,10 +19,11 @@  #include <linux/mfd/samsung/core.h>  #include <linux/mfd/samsung/irq.h>  #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps14.h>  #include <linux/mfd/samsung/s5m8763.h>  #include <linux/mfd/samsung/s5m8767.h> -static struct regmap_irq s2mps11_irqs[] = { +static const struct regmap_irq s2mps11_irqs[] = {  	[S2MPS11_IRQ_PWRONF] = {  		.reg_offset = 0,  		.mask = S2MPS11_IRQ_PWRONF_MASK, @@ -59,13 +60,13 @@ static struct regmap_irq s2mps11_irqs[] = {  		.reg_offset = 1,  		.mask = S2MPS11_IRQ_RTC60S_MASK,  	}, -	[S2MPS11_IRQ_RTCA1] = { +	[S2MPS11_IRQ_RTCA0] = {  		.reg_offset = 1, -		.mask = S2MPS11_IRQ_RTCA1_MASK, +		.mask = S2MPS11_IRQ_RTCA0_MASK,  	}, -	[S2MPS11_IRQ_RTCA2] = { +	[S2MPS11_IRQ_RTCA1] = {  		.reg_offset = 1, -		.mask = S2MPS11_IRQ_RTCA2_MASK, +		.mask = S2MPS11_IRQ_RTCA1_MASK,  	},  	[S2MPS11_IRQ_SMPL] = {  		.reg_offset = 1, @@ -89,8 +90,78 @@ static struct regmap_irq s2mps11_irqs[] = {  	},  }; +static const struct regmap_irq s2mps14_irqs[] = { +	[S2MPS14_IRQ_PWRONF] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_PWRONF_MASK, +	}, +	[S2MPS14_IRQ_PWRONR] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_PWRONR_MASK, +	}, +	[S2MPS14_IRQ_JIGONBF] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_JIGONBF_MASK, +	}, +	[S2MPS14_IRQ_JIGONBR] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_JIGONBR_MASK, +	}, +	[S2MPS14_IRQ_ACOKBF] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_ACOKBF_MASK, +	}, +	[S2MPS14_IRQ_ACOKBR] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_ACOKBR_MASK, +	}, +	[S2MPS14_IRQ_PWRON1S] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_PWRON1S_MASK, +	}, +	[S2MPS14_IRQ_MRB] = { +		.reg_offset = 0, +		.mask = S2MPS11_IRQ_MRB_MASK, +	}, +	[S2MPS14_IRQ_RTC60S] = { +		.reg_offset = 1, +		.mask = S2MPS11_IRQ_RTC60S_MASK, +	}, +	[S2MPS14_IRQ_RTCA1] = { +		.reg_offset = 1, +		.mask = S2MPS11_IRQ_RTCA1_MASK, +	}, +	[S2MPS14_IRQ_RTCA0] = { +		.reg_offset = 1, +		.mask = S2MPS11_IRQ_RTCA0_MASK, +	}, +	[S2MPS14_IRQ_SMPL] = { +		.reg_offset = 1, +		.mask = S2MPS11_IRQ_SMPL_MASK, +	}, +	[S2MPS14_IRQ_RTC1S] = { +		.reg_offset = 1, +		.mask = S2MPS11_IRQ_RTC1S_MASK, +	}, +	[S2MPS14_IRQ_WTSR] = { +		.reg_offset = 1, +		.mask = S2MPS11_IRQ_WTSR_MASK, +	}, +	[S2MPS14_IRQ_INT120C] = { +		.reg_offset = 2, +		.mask = S2MPS11_IRQ_INT120C_MASK, +	}, +	[S2MPS14_IRQ_INT140C] = { +		.reg_offset = 2, +		.mask = S2MPS11_IRQ_INT140C_MASK, +	}, +	[S2MPS14_IRQ_TSD] = { +		.reg_offset = 2, +		.mask = S2MPS14_IRQ_TSD_MASK, +	}, +}; -static struct regmap_irq s5m8767_irqs[] = { +static const struct regmap_irq s5m8767_irqs[] = {  	[S5M8767_IRQ_PWRR] = {  		.reg_offset = 0,  		.mask = S5M8767_IRQ_PWRR_MASK, @@ -161,7 +232,7 @@ static struct regmap_irq s5m8767_irqs[] = {  	},  }; -static struct regmap_irq s5m8763_irqs[] = { +static const struct regmap_irq s5m8763_irqs[] = {  	[S5M8763_IRQ_DCINF] = {  		.reg_offset = 0,  		.mask = S5M8763_IRQ_DCINF_MASK, @@ -236,7 +307,7 @@ static struct regmap_irq s5m8763_irqs[] = {  	},  }; -static struct regmap_irq_chip s2mps11_irq_chip = { +static const struct regmap_irq_chip s2mps11_irq_chip = {  	.name = "s2mps11",  	.irqs = s2mps11_irqs,  	.num_irqs = ARRAY_SIZE(s2mps11_irqs), @@ -246,7 +317,17 @@ static struct regmap_irq_chip s2mps11_irq_chip = {  	.ack_base = S2MPS11_REG_INT1,  }; -static struct regmap_irq_chip s5m8767_irq_chip = { +static const struct regmap_irq_chip s2mps14_irq_chip = { +	.name = "s2mps14", +	.irqs = s2mps14_irqs, +	.num_irqs = ARRAY_SIZE(s2mps14_irqs), +	.num_regs = 3, +	.status_base = S2MPS14_REG_INT1, +	.mask_base = S2MPS14_REG_INT1M, +	.ack_base = S2MPS14_REG_INT1, +}; + +static const struct regmap_irq_chip s5m8767_irq_chip = {  	.name = "s5m8767",  	.irqs = s5m8767_irqs,  	.num_irqs = ARRAY_SIZE(s5m8767_irqs), @@ -256,7 +337,7 @@ static struct regmap_irq_chip s5m8767_irq_chip = {  	.ack_base = S5M8767_REG_INT1,  }; -static struct regmap_irq_chip s5m8763_irq_chip = { +static const struct regmap_irq_chip s5m8763_irq_chip = {  	.name = "s5m8763",  	.irqs = s5m8763_irqs,  	.num_irqs = ARRAY_SIZE(s5m8763_irqs), @@ -280,25 +361,31 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)  	switch (type) {  	case S5M8763X: -		ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq, +		ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,  				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,  				  sec_pmic->irq_base, &s5m8763_irq_chip,  				  &sec_pmic->irq_data);  		break;  	case S5M8767X: -		ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq, +		ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,  				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,  				  sec_pmic->irq_base, &s5m8767_irq_chip,  				  &sec_pmic->irq_data);  		break;  	case S2MPS11X: -		ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq, +		ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,  				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,  				  sec_pmic->irq_base, &s2mps11_irq_chip,  				  &sec_pmic->irq_data);  		break; +	case S2MPS14X: +		ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, +				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +				  sec_pmic->irq_base, &s2mps14_irq_chip, +				  &sec_pmic->irq_data); +		break;  	default: -		dev_err(sec_pmic->dev, "Unknown device type %d\n", +		dev_err(sec_pmic->dev, "Unknown device type %lu\n",  			sec_pmic->device_type);  		return -EINVAL;  	} diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 33f040c558d..81e6d0932bf 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1232,7 +1232,7 @@ static ssize_t sm501_dbg_regs(struct device *dev,  } -static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL); +static DEVICE_ATTR(dbg_regs, 0444, sm501_dbg_regs, NULL);  /* sm501_init_reg   * @@ -1660,7 +1660,6 @@ static int sm501_pci_probe(struct pci_dev *dev,   err3:  	pci_disable_device(dev);   err2: -	pci_set_drvdata(dev, NULL);  	kfree(sm);   err1:  	return err; @@ -1695,7 +1694,6 @@ static void sm501_pci_remove(struct pci_dev *dev)  	release_resource(sm->regs_claim);  	kfree(sm->regs_claim); -	pci_set_drvdata(dev, NULL);  	pci_disable_device(dev);  } @@ -1712,7 +1710,7 @@ static int sm501_plat_remove(struct platform_device *dev)  	return 0;  } -static DEFINE_PCI_DEVICE_TABLE(sm501_pci_tbl) = { +static const struct pci_device_id sm501_pci_tbl[] = {  	{ 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },  	{ 0, },  }; @@ -1728,7 +1726,7 @@ static struct pci_driver sm501_pci_driver = {  MODULE_ALIAS("platform:sm501"); -static struct of_device_id of_sm501_match_tbl[] = { +static const struct of_device_id of_sm501_match_tbl[] = {  	{ .compatible = "smi,sm501", },  	{ /* end */ }  }; diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c index 24ae3d8421c..90112d4cc90 100644 --- a/drivers/mfd/smsc-ece1099.c +++ b/drivers/mfd/smsc-ece1099.c @@ -13,7 +13,6 @@  #include <linux/module.h>  #include <linux/moduleparam.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/i2c.h>  #include <linux/gpio.h> diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c index 102a2284429..b78942ed4c6 100644 --- a/drivers/mfd/ssbi.c +++ b/drivers/mfd/ssbi.c @@ -65,13 +65,19 @@  #define SSBI_TIMEOUT_US			100 +enum ssbi_controller_type { +	MSM_SBI_CTRL_SSBI = 0, +	MSM_SBI_CTRL_SSBI2, +	MSM_SBI_CTRL_PMIC_ARBITER, +}; +  struct ssbi {  	struct device		*slave;  	void __iomem		*base;  	spinlock_t		lock;  	enum ssbi_controller_type controller_type;  	int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); -	int (*write)(struct ssbi *, u16 addr, u8 *buf, int len); +	int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len);  };  #define to_ssbi(dev)	platform_get_drvdata(to_platform_device(dev)) @@ -140,7 +146,7 @@ err:  }  static int -ssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +ssbi_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)  {  	int ret = 0; @@ -217,7 +223,7 @@ err:  }  static int -ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)  {  	u32 cmd;  	int ret = 0; @@ -249,7 +255,7 @@ int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)  }  EXPORT_SYMBOL_GPL(ssbi_read); -int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) +int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len)  {  	struct ssbi *ssbi = to_ssbi(dev);  	unsigned long flags; @@ -311,7 +317,7 @@ static int ssbi_probe(struct platform_device *pdev)  	return of_platform_populate(np, NULL, NULL, &pdev->dev);  } -static struct of_device_id ssbi_match_table[] = { +static const struct of_device_id ssbi_match_table[] = {  	{ .compatible = "qcom,ssbi" },  	{}  }; diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 65c6fa671ac..5b72db07d9d 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -339,7 +339,7 @@ static int sta2x11_mfd_platform_probe(struct platform_device *dev,  	regmap_config->cache_type = REGCACHE_NONE;  	mfd->regmap[index] = devm_regmap_init_mmio(&dev->dev, mfd->regs[index],  						   regmap_config); -	WARN_ON(!mfd->regmap[index]); +	WARN_ON(IS_ERR(mfd->regmap[index]));  	return 0;  } @@ -529,7 +529,7 @@ static int sta2x11_mfd_resume(struct pci_dev *pdev)  {  	int err; -	pci_set_power_state(pdev, 0); +	pci_set_power_state(pdev, PCI_D0);  	err = pci_enable_device(pdev);  	if (err)  		return err; @@ -642,7 +642,7 @@ err_disable:  	return err;  } -static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = { +static const struct pci_device_id sta2x11_mfd_tbl[] = {  	{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)},  	{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIC)},  	{0,}, diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index 0da02e11d58..a45f9c0a330 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -14,6 +14,7 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/types.h> +#include <linux/of_device.h>  #include "stmpe.h"  static int i2c_reg_read(struct stmpe *stmpe, u8 reg) @@ -52,15 +53,41 @@ static struct stmpe_client_info i2c_ci = {  	.write_block = i2c_block_write,  }; +static const struct of_device_id stmpe_of_match[] = { +	{ .compatible = "st,stmpe610", .data = (void *)STMPE610, }, +	{ .compatible = "st,stmpe801", .data = (void *)STMPE801, }, +	{ .compatible = "st,stmpe811", .data = (void *)STMPE811, }, +	{ .compatible = "st,stmpe1601", .data = (void *)STMPE1601, }, +	{ .compatible = "st,stmpe1801", .data = (void *)STMPE1801, }, +	{ .compatible = "st,stmpe2401", .data = (void *)STMPE2401, }, +	{ .compatible = "st,stmpe2403", .data = (void *)STMPE2403, }, +	{}, +}; +MODULE_DEVICE_TABLE(of, stmpe_of_match); +  static int  stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)  { +	int partnum; +	const struct of_device_id *of_id; +  	i2c_ci.data = (void *)id;  	i2c_ci.irq = i2c->irq;  	i2c_ci.client = i2c;  	i2c_ci.dev = &i2c->dev; -	return stmpe_probe(&i2c_ci, id->driver_data); +	of_id = of_match_device(stmpe_of_match, &i2c->dev); +	if (!of_id) { +		/* +		 * This happens when the I2C ID matches the node name +		 * but no real compatible string has been given. +		 */ +		dev_info(&i2c->dev, "matching on node name, compatible is preferred\n"); +		partnum = id->driver_data; +	} else +		partnum = (int)of_id->data; + +	return stmpe_probe(&i2c_ci, partnum);  }  static int stmpe_i2c_remove(struct i2c_client *i2c) @@ -89,6 +116,7 @@ static struct i2c_driver stmpe_i2c_driver = {  #ifdef CONFIG_PM  		.pm = &stmpe_dev_pm_ops,  #endif +		.of_match_table = stmpe_of_match,  	},  	.probe		= stmpe_i2c_probe,  	.remove		= stmpe_i2c_remove, diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index fff63a41862..3b6bfa7184a 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -20,6 +20,7 @@  #include <linux/slab.h>  #include <linux/mfd/core.h>  #include <linux/delay.h> +#include <linux/regulator/consumer.h>  #include "stmpe.h"  static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks) @@ -297,14 +298,14 @@ static struct resource stmpe_gpio_resources[] = {  	},  }; -static struct mfd_cell stmpe_gpio_cell = { +static const struct mfd_cell stmpe_gpio_cell = {  	.name		= "stmpe-gpio",  	.of_compatible	= "st,stmpe-gpio",  	.resources	= stmpe_gpio_resources,  	.num_resources	= ARRAY_SIZE(stmpe_gpio_resources),  }; -static struct mfd_cell stmpe_gpio_cell_noirq = { +static const struct mfd_cell stmpe_gpio_cell_noirq = {  	.name		= "stmpe-gpio",  	.of_compatible	= "st,stmpe-gpio",  	/* gpio cell resources consist of an irq only so no resources here */ @@ -325,7 +326,7 @@ static struct resource stmpe_keypad_resources[] = {  	},  }; -static struct mfd_cell stmpe_keypad_cell = { +static const struct mfd_cell stmpe_keypad_cell = {  	.name		= "stmpe-keypad",  	.of_compatible  = "st,stmpe-keypad",  	.resources	= stmpe_keypad_resources, @@ -409,7 +410,7 @@ static struct resource stmpe_ts_resources[] = {  	},  }; -static struct mfd_cell stmpe_ts_cell = { +static const struct mfd_cell stmpe_ts_cell = {  	.name		= "stmpe-ts",  	.of_compatible	= "st,stmpe-ts",  	.resources	= stmpe_ts_resources, @@ -605,9 +606,18 @@ static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,  	if (blocks & STMPE_BLOCK_GPIO)  		mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO; +	else +		mask &= ~STMPE1601_SYS_CTRL_ENABLE_GPIO;  	if (blocks & STMPE_BLOCK_KEYPAD)  		mask |= STMPE1601_SYS_CTRL_ENABLE_KPC; +	else +		mask &= ~STMPE1601_SYS_CTRL_ENABLE_KPC; + +	if (blocks & STMPE_BLOCK_PWM) +		mask |= STMPE1601_SYS_CTRL_ENABLE_SPWM; +	else +		mask &= ~STMPE1601_SYS_CTRL_ENABLE_SPWM;  	return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,  				enable ? mask : 0); @@ -706,7 +716,7 @@ static int stmpe1801_reset(struct stmpe *stmpe)  		if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET))  			return 0;  		usleep_range(100, 200); -	}; +	}  	return -EIO;  } @@ -986,9 +996,6 @@ static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np)  	int base = 0;  	int num_irqs = stmpe->variant->num_irqs; -	if (!np) -		base = stmpe->irq_base; -  	stmpe->domain = irq_domain_add_simple(np, num_irqs, base,  					      &stmpe_irq_ops, stmpe);  	if (!stmpe->domain) { @@ -1064,10 +1071,10 @@ static int stmpe_chip_init(struct stmpe *stmpe)  	return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);  } -static int stmpe_add_device(struct stmpe *stmpe, struct mfd_cell *cell) +static int stmpe_add_device(struct stmpe *stmpe, const struct mfd_cell *cell)  {  	return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, -			       NULL, stmpe->irq_base, stmpe->domain); +			       NULL, 0, stmpe->domain);  }  static int stmpe_devices_init(struct stmpe *stmpe) @@ -1171,12 +1178,23 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)  	stmpe->dev = ci->dev;  	stmpe->client = ci->client;  	stmpe->pdata = pdata; -	stmpe->irq_base = pdata->irq_base;  	stmpe->ci = ci;  	stmpe->partnum = partnum;  	stmpe->variant = stmpe_variant_info[partnum];  	stmpe->regs = stmpe->variant->regs;  	stmpe->num_gpios = stmpe->variant->num_gpios; +	stmpe->vcc = devm_regulator_get_optional(ci->dev, "vcc"); +	if (!IS_ERR(stmpe->vcc)) { +		ret = regulator_enable(stmpe->vcc); +		if (ret) +			dev_warn(ci->dev, "failed to enable VCC supply\n"); +	} +	stmpe->vio = devm_regulator_get_optional(ci->dev, "vio"); +	if (!IS_ERR(stmpe->vio)) { +		ret = regulator_enable(stmpe->vio); +		if (ret) +			dev_warn(ci->dev, "failed to enable VIO supply\n"); +	}  	dev_set_drvdata(stmpe->dev, stmpe);  	if (ci->init) @@ -1243,6 +1261,11 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)  int stmpe_remove(struct stmpe *stmpe)  { +	if (!IS_ERR(stmpe->vio)) +		regulator_disable(stmpe->vio); +	if (!IS_ERR(stmpe->vcc)) +		regulator_disable(stmpe->vcc); +  	mfd_remove_devices(stmpe->dev);  	return 0; diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index ff2b09ba879..9e4d21d37a1 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -38,7 +38,7 @@ static inline void stmpe_dump_bytes(const char *str, const void *buf,   *		enable and altfunc callbacks   */  struct stmpe_variant_block { -	struct mfd_cell		*cell; +	const struct mfd_cell	*cell;  	int			irq;  	enum stmpe_block	block;  }; @@ -192,7 +192,7 @@ int stmpe_remove(struct stmpe *stmpe);  #define STMPE1601_SYS_CTRL_ENABLE_GPIO		(1 << 3)  #define STMPE1601_SYS_CTRL_ENABLE_KPC		(1 << 1) -#define STMPE1601_SYSCON_ENABLE_SPWM		(1 << 0) +#define STMPE1601_SYS_CTRL_ENABLE_SPWM		(1 << 0)  /* The 1601/2403 share the same masks */  #define STMPE1601_AUTOSLEEP_TIMEOUT_MASK	(0x7) diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c new file mode 100644 index 00000000000..7ceb3df09e2 --- /dev/null +++ b/drivers/mfd/stw481x.c @@ -0,0 +1,256 @@ +/* + * Core driver for STw4810/STw4811 + * + * Copyright (C) 2013 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/mfd/core.h> +#include <linux/mfd/stw481x.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> +#include <linux/slab.h> + +/* + * This driver can only access the non-USB portions of STw4811, the register + * range 0x00-0x10 dealing with USB is bound to the two special I2C pins used + * for USB control. + */ + +/* Registers inside the power control address space */ +#define STW_PC_VCORE_SEL	0x05U +#define STW_PC_VAUX_SEL		0x06U +#define STW_PC_VPLL_SEL		0x07U + +/** + * stw481x_get_pctl_reg() - get a power control register + * @stw481x: handle to the stw481x chip + * @reg: power control register to fetch + * + * The power control registers is a set of one-time-programmable registers + * in its own register space, accessed by writing addess bits to these + * two registers: bits 7,6,5 of PCTL_REG_LO corresponds to the 3 LSBs of + * the address and bits 8,9 of PCTL_REG_HI corresponds to the 2 MSBs of + * the address, forming an address space of 5 bits, i.e. 32 registers + * 0x00 ... 0x1f can be obtained. + */ +static int stw481x_get_pctl_reg(struct stw481x *stw481x, u8 reg) +{ +	u8 msb = (reg >> 3) & 0x03; +	u8 lsb = (reg << 5) & 0xe0; +	unsigned int val; +	u8 vrfy; +	int ret; + +	ret = regmap_write(stw481x->map, STW_PCTL_REG_HI, msb); +	if (ret) +		return ret; +	ret = regmap_write(stw481x->map, STW_PCTL_REG_LO, lsb); +	if (ret) +		return ret; +	ret = regmap_read(stw481x->map, STW_PCTL_REG_HI, &val); +	if (ret) +		return ret; +	vrfy = (val & 0x03) << 3; +	ret = regmap_read(stw481x->map, STW_PCTL_REG_LO, &val); +	if (ret) +		return ret; +	vrfy |= ((val >> 5) & 0x07); +	if (vrfy != reg) +		return -EIO; +	return (val >> 1) & 0x0f; +} + +static int stw481x_startup(struct stw481x *stw481x) +{ +	/* Voltages multiplied by 100 */ +	u8 vcore_val[] = { 100, 105, 110, 115, 120, 122, 124, 126, 128, +			   130, 132, 134, 136, 138, 140, 145 }; +	u8 vpll_val[] = { 105, 120, 130, 180 }; +	u8 vaux_val[] = { 15, 18, 25, 28 }; +	u8 vcore; +	u8 vcore_slp; +	u8 vpll; +	u8 vaux; +	bool vaux_en; +	bool it_warn; +	int ret; +	unsigned int val; + +	ret = regmap_read(stw481x->map, STW_CONF1, &val); +	if (ret) +		return ret; +	vaux_en = !!(val & STW_CONF1_PDN_VAUX); +	it_warn = !!(val & STW_CONF1_IT_WARN); + +	dev_info(&stw481x->client->dev, "voltages %s\n", +		(val & STW_CONF1_V_MONITORING) ? "OK" : "LOW"); +	dev_info(&stw481x->client->dev, "MMC level shifter %s\n", +		(val & STW_CONF1_MMC_LS_STATUS) ? "high impedance" : "ON"); +	dev_info(&stw481x->client->dev, "VMMC: %s\n", +		(val & STW_CONF1_PDN_VMMC) ? "ON" : "disabled"); + +	dev_info(&stw481x->client->dev, "STw481x power control registers:\n"); + +	ret = stw481x_get_pctl_reg(stw481x, STW_PC_VCORE_SEL); +	if (ret < 0) +		return ret; +	vcore = ret & 0x0f; + +	ret = stw481x_get_pctl_reg(stw481x, STW_PC_VAUX_SEL); +	if (ret < 0) +		return ret; +	vaux = (ret >> 2) & 3; +	vpll = (ret >> 4) & 1; /* Save bit 4 */ + +	ret = stw481x_get_pctl_reg(stw481x, STW_PC_VPLL_SEL); +	if (ret < 0) +		return ret; +	vpll |= (ret >> 1) & 2; + +	dev_info(&stw481x->client->dev, "VCORE: %u.%uV %s\n", +		vcore_val[vcore] / 100, vcore_val[vcore] % 100, +		(ret & 4) ? "ON" : "OFF"); + +	dev_info(&stw481x->client->dev, "VPLL:  %u.%uV %s\n", +		vpll_val[vpll] / 100, vpll_val[vpll] % 100, +		(ret & 0x10) ? "ON" : "OFF"); + +	dev_info(&stw481x->client->dev, "VAUX:  %u.%uV %s\n", +		vaux_val[vaux] / 10, vaux_val[vaux] % 10, +		vaux_en ? "ON" : "OFF"); + +	ret = regmap_read(stw481x->map, STW_CONF2, &val); +	if (ret) +		return ret; + +	dev_info(&stw481x->client->dev, "TWARN: %s threshold, %s\n", +		it_warn ? "below" : "above", +		(val & STW_CONF2_MASK_TWARN) ? +		 "enabled" : "mask through VDDOK"); +	dev_info(&stw481x->client->dev, "VMMC: %s\n", +		(val & STW_CONF2_VMMC_EXT) ? "internal" : "external"); +	dev_info(&stw481x->client->dev, "IT WAKE UP: %s\n", +		(val & STW_CONF2_MASK_IT_WAKE_UP) ? "enabled" : "masked"); +	dev_info(&stw481x->client->dev, "GPO1: %s\n", +		(val & STW_CONF2_GPO1) ? "low" : "high impedance"); +	dev_info(&stw481x->client->dev, "GPO2: %s\n", +		(val & STW_CONF2_GPO2) ? "low" : "high impedance"); + +	ret = regmap_read(stw481x->map, STW_VCORE_SLEEP, &val); +	if (ret) +		return ret; +	vcore_slp = val & 0x0f; +	dev_info(&stw481x->client->dev, "VCORE SLEEP: %u.%uV\n", +		vcore_val[vcore_slp] / 100, vcore_val[vcore_slp] % 100); + +	return 0; +} + +/* + * MFD cells - we have one cell which is selected operation + * mode, and we always have a GPIO cell. + */ +static struct mfd_cell stw481x_cells[] = { +	{ +		.of_compatible = "st,stw481x-vmmc", +		.name = "stw481x-vmmc-regulator", +		.id = -1, +	}, +}; + +static const struct regmap_config stw481x_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +}; + +static int stw481x_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	struct stw481x			*stw481x; +	int ret; +	int i; + +	stw481x = devm_kzalloc(&client->dev, sizeof(*stw481x), GFP_KERNEL); +	if (!stw481x) +		return -ENOMEM; + +	i2c_set_clientdata(client, stw481x); +	stw481x->client = client; +	stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config); +	if (IS_ERR(stw481x->map)) { +		ret = PTR_ERR(stw481x->map); +		dev_err(&client->dev, "Failed to allocate register map: %d\n", +			ret); +		return ret; +	} + +	ret = stw481x_startup(stw481x); +	if (ret) { +		dev_err(&client->dev, "chip initialization failed\n"); +		return ret; +	} + +	/* Set up and register the platform devices. */ +	for (i = 0; i < ARRAY_SIZE(stw481x_cells); i++) { +		/* One state holder for all drivers, this is simple */ +		stw481x_cells[i].platform_data = stw481x; +		stw481x_cells[i].pdata_size = sizeof(*stw481x); +	} + +	ret = mfd_add_devices(&client->dev, 0, stw481x_cells, +			ARRAY_SIZE(stw481x_cells), NULL, 0, NULL); +	if (ret) +		return ret; + +	dev_info(&client->dev, "initialized STw481x device\n"); + +	return ret; +} + +static int stw481x_remove(struct i2c_client *client) +{ +	mfd_remove_devices(&client->dev); +	return 0; +} + +/* + * This ID table is completely unused, as this is a pure + * device-tree probed driver, but it has to be here due to + * the structure of the I2C core. + */ +static const struct i2c_device_id stw481x_id[] = { +	{ "stw481x", 0 }, +	{ }, +}; + +static const struct of_device_id stw481x_match[] = { +	{ .compatible = "st,stw4810", }, +	{ .compatible = "st,stw4811", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, stw481x_match); + +static struct i2c_driver stw481x_driver = { +	.driver = { +		.name	= "stw481x", +		.of_match_table = stw481x_match, +	}, +	.probe		= stw481x_probe, +	.remove		= stw481x_remove, +	.id_table	= stw481x_id, +}; + +module_i2c_driver(stw481x_driver); + +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("STw481x PMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c new file mode 100644 index 00000000000..718fc4d2adc --- /dev/null +++ b/drivers/mfd/sun6i-prcm.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 Free Electrons + * + * License Terms: GNU General Public License v2 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> + * + * Allwinner PRCM (Power/Reset/Clock Management) driver + * + */ + +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/of.h> + +struct prcm_data { +	int nsubdevs; +	const struct mfd_cell *subdevs; +}; + +static const struct resource sun6i_a31_ar100_clk_res[] = { +	{ +		.start = 0x0, +		.end = 0x3, +		.flags = IORESOURCE_MEM, +	}, +}; + +static const struct resource sun6i_a31_apb0_clk_res[] = { +	{ +		.start = 0xc, +		.end = 0xf, +		.flags = IORESOURCE_MEM, +	}, +}; + +static const struct resource sun6i_a31_apb0_gates_clk_res[] = { +	{ +		.start = 0x28, +		.end = 0x2b, +		.flags = IORESOURCE_MEM, +	}, +}; + +static const struct resource sun6i_a31_apb0_rstc_res[] = { +	{ +		.start = 0xb0, +		.end = 0xb3, +		.flags = IORESOURCE_MEM, +	}, +}; + +static const struct mfd_cell sun6i_a31_prcm_subdevs[] = { +	{ +		.name = "sun6i-a31-ar100-clk", +		.of_compatible = "allwinner,sun6i-a31-ar100-clk", +		.num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res), +		.resources = sun6i_a31_ar100_clk_res, +	}, +	{ +		.name = "sun6i-a31-apb0-clk", +		.of_compatible = "allwinner,sun6i-a31-apb0-clk", +		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), +		.resources = sun6i_a31_apb0_clk_res, +	}, +	{ +		.name = "sun6i-a31-apb0-gates-clk", +		.of_compatible = "allwinner,sun6i-a31-apb0-gates-clk", +		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), +		.resources = sun6i_a31_apb0_gates_clk_res, +	}, +	{ +		.name = "sun6i-a31-apb0-clock-reset", +		.of_compatible = "allwinner,sun6i-a31-clock-reset", +		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), +		.resources = sun6i_a31_apb0_rstc_res, +	}, +}; + +static const struct prcm_data sun6i_a31_prcm_data = { +	.nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs), +	.subdevs = sun6i_a31_prcm_subdevs, +}; + +static const struct of_device_id sun6i_prcm_dt_ids[] = { +	{ +		.compatible = "allwinner,sun6i-a31-prcm", +		.data = &sun6i_a31_prcm_data, +	}, +	{ /* sentinel */ }, +}; + +static int sun6i_prcm_probe(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	const struct of_device_id *match; +	const struct prcm_data *data; +	struct resource *res; +	int ret; + +	match = of_match_node(sun6i_prcm_dt_ids, np); +	if (!match) +		return -EINVAL; + +	data = match->data; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		dev_err(&pdev->dev, "no prcm memory region provided\n"); +		return -ENOENT; +	} + +	ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs, +			      res, -1, NULL); +	if (ret) { +		dev_err(&pdev->dev, "failed to add subdevices\n"); +		return ret; +	} + +	return 0; +} + +static struct platform_driver sun6i_prcm_driver = { +	.driver = { +		.name = "sun6i-prcm", +		.owner = THIS_MODULE, +		.of_match_table = sun6i_prcm_dt_ids, +	}, +	.probe = sun6i_prcm_probe, +}; +module_platform_driver(sun6i_prcm_driver); + +MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); +MODULE_DESCRIPTION("Allwinner sun6i PRCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 71841f9181b..ca15878ce5c 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -18,6 +18,7 @@  #include <linux/of.h>  #include <linux/of_address.h>  #include <linux/of_platform.h> +#include <linux/platform_data/syscon.h>  #include <linux/platform_device.h>  #include <linux/regmap.h>  #include <linux/mfd/syscon.h> @@ -69,13 +70,6 @@ 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);  } @@ -101,7 +95,11 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,  	struct device_node *syscon_np;  	struct regmap *regmap; -	syscon_np = of_parse_phandle(np, property, 0); +	if (property) +		syscon_np = of_parse_phandle(np, property, 0); +	else +		syscon_np = np; +  	if (!syscon_np)  		return ERR_PTR(-ENODEV); @@ -126,6 +124,7 @@ static struct regmap_config syscon_regmap_config = {  static int syscon_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; +	struct syscon_platform_data *pdata = dev_get_platdata(dev);  	struct syscon *syscon;  	struct resource *res;  	void __iomem *base; @@ -143,6 +142,8 @@ static int syscon_probe(struct platform_device *pdev)  		return -ENOMEM;  	syscon_regmap_config.max_register = res->end - res->start - 3; +	if (pdata) +		syscon_regmap_config.name = pdata->label;  	syscon->regmap = devm_regmap_init_mmio(dev, base,  					&syscon_regmap_config);  	if (IS_ERR(syscon->regmap)) { @@ -152,7 +153,7 @@ static int syscon_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, syscon); -	dev_info(dev, "regmap %pR registered\n", res); +	dev_dbg(dev, "regmap %pR registered\n", res);  	return 0;  } diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 70f4909fee1..bd83accc0f6 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -13,8 +13,23 @@  #include <linux/slab.h>  #include <linux/i2c.h>  #include <linux/of.h> +#include <linux/of_device.h>  #include <linux/mfd/core.h>  #include <linux/mfd/tc3589x.h> +#include <linux/err.h> + +/** + * enum tc3589x_version - indicates the TC3589x version + */ +enum tc3589x_version { +	TC3589X_TC35890, +	TC3589X_TC35892, +	TC3589X_TC35893, +	TC3589X_TC35894, +	TC3589X_TC35895, +	TC3589X_TC35896, +	TC3589X_UNKNOWN, +};  #define TC3589x_CLKMODE_MODCTL_SLEEP		0x0  #define TC3589x_CLKMODE_MODCTL_OPERATION	(1 << 0) @@ -142,21 +157,21 @@ static struct resource keypad_resources[] = {  	},  }; -static struct mfd_cell tc3589x_dev_gpio[] = { +static const struct mfd_cell tc3589x_dev_gpio[] = {  	{  		.name		= "tc3589x-gpio",  		.num_resources	= ARRAY_SIZE(gpio_resources),  		.resources	= &gpio_resources[0], -		.of_compatible	= "tc3589x-gpio", +		.of_compatible	= "toshiba,tc3589x-gpio",  	},  }; -static struct mfd_cell tc3589x_dev_keypad[] = { +static const struct mfd_cell tc3589x_dev_keypad[] = {  	{  		.name           = "tc3589x-keypad",  		.num_resources  = ARRAY_SIZE(keypad_resources),  		.resources      = &keypad_resources[0], -		.of_compatible	= "tc3589x-keypad", +		.of_compatible	= "toshiba,tc3589x-keypad",  	},  }; @@ -305,45 +320,74 @@ static int tc3589x_device_init(struct tc3589x *tc3589x)  	return ret;  } -static int tc3589x_of_probe(struct device_node *np, -			struct tc3589x_platform_data *pdata) +#ifdef CONFIG_OF +static const struct of_device_id tc3589x_match[] = { +	/* Legacy compatible string */ +	{ .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN }, +	{ .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 }, +	{ .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 }, +	{ .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 }, +	{ .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 }, +	{ .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 }, +	{ .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 }, +	{ } +}; + +MODULE_DEVICE_TABLE(of, tc3589x_match); + +static struct tc3589x_platform_data * +tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)  { +	struct device_node *np = dev->of_node; +	struct tc3589x_platform_data *pdata;  	struct device_node *child; +	const struct of_device_id *of_id; + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return ERR_PTR(-ENOMEM); + +	of_id = of_match_device(tc3589x_match, dev); +	if (!of_id) +		return ERR_PTR(-ENODEV); +	*version = (enum tc3589x_version) of_id->data;  	for_each_child_of_node(np, child) { -		if (!strcmp(child->name, "tc3589x_gpio")) { +		if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))  			pdata->block |= TC3589x_BLOCK_GPIO; -		} -		if (!strcmp(child->name, "tc3589x_keypad")) { +		if (of_device_is_compatible(child, "toshiba,tc3589x-keypad"))  			pdata->block |= TC3589x_BLOCK_KEYPAD; -		}  	} -	return 0; +	return pdata; +} +#else +static inline struct tc3589x_platform_data * +tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) +{ +	dev_err(dev, "no device tree support\n"); +	return ERR_PTR(-ENODEV);  } +#endif  static int tc3589x_probe(struct i2c_client *i2c,  				   const struct i2c_device_id *id)  { -	struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);  	struct device_node *np = i2c->dev.of_node; +	struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);  	struct tc3589x *tc3589x; +	enum tc3589x_version version;  	int ret;  	if (!pdata) { -		if (np) { -			pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); -			if (!pdata) -				return -ENOMEM; - -			ret = tc3589x_of_probe(np, pdata); -			if (ret) -				return ret; -		} -		else { +		pdata = tc3589x_of_probe(&i2c->dev, &version); +		if (IS_ERR(pdata)) {  			dev_err(&i2c->dev, "No platform data or DT found\n"); -			return -EINVAL; +			return PTR_ERR(pdata);  		} +	} else { +		/* When not probing from device tree we have this ID */ +		version = id->driver_data;  	}  	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA @@ -361,7 +405,21 @@ static int tc3589x_probe(struct i2c_client *i2c,  	tc3589x->i2c = i2c;  	tc3589x->pdata = pdata;  	tc3589x->irq_base = pdata->irq_base; -	tc3589x->num_gpio = id->driver_data; + +	switch (version) { +	case TC3589X_TC35893: +	case TC3589X_TC35895: +	case TC3589X_TC35896: +		tc3589x->num_gpio = 20; +		break; +	case TC3589X_TC35890: +	case TC3589X_TC35892: +	case TC3589X_TC35894: +	case TC3589X_UNKNOWN: +	default: +		tc3589x->num_gpio = 24; +		break; +	}  	i2c_set_clientdata(i2c, tc3589x); @@ -432,15 +490,24 @@ static int tc3589x_resume(struct device *dev)  static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);  static const struct i2c_device_id tc3589x_id[] = { -	{ "tc3589x", 24 }, +	{ "tc35890", TC3589X_TC35890 }, +	{ "tc35892", TC3589X_TC35892 }, +	{ "tc35893", TC3589X_TC35893 }, +	{ "tc35894", TC3589X_TC35894 }, +	{ "tc35895", TC3589X_TC35895 }, +	{ "tc35896", TC3589X_TC35896 }, +	{ "tc3589x", TC3589X_UNKNOWN },  	{ }  };  MODULE_DEVICE_TABLE(i2c, tc3589x_id);  static struct i2c_driver tc3589x_driver = { -	.driver.name	= "tc3589x", -	.driver.owner	= THIS_MODULE, -	.driver.pm	= &tc3589x_dev_pm_ops, +	.driver = { +		.name	= "tc3589x", +		.owner	= THIS_MODULE, +		.pm	= &tc3589x_dev_pm_ops, +		.of_match_table = of_match_ptr(tc3589x_match), +	},  	.probe		= tc3589x_probe,  	.remove		= tc3589x_remove,  	.id_table	= tc3589x_id, diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index acd0f3a4104..591a331d8d8 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -126,7 +126,7 @@ static struct tmio_mmc_data tc6387xb_mmc_data = {  /*--------------------------------------------------------------------------*/ -static struct mfd_cell tc6387xb_cells[] = { +static const struct mfd_cell tc6387xb_cells[] = {  	[TC6387XB_CELL_MMC] = {  		.name = "tmio-mmc",  		.enable = tc6387xb_mmc_enable, diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c deleted file mode 100644 index 1c2b994e1f6..00000000000 --- a/drivers/mfd/ti-ssp.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs - * - * Copyright (C) 2010 Texas Instruments Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/clk.h> -#include <linux/interrupt.h> -#include <linux/device.h> -#include <linux/spinlock.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/mfd/core.h> -#include <linux/mfd/ti_ssp.h> - -/* Register Offsets */ -#define REG_REV		0x00 -#define REG_IOSEL_1	0x04 -#define REG_IOSEL_2	0x08 -#define REG_PREDIV	0x0c -#define REG_INTR_ST	0x10 -#define REG_INTR_EN	0x14 -#define REG_TEST_CTRL	0x18 - -/* Per port registers */ -#define PORT_CFG_2	0x00 -#define PORT_ADDR	0x04 -#define PORT_DATA	0x08 -#define PORT_CFG_1	0x0c -#define PORT_STATE	0x10 - -#define SSP_PORT_CONFIG_MASK	(SSP_EARLY_DIN | SSP_DELAY_DOUT) -#define SSP_PORT_CLKRATE_MASK	0x0f - -#define SSP_SEQRAM_WR_EN	BIT(4) -#define SSP_SEQRAM_RD_EN	BIT(5) -#define SSP_START		BIT(15) -#define SSP_BUSY		BIT(10) -#define SSP_PORT_ASL		BIT(7) -#define SSP_PORT_CFO1		BIT(6) - -#define SSP_PORT_SEQRAM_SIZE	32 - -static const int ssp_port_base[]   = {0x040, 0x080}; -static const int ssp_port_seqram[] = {0x100, 0x180}; - -struct ti_ssp { -	struct resource		*res; -	struct device		*dev; -	void __iomem		*regs; -	spinlock_t		lock; -	struct clk		*clk; -	int			irq; -	wait_queue_head_t	wqh; - -	/* -	 * Some of the iosel2 register bits always read-back as 0, we need to -	 * remember these values so that we don't clobber previously set -	 * values. -	 */ -	u32			iosel2; -}; - -static inline struct ti_ssp *dev_to_ssp(struct device *dev) -{ -	return dev_get_drvdata(dev->parent); -} - -static inline int dev_to_port(struct device *dev) -{ -	return to_platform_device(dev)->id; -} - -/* Register Access Helpers, rmw() functions need to run locked */ -static inline u32 ssp_read(struct ti_ssp *ssp, int reg) -{ -	return __raw_readl(ssp->regs + reg); -} - -static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) -{ -	__raw_writel(val, ssp->regs + reg); -} - -static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) -{ -	ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits); -} - -static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg) -{ -	return ssp_read(ssp, ssp_port_base[port] + reg); -} - -static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg, -				  u32 val) -{ -	ssp_write(ssp, ssp_port_base[port] + reg, val); -} - -static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg, -				u32 mask, u32 bits) -{ -	ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits); -} - -static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg, -				     u32 bits) -{ -	ssp_port_rmw(ssp, port, reg, bits, 0); -} - -static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg, -				     u32 bits) -{ -	ssp_port_rmw(ssp, port, reg, 0, bits); -} - -/* Called to setup port clock mode, caller must hold ssp->lock */ -static int __set_mode(struct ti_ssp *ssp, int port, int mode) -{ -	mode &= SSP_PORT_CONFIG_MASK; -	ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode); - -	return 0; -} - -int ti_ssp_set_mode(struct device *dev, int mode) -{ -	struct ti_ssp *ssp = dev_to_ssp(dev); -	int port = dev_to_port(dev); -	int ret; - -	spin_lock(&ssp->lock); -	ret = __set_mode(ssp, port, mode); -	spin_unlock(&ssp->lock); - -	return ret; -} -EXPORT_SYMBOL(ti_ssp_set_mode); - -/* Called to setup iosel2, caller must hold ssp->lock */ -static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val) -{ -	ssp->iosel2 = (ssp->iosel2 & ~mask) | val; -	ssp_write(ssp, REG_IOSEL_2, ssp->iosel2); -} - -/* Called to setup port iosel, caller must hold ssp->lock */ -static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) -{ -	unsigned val, shift = port ? 16 : 0; - -	/* IOSEL1 gets the least significant 16 bits */ -	val = ssp_read(ssp, REG_IOSEL_1); -	val &= 0xffff << (port ? 0 : 16); -	val |= (iosel & 0xffff) << (port ? 16 : 0); -	ssp_write(ssp, REG_IOSEL_1, val); - -	/* IOSEL2 gets the most significant 16 bits */ -	val = (iosel >> 16) & 0x7; -	__set_iosel2(ssp, 0x7 << shift, val << shift); -} - -int ti_ssp_set_iosel(struct device *dev, u32 iosel) -{ -	struct ti_ssp *ssp = dev_to_ssp(dev); -	int port = dev_to_port(dev); - -	spin_lock(&ssp->lock); -	__set_iosel(ssp, port, iosel); -	spin_unlock(&ssp->lock); - -	return 0; -} -EXPORT_SYMBOL(ti_ssp_set_iosel); - -int ti_ssp_load(struct device *dev, int offs, u32* prog, int len) -{ -	struct ti_ssp *ssp = dev_to_ssp(dev); -	int port = dev_to_port(dev); -	int i; - -	if (len > SSP_PORT_SEQRAM_SIZE) -		return -ENOSPC; - -	spin_lock(&ssp->lock); - -	/* Enable SeqRAM access */ -	ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); - -	/* Copy code */ -	for (i = 0; i < len; i++) { -		__raw_writel(prog[i], ssp->regs + offs + 4*i + -			     ssp_port_seqram[port]); -	} - -	/* Disable SeqRAM access */ -	ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); - -	spin_unlock(&ssp->lock); - -	return 0; -} -EXPORT_SYMBOL(ti_ssp_load); - -int ti_ssp_raw_read(struct device *dev) -{ -	struct ti_ssp *ssp = dev_to_ssp(dev); -	int port = dev_to_port(dev); -	int shift = port ? 27 : 11; - -	return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf; -} -EXPORT_SYMBOL(ti_ssp_raw_read); - -int ti_ssp_raw_write(struct device *dev, u32 val) -{ -	struct ti_ssp *ssp = dev_to_ssp(dev); -	int port = dev_to_port(dev), shift; - -	spin_lock(&ssp->lock); - -	shift = port ? 22 : 6; -	val &= 0xf; -	__set_iosel2(ssp, 0xf << shift, val << shift); - -	spin_unlock(&ssp->lock); - -	return 0; -} -EXPORT_SYMBOL(ti_ssp_raw_write); - -static inline int __xfer_done(struct ti_ssp *ssp, int port) -{ -	return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY); -} - -int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output) -{ -	struct ti_ssp *ssp = dev_to_ssp(dev); -	int port = dev_to_port(dev); -	int ret; - -	if (pc & ~(0x3f)) -		return -EINVAL; - -	/* Grab ssp->lock to serialize rmw on ssp registers */ -	spin_lock(&ssp->lock); - -	ssp_port_write(ssp, port, PORT_ADDR, input >> 16); -	ssp_port_write(ssp, port, PORT_DATA, input & 0xffff); -	ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc); - -	/* grab wait queue head lock to avoid race with the isr */ -	spin_lock_irq(&ssp->wqh.lock); - -	/* kick off sequence execution in hardware */ -	ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START); - -	/* drop ssp lock; no register writes beyond this */ -	spin_unlock(&ssp->lock); - -	ret = wait_event_interruptible_locked_irq(ssp->wqh, -						  __xfer_done(ssp, port)); -	spin_unlock_irq(&ssp->wqh.lock); - -	if (ret < 0) -		return ret; - -	if (output) { -		*output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) | -			  (ssp_port_read(ssp, port, PORT_DATA) &  0xffff); -	} - -	ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */ - -	return ret; -} -EXPORT_SYMBOL(ti_ssp_run); - -static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data) -{ -	struct ti_ssp *ssp = dev_data; - -	spin_lock(&ssp->wqh.lock); - -	ssp_write(ssp, REG_INTR_ST, 0x3); -	wake_up_locked(&ssp->wqh); - -	spin_unlock(&ssp->wqh.lock); - -	return IRQ_HANDLED; -} - -static int ti_ssp_probe(struct platform_device *pdev) -{ -	static struct ti_ssp *ssp; -	const struct ti_ssp_data *pdata = dev_get_platdata(&pdev->dev); -	int error = 0, prediv = 0xff, id; -	unsigned long sysclk; -	struct device *dev = &pdev->dev; -	struct mfd_cell cells[2]; - -	ssp = kzalloc(sizeof(*ssp), GFP_KERNEL); -	if (!ssp) { -		dev_err(dev, "cannot allocate device info\n"); -		return -ENOMEM; -	} - -	ssp->dev = dev; -	dev_set_drvdata(dev, ssp); - -	ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!ssp->res) { -		error = -ENODEV; -		dev_err(dev, "cannot determine register area\n"); -		goto error_res; -	} - -	if (!request_mem_region(ssp->res->start, resource_size(ssp->res), -				pdev->name)) { -		error = -ENOMEM; -		dev_err(dev, "cannot claim register memory\n"); -		goto error_res; -	} - -	ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res)); -	if (!ssp->regs) { -		error = -ENOMEM; -		dev_err(dev, "cannot map register memory\n"); -		goto error_map; -	} - -	ssp->clk = clk_get(dev, NULL); -	if (IS_ERR(ssp->clk)) { -		error = PTR_ERR(ssp->clk); -		dev_err(dev, "cannot claim device clock\n"); -		goto error_clk; -	} - -	ssp->irq = platform_get_irq(pdev, 0); -	if (ssp->irq < 0) { -		error = -ENODEV; -		dev_err(dev, "unknown irq\n"); -		goto error_irq; -	} - -	error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0, -				     dev_name(dev), ssp); -	if (error < 0) { -		dev_err(dev, "cannot acquire irq\n"); -		goto error_irq; -	} - -	spin_lock_init(&ssp->lock); -	init_waitqueue_head(&ssp->wqh); - -	/* Power on and initialize SSP */ -	error = clk_enable(ssp->clk); -	if (error) { -		dev_err(dev, "cannot enable device clock\n"); -		goto error_enable; -	} - -	/* Reset registers to a sensible known state */ -	ssp_write(ssp, REG_IOSEL_1, 0); -	ssp_write(ssp, REG_IOSEL_2, 0); -	ssp_write(ssp, REG_INTR_EN, 0x3); -	ssp_write(ssp, REG_INTR_ST, 0x3); -	ssp_write(ssp, REG_TEST_CTRL, 0); -	ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL); -	ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL); -	ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1); -	ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1); - -	sysclk = clk_get_rate(ssp->clk); -	if (pdata && pdata->out_clock) -		prediv = (sysclk / pdata->out_clock) - 1; -	prediv = clamp(prediv, 0, 0xff); -	ssp_rmw(ssp, REG_PREDIV, 0xff, prediv); - -	memset(cells, 0, sizeof(cells)); -	for (id = 0; id < 2; id++) { -		const struct ti_ssp_dev_data *data = &pdata->dev_data[id]; - -		cells[id].id		= id; -		cells[id].name		= data->dev_name; -		cells[id].platform_data	= data->pdata; -		cells[id].data_size	= data->pdata_size; -	} - -	error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL); -	if (error < 0) { -		dev_err(dev, "cannot add mfd cells\n"); -		goto error_enable; -	} - -	return 0; - -error_enable: -	free_irq(ssp->irq, ssp); -error_irq: -	clk_put(ssp->clk); -error_clk: -	iounmap(ssp->regs); -error_map: -	release_mem_region(ssp->res->start, resource_size(ssp->res)); -error_res: -	kfree(ssp); -	return error; -} - -static int ti_ssp_remove(struct platform_device *pdev) -{ -	struct device *dev = &pdev->dev; -	struct ti_ssp *ssp = dev_get_drvdata(dev); - -	mfd_remove_devices(dev); -	clk_disable(ssp->clk); -	free_irq(ssp->irq, ssp); -	clk_put(ssp->clk); -	iounmap(ssp->regs); -	release_mem_region(ssp->res->start, resource_size(ssp->res)); -	kfree(ssp); -	dev_set_drvdata(dev, NULL); -	return 0; -} - -static struct platform_driver ti_ssp_driver = { -	.probe		= ti_ssp_probe, -	.remove		= ti_ssp_remove, -	.driver		= { -		.name	= "ti-ssp", -		.owner	= THIS_MODULE, -	} -}; - -module_platform_driver(ti_ssp_driver); - -MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver"); -MODULE_AUTHOR("Cyril Chemparathy"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ti-ssp"); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index baaf5a8123b..dd4bf581622 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -14,7 +14,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/err.h>  #include <linux/io.h> @@ -24,6 +23,7 @@  #include <linux/pm_runtime.h>  #include <linux/of.h>  #include <linux/of_device.h> +#include <linux/sched.h>  #include <linux/mfd/ti_am335x_tscadc.h> @@ -48,29 +48,80 @@ static const struct regmap_config tscadc_regmap_config = {  	.val_bits = 32,  }; -void am335x_tsc_se_update(struct ti_tscadc_dev *tsadc) +void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val)  { -	tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); +	unsigned long flags; + +	spin_lock_irqsave(&tsadc->reg_lock, flags); +	tsadc->reg_se_cache = val; +	if (tsadc->adc_waiting) +		wake_up(&tsadc->reg_se_wait); +	else if (!tsadc->adc_in_use) +		tscadc_writel(tsadc, REG_SE, val); + +	spin_unlock_irqrestore(&tsadc->reg_lock, flags);  } -EXPORT_SYMBOL_GPL(am335x_tsc_se_update); +EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache); -void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val) +static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)  { -	spin_lock(&tsadc->reg_lock); -	tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE); -	tsadc->reg_se_cache |= val; -	am335x_tsc_se_update(tsadc); -	spin_unlock(&tsadc->reg_lock); +	DEFINE_WAIT(wait); +	u32 reg; + +	/* +	 * disable TSC steps so it does not run while the ADC is using it. If +	 * write 0 while it is running (it just started or was already running) +	 * then it completes all steps that were enabled and stops then. +	 */ +	tscadc_writel(tsadc, REG_SE, 0); +	reg = tscadc_readl(tsadc, REG_ADCFSM); +	if (reg & SEQ_STATUS) { +		tsadc->adc_waiting = true; +		prepare_to_wait(&tsadc->reg_se_wait, &wait, +				TASK_UNINTERRUPTIBLE); +		spin_unlock_irq(&tsadc->reg_lock); + +		schedule(); + +		spin_lock_irq(&tsadc->reg_lock); +		finish_wait(&tsadc->reg_se_wait, &wait); + +		reg = tscadc_readl(tsadc, REG_ADCFSM); +		WARN_ON(reg & SEQ_STATUS); +		tsadc->adc_waiting = false; +	} +	tsadc->adc_in_use = true;  } -EXPORT_SYMBOL_GPL(am335x_tsc_se_set); + +void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val) +{ +	spin_lock_irq(&tsadc->reg_lock); +	am335x_tscadc_need_adc(tsadc); + +	tscadc_writel(tsadc, REG_SE, val); +	spin_unlock_irq(&tsadc->reg_lock); +} +EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once); + +void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc) +{ +	unsigned long flags; + +	spin_lock_irqsave(&tsadc->reg_lock, flags); +	tsadc->adc_in_use = false; +	tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); +	spin_unlock_irqrestore(&tsadc->reg_lock, flags); +} +EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done);  void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)  { -	spin_lock(&tsadc->reg_lock); -	tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE); +	unsigned long flags; + +	spin_lock_irqsave(&tsadc->reg_lock, flags);  	tsadc->reg_se_cache &= ~val; -	am335x_tsc_se_update(tsadc); -	spin_unlock(&tsadc->reg_lock); +	tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); +	spin_unlock_irqrestore(&tsadc->reg_lock, flags);  }  EXPORT_SYMBOL_GPL(am335x_tsc_se_clr); @@ -95,7 +146,7 @@ static	int ti_tscadc_probe(struct platform_device *pdev)  	const __be32            *cur;  	u32			val;  	int			err, ctrl; -	int			clk_value, clock_rate; +	int			clock_rate;  	int			tsc_wires = 0, adc_channels = 0, total_channels;  	int			readouts = 0; @@ -132,12 +183,6 @@ static	int ti_tscadc_probe(struct platform_device *pdev)  		return -EINVAL;  	} -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) { -		dev_err(&pdev->dev, "no memory resource defined.\n"); -		return -EINVAL; -	} -  	/* Allocate memory for device */  	tscadc = devm_kzalloc(&pdev->dev,  			sizeof(struct ti_tscadc_dev), GFP_KERNEL); @@ -154,19 +199,10 @@ static	int ti_tscadc_probe(struct platform_device *pdev)  	} else  		tscadc->irq = err; -	res = devm_request_mem_region(&pdev->dev, -			res->start, resource_size(res), pdev->name); -	if (!res) { -		dev_err(&pdev->dev, "failed to reserve registers.\n"); -		return -EBUSY; -	} - -	tscadc->tscadc_base = devm_ioremap(&pdev->dev, -			res->start, resource_size(res)); -	if (!tscadc->tscadc_base) { -		dev_err(&pdev->dev, "failed to map registers.\n"); -		return -ENOMEM; -	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(tscadc->tscadc_base)) +		return PTR_ERR(tscadc->tscadc_base);  	tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,  			tscadc->tscadc_base, &tscadc_regmap_config); @@ -177,6 +213,8 @@ static	int ti_tscadc_probe(struct platform_device *pdev)  	}  	spin_lock_init(&tscadc->reg_lock); +	init_waitqueue_head(&tscadc->reg_se_wait); +  	pm_runtime_enable(&pdev->dev);  	pm_runtime_get_sync(&pdev->dev); @@ -196,11 +234,11 @@ static	int ti_tscadc_probe(struct platform_device *pdev)  	}  	clock_rate = clk_get_rate(clk);  	clk_put(clk); -	clk_value = clock_rate / ADC_CLK; +	tscadc->clk_div = clock_rate / ADC_CLK;  	/* TSCADC_CLKDIV needs to be configured to the value minus 1 */ -	clk_value = clk_value - 1; -	tscadc_writel(tscadc, REG_CLKDIV, clk_value); +	tscadc->clk_div--; +	tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div);  	/* Set the control register bits */  	ctrl = CNTRLREG_STEPCONFIGWRT | @@ -298,11 +336,12 @@ static int tscadc_resume(struct device *dev)  	if (tscadc_dev->tsc_cell != -1)  		tscadc_idle_config(tscadc_dev); -	am335x_tsc_se_update(tscadc_dev);  	restore = tscadc_readl(tscadc_dev, REG_CTRL);  	tscadc_writel(tscadc_dev, REG_CTRL,  			(restore | CNTRLREG_TSCSSENB)); +	tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div); +  	return 0;  } @@ -326,7 +365,7 @@ static struct platform_driver ti_tscadc_driver = {  		.name   = "ti_am3359-tscadc",  		.owner	= THIS_MODULE,  		.pm	= TSCADC_PM_OPS, -		.of_match_table = of_match_ptr(ti_tscadc_dt_ids), +		.of_match_table = ti_tscadc_dt_ids,  	},  	.probe	= ti_tscadc_probe,  	.remove	= ti_tscadc_remove, diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index a6755ec7bd6..6ce36d6970a 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -374,7 +374,7 @@ static const struct resource timberdale_dma_resources[] = {  	},  }; -static struct mfd_cell timberdale_cells_bar0_cfg0[] = { +static const struct mfd_cell timberdale_cells_bar0_cfg0[] = {  	{  		.name = "timb-dma",  		.num_resources = ARRAY_SIZE(timberdale_dma_resources), @@ -431,7 +431,7 @@ static struct mfd_cell timberdale_cells_bar0_cfg0[] = {  	},  }; -static struct mfd_cell timberdale_cells_bar0_cfg1[] = { +static const struct mfd_cell timberdale_cells_bar0_cfg1[] = {  	{  		.name = "timb-dma",  		.num_resources = ARRAY_SIZE(timberdale_dma_resources), @@ -498,7 +498,7 @@ static struct mfd_cell timberdale_cells_bar0_cfg1[] = {  	},  }; -static struct mfd_cell timberdale_cells_bar0_cfg2[] = { +static const struct mfd_cell timberdale_cells_bar0_cfg2[] = {  	{  		.name = "timb-dma",  		.num_resources = ARRAY_SIZE(timberdale_dma_resources), @@ -548,7 +548,7 @@ static struct mfd_cell timberdale_cells_bar0_cfg2[] = {  	},  }; -static struct mfd_cell timberdale_cells_bar0_cfg3[] = { +static const struct mfd_cell timberdale_cells_bar0_cfg3[] = {  	{  		.name = "timb-dma",  		.num_resources = ARRAY_SIZE(timberdale_dma_resources), @@ -619,7 +619,7 @@ static const struct resource timberdale_sdhc_resources[] = {  	},  }; -static struct mfd_cell timberdale_cells_bar1[] = { +static const struct mfd_cell timberdale_cells_bar1[] = {  	{  		.name = "sdhci",  		.num_resources = ARRAY_SIZE(timberdale_sdhc_resources), @@ -627,7 +627,7 @@ static struct mfd_cell timberdale_cells_bar1[] = {  	},  }; -static struct mfd_cell timberdale_cells_bar2[] = { +static const struct mfd_cell timberdale_cells_bar2[] = {  	{  		.name = "sdhci",  		.num_resources = ARRAY_SIZE(timberdale_sdhc_resources), @@ -678,7 +678,7 @@ static int timb_probe(struct pci_dev *dev,  	priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;  	if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {  		dev_err(&dev->dev, "Failed to request ctl mem\n"); -		goto err_request; +		goto err_start;  	}  	priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE); @@ -715,7 +715,7 @@ static int timb_probe(struct pci_dev *dev,  	for (i = 0; i < TIMBERDALE_NR_IRQS; i++)  		msix_entries[i].entry = i; -	err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS); +	err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);  	if (err) {  		dev_err(&dev->dev,  			"MSI-X init failed: %d, expected entries: %d\n", @@ -828,13 +828,10 @@ err_config:  	iounmap(priv->ctl_membase);  err_ioremap:  	release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); -err_request: -	pci_set_drvdata(dev, NULL);  err_start:  	pci_disable_device(dev);  err_enable:  	kfree(priv); -	pci_set_drvdata(dev, NULL);  	return -ENODEV;  } @@ -851,11 +848,10 @@ static void timb_remove(struct pci_dev *dev)  	pci_disable_msix(dev);  	pci_disable_device(dev); -	pci_set_drvdata(dev, NULL);  	kfree(priv);  } -static DEFINE_PCI_DEVICE_TABLE(timberdale_pci_tbl) = { +static const struct pci_device_id timberdale_pci_tbl[] = {  	{ PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },  	{ 0 }  }; diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index 5ad4b772b09..a2e1990c9de 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -19,11 +19,12 @@  #include <linux/init.h>  #include <linux/slab.h>  #include <linux/i2c.h> +#include <linux/of.h>  #include <linux/of_device.h>  #include <linux/mfd/core.h>  #include <linux/mfd/tps6507x.h> -static struct mfd_cell tps6507x_devs[] = { +static const struct mfd_cell tps6507x_devs[] = {  	{  		.name = "tps6507x-pmic",  	}, @@ -118,7 +119,7 @@ static const struct i2c_device_id tps6507x_i2c_id[] = {  MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);  #ifdef CONFIG_OF -static struct of_device_id tps6507x_of_match[] = { +static const struct of_device_id tps6507x_of_match[] = {  	{.compatible = "ti,tps6507x", },  	{},  }; diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index e6f03a73387..1c3e6e2efe4 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -32,14 +32,6 @@  #define NUM_INT_REG 2  #define TOTAL_NUM_REG 0x18 -/* interrupt status registers */ -#define TPS65090_INT_STS	0x0 -#define TPS65090_INT_STS2	0x1 - -/* interrupt mask registers */ -#define TPS65090_INT_MSK	0x2 -#define TPS65090_INT_MSK2	0x3 -  #define TPS65090_INT1_MASK_VAC_STATUS_CHANGE		1  #define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE		2  #define TPS65090_INT1_MASK_BAT_STATUS_CHANGE		3 @@ -64,11 +56,16 @@ static struct resource charger_resources[] = {  	}  }; +enum tps65090_cells { +	PMIC = 0, +	CHARGER = 1, +}; +  static struct mfd_cell tps65090s[] = { -	{ +	[PMIC] = {  		.name = "tps65090-pmic",  	}, -	{ +	[CHARGER] = {  		.name = "tps65090-charger",  		.num_resources = ARRAY_SIZE(charger_resources),  		.resources = &charger_resources[0], @@ -139,17 +136,26 @@ static struct regmap_irq_chip tps65090_irq_chip = {  	.irqs = tps65090_irqs,  	.num_irqs = ARRAY_SIZE(tps65090_irqs),  	.num_regs = NUM_INT_REG, -	.status_base = TPS65090_INT_STS, -	.mask_base = TPS65090_INT_MSK, +	.status_base = TPS65090_REG_INTR_STS, +	.mask_base = TPS65090_REG_INTR_MASK,  	.mask_invert = true,  };  static bool is_volatile_reg(struct device *dev, unsigned int reg)  { -	if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2)) -		return true; -	else +	/* Nearly all registers have status bits mixed in, except a few */ +	switch (reg) { +	case TPS65090_REG_INTR_MASK: +	case TPS65090_REG_INTR_MASK2: +	case TPS65090_REG_CG_CTRL0: +	case TPS65090_REG_CG_CTRL1: +	case TPS65090_REG_CG_CTRL2: +	case TPS65090_REG_CG_CTRL3: +	case TPS65090_REG_CG_CTRL4: +	case TPS65090_REG_CG_CTRL5:  		return false; +	} +	return true;  }  static const struct regmap_config tps65090_regmap_config = { @@ -211,6 +217,9 @@ static int tps65090_i2c_probe(struct i2c_client *client,  					"IRQ init failed with err: %d\n", ret);  			return ret;  		} +	} else { +		/* Don't tell children they have an IRQ that'll never fire */ +		tps65090s[CHARGER].num_resources = 0;  	}  	ret = mfd_add_devices(tps65090->dev, -1, tps65090s, diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index b8f48647661..3cc4c7084b9 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -30,7 +30,7 @@  #include <linux/mfd/core.h>  #include <linux/mfd/tps65217.h> -static struct mfd_cell tps65217s[] = { +static const struct mfd_cell tps65217s[] = {  	{  		.name = "tps65217-pmic",  	}, @@ -158,7 +158,7 @@ static int tps65217_probe(struct i2c_client *client,  {  	struct tps65217 *tps;  	unsigned int version; -	unsigned int chip_id = ids->driver_data; +	unsigned long chip_id = ids->driver_data;  	const struct of_device_id *match;  	bool status_off = false;  	int ret; @@ -170,7 +170,7 @@ static int tps65217_probe(struct i2c_client *client,  				"Failed to find matching dt id\n");  			return -EINVAL;  		} -		chip_id = (unsigned int)match->data; +		chip_id = (unsigned long)match->data;  		status_off = of_property_read_bool(client->dev.of_node,  					"ti,pmic-shutdown-controller");  	} @@ -245,7 +245,7 @@ static struct i2c_driver tps65217_driver = {  	.driver		= {  		.name	= "tps65217",  		.owner	= THIS_MODULE, -		.of_match_table = of_match_ptr(tps65217_of_match), +		.of_match_table = tps65217_of_match,  	},  	.id_table	= tps65217_id_table,  	.probe		= tps65217_probe, diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c new file mode 100644 index 00000000000..0d256cb002e --- /dev/null +++ b/drivers/mfd/tps65218.c @@ -0,0 +1,283 @@ +/* + * Driver for TPS65218 Integrated power management chipsets + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.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 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License version 2 for more details. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/tps65218.h> + +#define TPS65218_PASSWORD_REGS_UNLOCK   0x7D + +/** + * tps65218_reg_read: Read a single tps65218 register. + * + * @tps: Device to read from. + * @reg: Register to read. + * @val: Contians the value + */ +int tps65218_reg_read(struct tps65218 *tps, unsigned int reg, +			unsigned int *val) +{ +	return regmap_read(tps->regmap, reg, val); +} +EXPORT_SYMBOL_GPL(tps65218_reg_read); + +/** + * tps65218_reg_write: Write a single tps65218 register. + * + * @tps65218: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + * @level: Password protected level + */ +int tps65218_reg_write(struct tps65218 *tps, unsigned int reg, +			unsigned int val, unsigned int level) +{ +	int ret; +	unsigned int xor_reg_val; + +	switch (level) { +	case TPS65218_PROTECT_NONE: +		return regmap_write(tps->regmap, reg, val); +	case TPS65218_PROTECT_L1: +		xor_reg_val = reg ^ TPS65218_PASSWORD_REGS_UNLOCK; +		ret = regmap_write(tps->regmap, TPS65218_REG_PASSWORD, +							xor_reg_val); +		if (ret < 0) +			return ret; + +		return regmap_write(tps->regmap, reg, val); +	default: +		return -EINVAL; +	} +} +EXPORT_SYMBOL_GPL(tps65218_reg_write); + +/** + * tps65218_update_bits: Modify bits w.r.t mask, val and level. + * + * @tps65218: Device to write to. + * @reg: Register to read-write to. + * @mask: Mask. + * @val: Value to write. + * @level: Password protected level + */ +static int tps65218_update_bits(struct tps65218 *tps, unsigned int reg, +		unsigned int mask, unsigned int val, unsigned int level) +{ +	int ret; +	unsigned int data; + +	ret = tps65218_reg_read(tps, reg, &data); +	if (ret) { +		dev_err(tps->dev, "Read from reg 0x%x failed\n", reg); +		return ret; +	} + +	data &= ~mask; +	data |= val & mask; + +	mutex_lock(&tps->tps_lock); +	ret = tps65218_reg_write(tps, reg, data, level); +	if (ret) +		dev_err(tps->dev, "Write for reg 0x%x failed\n", reg); +	mutex_unlock(&tps->tps_lock); + +	return ret; +} + +int tps65218_set_bits(struct tps65218 *tps, unsigned int reg, +		unsigned int mask, unsigned int val, unsigned int level) +{ +	return tps65218_update_bits(tps, reg, mask, val, level); +} +EXPORT_SYMBOL_GPL(tps65218_set_bits); + +int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg, +		unsigned int mask, unsigned int level) +{ +	return tps65218_update_bits(tps, reg, mask, 0, level); +} +EXPORT_SYMBOL_GPL(tps65218_clear_bits); + +static struct regmap_config tps65218_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_irq tps65218_irqs[] = { +	/* INT1 IRQs */ +	[TPS65218_PRGC_IRQ] = { +		.mask = TPS65218_INT1_PRGC, +	}, +	[TPS65218_CC_AQC_IRQ] = { +		.mask = TPS65218_INT1_CC_AQC, +	}, +	[TPS65218_HOT_IRQ] = { +		.mask = TPS65218_INT1_HOT, +	}, +	[TPS65218_PB_IRQ] = { +		.mask = TPS65218_INT1_PB, +	}, +	[TPS65218_AC_IRQ] = { +		.mask = TPS65218_INT1_AC, +	}, +	[TPS65218_VPRG_IRQ] = { +		.mask = TPS65218_INT1_VPRG, +	}, +	[TPS65218_INVALID1_IRQ] = { +	}, +	[TPS65218_INVALID2_IRQ] = { +	}, +	/* INT2 IRQs*/ +	[TPS65218_LS1_I_IRQ] = { +		.mask = TPS65218_INT2_LS1_I, +		.reg_offset = 1, +	}, +	[TPS65218_LS2_I_IRQ] = { +		.mask = TPS65218_INT2_LS2_I, +		.reg_offset = 1, +	}, +	[TPS65218_LS3_I_IRQ] = { +		.mask = TPS65218_INT2_LS3_I, +		.reg_offset = 1, +	}, +	[TPS65218_LS1_F_IRQ] = { +		.mask = TPS65218_INT2_LS1_F, +		.reg_offset = 1, +	}, +	[TPS65218_LS2_F_IRQ] = { +		.mask = TPS65218_INT2_LS2_F, +		.reg_offset = 1, +	}, +	[TPS65218_LS3_F_IRQ] = { +		.mask = TPS65218_INT2_LS3_F, +		.reg_offset = 1, +	}, +	[TPS65218_INVALID3_IRQ] = { +	}, +	[TPS65218_INVALID4_IRQ] = { +	}, +}; + +static struct regmap_irq_chip tps65218_irq_chip = { +	.name = "tps65218", +	.irqs = tps65218_irqs, +	.num_irqs = ARRAY_SIZE(tps65218_irqs), + +	.num_regs = 2, +	.mask_base = TPS65218_REG_INT_MASK1, +}; + +static const struct of_device_id of_tps65218_match_table[] = { +	{ .compatible = "ti,tps65218", }, +	{} +}; + +static int tps65218_probe(struct i2c_client *client, +				const struct i2c_device_id *ids) +{ +	struct tps65218 *tps; +	const struct of_device_id *match; +	int ret; + +	match = of_match_device(of_tps65218_match_table, &client->dev); +	if (!match) { +		dev_err(&client->dev, +			"Failed to find matching dt id\n"); +		return -EINVAL; +	} + +	tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); +	if (!tps) +		return -ENOMEM; + +	i2c_set_clientdata(client, tps); +	tps->dev = &client->dev; +	tps->irq = client->irq; +	tps->regmap = devm_regmap_init_i2c(client, &tps65218_regmap_config); +	if (IS_ERR(tps->regmap)) { +		ret = PTR_ERR(tps->regmap); +		dev_err(tps->dev, "Failed to allocate register map: %d\n", +			ret); +		return ret; +	} + +	mutex_init(&tps->tps_lock); + +	ret = regmap_add_irq_chip(tps->regmap, tps->irq, +			IRQF_ONESHOT, 0, &tps65218_irq_chip, +			&tps->irq_data); +	if (ret < 0) +		return ret; + +	ret = of_platform_populate(client->dev.of_node, NULL, NULL, +				   &client->dev); +	if (ret < 0) +		goto err_irq; + +	return 0; + +err_irq: +	regmap_del_irq_chip(tps->irq, tps->irq_data); + +	return ret; +} + +static int tps65218_remove(struct i2c_client *client) +{ +	struct tps65218 *tps = i2c_get_clientdata(client); + +	regmap_del_irq_chip(tps->irq, tps->irq_data); + +	return 0; +} + +static const struct i2c_device_id tps65218_id_table[] = { +	{ "tps65218", TPS65218 }, +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, tps65218_id_table); + +static struct i2c_driver tps65218_driver = { +	.driver		= { +		.name	= "tps65218", +		.owner	= THIS_MODULE, +		.of_match_table = of_tps65218_match_table, +	}, +	.probe		= tps65218_probe, +	.remove		= tps65218_remove, +	.id_table       = tps65218_id_table, +}; + +module_i2c_driver(tps65218_driver); + +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>"); +MODULE_DESCRIPTION("TPS65218 chip family multi-function driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index f54fe4d4f77..8e1dbc46958 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -26,6 +26,7 @@  #include <linux/i2c.h>  #include <linux/platform_device.h>  #include <linux/regmap.h> +#include <linux/of.h>  #include <linux/mfd/core.h>  #include <linux/mfd/tps6586x.h> @@ -102,7 +103,7 @@ static struct resource tps6586x_rtc_resources[] = {  	},  }; -static struct mfd_cell tps6586x_cell[] = { +static const struct mfd_cell tps6586x_cell[] = {  	{  		.name = "tps6586x-gpio",  	}, @@ -123,7 +124,9 @@ struct tps6586x {  	struct device		*dev;  	struct i2c_client	*client;  	struct regmap		*regmap; +	int			version; +	int			irq;  	struct irq_chip		irq_chip;  	struct mutex		irq_lock;  	int			irq_base; @@ -206,6 +209,14 @@ int tps6586x_irq_get_virq(struct device *dev, int irq)  }  EXPORT_SYMBOL_GPL(tps6586x_irq_get_virq); +int tps6586x_get_version(struct device *dev) +{ +	struct tps6586x *tps6586x = dev_get_drvdata(dev); + +	return tps6586x->version; +} +EXPORT_SYMBOL_GPL(tps6586x_get_version); +  static int __remove_subdev(struct device *dev, void *unused)  {  	platform_device_unregister(to_platform_device(dev)); @@ -261,12 +272,23 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data)  	mutex_unlock(&tps6586x->irq_lock);  } +#ifdef CONFIG_PM_SLEEP +static int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on) +{ +	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); +	return irq_set_irq_wake(tps6586x->irq, on); +} +#else +#define tps6586x_irq_set_wake NULL +#endif +  static struct irq_chip tps6586x_irq_chip = {  	.name = "tps6586x",  	.irq_bus_lock = tps6586x_irq_lock,  	.irq_bus_sync_unlock = tps6586x_irq_sync_unlock,  	.irq_disable = tps6586x_irq_disable,  	.irq_enable = tps6586x_irq_enable, +	.irq_set_wake = tps6586x_irq_set_wake,  };  static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq, @@ -331,6 +353,8 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,  	int new_irq_base;  	int irq_num = ARRAY_SIZE(tps6586x_irqs); +	tps6586x->irq = irq; +  	mutex_init(&tps6586x->irq_lock);  	for (i = 0; i < 5; i++) {  		tps6586x->mask_reg[i] = 0xff; @@ -360,10 +384,8 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,  	ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,  				   "tps6586x", tps6586x); -	if (!ret) { +	if (!ret)  		device_init_wakeup(tps6586x->dev, 1); -		enable_irq_wake(irq); -	}  	return ret;  } @@ -422,7 +444,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien  	return pdata;  } -static struct of_device_id tps6586x_of_match[] = { +static const struct of_device_id tps6586x_of_match[] = {  	{ .compatible = "ti,tps6586x", },  	{ },  }; @@ -459,12 +481,42 @@ static void tps6586x_power_off(void)  	tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);  } +static void tps6586x_print_version(struct i2c_client *client, int version) +{ +	const char *name; + +	switch (version) { +	case TPS658621A: +		name = "TPS658621A"; +		break; +	case TPS658621CD: +		name = "TPS658621C/D"; +		break; +	case TPS658623: +		name = "TPS658623"; +		break; +	case TPS658640: +	case TPS658640v2: +		name = "TPS658640"; +		break; +	case TPS658643: +		name = "TPS658643"; +		break; +	default: +		name = "TPS6586X"; +		break; +	} + +	dev_info(&client->dev, "Found %s, VERSIONCRC is %02x\n", name, version); +} +  static int tps6586x_i2c_probe(struct i2c_client *client,  					const struct i2c_device_id *id)  {  	struct tps6586x_platform_data *pdata = dev_get_platdata(&client->dev);  	struct tps6586x *tps6586x;  	int ret; +	int version;  	if (!pdata && client->dev.of_node)  		pdata = tps6586x_parse_dt(client); @@ -474,19 +526,18 @@ static int tps6586x_i2c_probe(struct i2c_client *client,  		return -ENOTSUPP;  	} -	ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC); -	if (ret < 0) { -		dev_err(&client->dev, "Chip ID read failed: %d\n", ret); +	version = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC); +	if (version < 0) { +		dev_err(&client->dev, "Chip ID read failed: %d\n", version);  		return -EIO;  	} -	dev_info(&client->dev, "VERSIONCRC is %02x\n", ret); -  	tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL); -	if (tps6586x == NULL) { -		dev_err(&client->dev, "memory for tps6586x alloc failed\n"); +	if (!tps6586x)  		return -ENOMEM; -	} + +	tps6586x->version = version; +	tps6586x_print_version(client, tps6586x->version);  	tps6586x->client = client;  	tps6586x->dev = &client->dev; diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index d7927720483..f9e42ea1cb1 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -25,6 +25,7 @@  #include <linux/mfd/core.h>  #include <linux/regmap.h>  #include <linux/mfd/tps65910.h> +#include <linux/of.h>  #include <linux/of_device.h>  static struct resource rtc_resources[] = { @@ -35,7 +36,7 @@ static struct resource rtc_resources[] = {  	}  }; -static struct mfd_cell tps65910s[] = { +static const struct mfd_cell tps65910s[] = {  	{  		.name = "tps65910-gpio",  	}, @@ -254,8 +255,10 @@ static int tps65910_irq_init(struct tps65910 *tps65910, int irq,  	ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq,  		IRQF_ONESHOT, pdata->irq_base,  		tps6591x_irqs_chip, &tps65910->irq_data); -	if (ret < 0) +	if (ret < 0) {  		dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret); +		tps65910->chip_irq = 0; +	}  	return ret;  } @@ -376,7 +379,7 @@ err_sleep_init:  }  #ifdef CONFIG_OF -static struct of_device_id tps65910_of_match[] = { +static const struct of_device_id tps65910_of_match[] = {  	{ .compatible = "ti,tps65910", .data = (void *)TPS65910},  	{ .compatible = "ti,tps65911", .data = (void *)TPS65911},  	{ }, @@ -410,14 +413,10 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,  	ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);  	if (!ret)  		board_info->vmbch_threshold = prop; -	else if (*chip_id == TPS65911) -		dev_warn(&client->dev, "VMBCH-Threshold not specified");  	ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);  	if (!ret)  		board_info->vmbch2_threshold = prop; -	else if (*chip_id == TPS65911) -		dev_warn(&client->dev, "VMBCH2-Threshold not specified");  	prop = of_property_read_bool(np, "ti,en-ck32k-xtal");  	board_info->en_ck32k_xtal = prop; @@ -512,6 +511,7 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,  			      regmap_irq_get_domain(tps65910->irq_data));  	if (ret < 0) {  		dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); +		tps65910_irq_exit(tps65910);  		return ret;  	} diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 925a044cbdf..1f82d60b1d0 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -15,13 +15,12 @@  #include <linux/module.h>  #include <linux/moduleparam.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/gpio.h>  #include <linux/mfd/core.h>  #include <linux/mfd/tps65912.h> -static struct mfd_cell tps65912s[] = { +static const struct mfd_cell tps65912s[] = {  	{  		.name = "tps65912-pmic",  	}, diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c index d360a83a273..fbecec7f1e3 100644 --- a/drivers/mfd/tps65912-irq.c +++ b/drivers/mfd/tps65912-irq.c @@ -15,7 +15,6 @@  #include <linux/kernel.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/bug.h>  #include <linux/device.h>  #include <linux/interrupt.h> diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index f15ee6d5cfb..ed6c5b0956e 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -44,7 +44,7 @@ static struct resource tps80031_rtc_resources[] = {  };  /* TPS80031 sub mfd devices */ -static struct mfd_cell tps80031_cell[] = { +static const struct mfd_cell tps80031_cell[] = {  	{  		.name = "tps80031-pmic",  	}, diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 29473c2c95a..db11b4f4061 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -47,6 +47,9 @@  #include <linux/i2c.h>  #include <linux/i2c/twl.h> +/* Register descriptions for audio */ +#include <linux/mfd/twl4030-audio.h> +  #include "twl-core.h"  /* @@ -95,7 +98,11 @@  #define TWL4030_BASEADD_BACKUP		0x0014  #define TWL4030_BASEADD_INT		0x002E  #define TWL4030_BASEADD_PM_MASTER	0x0036 +  #define TWL4030_BASEADD_PM_RECEIVER	0x005B +#define TWL4030_DCDC_GLOBAL_CFG		0x06 +#define SMARTREFLEX_ENABLE		BIT(3) +  #define TWL4030_BASEADD_RTC		0x001C  #define TWL4030_BASEADD_SECURED_REG	0x0000 @@ -200,6 +207,105 @@ static struct twl_mapping twl4030_map[] = {  	{ 2, TWL5031_BASEADD_INTERRUPTS },  }; +static struct reg_default twl4030_49_defaults[] = { +	/* Audio Registers */ +	{ 0x01, 0x00}, /* CODEC_MODE	*/ +	{ 0x02, 0x00}, /* OPTION	*/ +	/* 0x03  Unused	*/ +	{ 0x04, 0x00}, /* MICBIAS_CTL	*/ +	{ 0x05, 0x00}, /* ANAMICL	*/ +	{ 0x06, 0x00}, /* ANAMICR	*/ +	{ 0x07, 0x00}, /* AVADC_CTL	*/ +	{ 0x08, 0x00}, /* ADCMICSEL	*/ +	{ 0x09, 0x00}, /* DIGMIXING	*/ +	{ 0x0a, 0x0f}, /* ATXL1PGA	*/ +	{ 0x0b, 0x0f}, /* ATXR1PGA	*/ +	{ 0x0c, 0x0f}, /* AVTXL2PGA	*/ +	{ 0x0d, 0x0f}, /* AVTXR2PGA	*/ +	{ 0x0e, 0x00}, /* AUDIO_IF	*/ +	{ 0x0f, 0x00}, /* VOICE_IF	*/ +	{ 0x10, 0x3f}, /* ARXR1PGA	*/ +	{ 0x11, 0x3f}, /* ARXL1PGA	*/ +	{ 0x12, 0x3f}, /* ARXR2PGA	*/ +	{ 0x13, 0x3f}, /* ARXL2PGA	*/ +	{ 0x14, 0x25}, /* VRXPGA	*/ +	{ 0x15, 0x00}, /* VSTPGA	*/ +	{ 0x16, 0x00}, /* VRX2ARXPGA	*/ +	{ 0x17, 0x00}, /* AVDAC_CTL	*/ +	{ 0x18, 0x00}, /* ARX2VTXPGA	*/ +	{ 0x19, 0x32}, /* ARXL1_APGA_CTL*/ +	{ 0x1a, 0x32}, /* ARXR1_APGA_CTL*/ +	{ 0x1b, 0x32}, /* ARXL2_APGA_CTL*/ +	{ 0x1c, 0x32}, /* ARXR2_APGA_CTL*/ +	{ 0x1d, 0x00}, /* ATX2ARXPGA	*/ +	{ 0x1e, 0x00}, /* BT_IF		*/ +	{ 0x1f, 0x55}, /* BTPGA		*/ +	{ 0x20, 0x00}, /* BTSTPGA	*/ +	{ 0x21, 0x00}, /* EAR_CTL	*/ +	{ 0x22, 0x00}, /* HS_SEL	*/ +	{ 0x23, 0x00}, /* HS_GAIN_SET	*/ +	{ 0x24, 0x00}, /* HS_POPN_SET	*/ +	{ 0x25, 0x00}, /* PREDL_CTL	*/ +	{ 0x26, 0x00}, /* PREDR_CTL	*/ +	{ 0x27, 0x00}, /* PRECKL_CTL	*/ +	{ 0x28, 0x00}, /* PRECKR_CTL	*/ +	{ 0x29, 0x00}, /* HFL_CTL	*/ +	{ 0x2a, 0x00}, /* HFR_CTL	*/ +	{ 0x2b, 0x05}, /* ALC_CTL	*/ +	{ 0x2c, 0x00}, /* ALC_SET1	*/ +	{ 0x2d, 0x00}, /* ALC_SET2	*/ +	{ 0x2e, 0x00}, /* BOOST_CTL	*/ +	{ 0x2f, 0x00}, /* SOFTVOL_CTL	*/ +	{ 0x30, 0x13}, /* DTMF_FREQSEL	*/ +	{ 0x31, 0x00}, /* DTMF_TONEXT1H	*/ +	{ 0x32, 0x00}, /* DTMF_TONEXT1L	*/ +	{ 0x33, 0x00}, /* DTMF_TONEXT2H	*/ +	{ 0x34, 0x00}, /* DTMF_TONEXT2L	*/ +	{ 0x35, 0x79}, /* DTMF_TONOFF	*/ +	{ 0x36, 0x11}, /* DTMF_WANONOFF	*/ +	{ 0x37, 0x00}, /* I2S_RX_SCRAMBLE_H */ +	{ 0x38, 0x00}, /* I2S_RX_SCRAMBLE_M */ +	{ 0x39, 0x00}, /* I2S_RX_SCRAMBLE_L */ +	{ 0x3a, 0x06}, /* APLL_CTL */ +	{ 0x3b, 0x00}, /* DTMF_CTL */ +	{ 0x3c, 0x44}, /* DTMF_PGA_CTL2	(0x3C) */ +	{ 0x3d, 0x69}, /* DTMF_PGA_CTL1	(0x3D) */ +	{ 0x3e, 0x00}, /* MISC_SET_1 */ +	{ 0x3f, 0x00}, /* PCMBTMUX */ +	/* 0x40 - 0x42  Unused */ +	{ 0x43, 0x00}, /* RX_PATH_SEL */ +	{ 0x44, 0x32}, /* VDL_APGA_CTL */ +	{ 0x45, 0x00}, /* VIBRA_CTL */ +	{ 0x46, 0x00}, /* VIBRA_SET */ +	{ 0x47, 0x00}, /* VIBRA_PWM_SET	*/ +	{ 0x48, 0x00}, /* ANAMIC_GAIN	*/ +	{ 0x49, 0x00}, /* MISC_SET_2	*/ +	/* End of Audio Registers */ +}; + +static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case 0x00: +	case 0x03: +	case 0x40: +	case 0x41: +	case 0x42: +		return false; +	default: +		return true; +	} +} + +static const struct regmap_range twl4030_49_volatile_ranges[] = { +	regmap_reg_range(TWL4030_BASEADD_TEST, 0xff), +}; + +static const struct regmap_access_table twl4030_49_volatile_table = { +	.yes_ranges = twl4030_49_volatile_ranges, +	.n_yes_ranges = ARRAY_SIZE(twl4030_49_volatile_ranges), +}; +  static struct regmap_config twl4030_regmap_config[4] = {  	{  		/* Address 0x48 */ @@ -212,6 +318,15 @@ static struct regmap_config twl4030_regmap_config[4] = {  		.reg_bits = 8,  		.val_bits = 8,  		.max_register = 0xff, + +		.readable_reg = twl4030_49_nop_reg, +		.writeable_reg = twl4030_49_nop_reg, + +		.volatile_table = &twl4030_49_volatile_table, + +		.reg_defaults = twl4030_49_defaults, +		.num_reg_defaults = ARRAY_SIZE(twl4030_49_defaults), +		.cache_type = REGCACHE_RBTREE,  	},  	{  		/* Address 0x4a */ @@ -302,35 +417,50 @@ unsigned int twl_rev(void)  EXPORT_SYMBOL(twl_rev);  /** - * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 + * twl_get_regmap - Get the regmap associated with the given module   * @mod_no: module number - * @value: an array of num_bytes+1 containing data to write - * @reg: register address (just offset will do) - * @num_bytes: number of bytes to transfer   * - * Returns the result of operation - 0 is success + * Returns the regmap pointer or NULL in case of failure.   */ -int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) +static struct regmap *twl_get_regmap(u8 mod_no)  { -	int ret;  	int sid;  	struct twl_client *twl;  	if (unlikely(!twl_priv || !twl_priv->ready)) {  		pr_err("%s: not initialized\n", DRIVER_NAME); -		return -EPERM; +		return NULL;  	}  	if (unlikely(mod_no >= twl_get_last_module())) {  		pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); -		return -EPERM; +		return NULL;  	}  	sid = twl_priv->twl_map[mod_no].sid;  	twl = &twl_priv->twl_modules[sid]; -	ret = regmap_bulk_write(twl->regmap, -				twl_priv->twl_map[mod_no].base + reg, value, -				num_bytes); +	return twl->regmap; +} + +/** + * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 + * @mod_no: module number + * @value: an array of num_bytes+1 containing data to write + * @reg: register address (just offset will do) + * @num_bytes: number of bytes to transfer + * + * Returns the result of operation - 0 is success + */ +int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) +{ +	struct regmap *regmap = twl_get_regmap(mod_no); +	int ret; + +	if (!regmap) +		return -EPERM; + +	ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg, +				value, num_bytes);  	if (ret)  		pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", @@ -351,25 +481,14 @@ EXPORT_SYMBOL(twl_i2c_write);   */  int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)  { +	struct regmap *regmap = twl_get_regmap(mod_no);  	int ret; -	int sid; -	struct twl_client *twl; -	if (unlikely(!twl_priv || !twl_priv->ready)) { -		pr_err("%s: not initialized\n", DRIVER_NAME); -		return -EPERM; -	} -	if (unlikely(mod_no >= twl_get_last_module())) { -		pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); +	if (!regmap)  		return -EPERM; -	} - -	sid = twl_priv->twl_map[mod_no].sid; -	twl = &twl_priv->twl_modules[sid]; -	ret = regmap_bulk_read(twl->regmap, -			       twl_priv->twl_map[mod_no].base + reg, value, -			       num_bytes); +	ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg, +			       value, num_bytes);  	if (ret)  		pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", @@ -379,6 +498,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)  }  EXPORT_SYMBOL(twl_i2c_read); +/** + * twl_regcache_bypass - Configure the regcache bypass for the regmap associated + *			 with the module + * @mod_no: module number + * @enable: Regcache bypass state + * + * Returns 0 else failure. + */ +int twl_set_regcache_bypass(u8 mod_no, bool enable) +{ +	struct regmap *regmap = twl_get_regmap(mod_no); + +	if (!regmap) +		return -EPERM; + +	regcache_cache_bypass(regmap, enable); + +	return 0; +} +EXPORT_SYMBOL(twl_set_regcache_bypass); +  /*----------------------------------------------------------------------*/  /** @@ -701,62 +841,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,  			usb3v1[0].dev_name = dev_name(child);  		}  	} -	if (IS_ENABLED(CONFIG_TWL6030_USB) && pdata->usb && -	    twl_class_is_6030()) { - -		static struct regulator_consumer_supply usb3v3; -		int regulator; - -		if (IS_ENABLED(CONFIG_REGULATOR_TWL4030)) { -			/* this is a template that gets copied */ -			struct regulator_init_data usb_fixed = { -				.constraints.valid_modes_mask = -					REGULATOR_MODE_NORMAL -					| REGULATOR_MODE_STANDBY, -				.constraints.valid_ops_mask = -					REGULATOR_CHANGE_MODE -					| REGULATOR_CHANGE_STATUS, -			}; - -			if (features & TWL6032_SUBCLASS) { -				usb3v3.supply =	"ldousb"; -				regulator = TWL6032_REG_LDOUSB; -			} else { -				usb3v3.supply = "vusb"; -				regulator = TWL6030_REG_VUSB; -			} -			child = add_regulator_linked(regulator, &usb_fixed, -							&usb3v3, 1, -							features); -			if (IS_ERR(child)) -				return PTR_ERR(child); -		} - -		pdata->usb->features = features; - -		child = add_child(TWL_MODULE_USB, "twl6030_usb", -			pdata->usb, sizeof(*pdata->usb), true, -			/* irq1 = VBUS_PRES, irq0 = USB ID */ -			irq_base + USBOTG_INTR_OFFSET, -			irq_base + USB_PRES_INTR_OFFSET); - -		if (IS_ERR(child)) -			return PTR_ERR(child); -		/* we need to connect regulators to this transceiver */ -		if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) -			usb3v3.dev_name = dev_name(child); -	} else if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && -		   twl_class_is_6030()) { -		if (features & TWL6032_SUBCLASS) -			child = add_regulator(TWL6032_REG_LDOUSB, -						pdata->ldousb, features); -		else -			child = add_regulator(TWL6030_REG_VUSB, -						pdata->vusb, features); - -			if (IS_ERR(child)) -					return PTR_ERR(child); -	}  	if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {  		child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL, @@ -870,148 +954,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,  			return PTR_ERR(child);  	} -	/* twl6030 regulators */ -	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030() && -			!(features & TWL6032_SUBCLASS)) { -		child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_V1V8, pdata->v1v8, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VPP, pdata->vpp, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VDAC, pdata->vdac, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); -	} - -	/* 6030 and 6025 share this regulator */ -	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030()) { -		child = add_regulator(TWL6030_REG_VANA, pdata->vana, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); -	} - -	/* twl6032 regulators */ -	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030() && -			(features & TWL6032_SUBCLASS)) { -		child = add_regulator(TWL6032_REG_LDO5, pdata->ldo5, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO1, pdata->ldo1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO7, pdata->ldo7, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO6, pdata->ldo6, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDOLN, pdata->ldoln, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO2, pdata->ldo2, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO4, pdata->ldo4, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO3, pdata->ldo3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_SMPS3, pdata->smps3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_SMPS4, pdata->smps4, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_VIO, pdata->vio6025, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -	} -  	if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&  			!(features & (TPS_SUBSET | TWL5031))) {  		child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci", @@ -1133,6 +1075,11 @@ static int twl_remove(struct i2c_client *client)  	return 0;  } +static struct of_dev_auxdata twl_auxdata_lookup[] = { +	OF_DEV_AUXDATA("ti,twl4030-gpio", 0, "twl4030-gpio", NULL), +	{ /* sentinel */ }, +}; +  /* NOTE: This driver only handles a single twl4030/tps659x0 chip */  static int  twl_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1261,6 +1208,11 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  	 * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.  	 * Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,  	 * SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0. +	 * +	 * Also, always enable SmartReflex bit as that's needed for omaps to +	 * to do anything over I2C4 for voltage scaling even if SmartReflex +	 * is disabled. Without the SmartReflex bit omap sys_clkreq idle +	 * signal will never trigger for retention idle.  	 */  	if (twl_class_is_4030()) {  		u8 temp; @@ -1269,12 +1221,22 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  		temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \  			I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);  		twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); + +		twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &temp, +				TWL4030_DCDC_GLOBAL_CFG); +		temp |= SMARTREFLEX_ENABLE; +		twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, temp, +				 TWL4030_DCDC_GLOBAL_CFG);  	} -	if (node) -		status = of_platform_populate(node, NULL, NULL, &client->dev); -	else +	if (node) { +		if (pdata) +			twl_auxdata_lookup[0].platform_data = pdata->gpio; +		status = of_platform_populate(node, NULL, twl_auxdata_lookup, +					      &client->dev); +	} else {  		status = add_children(pdata, irq_base, id->driver_data); +	}  fail:  	if (status < 0) diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 9aa6d1efa24..596b1f657e2 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -27,7 +27,6 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   */ -#include <linux/init.h>  #include <linux/export.h>  #include <linux/interrupt.h>  #include <linux/irq.h> diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c deleted file mode 100644 index 4c583e47133..00000000000 --- a/drivers/mfd/twl4030-madc.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * - * TWL4030 MADC module driver-This driver monitors the real time - * conversion of analog signals like battery temperature, - * battery type, battery level etc. - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * J Keerthy <j-keerthy@ti.com> - * - * Based on twl4030-madc.c - * Copyright (C) 2008 Nokia Corporation - * Mikko Ylinen <mikko.k.ylinen@nokia.com> - * - * Amit Kucheria <amit.kucheria@canonical.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/init.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/i2c/twl.h> -#include <linux/i2c/twl4030-madc.h> -#include <linux/module.h> -#include <linux/stddef.h> -#include <linux/mutex.h> -#include <linux/bitops.h> -#include <linux/jiffies.h> -#include <linux/types.h> -#include <linux/gfp.h> -#include <linux/err.h> - -/* - * struct twl4030_madc_data - a container for madc info - * @dev - pointer to device structure for madc - * @lock - mutex protecting this data structure - * @requests - Array of request struct corresponding to SW1, SW2 and RT - * @imr - Interrupt mask register of MADC - * @isr - Interrupt status register of MADC - */ -struct twl4030_madc_data { -	struct device *dev; -	struct mutex lock;	/* mutex protecting this data structure */ -	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; -	int imr; -	int isr; -}; - -static struct twl4030_madc_data *twl4030_madc; - -struct twl4030_prescale_divider_ratios { -	s16 numerator; -	s16 denominator; -}; - -static const struct twl4030_prescale_divider_ratios -twl4030_divider_ratios[16] = { -	{1, 1},		/* CHANNEL 0 No Prescaler */ -	{1, 1},		/* CHANNEL 1 No Prescaler */ -	{6, 10},	/* CHANNEL 2 */ -	{6, 10},	/* CHANNEL 3 */ -	{6, 10},	/* CHANNEL 4 */ -	{6, 10},	/* CHANNEL 5 */ -	{6, 10},	/* CHANNEL 6 */ -	{6, 10},	/* CHANNEL 7 */ -	{3, 14},	/* CHANNEL 8 */ -	{1, 3},		/* CHANNEL 9 */ -	{1, 1},		/* CHANNEL 10 No Prescaler */ -	{15, 100},	/* CHANNEL 11 */ -	{1, 4},		/* CHANNEL 12 */ -	{1, 1},		/* CHANNEL 13 Reserved channels */ -	{1, 1},		/* CHANNEL 14 Reseved channels */ -	{5, 11},	/* CHANNEL 15 */ -}; - - -/* - * Conversion table from -3 to 55 degree Celcius - */ -static int therm_tbl[] = { -30800,	29500,	28300,	27100, -26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700,	17900, -17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100,	12600,	12100, -11600,	11200,	10800,	10400,	10000,	9630,	9280,	8950,	8620,	8310, -8020,	7730,	7460,	7200,	6950,	6710,	6470,	6250,	6040,	5830, -5640,	5450,	5260,	5090,	4920,	4760,	4600,	4450,	4310,	4170, -4040,	3910,	3790,	3670,	3550 -}; - -/* - * Structure containing the registers - * of different conversion methods supported by MADC. - * Hardware or RT real time conversion request initiated by external host - * processor for RT Signal conversions. - * External host processors can also request for non RT conversions - * SW1 and SW2 software conversions also called asynchronous or GPC request. - */ -static -const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { -	[TWL4030_MADC_RT] = { -			     .sel = TWL4030_MADC_RTSELECT_LSB, -			     .avg = TWL4030_MADC_RTAVERAGE_LSB, -			     .rbase = TWL4030_MADC_RTCH0_LSB, -			     }, -	[TWL4030_MADC_SW1] = { -			      .sel = TWL4030_MADC_SW1SELECT_LSB, -			      .avg = TWL4030_MADC_SW1AVERAGE_LSB, -			      .rbase = TWL4030_MADC_GPCH0_LSB, -			      .ctrl = TWL4030_MADC_CTRL_SW1, -			      }, -	[TWL4030_MADC_SW2] = { -			      .sel = TWL4030_MADC_SW2SELECT_LSB, -			      .avg = TWL4030_MADC_SW2AVERAGE_LSB, -			      .rbase = TWL4030_MADC_GPCH0_LSB, -			      .ctrl = TWL4030_MADC_CTRL_SW2, -			      }, -}; - -/* - * Function to read a particular channel value. - * @madc - pointer to struct twl4030_madc_data - * @reg - lsb of ADC Channel - * If the i2c read fails it returns an error else returns 0. - */ -static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) -{ -	u8 msb, lsb; -	int ret; -	/* -	 * For each ADC channel, we have MSB and LSB register pair. MSB address -	 * is always LSB address+1. reg parameter is the address of LSB register -	 */ -	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); -	if (ret) { -		dev_err(madc->dev, "unable to read MSB register 0x%X\n", -			reg + 1); -		return ret; -	} -	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg); -	if (ret) { -		dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg); -		return ret; -	} - -	return (int)(((msb << 8) | lsb) >> 6); -} - -/* - * Return battery temperature - * Or < 0 on failure. - */ -static int twl4030battery_temperature(int raw_volt) -{ -	u8 val; -	int temp, curr, volt, res, ret; - -	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; -	/* Getting and calculating the supply current in micro ampers */ -	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, -		REG_BCICTL2); -	if (ret < 0) -		return ret; -	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; -	/* Getting and calculating the thermistor resistance in ohms */ -	res = volt * 1000 / curr; -	/* calculating temperature */ -	for (temp = 58; temp >= 0; temp--) { -		int actual = therm_tbl[temp]; - -		if ((actual - res) >= 0) -			break; -	} - -	return temp + 1; -} - -static int twl4030battery_current(int raw_volt) -{ -	int ret; -	u8 val; - -	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, -		TWL4030_BCI_BCICTL1); -	if (ret) -		return ret; -	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ -		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; -	else /* slope of 0.88 mV/mA */ -		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; -} -/* - * Function to read channel values - * @madc - pointer to twl4030_madc_data struct - * @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, -				      bool raw) -{ -	int count = 0, count_req = 0, i; -	u8 reg; - -	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { -		reg = reg_base + 2 * i; -		buf[i] = twl4030_madc_channel_raw_read(madc, reg); -		if (buf[i] < 0) { -			dev_err(madc->dev, -				"Unable to read register 0x%X\n", reg); -			count_req++; -			continue; -		} -		if (raw) { -			count++; -			continue; -		} -		switch (i) { -		case 10: -			buf[i] = twl4030battery_current(buf[i]); -			if (buf[i] < 0) { -				dev_err(madc->dev, "err reading current\n"); -				count_req++; -			} else { -				count++; -				buf[i] = buf[i] - 750; -			} -			break; -		case 1: -			buf[i] = twl4030battery_temperature(buf[i]); -			if (buf[i] < 0) { -				dev_err(madc->dev, "err reading temperature\n"); -				count_req++; -			} else { -				buf[i] -= 3; -				count++; -			} -			break; -		default: -			count++; -			/* Analog Input (V) = conv_result * step_size / R -			 * conv_result = decimal value of 10-bit conversion -			 *		 result -			 * step size = 1.5 / (2 ^ 10 -1) -			 * R = Prescaler ratio for input channels. -			 * Result given in mV hence multiplied by 1000. -			 */ -			buf[i] = (buf[i] * 3 * 1000 * -				 twl4030_divider_ratios[i].denominator) -				/ (2 * 1023 * -				twl4030_divider_ratios[i].numerator); -		} -	} -	if (count_req) -		dev_err(madc->dev, "%d channel conversion failed\n", count_req); - -	return count; -} - -/* - * Enables irq. - * @madc - pointer to twl4030_madc_data struct - * @id - irq number to be enabled - * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 - * corresponding to RT, SW1, SW2 conversion requests. - * If the i2c read fails it returns an error else returns 0. - */ -static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) -{ -	u8 val; -	int ret; - -	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); -	if (ret) { -		dev_err(madc->dev, "unable to read imr register 0x%X\n", -			madc->imr); -		return ret; -	} -	val &= ~(1 << id); -	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); -	if (ret) { -		dev_err(madc->dev, -			"unable to write imr register 0x%X\n", madc->imr); -		return ret; - -	} - -	return 0; -} - -/* - * Disables irq. - * @madc - pointer to twl4030_madc_data struct - * @id - irq number to be disabled - * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 - * corresponding to RT, SW1, SW2 conversion requests. - * Returns error if i2c read/write fails. - */ -static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) -{ -	u8 val; -	int ret; - -	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); -	if (ret) { -		dev_err(madc->dev, "unable to read imr register 0x%X\n", -			madc->imr); -		return ret; -	} -	val |= (1 << id); -	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); -	if (ret) { -		dev_err(madc->dev, -			"unable to write imr register 0x%X\n", madc->imr); -		return ret; -	} - -	return 0; -} - -static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) -{ -	struct twl4030_madc_data *madc = _madc; -	const struct twl4030_madc_conversion_method *method; -	u8 isr_val, imr_val; -	int i, len, ret; -	struct twl4030_madc_request *r; - -	mutex_lock(&madc->lock); -	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); -	if (ret) { -		dev_err(madc->dev, "unable to read isr register 0x%X\n", -			madc->isr); -		goto err_i2c; -	} -	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); -	if (ret) { -		dev_err(madc->dev, "unable to read imr register 0x%X\n", -			madc->imr); -		goto err_i2c; -	} -	isr_val &= ~imr_val; -	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { -		if (!(isr_val & (1 << i))) -			continue; -		ret = twl4030_madc_disable_irq(madc, i); -		if (ret < 0) -			dev_dbg(madc->dev, "Disable interrupt failed%d\n", i); -		madc->requests[i].result_pending = 1; -	} -	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { -		r = &madc->requests[i]; -		/* No pending results for this method, move to next one */ -		if (!r->result_pending) -			continue; -		method = &twl4030_conversion_methods[r->method]; -		/* Read results */ -		len = twl4030_madc_read_channels(madc, method->rbase, -						 r->channels, r->rbuf, r->raw); -		/* Return results to caller */ -		if (r->func_cb != NULL) { -			r->func_cb(len, r->channels, r->rbuf); -			r->func_cb = NULL; -		} -		/* Free request */ -		r->result_pending = 0; -		r->active = 0; -	} -	mutex_unlock(&madc->lock); - -	return IRQ_HANDLED; - -err_i2c: -	/* -	 * In case of error check whichever request is active -	 * and service the same. -	 */ -	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { -		r = &madc->requests[i]; -		if (r->active == 0) -			continue; -		method = &twl4030_conversion_methods[r->method]; -		/* Read results */ -		len = twl4030_madc_read_channels(madc, method->rbase, -						 r->channels, r->rbuf, r->raw); -		/* Return results to caller */ -		if (r->func_cb != NULL) { -			r->func_cb(len, r->channels, r->rbuf); -			r->func_cb = NULL; -		} -		/* Free request */ -		r->result_pending = 0; -		r->active = 0; -	} -	mutex_unlock(&madc->lock); - -	return IRQ_HANDLED; -} - -static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, -				struct twl4030_madc_request *req) -{ -	struct twl4030_madc_request *p; -	int ret; - -	p = &madc->requests[req->method]; -	memcpy(p, req, sizeof(*req)); -	ret = twl4030_madc_enable_irq(madc, req->method); -	if (ret < 0) { -		dev_err(madc->dev, "enable irq failed!!\n"); -		return ret; -	} - -	return 0; -} - -/* - * Function which enables the madc conversion - * by writing to the control register. - * @madc - pointer to twl4030_madc_data struct - * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 - * corresponding to RT SW1 or SW2 conversion methods. - * Returns 0 if succeeds else a negative error value - */ -static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, -					 int conv_method) -{ -	const struct twl4030_madc_conversion_method *method; -	int ret = 0; -	method = &twl4030_conversion_methods[conv_method]; -	switch (conv_method) { -	case TWL4030_MADC_SW1: -	case TWL4030_MADC_SW2: -		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, -				       TWL4030_MADC_SW_START, method->ctrl); -		if (ret) { -			dev_err(madc->dev, -				"unable to write ctrl register 0x%X\n", -				method->ctrl); -			return ret; -		} -		break; -	default: -		break; -	} - -	return 0; -} - -/* - * Function that waits for conversion to be ready - * @madc - pointer to twl4030_madc_data struct - * @timeout_ms - timeout value in milliseconds - * @status_reg - ctrl register - * returns 0 if succeeds else a negative error value - */ -static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, -					      unsigned int timeout_ms, -					      u8 status_reg) -{ -	unsigned long timeout; -	int ret; - -	timeout = jiffies + msecs_to_jiffies(timeout_ms); -	do { -		u8 reg; - -		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); -		if (ret) { -			dev_err(madc->dev, -				"unable to read status register 0x%X\n", -				status_reg); -			return ret; -		} -		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) -			return 0; -		usleep_range(500, 2000); -	} while (!time_after(jiffies, timeout)); -	dev_err(madc->dev, "conversion timeout!\n"); - -	return -EAGAIN; -} - -/* - * An exported function which can be called from other kernel drivers. - * @req twl4030_madc_request structure - * req->rbuf will be filled with read values of channels based on the - * channel index. If a particular channel reading fails there will - * be a negative error value in the corresponding array element. - * returns 0 if succeeds else error value - */ -int twl4030_madc_conversion(struct twl4030_madc_request *req) -{ -	const struct twl4030_madc_conversion_method *method; -	u8 ch_msb, ch_lsb; -	int ret; - -	if (!req || !twl4030_madc) -		return -EINVAL; - -	mutex_lock(&twl4030_madc->lock); -	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { -		ret = -EINVAL; -		goto out; -	} -	/* Do we have a conversion request ongoing */ -	if (twl4030_madc->requests[req->method].active) { -		ret = -EBUSY; -		goto out; -	} -	ch_msb = (req->channels >> 8) & 0xff; -	ch_lsb = req->channels & 0xff; -	method = &twl4030_conversion_methods[req->method]; -	/* Select channels to be converted */ -	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); -	if (ret) { -		dev_err(twl4030_madc->dev, -			"unable to write sel register 0x%X\n", method->sel + 1); -		goto out; -	} -	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); -	if (ret) { -		dev_err(twl4030_madc->dev, -			"unable to write sel register 0x%X\n", method->sel + 1); -		goto out; -	} -	/* Select averaging for all channels if do_avg is set */ -	if (req->do_avg) { -		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, -				       ch_msb, method->avg + 1); -		if (ret) { -			dev_err(twl4030_madc->dev, -				"unable to write avg register 0x%X\n", -				method->avg + 1); -			goto out; -		} -		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, -				       ch_lsb, method->avg); -		if (ret) { -			dev_err(twl4030_madc->dev, -				"unable to write sel reg 0x%X\n", -				method->sel + 1); -			goto out; -		} -	} -	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { -		ret = twl4030_madc_set_irq(twl4030_madc, req); -		if (ret < 0) -			goto out; -		ret = twl4030_madc_start_conversion(twl4030_madc, req->method); -		if (ret < 0) -			goto out; -		twl4030_madc->requests[req->method].active = 1; -		ret = 0; -		goto out; -	} -	/* With RT method we should not be here anymore */ -	if (req->method == TWL4030_MADC_RT) { -		ret = -EINVAL; -		goto out; -	} -	ret = twl4030_madc_start_conversion(twl4030_madc, req->method); -	if (ret < 0) -		goto out; -	twl4030_madc->requests[req->method].active = 1; -	/* Wait until conversion is ready (ctrl register returns EOC) */ -	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); -	if (ret) { -		twl4030_madc->requests[req->method].active = 0; -		goto out; -	} -	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, -					 req->channels, req->rbuf, req->raw); -	twl4030_madc->requests[req->method].active = 0; - -out: -	mutex_unlock(&twl4030_madc->lock); - -	return ret; -} -EXPORT_SYMBOL_GPL(twl4030_madc_conversion); - -/* - * Return channel value - * Or < 0 on failure. - */ -int twl4030_get_madc_conversion(int channel_no) -{ -	struct twl4030_madc_request req; -	int temp = 0; -	int ret; - -	req.channels = (1 << channel_no); -	req.method = TWL4030_MADC_SW2; -	req.active = 0; -	req.func_cb = NULL; -	ret = twl4030_madc_conversion(&req); -	if (ret < 0) -		return ret; -	if (req.rbuf[channel_no] > 0) -		temp = req.rbuf[channel_no]; - -	return temp; -} -EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); - -/* - * Function to enable or disable bias current for - * main battery type reading or temperature sensing - * @madc - pointer to twl4030_madc_data struct - * @chan - can be one of the two values - * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading - * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature - * sensing - * @on - enable or disable chan. - */ -static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, -					      int chan, int on) -{ -	int ret; -	u8 regval; - -	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, -			      ®val, TWL4030_BCI_BCICTL1); -	if (ret) { -		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", -			TWL4030_BCI_BCICTL1); -		return ret; -	} -	if (on) -		regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; -	else -		regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; -	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, -			       regval, TWL4030_BCI_BCICTL1); -	if (ret) { -		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", -			TWL4030_BCI_BCICTL1); -		return ret; -	} - -	return 0; -} - -/* - * Function that sets MADC software power on bit to enable MADC - * @madc - pointer to twl4030_madc_data struct - * @on - Enable or disable MADC software powen on bit. - * returns error if i2c read/write fails else 0 - */ -static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) -{ -	u8 regval; -	int ret; - -	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, -			      ®val, TWL4030_MADC_CTRL1); -	if (ret) { -		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", -			TWL4030_MADC_CTRL1); -		return ret; -	} -	if (on) -		regval |= TWL4030_MADC_MADCON; -	else -		regval &= ~TWL4030_MADC_MADCON; -	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); -	if (ret) { -		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", -			TWL4030_MADC_CTRL1); -		return ret; -	} - -	return 0; -} - -/* - * Initialize MADC and request for threaded irq - */ -static int twl4030_madc_probe(struct platform_device *pdev) -{ -	struct twl4030_madc_data *madc; -	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); -	int ret; -	u8 regval; - -	if (!pdata) { -		dev_err(&pdev->dev, "platform_data not available\n"); -		return -EINVAL; -	} -	madc = kzalloc(sizeof(*madc), GFP_KERNEL); -	if (!madc) -		return -ENOMEM; - -	madc->dev = &pdev->dev; - -	/* -	 * Phoenix provides 2 interrupt lines. The first one is connected to -	 * the OMAP. The other one can be connected to the other processor such -	 * as modem. Hence two separate ISR and IMR registers. -	 */ -	madc->imr = (pdata->irq_line == 1) ? -	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; -	madc->isr = (pdata->irq_line == 1) ? -	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; -	ret = twl4030_madc_set_power(madc, 1); -	if (ret < 0) -		goto err_power; -	ret = twl4030_madc_set_current_generator(madc, 0, 1); -	if (ret < 0) -		goto err_current_generator; - -	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, -			      ®val, TWL4030_BCI_BCICTL1); -	if (ret) { -		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", -			TWL4030_BCI_BCICTL1); -		goto err_i2c; -	} -	regval |= TWL4030_BCI_MESBAT; -	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, -			       regval, TWL4030_BCI_BCICTL1); -	if (ret) { -		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", -			TWL4030_BCI_BCICTL1); -		goto err_i2c; -	} - -	/* Check that MADC clock is on */ -	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); -	if (ret) { -		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", -				TWL4030_REG_GPBR1); -		goto err_i2c; -	} - -	/* If MADC clk is not on, turn it on */ -	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { -		dev_info(&pdev->dev, "clk disabled, enabling\n"); -		regval |= TWL4030_GPBR1_MADC_HFCLK_EN; -		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, -				       TWL4030_REG_GPBR1); -		if (ret) { -			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", -					TWL4030_REG_GPBR1); -			goto err_i2c; -		} -	} - -	platform_set_drvdata(pdev, madc); -	mutex_init(&madc->lock); -	ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, -				   twl4030_madc_threaded_irq_handler, -				   IRQF_TRIGGER_RISING, "twl4030_madc", madc); -	if (ret) { -		dev_dbg(&pdev->dev, "could not request irq\n"); -		goto err_i2c; -	} -	twl4030_madc = madc; -	return 0; -err_i2c: -	twl4030_madc_set_current_generator(madc, 0, 0); -err_current_generator: -	twl4030_madc_set_power(madc, 0); -err_power: -	kfree(madc); - -	return ret; -} - -static int twl4030_madc_remove(struct platform_device *pdev) -{ -	struct twl4030_madc_data *madc = platform_get_drvdata(pdev); - -	free_irq(platform_get_irq(pdev, 0), madc); -	twl4030_madc_set_current_generator(madc, 0, 0); -	twl4030_madc_set_power(madc, 0); -	kfree(madc); - -	return 0; -} - -static struct platform_driver twl4030_madc_driver = { -	.probe = twl4030_madc_probe, -	.remove = twl4030_madc_remove, -	.driver = { -		   .name = "twl4030_madc", -		   .owner = THIS_MODULE, -		   }, -}; - -module_platform_driver(twl4030_madc_driver); - -MODULE_DESCRIPTION("TWL4030 ADC driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("J Keerthy"); -MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 96162b62f3c..3bc969a5916 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -29,13 +29,21 @@  #include <linux/i2c/twl.h>  #include <linux/platform_device.h>  #include <linux/of.h> +#include <linux/of_device.h>  #include <asm/mach-types.h>  static u8 twl4030_start_script_address = 0x2b; -#define PWR_P1_SW_EVENTS	0x10 -#define PWR_DEVOFF		(1 << 0) +/* Register bits for P1, P2 and P3_SW_EVENTS */ +#define PWR_STOPON_PRWON	BIT(6) +#define PWR_STOPON_SYSEN	BIT(5) +#define PWR_ENABLE_WARMRESET	BIT(4) +#define PWR_LVL_WAKEUP		BIT(3) +#define PWR_DEVACT		BIT(2) +#define PWR_DEVSLP		BIT(1) +#define PWR_DEVOFF		BIT(0) +  #define SEQ_OFFSYNC		(1 << 0)  #define PHY_TO_OFF_PM_MASTER(p)		(p - 0x36) @@ -52,10 +60,6 @@ static u8 twl4030_start_script_address = 0x2b;  #define R_CFG_P2_TRANSITION	PHY_TO_OFF_PM_MASTER(0x37)  #define R_CFG_P3_TRANSITION	PHY_TO_OFF_PM_MASTER(0x38) -#define LVL_WAKEUP	0x08 - -#define ENABLE_WARMRESET (1<<4) -  #define END_OF_SCRIPT		0x3f  #define R_SEQ_ADD_A2S		PHY_TO_OFF_PM_MASTER(0x55) @@ -125,6 +129,53 @@ static u8 res_config_addrs[] = {  	[RES_MAIN_REF]	= 0x94,  }; +/* + * Usable values for .remap_sleep and .remap_off + * Based on table "5.3.3 Resource Operating modes" + */ +enum { +	TWL_REMAP_OFF = 0, +	TWL_REMAP_SLEEP = 8, +	TWL_REMAP_ACTIVE = 9, +}; + +/* + * Macros to configure the PM register states for various resources. + * Note that we can make MSG_SINGULAR etc private to this driver once + * omap3 has been made DT only. + */ +#define TWL_DFLT_DELAY		2	/* typically 2 32 KiHz cycles */ +#define TWL_DEV_GRP_P123	(DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3) +#define TWL_RESOURCE_SET(res, state)					\ +	{ MSG_SINGULAR(DEV_GRP_NULL, (res), (state)), TWL_DFLT_DELAY } +#define TWL_RESOURCE_ON(res)	TWL_RESOURCE_SET(res, RES_STATE_ACTIVE) +#define TWL_RESOURCE_OFF(res)	TWL_RESOURCE_SET(res, RES_STATE_OFF) +#define TWL_RESOURCE_RESET(res)	TWL_RESOURCE_SET(res, RES_STATE_WRST) +/* + * It seems that type1 and type2 is just the resource init order + * number for the type1 and type2 group. + */ +#define TWL_RESOURCE_SET_ACTIVE(res, state)			       	\ +	{ MSG_SINGULAR(DEV_GRP_NULL, (res), RES_STATE_ACTIVE), (state) } +#define TWL_RESOURCE_GROUP_RESET(group, type1, type2)			\ +	{ MSG_BROADCAST(DEV_GRP_NULL, (group), (type1), (type2),	\ +		RES_STATE_WRST), TWL_DFLT_DELAY } +#define TWL_RESOURCE_GROUP_SLEEP(group, type, type2)			\ +	{ MSG_BROADCAST(DEV_GRP_NULL, (group), (type), (type2),		\ +		RES_STATE_SLEEP), TWL_DFLT_DELAY } +#define TWL_RESOURCE_GROUP_ACTIVE(group, type, type2)			\ +	{ MSG_BROADCAST(DEV_GRP_NULL, (group), (type), (type2),		\ +		RES_STATE_ACTIVE), TWL_DFLT_DELAY } +#define TWL_REMAP_SLEEP(res, devgrp, typ, typ2)				\ +	{ .resource = (res), .devgroup = (devgrp),			\ +	  .type = (typ), .type2 = (typ2),				\ +	  .remap_off = TWL_REMAP_OFF,					\ +	  .remap_sleep = TWL_REMAP_SLEEP, } +#define TWL_REMAP_OFF(res, devgrp, typ, typ2)				\ +	{ .resource = (res), .devgroup = (devgrp),			\ +	  .type = (typ), .type2 = (typ2),				\ +	  .remap_off = TWL_REMAP_OFF, .remap_sleep = TWL_REMAP_OFF, } +  static int twl4030_write_script_byte(u8 address, u8 byte)  {  	int err; @@ -196,7 +247,7 @@ static int twl4030_config_wakeup3_sequence(u8 address)  	err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P3_SW_EVENTS);  	if (err)  		goto out; -	data |= LVL_WAKEUP; +	data |= PWR_LVL_WAKEUP;  	err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P3_SW_EVENTS);  out:  	if (err) @@ -219,7 +270,7 @@ static int twl4030_config_wakeup12_sequence(u8 address)  	if (err)  		goto out; -	data |= LVL_WAKEUP; +	data |= PWR_LVL_WAKEUP;  	err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P1_SW_EVENTS);  	if (err)  		goto out; @@ -228,7 +279,7 @@ static int twl4030_config_wakeup12_sequence(u8 address)  	if (err)  		goto out; -	data |= LVL_WAKEUP; +	data |= PWR_LVL_WAKEUP;  	err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P2_SW_EVENTS);  	if (err)  		goto out; @@ -281,7 +332,7 @@ static int twl4030_config_warmreset_sequence(u8 address)  	if (err)  		goto out; -	rd_data |= ENABLE_WARMRESET; +	rd_data |= PWR_ENABLE_WARMRESET;  	err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P1_SW_EVENTS);  	if (err)  		goto out; @@ -290,7 +341,7 @@ static int twl4030_config_warmreset_sequence(u8 address)  	if (err)  		goto out; -	rd_data |= ENABLE_WARMRESET; +	rd_data |= PWR_ENABLE_WARMRESET;  	err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P2_SW_EVENTS);  	if (err)  		goto out; @@ -299,7 +350,7 @@ static int twl4030_config_warmreset_sequence(u8 address)  	if (err)  		goto out; -	rd_data |= ENABLE_WARMRESET; +	rd_data |= PWR_ENABLE_WARMRESET;  	err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P3_SW_EVENTS);  out:  	if (err) @@ -421,6 +472,12 @@ static int load_twl4030_script(struct twl4030_script *tscript,  			goto out;  	}  	if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) { +		/* Reset any existing sleep script to avoid hangs on reboot */ +		err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, +				       R_SEQ_ADD_A2S); +		if (err) +			goto out; +  		err = twl4030_config_wakeup12_sequence(address);  		if (err)  			goto out; @@ -493,7 +550,8 @@ int twl4030_remove_script(u8 flags)  	return err;  } -static int twl4030_power_configure_scripts(struct twl4030_power_data *pdata) +static int +twl4030_power_configure_scripts(const struct twl4030_power_data *pdata)  {  	int err;  	int i; @@ -509,12 +567,34 @@ static int twl4030_power_configure_scripts(struct twl4030_power_data *pdata)  	return 0;  } -static int twl4030_power_configure_resources(struct twl4030_power_data *pdata) +static void twl4030_patch_rconfig(struct twl4030_resconfig *common, +				  struct twl4030_resconfig *board) +{ +	while (common->resource) { +		struct twl4030_resconfig *b = board; + +		while (b->resource) { +			if (b->resource == common->resource) { +				*common = *b; +				break; +			} +			b++; +		} +		common++; +	} +} + +static int +twl4030_power_configure_resources(const struct twl4030_power_data *pdata)  {  	struct twl4030_resconfig *resconfig = pdata->resource_config; +	struct twl4030_resconfig *boardconf = pdata->board_config;  	int err;  	if (resconfig) { +		if (boardconf) +			twl4030_patch_rconfig(resconfig, boardconf); +  		while (resconfig->resource) {  			err = twl4030_configure_resource(resconfig);  			if (err) @@ -541,7 +621,7 @@ void twl4030_power_off(void)  		pr_err("TWL4030 Unable to power off\n");  } -static bool twl4030_power_use_poweroff(struct twl4030_power_data *pdata, +static bool twl4030_power_use_poweroff(const struct twl4030_power_data *pdata,  					struct device_node *node)  {  	if (pdata && pdata->use_poweroff) @@ -553,10 +633,170 @@ static bool twl4030_power_use_poweroff(struct twl4030_power_data *pdata,  	return false;  } +#ifdef CONFIG_OF + +/* Generic warm reset configuration for omap3 */ + +static struct twl4030_ins omap3_wrst_seq[] = { +	TWL_RESOURCE_OFF(RES_NRES_PWRON), +	TWL_RESOURCE_OFF(RES_RESET), +	TWL_RESOURCE_RESET(RES_MAIN_REF), +	TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2), +	TWL_RESOURCE_RESET(RES_VUSB_3V1), +	TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1), +	TWL_RESOURCE_GROUP_RESET(RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0), +	TWL_RESOURCE_ON(RES_RESET), +	TWL_RESOURCE_ON(RES_NRES_PWRON), +}; + +static struct twl4030_script omap3_wrst_script = { +	.script	= omap3_wrst_seq, +	.size	= ARRAY_SIZE(omap3_wrst_seq), +	.flags	= TWL4030_WRST_SCRIPT, +}; + +static struct twl4030_script *omap3_reset_scripts[] = { +	&omap3_wrst_script, +}; + +static struct twl4030_resconfig omap3_rconfig[] = { +	TWL_REMAP_SLEEP(RES_HFCLKOUT, DEV_GRP_P3, -1, -1), +	TWL_REMAP_SLEEP(RES_VDD1, DEV_GRP_P1, -1, -1), +	TWL_REMAP_SLEEP(RES_VDD2, DEV_GRP_P1, -1, -1), +	{ 0, 0 }, +}; + +static struct twl4030_power_data omap3_reset = { +	.scripts		= omap3_reset_scripts, +	.num			= ARRAY_SIZE(omap3_reset_scripts), +	.resource_config	= omap3_rconfig, +}; + +/* Recommended generic default idle configuration for off-idle */ + +/* Broadcast message to put res to sleep */ +static struct twl4030_ins omap3_idle_sleep_on_seq[] = { +	TWL_RESOURCE_GROUP_SLEEP(RES_GRP_ALL, RES_TYPE_ALL, 0), +}; + +static struct twl4030_script omap3_idle_sleep_on_script = { +	.script	= omap3_idle_sleep_on_seq, +	.size	= ARRAY_SIZE(omap3_idle_sleep_on_seq), +	.flags	= TWL4030_SLEEP_SCRIPT, +}; + +/* Broadcast message to put res to active */ +static struct twl4030_ins omap3_idle_wakeup_p12_seq[] = { +	TWL_RESOURCE_GROUP_ACTIVE(RES_GRP_ALL, RES_TYPE_ALL, 0), +}; + +static struct twl4030_script omap3_idle_wakeup_p12_script = { +	.script	= omap3_idle_wakeup_p12_seq, +	.size	= ARRAY_SIZE(omap3_idle_wakeup_p12_seq), +	.flags	= TWL4030_WAKEUP12_SCRIPT, +}; + +/* Broadcast message to put res to active */ +static struct twl4030_ins omap3_idle_wakeup_p3_seq[] = { +	TWL_RESOURCE_SET_ACTIVE(RES_CLKEN, 0x37), +	TWL_RESOURCE_GROUP_ACTIVE(RES_GRP_ALL, RES_TYPE_ALL, 0), +}; + +static struct twl4030_script omap3_idle_wakeup_p3_script = { +	.script	= omap3_idle_wakeup_p3_seq, +	.size	= ARRAY_SIZE(omap3_idle_wakeup_p3_seq), +	.flags	= TWL4030_WAKEUP3_SCRIPT, +}; + +static struct twl4030_script *omap3_idle_scripts[] = { +	&omap3_idle_wakeup_p12_script, +	&omap3_idle_wakeup_p3_script, +	&omap3_wrst_script, +	&omap3_idle_sleep_on_script, +}; + +/* + * Recommended configuration based on "Recommended Sleep + * Sequences for the Zoom Platform": + * http://omappedia.com/wiki/File:Recommended_Sleep_Sequences_Zoom.pdf + * Note that the type1 and type2 seem to be just the init order number + * for type1 and type2 groups as specified in the document mentioned + * above. + */ +static struct twl4030_resconfig omap3_idle_rconfig[] = { +	TWL_REMAP_SLEEP(RES_VAUX1, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VAUX2, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VAUX3, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VAUX4, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VMMC1, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VMMC2, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_OFF(RES_VPLL1, DEV_GRP_P1, 3, 1), +	TWL_REMAP_SLEEP(RES_VPLL2, DEV_GRP_P1, 0, 0), +	TWL_REMAP_SLEEP(RES_VSIM, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VDAC, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VINTANA1, TWL_DEV_GRP_P123, 1, 2), +	TWL_REMAP_SLEEP(RES_VINTANA2, TWL_DEV_GRP_P123, 0, 2), +	TWL_REMAP_SLEEP(RES_VINTDIG, TWL_DEV_GRP_P123, 1, 2), +	TWL_REMAP_SLEEP(RES_VIO, TWL_DEV_GRP_P123, 2, 2), +	TWL_REMAP_OFF(RES_VDD1, DEV_GRP_P1, 4, 1), +	TWL_REMAP_OFF(RES_VDD2, DEV_GRP_P1, 3, 1), +	TWL_REMAP_SLEEP(RES_VUSB_1V5, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VUSB_1V8, DEV_GRP_NULL, 0, 0), +	TWL_REMAP_SLEEP(RES_VUSB_3V1, TWL_DEV_GRP_P123, 0, 0), +	/* Resource #20 USB charge pump skipped */ +	TWL_REMAP_SLEEP(RES_REGEN, TWL_DEV_GRP_P123, 2, 1), +	TWL_REMAP_SLEEP(RES_NRES_PWRON, TWL_DEV_GRP_P123, 0, 1), +	TWL_REMAP_SLEEP(RES_CLKEN, TWL_DEV_GRP_P123, 3, 2), +	TWL_REMAP_SLEEP(RES_SYSEN, TWL_DEV_GRP_P123, 6, 1), +	TWL_REMAP_SLEEP(RES_HFCLKOUT, DEV_GRP_P3, 0, 2), +	TWL_REMAP_SLEEP(RES_32KCLKOUT, TWL_DEV_GRP_P123, 0, 0), +	TWL_REMAP_SLEEP(RES_RESET, TWL_DEV_GRP_P123, 6, 0), +	TWL_REMAP_SLEEP(RES_MAIN_REF, TWL_DEV_GRP_P123, 0, 0), +	{ /* Terminator */ }, +}; + +static struct twl4030_power_data omap3_idle = { +	.scripts		= omap3_idle_scripts, +	.num			= ARRAY_SIZE(omap3_idle_scripts), +	.resource_config	= omap3_idle_rconfig, +}; + +/* Disable 32 KiHz oscillator during idle */ +static struct twl4030_resconfig osc_off_rconfig[] = { +	TWL_REMAP_OFF(RES_CLKEN, DEV_GRP_P1 | DEV_GRP_P3, 3, 2), +	{ /* Terminator */ }, +}; + +static struct twl4030_power_data osc_off_idle = { +	.scripts		= omap3_idle_scripts, +	.num			= ARRAY_SIZE(omap3_idle_scripts), +	.resource_config	= omap3_idle_rconfig, +	.board_config		= osc_off_rconfig, +}; + +static struct of_device_id twl4030_power_of_match[] = { +	{ +		.compatible = "ti,twl4030-power-reset", +		.data = &omap3_reset, +	}, +	{ +		.compatible = "ti,twl4030-power-idle", +		.data = &omap3_idle, +	}, +	{ +		.compatible = "ti,twl4030-power-idle-osc-off", +		.data = &osc_off_idle, +	}, +	{ }, +}; +MODULE_DEVICE_TABLE(of, twl4030_power_of_match); +#endif	/* CONFIG_OF */ +  static int twl4030_power_probe(struct platform_device *pdev)  { -	struct twl4030_power_data *pdata = dev_get_platdata(&pdev->dev); +	const struct twl4030_power_data *pdata = dev_get_platdata(&pdev->dev);  	struct device_node *node = pdev->dev.of_node; +	const struct of_device_id *match;  	int err = 0;  	int err2 = 0;  	u8 val; @@ -577,8 +817,12 @@ static int twl4030_power_probe(struct platform_device *pdev)  		return err;  	} +	match = of_match_device(of_match_ptr(twl4030_power_of_match), +				&pdev->dev); +	if (match && match->data) +		pdata = match->data; +  	if (pdata) { -		/* TODO: convert to device tree */  		err = twl4030_power_configure_scripts(pdata);  		if (err) {  			pr_err("TWL4030 failed to load scripts\n"); @@ -628,14 +872,6 @@ static int twl4030_power_remove(struct platform_device *pdev)  	return 0;  } -#ifdef CONFIG_OF -static const struct of_device_id twl4030_power_of_match[] = { -	{.compatible = "ti,twl4030-power", }, -	{ }, -}; -MODULE_DEVICE_TABLE(of, twl4030_power_of_match); -#endif -  static struct platform_driver twl4030_power_driver = {  	.driver = {  		.name	= "twl4030_power", diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 517eda832f7..a6bb17d908b 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -31,7 +31,6 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   */ -#include <linux/init.h>  #include <linux/export.h>  #include <linux/interrupt.h>  #include <linux/irq.h> @@ -176,8 +175,9 @@ static irqreturn_t twl6030_irq_thread(int irq, void *data)  	int i, ret;  	union {  		u8 bytes[4]; -		u32 int_sts; +		__le32 int_sts;  	} sts; +	u32 int_sts; /* sts.int_sts converted to CPU endianness */  	struct twl6030_irq *pdata = data;  	/* read INT_STS_A, B and C in one shot using a burst read */ @@ -196,8 +196,9 @@ static irqreturn_t twl6030_irq_thread(int irq, void *data)  	if (sts.bytes[2] & 0x10)  		sts.bytes[2] |= 0x08; -	for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) -		if (sts.int_sts & 0x1) { +	int_sts = le32_to_cpu(sts.int_sts); +	for (i = 0; int_sts; int_sts >>= 1, i++) +		if (int_sts & 0x1) {  			int module_irq =  				irq_find_mapping(pdata->irq_domain,  						 pdata->irq_mapping_tbl[i]); diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index daf66942071..ae26d84b3a5 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -44,6 +44,59 @@  #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)  #define TWL6040_NUM_SUPPLIES	(2) +static struct reg_default twl6040_defaults[] = { +	{ 0x01, 0x4B }, /* REG_ASICID	(ro) */ +	{ 0x02, 0x00 }, /* REG_ASICREV	(ro) */ +	{ 0x03, 0x00 }, /* REG_INTID	*/ +	{ 0x04, 0x00 }, /* REG_INTMR	*/ +	{ 0x05, 0x00 }, /* REG_NCPCTRL	*/ +	{ 0x06, 0x00 }, /* REG_LDOCTL	*/ +	{ 0x07, 0x60 }, /* REG_HPPLLCTL	*/ +	{ 0x08, 0x00 }, /* REG_LPPLLCTL	*/ +	{ 0x09, 0x4A }, /* REG_LPPLLDIV	*/ +	{ 0x0A, 0x00 }, /* REG_AMICBCTL	*/ +	{ 0x0B, 0x00 }, /* REG_DMICBCTL	*/ +	{ 0x0C, 0x00 }, /* REG_MICLCTL	*/ +	{ 0x0D, 0x00 }, /* REG_MICRCTL	*/ +	{ 0x0E, 0x00 }, /* REG_MICGAIN	*/ +	{ 0x0F, 0x1B }, /* REG_LINEGAIN	*/ +	{ 0x10, 0x00 }, /* REG_HSLCTL	*/ +	{ 0x11, 0x00 }, /* REG_HSRCTL	*/ +	{ 0x12, 0x00 }, /* REG_HSGAIN	*/ +	{ 0x13, 0x00 }, /* REG_EARCTL	*/ +	{ 0x14, 0x00 }, /* REG_HFLCTL	*/ +	{ 0x15, 0x00 }, /* REG_HFLGAIN	*/ +	{ 0x16, 0x00 }, /* REG_HFRCTL	*/ +	{ 0x17, 0x00 }, /* REG_HFRGAIN	*/ +	{ 0x18, 0x00 }, /* REG_VIBCTLL	*/ +	{ 0x19, 0x00 }, /* REG_VIBDATL	*/ +	{ 0x1A, 0x00 }, /* REG_VIBCTLR	*/ +	{ 0x1B, 0x00 }, /* REG_VIBDATR	*/ +	{ 0x1C, 0x00 }, /* REG_HKCTL1	*/ +	{ 0x1D, 0x00 }, /* REG_HKCTL2	*/ +	{ 0x1E, 0x00 }, /* REG_GPOCTL	*/ +	{ 0x1F, 0x00 }, /* REG_ALB	*/ +	{ 0x20, 0x00 }, /* REG_DLB	*/ +	/* 0x28, REG_TRIM1 */ +	/* 0x29, REG_TRIM2 */ +	/* 0x2A, REG_TRIM3 */ +	/* 0x2B, REG_HSOTRIM */ +	/* 0x2C, REG_HFOTRIM */ +	{ 0x2D, 0x08 }, /* REG_ACCCTL	*/ +	{ 0x2E, 0x00 }, /* REG_STATUS	(ro) */ +}; + +static struct reg_default twl6040_patch[] = { +	/* +	 * Select I2C bus access to dual access registers +	 * Interrupt register is cleared on read +	 * Select fast mode for i2c (400KHz) +	 */ +	{ TWL6040_REG_ACCCTL, +		TWL6040_I2CSEL | TWL6040_INTCLRMODE | TWL6040_I2CMODE(1) }, +}; + +  static bool twl6040_has_vibra(struct device_node *node)  {  #ifdef CONFIG_OF @@ -238,6 +291,11 @@ int twl6040_power(struct twl6040 *twl6040, int on)  		if (twl6040->power_count++)  			goto out; +		clk_prepare_enable(twl6040->clk32k); + +		/* Allow writes to the chip */ +		regcache_cache_only(twl6040->regmap, false); +  		if (gpio_is_valid(twl6040->audpwron)) {  			/* use automatic power-up sequence */  			ret = twl6040_power_up_automatic(twl6040); @@ -253,6 +311,10 @@ int twl6040_power(struct twl6040 *twl6040, int on)  				goto out;  			}  		} + +		/* Sync with the HW */ +		regcache_sync(twl6040->regmap); +  		/* Default PLL configuration after power up */  		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;  		twl6040->sysclk = 19200000; @@ -279,8 +341,15 @@ int twl6040_power(struct twl6040 *twl6040, int on)  			/* use manual power-down sequence */  			twl6040_power_down_manual(twl6040);  		} + +		/* Set regmap to cache only and mark it as dirty */ +		regcache_cache_only(twl6040->regmap, true); +		regcache_mark_dirty(twl6040->regmap); +  		twl6040->sysclk = 0;  		twl6040->mclk = 0; + +		clk_disable_unprepare(twl6040->clk32k);  	}  out: @@ -372,12 +441,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,  					    TWL6040_HPLLENA;  				break;  			case 19200000: -				/* -				* PLL disabled -				* (enable PLL if MCLK jitter quality -				*  doesn't meet specification) -				*/ -				hppllctl |= TWL6040_MCLK_19200KHZ; +				/* PLL enabled, bypass mode */ +				hppllctl |= TWL6040_MCLK_19200KHZ | +					    TWL6040_HPLLBP | TWL6040_HPLLENA;  				break;  			case 26000000:  				/* PLL enabled, active mode */ @@ -385,9 +451,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,  					    TWL6040_HPLLENA;  				break;  			case 38400000: -				/* PLL enabled, active mode */ +				/* PLL enabled, bypass mode */  				hppllctl |= TWL6040_MCLK_38400KHZ | -					    TWL6040_HPLLENA; +					    TWL6040_HPLLBP | TWL6040_HPLLENA;  				break;  			default:  				dev_err(twl6040->dev, @@ -490,9 +556,24 @@ static bool twl6040_readable_reg(struct device *dev, unsigned int reg)  static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)  {  	switch (reg) { -	case TWL6040_REG_VIBCTLL: -	case TWL6040_REG_VIBCTLR: -	case TWL6040_REG_INTMR: +	case TWL6040_REG_ASICID: +	case TWL6040_REG_ASICREV: +	case TWL6040_REG_INTID: +	case TWL6040_REG_LPPLLCTL: +	case TWL6040_REG_HPPLLCTL: +	case TWL6040_REG_STATUS: +		return true; +	default: +		return false; +	} +} + +static bool twl6040_writeable_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case TWL6040_REG_ASICID: +	case TWL6040_REG_ASICREV: +	case TWL6040_REG_STATUS:  		return false;  	default:  		return true; @@ -502,10 +583,15 @@ static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)  static struct regmap_config twl6040_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, + +	.reg_defaults = twl6040_defaults, +	.num_reg_defaults = ARRAY_SIZE(twl6040_defaults), +  	.max_register = TWL6040_REG_STATUS, /* 0x2e */  	.readable_reg = twl6040_readable_reg,  	.volatile_reg = twl6040_volatile_reg, +	.writeable_reg = twl6040_writeable_reg,  	.cache_type = REGCACHE_RBTREE,  }; @@ -559,19 +645,25 @@ static int twl6040_probe(struct i2c_client *client,  	i2c_set_clientdata(client, twl6040); +	twl6040->clk32k = devm_clk_get(&client->dev, "clk32k"); +	if (IS_ERR(twl6040->clk32k)) { +		dev_info(&client->dev, "clk32k is not handled\n"); +		twl6040->clk32k = NULL; +	} +  	twl6040->supplies[0].supply = "vio";  	twl6040->supplies[1].supply = "v2v1";  	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); -		goto regulator_get_err; +		return ret;  	}  	ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);  	if (ret != 0) {  		dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); -		goto regulator_get_err; +		return ret;  	}  	twl6040->dev = &client->dev; @@ -580,7 +672,15 @@ static int twl6040_probe(struct i2c_client *client,  	mutex_init(&twl6040->mutex);  	init_completion(&twl6040->ready); +	regmap_register_patch(twl6040->regmap, twl6040_patch, +			      ARRAY_SIZE(twl6040_patch)); +  	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); +	if (twl6040->rev < 0) { +		dev_err(&client->dev, "Failed to read revision register: %d\n", +			twl6040->rev); +		goto gpio_err; +	}  	/* ERRATA: Automatic power-up is not possible in ES1.0 */  	if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) @@ -594,6 +694,9 @@ static int twl6040_probe(struct i2c_client *client,  					    GPIOF_OUT_INIT_LOW, "audpwron");  		if (ret)  			goto gpio_err; + +		/* Clear any pending interrupt */ +		twl6040_reg_read(twl6040, TWL6040_REG_INTID);  	}  	ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT, @@ -619,12 +722,9 @@ static int twl6040_probe(struct i2c_client *client,  					"twl6040_irq_th", twl6040);  	if (ret) {  		dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret); -		goto thirq_err; +		goto readyirq_err;  	} -	/* dual-access registers controlled by I2C only */ -	twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); -  	/*  	 * The main functionality of twl6040 to provide audio on OMAP4+ systems.  	 * We can add the ASoC codec child whenever this driver has been loaded. @@ -656,24 +756,21 @@ static int twl6040_probe(struct i2c_client *client,  	cell->name = "twl6040-gpo";  	children++; +	/* The chip is powered down so mark regmap to cache only and dirty */ +	regcache_cache_only(twl6040->regmap, true); +	regcache_mark_dirty(twl6040->regmap); +  	ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,  			      NULL, 0, NULL);  	if (ret) -		goto mfd_err; +		goto readyirq_err;  	return 0; -mfd_err: -	devm_free_irq(&client->dev, twl6040->irq_th, twl6040); -thirq_err: -	devm_free_irq(&client->dev, twl6040->irq_ready, twl6040);  readyirq_err:  	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);  gpio_err:  	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); -regulator_get_err: -	i2c_set_clientdata(client, NULL); -  	return ret;  } @@ -684,12 +781,9 @@ static int twl6040_remove(struct i2c_client *client)  	if (twl6040->power_count)  		twl6040_power(twl6040, 0); -	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); diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index d5966e6b5a7..153d595afaa 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -553,6 +553,7 @@ static int ucb1x00_probe(struct mcp *mcp)  	if (ucb->irq_base < 0) {  		dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",  			ucb->irq_base); +		ret = ucb->irq_base;  		goto err_irq_alloc;  	} @@ -741,9 +742,7 @@ static int ucb1x00_resume(struct device *dev)  }  #endif -static const struct dev_pm_ops ucb1x00_pm_ops = { -	SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume) -}; +static SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, ucb1x00_suspend, ucb1x00_resume);  static struct mcp_driver ucb1x00_driver = {  	.drv		= { diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c deleted file mode 100644 index 84ce6b9daa3..00000000000 --- a/drivers/mfd/vexpress-config.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * Copyright (C) 2012 ARM Limited - */ - -#define pr_fmt(fmt) "vexpress-config: " fmt - -#include <linux/bitops.h> -#include <linux/completion.h> -#include <linux/export.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/vexpress.h> - - -#define VEXPRESS_CONFIG_MAX_BRIDGES 2 - -struct vexpress_config_bridge { -	struct device_node *node; -	struct vexpress_config_bridge_info *info; -	struct list_head transactions; -	spinlock_t transactions_lock; -} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; - -static DECLARE_BITMAP(vexpress_config_bridges_map, -		ARRAY_SIZE(vexpress_config_bridges)); -static DEFINE_MUTEX(vexpress_config_bridges_mutex); - -struct vexpress_config_bridge *vexpress_config_bridge_register( -		struct device_node *node, -		struct vexpress_config_bridge_info *info) -{ -	struct vexpress_config_bridge *bridge; -	int i; - -	pr_debug("Registering bridge '%s'\n", info->name); - -	mutex_lock(&vexpress_config_bridges_mutex); -	i = find_first_zero_bit(vexpress_config_bridges_map, -			ARRAY_SIZE(vexpress_config_bridges)); -	if (i >= ARRAY_SIZE(vexpress_config_bridges)) { -		pr_err("Can't register more bridges!\n"); -		mutex_unlock(&vexpress_config_bridges_mutex); -		return NULL; -	} -	__set_bit(i, vexpress_config_bridges_map); -	bridge = &vexpress_config_bridges[i]; - -	bridge->node = node; -	bridge->info = info; -	INIT_LIST_HEAD(&bridge->transactions); -	spin_lock_init(&bridge->transactions_lock); - -	mutex_unlock(&vexpress_config_bridges_mutex); - -	return bridge; -} -EXPORT_SYMBOL(vexpress_config_bridge_register); - -void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) -{ -	struct vexpress_config_bridge __bridge = *bridge; -	int i; - -	mutex_lock(&vexpress_config_bridges_mutex); -	for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) -		if (&vexpress_config_bridges[i] == bridge) -			__clear_bit(i, vexpress_config_bridges_map); -	mutex_unlock(&vexpress_config_bridges_mutex); - -	WARN_ON(!list_empty(&__bridge.transactions)); -	while (!list_empty(&__bridge.transactions)) -		cpu_relax(); -} -EXPORT_SYMBOL(vexpress_config_bridge_unregister); - - -struct vexpress_config_func { -	struct vexpress_config_bridge *bridge; -	void *func; -}; - -struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, -		struct device_node *node) -{ -	struct device_node *bridge_node; -	struct vexpress_config_func *func; -	int i; - -	if (WARN_ON(dev && node && dev->of_node != node)) -		return NULL; -	if (dev && !node) -		node = dev->of_node; - -	func = kzalloc(sizeof(*func), GFP_KERNEL); -	if (!func) -		return NULL; - -	bridge_node = of_node_get(node); -	while (bridge_node) { -		const __be32 *prop = of_get_property(bridge_node, -				"arm,vexpress,config-bridge", NULL); - -		if (prop) { -			bridge_node = of_find_node_by_phandle( -					be32_to_cpup(prop)); -			break; -		} - -		bridge_node = of_get_next_parent(bridge_node); -	} - -	mutex_lock(&vexpress_config_bridges_mutex); -	for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { -		struct vexpress_config_bridge *bridge = -				&vexpress_config_bridges[i]; - -		if (test_bit(i, vexpress_config_bridges_map) && -				bridge->node == bridge_node) { -			func->bridge = bridge; -			func->func = bridge->info->func_get(dev, node); -			break; -		} -	} -	mutex_unlock(&vexpress_config_bridges_mutex); - -	if (!func->func) { -		of_node_put(node); -		kfree(func); -		return NULL; -	} - -	return func; -} -EXPORT_SYMBOL(__vexpress_config_func_get); - -void vexpress_config_func_put(struct vexpress_config_func *func) -{ -	func->bridge->info->func_put(func->func); -	of_node_put(func->bridge->node); -	kfree(func); -} -EXPORT_SYMBOL(vexpress_config_func_put); - -struct vexpress_config_trans { -	struct vexpress_config_func *func; -	int offset; -	bool write; -	u32 *data; -	int status; -	struct completion completion; -	struct list_head list; -}; - -static void vexpress_config_dump_trans(const char *what, -		struct vexpress_config_trans *trans) -{ -	pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", -			what, trans->write ? "write" : "read", trans, -			trans->func->func, trans->offset, -			trans->data ? *trans->data : 0, trans->status); -} - -static int vexpress_config_schedule(struct vexpress_config_trans *trans) -{ -	int status; -	struct vexpress_config_bridge *bridge = trans->func->bridge; -	unsigned long flags; - -	init_completion(&trans->completion); -	trans->status = -EFAULT; - -	spin_lock_irqsave(&bridge->transactions_lock, flags); - -	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 { -		vexpress_config_dump_trans("Queuing", trans); -		status = VEXPRESS_CONFIG_STATUS_WAIT; -	} - -	switch (status) { -	case VEXPRESS_CONFIG_STATUS_DONE: -		vexpress_config_dump_trans("Finished", trans); -		trans->status = status; -		break; -	case VEXPRESS_CONFIG_STATUS_WAIT: -		list_add_tail(&trans->list, &bridge->transactions); -		break; -	} - -	spin_unlock_irqrestore(&bridge->transactions_lock, flags); - -	return status; -} - -void vexpress_config_complete(struct vexpress_config_bridge *bridge, -		int status) -{ -	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); -	trans->status = status; - -	do { -		vexpress_config_dump_trans(message, trans); -		list_del(&trans->list); -		complete(&trans->completion); - -		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); - -	spin_unlock_irqrestore(&bridge->transactions_lock, flags); -} -EXPORT_SYMBOL(vexpress_config_complete); - -int vexpress_config_wait(struct vexpress_config_trans *trans) -{ -	wait_for_completion(&trans->completion); - -	return trans->status; -} -EXPORT_SYMBOL(vexpress_config_wait); - -int vexpress_config_read(struct vexpress_config_func *func, int offset, -		u32 *data) -{ -	struct vexpress_config_trans trans = { -		.func = func, -		.offset = offset, -		.write = false, -		.data = data, -		.status = 0, -	}; -	int status = vexpress_config_schedule(&trans); - -	if (status == VEXPRESS_CONFIG_STATUS_WAIT) -		status = vexpress_config_wait(&trans); - -	return status; -} -EXPORT_SYMBOL(vexpress_config_read); - -int vexpress_config_write(struct vexpress_config_func *func, int offset, -		u32 data) -{ -	struct vexpress_config_trans trans = { -		.func = func, -		.offset = offset, -		.write = true, -		.data = &data, -		.status = 0, -	}; -	int status = vexpress_config_schedule(&trans); - -	if (status == VEXPRESS_CONFIG_STATUS_WAIT) -		status = vexpress_config_wait(&trans); - -	return status; -} -EXPORT_SYMBOL(vexpress_config_write); diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 981bef4b7eb..9e21e4fc959 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -11,23 +11,22 @@   * Copyright (C) 2012 ARM Limited   */ +#include <linux/basic_mmio_gpio.h>  #include <linux/err.h> -#include <linux/gpio.h>  #include <linux/io.h> -#include <linux/leds.h> +#include <linux/mfd/core.h>  #include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_data/syscon.h>  #include <linux/platform_device.h> -#include <linux/regulator/driver.h>  #include <linux/slab.h>  #include <linux/stat.h> -#include <linux/timer.h>  #include <linux/vexpress.h>  #define SYS_ID			0x000  #define SYS_SW			0x004  #define SYS_LED			0x008  #define SYS_100HZ		0x024 -#define SYS_FLAGS		0x030  #define SYS_FLAGSSET		0x030  #define SYS_FLAGSCLR		0x034  #define SYS_NVFLAGS		0x038 @@ -46,465 +45,209 @@  #define SYS_CFGSTAT		0x0a8  #define SYS_HBI_MASK		0xfff -#define SYS_ID_HBI_SHIFT	16  #define SYS_PROCIDx_HBI_SHIFT	0 -#define SYS_LED_LED(n)		(1 << (n)) -  #define SYS_MCI_CARDIN		(1 << 0)  #define SYS_MCI_WPROT		(1 << 1) -#define SYS_FLASH_WPn		(1 << 0) -  #define SYS_MISC_MASTERSITE	(1 << 14) -#define SYS_CFGCTRL_START	(1 << 31) -#define SYS_CFGCTRL_WRITE	(1 << 30) -#define SYS_CFGCTRL_DCC(n)	(((n) & 0xf) << 26) -#define SYS_CFGCTRL_FUNC(n)	(((n) & 0x3f) << 20) -#define SYS_CFGCTRL_SITE(n)	(((n) & 0x3) << 16) -#define SYS_CFGCTRL_POSITION(n)	(((n) & 0xf) << 12) -#define SYS_CFGCTRL_DEVICE(n)	(((n) & 0xfff) << 0) - -#define SYS_CFGSTAT_ERR		(1 << 1) -#define SYS_CFGSTAT_COMPLETE	(1 << 0) - - -static void __iomem *vexpress_sysreg_base; -static struct device *vexpress_sysreg_dev; -static int vexpress_master_site; - -void vexpress_flags_set(u32 data) -{ -	writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); -	writel(data, vexpress_sysreg_base + SYS_FLAGSSET); -} +static void __iomem *__vexpress_sysreg_base; -u32 vexpress_get_procid(int site) +static void __iomem *vexpress_sysreg_base(void)  { -	if (site == VEXPRESS_SITE_MASTER) -		site = vexpress_master_site; - -	return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? -			SYS_PROCID0 : SYS_PROCID1)); -} +	if (!__vexpress_sysreg_base) { +		struct device_node *node = of_find_compatible_node(NULL, NULL, +				"arm,vexpress-sysreg"); -u32 vexpress_get_hbi(int site) -{ -	u32 id; - -	switch (site) { -	case VEXPRESS_SITE_MB: -		id = readl(vexpress_sysreg_base + SYS_ID); -		return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; -	case VEXPRESS_SITE_MASTER: -	case VEXPRESS_SITE_DB1: -	case VEXPRESS_SITE_DB2: -		id = vexpress_get_procid(site); -		return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; +		__vexpress_sysreg_base = of_iomap(node, 0);  	} -	return ~0; -} +	WARN_ON(!__vexpress_sysreg_base); -void __iomem *vexpress_get_24mhz_clock_base(void) -{ -	return vexpress_sysreg_base + SYS_24MHZ; +	return __vexpress_sysreg_base;  } -static void vexpress_sysreg_find_prop(struct device_node *node, -		const char *name, u32 *val) +static int vexpress_sysreg_get_master(void)  { -	of_node_get(node); -	while (node) { -		if (of_property_read_u32(node, name, val) == 0) { -			of_node_put(node); -			return; -		} -		node = of_get_next_parent(node); -	} -} - -unsigned __vexpress_get_site(struct device *dev, struct device_node *node) -{ -	u32 site = 0; - -	WARN_ON(dev && node && dev->of_node != node); -	if (dev && !node) -		node = dev->of_node; - -	if (node) { -		vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); -	} else if (dev && dev->bus == &platform_bus_type) { -		struct platform_device *pdev = to_platform_device(dev); - -		if (pdev->num_resources == 1 && -				pdev->resource[0].flags == IORESOURCE_BUS) -			site = pdev->resource[0].start; -	} else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) { -		site = VEXPRESS_SITE_MASTER; -	} +	if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE) +		return VEXPRESS_SITE_DB2; -	if (site == VEXPRESS_SITE_MASTER) -		site = vexpress_master_site; - -	return site; +	return VEXPRESS_SITE_DB1;  } - -struct vexpress_sysreg_config_func { -	u32 template; -	u32 device; -}; - -static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; -static struct timer_list vexpress_sysreg_config_timer; -static u32 *vexpress_sysreg_config_data; -static int vexpress_sysreg_config_tries; - -static void *vexpress_sysreg_config_func_get(struct device *dev, -		struct device_node *node) +void vexpress_flags_set(u32 data)  { -	struct vexpress_sysreg_config_func *config_func; -	u32 site; -	u32 position = 0; -	u32 dcc = 0; -	u32 func_device[2]; -	int err = -EFAULT; - -	if (node) { -		of_node_get(node); -		vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); -		vexpress_sysreg_find_prop(node, "arm,vexpress,position", -				&position); -		vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc); -		err = of_property_read_u32_array(node, -				"arm,vexpress-sysreg,func", func_device, -				ARRAY_SIZE(func_device)); -		of_node_put(node); -	} else if (dev && dev->bus == &platform_bus_type) { -		struct platform_device *pdev = to_platform_device(dev); - -		if (pdev->num_resources == 1 && -				pdev->resource[0].flags == IORESOURCE_BUS) { -			site = pdev->resource[0].start; -			func_device[0] = pdev->resource[0].end; -			func_device[1] = pdev->id; -			err = 0; -		} -	} -	if (err) -		return NULL; - -	config_func = kzalloc(sizeof(*config_func), GFP_KERNEL); -	if (!config_func) -		return NULL; - -	config_func->template = SYS_CFGCTRL_DCC(dcc); -	config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]); -	config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ? -			vexpress_master_site : site); -	config_func->template |= SYS_CFGCTRL_POSITION(position); -	config_func->device |= func_device[1]; - -	dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func, -			config_func->template, config_func->device); - -	return config_func; +	writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR); +	writel(data, vexpress_sysreg_base() + SYS_FLAGSSET);  } -static void vexpress_sysreg_config_func_put(void *func) +unsigned int vexpress_get_mci_cardin(struct device *dev)  { -	kfree(func); +	return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN;  } -static int vexpress_sysreg_config_func_exec(void *func, int offset, -		bool write, u32 *data) +u32 vexpress_get_procid(int site)  { -	int status; -	struct vexpress_sysreg_config_func *config_func = func; -	u32 command; - -	if (WARN_ON(!vexpress_sysreg_base)) -		return -ENOENT; - -	command = readl(vexpress_sysreg_base + SYS_CFGCTRL); -	if (WARN_ON(command & SYS_CFGCTRL_START)) -		return -EBUSY; - -	command = SYS_CFGCTRL_START; -	command |= write ? SYS_CFGCTRL_WRITE : 0; -	command |= config_func->template; -	command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); - -	/* Use a canary for reads */ -	if (!write) -		*data = 0xdeadbeef; - -	dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", -			command, *data); -	writel(*data, vexpress_sysreg_base + SYS_CFGDATA); -	writel(0, vexpress_sysreg_base + SYS_CFGSTAT); -	writel(command, vexpress_sysreg_base + SYS_CFGCTRL); -	mb(); - -	if (vexpress_sysreg_dev) { -		/* Schedule completion check */ -		if (!write) -			vexpress_sysreg_config_data = data; -		vexpress_sysreg_config_tries = 100; -		mod_timer(&vexpress_sysreg_config_timer, -				jiffies + usecs_to_jiffies(100)); -		status = VEXPRESS_CONFIG_STATUS_WAIT; -	} else { -		/* Early execution, no timer available, have to spin */ -		u32 cfgstat; - -		do { -			cpu_relax(); -			cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); -		} while (!cfgstat); - -		if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) -			*data = readl(vexpress_sysreg_base + SYS_CFGDATA); -		status = VEXPRESS_CONFIG_STATUS_DONE; - -		if (cfgstat & SYS_CFGSTAT_ERR) -			status = -EINVAL; -	} +	if (site == VEXPRESS_SITE_MASTER) +		site = vexpress_sysreg_get_master(); -	return status; +	return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ? +			SYS_PROCID0 : SYS_PROCID1));  } -struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { -	.name = "vexpress-sysreg", -	.func_get = vexpress_sysreg_config_func_get, -	.func_put = vexpress_sysreg_config_func_put, -	.func_exec = vexpress_sysreg_config_func_exec, -}; - -static void vexpress_sysreg_config_complete(unsigned long data) +void __iomem *vexpress_get_24mhz_clock_base(void)  { -	int status = VEXPRESS_CONFIG_STATUS_DONE; -	u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); - -	if (cfgstat & SYS_CFGSTAT_ERR) -		status = -EINVAL; -	if (!vexpress_sysreg_config_tries--) -		status = -ETIMEDOUT; - -	if (status < 0) { -		dev_err(vexpress_sysreg_dev, "error %d\n", status); -	} else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { -		mod_timer(&vexpress_sysreg_config_timer, -				jiffies + usecs_to_jiffies(50)); -		return; -	} - -	if (vexpress_sysreg_config_data) { -		*vexpress_sysreg_config_data = readl(vexpress_sysreg_base + -				SYS_CFGDATA); -		dev_dbg(vexpress_sysreg_dev, "read data %x\n", -				*vexpress_sysreg_config_data); -		vexpress_sysreg_config_data = NULL; -	} - -	vexpress_config_complete(vexpress_sysreg_config_bridge, status); +	return vexpress_sysreg_base() + SYS_24MHZ;  } -void vexpress_sysreg_setup(struct device_node *node) -{ -	if (WARN_ON(!vexpress_sysreg_base)) -		return; - -	if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) -		vexpress_master_site = VEXPRESS_SITE_DB2; -	else -		vexpress_master_site = VEXPRESS_SITE_DB1; - -	vexpress_sysreg_config_bridge = vexpress_config_bridge_register( -			node, &vexpress_sysreg_config_bridge_info); -	WARN_ON(!vexpress_sysreg_config_bridge); -} -  void __init vexpress_sysreg_early_init(void __iomem *base)  { -	vexpress_sysreg_base = base; -	vexpress_sysreg_setup(NULL); -} - -void __init vexpress_sysreg_of_early_init(void) -{ -	struct device_node *node; - -	if (vexpress_sysreg_base) -		return; +	__vexpress_sysreg_base = base; -	node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); -	if (node) { -		vexpress_sysreg_base = of_iomap(node, 0); -		vexpress_sysreg_setup(node); -	} +	vexpress_config_set_master(vexpress_sysreg_get_master());  } -#ifdef CONFIG_GPIOLIB - -#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ -	[VEXPRESS_GPIO_##_name] = { \ -		.reg = _reg, \ -		.value = _reg##_##_value, \ -	} +/* The sysreg block is just a random collection of various functions... */ -static struct vexpress_sysreg_gpio { -	unsigned long reg; -	u32 value; -} vexpress_sysreg_gpios[] = { -	VEXPRESS_SYSREG_GPIO(MMC_CARDIN,	SYS_MCI,	CARDIN), -	VEXPRESS_SYSREG_GPIO(MMC_WPROT,		SYS_MCI,	WPROT), -	VEXPRESS_SYSREG_GPIO(FLASH_WPn,		SYS_FLASH,	WPn), -	VEXPRESS_SYSREG_GPIO(LED0,		SYS_LED,	LED(0)), -	VEXPRESS_SYSREG_GPIO(LED1,		SYS_LED,	LED(1)), -	VEXPRESS_SYSREG_GPIO(LED2,		SYS_LED,	LED(2)), -	VEXPRESS_SYSREG_GPIO(LED3,		SYS_LED,	LED(3)), -	VEXPRESS_SYSREG_GPIO(LED4,		SYS_LED,	LED(4)), -	VEXPRESS_SYSREG_GPIO(LED5,		SYS_LED,	LED(5)), -	VEXPRESS_SYSREG_GPIO(LED6,		SYS_LED,	LED(6)), -	VEXPRESS_SYSREG_GPIO(LED7,		SYS_LED,	LED(7)), +static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = { +	.label = "sys_id",  }; -static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, -				       unsigned offset) -{ -	return 0; -} - -static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, -				       unsigned offset) -{ -	struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; -	u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); - -	return !!(reg_value & gpio->value); -} - -static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, -				       unsigned offset, int value) -{ -	struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; -	u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); - -	if (value) -		reg_value |= gpio->value; -	else -		reg_value &= ~gpio->value; - -	writel(reg_value, vexpress_sysreg_base + gpio->reg); -} - -static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, -						unsigned offset, int value) -{ -	vexpress_sysreg_gpio_set(chip, offset, value); - -	return 0; -} - -static struct gpio_chip vexpress_sysreg_gpio_chip = { -	.label = "vexpress-sysreg", -	.direction_input = vexpress_sysreg_gpio_direction_input, -	.direction_output = vexpress_sysreg_gpio_direction_output, -	.get = vexpress_sysreg_gpio_get, -	.set = vexpress_sysreg_gpio_set, -	.ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), -	.base = 0, +static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = { +	.label = "sys_led", +	.base = -1, +	.ngpio = 8,  }; - -#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ -	{ \ -		.name = "v2m:green:"_name, \ -		.default_trigger = _default_trigger, \ -		.gpio = VEXPRESS_GPIO_##_gpio, \ -	} - -struct gpio_led vexpress_sysreg_leds[] = { -	VEXPRESS_SYSREG_GREEN_LED("user1",	"heartbeat",	LED0), -	VEXPRESS_SYSREG_GREEN_LED("user2",	"mmc0",		LED1), -	VEXPRESS_SYSREG_GREEN_LED("user3",	"cpu0",		LED2), -	VEXPRESS_SYSREG_GREEN_LED("user4",	"cpu1",		LED3), -	VEXPRESS_SYSREG_GREEN_LED("user5",	"cpu2",		LED4), -	VEXPRESS_SYSREG_GREEN_LED("user6",	"cpu3",		LED5), -	VEXPRESS_SYSREG_GREEN_LED("user7",	"cpu4",		LED6), -	VEXPRESS_SYSREG_GREEN_LED("user8",	"cpu5",		LED7), +static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = { +	.label = "sys_mci", +	.base = -1, +	.ngpio = 2,  }; -struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { -	.num_leds = ARRAY_SIZE(vexpress_sysreg_leds), -	.leds = vexpress_sysreg_leds, +static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = { +	.label = "sys_flash", +	.base = -1, +	.ngpio = 1,  }; -#endif - +static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = { +	.label = "sys_misc", +}; -static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, -		struct device_attribute *attr, char *buf) -{ -	return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); -} +static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = { +	.label = "sys_procid", +}; -DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); +static struct mfd_cell vexpress_sysreg_cells[] = { +	{ +		.name = "syscon", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM(SYS_ID, 0x4), +		}, +		.platform_data = &vexpress_sysreg_sys_id_pdata, +		.pdata_size = sizeof(vexpress_sysreg_sys_id_pdata), +	}, { +		.name = "basic-mmio-gpio", +		.of_compatible = "arm,vexpress-sysreg,sys_led", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), +		}, +		.platform_data = &vexpress_sysreg_sys_led_pdata, +		.pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), +	}, { +		.name = "basic-mmio-gpio", +		.of_compatible = "arm,vexpress-sysreg,sys_mci", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), +		}, +		.platform_data = &vexpress_sysreg_sys_mci_pdata, +		.pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), +	}, { +		.name = "basic-mmio-gpio", +		.of_compatible = "arm,vexpress-sysreg,sys_flash", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), +		}, +		.platform_data = &vexpress_sysreg_sys_flash_pdata, +		.pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), +	}, { +		.name = "syscon", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM(SYS_MISC, 0x4), +		}, +		.platform_data = &vexpress_sysreg_sys_misc_pdata, +		.pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata), +	}, { +		.name = "syscon", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM(SYS_PROCID0, 0x8), +		}, +		.platform_data = &vexpress_sysreg_sys_procid_pdata, +		.pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata), +	}, { +		.name = "vexpress-syscfg", +		.num_resources = 1, +		.resources = (struct resource []) { +			DEFINE_RES_MEM(SYS_CFGDATA, 0xc), +		}, +	} +};  static int vexpress_sysreg_probe(struct platform_device *pdev)  { -	int err; -	struct resource *res = platform_get_resource(pdev, -			IORESOURCE_MEM, 0); - -	if (!devm_request_mem_region(&pdev->dev, res->start, -			resource_size(res), pdev->name)) { -		dev_err(&pdev->dev, "Failed to request memory region!\n"); -		return -EBUSY; -	} +	struct resource *mem; +	void __iomem *base; +	struct bgpio_chip *mmc_gpio_chip; +	u32 dt_hbi; -	if (!vexpress_sysreg_base) { -		vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, -				resource_size(res)); -		vexpress_sysreg_setup(pdev->dev.of_node); -	} +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!mem) +		return -EINVAL; -	if (!vexpress_sysreg_base) { -		dev_err(&pdev->dev, "Failed to obtain base address!\n"); -		return -EFAULT; -	} +	base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); +	if (!base) +		return -ENOMEM; -	setup_timer(&vexpress_sysreg_config_timer, -			vexpress_sysreg_config_complete, 0); +	vexpress_config_set_master(vexpress_sysreg_get_master()); -	vexpress_sysreg_dev = &pdev->dev; +	/* Confirm board type against DT property, if available */ +	if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { +		u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); +		u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; -#ifdef CONFIG_GPIOLIB -	vexpress_sysreg_gpio_chip.dev = &pdev->dev; -	err = gpiochip_add(&vexpress_sysreg_gpio_chip); -	if (err) { -		vexpress_config_bridge_unregister( -				vexpress_sysreg_config_bridge); -		dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", -				err); -		return err; +		if (WARN_ON(dt_hbi != hbi)) +			dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n", +					dt_hbi, hbi);  	} -	platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", -			PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, -			sizeof(vexpress_sysreg_leds_pdata)); -#endif - -	device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); - -	return 0; +	/* +	 * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with +	 * older trees using sysreg node for MMC control lines. +	 */ +	mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip), +			GFP_KERNEL); +	if (!mmc_gpio_chip) +		return -ENOMEM; +	bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI, +			NULL, NULL, NULL, NULL, 0); +	mmc_gpio_chip->gc.ngpio = 2; +	gpiochip_add(&mmc_gpio_chip->gc); + +	return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, +			vexpress_sysreg_cells, +			ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL);  }  static const struct of_device_id vexpress_sysreg_match[] = { @@ -522,7 +265,12 @@ static struct platform_driver vexpress_sysreg_driver = {  static int __init vexpress_sysreg_init(void)  { -	vexpress_sysreg_of_early_init(); +	struct device_node *node; + +	/* Need the sysreg early, before any other device... */ +	for_each_matching_node(node, vexpress_sysreg_match) +		of_platform_device_create(node, NULL, NULL); +  	return platform_driver_register(&vexpress_sysreg_driver);  }  core_initcall(vexpress_sysreg_init); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index af2a6703f34..e00f5340ed8 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -37,7 +37,7 @@ static const struct usb_device_id vprbrd_table[] = {  MODULE_DEVICE_TABLE(usb, vprbrd_table); -static struct mfd_cell vprbrd_devs[] = { +static const struct mfd_cell vprbrd_devs[] = {  	{  		.name = "viperboard-gpio",  	}, diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c index 757ecc63338..84f01da4875 100644 --- a/drivers/mfd/vx855.c +++ b/drivers/mfd/vx855.c @@ -60,7 +60,7 @@ static struct resource vx855_gpio_resources[] = {  	},  }; -static struct mfd_cell vx855_cells[] = { +static const struct mfd_cell vx855_cells[] = {  	{  		.name = "vx855_gpio",  		.num_resources = ARRAY_SIZE(vx855_gpio_resources), @@ -118,7 +118,7 @@ static void vx855_remove(struct pci_dev *pdev)  	pci_disable_device(pdev);  } -static DEFINE_PCI_DEVICE_TABLE(vx855_pci_tbl) = { +static const struct pci_device_id vx855_pci_tbl[] = {  	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },  	{ 0, }  }; diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 802dd3cb18c..c8a993bd17a 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -73,6 +73,7 @@ static const struct reg_default wm5102_revb_patch[] = {  	{ 0x171, 0x0000 },  	{ 0x35E, 0x000C },  	{ 0x2D4, 0x0000 }, +	{ 0x4DC, 0x0900 },  	{ 0x80, 0x0000 },  }; @@ -80,8 +81,7 @@ static const struct reg_default wm5102_revb_patch[] = {  int wm5102_patch(struct arizona *arizona)  {  	const struct reg_default *wm5102_patch; -	int ret = 0; -	int i, patch_size; +	int patch_size;  	switch (arizona->rev) {  	case 0: @@ -92,21 +92,9 @@ int wm5102_patch(struct arizona *arizona)  		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; +	return regmap_multi_reg_write_bypassed(arizona->regmap, +					       wm5102_patch, +					       patch_size);  }  static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = { @@ -345,7 +333,7 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x000001AA, 0x0004 },   /* R426   - FLL2 GPIO Clock */   	{ 0x00000200, 0x0006 },   /* R512   - Mic Charge Pump 1 */   	{ 0x00000210, 0x00D4 },   /* R528   - LDO1 Control 1 */  -	{ 0x00000212, 0x0001 },   /* R530   - LDO1 Control 2 */ +	{ 0x00000212, 0x0000 },   /* R530   - LDO1 Control 2 */  	{ 0x00000213, 0x0344 },   /* R531   - LDO2 Control 1 */   	{ 0x00000218, 0x01A6 },   /* R536   - Mic Bias Ctrl 1 */   	{ 0x00000219, 0x01A6 },   /* R537   - Mic Bias Ctrl 2 */  @@ -903,7 +891,6 @@ static const struct reg_default wm5102_reg_default[] = {  	{ 0x00000D1B, 0xFFFF },   /* R3355  - IRQ2 Status 4 Mask */   	{ 0x00000D1C, 0xFFFF },   /* R3356  - IRQ2 Status 5 Mask */   	{ 0x00000D1F, 0x0000 },   /* R3359  - IRQ2 Control */  -	{ 0x00000D50, 0x0000 },   /* R3408  - AOD wkup and trig */  	{ 0x00000D53, 0xFFFF },   /* R3411  - AOD IRQ Mask IRQ1 */   	{ 0x00000D54, 0xFFFF },   /* R3412  - AOD IRQ Mask IRQ2 */   	{ 0x00000D56, 0x0000 },   /* R3414  - Jack detect debounce */  @@ -1050,6 +1037,8 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6: +	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_7: +	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_8:  	case ARIZONA_COMFORT_NOISE_GENERATOR:  	case ARIZONA_HAPTICS_CONTROL_1:  	case ARIZONA_HAPTICS_CONTROL_2: @@ -1853,6 +1842,23 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_DSP1_STATUS_1:  	case ARIZONA_DSP1_STATUS_2:  	case ARIZONA_DSP1_STATUS_3: +	case ARIZONA_DSP1_WDMA_BUFFER_1: +	case ARIZONA_DSP1_WDMA_BUFFER_2: +	case ARIZONA_DSP1_WDMA_BUFFER_3: +	case ARIZONA_DSP1_WDMA_BUFFER_4: +	case ARIZONA_DSP1_WDMA_BUFFER_5: +	case ARIZONA_DSP1_WDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_BUFFER_7: +	case ARIZONA_DSP1_WDMA_BUFFER_8: +	case ARIZONA_DSP1_RDMA_BUFFER_1: +	case ARIZONA_DSP1_RDMA_BUFFER_2: +	case ARIZONA_DSP1_RDMA_BUFFER_3: +	case ARIZONA_DSP1_RDMA_BUFFER_4: +	case ARIZONA_DSP1_RDMA_BUFFER_5: +	case ARIZONA_DSP1_RDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_CONFIG_1: +	case ARIZONA_DSP1_WDMA_CONFIG_2: +	case ARIZONA_DSP1_RDMA_CONFIG_1:  	case ARIZONA_DSP1_SCRATCH_0:  	case ARIZONA_DSP1_SCRATCH_1:  	case ARIZONA_DSP1_SCRATCH_2: @@ -1908,9 +1914,27 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)  	case ARIZONA_AOD_IRQ1:  	case ARIZONA_AOD_IRQ2:  	case ARIZONA_AOD_IRQ_RAW_STATUS: +	case ARIZONA_DSP1_CLOCKING_1:  	case ARIZONA_DSP1_STATUS_1:  	case ARIZONA_DSP1_STATUS_2:  	case ARIZONA_DSP1_STATUS_3: +	case ARIZONA_DSP1_WDMA_BUFFER_1: +	case ARIZONA_DSP1_WDMA_BUFFER_2: +	case ARIZONA_DSP1_WDMA_BUFFER_3: +	case ARIZONA_DSP1_WDMA_BUFFER_4: +	case ARIZONA_DSP1_WDMA_BUFFER_5: +	case ARIZONA_DSP1_WDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_BUFFER_7: +	case ARIZONA_DSP1_WDMA_BUFFER_8: +	case ARIZONA_DSP1_RDMA_BUFFER_1: +	case ARIZONA_DSP1_RDMA_BUFFER_2: +	case ARIZONA_DSP1_RDMA_BUFFER_3: +	case ARIZONA_DSP1_RDMA_BUFFER_4: +	case ARIZONA_DSP1_RDMA_BUFFER_5: +	case ARIZONA_DSP1_RDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_CONFIG_1: +	case ARIZONA_DSP1_WDMA_CONFIG_2: +	case ARIZONA_DSP1_RDMA_CONFIG_1:  	case ARIZONA_DSP1_SCRATCH_0:  	case ARIZONA_DSP1_SCRATCH_1:  	case ARIZONA_DSP1_SCRATCH_2: diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 3113e39b318..41a7f6fb780 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -14,6 +14,7 @@  #include <linux/mfd/arizona/core.h>  #include <linux/mfd/arizona/registers.h> +#include <linux/device.h>  #include "arizona.h" @@ -223,6 +224,31 @@ static const struct reg_default wm5110_revb_patch[] = {  	{ 0x80, 0x0 },  }; +static const struct reg_default wm5110_revd_patch[] = { +	{ 0x80, 0x3 }, +	{ 0x80, 0x3 }, +	{ 0x393, 0x27 }, +	{ 0x394, 0x27 }, +	{ 0x395, 0x27 }, +	{ 0x396, 0x27 }, +	{ 0x397, 0x27 }, +	{ 0x398, 0x26 }, +	{ 0x221, 0x90 }, +	{ 0x211, 0x8 }, +	{ 0x36c, 0x1fb }, +	{ 0x26e, 0x64 }, +	{ 0x26f, 0xea }, +	{ 0x270, 0x1f16 }, +	{ 0x51b, 0x1 }, +	{ 0x55b, 0x1 }, +	{ 0x59b, 0x1 }, +	{ 0x4f0, 0x633 }, +	{ 0x441, 0xc059 }, +	{ 0x209, 0x27 }, +	{ 0x80, 0x0 }, +	{ 0x80, 0x0 }, +}; +  /* We use a function so we can use ARRAY_SIZE() */  int wm5110_patch(struct arizona *arizona)  { @@ -235,7 +261,10 @@ int wm5110_patch(struct arizona *arizona)  		return regmap_register_patch(arizona->regmap,  					     wm5110_revb_patch,  					     ARRAY_SIZE(wm5110_revb_patch)); - +	case 3: +		return regmap_register_patch(arizona->regmap, +					     wm5110_revd_patch, +					     ARRAY_SIZE(wm5110_revd_patch));  	default:  		return 0;  	} @@ -243,6 +272,12 @@ int wm5110_patch(struct arizona *arizona)  EXPORT_SYMBOL_GPL(wm5110_patch);  static const struct regmap_irq wm5110_aod_irqs[ARIZONA_NUM_IRQ] = { +	[ARIZONA_IRQ_MICD_CLAMP_FALL] = { +		.mask = ARIZONA_MICD_CLAMP_FALL_EINT1 +	}, +	[ARIZONA_IRQ_MICD_CLAMP_RISE] = { +		.mask = ARIZONA_MICD_CLAMP_RISE_EINT1 +	},  	[ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },  	[ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },  	[ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 }, @@ -433,10 +468,12 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x00000062, 0x01FF },    /* R98    - Sample Rate Sequence Select 2 */  	{ 0x00000063, 0x01FF },    /* R99    - Sample Rate Sequence Select 3 */  	{ 0x00000064, 0x01FF },    /* R100   - Sample Rate Sequence Select 4 */ -	{ 0x00000068, 0x01FF },    /* R104   - Always On Triggers Sequence Select 1 */ -	{ 0x00000069, 0x01FF },    /* R105   - Always On Triggers Sequence Select 2 */ -	{ 0x0000006A, 0x01FF },    /* R106   - Always On Triggers Sequence Select 3 */ -	{ 0x0000006B, 0x01FF },    /* R107   - Always On Triggers Sequence Select 4 */ +	{ 0x00000066, 0x01FF },    /* R102   - Always On Triggers Sequence Select 1 */ +	{ 0x00000067, 0x01FF },    /* R103   - Always On Triggers Sequence Select 2 */ +	{ 0x00000068, 0x01FF },    /* R104   - Always On Triggers Sequence Select 3 */ +	{ 0x00000069, 0x01FF },    /* R105   - Always On Triggers Sequence Select 4 */ +	{ 0x0000006A, 0x01FF },    /* R106   - Always On Triggers Sequence Select 5 */ +	{ 0x0000006B, 0x01FF },    /* R107   - Always On Triggers Sequence Select 6 */  	{ 0x00000070, 0x0000 },    /* R112   - Comfort Noise Generator */  	{ 0x00000090, 0x0000 },    /* R144   - Haptics Control 1 */  	{ 0x00000091, 0x7FFF },    /* R145   - Haptics Control 2 */ @@ -498,13 +535,14 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x000001AA, 0x0004 },    /* R426   - FLL2 GPIO Clock */  	{ 0x00000200, 0x0006 },    /* R512   - Mic Charge Pump 1 */  	{ 0x00000210, 0x0184 },    /* R528   - LDO1 Control 1 */ -	{ 0x00000213, 0x0344 },    /* R531   - LDO2 Control 1 */ +	{ 0x00000213, 0x03E4 },    /* R531   - LDO2 Control 1 */  	{ 0x00000218, 0x01A6 },    /* R536   - Mic Bias Ctrl 1 */  	{ 0x00000219, 0x01A6 },    /* R537   - Mic Bias Ctrl 2 */  	{ 0x0000021A, 0x01A6 },    /* R538   - Mic Bias Ctrl 3 */  	{ 0x00000293, 0x0000 },    /* R659   - Accessory Detect Mode 1 */ -	{ 0x0000029B, 0x0020 },    /* R667   - Headphone Detect 1 */ +	{ 0x0000029B, 0x0028 },    /* R667   - Headphone Detect 1 */  	{ 0x0000029C, 0x0000 },    /* R668   - Headphone Detect 2 */ +	{ 0x000002A2, 0x0000 },    /* R674   - Micd clamp control */  	{ 0x000002A3, 0x1102 },    /* R675   - Mic Detect 1 */  	{ 0x000002A4, 0x009F },    /* R676   - Mic Detect 2 */  	{ 0x000002A5, 0x0000 },    /* R677   - Mic Detect 3 */ @@ -513,10 +551,12 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x000002A8, 0x1422 },    /* R680   - Mic Detect Level 3 */  	{ 0x000002A9, 0x300A },    /* R681   - Mic Detect Level 4 */  	{ 0x000002C3, 0x0000 },    /* R707   - Mic noise mix control 1 */ +	{ 0x000002CB, 0x0000 },    /* R715   - Isolation control */  	{ 0x000002D3, 0x0000 },    /* R723   - Jack detect analogue */  	{ 0x00000300, 0x0000 },    /* R768   - Input Enables */  	{ 0x00000308, 0x0000 },    /* R776   - Input Rate */  	{ 0x00000309, 0x0022 },    /* R777   - Input Volume Ramp */ +	{ 0x0000030C, 0x0002 },    /* R780   - HPF Control */  	{ 0x00000310, 0x2080 },    /* R784   - IN1L Control */  	{ 0x00000311, 0x0180 },    /* R785   - ADC Digital Volume 1L */  	{ 0x00000312, 0x0000 },    /* R786   - DMIC1L Control */ @@ -538,6 +578,7 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x00000328, 0x2000 },    /* R808   - IN4L Control */  	{ 0x00000329, 0x0180 },    /* R809   - ADC Digital Volume 4L */  	{ 0x0000032A, 0x0000 },    /* R810   - DMIC4L Control */ +	{ 0x0000032C, 0x0000 },    /* R812   - IN4R Control */  	{ 0x0000032D, 0x0180 },    /* R813   - ADC Digital Volume 4R */  	{ 0x0000032E, 0x0000 },    /* R814   - DMIC4R Control */  	{ 0x00000400, 0x0000 },    /* R1024  - Output Enables 1 */ @@ -591,14 +632,18 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x0000043D, 0x0180 },    /* R1085  - DAC Digital Volume 6R */  	{ 0x0000043E, 0x0080 },    /* R1086  - DAC Volume Limit 6R */  	{ 0x0000043F, 0x0800 },    /* R1087  - Noise Gate Select 6R */ +	{ 0x00000440, 0x8FFF },    /* R1088  - DRE Enable */  	{ 0x00000450, 0x0000 },    /* R1104  - DAC AEC Control 1 */ -	{ 0x00000458, 0x0001 },    /* R1112  - Noise Gate Control */ +	{ 0x00000458, 0x0000 },    /* R1112  - Noise Gate Control */  	{ 0x00000480, 0x0040 },    /* R1152  - Class W ANC Threshold 1 */  	{ 0x00000481, 0x0040 },    /* R1153  - Class W ANC Threshold 2 */  	{ 0x00000490, 0x0069 },    /* R1168  - PDM SPK1 CTRL 1 */  	{ 0x00000491, 0x0000 },    /* R1169  - PDM SPK1 CTRL 2 */  	{ 0x00000492, 0x0069 },    /* R1170  - PDM SPK2 CTRL 1 */  	{ 0x00000493, 0x0000 },    /* R1171  - PDM SPK2 CTRL 2 */ +	{ 0x000004A0, 0x3480 },    /* R1184  - HP1 Short Circuit Ctrl */ +	{ 0x000004A1, 0x3480 },    /* R1185  - HP2 Short Circuit Ctrl */ +	{ 0x000004A2, 0x3480 },    /* R1186  - HP3 Short Circuit Ctrl */  	{ 0x00000500, 0x000C },    /* R1280  - AIF1 BCLK Ctrl */  	{ 0x00000501, 0x0008 },    /* R1281  - AIF1 Tx Pin Ctrl */  	{ 0x00000502, 0x0000 },    /* R1282  - AIF1 Rx Pin Ctrl */ @@ -875,6 +920,38 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x0000074D, 0x0080 },    /* R1869  - AIF2TX2MIX Input 3 Volume */  	{ 0x0000074E, 0x0000 },    /* R1870  - AIF2TX2MIX Input 4 Source */  	{ 0x0000074F, 0x0080 },    /* R1871  - AIF2TX2MIX Input 4 Volume */ +	{ 0x00000750, 0x0000 },    /* R1872  - AIF2TX3MIX Input 1 Source */ +	{ 0x00000751, 0x0080 },    /* R1873  - AIF2TX3MIX Input 1 Volume */ +	{ 0x00000752, 0x0000 },    /* R1874  - AIF2TX3MIX Input 2 Source */ +	{ 0x00000753, 0x0080 },    /* R1875  - AIF2TX3MIX Input 2 Volume */ +	{ 0x00000754, 0x0000 },    /* R1876  - AIF2TX3MIX Input 3 Source */ +	{ 0x00000755, 0x0080 },    /* R1877  - AIF2TX3MIX Input 3 Volume */ +	{ 0x00000756, 0x0000 },    /* R1878  - AIF2TX3MIX Input 4 Source */ +	{ 0x00000757, 0x0080 },    /* R1879  - AIF2TX3MIX Input 4 Volume */ +	{ 0x00000758, 0x0000 },    /* R1880  - AIF2TX4MIX Input 1 Source */ +	{ 0x00000759, 0x0080 },    /* R1881  - AIF2TX4MIX Input 1 Volume */ +	{ 0x0000075A, 0x0000 },    /* R1882  - AIF2TX4MIX Input 2 Source */ +	{ 0x0000075B, 0x0080 },    /* R1883  - AIF2TX4MIX Input 2 Volume */ +	{ 0x0000075C, 0x0000 },    /* R1884  - AIF2TX4MIX Input 3 Source */ +	{ 0x0000075D, 0x0080 },    /* R1885  - AIF2TX4MIX Input 3 Volume */ +	{ 0x0000075E, 0x0000 },    /* R1886  - AIF2TX4MIX Input 4 Source */ +	{ 0x0000075F, 0x0080 },    /* R1887  - AIF2TX4MIX Input 4 Volume */ +	{ 0x00000760, 0x0000 },    /* R1888  - AIF2TX5MIX Input 1 Source */ +	{ 0x00000761, 0x0080 },    /* R1889  - AIF2TX5MIX Input 1 Volume */ +	{ 0x00000762, 0x0000 },    /* R1890  - AIF2TX5MIX Input 2 Source */ +	{ 0x00000763, 0x0080 },    /* R1891  - AIF2TX5MIX Input 2 Volume */ +	{ 0x00000764, 0x0000 },    /* R1892  - AIF2TX5MIX Input 3 Source */ +	{ 0x00000765, 0x0080 },    /* R1893  - AIF2TX5MIX Input 3 Volume */ +	{ 0x00000766, 0x0000 },    /* R1894  - AIF2TX5MIX Input 4 Source */ +	{ 0x00000767, 0x0080 },    /* R1895  - AIF2TX5MIX Input 4 Volume */ +	{ 0x00000768, 0x0000 },    /* R1896  - AIF2TX6MIX Input 1 Source */ +	{ 0x00000769, 0x0080 },    /* R1897  - AIF2TX6MIX Input 1 Volume */ +	{ 0x0000076A, 0x0000 },    /* R1898  - AIF2TX6MIX Input 2 Source */ +	{ 0x0000076B, 0x0080 },    /* R1899  - AIF2TX6MIX Input 2 Volume */ +	{ 0x0000076C, 0x0000 },    /* R1900  - AIF2TX6MIX Input 3 Source */ +	{ 0x0000076D, 0x0080 },    /* R1901  - AIF2TX6MIX Input 3 Volume */ +	{ 0x0000076E, 0x0000 },    /* R1902  - AIF2TX6MIX Input 4 Source */ +	{ 0x0000076F, 0x0080 },    /* R1903  - AIF2TX6MIX Input 4 Volume */  	{ 0x00000780, 0x0000 },    /* R1920  - AIF3TX1MIX Input 1 Source */  	{ 0x00000781, 0x0080 },    /* R1921  - AIF3TX1MIX Input 1 Volume */  	{ 0x00000782, 0x0000 },    /* R1922  - AIF3TX1MIX Input 2 Source */ @@ -1204,7 +1281,6 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x00000D1B, 0xFFFF },    /* R3355  - IRQ2 Status 4 Mask */  	{ 0x00000D1C, 0xFFFF },    /* R3356  - IRQ2 Status 5 Mask */  	{ 0x00000D1F, 0x0000 },    /* R3359  - IRQ2 Control */ -	{ 0x00000D50, 0x0000 },    /* R3408  - AOD wkup and trig */  	{ 0x00000D53, 0xFFFF },    /* R3411  - AOD IRQ Mask IRQ1 */  	{ 0x00000D54, 0xFFFF },    /* R3412  - AOD IRQ Mask IRQ2 */  	{ 0x00000D56, 0x0000 },    /* R3414  - Jack detect debounce */ @@ -1336,6 +1412,64 @@ static const struct reg_default wm5110_reg_default[] = {  	{ 0x00001404, 0x0000 },    /* R5124  - DSP4 Status 1 */  }; +static bool wm5110_is_rev_b_adsp_memory(unsigned int reg) +{ +	if ((reg >= 0x100000 && reg < 0x103000) || +	    (reg >= 0x180000 && reg < 0x181000) || +	    (reg >= 0x190000 && reg < 0x192000) || +	    (reg >= 0x1a8000 && reg < 0x1a9000) || +	    (reg >= 0x200000 && reg < 0x209000) || +	    (reg >= 0x280000 && reg < 0x281000) || +	    (reg >= 0x290000 && reg < 0x29a000) || +	    (reg >= 0x2a8000 && reg < 0x2aa000) || +	    (reg >= 0x300000 && reg < 0x30f000) || +	    (reg >= 0x380000 && reg < 0x382000) || +	    (reg >= 0x390000 && reg < 0x39e000) || +	    (reg >= 0x3a8000 && reg < 0x3b6000) || +	    (reg >= 0x400000 && reg < 0x403000) || +	    (reg >= 0x480000 && reg < 0x481000) || +	    (reg >= 0x490000 && reg < 0x492000) || +	    (reg >= 0x4a8000 && reg < 0x4a9000)) +		return true; +	else +		return false; +} + +static bool wm5110_is_rev_d_adsp_memory(unsigned int reg) +{ +	if ((reg >= 0x100000 && reg < 0x106000) || +	    (reg >= 0x180000 && reg < 0x182000) || +	    (reg >= 0x190000 && reg < 0x198000) || +	    (reg >= 0x1a8000 && reg < 0x1aa000) || +	    (reg >= 0x200000 && reg < 0x20f000) || +	    (reg >= 0x280000 && reg < 0x282000) || +	    (reg >= 0x290000 && reg < 0x29c000) || +	    (reg >= 0x2a6000 && reg < 0x2b4000) || +	    (reg >= 0x300000 && reg < 0x30f000) || +	    (reg >= 0x380000 && reg < 0x382000) || +	    (reg >= 0x390000 && reg < 0x3a2000) || +	    (reg >= 0x3a6000 && reg < 0x3b4000) || +	    (reg >= 0x400000 && reg < 0x406000) || +	    (reg >= 0x480000 && reg < 0x482000) || +	    (reg >= 0x490000 && reg < 0x498000) || +	    (reg >= 0x4a8000 && reg < 0x4aa000)) +		return true; +	else +		return false; +} + +static bool wm5110_is_adsp_memory(struct device *dev, unsigned int reg) +{ +	struct arizona *arizona = dev_get_drvdata(dev); + +	switch (arizona->rev) { +	case 0 ... 2: +		return wm5110_is_rev_b_adsp_memory(reg); +	default: +		return wm5110_is_rev_d_adsp_memory(reg); +	} +} +  static bool wm5110_readable_register(struct device *dev, unsigned int reg)  {  	switch (reg) { @@ -1367,6 +1501,8 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: +	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5: +	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:  	case ARIZONA_COMFORT_NOISE_GENERATOR:  	case ARIZONA_HAPTICS_CONTROL_1:  	case ARIZONA_HAPTICS_CONTROL_2: @@ -1440,6 +1576,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_ACCESSORY_DETECT_MODE_1:  	case ARIZONA_HEADPHONE_DETECT_1:  	case ARIZONA_HEADPHONE_DETECT_2: +	case ARIZONA_MICD_CLAMP_CONTROL:  	case ARIZONA_MIC_DETECT_1:  	case ARIZONA_MIC_DETECT_2:  	case ARIZONA_MIC_DETECT_3: @@ -1448,11 +1585,13 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_MIC_DETECT_LEVEL_3:  	case ARIZONA_MIC_DETECT_LEVEL_4:  	case ARIZONA_MIC_NOISE_MIX_CONTROL_1: +	case ARIZONA_ISOLATION_CONTROL:  	case ARIZONA_JACK_DETECT_ANALOGUE:  	case ARIZONA_INPUT_ENABLES:  	case ARIZONA_INPUT_ENABLES_STATUS:  	case ARIZONA_INPUT_RATE:  	case ARIZONA_INPUT_VOLUME_RAMP: +	case ARIZONA_HPF_CONTROL:  	case ARIZONA_IN1L_CONTROL:  	case ARIZONA_ADC_DIGITAL_VOLUME_1L:  	case ARIZONA_DMIC1L_CONTROL: @@ -1474,6 +1613,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_IN4L_CONTROL:  	case ARIZONA_ADC_DIGITAL_VOLUME_4L:  	case ARIZONA_DMIC4L_CONTROL: +	case ARIZONA_IN4R_CONTROL:  	case ARIZONA_ADC_DIGITAL_VOLUME_4R:  	case ARIZONA_DMIC4R_CONTROL:  	case ARIZONA_OUTPUT_ENABLES_1: @@ -1529,12 +1669,16 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_DAC_DIGITAL_VOLUME_6R:  	case ARIZONA_DAC_VOLUME_LIMIT_6R:  	case ARIZONA_NOISE_GATE_SELECT_6R: +	case ARIZONA_DRE_ENABLE:  	case ARIZONA_DAC_AEC_CONTROL_1:  	case ARIZONA_NOISE_GATE_CONTROL:  	case ARIZONA_PDM_SPK1_CTRL_1:  	case ARIZONA_PDM_SPK1_CTRL_2:  	case ARIZONA_PDM_SPK2_CTRL_1:  	case ARIZONA_PDM_SPK2_CTRL_2: +	case ARIZONA_HP1_SHORT_CIRCUIT_CTRL: +	case ARIZONA_HP2_SHORT_CIRCUIT_CTRL: +	case ARIZONA_HP3_SHORT_CIRCUIT_CTRL:  	case ARIZONA_AIF1_BCLK_CTRL:  	case ARIZONA_AIF1_TX_PIN_CTRL:  	case ARIZONA_AIF1_RX_PIN_CTRL: @@ -1813,6 +1957,38 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:  	case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:  	case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME: +	case ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE: +	case ARIZONA_AIF2TX3MIX_INPUT_1_VOLUME: +	case ARIZONA_AIF2TX3MIX_INPUT_2_SOURCE: +	case ARIZONA_AIF2TX3MIX_INPUT_2_VOLUME: +	case ARIZONA_AIF2TX3MIX_INPUT_3_SOURCE: +	case ARIZONA_AIF2TX3MIX_INPUT_3_VOLUME: +	case ARIZONA_AIF2TX3MIX_INPUT_4_SOURCE: +	case ARIZONA_AIF2TX3MIX_INPUT_4_VOLUME: +	case ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE: +	case ARIZONA_AIF2TX4MIX_INPUT_1_VOLUME: +	case ARIZONA_AIF2TX4MIX_INPUT_2_SOURCE: +	case ARIZONA_AIF2TX4MIX_INPUT_2_VOLUME: +	case ARIZONA_AIF2TX4MIX_INPUT_3_SOURCE: +	case ARIZONA_AIF2TX4MIX_INPUT_3_VOLUME: +	case ARIZONA_AIF2TX4MIX_INPUT_4_SOURCE: +	case ARIZONA_AIF2TX4MIX_INPUT_4_VOLUME: +	case ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE: +	case ARIZONA_AIF2TX5MIX_INPUT_1_VOLUME: +	case ARIZONA_AIF2TX5MIX_INPUT_2_SOURCE: +	case ARIZONA_AIF2TX5MIX_INPUT_2_VOLUME: +	case ARIZONA_AIF2TX5MIX_INPUT_3_SOURCE: +	case ARIZONA_AIF2TX5MIX_INPUT_3_VOLUME: +	case ARIZONA_AIF2TX5MIX_INPUT_4_SOURCE: +	case ARIZONA_AIF2TX5MIX_INPUT_4_VOLUME: +	case ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE: +	case ARIZONA_AIF2TX6MIX_INPUT_1_VOLUME: +	case ARIZONA_AIF2TX6MIX_INPUT_2_SOURCE: +	case ARIZONA_AIF2TX6MIX_INPUT_2_VOLUME: +	case ARIZONA_AIF2TX6MIX_INPUT_3_SOURCE: +	case ARIZONA_AIF2TX6MIX_INPUT_3_VOLUME: +	case ARIZONA_AIF2TX6MIX_INPUT_4_SOURCE: +	case ARIZONA_AIF2TX6MIX_INPUT_4_VOLUME:  	case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:  	case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:  	case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE: @@ -2291,24 +2467,124 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_DSP1_STATUS_1:  	case ARIZONA_DSP1_STATUS_2:  	case ARIZONA_DSP1_STATUS_3: +	case ARIZONA_DSP1_STATUS_4: +	case ARIZONA_DSP1_WDMA_BUFFER_1: +	case ARIZONA_DSP1_WDMA_BUFFER_2: +	case ARIZONA_DSP1_WDMA_BUFFER_3: +	case ARIZONA_DSP1_WDMA_BUFFER_4: +	case ARIZONA_DSP1_WDMA_BUFFER_5: +	case ARIZONA_DSP1_WDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_BUFFER_7: +	case ARIZONA_DSP1_WDMA_BUFFER_8: +	case ARIZONA_DSP1_RDMA_BUFFER_1: +	case ARIZONA_DSP1_RDMA_BUFFER_2: +	case ARIZONA_DSP1_RDMA_BUFFER_3: +	case ARIZONA_DSP1_RDMA_BUFFER_4: +	case ARIZONA_DSP1_RDMA_BUFFER_5: +	case ARIZONA_DSP1_RDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_CONFIG_1: +	case ARIZONA_DSP1_WDMA_CONFIG_2: +	case ARIZONA_DSP1_WDMA_OFFSET_1: +	case ARIZONA_DSP1_RDMA_CONFIG_1: +	case ARIZONA_DSP1_RDMA_OFFSET_1: +	case ARIZONA_DSP1_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP1_SCRATCH_0: +	case ARIZONA_DSP1_SCRATCH_1: +	case ARIZONA_DSP1_SCRATCH_2: +	case ARIZONA_DSP1_SCRATCH_3:  	case ARIZONA_DSP2_CONTROL_1:  	case ARIZONA_DSP2_CLOCKING_1:  	case ARIZONA_DSP2_STATUS_1:  	case ARIZONA_DSP2_STATUS_2:  	case ARIZONA_DSP2_STATUS_3: +	case ARIZONA_DSP2_STATUS_4: +	case ARIZONA_DSP2_WDMA_BUFFER_1: +	case ARIZONA_DSP2_WDMA_BUFFER_2: +	case ARIZONA_DSP2_WDMA_BUFFER_3: +	case ARIZONA_DSP2_WDMA_BUFFER_4: +	case ARIZONA_DSP2_WDMA_BUFFER_5: +	case ARIZONA_DSP2_WDMA_BUFFER_6: +	case ARIZONA_DSP2_WDMA_BUFFER_7: +	case ARIZONA_DSP2_WDMA_BUFFER_8: +	case ARIZONA_DSP2_RDMA_BUFFER_1: +	case ARIZONA_DSP2_RDMA_BUFFER_2: +	case ARIZONA_DSP2_RDMA_BUFFER_3: +	case ARIZONA_DSP2_RDMA_BUFFER_4: +	case ARIZONA_DSP2_RDMA_BUFFER_5: +	case ARIZONA_DSP2_RDMA_BUFFER_6: +	case ARIZONA_DSP2_WDMA_CONFIG_1: +	case ARIZONA_DSP2_WDMA_CONFIG_2: +	case ARIZONA_DSP2_WDMA_OFFSET_1: +	case ARIZONA_DSP2_RDMA_CONFIG_1: +	case ARIZONA_DSP2_RDMA_OFFSET_1: +	case ARIZONA_DSP2_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP2_SCRATCH_0: +	case ARIZONA_DSP2_SCRATCH_1: +	case ARIZONA_DSP2_SCRATCH_2: +	case ARIZONA_DSP2_SCRATCH_3:  	case ARIZONA_DSP3_CONTROL_1:  	case ARIZONA_DSP3_CLOCKING_1:  	case ARIZONA_DSP3_STATUS_1:  	case ARIZONA_DSP3_STATUS_2:  	case ARIZONA_DSP3_STATUS_3: +	case ARIZONA_DSP3_STATUS_4: +	case ARIZONA_DSP3_WDMA_BUFFER_1: +	case ARIZONA_DSP3_WDMA_BUFFER_2: +	case ARIZONA_DSP3_WDMA_BUFFER_3: +	case ARIZONA_DSP3_WDMA_BUFFER_4: +	case ARIZONA_DSP3_WDMA_BUFFER_5: +	case ARIZONA_DSP3_WDMA_BUFFER_6: +	case ARIZONA_DSP3_WDMA_BUFFER_7: +	case ARIZONA_DSP3_WDMA_BUFFER_8: +	case ARIZONA_DSP3_RDMA_BUFFER_1: +	case ARIZONA_DSP3_RDMA_BUFFER_2: +	case ARIZONA_DSP3_RDMA_BUFFER_3: +	case ARIZONA_DSP3_RDMA_BUFFER_4: +	case ARIZONA_DSP3_RDMA_BUFFER_5: +	case ARIZONA_DSP3_RDMA_BUFFER_6: +	case ARIZONA_DSP3_WDMA_CONFIG_1: +	case ARIZONA_DSP3_WDMA_CONFIG_2: +	case ARIZONA_DSP3_WDMA_OFFSET_1: +	case ARIZONA_DSP3_RDMA_CONFIG_1: +	case ARIZONA_DSP3_RDMA_OFFSET_1: +	case ARIZONA_DSP3_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP3_SCRATCH_0: +	case ARIZONA_DSP3_SCRATCH_1: +	case ARIZONA_DSP3_SCRATCH_2: +	case ARIZONA_DSP3_SCRATCH_3:  	case ARIZONA_DSP4_CONTROL_1:  	case ARIZONA_DSP4_CLOCKING_1:  	case ARIZONA_DSP4_STATUS_1:  	case ARIZONA_DSP4_STATUS_2:  	case ARIZONA_DSP4_STATUS_3: +	case ARIZONA_DSP4_STATUS_4: +	case ARIZONA_DSP4_WDMA_BUFFER_1: +	case ARIZONA_DSP4_WDMA_BUFFER_2: +	case ARIZONA_DSP4_WDMA_BUFFER_3: +	case ARIZONA_DSP4_WDMA_BUFFER_4: +	case ARIZONA_DSP4_WDMA_BUFFER_5: +	case ARIZONA_DSP4_WDMA_BUFFER_6: +	case ARIZONA_DSP4_WDMA_BUFFER_7: +	case ARIZONA_DSP4_WDMA_BUFFER_8: +	case ARIZONA_DSP4_RDMA_BUFFER_1: +	case ARIZONA_DSP4_RDMA_BUFFER_2: +	case ARIZONA_DSP4_RDMA_BUFFER_3: +	case ARIZONA_DSP4_RDMA_BUFFER_4: +	case ARIZONA_DSP4_RDMA_BUFFER_5: +	case ARIZONA_DSP4_RDMA_BUFFER_6: +	case ARIZONA_DSP4_WDMA_CONFIG_1: +	case ARIZONA_DSP4_WDMA_CONFIG_2: +	case ARIZONA_DSP4_WDMA_OFFSET_1: +	case ARIZONA_DSP4_RDMA_CONFIG_1: +	case ARIZONA_DSP4_RDMA_OFFSET_1: +	case ARIZONA_DSP4_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP4_SCRATCH_0: +	case ARIZONA_DSP4_SCRATCH_1: +	case ARIZONA_DSP4_SCRATCH_2: +	case ARIZONA_DSP4_SCRATCH_3:  		return true;  	default: -		return false; +		return wm5110_is_adsp_memory(dev, reg);  	}  } @@ -2347,37 +2623,143 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)  	case ARIZONA_INTERRUPT_RAW_STATUS_7:  	case ARIZONA_INTERRUPT_RAW_STATUS_8:  	case ARIZONA_IRQ_PIN_STATUS: +	case ARIZONA_AOD_WKUP_AND_TRIG:  	case ARIZONA_AOD_IRQ1:  	case ARIZONA_AOD_IRQ2: +	case ARIZONA_AOD_IRQ_RAW_STATUS:  	case ARIZONA_FX_CTRL2:  	case ARIZONA_ASRC_STATUS:  	case ARIZONA_DSP_STATUS: -	case ARIZONA_DSP1_CONTROL_1: -	case ARIZONA_DSP1_CLOCKING_1:  	case ARIZONA_DSP1_STATUS_1:  	case ARIZONA_DSP1_STATUS_2:  	case ARIZONA_DSP1_STATUS_3: +	case ARIZONA_DSP1_STATUS_4: +	case ARIZONA_DSP1_WDMA_BUFFER_1: +	case ARIZONA_DSP1_WDMA_BUFFER_2: +	case ARIZONA_DSP1_WDMA_BUFFER_3: +	case ARIZONA_DSP1_WDMA_BUFFER_4: +	case ARIZONA_DSP1_WDMA_BUFFER_5: +	case ARIZONA_DSP1_WDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_BUFFER_7: +	case ARIZONA_DSP1_WDMA_BUFFER_8: +	case ARIZONA_DSP1_RDMA_BUFFER_1: +	case ARIZONA_DSP1_RDMA_BUFFER_2: +	case ARIZONA_DSP1_RDMA_BUFFER_3: +	case ARIZONA_DSP1_RDMA_BUFFER_4: +	case ARIZONA_DSP1_RDMA_BUFFER_5: +	case ARIZONA_DSP1_RDMA_BUFFER_6: +	case ARIZONA_DSP1_WDMA_CONFIG_1: +	case ARIZONA_DSP1_WDMA_CONFIG_2: +	case ARIZONA_DSP1_WDMA_OFFSET_1: +	case ARIZONA_DSP1_RDMA_CONFIG_1: +	case ARIZONA_DSP1_RDMA_OFFSET_1: +	case ARIZONA_DSP1_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP1_SCRATCH_0: +	case ARIZONA_DSP1_SCRATCH_1: +	case ARIZONA_DSP1_SCRATCH_2: +	case ARIZONA_DSP1_SCRATCH_3: +	case ARIZONA_DSP1_CLOCKING_1:  	case ARIZONA_DSP2_STATUS_1:  	case ARIZONA_DSP2_STATUS_2:  	case ARIZONA_DSP2_STATUS_3: +	case ARIZONA_DSP2_STATUS_4: +	case ARIZONA_DSP2_WDMA_BUFFER_1: +	case ARIZONA_DSP2_WDMA_BUFFER_2: +	case ARIZONA_DSP2_WDMA_BUFFER_3: +	case ARIZONA_DSP2_WDMA_BUFFER_4: +	case ARIZONA_DSP2_WDMA_BUFFER_5: +	case ARIZONA_DSP2_WDMA_BUFFER_6: +	case ARIZONA_DSP2_WDMA_BUFFER_7: +	case ARIZONA_DSP2_WDMA_BUFFER_8: +	case ARIZONA_DSP2_RDMA_BUFFER_1: +	case ARIZONA_DSP2_RDMA_BUFFER_2: +	case ARIZONA_DSP2_RDMA_BUFFER_3: +	case ARIZONA_DSP2_RDMA_BUFFER_4: +	case ARIZONA_DSP2_RDMA_BUFFER_5: +	case ARIZONA_DSP2_RDMA_BUFFER_6: +	case ARIZONA_DSP2_WDMA_CONFIG_1: +	case ARIZONA_DSP2_WDMA_CONFIG_2: +	case ARIZONA_DSP2_WDMA_OFFSET_1: +	case ARIZONA_DSP2_RDMA_CONFIG_1: +	case ARIZONA_DSP2_RDMA_OFFSET_1: +	case ARIZONA_DSP2_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP2_SCRATCH_0: +	case ARIZONA_DSP2_SCRATCH_1: +	case ARIZONA_DSP2_SCRATCH_2: +	case ARIZONA_DSP2_SCRATCH_3: +	case ARIZONA_DSP2_CLOCKING_1:  	case ARIZONA_DSP3_STATUS_1:  	case ARIZONA_DSP3_STATUS_2:  	case ARIZONA_DSP3_STATUS_3: +	case ARIZONA_DSP3_STATUS_4: +	case ARIZONA_DSP3_WDMA_BUFFER_1: +	case ARIZONA_DSP3_WDMA_BUFFER_2: +	case ARIZONA_DSP3_WDMA_BUFFER_3: +	case ARIZONA_DSP3_WDMA_BUFFER_4: +	case ARIZONA_DSP3_WDMA_BUFFER_5: +	case ARIZONA_DSP3_WDMA_BUFFER_6: +	case ARIZONA_DSP3_WDMA_BUFFER_7: +	case ARIZONA_DSP3_WDMA_BUFFER_8: +	case ARIZONA_DSP3_RDMA_BUFFER_1: +	case ARIZONA_DSP3_RDMA_BUFFER_2: +	case ARIZONA_DSP3_RDMA_BUFFER_3: +	case ARIZONA_DSP3_RDMA_BUFFER_4: +	case ARIZONA_DSP3_RDMA_BUFFER_5: +	case ARIZONA_DSP3_RDMA_BUFFER_6: +	case ARIZONA_DSP3_WDMA_CONFIG_1: +	case ARIZONA_DSP3_WDMA_CONFIG_2: +	case ARIZONA_DSP3_WDMA_OFFSET_1: +	case ARIZONA_DSP3_RDMA_CONFIG_1: +	case ARIZONA_DSP3_RDMA_OFFSET_1: +	case ARIZONA_DSP3_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP3_SCRATCH_0: +	case ARIZONA_DSP3_SCRATCH_1: +	case ARIZONA_DSP3_SCRATCH_2: +	case ARIZONA_DSP3_SCRATCH_3: +	case ARIZONA_DSP3_CLOCKING_1:  	case ARIZONA_DSP4_STATUS_1:  	case ARIZONA_DSP4_STATUS_2:  	case ARIZONA_DSP4_STATUS_3: +	case ARIZONA_DSP4_STATUS_4: +	case ARIZONA_DSP4_WDMA_BUFFER_1: +	case ARIZONA_DSP4_WDMA_BUFFER_2: +	case ARIZONA_DSP4_WDMA_BUFFER_3: +	case ARIZONA_DSP4_WDMA_BUFFER_4: +	case ARIZONA_DSP4_WDMA_BUFFER_5: +	case ARIZONA_DSP4_WDMA_BUFFER_6: +	case ARIZONA_DSP4_WDMA_BUFFER_7: +	case ARIZONA_DSP4_WDMA_BUFFER_8: +	case ARIZONA_DSP4_RDMA_BUFFER_1: +	case ARIZONA_DSP4_RDMA_BUFFER_2: +	case ARIZONA_DSP4_RDMA_BUFFER_3: +	case ARIZONA_DSP4_RDMA_BUFFER_4: +	case ARIZONA_DSP4_RDMA_BUFFER_5: +	case ARIZONA_DSP4_RDMA_BUFFER_6: +	case ARIZONA_DSP4_WDMA_CONFIG_1: +	case ARIZONA_DSP4_WDMA_CONFIG_2: +	case ARIZONA_DSP4_WDMA_OFFSET_1: +	case ARIZONA_DSP4_RDMA_CONFIG_1: +	case ARIZONA_DSP4_RDMA_OFFSET_1: +	case ARIZONA_DSP4_EXTERNAL_START_SELECT_1: +	case ARIZONA_DSP4_SCRATCH_0: +	case ARIZONA_DSP4_SCRATCH_1: +	case ARIZONA_DSP4_SCRATCH_2: +	case ARIZONA_DSP4_SCRATCH_3: +	case ARIZONA_DSP4_CLOCKING_1:  		return true;  	default: -		return false; +		return wm5110_is_adsp_memory(dev, reg);  	}  } +#define WM5110_MAX_REGISTER 0x4a9fff +  const struct regmap_config wm5110_spi_regmap = {  	.reg_bits = 32,  	.pad_bits = 16,  	.val_bits = 16, -	.max_register = ARIZONA_DSP1_STATUS_2, +	.max_register = WM5110_MAX_REGISTER,  	.readable_reg = wm5110_readable_register,  	.volatile_reg = wm5110_volatile_register, @@ -2391,7 +2773,7 @@ const struct regmap_config wm5110_i2c_regmap = {  	.reg_bits = 32,  	.val_bits = 16, -	.max_register = ARIZONA_DSP1_STATUS_2, +	.max_register = WM5110_MAX_REGISTER,  	.readable_reg = wm5110_readable_register,  	.volatile_reg = wm5110_volatile_register, diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 5c459f46922..28366a90e1a 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1011,7 +1011,7 @@ static struct resource wm831x_wdt_resources[] = {  	},  }; -static struct mfd_cell wm8310_devs[] = { +static const struct mfd_cell wm8310_devs[] = {  	{  		.name = "wm831x-backup",  	}, @@ -1165,7 +1165,7 @@ static struct mfd_cell wm8310_devs[] = {  	},  }; -static struct mfd_cell wm8311_devs[] = { +static const struct mfd_cell wm8311_devs[] = {  	{  		.name = "wm831x-backup",  	}, @@ -1295,7 +1295,7 @@ static struct mfd_cell wm8311_devs[] = {  	},  }; -static struct mfd_cell wm8312_devs[] = { +static const struct mfd_cell wm8312_devs[] = {  	{  		.name = "wm831x-backup",  	}, @@ -1449,7 +1449,7 @@ static struct mfd_cell wm8312_devs[] = {  	},  }; -static struct mfd_cell wm8320_devs[] = { +static const struct mfd_cell wm8320_devs[] = {  	{  		.name = "wm831x-backup",  	}, @@ -1578,7 +1578,7 @@ static struct mfd_cell wm8320_devs[] = {  	},  }; -static struct mfd_cell touch_devs[] = { +static const struct mfd_cell touch_devs[] = {  	{  		.name = "wm831x-touch",  		.num_resources = ARRAY_SIZE(wm831x_touch_resources), @@ -1586,7 +1586,7 @@ static struct mfd_cell touch_devs[] = {  	},  }; -static struct mfd_cell rtc_devs[] = { +static const struct mfd_cell rtc_devs[] = {  	{  		.name = "wm831x-rtc",  		.num_resources = ARRAY_SIZE(wm831x_rtc_resources), @@ -1594,7 +1594,7 @@ static struct mfd_cell rtc_devs[] = {  	},  }; -static struct mfd_cell backlight_devs[] = { +static const struct mfd_cell backlight_devs[] = {  	{  		.name = "wm831x-backlight",  	}, diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 2b29caebc9c..a4cbefe5430 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -64,11 +64,13 @@ static int wm831x_i2c_suspend(struct device *dev)  	return wm831x_device_suspend(wm831x);  } -static void wm831x_i2c_shutdown(struct i2c_client *i2c) +static int wm831x_i2c_poweroff(struct device *dev)  { -	struct wm831x *wm831x = i2c_get_clientdata(i2c); +	struct wm831x *wm831x = dev_get_drvdata(dev);  	wm831x_device_shutdown(wm831x); + +	return 0;  }  static const struct i2c_device_id wm831x_i2c_id[] = { @@ -85,6 +87,7 @@ MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);  static const struct dev_pm_ops wm831x_pm_ops = {  	.suspend = wm831x_i2c_suspend, +	.poweroff = wm831x_i2c_poweroff,  };  static struct i2c_driver wm831x_i2c_driver = { @@ -95,7 +98,6 @@ static struct i2c_driver wm831x_i2c_driver = {  	},  	.probe = wm831x_i2c_probe,  	.remove = wm831x_i2c_remove, -	.shutdown = wm831x_i2c_shutdown,  	.id_table = wm831x_i2c_id,  }; diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 07de3cc5a0d..b8a5e3b34ec 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -66,16 +66,19 @@ static int wm831x_spi_suspend(struct device *dev)  	return wm831x_device_suspend(wm831x);  } -static void wm831x_spi_shutdown(struct spi_device *spi) +static int wm831x_spi_poweroff(struct device *dev)  { -	struct wm831x *wm831x = spi_get_drvdata(spi); +	struct wm831x *wm831x = dev_get_drvdata(dev);  	wm831x_device_shutdown(wm831x); + +	return 0;  }  static const struct dev_pm_ops wm831x_spi_pm = {  	.freeze = wm831x_spi_suspend,  	.suspend = wm831x_spi_suspend, +	.poweroff = wm831x_spi_poweroff,  };  static const struct spi_device_id wm831x_spi_ids[] = { @@ -99,7 +102,6 @@ static struct spi_driver wm831x_spi_driver = {  	.id_table	= wm831x_spi_ids,  	.probe		= wm831x_spi_probe,  	.remove		= wm831x_spi_remove, -	.shutdown	= wm831x_spi_shutdown,  };  static int __init wm831x_spi_init(void) diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 7c1ae24605d..4ab527f5c53 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -14,7 +14,6 @@  #include <linux/kernel.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/bug.h>  #include <linux/device.h> diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index 624ff90501c..cd01f7962df 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -14,7 +14,6 @@  #include <linux/kernel.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/bug.h>  #include <linux/device.h>  #include <linux/interrupt.h> diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index d66d256551f..c6fb5d16ca0 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -64,7 +64,7 @@ EXPORT_SYMBOL_GPL(wm8400_block_read);  static int wm8400_register_codec(struct wm8400 *wm8400)  { -	struct mfd_cell cell = { +	const struct mfd_cell cell = {  		.name = "wm8400-codec",  		.platform_data = wm8400,  		.pdata_size = sizeof(*wm8400), @@ -161,31 +161,19 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,  			    const struct i2c_device_id *id)  {  	struct wm8400 *wm8400; -	int ret;  	wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL); -	if (wm8400 == NULL) { -		ret = -ENOMEM; -		goto err; -	} +	if (!wm8400) +		return -ENOMEM;  	wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config); -	if (IS_ERR(wm8400->regmap)) { -		ret = PTR_ERR(wm8400->regmap); -		goto err; -	} +	if (IS_ERR(wm8400->regmap)) +		return PTR_ERR(wm8400->regmap);  	wm8400->dev = &i2c->dev;  	i2c_set_clientdata(i2c, wm8400); -	ret = wm8400_init(wm8400, dev_get_platdata(&i2c->dev)); -	if (ret != 0) -		goto err; - -	return 0; - -err: -	return ret; +	return wm8400_init(wm8400, dev_get_platdata(&i2c->dev));  }  static int wm8400_i2c_remove(struct i2c_client *i2c) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index e1c283e6d4e..e6fab94e2c8 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -33,85 +33,7 @@  #include "wm8994.h" -/** - * wm8994_reg_read: Read a single WM8994 register. - * - * @wm8994: Device to read from. - * @reg: Register to read. - */ -int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg) -{ -	unsigned int val; -	int ret; - -	ret = regmap_read(wm8994->regmap, reg, &val); - -	if (ret < 0) -		return ret; -	else -		return val; -} -EXPORT_SYMBOL_GPL(wm8994_reg_read); - -/** - * wm8994_bulk_read: Read multiple WM8994 registers - * - * @wm8994: Device to read from - * @reg: First register - * @count: Number of registers - * @buf: Buffer to fill.  The data will be returned big endian. - */ -int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, -		     int count, u16 *buf) -{ -	return regmap_bulk_read(wm8994->regmap, reg, buf, count); -} - -/** - * wm8994_reg_write: Write a single WM8994 register. - * - * @wm8994: Device to write to. - * @reg: Register to write to. - * @val: Value to write. - */ -int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg, -		     unsigned short val) -{ -	return regmap_write(wm8994->regmap, reg, val); -} -EXPORT_SYMBOL_GPL(wm8994_reg_write); - -/** - * wm8994_bulk_write: Write multiple WM8994 registers - * - * @wm8994: Device to write to - * @reg: First register - * @count: Number of registers - * @buf: Buffer to write from.  Data must be big-endian formatted. - */ -int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg, -		      int count, const u16 *buf) -{ -	return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16)); -} -EXPORT_SYMBOL_GPL(wm8994_bulk_write); - -/** - * wm8994_set_bits: Set the value of a bitfield in a WM8994 register - * - * @wm8994: Device to write to. - * @reg: Register to write to. - * @mask: Mask of bits to set. - * @val: Value to set (unshifted) - */ -int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg, -		    unsigned short mask, unsigned short val) -{ -	return regmap_update_bits(wm8994->regmap, reg, mask, val); -} -EXPORT_SYMBOL_GPL(wm8994_set_bits); - -static struct mfd_cell wm8994_regulator_devs[] = { +static const struct mfd_cell wm8994_regulator_devs[] = {  	{  		.name = "wm8994-ldo",  		.id = 1, @@ -140,7 +62,7 @@ static struct resource wm8994_gpio_resources[] = {  	},  }; -static struct mfd_cell wm8994_devs[] = { +static const struct mfd_cell wm8994_devs[] = {  	{  		.name = "wm8994-codec",  		.num_resources = ARRAY_SIZE(wm8994_codec_resources), @@ -714,7 +636,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,  	if (i2c->dev.of_node) {  		of_id = of_match_device(wm8994_of_match, &i2c->dev);  		if (of_id) -			wm8994->type = (int)of_id->data; +			wm8994->type = (enum wm8994_type)of_id->data;  	} else {  		wm8994->type = id->driver_data;  	} diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index 5aa80768777..c7a81da64ee 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -174,10 +174,10 @@ static const struct reg_default wm8997_reg_default[] = {  	{ 0x00000062, 0x01FF },    /* R98    - Sample Rate Sequence Select 2 */  	{ 0x00000063, 0x01FF },    /* R99    - Sample Rate Sequence Select 3 */  	{ 0x00000064, 0x01FF },    /* R100   - Sample Rate Sequence Select 4 */ -	{ 0x00000068, 0x01FF },    /* R104   - Always On Triggers Sequence Select 1 */ -	{ 0x00000069, 0x01FF },    /* R105   - Always On Triggers Sequence Select 2 */ -	{ 0x0000006A, 0x01FF },    /* R106   - Always On Triggers Sequence Select 3 */ -	{ 0x0000006B, 0x01FF },    /* R107   - Always On Triggers Sequence Select 4 */ +	{ 0x00000068, 0x01FF },    /* R104   - Always On Triggers Sequence Select 3 */ +	{ 0x00000069, 0x01FF },    /* R105   - Always On Triggers Sequence Select 4 */ +	{ 0x0000006A, 0x01FF },    /* R106   - Always On Triggers Sequence Select 5 */ +	{ 0x0000006B, 0x01FF },    /* R107   - Always On Triggers Sequence Select 6 */  	{ 0x00000070, 0x0000 },    /* R112   - Comfort Noise Generator */  	{ 0x00000090, 0x0000 },    /* R144   - Haptics Control 1 */  	{ 0x00000091, 0x7FFF },    /* R145   - Haptics Control 2 */ @@ -814,10 +814,10 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:  	case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:  	case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4: -	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1: -	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:  	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: +	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5: +	case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:  	case ARIZONA_COMFORT_NOISE_GENERATOR:  	case ARIZONA_HAPTICS_CONTROL_1:  	case ARIZONA_HAPTICS_CONTROL_2: @@ -846,6 +846,7 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_RATE_ESTIMATOR_3:  	case ARIZONA_RATE_ESTIMATOR_4:  	case ARIZONA_RATE_ESTIMATOR_5: +	case ARIZONA_DYNAMIC_FREQUENCY_SCALING_1:  	case ARIZONA_FLL1_CONTROL_1:  	case ARIZONA_FLL1_CONTROL_2:  	case ARIZONA_FLL1_CONTROL_3: @@ -880,6 +881,7 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg)  	case ARIZONA_FLL2_GPIO_CLOCK:  	case ARIZONA_MIC_CHARGE_PUMP_1:  	case ARIZONA_LDO1_CONTROL_1: +	case ARIZONA_LDO1_CONTROL_2:  	case ARIZONA_LDO2_CONTROL_1:  	case ARIZONA_MIC_BIAS_CTRL_1:  	case ARIZONA_MIC_BIAS_CTRL_2:  | 
