diff options
212 files changed, 6699 insertions, 2056 deletions
diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt new file mode 100644 index 000000000000..f64de95a8e8b --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt @@ -0,0 +1,51 @@ +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver + +Required properties: + - compatible: value should be one of the following: + "atmel,sama5d3-hlcdc" + - reg: base address and size of the HLCDC device registers. + - clock-names: the name of the 3 clocks requested by the HLCDC device. + Should contain "periph_clk", "sys_clk" and "slow_clk". + - clocks: should contain the 3 clocks requested by the HLCDC device. + - interrupts: should contain the description of the HLCDC interrupt line + +The HLCDC IP exposes two subdevices: + - a PWM chip: see ../pwm/atmel-hlcdc-pwm.txt + - a Display Controller: see ../drm/atmel-hlcdc-dc.txt + +Example: + + hlcdc: hlcdc@f0030000 { + compatible = "atmel,sama5d3-hlcdc"; + reg = <0xf0030000 0x2000>; + clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>; + clock-names = "periph_clk","sys_clk", "slow_clk"; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>; + status = "disabled"; + + hlcdc-display-controller { + compatible = "atmel,hlcdc-display-controller"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + hlcdc_panel_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&panel_input>; + }; + }; + }; + + hlcdc_pwm: hlcdc-pwm { + compatible = "atmel,hlcdc-pwm"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd_pwm>; + #pwm-cells = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/max77686.txt b/Documentation/devicetree/bindings/mfd/max77686.txt index 678f3cf0b8f0..75fdfaf41831 100644 --- a/Documentation/devicetree/bindings/mfd/max77686.txt +++ b/Documentation/devicetree/bindings/mfd/max77686.txt @@ -34,6 +34,12 @@ to get matched with their hardware counterparts as follow: -BUCKn : for BUCKs, where n can lie in range 1 to 9. example: BUCK1, BUCK5, BUCK9. + Regulators which can be turned off during system suspend: + -LDOn : 2, 6-8, 10-12, 14-16, + -BUCKn : 1-4. + Use standard regulator bindings for it ('regulator-off-in-suspend'). + + Example: max77686@09 { diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt index 11921cc417bf..01e9f30fe678 100644 --- a/Documentation/devicetree/bindings/mfd/max77693.txt +++ b/Documentation/devicetree/bindings/mfd/max77693.txt @@ -27,6 +27,20 @@ Optional properties: [*] refer Documentation/devicetree/bindings/regulator/regulator.txt +- haptic : The MAX77693 haptic device utilises a PWM controlled motor to provide + users with tactile feedback. PWM period and duty-cycle are varied in + order to provide the approprite level of feedback. + + Required properties: + - compatible : Must be "maxim,max77693-hpatic" + - haptic-supply : power supply for the haptic motor + [*] refer Documentation/devicetree/bindings/regulator/regulator.txt + - pwms : phandle to the physical PWM(Pulse Width Modulation) device. + PWM properties should be named "pwms". And number of cell is different + for each pwm device. + To get more informations, please refer to documentaion. + [*] refer Documentation/devicetree/bindings/pwm/pwm.txt + Example: max77693@66 { compatible = "maxim,max77693"; @@ -52,4 +66,11 @@ Example: regulator-boot-on; }; }; + + haptic { + compatible = "maxim,max77693-haptic"; + haptic-supply = <&haptic_supply>; + pwms = <&pwm 0 40000 0>; + pwm-names = "haptic"; + }; }; diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt index 0e4026a6cbbf..57a045016fca 100644 --- a/Documentation/devicetree/bindings/mfd/s2mps11.txt +++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt @@ -1,5 +1,5 @@ -* Samsung S2MPS11, S2MPS14 and S2MPU02 Voltage and Current Regulator +* Samsung S2MPS11, S2MPS13, S2MPS14 and S2MPU02 Voltage and Current Regulator The Samsung S2MPS11 is a multi-function device which includes voltage and current regulators, RTC, charger controller and other sub-blocks. It is @@ -7,8 +7,8 @@ interfaced to the host controller using an I2C interface. Each sub-block is addressed by the host system using different I2C slave addresses. Required properties: -- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps14-pmic" - or "samsung,s2mpu02-pmic". +- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps13-pmic" + or "samsung,s2mps14-pmic" or "samsung,s2mpu02-pmic". - reg: Specifies the I2C slave address of the pmic block. It should be 0x66. Optional properties: @@ -17,8 +17,8 @@ Optional properties: - interrupts: Interrupt specifiers for interrupt sources. Optional nodes: -- clocks: s2mps11 and s5m8767 provide three(AP/CP/BT) buffered 32.768 KHz - outputs, so to register these as clocks with common clock framework +- clocks: s2mps11, s2mps13 and s5m8767 provide three(AP/CP/BT) buffered 32.768 + KHz outputs, so to register these as clocks with common clock framework instantiate a sub-node named "clocks". It uses the common clock binding documented in : [Documentation/devicetree/bindings/clock/clock-bindings.txt] @@ -30,12 +30,12 @@ Optional nodes: the clock which they consume. Clock ID Devices ---------------------------------------------------------- - 32KhzAP 0 S2MPS11, S2MPS14, S5M8767 - 32KhzCP 1 S2MPS11, S5M8767 - 32KhzBT 2 S2MPS11, S2MPS14, S5M8767 + 32KhzAP 0 S2MPS11, S2MPS13, S2MPS14, S5M8767 + 32KhzCP 1 S2MPS11, S2MPS13, S5M8767 + 32KhzBT 2 S2MPS11, S2MPS13, S2MPS14, S5M8767 - - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps14-clk", - "samsung,s5m8767-clk" + - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps13-clk", + "samsung,s2mps14-clk", "samsung,s5m8767-clk" - regulators: The regulators of s2mps11 that have to be instantiated should be included in a sub-node named 'regulators'. Regulator nodes included in this @@ -81,12 +81,14 @@ as per the datasheet of s2mps11. - LDOn - valid values for n are: - S2MPS11: 1 to 38 + - S2MPS13: 1 to 40 - S2MPS14: 1 to 25 - S2MPU02: 1 to 28 - Example: LDO1, LDO2, LDO28 - BUCKn - valid values for n are: - S2MPS11: 1 to 10 + - S2MPS13: 1 to 10 - S2MPS14: 1 to 5 - S2MPU02: 1 to 7 - Example: BUCK1, BUCK2, BUCK9 diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt index 6cd3525d0e09..ee4fc0576c7d 100644 --- a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt @@ -18,6 +18,10 @@ Required Properties: specific extensions. - "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420 specific extensions. + - "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7 + specific extensions. + - "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7 + specific extensions having an SMU. * samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface unit (ciu) clock. This property is applicable only for Exynos5 SoC's and diff --git a/Documentation/devicetree/bindings/mmc/img-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/img-dw-mshc.txt new file mode 100644 index 000000000000..85de99fcaa2f --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/img-dw-mshc.txt @@ -0,0 +1,29 @@ +* Imagination specific extensions to the Synopsys Designware Mobile Storage + Host Controller + +The Synopsys designware mobile storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core Synopsys dw mshc controller properties described +by synopsys-dw-mshc.txt and the properties used by the Imagination specific +extensions to the Synopsys Designware Mobile Storage Host Controller. + +Required Properties: + +* compatible: should be + - "img,pistachio-dw-mshc": for Pistachio SoCs + +Example: + + mmc@18142000 { + compatible = "img,pistachio-dw-mshc"; + reg = <0x18142000 0x400>; + interrupts = <GIC_SHARED 39 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&system_clk>, <&sdhost_clk>; + clock-names = "biu", "ciu"; + + fifo-depth = <0x20>; + bus-width = <4>; + num-slots = <1>; + disable-wp; + }; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt index 86223c3eda90..4dd6deb90719 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt @@ -12,6 +12,10 @@ Required properties: * for "marvell,armada-380-sdhci", two register areas. The first one for the SDHCI registers themselves, and the second one for the AXI/Mbus bridge registers of the SDHCI unit. +- clocks: Array of clocks required for SDHCI; requires at least one for + I/O clock. +- clock-names: Array of names corresponding to clocks property; shall be + "io" for I/O clock and "core" for optional core clock. Optional properties: - mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning. @@ -23,6 +27,8 @@ sdhci@d4280800 { reg = <0xd4280800 0x800>; bus-width = <8>; interrupts = <27>; + clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>; + clock-names = "io", "core"; non-removable; mrvl,clk-delay-cycles = <31>; }; @@ -32,5 +38,6 @@ sdhci@d8000 { reg = <0xd8000 0x1000>, <0xdc000 0x100>; interrupts = <0 25 0x4>; clocks = <&gateclk 17>; + clock-names = "io"; mrvl,clk-delay-cycles = <0x1F>; }; diff --git a/MAINTAINERS b/MAINTAINERS index 0ff630de8a6d..c721042e7e45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1828,7 +1828,7 @@ F: include/net/ax25.h F: net/ax25/ AZ6007 DVB DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -2198,7 +2198,7 @@ F: Documentation/filesystems/btrfs.txt F: fs/btrfs/ BTTV VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -2719,7 +2719,7 @@ F: drivers/media/common/cx2341x* F: include/media/cx2341x* CX88 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -3402,7 +3402,7 @@ F: fs/ecryptfs/ EDAC-CORE M: Doug Thompson <dougthompson@xmission.com> M: Borislav Petkov <bp@alien8.de> -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Supported @@ -3451,7 +3451,7 @@ S: Maintained F: drivers/edac/e7xxx_edac.c EDAC-GHES -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained @@ -3479,21 +3479,21 @@ S: Maintained F: drivers/edac/i5000_edac.c EDAC-I5400 -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i5400_edac.c EDAC-I7300 -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i7300_edac.c EDAC-I7CORE -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained @@ -3536,7 +3536,7 @@ S: Maintained F: drivers/edac/r82600_edac.c EDAC-SBRIDGE -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained @@ -3596,7 +3596,7 @@ S: Maintained F: drivers/net/ethernet/ibm/ehea/ EM28XX VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -5962,7 +5962,7 @@ S: Maintained F: drivers/media/radio/radio-maxiradio* MEDIA INPUT INFRASTRUCTURE (V4L/DVB) -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> P: LinuxTV.org Project L: linux-media@vger.kernel.org W: http://linuxtv.org @@ -8013,7 +8013,7 @@ S: Odd Fixes F: drivers/media/i2c/saa6588* SAA7134 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -8471,7 +8471,7 @@ S: Maintained F: drivers/media/radio/si4713/radio-usb-si4713.c SIANO DVB DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -8682,7 +8682,9 @@ S: Maintained F: drivers/leds/leds-net48xx.c SOFTLOGIC 6x10 MPEG CODEC -M: Ismael Luceno <ismael.luceno@corp.bluecherry.net> +M: Bluecherry Maintainers <maintainers@bluecherrydvr.com> +M: Andrey Utkin <andrey.utkin@corp.bluecherry.net> +M: Andrey Utkin <andrey.krieger.utkin@gmail.com> L: linux-media@vger.kernel.org S: Supported F: drivers/media/pci/solo6x10/ @@ -9156,7 +9158,7 @@ S: Maintained F: drivers/media/i2c/tda9840* TEA5761 TUNER DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -9164,7 +9166,7 @@ S: Odd fixes F: drivers/media/tuners/tea5761.* TEA5767 TUNER DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -9476,7 +9478,7 @@ F: include/linux/shmem_fs.h F: mm/shmem.c TM6000 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -10297,7 +10299,7 @@ S: Maintained F: arch/x86/kernel/cpu/mcheck/* XC2028/3028 TUNER DRIVER -M: Mauro Carvalho Chehab <m.chehab@samsung.com> +M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 18 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Diseased Newt # *DOCUMENTATION* diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 21ab782cc8e9..06ecbafd01ee 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -19,6 +19,7 @@ #include <linux/i2c-gpio.h> #include <linux/atmel-mci.h> #include <linux/platform_data/crypto-atmel.h> +#include <linux/platform_data/mmc-atmel-mci.h> #include <linux/platform_data/at91_adc.h> @@ -30,7 +31,6 @@ #include <mach/at91_matrix.h> #include <mach/at91sam9_smc.h> #include <linux/platform_data/dma-atmel.h> -#include <mach/atmel-mci.h> #include <mach/hardware.h> #include <media/atmel-isi.h> diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index 97767a27ca9d..e0ad64fde20e 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -21,8 +21,10 @@ #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/usb/musb.h> +#include <linux/mmc/host.h> #include <linux/platform_data/spi-omap2-mcspi.h> #include <linux/platform_data/mtd-onenand-omap2.h> +#include <linux/platform_data/mmc-omap.h> #include <linux/mfd/menelaus.h> #include <sound/tlv320aic3x.h> diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index ddfc8df83c6a..3d5040f82e90 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -484,7 +484,7 @@ static struct omap_mux_partition *partition; * Current flows to eMMC when eMMC is off and the data lines are pulled up, * so pull them down. N.B. we pull 8 lines because we are using 8 lines. */ -static void rx51_mmc2_remux(struct device *dev, int slot, int power_on) +static void rx51_mmc2_remux(struct device *dev, int power_on) { if (power_on) omap_mux_write_array(partition, rx51_mmc2_on_mux); @@ -500,7 +500,6 @@ static struct omap2_hsmmc_info mmc[] __initdata = { .cover_only = true, .gpio_cd = 160, .gpio_wp = -EINVAL, - .power_saving = true, }, { .name = "internal", @@ -510,7 +509,6 @@ static struct omap2_hsmmc_info mmc[] __initdata = { .gpio_cd = -EINVAL, .gpio_wp = -EINVAL, .nonremovable = true, - .power_saving = true, .remux = rx51_mmc2_remux, }, {} /* Terminator */ diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index 07d4c7b35754..dc6e79c4484a 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -14,14 +14,15 @@ #include <linux/string.h> #include <linux/delay.h> #include <linux/gpio.h> +#include <linux/mmc/host.h> #include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/hsmmc-omap.h> #include "soc.h" #include "omap_device.h" #include "omap-pm.h" #include "mux.h" -#include "mmc.h" #include "hsmmc.h" #include "control.h" @@ -32,25 +33,14 @@ static u16 control_devconf1_offset; #define HSMMC_NAME_LEN 9 -#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) - -static int hsmmc_get_context_loss(struct device *dev) -{ - return omap_pm_get_dev_context_loss_count(dev); -} - -#else -#define hsmmc_get_context_loss NULL -#endif - -static void omap_hsmmc1_before_set_reg(struct device *dev, int slot, - int power_on, int vdd) +static void omap_hsmmc1_before_set_reg(struct device *dev, + int power_on, int vdd) { u32 reg, prog_io; - struct omap_mmc_platform_data *mmc = dev->platform_data; + struct omap_hsmmc_platform_data *mmc = dev->platform_data; - if (mmc->slots[0].remux) - mmc->slots[0].remux(dev, slot, power_on); + if (mmc->remux) + mmc->remux(dev, power_on); /* * Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the @@ -72,7 +62,7 @@ static void omap_hsmmc1_before_set_reg(struct device *dev, int slot, omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1); } - if (mmc->slots[0].internal_clock) { + if (mmc->internal_clock) { reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); reg |= OMAP2_MMCSDIO1ADPCLKISEL; omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0); @@ -96,8 +86,7 @@ static void omap_hsmmc1_before_set_reg(struct device *dev, int slot, } } -static void omap_hsmmc1_after_set_reg(struct device *dev, int slot, - int power_on, int vdd) +static void omap_hsmmc1_after_set_reg(struct device *dev, int power_on, int vdd) { u32 reg; @@ -120,34 +109,32 @@ static void omap_hsmmc1_after_set_reg(struct device *dev, int slot, } } -static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc) +static void hsmmc2_select_input_clk_src(struct omap_hsmmc_platform_data *mmc) { u32 reg; reg = omap_ctrl_readl(control_devconf1_offset); - if (mmc->slots[0].internal_clock) + if (mmc->internal_clock) reg |= OMAP2_MMCSDIO2ADPCLKISEL; else reg &= ~OMAP2_MMCSDIO2ADPCLKISEL; omap_ctrl_writel(reg, control_devconf1_offset); } -static void hsmmc2_before_set_reg(struct device *dev, int slot, - int power_on, int vdd) +static void hsmmc2_before_set_reg(struct device *dev, int power_on, int vdd) { - struct omap_mmc_platform_data *mmc = dev->platform_data; + struct omap_hsmmc_platform_data *mmc = dev->platform_data; - if (mmc->slots[0].remux) - mmc->slots[0].remux(dev, slot, power_on); + if (mmc->remux) + mmc->remux(dev, power_on); if (power_on) hsmmc2_select_input_clk_src(mmc); } -static int am35x_hsmmc2_set_power(struct device *dev, int slot, - int power_on, int vdd) +static int am35x_hsmmc2_set_power(struct device *dev, int power_on, int vdd) { - struct omap_mmc_platform_data *mmc = dev->platform_data; + struct omap_hsmmc_platform_data *mmc = dev->platform_data; if (power_on) hsmmc2_select_input_clk_src(mmc); @@ -155,23 +142,22 @@ static int am35x_hsmmc2_set_power(struct device *dev, int slot, return 0; } -static int nop_mmc_set_power(struct device *dev, int slot, int power_on, - int vdd) +static int nop_mmc_set_power(struct device *dev, int power_on, int vdd) { return 0; } -static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, - int controller_nr) +static inline void omap_hsmmc_mux(struct omap_hsmmc_platform_data + *mmc_controller, int controller_nr) { - if (gpio_is_valid(mmc_controller->slots[0].switch_pin) && - (mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES)) - omap_mux_init_gpio(mmc_controller->slots[0].switch_pin, - OMAP_PIN_INPUT_PULLUP); - if (gpio_is_valid(mmc_controller->slots[0].gpio_wp) && - (mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES)) - omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp, - OMAP_PIN_INPUT_PULLUP); + if (gpio_is_valid(mmc_controller->switch_pin) && + (mmc_controller->switch_pin < OMAP_MAX_GPIO_LINES)) + omap_mux_init_gpio(mmc_controller->switch_pin, + OMAP_PIN_INPUT_PULLUP); + if (gpio_is_valid(mmc_controller->gpio_wp) && + (mmc_controller->gpio_wp < OMAP_MAX_GPIO_LINES)) + omap_mux_init_gpio(mmc_controller->gpio_wp, + OMAP_PIN_INPUT_PULLUP); if (cpu_is_omap34xx()) { if (controller_nr == 0) { omap_mux_init_signal("sdmmc1_clk", @@ -180,7 +166,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("sdmmc1_dat0", OMAP_PIN_INPUT_PULLUP); - if (mmc_controller->slots[0].caps & + if (mmc_controller->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) { omap_mux_init_signal("sdmmc1_dat1", OMAP_PIN_INPUT_PULLUP); @@ -189,7 +175,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, omap_mux_init_signal("sdmmc1_dat3", OMAP_PIN_INPUT_PULLUP); } - if (mmc_controller->slots[0].caps & + if (mmc_controller->caps & MMC_CAP_8_BIT_DATA) { omap_mux_init_signal("sdmmc1_dat4", OMAP_PIN_INPUT_PULLUP); @@ -214,7 +200,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, * For 8 wire configurations, Lines DAT4, 5, 6 and 7 * need to be muxed in the board-*.c files */ - if (mmc_controller->slots[0].caps & + if (mmc_controller->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) { omap_mux_init_signal("sdmmc2_dat1", OMAP_PIN_INPUT_PULLUP); @@ -223,7 +209,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, omap_mux_init_signal("sdmmc2_dat3", OMAP_PIN_INPUT_PULLUP); } - if (mmc_controller->slots[0].caps & + if (mmc_controller->caps & MMC_CAP_8_BIT_DATA) { omap_mux_init_signal("sdmmc2_dat4.sdmmc2_dat4", OMAP_PIN_INPUT_PULLUP); @@ -243,7 +229,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, } static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, - struct omap_mmc_platform_data *mmc) + struct omap_hsmmc_platform_data *mmc) { char *hc_name; @@ -259,38 +245,22 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, else snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i", c->mmc, 1); - mmc->slots[0].name = hc_name; - mmc->nr_slots = 1; - mmc->slots[0].caps = c->caps; - mmc->slots[0].pm_caps = c->pm_caps; - mmc->slots[0].internal_clock = !c->ext_clock; - mmc->max_freq = c->max_freq; + mmc->name = hc_name; + mmc->caps = c->caps; + mmc->internal_clock = !c->ext_clock; mmc->reg_offset = 0; - mmc->get_context_loss_count = hsmmc_get_context_loss; - mmc->slots[0].switch_pin = c->gpio_cd; - mmc->slots[0].gpio_wp = c->gpio_wp; + mmc->switch_pin = c->gpio_cd; + mmc->gpio_wp = c->gpio_wp; - mmc->slots[0].remux = c->remux; - mmc->slots[0].init_card = c->init_card; + mmc->remux = c->remux; + mmc->init_card = c->init_card; if (c->cover_only) - mmc->slots[0].cover = 1; + mmc->cover = 1; if (c->nonremovable) - mmc->slots[0].nonremovable = 1; - - if (c->power_saving) - mmc->slots[0].power_saving = 1; - - if (c->no_off) - mmc->slots[0].no_off = 1; - - if (c->no_off_init) - mmc->slots[0].no_regulator_off_init = c->no_off_init; - - if (c->vcc_aux_disable_is_sleep) - mmc->slots[0].vcc_aux_disable_is_sleep = 1; + mmc->nonremovable = 1; /* * NOTE: MMC slots should have a Vcc regulator set up. @@ -300,42 +270,42 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, * temporary HACK: ocr_mask instead of fixed supply */ if (soc_is_am35xx()) - mmc->slots[0].ocr_mask = MMC_VDD_165_195 | + mmc->ocr_mask = MMC_VDD_165_195 | MMC_VDD_26_27 | MMC_VDD_27_28 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32; else - mmc->slots[0].ocr_mask = c->ocr_mask; + mmc->ocr_mask = c->ocr_mask; if (!soc_is_am35xx()) - mmc->slots[0].features |= HSMMC_HAS_PBIAS; + mmc->features |= HSMMC_HAS_PBIAS; switch (c->mmc) { case 1: - if (mmc->slots[0].features & HSMMC_HAS_PBIAS) { + if (mmc->features & HSMMC_HAS_PBIAS) { /* on-chip level shifting via PBIAS0/PBIAS1 */ - mmc->slots[0].before_set_reg = + mmc->before_set_reg = omap_hsmmc1_before_set_reg; - mmc->slots[0].after_set_reg = + mmc->after_set_reg = omap_hsmmc1_after_set_reg; } if (soc_is_am35xx()) - mmc->slots[0].set_power = nop_mmc_set_power; + mmc->set_power = nop_mmc_set_power; /* OMAP3630 HSMMC1 supports only 4-bit */ if (cpu_is_omap3630() && (c->caps & MMC_CAP_8_BIT_DATA)) { c->caps &= ~MMC_CAP_8_BIT_DATA; c->caps |= MMC_CAP_4_BIT_DATA; - mmc->slots[0].caps = c->caps; + mmc->caps = c->caps; } break; case 2: if (soc_is_am35xx()) - mmc->slots[0].set_power = am35x_hsmmc2_set_power; + mmc->set_power = am35x_hsmmc2_set_power; if (c->ext_clock) c->transceiver = 1; @@ -343,17 +313,17 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, c->caps &= ~MMC_CAP_8_BIT_DATA; c->caps |= MMC_CAP_4_BIT_DATA; } - if (mmc->slots[0].features & HSMMC_HAS_PBIAS) { + if (mmc->features & HSMMC_HAS_PBIAS) { /* off-chip level shifting, or none */ - mmc->slots[0].before_set_reg = hsmmc2_before_set_reg; - mmc->slots[0].after_set_reg = NULL; + mmc->before_set_reg = hsmmc2_before_set_reg; + mmc->after_set_reg = NULL; } break; case 3: case 4: case 5: - mmc->slots[0].before_set_reg = NULL; - mmc->slots[0].after_set_reg = NULL; + mmc->before_set_reg = NULL; + mmc->after_set_reg = NULL; break; default: pr_err("MMC%d configuration not supported!\n", c->mmc); @@ -368,7 +338,7 @@ static int omap_hsmmc_done; void omap_hsmmc_late_init(struct omap2_hsmmc_info *c) { struct platform_device *pdev; - struct omap_mmc_platform_data *mmc_pdata; + struct omap_hsmmc_platform_data *mmc_pdata; int res; if (omap_hsmmc_done != 1) @@ -388,8 +358,8 @@ void omap_hsmmc_late_init(struct omap2_hsmmc_info *c) if (!mmc_pdata) continue; - mmc_pdata->slots[0].switch_pin = c->gpio_cd; - mmc_pdata->slots[0].gpio_wp = c->gpio_wp; + mmc_pdata->switch_pin = c->gpio_cd; + mmc_pdata->gpio_wp = c->gpio_wp; res = omap_device_register(pdev); if (res) @@ -408,12 +378,12 @@ static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo, struct omap_device *od; struct platform_device *pdev; char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN]; - struct omap_mmc_platform_data *mmc_data; - struct omap_mmc_dev_attr *mmc_dev_attr; + struct omap_hsmmc_platform_data *mmc_data; + struct omap_hsmmc_dev_attr *mmc_dev_attr; char *name; int res; - mmc_data = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL); + mmc_data = kzalloc(sizeof(*mmc_data), GFP_KERNEL); if (!mmc_data) { pr_err("Cannot allocate memory for mmc device!\n"); return; @@ -463,7 +433,7 @@ static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo, } res = platform_device_add_data(pdev, mmc_data, - sizeof(struct omap_mmc_platform_data)); + sizeof(struct omap_hsmmc_platform_data)); if (res) { pr_err("Could not add pdata for %s\n", name); goto put_pdev; @@ -489,7 +459,7 @@ put_pdev: platform_device_put(pdev); free_name: - kfree(mmc_data->slots[0].name); + kfree(mmc_data->name); free_mmc: kfree(mmc_data); diff --git a/arch/arm/mach-omap2/hsmmc.h b/arch/arm/mach-omap2/hsmmc.h index 7f2e790e0929..148cd9b15499 100644 --- a/arch/arm/mach-omap2/hsmmc.h +++ b/arch/arm/mach-omap2/hsmmc.h @@ -12,25 +12,18 @@ struct omap2_hsmmc_info { u8 mmc; /* controller 1/2/3 */ u32 caps; /* 4/8 wires and any additional host * capabilities OR'd (ref. linux/mmc/host.h) */ - u32 pm_caps; /* PM capabilities */ bool transceiver; /* MMC-2 option */ bool ext_clock; /* use external pin for input clock */ bool cover_only; /* No card detect - just cover switch */ bool nonremovable; /* Nonremovable e.g. eMMC */ - bool power_saving; /* Try to sleep or power off when possible */ - bool no_off; /* power_saving and power is not to go off */ - bool no_off_init; /* no power off when not in MMC sleep state */ - bool vcc_aux_disable_is_sleep; /* Regulator off remapped to sleep */ bool deferred; /* mmc needs a deferred probe */ int gpio_cd; /* or -EINVAL */ int gpio_wp; /* or -EINVAL */ char *name; /* or NULL for default */ struct platform_device *pdev; /* mmc controller instance */ int ocr_mask; /* temporary HACK */ - int max_freq; /* maximum clock, if constrained by external - * circuitry, or 0 for default */ /* Remux (pad configuration) when powering on/off */ - void (*remux)(struct device *dev, int slot, int power_on); + void (*remux)(struct device *dev, int power_on); /* init some special card */ void (*init_card)(struct mmc_card *card); }; diff --git a/arch/arm/mach-omap2/mmc.h b/arch/arm/mach-omap2/mmc.h index 0cd4b089da9c..30d39b97e7dd 100644 --- a/arch/arm/mach-omap2/mmc.h +++ b/arch/arm/mach-omap2/mmc.h @@ -1,5 +1,3 @@ -#include <linux/mmc/host.h> -#include <linux/platform_data/mmc-omap.h> #define OMAP24XX_NR_MMC 2 #define OMAP2420_MMC_SIZE OMAP1_MMC_SIZE @@ -7,14 +5,6 @@ #define OMAP4_MMC_REG_OFFSET 0x100 -#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) -void omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data); -#else -static inline void omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data) -{ -} -#endif - struct omap_hwmod; int omap_msdi_reset(struct omap_hwmod *oh); diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 16b20cedc38d..b7cb44abe49b 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -36,7 +36,6 @@ #include "soc.h" #include "iomap.h" #include "common.h" -#include "mmc.h" #include "prminst44xx.h" #include "prcm_mpu44xx.h" #include "omap4-sar-layout.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index c2555cb95e71..79127b35fe60 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -15,12 +15,12 @@ #include <linux/i2c-omap.h> #include <linux/platform_data/asoc-ti-mcbsp.h> +#include <linux/platform_data/hsmmc-omap.h> #include <linux/platform_data/spi-omap2-mcspi.h> #include <linux/omap-dma.h> #include <plat/dmtimer.h> #include "omap_hwmod.h" -#include "mmc.h" #include "l3_2xxx.h" #include "soc.h" @@ -372,7 +372,7 @@ static struct omap_hwmod_opt_clk omap2430_mmc1_opt_clks[] = { { .role = "dbck", .clk = "mmchsdb1_fck" }, }; -static struct omap_mmc_dev_attr mmc1_dev_attr = { +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c index a579b89ce9b7..cabc5695b504 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c @@ -15,10 +15,10 @@ */ #include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/hsmmc-omap.h> #include <linux/platform_data/spi-omap2-mcspi.h> #include "omap_hwmod.h" #include "i2c.h" -#include "mmc.h" #include "wd_timer.h" #include "cm33xx.h" #include "prm33xx.h" @@ -836,7 +836,7 @@ static struct omap_hwmod_class am33xx_mmc_hwmod_class = { }; /* mmc0 */ -static struct omap_mmc_dev_attr am33xx_mmc0_dev_attr = { +static struct omap_hsmmc_dev_attr am33xx_mmc0_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; @@ -854,7 +854,7 @@ struct omap_hwmod am33xx_mmc0_hwmod = { }; /* mmc1 */ -static struct omap_mmc_dev_attr am33xx_mmc1_dev_attr = { +static struct omap_hsmmc_dev_attr am33xx_mmc1_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; @@ -872,7 +872,7 @@ struct omap_hwmod am33xx_mmc1_hwmod = { }; /* mmc2 */ -static struct omap_mmc_dev_attr am33xx_mmc2_dev_attr = { +static struct omap_hsmmc_dev_attr am33xx_mmc2_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; struct omap_hwmod am33xx_mmc2_hwmod = { diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 6b406ca4bd3b..0cf7b563dcd1 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -27,7 +27,6 @@ #include "prm33xx.h" #include "prm-regbits-33xx.h" #include "i2c.h" -#include "mmc.h" #include "wd_timer.h" #include "omap_hwmod_33xx_43xx_common_data.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 2a78b093c0ce..11468eea3871 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -18,6 +18,7 @@ #include <linux/i2c-omap.h> #include <linux/power/smartreflex.h> #include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/hsmmc-omap.h> #include <linux/omap-dma.h> #include "l3_3xxx.h" @@ -37,7 +38,6 @@ #include "cm-regbits-34xx.h" #include "i2c.h" -#include "mmc.h" #include "wd_timer.h" #include "serial.h" @@ -1786,12 +1786,12 @@ static struct omap_hwmod_opt_clk omap34xx_mmc1_opt_clks[] = { { .role = "dbck", .clk = "omap_32k_fck", }, }; -static struct omap_mmc_dev_attr mmc1_dev_attr = { +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; /* See 35xx errata 2.1.1.128 in SPRZ278F */ -static struct omap_mmc_dev_attr mmc1_pre_es3_dev_attr = { +static struct omap_hsmmc_dev_attr mmc1_pre_es3_dev_attr = { .flags = (OMAP_HSMMC_SUPPORTS_DUAL_VOLT | OMAP_HSMMC_BROKEN_MULTIBLOCK_READ), }; @@ -1854,7 +1854,7 @@ static struct omap_hwmod_opt_clk omap34xx_mmc2_opt_clks[] = { }; /* See 35xx errata 2.1.1.128 in SPRZ278F */ -static struct omap_mmc_dev_attr mmc2_pre_es3_dev_attr = { +static struct omap_hsmmc_dev_attr mmc2_pre_es3_dev_attr = { .flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 44e5634bba34..d8a3cf1c1787 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -22,6 +22,7 @@ #include <linux/io.h> #include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/hsmmc-omap.h> #include <linux/power/smartreflex.h> #include <linux/i2c-omap.h> @@ -39,7 +40,6 @@ #include "prm44xx.h" #include "prm-regbits-44xx.h" #include "i2c.h" -#include "mmc.h" #include "wd_timer.h" /* Base offset for all OMAP4 interrupts external to MPUSS */ @@ -1952,7 +1952,7 @@ static struct omap_hwmod_dma_info omap44xx_mmc1_sdma_reqs[] = { }; /* mmc1 dev_attr */ -static struct omap_mmc_dev_attr mmc1_dev_attr = { +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c index 1103aa0e0d29..5ec786a76d3c 100644 --- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/hsmmc-omap.h> #include <linux/power/smartreflex.h> #include <linux/i2c-omap.h> @@ -33,7 +34,6 @@ #include "cm2_54xx.h" #include "prm54xx.h" #include "i2c.h" -#include "mmc.h" #include "wd_timer.h" /* Base offset for all OMAP5 interrupts external to MPUSS */ @@ -1269,7 +1269,7 @@ static struct omap_hwmod_opt_clk mmc1_opt_clks[] = { }; /* mmc1 dev_attr */ -static struct omap_mmc_dev_attr mmc1_dev_attr = { +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index 5684f112654b..711c97e90990 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/hsmmc-omap.h> #include <linux/power/smartreflex.h> #include <linux/i2c-omap.h> @@ -33,7 +34,6 @@ #include "cm2_7xx.h" #include "prm7xx.h" #include "i2c.h" -#include "mmc.h" #include "wd_timer.h" #include "soc.h" @@ -1301,7 +1301,7 @@ static struct omap_hwmod_opt_clk mmc1_opt_clks[] = { }; /* mmc1 dev_attr */ -static struct omap_mmc_dev_attr mmc1_dev_attr = { +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, }; diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 37b75602adf6..cc92cdb9994c 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -17,7 +17,7 @@ #include <linux/spi/spi.h> #include <linux/usb/atmel_usba_udc.h> -#include <mach/atmel-mci.h> +#include <linux/platform_data/mmc-atmel-mci.h> #include <linux/atmel-mci.h> #include <asm/io.h> diff --git a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h b/arch/avr32/mach-at32ap/include/mach/atmel-mci.h deleted file mode 100644 index 11d7f4b28dc8..000000000000 --- a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __MACH_ATMEL_MCI_H -#define __MACH_ATMEL_MCI_H - -#include <linux/platform_data/dma-dw.h> - -/** - * struct mci_dma_data - DMA data for MCI interface - */ -struct mci_dma_data { - struct dw_dma_slave sdata; -}; - -/* accessor macros */ -#define slave_data_ptr(s) (&(s)->sdata) -#define find_slave_dev(s) ((s)->sdata.dma_dev) - -#endif /* __MACH_ATMEL_MCI_H */ diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index dd1c24ceda50..3f51cf4e8f02 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -54,12 +54,8 @@ void s390_handle_mcck(void) */ local_irq_save(flags); local_mcck_disable(); - /* - * Ummm... Does this make sense at all? Copying the percpu struct - * and then zapping it one statement later? - */ - memcpy(&mcck, this_cpu_ptr(&cpu_mcck), sizeof(mcck)); - memset(&mcck, 0, sizeof(struct mcck_struct)); + mcck = *this_cpu_ptr(&cpu_mcck); + memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck)); clear_cpu_flag(CIF_MCCK_PENDING); local_mcck_enable(); local_irq_restore(flags); diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index be1e07d4b596..45abc363dd3e 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -76,7 +76,7 @@ suffix-$(CONFIG_KERNEL_XZ) := xz suffix-$(CONFIG_KERNEL_LZO) := lzo suffix-$(CONFIG_KERNEL_LZ4) := lz4 -RUN_SIZE = $(shell objdump -h vmlinux | \ +RUN_SIZE = $(shell $(OBJDUMP) -h vmlinux | \ perl $(srctree)/arch/x86/tools/calc_run_size.pl) quiet_cmd_mkpiggy = MKPIGGY $@ cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false ) diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index f04dbb3069b8..5caed1dd7ccf 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -21,6 +21,7 @@ const struct pci_device_id amd_nb_misc_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, {} @@ -30,6 +31,7 @@ EXPORT_SYMBOL(amd_nb_misc_ids); static const struct pci_device_id amd_nb_link_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) }, {} diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index 2ce9051174e6..08fe6e8a726e 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -465,6 +465,7 @@ static void mc_bp_resume(void) if (uci->valid && uci->mc) microcode_ops->apply_microcode(cpu); +#ifdef CONFIG_X86_64 else if (!uci->mc) /* * We might resume and not have applied late microcode but still @@ -473,6 +474,7 @@ static void mc_bp_resume(void) * applying patches early on the APs. */ load_ucode_ap(); +#endif } static struct syscore_ops mc_syscore_ops = { diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 0984232e429f..5cbd5d9ea61d 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -216,9 +216,10 @@ static int bio_integrity_process(struct bio *bio, { struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); struct blk_integrity_iter iter; - struct bio_vec *bv; + struct bvec_iter bviter; + struct bio_vec bv; struct bio_integrity_payload *bip = bio_integrity(bio); - unsigned int i, ret = 0; + unsigned int ret = 0; void *prot_buf = page_address(bip->bip_vec->bv_page) + bip->bip_vec->bv_offset; @@ -227,11 +228,11 @@ static int bio_integrity_process(struct bio *bio, iter.seed = bip_get_seed(bip); iter.prot_buf = prot_buf; - bio_for_each_segment_all(bv, bio, i) { - void *kaddr = kmap_atomic(bv->bv_page); + bio_for_each_segment(bv, bio, bviter) { + void *kaddr = kmap_atomic(bv.bv_page); - iter.data_buf = kaddr + bv->bv_offset; - iter.data_size = bv->bv_len; + iter.data_buf = kaddr + bv.bv_offset; + iter.data_size = bv.bv_len; ret = proc_fn(&iter); if (ret) { diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 807a88a0f394..9d75ead2a1f9 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1164,7 +1164,8 @@ static bool acpi_video_device_in_dod(struct acpi_video_device *device) return true; for (i = 0; i < video->attached_count; i++) { - if (video->attached_array[i].bind_info == device) + if ((video->attached_array[i].value.int_val & 0xfff) == + (device->device_id & 0xfff)) return true; } diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e45f83789809..49f1e6890587 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -321,6 +321,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */ { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */ { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */ + { PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */ + { PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */ @@ -492,6 +495,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731 */ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi }, + { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi }, /* Enmotus */ { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 07bc7e4dbd04..65071591b143 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1488,7 +1488,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) host_priv->csr_base = csr_base; irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); - if (irq < 0) { + if (!irq) { dev_err(&ofdev->dev, "invalid irq from platform\n"); goto error_exit_with_cleanup; } diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index b7797fb12e12..7bb13af8e214 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -23,6 +23,7 @@ #include <linux/clk-provider.h> #include <linux/platform_device.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s5m8767.h> #include <linux/mfd/samsung/core.h> @@ -120,6 +121,24 @@ static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = { }, }; +static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = { + [S2MPS11_CLK_AP] = { + .name = "s2mps13_ap", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_CP] = { + .name = "s2mps13_cp", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_BT] = { + .name = "s2mps13_bt", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = { [S2MPS11_CLK_AP] = { .name = "s2mps14_ap", @@ -184,6 +203,10 @@ static int s2mps11_clk_probe(struct platform_device *pdev) s2mps11_reg = S2MPS11_REG_RTC_CTRL; clks_init = s2mps11_clks_init; break; + case S2MPS13X: + s2mps11_reg = S2MPS13_REG_RTCCTRL; + clks_init = s2mps13_clks_init; + break; case S2MPS14X: s2mps11_reg = S2MPS14_REG_RTCCTRL; clks_init = s2mps14_clks_init; @@ -279,6 +302,7 @@ static int s2mps11_clk_remove(struct platform_device *pdev) static const struct platform_device_id s2mps11_clk_id[] = { { "s2mps11-clk", S2MPS11X}, + { "s2mps13-clk", S2MPS13X}, { "s2mps14-clk", S2MPS14X}, { "s5m8767-clk", S5M8767X}, { }, diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 7072c2892d63..49c265255a07 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -61,14 +61,14 @@ config EDAC_DECODE_MCE has been initialized. config EDAC_MCE_INJ - tristate "Simple MCE injection interface over /sysfs" - depends on EDAC_DECODE_MCE + tristate "Simple MCE injection interface" + depends on EDAC_DECODE_MCE && DEBUG_FS default n help - This is a simple interface to inject MCEs over /sysfs and test - the MCE decoding code in EDAC. + This is a simple debugfs interface to inject MCEs and test different + aspects of the MCE handling code. - This is currently AMD-only. + WARNING: Do not even assume this interface is staying stable! config EDAC_MM_EDAC tristate "Main Memory EDAC (Error Detection And Correction) reporting" @@ -105,11 +105,11 @@ config EDAC_GHES In doubt, say 'Y'. config EDAC_AMD64 - tristate "AMD64 (Opteron, Athlon64) K8, F10h" - depends on EDAC_MM_EDAC && AMD_NB && X86_64 && EDAC_DECODE_MCE + tristate "AMD64 (Opteron, Athlon64)" + depends on EDAC_MM_EDAC && AMD_NB && EDAC_DECODE_MCE help Support for error detection and correction of DRAM ECC errors on - the AMD64 families of memory controllers (K8 and F10h) + the AMD64 families (>= K8) of memory controllers. config EDAC_AMD64_ERROR_INJECTION bool "Sysfs HW Error injection facilities" diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 359aa499b200..d40c69a04df7 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_EDAC) := edac_stub.o obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o -edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o +edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_core-y += edac_module.o edac_device_sysfs.o ifdef CONFIG_PCI diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index bbd65149cdb2..17638d7cf5c2 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -692,9 +692,19 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) { edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); - edac_dbg(1, " DIMM type: %sbuffered; all DIMMs support ECC: %s\n", - (dclr & BIT(16)) ? "un" : "", - (dclr & BIT(19)) ? "yes" : "no"); + if (pvt->dram_type == MEM_LRDDR3) { + u32 dcsm = pvt->csels[chan].csmasks[0]; + /* + * It's assumed all LRDIMMs in a DCT are going to be of + * same 'type' until proven otherwise. So, use a cs + * value of '0' here to get dcsm value. + */ + edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); + } + + edac_dbg(1, "All DIMMs support ECC:%s\n", + (dclr & BIT(19)) ? "yes" : "no"); + edac_dbg(1, " PAR/ERR parity: %s\n", (dclr & BIT(8)) ? "enabled" : "disabled"); @@ -756,7 +766,7 @@ static void prep_chip_selects(struct amd64_pvt *pvt) if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; - } else if (pvt->fam == 0x15 && pvt->model >= 0x30) { + } else if (pvt->fam == 0x15 && pvt->model == 0x30) { pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; } else { @@ -813,25 +823,63 @@ static void read_dct_base_mask(struct amd64_pvt *pvt) } } -static enum mem_type determine_memory_type(struct amd64_pvt *pvt, int cs) +static void determine_memory_type(struct amd64_pvt *pvt) { - enum mem_type type; + u32 dram_ctrl, dcsm; - /* F15h supports only DDR3 */ - if (pvt->fam >= 0x15) - type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; - else if (pvt->fam == 0x10 || pvt->ext_model >= K8_REV_F) { + switch (pvt->fam) { + case 0xf: + if (pvt->ext_model >= K8_REV_F) + goto ddr3; + + pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; + return; + + case 0x10: if (pvt->dchr0 & DDR3_MODE) - type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; + goto ddr3; + + pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; + return; + + case 0x15: + if (pvt->model < 0x60) + goto ddr3; + + /* + * Model 0x60h needs special handling: + * + * We use a Chip Select value of '0' to obtain dcsm. + * Theoretically, it is possible to populate LRDIMMs of different + * 'Rank' value on a DCT. But this is not the common case. So, + * it's reasonable to assume all DIMMs are going to be of same + * 'type' until proven otherwise. + */ + amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); + dcsm = pvt->csels[0].csmasks[0]; + + if (((dram_ctrl >> 8) & 0x7) == 0x2) + pvt->dram_type = MEM_DDR4; + else if (pvt->dclr0 & BIT(16)) + pvt->dram_type = MEM_DDR3; + else if (dcsm & 0x3) + pvt->dram_type = MEM_LRDDR3; else - type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; - } else { - type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; - } + pvt->dram_type = MEM_RDDR3; - amd64_info("CS%d: %s\n", cs, edac_mem_types[type]); + return; - return type; + case 0x16: + goto ddr3; + + default: + WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); + pvt->dram_type = MEM_EMPTY; + } + return; + +ddr3: + pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; } /* Get the number of DCT channels the memory controller is using. */ @@ -958,8 +1006,12 @@ static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) if (WARN_ON(!nb)) return; - pci_func = (pvt->model == 0x30) ? PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 - : PCI_DEVICE_ID_AMD_15H_NB_F1; + if (pvt->model == 0x60) + pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; + else if (pvt->model == 0x30) + pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; + else + pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); if (WARN_ON(!f1)) @@ -1049,7 +1101,7 @@ static int ddr2_cs_size(unsigned i, bool dct_width) } static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; @@ -1167,8 +1219,43 @@ static int ddr3_cs_size(unsigned i, bool dct_width) return cs_size; } +static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) +{ + unsigned shift = 0; + int cs_size = 0; + + if (i < 4 || i == 6) + cs_size = -1; + else if (i == 12) + shift = 7; + else if (!(i & 0x1)) + shift = i >> 1; + else + shift = (i + 1) >> 1; + + if (cs_size != -1) + cs_size = rank_multiply * (128 << shift); + + return cs_size; +} + +static int ddr4_cs_size(unsigned i) +{ + int cs_size = 0; + + if (i == 0) + cs_size = -1; + else if (i == 1) + cs_size = 1024; + else + /* Min cs_size = 1G */ + cs_size = 1024 * (1 << (i >> 1)); + + return cs_size; +} + static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; @@ -1184,18 +1271,49 @@ static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, * F15h supports only 64bit DCT interfaces */ static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { WARN_ON(cs_mode > 12); return ddr3_cs_size(cs_mode, false); } +/* F15h M60h supports DDR4 mapping as well.. */ +static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, + unsigned cs_mode, int cs_mask_nr) +{ + int cs_size; + u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; + + WARN_ON(cs_mode > 12); + + if (pvt->dram_type == MEM_DDR4) { + if (cs_mode > 9) + return -1; + + cs_size = ddr4_cs_size(cs_mode); + } else if (pvt->dram_type == MEM_LRDDR3) { + unsigned rank_multiply = dcsm & 0xf; + + if (rank_multiply == 3) + rank_multiply = 4; + cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); + } else { + /* Minimum cs size is 512mb for F15hM60h*/ + if (cs_mode == 0x1) + return -1; + + cs_size = ddr3_cs_size(cs_mode, false); + } + + return cs_size; +} + /* * F16h and F15h model 30h have only limited cs_modes. */ static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { WARN_ON(cs_mode > 12); @@ -1757,13 +1875,20 @@ static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) size0 = 0; if (dcsb[dimm*2] & DCSB_CS_ENABLE) + /* For f15m60h, need multiplier for LRDIMM cs_size + * calculation. We pass 'dimm' value to the dbam_to_cs + * mapper so we can find the multiplier from the + * corresponding DCSM. + */ size0 = pvt->ops->dbam_to_cs(pvt, ctrl, - DBAM_DIMM(dimm, dbam)); + DBAM_DIMM(dimm, dbam), + dimm); size1 = 0; if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) size1 = pvt->ops->dbam_to_cs(pvt, ctrl, - DBAM_DIMM(dimm, dbam)); + DBAM_DIMM(dimm, dbam), + dimm); amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", dimm * 2, size0, @@ -1812,6 +1937,16 @@ static struct amd64_family_type family_types[] = { .dbam_to_cs = f16_dbam_to_chip_select, } }, + [F15_M60H_CPUS] = { + .ctl_name = "F15h_M60h", + .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, + .f3_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F3, + .ops = { + .early_channel_count = f1x_early_channel_count, + .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, + .dbam_to_cs = f15_m60h_dbam_to_chip_select, + } + }, [F16_CPUS] = { .ctl_name = "F16h", .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, @@ -2175,6 +2310,8 @@ static void read_mc_regs(struct amd64_pvt *pvt) } pvt->ecc_sym_sz = 4; + determine_memory_type(pvt); + edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); if (pvt->fam >= 0x10) { amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); @@ -2238,7 +2375,8 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) */ cs_mode = DBAM_DIMM(csrow_nr / 2, dbam); - nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT); + nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2)) + << (20 - PAGE_SHIFT); edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", csrow_nr, dct, cs_mode); @@ -2257,7 +2395,6 @@ static int init_csrows(struct mem_ctl_info *mci) struct csrow_info *csrow; struct dimm_info *dimm; enum edac_type edac_mode; - enum mem_type mtype; int i, j, empty = 1; int nr_pages = 0; u32 val; @@ -2302,8 +2439,6 @@ static int init_csrows(struct mem_ctl_info *mci) nr_pages += row_dct1_pages; } - mtype = determine_memory_type(pvt, i); - edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); /* @@ -2317,7 +2452,7 @@ static int init_csrows(struct mem_ctl_info *mci) for (j = 0; j < pvt->channel_count; j++) { dimm = csrow->channels[j]->dimm; - dimm->mtype = mtype; + dimm->mtype = pvt->dram_type; dimm->edac_mode = edac_mode; } } @@ -2604,6 +2739,10 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) fam_type = &family_types[F15_M30H_CPUS]; pvt->ops = &family_types[F15_M30H_CPUS].ops; break; + } else if (pvt->model == 0x60) { + fam_type = &family_types[F15_M60H_CPUS]; + pvt->ops = &family_types[F15_M60H_CPUS].ops; + break; } fam_type = &family_types[F15_CPUS]; @@ -2828,55 +2967,13 @@ static void remove_one_instance(struct pci_dev *pdev) * inquiry this table to see if this driver is for a given device found. */ static const struct pci_device_id amd64_pci_table[] = { - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_10H_NB_DRAM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_15H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_16H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_K8_NB_MEMCTL) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_DRAM) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F2) }, {0, } }; MODULE_DEVICE_TABLE(pci, amd64_pci_table); @@ -2938,6 +3035,11 @@ static int __init amd64_edac_init(void) goto err_no_instances; setup_pci_device(); + +#ifdef CONFIG_X86_32 + amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); +#endif + return 0; err_no_instances: diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 55fb5941c6d4..d8468c667925 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -162,10 +162,12 @@ /* * PCI-defined configuration space registers */ -#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b -#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F2 0x141c #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 +#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b +#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F2 0x141c +#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F1 0x1571 +#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F2 0x1572 #define PCI_DEVICE_ID_AMD_16H_NB_F1 0x1531 #define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532 #define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581 @@ -221,6 +223,8 @@ #define csrow_enabled(i, dct, pvt) ((pvt)->csels[(dct)].csbases[(i)] & DCSB_CS_ENABLE) +#define DRAM_CONTROL 0x78 + #define DBAM0 0x80 #define DBAM1 0x180 @@ -301,6 +305,7 @@ enum amd_families { F10_CPUS, F15_CPUS, F15_M30H_CPUS, + F15_M60H_CPUS, F16_CPUS, F16_M30H_CPUS, NUM_FAMILIES, @@ -379,6 +384,9 @@ struct amd64_pvt { /* place to store error injection parameters prior to issue */ struct error_injection injection; + + /* cache the dram_type */ + enum mem_type dram_type; }; enum err_codes { @@ -480,7 +488,8 @@ struct low_ops { int (*early_channel_count) (struct amd64_pvt *pvt); void (*map_sysaddr_to_csrow) (struct mem_ctl_info *mci, u64 sys_addr, struct err_info *); - int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct, unsigned cs_mode); + int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct, + unsigned cs_mode, int cs_mask_nr); }; struct amd64_family_type { diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index c3893b0ddb18..1747906f10ce 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -125,27 +125,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) #endif /* CONFIG_EDAC_DEBUG */ -/* - * keep those in sync with the enum mem_type - */ const char * const edac_mem_types[] = { - "Empty csrow", - "Reserved csrow type", - "Unknown csrow type", - "Fast page mode RAM", - "Extended data out RAM", - "Burst Extended data out RAM", - "Single data rate SDRAM", - "Registered single data rate SDRAM", - "Double data rate SDRAM", - "Registered Double data rate SDRAM", - "Rambus DRAM", - "Unbuffered DDR2 RAM", - "Fully buffered DDR2", - "Registered DDR2 RAM", - "Rambus XDR", - "Unbuffered DDR3 RAM", - "Registered DDR3 RAM", + [MEM_EMPTY] = "Empty csrow", + [MEM_RESERVED] = "Reserved csrow type", + [MEM_UNKNOWN] = "Unknown csrow type", + [MEM_FPM] = "Fast page mode RAM", + [MEM_EDO] = "Extended data out RAM", + [MEM_BEDO] = "Burst Extended data out RAM", + [MEM_SDR] = "Single data rate SDRAM", + [MEM_RDR] = "Registered single data rate SDRAM", + [MEM_DDR] = "Double data rate SDRAM", + [MEM_RDDR] = "Registered Double data rate SDRAM", + [MEM_RMBS] = "Rambus DRAM", + [MEM_DDR2] = "Unbuffered DDR2 RAM", + [MEM_FB_DDR2] = "Fully buffered DDR2", + [MEM_RDDR2] = "Registered DDR2 RAM", + [MEM_XDR] = "Rambus XDR", + [MEM_DDR3] = "Unbuffered DDR3 RAM", + [MEM_RDDR3] = "Registered DDR3 RAM", + [MEM_LRDDR3] = "Load-Reduced DDR3 RAM", + [MEM_DDR4] = "Unbuffered DDR4 RAM", + [MEM_RDDR4] = "Registered DDR4 RAM", }; EXPORT_SYMBOL_GPL(edac_mem_types); diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index e8658e451762..24d877f6e577 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -14,9 +14,6 @@ #include "edac_core.h" #include "edac_module.h" -/* Turn off this whole feature if PCI is not configured */ -#ifdef CONFIG_PCI - #define EDAC_PCI_SYMLINK "device" /* data variables exported via sysfs */ @@ -761,5 +758,3 @@ MODULE_PARM_DESC(check_pci_errors, module_param(edac_pci_panic_on_pe, int, 0644); MODULE_PARM_DESC(edac_pci_panic_on_pe, "Panic on PCI Bus Parity error: 0=off 1=on"); - -#endif /* CONFIG_PCI */ diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 8399b4e16fe0..b24681998740 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -413,8 +413,8 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, /* Generate the trace event */ grain_bits = fls_long(e->grain); - sprintf(pvt->detail_location, "APEI location: %s %s", - e->location, e->other_detail); + snprintf(pvt->detail_location, sizeof(pvt->detail_location), + "APEI location: %s %s", e->location, e->other_detail); trace_mc_event(type, e->msg, e->label, e->error_count, mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer, PAGES_TO_MiB(e->page_frame_number) | e->offset_in_page, diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index cd28b968e5c7..5cb36a6022cc 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -542,8 +542,7 @@ fail1: pci_unregister_driver(&i3000_driver); fail0: - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index aa98b136f5d0..4ad062b0ef26 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -523,8 +523,7 @@ fail1: pci_unregister_driver(&i3200_driver); fail0: - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index d730e276d1a8..b4705d9366bf 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -458,8 +458,7 @@ static void __exit i82443bxgx_edacmc_exit(void) if (!i82443bxgx_registered) i82443bxgx_edacmc_remove_one(mci_pdev); - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); } module_init(i82443bxgx_edacmc_init); diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index f78c1c54dbd5..58586d59bf8e 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -138,6 +138,15 @@ static const char * const mc5_mce_desc[] = { "Retire status queue" }; +static const char * const mc6_mce_desc[] = { + "Hardware Assertion", + "Free List", + "Physical Register File", + "Retire Queue", + "Scheduler table", + "Status Register File", +}; + static bool f12h_mc0_mce(u16 ec, u8 xec) { bool ret = false; @@ -432,8 +441,8 @@ static bool k8_mc2_mce(u16 ec, u8 xec) pr_cont(": %s error in the L2 cache tags.\n", R4_MSG(ec)); else if (xec == 0x0) { if (TLB_ERROR(ec)) - pr_cont(": %s error in a Page Descriptor Cache or " - "Guest TLB.\n", TT_MSG(ec)); + pr_cont("%s error in a Page Descriptor Cache or Guest TLB.\n", + TT_MSG(ec)); else if (BUS_ERROR(ec)) pr_cont(": %s/ECC error in data read from NB: %s.\n", R4_MSG(ec), PP_MSG(ec)); @@ -672,38 +681,10 @@ static void decode_mc6_mce(struct mce *m) pr_emerg(HW_ERR "MC6 Error: "); - switch (xec) { - case 0x0: - pr_cont("Hardware Assertion"); - break; - - case 0x1: - pr_cont("Free List"); - break; - - case 0x2: - pr_cont("Physical Register File"); - break; - - case 0x3: - pr_cont("Retire Queue"); - break; - - case 0x4: - pr_cont("Scheduler table"); - break; - - case 0x5: - pr_cont("Status Register File"); - break; - - default: + if (xec > 0x5) goto wrong_mc6_mce; - break; - } - - pr_cont(" parity error.\n"); + pr_cont("%s parity error.\n", mc6_mce_desc[xec]); return; wrong_mc6_mce: @@ -800,7 +781,7 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) pr_cont("]: 0x%016llx\n", m->status); if (m->status & MCI_STATUS_ADDRV) - pr_emerg(HW_ERR "MC%d_ADDR: 0x%016llx\n", m->bank, m->addr); + pr_emerg(HW_ERR "MC%d Error Address: 0x%016llx\n", m->bank, m->addr); if (!fam_ops) goto err_code; diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 5e46a9fea31b..0bd91a802c67 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -1,173 +1,262 @@ /* - * A simple MCE injection facility for testing the MCE decoding code. This - * driver should be built as module so that it can be loaded on production - * kernels for testing purposes. + * A simple MCE injection facility for testing different aspects of the RAS + * code. This driver should be built as module so that it can be loaded + * on production kernels for testing purposes. * * This file may be distributed under the terms of the GNU General Public * License version 2. * - * Copyright (c) 2010: Borislav Petkov <bp@alien8.de> + * Copyright (c) 2010-14: Borislav Petkov <bp@alien8.de> * Advanced Micro Devices Inc. */ #include <linux/kobject.h> +#include <linux/debugfs.h> #include <linux/device.h> -#include <linux/edac.h> #include <linux/module.h> +#include <linux/cpu.h> #include <asm/mce.h> #include "mce_amd.h" -struct edac_mce_attr { - struct attribute attr; - ssize_t (*show) (struct kobject *kobj, struct edac_mce_attr *attr, char *buf); - ssize_t (*store)(struct kobject *kobj, struct edac_mce_attr *attr, - const char *buf, size_t count); -}; - -#define EDAC_MCE_ATTR(_name, _mode, _show, _store) \ -static struct edac_mce_attr mce_attr_##_name = __ATTR(_name, _mode, _show, _store) - -static struct kobject *mce_kobj; - /* * Collect all the MCi_XXX settings */ static struct mce i_mce; +static struct dentry *dfs_inj; -#define MCE_INJECT_STORE(reg) \ -static ssize_t edac_inject_##reg##_store(struct kobject *kobj, \ - struct edac_mce_attr *attr, \ - const char *data, size_t count)\ +#define MCE_INJECT_SET(reg) \ +static int inj_##reg##_set(void *data, u64 val) \ { \ - int ret = 0; \ - unsigned long value; \ - \ - ret = kstrtoul(data, 16, &value); \ - if (ret < 0) \ - printk(KERN_ERR "Error writing MCE " #reg " field.\n"); \ + struct mce *m = (struct mce *)data; \ \ - i_mce.reg = value; \ - \ - return count; \ + m->reg = val; \ + return 0; \ } -MCE_INJECT_STORE(status); -MCE_INJECT_STORE(misc); -MCE_INJECT_STORE(addr); +MCE_INJECT_SET(status); +MCE_INJECT_SET(misc); +MCE_INJECT_SET(addr); -#define MCE_INJECT_SHOW(reg) \ -static ssize_t edac_inject_##reg##_show(struct kobject *kobj, \ - struct edac_mce_attr *attr, \ - char *buf) \ +#define MCE_INJECT_GET(reg) \ +static int inj_##reg##_get(void *data, u64 *val) \ { \ - return sprintf(buf, "0x%016llx\n", i_mce.reg); \ + struct mce *m = (struct mce *)data; \ + \ + *val = m->reg; \ + return 0; \ } -MCE_INJECT_SHOW(status); -MCE_INJECT_SHOW(misc); -MCE_INJECT_SHOW(addr); +MCE_INJECT_GET(status); +MCE_INJECT_GET(misc); +MCE_INJECT_GET(addr); -EDAC_MCE_ATTR(status, 0644, edac_inject_status_show, edac_inject_status_store); -EDAC_MCE_ATTR(misc, 0644, edac_inject_misc_show, edac_inject_misc_store); -EDAC_MCE_ATTR(addr, 0644, edac_inject_addr_show, edac_inject_addr_store); +DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n"); /* - * This denotes into which bank we're injecting and triggers - * the injection, at the same time. + * Caller needs to be make sure this cpu doesn't disappear + * from under us, i.e.: get_cpu/put_cpu. */ -static ssize_t edac_inject_bank_store(struct kobject *kobj, - struct edac_mce_attr *attr, - const char *data, size_t count) +static int toggle_hw_mce_inject(unsigned int cpu, bool enable) { - int ret = 0; - unsigned long value; + u32 l, h; + int err; - ret = kstrtoul(data, 10, &value); - if (ret < 0) { - printk(KERN_ERR "Invalid bank value!\n"); - return -EINVAL; + err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h); + if (err) { + pr_err("%s: error reading HWCR\n", __func__); + return err; } - if (value > 5) - if (boot_cpu_data.x86 != 0x15 || value > 6) { - printk(KERN_ERR "Non-existent MCE bank: %lu\n", value); - return -EINVAL; - } + enable ? (l |= BIT(18)) : (l &= ~BIT(18)); - i_mce.bank = value; + err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h); + if (err) + pr_err("%s: error writing HWCR\n", __func__); - amd_decode_mce(NULL, 0, &i_mce); + return err; +} - return count; +static int flags_get(void *data, u64 *val) +{ + struct mce *m = (struct mce *)data; + + *val = m->inject_flags; + + return 0; } -static ssize_t edac_inject_bank_show(struct kobject *kobj, - struct edac_mce_attr *attr, char *buf) +static int flags_set(void *data, u64 val) { - return sprintf(buf, "%d\n", i_mce.bank); + struct mce *m = (struct mce *)data; + + m->inject_flags = (u8)val; + return 0; } -EDAC_MCE_ATTR(bank, 0644, edac_inject_bank_show, edac_inject_bank_store); +DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n"); -static struct edac_mce_attr *sysfs_attrs[] = { &mce_attr_status, &mce_attr_misc, - &mce_attr_addr, &mce_attr_bank -}; +/* + * On which CPU to inject? + */ +MCE_INJECT_GET(extcpu); -static int __init edac_init_mce_inject(void) +static int inj_extcpu_set(void *data, u64 val) { - struct bus_type *edac_subsys = NULL; - int i, err = 0; + struct mce *m = (struct mce *)data; - edac_subsys = edac_get_sysfs_subsys(); - if (!edac_subsys) + if (val >= nr_cpu_ids || !cpu_online(val)) { + pr_err("%s: Invalid CPU: %llu\n", __func__, val); return -EINVAL; + } + m->extcpu = val; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n"); - mce_kobj = kobject_create_and_add("mce", &edac_subsys->dev_root->kobj); - if (!mce_kobj) { - printk(KERN_ERR "Error creating a mce kset.\n"); - err = -ENOMEM; - goto err_mce_kobj; +static void trigger_mce(void *info) +{ + asm volatile("int $18"); +} + +static void do_inject(void) +{ + u64 mcg_status = 0; + unsigned int cpu = i_mce.extcpu; + u8 b = i_mce.bank; + + if (!(i_mce.inject_flags & MCJ_EXCEPTION)) { + amd_decode_mce(NULL, 0, &i_mce); + return; } - for (i = 0; i < ARRAY_SIZE(sysfs_attrs); i++) { - err = sysfs_create_file(mce_kobj, &sysfs_attrs[i]->attr); - if (err) { - printk(KERN_ERR "Error creating %s in sysfs.\n", - sysfs_attrs[i]->attr.name); - goto err_sysfs_create; + get_online_cpus(); + if (!cpu_online(cpu)) + goto err; + + /* prep MCE global settings for the injection */ + mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV; + + if (!(i_mce.status & MCI_STATUS_PCC)) + mcg_status |= MCG_STATUS_RIPV; + + toggle_hw_mce_inject(cpu, true); + + wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS, + (u32)mcg_status, (u32)(mcg_status >> 32)); + + wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b), + (u32)i_mce.status, (u32)(i_mce.status >> 32)); + + wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b), + (u32)i_mce.addr, (u32)(i_mce.addr >> 32)); + + wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b), + (u32)i_mce.misc, (u32)(i_mce.misc >> 32)); + + toggle_hw_mce_inject(cpu, false); + + smp_call_function_single(cpu, trigger_mce, NULL, 0); + +err: + put_online_cpus(); + +} + +/* + * This denotes into which bank we're injecting and triggers + * the injection, at the same time. + */ +static int inj_bank_set(void *data, u64 val) +{ + struct mce *m = (struct mce *)data; + + if (val > 5) { + if (boot_cpu_data.x86 != 0x15 || val > 6) { + pr_err("Non-existent MCE bank: %llu\n", val); + return -EINVAL; } } - return 0; -err_sysfs_create: - while (--i >= 0) - sysfs_remove_file(mce_kobj, &sysfs_attrs[i]->attr); + m->bank = val; + do_inject(); - kobject_del(mce_kobj); + return 0; +} -err_mce_kobj: - edac_put_sysfs_subsys(); +static int inj_bank_get(void *data, u64 *val) +{ + struct mce *m = (struct mce *)data; - return err; + *val = m->bank; + return 0; } -static void __exit edac_exit_mce_inject(void) +DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); + +struct dfs_node { + char *name; + struct dentry *d; + const struct file_operations *fops; +} dfs_fls[] = { + { .name = "status", .fops = &status_fops }, + { .name = "misc", .fops = &misc_fops }, + { .name = "addr", .fops = &addr_fops }, + { .name = "bank", .fops = &bank_fops }, + { .name = "flags", .fops = &flags_fops }, + { .name = "cpu", .fops = &extcpu_fops }, +}; + +static int __init init_mce_inject(void) { int i; - for (i = 0; i < ARRAY_SIZE(sysfs_attrs); i++) - sysfs_remove_file(mce_kobj, &sysfs_attrs[i]->attr); + dfs_inj = debugfs_create_dir("mce-inject", NULL); + if (!dfs_inj) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) { + dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name, + S_IRUSR | S_IWUSR, + dfs_inj, + &i_mce, + dfs_fls[i].fops); + + if (!dfs_fls[i].d) + goto err_dfs_add; + } + + return 0; + +err_dfs_add: + while (--i >= 0) + debugfs_remove(dfs_fls[i].d); - kobject_del(mce_kobj); + debugfs_remove(dfs_inj); + dfs_inj = NULL; - edac_put_sysfs_subsys(); + return -ENOMEM; } -module_init(edac_init_mce_inject); -module_exit(edac_exit_mce_inject); +static void __exit exit_mce_inject(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) + debugfs_remove(dfs_fls[i].d); + + memset(&dfs_fls, 0, sizeof(dfs_fls)); + + debugfs_remove(dfs_inj); + dfs_inj = NULL; +} +module_init(init_mce_inject); +module_exit(exit_mce_inject); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>"); MODULE_AUTHOR("AMD Inc."); -MODULE_DESCRIPTION("MCE injection facility for testing MCE decoding"); +MODULE_DESCRIPTION("MCE injection facility for RAS testing"); diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index 542fad70e360..6366e880f978 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -178,7 +178,7 @@ static int mv64x60_pci_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_pci_isr, - IRQF_DISABLED, + 0, "[EDAC] PCI err", pci); if (res < 0) { @@ -345,7 +345,7 @@ static int mv64x60_sram_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_sram_isr, - IRQF_DISABLED, + 0, "[EDAC] SRAM err", edac_dev); if (res < 0) { @@ -540,7 +540,7 @@ static int mv64x60_cpu_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_cpu_isr, - IRQF_DISABLED, + 0, "[EDAC] CPU err", edac_dev); if (res < 0) { @@ -800,7 +800,7 @@ static int mv64x60_mc_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_mc_isr, - IRQF_DISABLED, + 0, "[EDAC] MC err", mci); if (res < 0) { diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 0f04d5ead521..41593539cec4 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -1120,7 +1120,7 @@ static int ppc4xx_edac_register_irq(struct platform_device *op, status = request_irq(ded_irq, ppc4xx_edac_isr, - IRQF_DISABLED, + 0, "[EDAC] MC ECCDED", mci); @@ -1134,7 +1134,7 @@ static int ppc4xx_edac_register_irq(struct platform_device *op, status = request_irq(sec_irq, ppc4xx_edac_isr, - IRQF_DISABLED, + 0, "[EDAC] MC ECCSEC", mci); diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index e644b52c287c..7c5cdc62f31c 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -500,8 +500,7 @@ fail1: pci_unregister_driver(&x38_driver); fail0: - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0959ca9b6b27..23dfd5f59b39 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -905,4 +905,16 @@ config GPIO_VIPERBOARD River Tech's viperboard.h for detailed meaning of the module parameters. +config GPIO_DLN2 + tristate "Diolan DLN2 GPIO support" + depends on MFD_DLN2 + select GPIOLIB_IRQCHIP + + help + Select this option to enable GPIO driver for the Diolan DLN2 + board. + + This driver can also be built as a module. If so, the module + will be called gpio-dln2. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e5d346cf3b6e..e60677b8ccb4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o +obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c new file mode 100644 index 000000000000..978b51eae2ec --- /dev/null +++ b/drivers/gpio/gpio-dln2.c @@ -0,0 +1,553 @@ +/* + * Driver for the Diolan DLN-2 USB-GPIO adapter + * + * Copyright (c) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#define DLN2_GPIO_ID 0x01 + +#define DLN2_GPIO_GET_PIN_COUNT DLN2_CMD(0x01, DLN2_GPIO_ID) +#define DLN2_GPIO_SET_DEBOUNCE DLN2_CMD(0x04, DLN2_GPIO_ID) +#define DLN2_GPIO_GET_DEBOUNCE DLN2_CMD(0x05, DLN2_GPIO_ID) +#define DLN2_GPIO_PORT_GET_VAL DLN2_CMD(0x06, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_VAL DLN2_CMD(0x0B, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_OUT_VAL DLN2_CMD(0x0C, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_OUT_VAL DLN2_CMD(0x0D, DLN2_GPIO_ID) +#define DLN2_GPIO_CONDITION_MET_EV DLN2_CMD(0x0F, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_ENABLE DLN2_CMD(0x10, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_DISABLE DLN2_CMD(0x11, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_DIRECTION DLN2_CMD(0x13, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_DIRECTION DLN2_CMD(0x14, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_EVENT_CFG DLN2_CMD(0x1E, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_EVENT_CFG DLN2_CMD(0x1F, DLN2_GPIO_ID) + +#define DLN2_GPIO_EVENT_NONE 0 +#define DLN2_GPIO_EVENT_CHANGE 1 +#define DLN2_GPIO_EVENT_LVL_HIGH 2 +#define DLN2_GPIO_EVENT_LVL_LOW 3 +#define DLN2_GPIO_EVENT_CHANGE_RISING 0x11 +#define DLN2_GPIO_EVENT_CHANGE_FALLING 0x21 +#define DLN2_GPIO_EVENT_MASK 0x0F + +#define DLN2_GPIO_MAX_PINS 32 + +struct dln2_irq_work { + struct work_struct work; + struct dln2_gpio *dln2; + int pin; + int type; +}; + +struct dln2_gpio { + struct platform_device *pdev; + struct gpio_chip gpio; + + /* + * Cache pin direction to save us one transfer, since the hardware has + * separate commands to read the in and out values. + */ + DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS); + + DECLARE_BITMAP(irqs_masked, DLN2_GPIO_MAX_PINS); + DECLARE_BITMAP(irqs_enabled, DLN2_GPIO_MAX_PINS); + DECLARE_BITMAP(irqs_pending, DLN2_GPIO_MAX_PINS); + struct dln2_irq_work *irq_work; +}; + +struct dln2_gpio_pin { + __le16 pin; +}; + +struct dln2_gpio_pin_val { + __le16 pin __packed; + u8 value; +}; + +static int dln2_gpio_get_pin_count(struct platform_device *pdev) +{ + int ret; + __le16 count; + int len = sizeof(count); + + ret = dln2_transfer_rx(pdev, DLN2_GPIO_GET_PIN_COUNT, &count, &len); + if (ret < 0) + return ret; + if (len < sizeof(count)) + return -EPROTO; + + return le16_to_cpu(count); +} + +static int dln2_gpio_pin_cmd(struct dln2_gpio *dln2, int cmd, unsigned pin) +{ + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(pin), + }; + + return dln2_transfer_tx(dln2->pdev, cmd, &req, sizeof(req)); +} + +static int dln2_gpio_pin_val(struct dln2_gpio *dln2, int cmd, unsigned int pin) +{ + int ret; + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(pin), + }; + struct dln2_gpio_pin_val rsp; + int len = sizeof(rsp); + + ret = dln2_transfer(dln2->pdev, cmd, &req, sizeof(req), &rsp, &len); + if (ret < 0) + return ret; + if (len < sizeof(rsp) || req.pin != rsp.pin) + return -EPROTO; + + return rsp.value; +} + +static int dln2_gpio_pin_get_in_val(struct dln2_gpio *dln2, unsigned int pin) +{ + int ret; + + ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_VAL, pin); + if (ret < 0) + return ret; + return !!ret; +} + +static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin) +{ + int ret; + + ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_OUT_VAL, pin); + if (ret < 0) + return ret; + return !!ret; +} + +static void dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2, + unsigned int pin, int value) +{ + struct dln2_gpio_pin_val req = { + .pin = cpu_to_le16(pin), + .value = value, + }; + + dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req, + sizeof(req)); +} + +#define DLN2_GPIO_DIRECTION_IN 0 +#define DLN2_GPIO_DIRECTION_OUT 1 + +static int dln2_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(offset), + }; + struct dln2_gpio_pin_val rsp; + int len = sizeof(rsp); + int ret; + + ret = dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_ENABLE, offset); + if (ret < 0) + return ret; + + /* cache the pin direction */ + ret = dln2_transfer(dln2->pdev, DLN2_GPIO_PIN_GET_DIRECTION, + &req, sizeof(req), &rsp, &len); + if (ret < 0) + return ret; + if (len < sizeof(rsp) || req.pin != rsp.pin) { + ret = -EPROTO; + goto out_disable; + } + + switch (rsp.value) { + case DLN2_GPIO_DIRECTION_IN: + clear_bit(offset, dln2->output_enabled); + return 0; + case DLN2_GPIO_DIRECTION_OUT: + set_bit(offset, dln2->output_enabled); + return 0; + default: + ret = -EPROTO; + goto out_disable; + } + +out_disable: + dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); + return ret; +} + +static void dln2_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); +} + +static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + if (test_bit(offset, dln2->output_enabled)) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + int dir; + + dir = dln2_gpio_get_direction(chip, offset); + if (dir < 0) + return dir; + + if (dir == GPIOF_DIR_IN) + return dln2_gpio_pin_get_in_val(dln2, offset); + + return dln2_gpio_pin_get_out_val(dln2, offset); +} + +static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + dln2_gpio_pin_set_out_val(dln2, offset, value); +} + +static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, + unsigned dir) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + struct dln2_gpio_pin_val req = { + .pin = cpu_to_le16(offset), + .value = dir, + }; + int ret; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_DIRECTION, + &req, sizeof(req)); + if (ret < 0) + return ret; + + if (dir == DLN2_GPIO_DIRECTION_OUT) + set_bit(offset, dln2->output_enabled); + else + clear_bit(offset, dln2->output_enabled); + + return ret; +} + +static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_IN); +} + +static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT); +} + +static int dln2_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + __le32 duration = cpu_to_le32(debounce); + + return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_SET_DEBOUNCE, + &duration, sizeof(duration)); +} + +static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin, + unsigned type, unsigned period) +{ + struct { + __le16 pin; + u8 type; + __le16 period; + } __packed req = { + .pin = cpu_to_le16(pin), + .type = type, + .period = cpu_to_le16(period), + }; + + return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_EVENT_CFG, + &req, sizeof(req)); +} + +static void dln2_irq_work(struct work_struct *w) +{ + struct dln2_irq_work *iw = container_of(w, struct dln2_irq_work, work); + struct dln2_gpio *dln2 = iw->dln2; + u8 type = iw->type & DLN2_GPIO_EVENT_MASK; + + if (test_bit(iw->pin, dln2->irqs_enabled)) + dln2_gpio_set_event_cfg(dln2, iw->pin, type, 0); + else + dln2_gpio_set_event_cfg(dln2, iw->pin, DLN2_GPIO_EVENT_NONE, 0); +} + +static void dln2_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + set_bit(pin, dln2->irqs_enabled); + schedule_work(&dln2->irq_work[pin].work); +} + +static void dln2_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + clear_bit(pin, dln2->irqs_enabled); + schedule_work(&dln2->irq_work[pin].work); +} + +static void dln2_irq_mask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + set_bit(pin, dln2->irqs_masked); +} + +static void dln2_irq_unmask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + struct device *dev = dln2->gpio.dev; + int pin = irqd_to_hwirq(irqd); + + if (test_and_clear_bit(pin, dln2->irqs_pending)) { + int irq; + + irq = irq_find_mapping(dln2->gpio.irqdomain, pin); + if (!irq) { + dev_err(dev, "pin %d not mapped to IRQ\n", pin); + return; + } + + generic_handle_irq(irq); + } +} + +static int dln2_irq_set_type(struct irq_data *irqd, unsigned type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_LOW; + break; + case IRQ_TYPE_EDGE_BOTH: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE; + break; + case IRQ_TYPE_EDGE_RISING: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_FALLING; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct irq_chip dln2_gpio_irqchip = { + .name = "dln2-irq", + .irq_enable = dln2_irq_enable, + .irq_disable = dln2_irq_disable, + .irq_mask = dln2_irq_mask, + .irq_unmask = dln2_irq_unmask, + .irq_set_type = dln2_irq_set_type, +}; + +static void dln2_gpio_event(struct platform_device *pdev, u16 echo, + const void *data, int len) +{ + int pin, irq; + const struct { + __le16 count; + __u8 type; + __le16 pin; + __u8 value; + } __packed *event = data; + struct dln2_gpio *dln2 = platform_get_drvdata(pdev); + + if (len < sizeof(*event)) { + dev_err(dln2->gpio.dev, "short event message\n"); + return; + } + + pin = le16_to_cpu(event->pin); + if (pin >= dln2->gpio.ngpio) { + dev_err(dln2->gpio.dev, "out of bounds pin %d\n", pin); + return; + } + + irq = irq_find_mapping(dln2->gpio.irqdomain, pin); + if (!irq) { + dev_err(dln2->gpio.dev, "pin %d not mapped to IRQ\n", pin); + return; + } + + if (!test_bit(pin, dln2->irqs_enabled)) + return; + if (test_bit(pin, dln2->irqs_masked)) { + set_bit(pin, dln2->irqs_pending); + return; + } + + switch (dln2->irq_work[pin].type) { + case DLN2_GPIO_EVENT_CHANGE_RISING: + if (event->value) + generic_handle_irq(irq); + break; + case DLN2_GPIO_EVENT_CHANGE_FALLING: + if (!event->value) + generic_handle_irq(irq); + break; + default: + generic_handle_irq(irq); + } +} + +static int dln2_gpio_probe(struct platform_device *pdev) +{ + struct dln2_gpio *dln2; + struct device *dev = &pdev->dev; + int pins; + int i, ret; + + pins = dln2_gpio_get_pin_count(pdev); + if (pins < 0) { + dev_err(dev, "failed to get pin count: %d\n", pins); + return pins; + } + if (pins > DLN2_GPIO_MAX_PINS) { + pins = DLN2_GPIO_MAX_PINS; + dev_warn(dev, "clamping pins to %d\n", DLN2_GPIO_MAX_PINS); + } + + dln2 = devm_kzalloc(&pdev->dev, sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->irq_work = devm_kcalloc(&pdev->dev, pins, + sizeof(struct dln2_irq_work), GFP_KERNEL); + if (!dln2->irq_work) + return -ENOMEM; + for (i = 0; i < pins; i++) { + INIT_WORK(&dln2->irq_work[i].work, dln2_irq_work); + dln2->irq_work[i].pin = i; + dln2->irq_work[i].dln2 = dln2; + } + + dln2->pdev = pdev; + + dln2->gpio.label = "dln2"; + dln2->gpio.dev = dev; + dln2->gpio.owner = THIS_MODULE; + dln2->gpio.base = -1; + dln2->gpio.ngpio = pins; + dln2->gpio.exported = true; + dln2->gpio.can_sleep = true; + dln2->gpio.irq_not_threaded = true; + dln2->gpio.set = dln2_gpio_set; + dln2->gpio.get = dln2_gpio_get; + dln2->gpio.request = dln2_gpio_request; + dln2->gpio.free = dln2_gpio_free; + dln2->gpio.get_direction = dln2_gpio_get_direction; + dln2->gpio.direction_input = dln2_gpio_direction_input; + dln2->gpio.direction_output = dln2_gpio_direction_output; + dln2->gpio.set_debounce = dln2_gpio_set_debounce; + + platform_set_drvdata(pdev, dln2); + + ret = gpiochip_add(&dln2->gpio); + if (ret < 0) { + dev_err(dev, "failed to add gpio chip: %d\n", ret); + goto out; + } + + ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret < 0) { + dev_err(dev, "failed to add irq chip: %d\n", ret); + goto out_gpiochip_remove; + } + + ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV, + dln2_gpio_event); + if (ret) { + dev_err(dev, "failed to register event cb: %d\n", ret); + goto out_gpiochip_remove; + } + + return 0; + +out_gpiochip_remove: + gpiochip_remove(&dln2->gpio); +out: + return ret; +} + +static int dln2_gpio_remove(struct platform_device *pdev) +{ + struct dln2_gpio *dln2 = platform_get_drvdata(pdev); + int i; + + dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV); + for (i = 0; i < dln2->gpio.ngpio; i++) + flush_work(&dln2->irq_work[i].work); + gpiochip_remove(&dln2->gpio); + + return 0; +} + +static struct platform_driver dln2_gpio_driver = { + .driver.name = "dln2-gpio", + .probe = dln2_gpio_probe, + .remove = dln2_gpio_remove, +}; + +module_platform_driver(dln2_gpio_driver); + +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com"); +MODULE_DESCRIPTION("Driver for the Diolan DLN2 GPIO interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-gpio"); diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index ae0f6466eb09..abdcf58935f5 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -262,7 +262,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip = template_chip; tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; tc3589x_gpio->chip.dev = &pdev->dev; - tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1; + tc3589x_gpio->chip.base = -1; #ifdef CONFIG_OF_GPIO tc3589x_gpio->chip.of_node = np; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8bcdb981d540..9cb5c95d5898 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4325,7 +4325,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ironlake_fdi_disable(crtc); ironlake_disable_pch_transcoder(dev_priv, pipe); - intel_set_pch_fifo_underrun_reporting(dev, pipe, true); if (HAS_PCH_CPT(dev)) { /* disable TRANS_DP_CTL */ @@ -4389,7 +4388,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) { lpt_disable_pch_transcoder(dev_priv); - intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); intel_ddi_fdi_disable(crtc); } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index a6bd1422e38f..c0bbf2172446 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -899,6 +899,17 @@ void intel_lvds_init(struct drm_device *dev) int pipe; u8 pin; + /* + * Unlock registers and just leave them unlocked. Do this before + * checking quirk lists to avoid bogus WARNINGs. + */ + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } if (!intel_lvds_supported(dev)) return; @@ -1097,17 +1108,6 @@ out: lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK; - /* - * Unlock registers and just - * leave them unlocked - */ - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_PP_CONTROL, - I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); - } else { - I915_WRITE(PP_CONTROL, - I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); - } lvds_connector->lid_notifier.notifier_call = intel_lid_notify; if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) { DRM_DEBUG_KMS("lid notifier registration failed\n"); diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index cd05677ad4b7..72a40f95d048 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -218,7 +218,6 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; - device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass; device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass; break; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index 5ae6a43893b5..1931057f9962 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -551,8 +551,8 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (status & 0x40000000) { - nouveau_fifo_uevent(&priv->base); nv_wr32(priv, 0x002100, 0x40000000); + nouveau_fifo_uevent(&priv->base); status &= ~0x40000000; } } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index 1fe1f8fbda0c..074d434c3077 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -740,6 +740,8 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) u32 inte = nv_rd32(priv, 0x002628); u32 unkn; + nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); + for (unkn = 0; unkn < 8; unkn++) { u32 ints = (intr >> (unkn * 0x04)) & inte; if (ints & 0x1) { @@ -751,8 +753,6 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) nv_mask(priv, 0x002628, ints, 0); } } - - nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); } static void diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index d2f0fd39c145..f8734eb74eaa 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -952,8 +952,8 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) } if (stat & 0x80000000) { - nve0_fifo_intr_engine(priv); nv_wr32(priv, 0x002100, 0x80000000); + nve0_fifo_intr_engine(priv); stat &= ~0x80000000; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 57238076049f..62b97c4eef8d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -629,7 +629,6 @@ int nouveau_pmops_suspend(struct device *dev) pci_save_state(pdev); pci_disable_device(pdev); - pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; } @@ -933,6 +932,7 @@ static int nouveau_pmops_runtime_suspend(struct device *dev) ret = nouveau_do_suspend(drm_dev, true); pci_save_state(pdev); pci_disable_device(pdev); + pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3cold); drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 515cd9aebb99..f32a434724e3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence) return container_of(fence->base.lock, struct nouveau_fence_chan, lock); } -static void +static int nouveau_fence_signal(struct nouveau_fence *fence) { + int drop = 0; + fence_signal_locked(&fence->base); list_del(&fence->head); + rcu_assign_pointer(fence->channel, NULL); if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); if (!--fctx->notify_ref) - nvif_notify_put(&fctx->notify); + drop = 1; } fence_put(&fence->base); + return drop; } static struct nouveau_fence * @@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence; - nvif_notify_fini(&fctx->notify); - spin_lock_irq(&fctx->lock); while (!list_empty(&fctx->pending)) { fence = list_entry(fctx->pending.next, typeof(*fence), head); - nouveau_fence_signal(fence); - fence->channel = NULL; + if (nouveau_fence_signal(fence)) + nvif_notify_put(&fctx->notify); } spin_unlock_irq(&fctx->lock); + + nvif_notify_fini(&fctx->notify); + fctx->dead = 1; + + /* + * Ensure that all accesses to fence->channel complete before freeing + * the channel. + */ + synchronize_rcu(); } static void @@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) kref_put(&fctx->fence_ref, nouveau_fence_context_put); } -static void +static int nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence; - + int drop = 0; u32 seq = fctx->read(chan); while (!list_empty(&fctx->pending)) { fence = list_entry(fctx->pending.next, typeof(*fence), head); if ((int)(seq - fence->base.seqno) < 0) - return; + break; - nouveau_fence_signal(fence); + drop |= nouveau_fence_signal(fence); } + + return drop; } static int @@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) struct nouveau_fence_chan *fctx = container_of(notify, typeof(*fctx), notify); unsigned long flags; + int ret = NVIF_NOTIFY_KEEP; spin_lock_irqsave(&fctx->lock, flags); if (!list_empty(&fctx->pending)) { struct nouveau_fence *fence; + struct nouveau_channel *chan; fence = list_entry(fctx->pending.next, typeof(*fence), head); - nouveau_fence_update(fence->channel, fctx); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (nouveau_fence_update(fence->channel, fctx)) + ret = NVIF_NOTIFY_DROP; } spin_unlock_irqrestore(&fctx->lock, flags); - /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */ - return NVIF_NOTIFY_KEEP; + return ret; } void @@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) if (!ret) { fence_get(&fence->base); spin_lock_irq(&fctx->lock); - nouveau_fence_update(chan, fctx); + + if (nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); + list_add_tail(&fence->head, &fctx->pending); spin_unlock_irq(&fctx->lock); } @@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence) if (fence->base.ops == &nouveau_fence_ops_legacy || fence->base.ops == &nouveau_fence_ops_uevent) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; unsigned long flags; if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) return true; spin_lock_irqsave(&fctx->lock, flags); - nouveau_fence_update(fence->channel, fctx); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (chan && nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); spin_unlock_irqrestore(&fctx->lock, flags); } return fence_is_signaled(&fence->base); @@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e if (fence && (!exclusive || !fobj || !fobj->shared_count)) { struct nouveau_channel *prev = NULL; + bool must_wait = true; f = nouveau_local_fence(fence, chan->drm); - if (f) - prev = f->channel; + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } - if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + if (must_wait) ret = fence_wait(fence, intr); return ret; @@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e for (i = 0; i < fobj->shared_count && !ret; ++i) { struct nouveau_channel *prev = NULL; + bool must_wait = true; fence = rcu_dereference_protected(fobj->shared[i], reservation_object_held(resv)); f = nouveau_local_fence(fence, chan->drm); - if (f) - prev = f->channel; + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } - if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + if (must_wait) ret = fence_wait(fence, intr); - - if (ret) - break; } return ret; @@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f) struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - return fence->channel ? fctx->name : "dead channel"; + return !fctx->dead ? fctx->name : "dead channel"; } /* @@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f) { struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - struct nouveau_channel *chan = fence->channel; + struct nouveau_channel *chan; + bool ret = false; + + rcu_read_lock(); + chan = rcu_dereference(fence->channel); + if (chan) + ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0; + rcu_read_unlock(); - return (int)(fctx->read(chan) - fence->base.seqno) >= 0; + return ret; } static bool nouveau_fence_no_signaling(struct fence *f) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 943b0b17b1fc..96e461c6f68f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -14,7 +14,7 @@ struct nouveau_fence { bool sysmem; - struct nouveau_channel *channel; + struct nouveau_channel __rcu *channel; unsigned long timeout; }; @@ -47,7 +47,7 @@ struct nouveau_fence_chan { char name[32]; struct nvif_notify notify; - int notify_ref; + int notify_ref, dead; }; struct nouveau_fence_priv { diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index a3e7aed7e680..6f377de099f9 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -251,22 +251,19 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority static int radeon_cs_sync_rings(struct radeon_cs_parser *p) { - int i, r = 0; + struct radeon_cs_reloc *reloc; + int r; - for (i = 0; i < p->nrelocs; i++) { + list_for_each_entry(reloc, &p->validated, tv.head) { struct reservation_object *resv; - if (!p->relocs[i].robj) - continue; - - resv = p->relocs[i].robj->tbo.resv; + resv = reloc->robj->tbo.resv; r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv, - p->relocs[i].tv.shared); - + reloc->tv.shared); if (r) - break; + return r; } - return r; + return 0; } /* XXX: note that this is called from the legacy UMS CS ioctl as well */ diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 8309b11e674d..03586763ee86 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -795,6 +795,8 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, /* Get associated drm_crtc: */ drmcrtc = &rdev->mode_info.crtcs[crtc]->base; + if (!drmcrtc) + return -EINVAL; /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 99a960a4f302..4c0d786d5c7a 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -213,6 +213,13 @@ int radeon_bo_create(struct radeon_device *rdev, if (!(rdev->flags & RADEON_IS_PCIE)) bo->flags &= ~(RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC); +#ifdef CONFIG_X86_32 + /* XXX: Write-combined CPU mappings of GTT seem broken on 32-bit + * See https://bugs.freedesktop.org/show_bug.cgi?id=84627 + */ + bo->flags &= ~RADEON_GEM_GTT_WC; +#endif + radeon_ttm_placement_from_domain(bo, domain); /* Kernel allocation are uninterruptible */ down_read(&rdev->pm.mclk_lock); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index e6d8e18dae97..6a58b6c723aa 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -641,9 +641,6 @@ static int sensor_hub_probe(struct hid_device *hdev, goto err_stop_hw; } sd->hid_sensor_hub_client_devs[ - sd->hid_sensor_client_cnt].id = - PLATFORM_DEVID_AUTO; - sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].name = name; sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].platform_data = @@ -659,8 +656,9 @@ static int sensor_hub_probe(struct hid_device *hdev, if (last_hsdev) last_hsdev->end_collection_index = i; - ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, - sd->hid_sensor_client_cnt, NULL, 0, NULL); + ret = mfd_add_hotplug_devices(&hdev->dev, + sd->hid_sensor_hub_client_devs, + sd->hid_sensor_client_cnt); if (ret < 0) goto err_stop_hw; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 917c3585f45b..b4d135cc2f39 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -881,6 +881,16 @@ config I2C_DIOLAN_U2C This driver can also be built as a module. If so, the module will be called i2c-diolan-u2c. +config I2C_DLN2 + tristate "Diolan DLN-2 USB I2C adapter" + depends on MFD_DLN2 + help + If you say yes to this option, support will be included for Diolan + DLN2, a USB to I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-dln2. + config I2C_PARPORT tristate "Parallel port adapter" depends on PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d56c54ba2b..cdac7f15eab5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o +obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 63f3f03ecc9b..c604f4c3ac0d 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -111,6 +111,8 @@ #define CDNS_I2C_DIVA_MAX 4 #define CDNS_I2C_DIVB_MAX 64 +#define CDNS_I2C_TIMEOUT_MAX 0xFF + #define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset) #define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset) @@ -852,6 +854,15 @@ static int cdns_i2c_probe(struct platform_device *pdev) goto err_clk_dis; } + /* + * Cadence I2C controller has a bug wherein it generates + * invalid read transaction after HW timeout in master receiver mode. + * HW timeout is not used by this driver and the interrupt is disabled. + * But the feature itself cannot be disabled. Hence maximum value + * is written to this register to reduce the chances of error. + */ + cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET); + dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n", id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq); diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index d15b7c9b9219..01f0cd87a4a5 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -407,11 +407,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) if (dev->cmd_err & DAVINCI_I2C_STR_NACK) { if (msg->flags & I2C_M_IGNORE_NAK) return msg->len; - if (stop) { - w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - w |= DAVINCI_I2C_MDR_STP; - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); - } + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); return -EREMOTEIO; } return -EIO; diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index edca99dbba23..23628b7bfb8d 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -359,7 +359,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev) } /* Configure Tx/Rx FIFO threshold levels */ - dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL); + dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL); dw_writel(dev, 0, DW_IC_RX_TL); /* configure the i2c master */ diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c new file mode 100644 index 000000000000..b3fb86af4cbb --- /dev/null +++ b/drivers/i2c/busses/i2c-dln2.c @@ -0,0 +1,262 @@ +/* + * Driver for the Diolan DLN-2 USB-I2C adapter + * + * Copyright (c) 2014 Intel Corporation + * + * Derived from: + * i2c-diolan-u2c.c + * Copyright (c) 2010-2011 Ericsson AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#define DLN2_I2C_MODULE_ID 0x03 +#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) + +/* I2C commands */ +#define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00) +#define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01) +#define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02) +#define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03) +#define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06) +#define DLN2_I2C_READ DLN2_I2C_CMD(0x07) +#define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08) +#define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09) +#define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A) +#define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B) +#define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C) +#define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D) +#define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E) + +#define DLN2_I2C_MAX_XFER_SIZE 256 +#define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16) + +struct dln2_i2c { + struct platform_device *pdev; + struct i2c_adapter adapter; + u8 port; + /* + * Buffer to hold the packet for read or write transfers. One is enough + * since we can't have multiple transfers in parallel on the i2c bus. + */ + void *buf; +}; + +static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) +{ + u16 cmd; + struct { + u8 port; + } tx; + + tx.port = dln2->port; + + if (enable) + cmd = DLN2_I2C_ENABLE; + else + cmd = DLN2_I2C_DISABLE; + + return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); +} + +static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, + u8 *data, u16 data_len) +{ + int ret; + struct { + u8 port; + u8 addr; + u8 mem_addr_len; + __le32 mem_addr; + __le16 buf_len; + u8 buf[DLN2_I2C_MAX_XFER_SIZE]; + } __packed *tx = dln2->buf; + unsigned len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); + + tx->port = dln2->port; + tx->addr = addr; + tx->mem_addr_len = 0; + tx->mem_addr = 0; + tx->buf_len = cpu_to_le16(data_len); + memcpy(tx->buf, data, data_len); + + len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; + ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); + if (ret < 0) + return ret; + + return data_len; +} + +static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, + u16 data_len) +{ + int ret; + struct { + u8 port; + u8 addr; + u8 mem_addr_len; + __le32 mem_addr; + __le16 buf_len; + } __packed tx; + struct { + __le16 buf_len; + u8 buf[DLN2_I2C_MAX_XFER_SIZE]; + } __packed *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + + BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); + + tx.port = dln2->port; + tx.addr = addr; + tx.mem_addr_len = 0; + tx.mem_addr = 0; + tx.buf_len = cpu_to_le16(data_len); + + ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->buf_len) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->buf_len) != data_len) + return -EPROTO; + + memcpy(data, rx->buf, data_len); + + return data_len; +} + +static int dln2_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + struct device *dev = &dln2->adapter.dev; + int i; + + for (i = 0; i < num; i++) { + int ret; + + pmsg = &msgs[i]; + + if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) { + dev_warn(dev, "maximum transfer size exceeded\n"); + return -EOPNOTSUPP; + } + + if (pmsg->flags & I2C_M_RD) { + ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, + pmsg->len); + if (ret < 0) + return ret; + + pmsg->len = ret; + } else { + ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, + pmsg->len); + if (ret != pmsg->len) + return -EPROTO; + } + } + + return num; +} + +static u32 dln2_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm dln2_i2c_usb_algorithm = { + .master_xfer = dln2_i2c_xfer, + .functionality = dln2_i2c_func, +}; + +static int dln2_i2c_probe(struct platform_device *pdev) +{ + int ret; + struct dln2_i2c *dln2; + struct device *dev = &pdev->dev; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + + dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); + if (!dln2->buf) + return -ENOMEM; + + dln2->pdev = pdev; + dln2->port = pdata->port; + + /* setup i2c adapter description */ + dln2->adapter.owner = THIS_MODULE; + dln2->adapter.class = I2C_CLASS_HWMON; + dln2->adapter.algo = &dln2_i2c_usb_algorithm; + dln2->adapter.dev.parent = dev; + i2c_set_adapdata(&dln2->adapter, dln2); + snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", + "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); + + platform_set_drvdata(pdev, dln2); + + /* initialize the i2c interface */ + ret = dln2_i2c_enable(dln2, true); + if (ret < 0) { + dev_err(dev, "failed to initialize adapter: %d\n", ret); + return ret; + } + + /* and finally attach to i2c layer */ + ret = i2c_add_adapter(&dln2->adapter); + if (ret < 0) { + dev_err(dev, "failed to add I2C adapter: %d\n", ret); + goto out_disable; + } + + return 0; + +out_disable: + dln2_i2c_enable(dln2, false); + + return ret; +} + +static int dln2_i2c_remove(struct platform_device *pdev) +{ + struct dln2_i2c *dln2 = platform_get_drvdata(pdev); + + i2c_del_adapter(&dln2->adapter); + dln2_i2c_enable(dln2, false); + + return 0; +} + +static struct platform_driver dln2_i2c_driver = { + .driver.name = "dln2-i2c", + .probe = dln2_i2c_probe, + .remove = dln2_i2c_remove, +}; + +module_platform_driver(dln2_i2c_driver); + +MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); +MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-i2c"); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 26942c159de1..277a2288d4a8 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -922,14 +922,12 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (stat & OMAP_I2C_STAT_NACK) { err |= OMAP_I2C_STAT_NACK; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK); - break; } if (stat & OMAP_I2C_STAT_AL) { dev_err(dev->dev, "Arbitration lost\n"); err |= OMAP_I2C_STAT_AL; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL); - break; } /* @@ -954,11 +952,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (dev->fifo_size) num_bytes = dev->buf_len; - omap_i2c_receive_data(dev, num_bytes, true); - - if (dev->errata & I2C_OMAP_ERRATA_I207) + if (dev->errata & I2C_OMAP_ERRATA_I207) { i2c_omap_errata_i207(dev, stat); + num_bytes = (omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F; + } + omap_i2c_receive_data(dev, num_bytes, true); omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); continue; } diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 88bdc8f612e2..bc4e787096e8 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -127,6 +127,14 @@ config AT91_ADC help Say yes here to build support for Atmel AT91 ADC. +config AXP288_ADC + tristate "X-Powers AXP288 ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index cb88a6a23b8f..f30093f5b67a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1027) += max1027.o diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c new file mode 100644 index 000000000000..08bcfb061ca5 --- /dev/null +++ b/drivers/iio/adc/axp288_adc.c @@ -0,0 +1,261 @@ +/* + * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver + * + * Copyright (C) 2014 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/mfd/axp20x.h> +#include <linux/platform_device.h> + +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> + +#define AXP288_ADC_EN_MASK 0xF1 +#define AXP288_ADC_TS_PIN_GPADC 0xF2 +#define AXP288_ADC_TS_PIN_ON 0xF3 + +enum axp288_adc_id { + AXP288_ADC_TS, + AXP288_ADC_PMIC, + AXP288_ADC_GP, + AXP288_ADC_BATT_CHRG_I, + AXP288_ADC_BATT_DISCHRG_I, + AXP288_ADC_BATT_V, + AXP288_ADC_NR_CHAN, +}; + +struct axp288_adc_info { + int irq; + struct regmap *regmap; +}; + +static const struct iio_chan_spec const axp288_adc_channels[] = { + { + .indexed = 1, + .type = IIO_TEMP, + .channel = 0, + .address = AXP288_TS_ADC_H, + .datasheet_name = "TS_PIN", + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 1, + .address = AXP288_PMIC_ADC_H, + .datasheet_name = "PMIC_TEMP", + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 2, + .address = AXP288_GP_ADC_H, + .datasheet_name = "GPADC", + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 3, + .address = AXP20X_BATT_CHRG_I_H, + .datasheet_name = "BATT_CHG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 4, + .address = AXP20X_BATT_DISCHRG_I_H, + .datasheet_name = "BATT_DISCHRG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_VOLTAGE, + .channel = 5, + .address = AXP20X_BATT_V_H, + .datasheet_name = "BATT_V", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \ + _consumer_channel) \ + { \ + .adc_channel_label = _adc_channel_label, \ + .consumer_dev_name = _consumer_dev_name, \ + .consumer_channel = _consumer_channel, \ + } + +/* for consumer drivers */ +static struct iio_map axp288_adc_default_maps[] = { + AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), + AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), + AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), + AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), + AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), + AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), + {}, +}; + +static int axp288_adc_read_channel(int *val, unsigned long address, + struct regmap *regmap) +{ + u8 buf[2]; + + if (regmap_bulk_read(regmap, address, buf, 2)) + return -EIO; + *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); + + return IIO_VAL_INT; +} + +static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, + unsigned long address) +{ + /* channels other than GPADC do not need to switch TS pin */ + if (address != AXP288_GP_ADC_H) + return 0; + + return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); +} + +static int axp288_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct axp288_adc_info *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, + chan->address)) { + dev_err(&indio_dev->dev, "GPADC mode\n"); + ret = -EINVAL; + break; + } + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, + chan->address)) + dev_err(&indio_dev->dev, "TS pin restore\n"); + break; + case IIO_CHAN_INFO_PROCESSED: + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int axp288_adc_set_state(struct regmap *regmap) +{ + /* ADC should be always enabled for internal FG to function */ + if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) + return -EIO; + + return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); +} + +static const struct iio_info axp288_adc_iio_info = { + .read_raw = &axp288_adc_read_raw, + .driver_module = THIS_MODULE, +}; + +static int axp288_adc_probe(struct platform_device *pdev) +{ + int ret; + struct axp288_adc_info *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return info->irq; + } + platform_set_drvdata(pdev, indio_dev); + info->regmap = axp20x->regmap; + /* + * Set ADC to enabled state at all time, including system suspend. + * otherwise internal fuel gauge functionality may be affected. + */ + ret = axp288_adc_set_state(axp20x->regmap); + if (ret) { + dev_err(&pdev->dev, "unable to enable ADC device\n"); + return ret; + } + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = pdev->name; + indio_dev->channels = axp288_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); + indio_dev->info = &axp288_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register iio device\n"); + goto err_array_unregister; + } + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + + return ret; +} + +static int axp288_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static struct platform_device_id axp288_adc_id_table[] = { + { .name = "axp288_adc" }, + {}, +}; + +static struct platform_driver axp288_adc_driver = { + .probe = axp288_adc_probe, + .remove = axp288_adc_remove, + .id_table = axp288_adc_id_table, + .driver = { + .name = "axp288_adc", + }, +}; + +MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); + +module_platform_driver(axp288_adc_driver); + +MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>"); +MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index bc203485716d..8afa28e4570e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -421,7 +421,7 @@ static int evdev_open(struct inode *inode, struct file *file) err_free_client: evdev_detach_client(evdev, client); - kfree(client); + kvfree(client); return error; } diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 932ed9be9ff3..b10aaeda2bb4 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2190,7 +2190,7 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, ret = smiapp_set_compose(subdev, fh, sel); break; default: - BUG(); + ret = -EINVAL; } mutex_unlock(&sensor->mutex); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 331eddac7222..3bd386c371f7 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1078,7 +1078,7 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, for (line = 0; line < lines; line++) { while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); - sg++; + sg = sg_next(sg); } if (lpi && line > 0 && !(line % lpi)) @@ -1101,14 +1101,14 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, *(rp++) = cpu_to_le32(0); /* bits 63-32 */ todo -= (sg_dma_len(sg)-offset); offset = 0; - sg++; + sg = sg_next(sg); while (todo > sg_dma_len(sg)) { *(rp++) = cpu_to_le32(RISC_WRITE| sg_dma_len(sg)); *(rp++) = cpu_to_le32(sg_dma_address(sg)); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ todo -= sg_dma_len(sg); - sg++; + sg = sg_next(sg); } *(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo); *(rp++) = cpu_to_le32(sg_dma_address(sg)); diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 172583d736fe..8cbe6b49f4c2 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -105,11 +105,8 @@ static irqreturn_t solo_isr(int irq, void *data) if (!status) return IRQ_NONE; - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } + /* Acknowledge all interrupts immediately */ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); if (status & SOLO_IRQ_PCI_ERR) solo_p2m_error_isr(solo_dev); @@ -132,9 +129,6 @@ static irqreturn_t solo_isr(int irq, void *data) if (status & SOLO_IRQ_G723) solo_g723_isr(solo_dev); - /* Clear all interrupts handled */ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); - return IRQ_HANDLED; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index f1f098e22f7e..d16bc67af732 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -259,8 +259,8 @@ again: case 32: if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { protocol = RC_TYPE_RC6_MCE; - scancode &= ~RC6_6A_MCE_TOGGLE_MASK; toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; } else { protocol = RC_BIT_RC6_6A_32; toggle = 0; diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index ccc00099b261..1c0dbf428a3a 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -632,7 +632,7 @@ static void s2255_fillbuff(struct s2255_vc *vc, break; case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: - buf->vb.v4l2_buf.length = jpgsize; + vb2_set_plane_payload(&buf->vb, 0, jpgsize); memcpy(vbuf, tmpbuf, jpgsize); break; case V4L2_PIX_FMT_YUV422P: diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1456ea70bbc7..2e6b7311fabc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -59,6 +59,17 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. +config MFD_ATMEL_HLCDC + tristate "Atmel HLCDC (High-end LCD Controller)" + select MFD_CORE + select REGMAP_MMIO + depends on OF + help + If you say yes here you get support for the HLCDC block. + 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_BCM590XX tristate "Broadcom BCM590xx PMUs" select MFD_CORE @@ -74,7 +85,8 @@ config MFD_AXP20X select REGMAP_IRQ depends on I2C=y help - If you say Y here you get support for the X-Powers AXP202 and AXP209. + If you say Y here you get support for the X-Powers AXP202, AXP209 and + AXP288 power management IC (PMIC). 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. @@ -183,6 +195,16 @@ config MFD_DA9063 Additional drivers must be enabled in order to use the functionality of the device. +config MFD_DLN2 + tristate "Diolan DLN2 support" + select MFD_CORE + depends on USB + help + This adds support for Diolan USB-I2C/SPI/GPIO Master Adapter + DLN-2. Additional drivers such as I2C_DLN2, GPIO_DLN2, + etc. must be enabled in order to use the functionality of + the device. + config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) @@ -655,7 +677,6 @@ config MFD_SEC_CORE select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - select REGULATOR help Support for the Samsung Electronics MFD series. This driver provides common support for accessing the device, diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8bd54b1253af..53467e211381 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,7 +13,7 @@ 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 +rtsx_pci-objs := rtsx_pcr.o rtsx_gops.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 @@ -157,6 +157,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o +obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o @@ -174,6 +175,7 @@ obj-$(CONFIG_MFD_STW481X) += stw481x.o obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o +obj-$(CONFIG_MFD_DLN2) += dln2.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 8e0dae59844d..94dbcdd2a1ff 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -85,63 +85,6 @@ shutdown: } } -/* - * Use the AB WD to reset the platform. It will perform a hard - * reset instead of a soft reset. Write the reset reason to - * the AB before reset, which can be read upon restart. - */ -void ab8500_restart(char mode, const char *cmd) -{ - struct ab8500_platform_data *plat; - struct ab8500_sysctrl_platform_data *pdata; - u16 reason = 0; - u8 val; - - if (sysctrl_dev == NULL) { - pr_err("%s: sysctrl not initialized\n", __func__); - return; - } - - plat = dev_get_platdata(sysctrl_dev->parent); - pdata = plat->sysctrl; - if (pdata && pdata->reboot_reason_code) - reason = pdata->reboot_reason_code(cmd); - else - pr_warn("[%s] No reboot reason set. Default reason %d\n", - __func__, reason); - - /* - * Disable RTC alarm, just a precaution so that no alarm - * is running when WD reset is executed. - */ - abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC, - RTC_CTRL , &val); - abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, - RTC_CTRL , (val & ~RTC_ALARM_ENABLE)); - - /* - * Android is not using the RTC alarm registers during reboot - * so we borrow them for writing the reason of reset - */ - - /* reason[8 LSB] */ - val = reason & 0xFF; - abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, - AB8500_ALARM_MIN_LOW , val); - - /* reason[8 MSB] */ - val = (reason>>8) & 0xFF; - abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, - AB8500_ALARM_MIN_MID , val); - - /* Setting WD timeout to 0 */ - ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0); - - /* Setting the parameters to AB8500 WD*/ - ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD | - AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD)); -} - static inline bool valid_bank(u8 bank) { return ((bank == AB8500_SYS_CTRL1_BLOCK) || diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 5145d78bf07e..8ef58bcff193 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -75,7 +75,9 @@ static int arizona_spi_probe(struct spi_device *spi) static int arizona_spi_remove(struct spi_device *spi) { struct arizona *arizona = spi_get_drvdata(spi); + arizona_dev_exit(arizona); + return 0; } diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c new file mode 100644 index 000000000000..cfd58f4cc5c3 --- /dev/null +++ b/drivers/mfd/atmel-hlcdc.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014 Free Electrons + * Copyright (C) 2014 Atmel + * + * Author: Boris BREZILLON <boris.brezillon@free-electrons.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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/mfd/atmel-hlcdc.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4) + +static const struct mfd_cell atmel_hlcdc_cells[] = { + { + .name = "atmel-hlcdc-pwm", + .of_compatible = "atmel,hlcdc-pwm", + }, + { + .name = "atmel-hlcdc-dc", + .of_compatible = "atmel,hlcdc-display-controller", + }, +}; + +static const struct regmap_config atmel_hlcdc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = ATMEL_HLCDC_REG_MAX, +}; + +static int atmel_hlcdc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atmel_hlcdc *hlcdc; + struct resource *res; + void __iomem *regs; + + hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL); + if (!hlcdc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + hlcdc->irq = platform_get_irq(pdev, 0); + if (hlcdc->irq < 0) + return hlcdc->irq; + + hlcdc->periph_clk = devm_clk_get(dev, "periph_clk"); + if (IS_ERR(hlcdc->periph_clk)) { + dev_err(dev, "failed to get peripheral clock\n"); + return PTR_ERR(hlcdc->periph_clk); + } + + hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); + if (IS_ERR(hlcdc->sys_clk)) { + dev_err(dev, "failed to get system clock\n"); + return PTR_ERR(hlcdc->sys_clk); + } + + hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); + if (IS_ERR(hlcdc->slow_clk)) { + dev_err(dev, "failed to get slow clock\n"); + return PTR_ERR(hlcdc->slow_clk); + } + + hlcdc->regmap = devm_regmap_init_mmio(dev, regs, + &atmel_hlcdc_regmap_config); + if (IS_ERR(hlcdc->regmap)) + return PTR_ERR(hlcdc->regmap); + + dev_set_drvdata(dev, hlcdc); + + return mfd_add_devices(dev, -1, atmel_hlcdc_cells, + ARRAY_SIZE(atmel_hlcdc_cells), + NULL, 0, NULL); +} + +static int atmel_hlcdc_remove(struct platform_device *pdev) +{ + mfd_remove_devices(&pdev->dev); + + return 0; +} + +static const struct of_device_id atmel_hlcdc_match[] = { + { .compatible = "atmel,sama5d3-hlcdc" }, + { /* sentinel */ }, +}; + +static struct platform_driver atmel_hlcdc_driver = { + .probe = atmel_hlcdc_probe, + .remove = atmel_hlcdc_remove, + .driver = { + .name = "atmel-hlcdc", + .of_match_table = atmel_hlcdc_match, + }, +}; +module_platform_driver(atmel_hlcdc_driver); + +MODULE_ALIAS("platform:atmel-hlcdc"); +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); +MODULE_DESCRIPTION("Atmel HLCDC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 6231adbb295d..c522ee22b1c0 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -1,9 +1,9 @@ /* - * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209 + * axp20x.c - MFD core driver for the X-Powers' Power Management ICs * - * 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. + * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC + * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature + * as well as configurable GPIOs. * * Author: Carlo Caione <carlo@caione.org> * @@ -25,9 +25,16 @@ #include <linux/mfd/core.h> #include <linux/of_device.h> #include <linux/of_irq.h> +#include <linux/acpi.h> #define AXP20X_OFF 0x80 +static const char const *axp20x_model_names[] = { + "AXP202", + "AXP209", + "AXP288", +}; + 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), @@ -47,6 +54,25 @@ static const struct regmap_access_table axp20x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), }; +static const struct regmap_range axp288_writeable_ranges[] = { + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), + regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), +}; + +static const struct regmap_range axp288_volatile_ranges[] = { + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), +}; + +static const struct regmap_access_table axp288_writeable_table = { + .yes_ranges = axp288_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp288_writeable_ranges), +}; + +static const struct regmap_access_table axp288_volatile_table = { + .yes_ranges = axp288_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges), +}; + static struct resource axp20x_pek_resources[] = { { .name = "PEK_DBR", @@ -61,6 +87,39 @@ static struct resource axp20x_pek_resources[] = { }, }; +static struct resource axp288_battery_resources[] = { + { + .start = AXP288_IRQ_QWBTU, + .end = AXP288_IRQ_QWBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WBTU, + .end = AXP288_IRQ_WBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_QWBTO, + .end = AXP288_IRQ_QWBTO, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WBTO, + .end = AXP288_IRQ_WBTO, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WL2, + .end = AXP288_IRQ_WL2, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WL1, + .end = AXP288_IRQ_WL1, + .flags = IORESOURCE_IRQ, + }, +}; + static const struct regmap_config axp20x_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -70,47 +129,96 @@ static const struct regmap_config axp20x_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -#define AXP20X_IRQ(_irq, _off, _mask) \ - [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } +static const struct regmap_config axp288_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp288_writeable_table, + .volatile_table = &axp288_volatile_table, + .max_register = AXP288_FG_TUNE5, + .cache_type = REGCACHE_RBTREE, +}; + +#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ + [_variant##_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), + INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP20X, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP20X, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP20X, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP20X, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP20X, VBUS_V_LOW, 0, 1), + INIT_REGMAP_IRQ(AXP20X, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP20X, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP20X, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP20X, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP20X, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP20X, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_HIGH, 1, 1), + INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_LOW, 1, 0), + INIT_REGMAP_IRQ(AXP20X, DIE_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP20X, CHARG_I_LOW, 2, 6), + INIT_REGMAP_IRQ(AXP20X, DCDC1_V_LONG, 2, 5), + INIT_REGMAP_IRQ(AXP20X, DCDC2_V_LONG, 2, 4), + INIT_REGMAP_IRQ(AXP20X, DCDC3_V_LONG, 2, 3), + INIT_REGMAP_IRQ(AXP20X, PEK_SHORT, 2, 1), + INIT_REGMAP_IRQ(AXP20X, PEK_LONG, 2, 0), + INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_ON, 3, 7), + INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_OFF, 3, 6), + INIT_REGMAP_IRQ(AXP20X, VBUS_VALID, 3, 5), + INIT_REGMAP_IRQ(AXP20X, VBUS_NOT_VALID, 3, 4), + INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_VALID, 3, 3), + INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_END, 3, 2), + INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL1, 3, 1), + INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL2, 3, 0), + INIT_REGMAP_IRQ(AXP20X, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP20X, PEK_RIS_EDGE, 4, 6), + INIT_REGMAP_IRQ(AXP20X, PEK_FAL_EDGE, 4, 5), + INIT_REGMAP_IRQ(AXP20X, GPIO3_INPUT, 4, 3), + INIT_REGMAP_IRQ(AXP20X, GPIO2_INPUT, 4, 2), + INIT_REGMAP_IRQ(AXP20X, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0), +}; + +/* some IRQs are compatible with axp20x models */ +static const struct regmap_irq axp288_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2), + INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3), + INIT_REGMAP_IRQ(AXP288, OV, 0, 4), + + INIT_REGMAP_IRQ(AXP288, DONE, 1, 2), + INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3), + INIT_REGMAP_IRQ(AXP288, SAFE_QUIT, 1, 4), + INIT_REGMAP_IRQ(AXP288, SAFE_ENTER, 1, 5), + INIT_REGMAP_IRQ(AXP288, ABSENT, 1, 6), + INIT_REGMAP_IRQ(AXP288, APPEND, 1, 7), + + INIT_REGMAP_IRQ(AXP288, QWBTU, 2, 0), + INIT_REGMAP_IRQ(AXP288, WBTU, 2, 1), + INIT_REGMAP_IRQ(AXP288, QWBTO, 2, 2), + INIT_REGMAP_IRQ(AXP288, WBTO, 2, 3), + INIT_REGMAP_IRQ(AXP288, QCBTU, 2, 4), + INIT_REGMAP_IRQ(AXP288, CBTU, 2, 5), + INIT_REGMAP_IRQ(AXP288, QCBTO, 2, 6), + INIT_REGMAP_IRQ(AXP288, CBTO, 2, 7), + + INIT_REGMAP_IRQ(AXP288, WL2, 3, 0), + INIT_REGMAP_IRQ(AXP288, WL1, 3, 1), + INIT_REGMAP_IRQ(AXP288, GPADC, 3, 2), + INIT_REGMAP_IRQ(AXP288, OT, 3, 7), + + INIT_REGMAP_IRQ(AXP288, GPIO0, 4, 0), + INIT_REGMAP_IRQ(AXP288, GPIO1, 4, 1), + INIT_REGMAP_IRQ(AXP288, POKO, 4, 2), + INIT_REGMAP_IRQ(AXP288, POKL, 4, 3), + INIT_REGMAP_IRQ(AXP288, POKS, 4, 4), + INIT_REGMAP_IRQ(AXP288, POKN, 4, 5), + INIT_REGMAP_IRQ(AXP288, POKP, 4, 6), + INIT_REGMAP_IRQ(AXP288, TIMER, 4, 7), + + INIT_REGMAP_IRQ(AXP288, MV_CHNG, 5, 0), + INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; static const struct of_device_id axp20x_of_match[] = { @@ -128,16 +236,39 @@ static const struct i2c_device_id axp20x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); +static const struct acpi_device_id axp20x_acpi_match[] = { + { + .id = "INT33F4", + .driver_data = AXP288_ID, + }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match); + 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, + .mask_invert = true, + .init_ack_masked = true, .irqs = axp20x_regmap_irqs, .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), + .num_regs = 5, + +}; + +static const struct regmap_irq_chip axp288_regmap_irq_chip = { + .name = "axp288_irq_chip", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, .mask_invert = true, .init_ack_masked = true, + .irqs = axp288_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp288_regmap_irqs), + .num_regs = 6, + }; static struct mfd_cell axp20x_cells[] = { @@ -150,36 +281,155 @@ static struct mfd_cell axp20x_cells[] = { }, }; +static struct resource axp288_adc_resources[] = { + { + .name = "GPADC", + .start = AXP288_IRQ_GPADC, + .end = AXP288_IRQ_GPADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource axp288_charger_resources[] = { + { + .start = AXP288_IRQ_OV, + .end = AXP288_IRQ_OV, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_DONE, + .end = AXP288_IRQ_DONE, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_CHARGING, + .end = AXP288_IRQ_CHARGING, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_SAFE_QUIT, + .end = AXP288_IRQ_SAFE_QUIT, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_SAFE_ENTER, + .end = AXP288_IRQ_SAFE_ENTER, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_QCBTU, + .end = AXP288_IRQ_QCBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_CBTU, + .end = AXP288_IRQ_CBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_QCBTO, + .end = AXP288_IRQ_QCBTO, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_CBTO, + .end = AXP288_IRQ_CBTO, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell axp288_cells[] = { + { + .name = "axp288_adc", + .num_resources = ARRAY_SIZE(axp288_adc_resources), + .resources = axp288_adc_resources, + }, + { + .name = "axp288_charger", + .num_resources = ARRAY_SIZE(axp288_charger_resources), + .resources = axp288_charger_resources, + }, + { + .name = "axp288_battery", + .num_resources = ARRAY_SIZE(axp288_battery_resources), + .resources = axp288_battery_resources, + }, +}; + static struct axp20x_dev *axp20x_pm_power_off; static void axp20x_power_off(void) { + if (axp20x_pm_power_off->variant == AXP288_ID) + return; + regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, AXP20X_OFF); } +static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) +{ + const struct acpi_device_id *acpi_id; + const struct of_device_id *of_id; + + if (dev->of_node) { + of_id = of_match_device(axp20x_of_match, dev); + if (!of_id) { + dev_err(dev, "Unable to match OF ID\n"); + return -ENODEV; + } + axp20x->variant = (long) of_id->data; + } else { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id || !acpi_id->driver_data) { + dev_err(dev, "Unable to match ACPI ID and data\n"); + return -ENODEV; + } + axp20x->variant = (long) acpi_id->driver_data; + } + + switch (axp20x->variant) { + case AXP202_ID: + case AXP209_ID: + axp20x->nr_cells = ARRAY_SIZE(axp20x_cells); + axp20x->cells = axp20x_cells; + axp20x->regmap_cfg = &axp20x_regmap_config; + axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; + break; + case AXP288_ID: + axp20x->cells = axp288_cells; + axp20x->nr_cells = ARRAY_SIZE(axp288_cells); + axp20x->regmap_cfg = &axp288_regmap_config; + axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; + break; + default: + dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant); + return -EINVAL; + } + dev_info(dev, "AXP20x variant %s found\n", + axp20x_model_names[axp20x->variant]); + + return 0; +} + 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; + ret = axp20x_match_device(axp20x, &i2c->dev); + if (ret) + return ret; axp20x->i2c_client = i2c; axp20x->dev = &i2c->dev; dev_set_drvdata(axp20x->dev, axp20x); - axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config); + axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); if (IS_ERR(axp20x->regmap)) { ret = PTR_ERR(axp20x->regmap); dev_err(&i2c->dev, "regmap init failed: %d\n", ret); @@ -188,15 +438,15 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, IRQF_ONESHOT | IRQF_SHARED, -1, - &axp20x_regmap_irq_chip, + 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); + ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells, + axp20x->nr_cells, NULL, 0, NULL); if (ret) { dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); @@ -234,6 +484,7 @@ static struct i2c_driver axp20x_i2c_driver = { .name = "axp20x", .owner = THIS_MODULE, .of_match_table = of_match_ptr(axp20x_of_match), + .acpi_match_table = ACPI_PTR(axp20x_acpi_match), }, .probe = axp20x_i2c_probe, .remove = axp20x_i2c_remove, diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index 93db8bb8c8f0..f38bc98a3c57 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -118,7 +118,7 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) da9063->irq_base = pdata->irq_base; } else { da9063->flags = 0; - da9063->irq_base = 0; + da9063->irq_base = -1; } da9063->chip_irq = irq; @@ -168,6 +168,8 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) return ret; } + da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq); + ret = mfd_add_devices(da9063->dev, -1, da9063_devs, ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, NULL); diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 193cf168ba84..a8204730f01c 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -3150,23 +3150,28 @@ static int db8500_prcmu_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu"); if (!res) { dev_err(&pdev->dev, "no prcmu memory region provided\n"); - return -ENOENT; + return -EINVAL; } prcmu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!prcmu_base) { dev_err(&pdev->dev, "failed to ioremap prcmu register memory\n"); - return -ENOENT; + return -ENOMEM; } init_prcm_registers(); dbx500_fw_version_init(pdev, pdata->version_offset); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); if (!res) { dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); - return -ENOENT; + return -EINVAL; } tcdm_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!tcdm_base) { + dev_err(&pdev->dev, + "failed to ioremap prcmu-tcdm register memory\n"); + return -ENOMEM; + } /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); @@ -3174,15 +3179,14 @@ static int db8500_prcmu_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "no prcmu irq provided\n"); - return -ENOENT; + return irq; } err = request_threaded_irq(irq, prcmu_irq_handler, prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); if (err < 0) { pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n"); - err = -EBUSY; - goto no_irq_return; + return err; } db8500_irq_init(np); @@ -3206,7 +3210,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) if (err) { mfd_remove_devices(&pdev->dev); pr_err("prcmu: Failed to add subdevices\n"); - goto no_irq_return; + return err; } } @@ -3214,12 +3218,10 @@ static int db8500_prcmu_probe(struct platform_device *pdev) if (err) { mfd_remove_devices(&pdev->dev); pr_err("prcmu: Failed to add ab8500 subdevice\n"); - goto no_irq_return; + return err; } pr_info("DB8500 PRCMU initialized\n"); - -no_irq_return: return err; } static const struct of_device_id db8500_prcmu_match[] = { diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c new file mode 100644 index 000000000000..6d49685d4ee4 --- /dev/null +++ b/drivers/mfd/dln2.c @@ -0,0 +1,781 @@ +/* + * Driver for the Diolan DLN-2 USB adapter + * + * Copyright (c) 2014 Intel Corporation + * + * Derived from: + * i2c-diolan-u2c.c + * Copyright (c) 2010-2011 Ericsson AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/dln2.h> +#include <linux/rculist.h> + +struct dln2_header { + __le16 size; + __le16 id; + __le16 echo; + __le16 handle; +}; + +struct dln2_response { + struct dln2_header hdr; + __le16 result; +}; + +#define DLN2_GENERIC_MODULE_ID 0x00 +#define DLN2_GENERIC_CMD(cmd) DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID) +#define CMD_GET_DEVICE_VER DLN2_GENERIC_CMD(0x30) +#define CMD_GET_DEVICE_SN DLN2_GENERIC_CMD(0x31) + +#define DLN2_HW_ID 0x200 +#define DLN2_USB_TIMEOUT 200 /* in ms */ +#define DLN2_MAX_RX_SLOTS 16 +#define DLN2_MAX_URBS 16 +#define DLN2_RX_BUF_SIZE 512 + +enum dln2_handle { + DLN2_HANDLE_EVENT = 0, /* don't change, hardware defined */ + DLN2_HANDLE_CTRL, + DLN2_HANDLE_GPIO, + DLN2_HANDLE_I2C, + DLN2_HANDLE_SPI, + DLN2_HANDLES +}; + +/* + * Receive context used between the receive demultiplexer and the transfer + * routine. While sending a request the transfer routine will look for a free + * receive context and use it to wait for a response and to receive the URB and + * thus the response data. + */ +struct dln2_rx_context { + /* completion used to wait for a response */ + struct completion done; + + /* if non-NULL the URB contains the response */ + struct urb *urb; + + /* if true then this context is used to wait for a response */ + bool in_use; +}; + +/* + * Receive contexts for a particular DLN2 module (i2c, gpio, etc.). We use the + * handle header field to identify the module in dln2_dev.mod_rx_slots and then + * the echo header field to index the slots field and find the receive context + * for a particular request. + */ +struct dln2_mod_rx_slots { + /* RX slots bitmap */ + DECLARE_BITMAP(bmap, DLN2_MAX_RX_SLOTS); + + /* used to wait for a free RX slot */ + wait_queue_head_t wq; + + /* used to wait for an RX operation to complete */ + struct dln2_rx_context slots[DLN2_MAX_RX_SLOTS]; + + /* avoid races between alloc/free_rx_slot and dln2_rx_transfer */ + spinlock_t lock; +}; + +struct dln2_dev { + struct usb_device *usb_dev; + struct usb_interface *interface; + u8 ep_in; + u8 ep_out; + + struct urb *rx_urb[DLN2_MAX_URBS]; + void *rx_buf[DLN2_MAX_URBS]; + + struct dln2_mod_rx_slots mod_rx_slots[DLN2_HANDLES]; + + struct list_head event_cb_list; + spinlock_t event_cb_lock; + + bool disconnect; + int active_transfers; + wait_queue_head_t disconnect_wq; + spinlock_t disconnect_lock; +}; + +struct dln2_event_cb_entry { + struct list_head list; + u16 id; + struct platform_device *pdev; + dln2_event_cb_t callback; +}; + +int dln2_register_event_cb(struct platform_device *pdev, u16 id, + dln2_event_cb_t event_cb) +{ + struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); + struct dln2_event_cb_entry *i, *entry; + unsigned long flags; + int ret = 0; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->id = id; + entry->callback = event_cb; + entry->pdev = pdev; + + spin_lock_irqsave(&dln2->event_cb_lock, flags); + + list_for_each_entry(i, &dln2->event_cb_list, list) { + if (i->id == id) { + ret = -EBUSY; + break; + } + } + + if (!ret) + list_add_rcu(&entry->list, &dln2->event_cb_list); + + spin_unlock_irqrestore(&dln2->event_cb_lock, flags); + + if (ret) + kfree(entry); + + return ret; +} +EXPORT_SYMBOL(dln2_register_event_cb); + +void dln2_unregister_event_cb(struct platform_device *pdev, u16 id) +{ + struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); + struct dln2_event_cb_entry *i; + unsigned long flags; + bool found = false; + + spin_lock_irqsave(&dln2->event_cb_lock, flags); + + list_for_each_entry(i, &dln2->event_cb_list, list) { + if (i->id == id) { + list_del_rcu(&i->list); + found = true; + break; + } + } + + spin_unlock_irqrestore(&dln2->event_cb_lock, flags); + + if (found) { + synchronize_rcu(); + kfree(i); + } +} +EXPORT_SYMBOL(dln2_unregister_event_cb); + +/* + * Returns true if a valid transfer slot is found. In this case the URB must not + * be resubmitted immediately in dln2_rx as we need the data when dln2_transfer + * is woke up. It will be resubmitted there. + */ +static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb, + u16 handle, u16 rx_slot) +{ + struct device *dev = &dln2->interface->dev; + struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; + struct dln2_rx_context *rxc; + bool valid_slot = false; + + if (rx_slot >= DLN2_MAX_RX_SLOTS) + goto out; + + rxc = &rxs->slots[rx_slot]; + + /* + * No need to disable interrupts as this lock is not taken in interrupt + * context elsewhere in this driver. This function (or its callers) are + * also not exported to other modules. + */ + spin_lock(&rxs->lock); + if (rxc->in_use && !rxc->urb) { + rxc->urb = urb; + complete(&rxc->done); + valid_slot = true; + } + spin_unlock(&rxs->lock); + +out: + if (!valid_slot) + dev_warn(dev, "bad/late response %d/%d\n", handle, rx_slot); + + return valid_slot; +} + +static void dln2_run_event_callbacks(struct dln2_dev *dln2, u16 id, u16 echo, + void *data, int len) +{ + struct dln2_event_cb_entry *i; + + rcu_read_lock(); + + list_for_each_entry_rcu(i, &dln2->event_cb_list, list) { + if (i->id == id) { + i->callback(i->pdev, echo, data, len); + break; + } + } + + rcu_read_unlock(); +} + +static void dln2_rx(struct urb *urb) +{ + struct dln2_dev *dln2 = urb->context; + struct dln2_header *hdr = urb->transfer_buffer; + struct device *dev = &dln2->interface->dev; + u16 id, echo, handle, size; + u8 *data; + int len; + int err; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EPIPE: + /* this urb is terminated, clean up */ + dev_dbg(dev, "urb shutting down with status %d\n", urb->status); + return; + default: + dev_dbg(dev, "nonzero urb status received %d\n", urb->status); + goto out; + } + + if (urb->actual_length < sizeof(struct dln2_header)) { + dev_err(dev, "short response: %d\n", urb->actual_length); + goto out; + } + + handle = le16_to_cpu(hdr->handle); + id = le16_to_cpu(hdr->id); + echo = le16_to_cpu(hdr->echo); + size = le16_to_cpu(hdr->size); + + if (size != urb->actual_length) { + dev_err(dev, "size mismatch: handle %x cmd %x echo %x size %d actual %d\n", + handle, id, echo, size, urb->actual_length); + goto out; + } + + if (handle >= DLN2_HANDLES) { + dev_warn(dev, "invalid handle %d\n", handle); + goto out; + } + + data = urb->transfer_buffer + sizeof(struct dln2_header); + len = urb->actual_length - sizeof(struct dln2_header); + + if (handle == DLN2_HANDLE_EVENT) { + dln2_run_event_callbacks(dln2, id, echo, data, len); + } else { + /* URB will be re-submitted in _dln2_transfer (free_rx_slot) */ + if (dln2_transfer_complete(dln2, urb, handle, echo)) + return; + } + +out: + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) + dev_err(dev, "failed to resubmit RX URB: %d\n", err); +} + +static void *dln2_prep_buf(u16 handle, u16 cmd, u16 echo, const void *obuf, + int *obuf_len, gfp_t gfp) +{ + int len; + void *buf; + struct dln2_header *hdr; + + len = *obuf_len + sizeof(*hdr); + buf = kmalloc(len, gfp); + if (!buf) + return NULL; + + hdr = (struct dln2_header *)buf; + hdr->id = cpu_to_le16(cmd); + hdr->size = cpu_to_le16(len); + hdr->echo = cpu_to_le16(echo); + hdr->handle = cpu_to_le16(handle); + + memcpy(buf + sizeof(*hdr), obuf, *obuf_len); + + *obuf_len = len; + + return buf; +} + +static int dln2_send_wait(struct dln2_dev *dln2, u16 handle, u16 cmd, u16 echo, + const void *obuf, int obuf_len) +{ + int ret = 0; + int len = obuf_len; + void *buf; + int actual; + + buf = dln2_prep_buf(handle, cmd, echo, obuf, &len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_bulk_msg(dln2->usb_dev, + usb_sndbulkpipe(dln2->usb_dev, dln2->ep_out), + buf, len, &actual, DLN2_USB_TIMEOUT); + + kfree(buf); + + return ret; +} + +static bool find_free_slot(struct dln2_dev *dln2, u16 handle, int *slot) +{ + struct dln2_mod_rx_slots *rxs; + unsigned long flags; + + if (dln2->disconnect) { + *slot = -ENODEV; + return true; + } + + rxs = &dln2->mod_rx_slots[handle]; + + spin_lock_irqsave(&rxs->lock, flags); + + *slot = find_first_zero_bit(rxs->bmap, DLN2_MAX_RX_SLOTS); + + if (*slot < DLN2_MAX_RX_SLOTS) { + struct dln2_rx_context *rxc = &rxs->slots[*slot]; + + set_bit(*slot, rxs->bmap); + rxc->in_use = true; + } + + spin_unlock_irqrestore(&rxs->lock, flags); + + return *slot < DLN2_MAX_RX_SLOTS; +} + +static int alloc_rx_slot(struct dln2_dev *dln2, u16 handle) +{ + int ret; + int slot; + + /* + * No need to timeout here, the wait is bounded by the timeout in + * _dln2_transfer. + */ + ret = wait_event_interruptible(dln2->mod_rx_slots[handle].wq, + find_free_slot(dln2, handle, &slot)); + if (ret < 0) + return ret; + + return slot; +} + +static void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot) +{ + struct dln2_mod_rx_slots *rxs; + struct urb *urb = NULL; + unsigned long flags; + struct dln2_rx_context *rxc; + + rxs = &dln2->mod_rx_slots[handle]; + + spin_lock_irqsave(&rxs->lock, flags); + + clear_bit(slot, rxs->bmap); + + rxc = &rxs->slots[slot]; + rxc->in_use = false; + urb = rxc->urb; + rxc->urb = NULL; + reinit_completion(&rxc->done); + + spin_unlock_irqrestore(&rxs->lock, flags); + + if (urb) { + int err; + struct device *dev = &dln2->interface->dev; + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) + dev_err(dev, "failed to resubmit RX URB: %d\n", err); + } + + wake_up_interruptible(&rxs->wq); +} + +static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, + const void *obuf, unsigned obuf_len, + void *ibuf, unsigned *ibuf_len) +{ + int ret = 0; + int rx_slot; + struct dln2_response *rsp; + struct dln2_rx_context *rxc; + struct device *dev = &dln2->interface->dev; + const unsigned long timeout = DLN2_USB_TIMEOUT * HZ / 1000; + struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; + int size; + + spin_lock(&dln2->disconnect_lock); + if (!dln2->disconnect) + dln2->active_transfers++; + else + ret = -ENODEV; + spin_unlock(&dln2->disconnect_lock); + + if (ret) + return ret; + + rx_slot = alloc_rx_slot(dln2, handle); + if (rx_slot < 0) { + ret = rx_slot; + goto out_decr; + } + + ret = dln2_send_wait(dln2, handle, cmd, rx_slot, obuf, obuf_len); + if (ret < 0) { + dev_err(dev, "USB write failed: %d\n", ret); + goto out_free_rx_slot; + } + + rxc = &rxs->slots[rx_slot]; + + ret = wait_for_completion_interruptible_timeout(&rxc->done, timeout); + if (ret <= 0) { + if (!ret) + ret = -ETIMEDOUT; + goto out_free_rx_slot; + } else { + ret = 0; + } + + if (dln2->disconnect) { + ret = -ENODEV; + goto out_free_rx_slot; + } + + /* if we got here we know that the response header has been checked */ + rsp = rxc->urb->transfer_buffer; + size = le16_to_cpu(rsp->hdr.size); + + if (size < sizeof(*rsp)) { + ret = -EPROTO; + goto out_free_rx_slot; + } + + if (le16_to_cpu(rsp->result) > 0x80) { + dev_dbg(dev, "%d received response with error %d\n", + handle, le16_to_cpu(rsp->result)); + ret = -EREMOTEIO; + goto out_free_rx_slot; + } + + if (!ibuf) + goto out_free_rx_slot; + + if (*ibuf_len > size - sizeof(*rsp)) + *ibuf_len = size - sizeof(*rsp); + + memcpy(ibuf, rsp + 1, *ibuf_len); + +out_free_rx_slot: + free_rx_slot(dln2, handle, rx_slot); +out_decr: + spin_lock(&dln2->disconnect_lock); + dln2->active_transfers--; + spin_unlock(&dln2->disconnect_lock); + if (dln2->disconnect) + wake_up(&dln2->disconnect_wq); + + return ret; +} + +int dln2_transfer(struct platform_device *pdev, u16 cmd, + const void *obuf, unsigned obuf_len, + void *ibuf, unsigned *ibuf_len) +{ + struct dln2_platform_data *dln2_pdata; + struct dln2_dev *dln2; + u16 handle; + + dln2 = dev_get_drvdata(pdev->dev.parent); + dln2_pdata = dev_get_platdata(&pdev->dev); + handle = dln2_pdata->handle; + + return _dln2_transfer(dln2, handle, cmd, obuf, obuf_len, ibuf, + ibuf_len); +} +EXPORT_SYMBOL(dln2_transfer); + +static int dln2_check_hw(struct dln2_dev *dln2) +{ + int ret; + __le32 hw_type; + int len = sizeof(hw_type); + + ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_VER, + NULL, 0, &hw_type, &len); + if (ret < 0) + return ret; + if (len < sizeof(hw_type)) + return -EREMOTEIO; + + if (le32_to_cpu(hw_type) != DLN2_HW_ID) { + dev_err(&dln2->interface->dev, "Device ID 0x%x not supported\n", + le32_to_cpu(hw_type)); + return -ENODEV; + } + + return 0; +} + +static int dln2_print_serialno(struct dln2_dev *dln2) +{ + int ret; + __le32 serial_no; + int len = sizeof(serial_no); + struct device *dev = &dln2->interface->dev; + + ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_SN, NULL, 0, + &serial_no, &len); + if (ret < 0) + return ret; + if (len < sizeof(serial_no)) + return -EREMOTEIO; + + dev_info(dev, "Diolan DLN2 serial %u\n", le32_to_cpu(serial_no)); + + return 0; +} + +static int dln2_hw_init(struct dln2_dev *dln2) +{ + int ret; + + ret = dln2_check_hw(dln2); + if (ret < 0) + return ret; + + return dln2_print_serialno(dln2); +} + +static void dln2_free_rx_urbs(struct dln2_dev *dln2) +{ + int i; + + for (i = 0; i < DLN2_MAX_URBS; i++) { + usb_kill_urb(dln2->rx_urb[i]); + usb_free_urb(dln2->rx_urb[i]); + kfree(dln2->rx_buf[i]); + } +} + +static void dln2_free(struct dln2_dev *dln2) +{ + dln2_free_rx_urbs(dln2); + usb_put_dev(dln2->usb_dev); + kfree(dln2); +} + +static int dln2_setup_rx_urbs(struct dln2_dev *dln2, + struct usb_host_interface *hostif) +{ + int i; + int ret; + const int rx_max_size = DLN2_RX_BUF_SIZE; + struct device *dev = &dln2->interface->dev; + + for (i = 0; i < DLN2_MAX_URBS; i++) { + dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); + if (!dln2->rx_buf[i]) + return -ENOMEM; + + dln2->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dln2->rx_urb[i]) + return -ENOMEM; + + usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev, + usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in), + dln2->rx_buf[i], rx_max_size, dln2_rx, dln2); + + ret = usb_submit_urb(dln2->rx_urb[i], GFP_KERNEL); + if (ret < 0) { + dev_err(dev, "failed to submit RX URB: %d\n", ret); + return ret; + } + } + + return 0; +} + +static struct dln2_platform_data dln2_pdata_gpio = { + .handle = DLN2_HANDLE_GPIO, +}; + +/* Only one I2C port seems to be supported on current hardware */ +static struct dln2_platform_data dln2_pdata_i2c = { + .handle = DLN2_HANDLE_I2C, + .port = 0, +}; + +/* Only one SPI port supported */ +static struct dln2_platform_data dln2_pdata_spi = { + .handle = DLN2_HANDLE_SPI, + .port = 0, +}; + +static const struct mfd_cell dln2_devs[] = { + { + .name = "dln2-gpio", + .platform_data = &dln2_pdata_gpio, + .pdata_size = sizeof(struct dln2_platform_data), + }, + { + .name = "dln2-i2c", + .platform_data = &dln2_pdata_i2c, + .pdata_size = sizeof(struct dln2_platform_data), + }, + { + .name = "dln2-spi", + .platform_data = &dln2_pdata_spi, + .pdata_size = sizeof(struct dln2_platform_data), + }, +}; + +static void dln2_disconnect(struct usb_interface *interface) +{ + struct dln2_dev *dln2 = usb_get_intfdata(interface); + int i, j; + + /* don't allow starting new transfers */ + spin_lock(&dln2->disconnect_lock); + dln2->disconnect = true; + spin_unlock(&dln2->disconnect_lock); + + /* cancel in progress transfers */ + for (i = 0; i < DLN2_HANDLES; i++) { + struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[i]; + unsigned long flags; + + spin_lock_irqsave(&rxs->lock, flags); + + /* cancel all response waiters */ + for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) { + struct dln2_rx_context *rxc = &rxs->slots[j]; + + if (rxc->in_use) + complete(&rxc->done); + } + + spin_unlock_irqrestore(&rxs->lock, flags); + } + + /* wait for transfers to end */ + wait_event(dln2->disconnect_wq, !dln2->active_transfers); + + mfd_remove_devices(&interface->dev); + + dln2_free(dln2); +} + +static int dln2_probe(struct usb_interface *interface, + const struct usb_device_id *usb_id) +{ + struct usb_host_interface *hostif = interface->cur_altsetting; + struct device *dev = &interface->dev; + struct dln2_dev *dln2; + int ret; + int i, j; + + if (hostif->desc.bInterfaceNumber != 0 || + hostif->desc.bNumEndpoints < 2) + return -ENODEV; + + dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress; + dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress; + dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dln2->interface = interface; + usb_set_intfdata(interface, dln2); + init_waitqueue_head(&dln2->disconnect_wq); + + for (i = 0; i < DLN2_HANDLES; i++) { + init_waitqueue_head(&dln2->mod_rx_slots[i].wq); + spin_lock_init(&dln2->mod_rx_slots[i].lock); + for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) + init_completion(&dln2->mod_rx_slots[i].slots[j].done); + } + + spin_lock_init(&dln2->event_cb_lock); + spin_lock_init(&dln2->disconnect_lock); + INIT_LIST_HEAD(&dln2->event_cb_list); + + ret = dln2_setup_rx_urbs(dln2, hostif); + if (ret) + goto out_cleanup; + + ret = dln2_hw_init(dln2); + if (ret < 0) { + dev_err(dev, "failed to initialize hardware\n"); + goto out_cleanup; + } + + ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs)); + if (ret != 0) { + dev_err(dev, "failed to add mfd devices to core\n"); + goto out_cleanup; + } + + return 0; + +out_cleanup: + dln2_free(dln2); + + return ret; +} + +static const struct usb_device_id dln2_table[] = { + { USB_DEVICE(0xa257, 0x2013) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dln2_table); + +static struct usb_driver dln2_driver = { + .name = "dln2", + .probe = dln2_probe, + .disconnect = dln2_disconnect, + .id_table = dln2_table, +}; + +module_usb_driver(dln2_driver); + +MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); +MODULE_DESCRIPTION("Core driver for the Diolan DLN2 interface adapter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index c980da479a35..5c38df35a84d 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -193,11 +193,7 @@ static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; } - ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); - if (ret) - mfd_remove_devices(&dev->dev); - - return ret; + return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); } static void lpc_sch_remove(struct pci_dev *dev) diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index de96b7fb1f6d..3bf8def82f1e 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -1,7 +1,7 @@ /* * max14577.c - mfd core driver for the Maxim 14577/77836 * - * Copyright (C) 2014 Samsung Electrnoics + * Copyright (C) 2014 Samsung Electronics * Chanwoo Choi <cw00.choi@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com> * diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index 711773e8e64b..a159593e27a0 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -43,9 +43,15 @@ static const struct mfd_cell max77693_devs[] = { { .name = "max77693-pmic", }, - { .name = "max77693-charger", }, + { + .name = "max77693-charger", + .of_compatible = "maxim,max77693-charger", + }, { .name = "max77693-muic", }, - { .name = "max77693-haptic", }, + { + .name = "max77693-haptic", + .of_compatible = "maxim,max77693-haptic", + }, { .name = "max77693-flash", .of_compatible = "maxim,max77693-flash", @@ -147,6 +153,12 @@ static const struct regmap_irq_chip max77693_muic_irq_chip = { .num_irqs = ARRAY_SIZE(max77693_muic_irqs), }; +static const struct regmap_config max77693_regmap_haptic_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77693_HAPTIC_REG_END, +}; + static int max77693_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -196,6 +208,15 @@ static int max77693_i2c_probe(struct i2c_client *i2c, } i2c_set_clientdata(max77693->haptic, max77693); + max77693->regmap_haptic = devm_regmap_init_i2c(max77693->haptic, + &max77693_regmap_haptic_config); + if (IS_ERR(max77693->regmap_haptic)) { + ret = PTR_ERR(max77693->regmap_haptic); + dev_err(max77693->dev, + "failed to initialize haptic register map: %d\n", ret); + goto err_regmap; + } + /* * Initialize register map for MUIC device because use regmap-muic * instance of MUIC device when irq of max77693 is initialized @@ -207,7 +228,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(max77693->regmap_muic); dev_err(max77693->dev, "failed to allocate register map: %d\n", ret); - goto err_regmap_muic; + goto err_regmap; } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, @@ -217,7 +238,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, &max77693->irq_data_led); if (ret) { dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); - goto err_regmap_muic; + goto err_regmap; } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, @@ -280,7 +301,7 @@ err_irq_charger: regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys); err_irq_topsys: regmap_del_irq_chip(max77693->irq, max77693->irq_data_led); -err_regmap_muic: +err_regmap: i2c_unregister_device(max77693->haptic); err_i2c_haptic: i2c_unregister_device(max77693->muic); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f3338fe9d069..2a87f69be53d 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -125,9 +125,15 @@ static int mfd_add_device(struct device *parent, int id, struct platform_device *pdev; struct device_node *np = NULL; int ret = -ENOMEM; + int platform_id; int r; - pdev = platform_device_alloc(cell->name, id + cell->id); + if (id < 0) + platform_id = id; + else + platform_id = id + cell->id; + + pdev = platform_device_alloc(cell->name, platform_id); if (!pdev) goto fail_alloc; diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index 9c8eec80ceed..32407404d838 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -130,6 +130,12 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) static int rts5227_optimize_phy(struct rtsx_pcr *pcr) { + int err; + + err = rtsx_gops_pm_reset(pcr); + if (err < 0) + return err; + /* Optimize RX sensitivity */ return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); } diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 573de7bfcced..cf425cc959d5 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -130,6 +130,10 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) { int err; + err = rtsx_gops_pm_reset(pcr); + if (err < 0) + return err; + 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 | diff --git a/drivers/mfd/rtsx_gops.c b/drivers/mfd/rtsx_gops.c new file mode 100644 index 000000000000..b1a98c678593 --- /dev/null +++ b/drivers/mfd/rtsx_gops.c @@ -0,0 +1,37 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: + * Micky Ching <micky_ching@realsil.com.cn> + */ + +#include <linux/mfd/rtsx_pci.h> +#include "rtsx_pcr.h" + +int rtsx_gops_pm_reset(struct rtsx_pcr *pcr) +{ + int err; + + /* init aspm */ + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0x00); + err = rtsx_pci_update_cfg_byte(pcr, LCTLR, ~LCTLR_ASPM_CTL_MASK, 0x00); + if (err < 0) + return err; + + /* reset PM_CTRL3 before send buffer cmd */ + return rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); +} diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 07e4c2ebf05a..fe2bbb67defc 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -72,4 +72,7 @@ do { \ pcr->ms_pull_ctl_disable_tbl = __device##_ms_pull_ctl_disable_tbl; \ } while (0) +/* generic operations */ +int rtsx_gops_pm_reset(struct rtsx_pcr *pcr); + #endif diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index 9cf98d142d9a..dbdd0faeb6ce 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -647,8 +647,8 @@ static int rtsx_usb_probe(struct usb_interface *intf, /* 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); + ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells, + ARRAY_SIZE(rtsx_usb_cells)); if (ret) goto out_init_fail; diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index dba7e2b6f8e9..0a7bc43db4e4 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -27,11 +27,11 @@ #include <linux/mfd/samsung/irq.h> #include <linux/mfd/samsung/s2mpa01.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mpu02.h> #include <linux/mfd/samsung/s5m8763.h> #include <linux/mfd/samsung/s5m8767.h> -#include <linux/regulator/machine.h> #include <linux/regmap.h> static const struct mfd_cell s5m8751_devs[] = { @@ -74,6 +74,15 @@ static const struct mfd_cell s2mps11_devs[] = { } }; +static const struct mfd_cell s2mps13_devs[] = { + { .name = "s2mps13-pmic", }, + { .name = "s2mps13-rtc", }, + { + .name = "s2mps13-clk", + .of_compatible = "samsung,s2mps13-clk", + }, +}; + static const struct mfd_cell s2mps14_devs[] = { { .name = "s2mps14-pmic", @@ -108,6 +117,9 @@ static const struct of_device_id sec_dt_match[] = { .compatible = "samsung,s2mps11-pmic", .data = (void *)S2MPS11X, }, { + .compatible = "samsung,s2mps13-pmic", + .data = (void *)S2MPS13X, + }, { .compatible = "samsung,s2mps14-pmic", .data = (void *)S2MPS14X, }, { @@ -194,6 +206,15 @@ static const struct regmap_config s2mps11_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static const struct regmap_config s2mps13_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPS13_REG_LDODSCH5, + .volatile_reg = s2mps11_volatile, + .cache_type = REGCACHE_FLAT, +}; + static const struct regmap_config s2mps14_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -325,6 +346,9 @@ static int sec_pmic_probe(struct i2c_client *i2c, case S2MPS11X: regmap = &s2mps11_regmap_config; break; + case S2MPS13X: + regmap = &s2mps13_regmap_config; + break; case S2MPS14X: regmap = &s2mps14_regmap_config; break; @@ -378,6 +402,10 @@ static int sec_pmic_probe(struct i2c_client *i2c, sec_devs = s2mps11_devs; num_sec_devs = ARRAY_SIZE(s2mps11_devs); break; + case S2MPS13X: + sec_devs = s2mps13_devs; + num_sec_devs = ARRAY_SIZE(s2mps13_devs); + break; case S2MPS14X: sec_devs = s2mps14_devs; num_sec_devs = ARRAY_SIZE(s2mps14_devs); @@ -432,15 +460,6 @@ static int sec_pmic_suspend(struct device *dev) */ disable_irq(sec_pmic->irq); - switch (sec_pmic->device_type) { - case S2MPS14X: - case S2MPU02: - regulator_suspend_prepare(PM_SUSPEND_MEM); - break; - default: - break; - } - return 0; } diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index f9a57869e3ec..ba86a918c2da 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -389,14 +389,22 @@ static const struct regmap_irq_chip s2mps11_irq_chip = { .ack_base = S2MPS11_REG_INT1, }; +#define S2MPS1X_IRQ_CHIP_COMMON_DATA \ + .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 s2mps13_irq_chip = { + .name = "s2mps13", + S2MPS1X_IRQ_CHIP_COMMON_DATA, +}; + 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, + S2MPS1X_IRQ_CHIP_COMMON_DATA, }; static const struct regmap_irq_chip s2mpu02_irq_chip = { @@ -452,6 +460,9 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) case S2MPS11X: sec_irq_chip = &s2mps11_irq_chip; break; + case S2MPS13X: + sec_irq_chip = &s2mps13_irq_chip; + break; case S2MPS14X: sec_irq_chip = &s2mps14_irq_chip; break; diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index ca15878ce5c0..72373b113885 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/list.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -22,31 +23,94 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> +#include <linux/slab.h> static struct platform_driver syscon_driver; +static DEFINE_SPINLOCK(syscon_list_slock); +static LIST_HEAD(syscon_list); + struct syscon { + struct device_node *np; struct regmap *regmap; + struct list_head list; +}; + +static struct regmap_config syscon_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, }; -static int syscon_match_node(struct device *dev, void *data) +static struct syscon *of_syscon_register(struct device_node *np) { - struct device_node *dn = data; + struct syscon *syscon; + struct regmap *regmap; + void __iomem *base; + int ret; + struct regmap_config syscon_config = syscon_regmap_config; + + if (!of_device_is_compatible(np, "syscon")) + return ERR_PTR(-EINVAL); + + syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); + if (!syscon) + return ERR_PTR(-ENOMEM); + + base = of_iomap(np, 0); + if (!base) { + ret = -ENOMEM; + goto err_map; + } + + /* Parse the device's DT node for an endianness specification */ + if (of_property_read_bool(np, "big-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; + + regmap = regmap_init_mmio(NULL, base, &syscon_config); + if (IS_ERR(regmap)) { + pr_err("regmap init failed\n"); + ret = PTR_ERR(regmap); + goto err_regmap; + } + + syscon->regmap = regmap; + syscon->np = np; + + spin_lock(&syscon_list_slock); + list_add_tail(&syscon->list, &syscon_list); + spin_unlock(&syscon_list_slock); - return (dev->of_node == dn) ? 1 : 0; + return syscon; + +err_regmap: + iounmap(base); +err_map: + kfree(syscon); + return ERR_PTR(ret); } struct regmap *syscon_node_to_regmap(struct device_node *np) { - struct syscon *syscon; - struct device *dev; + struct syscon *entry, *syscon = NULL; - dev = driver_find_device(&syscon_driver.driver, NULL, np, - syscon_match_node); - if (!dev) - return ERR_PTR(-EPROBE_DEFER); + spin_lock(&syscon_list_slock); - syscon = dev_get_drvdata(dev); + list_for_each_entry(entry, &syscon_list, list) + if (entry->np == np) { + syscon = entry; + break; + } + + spin_unlock(&syscon_list_slock); + + if (!syscon) + syscon = of_syscon_register(np); + + if (IS_ERR(syscon)) + return ERR_CAST(syscon); return syscon->regmap; } @@ -110,17 +174,6 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); -static const struct of_device_id of_syscon_match[] = { - { .compatible = "syscon", }, - { }, -}; - -static struct regmap_config syscon_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -167,7 +220,6 @@ static struct platform_driver syscon_driver = { .driver = { .name = "syscon", .owner = THIS_MODULE, - .of_match_table = of_syscon_match, }, .probe = syscon_probe, .id_table = syscon_ids, diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 9e04a7485981..439d905bb219 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -87,7 +87,7 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc) unsigned long flags; u8 dev_ctl; - clk_enable(t7l66xb->clk32k); + clk_prepare_enable(t7l66xb->clk32k); spin_lock_irqsave(&t7l66xb->lock, flags); @@ -118,7 +118,7 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc) spin_unlock_irqrestore(&t7l66xb->lock, flags); - clk_disable(t7l66xb->clk32k); + clk_disable_unprepare(t7l66xb->clk32k); return 0; } @@ -285,7 +285,7 @@ static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) if (pdata && pdata->suspend) pdata->suspend(dev); - clk_disable(t7l66xb->clk48m); + clk_disable_unprepare(t7l66xb->clk48m); return 0; } @@ -295,7 +295,7 @@ static int t7l66xb_resume(struct platform_device *dev) struct t7l66xb *t7l66xb = platform_get_drvdata(dev); struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev); - clk_enable(t7l66xb->clk48m); + clk_prepare_enable(t7l66xb->clk48m); if (pdata && pdata->resume) pdata->resume(dev); @@ -369,7 +369,7 @@ static int t7l66xb_probe(struct platform_device *dev) goto err_ioremap; } - clk_enable(t7l66xb->clk48m); + clk_prepare_enable(t7l66xb->clk48m); if (pdata && pdata->enable) pdata->enable(dev); @@ -414,9 +414,9 @@ static int t7l66xb_remove(struct platform_device *dev) int ret; ret = pdata->disable(dev); - clk_disable(t7l66xb->clk48m); + clk_disable_unprepare(t7l66xb->clk48m); clk_put(t7l66xb->clk48m); - clk_disable(t7l66xb->clk32k); + clk_disable_unprepare(t7l66xb->clk32k); clk_put(t7l66xb->clk32k); t7l66xb_detach_irq(dev); iounmap(t7l66xb->scr); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 0072e668c208..aacb3720065c 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -241,10 +241,8 @@ static struct irq_domain_ops tc3589x_irq_ops = { static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np) { - int base = tc3589x->irq_base; - tc3589x->domain = irq_domain_add_simple( - np, TC3589x_NR_INTERNAL_IRQS, base, + np, TC3589x_NR_INTERNAL_IRQS, 0, &tc3589x_irq_ops, tc3589x); if (!tc3589x->domain) { @@ -298,7 +296,7 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) if (blocks & TC3589x_BLOCK_GPIO) { ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, ARRAY_SIZE(tc3589x_dev_gpio), NULL, - tc3589x->irq_base, tc3589x->domain); + 0, tc3589x->domain); if (ret) { dev_err(tc3589x->dev, "failed to add gpio child\n"); return ret; @@ -309,7 +307,7 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) if (blocks & TC3589x_BLOCK_KEYPAD) { ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, ARRAY_SIZE(tc3589x_dev_keypad), NULL, - tc3589x->irq_base, tc3589x->domain); + 0, tc3589x->domain); if (ret) { dev_err(tc3589x->dev, "failed to keypad child\n"); return ret; @@ -404,7 +402,6 @@ static int tc3589x_probe(struct i2c_client *i2c, tc3589x->dev = &i2c->dev; tc3589x->i2c = i2c; tc3589x->pdata = pdata; - tc3589x->irq_base = pdata->irq_base; switch (version) { case TC3589X_TC35893: diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index e71f88000ae5..85fab3729102 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -52,7 +52,7 @@ static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) if (pdata && pdata->suspend) pdata->suspend(dev); - clk_disable(tc6387xb->clk32k); + clk_disable_unprepare(tc6387xb->clk32k); return 0; } @@ -62,7 +62,7 @@ static int tc6387xb_resume(struct platform_device *dev) struct tc6387xb *tc6387xb = platform_get_drvdata(dev); struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev); - clk_enable(tc6387xb->clk32k); + clk_prepare_enable(tc6387xb->clk32k); if (pdata && pdata->resume) pdata->resume(dev); @@ -100,7 +100,7 @@ static int tc6387xb_mmc_enable(struct platform_device *mmc) struct platform_device *dev = to_platform_device(mmc->dev.parent); struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - clk_enable(tc6387xb->clk32k); + clk_prepare_enable(tc6387xb->clk32k); tmio_core_mmc_enable(tc6387xb->scr + 0x200, 0, tc6387xb_mmc_resources[0].start & 0xfffe); @@ -113,7 +113,7 @@ static int tc6387xb_mmc_disable(struct platform_device *mmc) struct platform_device *dev = to_platform_device(mmc->dev.parent); struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - clk_disable(tc6387xb->clk32k); + clk_disable_unprepare(tc6387xb->clk32k); return 0; } @@ -214,7 +214,7 @@ static int tc6387xb_remove(struct platform_device *dev) mfd_remove_devices(&dev->dev); iounmap(tc6387xb->scr); release_resource(&tc6387xb->rscr); - clk_disable(tc6387xb->clk32k); + clk_disable_unprepare(tc6387xb->clk32k); clk_put(tc6387xb->clk32k); kfree(tc6387xb); diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 4fac16bcd732..d35f11fbeab7 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -263,6 +263,17 @@ static int tc6393xb_ohci_disable(struct platform_device *dev) return 0; } +static int tc6393xb_ohci_suspend(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev_get_platdata(dev->dev.parent); + + /* We can't properly store/restore OHCI state, so fail here */ + if (tcpd->resume_restore) + return -EBUSY; + + return tc6393xb_ohci_disable(dev); +} + static int tc6393xb_fb_enable(struct platform_device *dev) { struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent); @@ -403,7 +414,7 @@ static struct mfd_cell tc6393xb_cells[] = { .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources), .resources = tc6393xb_ohci_resources, .enable = tc6393xb_ohci_enable, - .suspend = tc6393xb_ohci_disable, + .suspend = tc6393xb_ohci_suspend, .resume = tc6393xb_ohci_enable, .disable = tc6393xb_ohci_disable, }, @@ -654,7 +665,7 @@ static int tc6393xb_probe(struct platform_device *dev) goto err_ioremap; } - ret = clk_enable(tc6393xb->clk); + ret = clk_prepare_enable(tc6393xb->clk); if (ret) goto err_clk_enable; @@ -717,7 +728,7 @@ err_gpio_add: gpiochip_remove(&tc6393xb->gpio); tcpd->disable(dev); err_enable: - clk_disable(tc6393xb->clk); + clk_disable_unprepare(tc6393xb->clk); err_clk_enable: iounmap(tc6393xb->scr); err_ioremap: @@ -748,7 +759,7 @@ static int tc6393xb_remove(struct platform_device *dev) gpiochip_remove(&tc6393xb->gpio); ret = tcpd->disable(dev); - clk_disable(tc6393xb->clk); + clk_disable_unprepare(tc6393xb->clk); iounmap(tc6393xb->scr); release_resource(&tc6393xb->rscr); clk_put(tc6393xb->clk); @@ -776,7 +787,7 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); } ret = tcpd->suspend(dev); - clk_disable(tc6393xb->clk); + clk_disable_unprepare(tc6393xb->clk); return ret; } @@ -788,7 +799,7 @@ static int tc6393xb_resume(struct platform_device *dev) int ret; int i; - clk_enable(tc6393xb->clk); + clk_prepare_enable(tc6393xb->clk); ret = tcpd->resume(dev); if (ret) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 1c3e6e2efe41..14b62e11aff4 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -76,58 +76,58 @@ static struct mfd_cell tps65090s[] = { static const struct regmap_irq tps65090_irqs[] = { /* INT1 IRQs*/ [TPS65090_IRQ_VAC_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, }, [TPS65090_IRQ_VSYS_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, }, [TPS65090_IRQ_BAT_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, }, [TPS65090_IRQ_CHARGING_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, }, [TPS65090_IRQ_CHARGING_COMPLETE] = { - .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, + .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, }, [TPS65090_IRQ_OVERLOAD_DCDC1] = { - .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, }, [TPS65090_IRQ_OVERLOAD_DCDC2] = { - .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, }, /* INT2 IRQs*/ [TPS65090_IRQ_OVERLOAD_DCDC3] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, }, [TPS65090_IRQ_OVERLOAD_FET1] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, }, [TPS65090_IRQ_OVERLOAD_FET2] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, }, [TPS65090_IRQ_OVERLOAD_FET3] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, }, [TPS65090_IRQ_OVERLOAD_FET4] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, }, [TPS65090_IRQ_OVERLOAD_FET5] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, }, [TPS65090_IRQ_OVERLOAD_FET6] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, }, [TPS65090_IRQ_OVERLOAD_FET7] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, }, }; @@ -176,7 +176,7 @@ MODULE_DEVICE_TABLE(of, tps65090_of_match); #endif static int tps65090_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct tps65090_platform_data *pdata = dev_get_platdata(&client->dev); int irq_base = 0; @@ -210,11 +210,11 @@ static int tps65090_i2c_probe(struct i2c_client *client, if (client->irq) { ret = regmap_add_irq_chip(tps65090->rmap, client->irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, - &tps65090_irq_chip, &tps65090->irq_data); - if (ret) { - dev_err(&client->dev, - "IRQ init failed with err: %d\n", ret); + IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, + &tps65090_irq_chip, &tps65090->irq_data); + if (ret) { + dev_err(&client->dev, + "IRQ init failed with err: %d\n", ret); return ret; } } else { @@ -223,8 +223,8 @@ static int tps65090_i2c_probe(struct i2c_client *client, } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, - ARRAY_SIZE(tps65090s), NULL, - 0, regmap_irq_get_domain(tps65090->irq_data)); + ARRAY_SIZE(tps65090s), NULL, + 0, regmap_irq_get_domain(tps65090->irq_data)); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index a8ee52c95f2f..80a919a8ca97 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -33,9 +33,11 @@ static const struct mfd_cell tps65217s[] = { { .name = "tps65217-pmic", + .of_compatible = "ti,tps65217-pmic", }, { .name = "tps65217-bl", + .of_compatible = "ti,tps65217-bl", }, }; diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 50f9091bcd38..7d63e324e6a8 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -831,6 +831,9 @@ static struct twl4030_power_data osc_off_idle = { static struct of_device_id twl4030_power_of_match[] = { { + .compatible = "ti,twl4030-power", + }, + { .compatible = "ti,twl4030-power-reset", .data = &omap3_reset, }, diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index 3c2b8f9e3c84..e6b3c70aeb22 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -93,9 +93,8 @@ static int vprbrd_probe(struct usb_interface *interface, version >> 8, version & 0xff, vb->usb_dev->bus->busnum, vb->usb_dev->devnum); - ret = mfd_add_devices(&interface->dev, PLATFORM_DEVID_AUTO, - vprbrd_devs, ARRAY_SIZE(vprbrd_devs), NULL, 0, - NULL); + ret = mfd_add_hotplug_devices(&interface->dev, vprbrd_devs, + ARRAY_SIZE(vprbrd_devs)); if (ret != 0) { dev_err(&interface->dev, "Failed to add mfd devices to core."); goto error; diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index d6f35bbf795b..b326a82017ee 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -336,8 +336,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ - { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */ - { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */ @@ -1112,6 +1110,8 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: @@ -1949,6 +1949,8 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: case ARIZONA_DSP1_SCRATCH_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_HP_DACVAL: case ARIZONA_MIC_DETECT_3: diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 4642b5b816a0..12cad94b4035 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -895,8 +895,16 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */ { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */ { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */ + { 0x0000054B, 0x0002 }, /* R1355 - AIF2 Frame Ctrl 5 */ + { 0x0000054C, 0x0003 }, /* R1356 - AIF2 Frame Ctrl 6 */ + { 0x0000054D, 0x0004 }, /* R1357 - AIF2 Frame Ctrl 7 */ + { 0x0000054E, 0x0005 }, /* R1358 - AIF2 Frame Ctrl 8 */ { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */ { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */ + { 0x00000553, 0x0002 }, /* R1363 - AIF2 Frame Ctrl 13 */ + { 0x00000554, 0x0003 }, /* R1364 - AIF2 Frame Ctrl 14 */ + { 0x00000555, 0x0004 }, /* R1365 - AIF2 Frame Ctrl 15 */ + { 0x00000556, 0x0005 }, /* R1366 - AIF2 Frame Ctrl 16 */ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */ @@ -1790,6 +1798,8 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: @@ -1934,8 +1944,16 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_AIF2_FRAME_CTRL_2: case ARIZONA_AIF2_FRAME_CTRL_3: case ARIZONA_AIF2_FRAME_CTRL_4: + case ARIZONA_AIF2_FRAME_CTRL_5: + case ARIZONA_AIF2_FRAME_CTRL_6: + case ARIZONA_AIF2_FRAME_CTRL_7: + case ARIZONA_AIF2_FRAME_CTRL_8: case ARIZONA_AIF2_FRAME_CTRL_11: case ARIZONA_AIF2_FRAME_CTRL_12: + case ARIZONA_AIF2_FRAME_CTRL_13: + case ARIZONA_AIF2_FRAME_CTRL_14: + case ARIZONA_AIF2_FRAME_CTRL_15: + case ARIZONA_AIF2_FRAME_CTRL_16: case ARIZONA_AIF2_TX_ENABLES: case ARIZONA_AIF2_RX_ENABLES: case ARIZONA_AIF3_BCLK_CTRL: @@ -2825,6 +2843,8 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS: case ARIZONA_MIC_DETECT_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_INPUT_ENABLES_STATUS: case ARIZONA_OUTPUT_STATUS_1: diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 4ab527f5c53b..f5124a8acad8 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -308,7 +308,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, goto err; } - mode = id2 & WM8350_CONF_STS_MASK >> 10; + mode = (id2 & WM8350_CONF_STS_MASK) >> 10; cust_id = id2 & WM8350_CUST_ID_MASK; chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12; dev_info(wm8350->dev, diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index 510da3b52324..c0c25d75aacc 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -670,6 +670,7 @@ static const struct reg_default wm8997_reg_default[] = { { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */ + { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */ { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */ { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */ { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */ @@ -886,6 +887,8 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: @@ -1328,6 +1331,7 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg) case ARIZONA_INTERRUPT_STATUS_4: case ARIZONA_INTERRUPT_STATUS_5: case ARIZONA_INTERRUPT_STATUS_1_MASK: + case ARIZONA_INTERRUPT_STATUS_2_MASK: case ARIZONA_INTERRUPT_STATUS_3_MASK: case ARIZONA_INTERRUPT_STATUS_4_MASK: case ARIZONA_INTERRUPT_STATUS_5_MASK: @@ -1477,6 +1481,8 @@ static bool wm8997_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_SAMPLE_RATE_3_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: case ARIZONA_MIC_DETECT_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_INPUT_ENABLES_STATUS: case ARIZONA_OUTPUT_STATUS_1: diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1fa4c80ff886..4409d79ed650 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -78,13 +78,16 @@ static int perdev_minors = CONFIG_MMC_BLOCK_MINORS; /* * We've only got one major, so number of mmcblk devices is - * limited to 256 / number of minors per device. + * limited to (1 << 20) / number of minors per device. It is also + * currently limited by the size of the static bitmaps below. */ static int max_devices; -/* 256 minors, so at most 256 separate devices */ -static DECLARE_BITMAP(dev_use, 256); -static DECLARE_BITMAP(name_use, 256); +#define MAX_DEVICES 256 + +/* TODO: Replace these with struct ida */ +static DECLARE_BITMAP(dev_use, MAX_DEVICES); +static DECLARE_BITMAP(name_use, MAX_DEVICES); /* * There is one mmc_blk_data per slot. @@ -112,7 +115,7 @@ struct mmc_blk_data { /* * Only set in main mmc_blk_data associated - * with mmc_card with mmc_set_drvdata, and keeps + * with mmc_card with dev_set_drvdata, and keeps * track of the current selected device partition. */ unsigned int part_curr; @@ -260,7 +263,7 @@ static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr, int ret; struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); - ret = snprintf(buf, PAGE_SIZE, "%d", + ret = snprintf(buf, PAGE_SIZE, "%d\n", get_disk_ro(dev_to_disk(dev)) ^ md->read_only); mmc_blk_put(md); @@ -642,7 +645,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, struct mmc_blk_data *md) { int ret; - struct mmc_blk_data *main_md = mmc_get_drvdata(card); + struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev); if (main_md->part_curr == md->part_type) return 0; @@ -1004,7 +1007,8 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host, err = mmc_hw_reset(host); /* Ensure we switch back to the correct partition */ if (err != -EOPNOTSUPP) { - struct mmc_blk_data *main_md = mmc_get_drvdata(host->card); + struct mmc_blk_data *main_md = + dev_get_drvdata(&host->card->dev); int part_err; main_md->part_curr = main_md->part_type; @@ -1308,19 +1312,11 @@ static int mmc_blk_packed_err_check(struct mmc_card *card, } if (status & R1_EXCEPTION_EVENT) { - ext_csd = kzalloc(512, GFP_KERNEL); - if (!ext_csd) { - pr_err("%s: unable to allocate buffer for ext_csd\n", - req->rq_disk->disk_name); - return -ENOMEM; - } - - err = mmc_send_ext_csd(card, ext_csd); + err = mmc_get_ext_csd(card, &ext_csd); if (err) { pr_err("%s: error %d sending ext_csd\n", req->rq_disk->disk_name, err); - check = MMC_BLK_ABORT; - goto free; + return MMC_BLK_ABORT; } if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] & @@ -1338,7 +1334,6 @@ static int mmc_blk_packed_err_check(struct mmc_card *card, req->rq_disk->disk_name, packed->nr_entries, packed->blocks, packed->idx_failure); } -free: kfree(ext_csd); } @@ -2093,7 +2088,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, /* * !subname implies we are creating main mmc_blk_data that will be - * associated with mmc_card with mmc_set_drvdata. Due to device + * associated with mmc_card with dev_set_drvdata. Due to device * partitions, devidx will not coincide with a per-physical card * index anymore so we keep track of a name index. */ @@ -2425,8 +2420,9 @@ static const struct mmc_fixup blk_fixups[] = END_FIXUP }; -static int mmc_blk_probe(struct mmc_card *card) +static int mmc_blk_probe(struct device *dev) { + struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_blk_data *md, *part_md; char cap_str[10]; @@ -2451,7 +2447,7 @@ static int mmc_blk_probe(struct mmc_card *card) if (mmc_blk_alloc_parts(card, md)) goto out; - mmc_set_drvdata(card, md); + dev_set_drvdata(dev, md); if (mmc_add_disk(md)) goto out; @@ -2481,9 +2477,10 @@ static int mmc_blk_probe(struct mmc_card *card) return 0; } -static void mmc_blk_remove(struct mmc_card *card) +static int mmc_blk_remove(struct device *dev) { - struct mmc_blk_data *md = mmc_get_drvdata(card); + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_blk_data *md = dev_get_drvdata(dev); mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); @@ -2494,13 +2491,15 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_disable(&card->dev); pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); - mmc_set_drvdata(card, NULL); + dev_set_drvdata(dev, NULL); + + return 0; } -static int _mmc_blk_suspend(struct mmc_card *card) +static int _mmc_blk_suspend(struct device *dev) { struct mmc_blk_data *part_md; - struct mmc_blk_data *md = mmc_get_drvdata(card); + struct mmc_blk_data *md = dev_get_drvdata(dev); if (md) { mmc_queue_suspend(&md->queue); @@ -2511,21 +2510,21 @@ static int _mmc_blk_suspend(struct mmc_card *card) return 0; } -static void mmc_blk_shutdown(struct mmc_card *card) +static void mmc_blk_shutdown(struct device *dev) { - _mmc_blk_suspend(card); + _mmc_blk_suspend(dev); } -#ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card) +#ifdef CONFIG_PM_SLEEP +static int mmc_blk_suspend(struct device *dev) { - return _mmc_blk_suspend(card); + return _mmc_blk_suspend(dev); } -static int mmc_blk_resume(struct mmc_card *card) +static int mmc_blk_resume(struct device *dev) { struct mmc_blk_data *part_md; - struct mmc_blk_data *md = mmc_get_drvdata(card); + struct mmc_blk_data *md = dev_get_drvdata(dev); if (md) { /* @@ -2540,19 +2539,15 @@ static int mmc_blk_resume(struct mmc_card *card) } return 0; } -#else -#define mmc_blk_suspend NULL -#define mmc_blk_resume NULL #endif -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmcblk", - }, +static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume); + +static struct device_driver mmc_driver = { + .name = "mmcblk", + .pm = &mmc_blk_pm_ops, .probe = mmc_blk_probe, .remove = mmc_blk_remove, - .suspend = mmc_blk_suspend, - .resume = mmc_blk_resume, .shutdown = mmc_blk_shutdown, }; @@ -2563,7 +2558,7 @@ static int __init mmc_blk_init(void) if (perdev_minors != CONFIG_MMC_BLOCK_MINORS) pr_info("mmcblk: using %d minors per device\n", perdev_minors); - max_devices = 256 / perdev_minors; + max_devices = min(MAX_DEVICES, (1 << MINORBITS) / perdev_minors); res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); if (res) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 0c0fc52d42c5..0a7430f94d29 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -14,6 +14,7 @@ #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/slab.h> +#include <linux/device.h> #include <linux/scatterlist.h> #include <linux/swap.h> /* For nr_free_buffer_pages() */ @@ -32,6 +33,8 @@ #define BUFFER_ORDER 2 #define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER) +#define TEST_ALIGN_END 8 + /* * Limit the test area size to the maximum MMC HC erase group size. Note that * the maximum SD allocation unit size is just 4MiB. @@ -1174,7 +1177,7 @@ static int mmc_test_align_write(struct mmc_test_card *test) int ret, i; struct scatterlist sg; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, 512); ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); if (ret) @@ -1189,7 +1192,7 @@ static int mmc_test_align_read(struct mmc_test_card *test) int ret, i; struct scatterlist sg; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, 512); ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); if (ret) @@ -1216,7 +1219,7 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test) if (size < 1024) return RESULT_UNSUP_HOST; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, size); ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); if (ret) @@ -1243,7 +1246,7 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test) if (size < 1024) return RESULT_UNSUP_HOST; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, size); ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); if (ret) @@ -2997,8 +3000,9 @@ err: return ret; } -static int mmc_test_probe(struct mmc_card *card) +static int mmc_test_probe(struct device *dev) { + struct mmc_card *card = mmc_dev_to_card(dev); int ret; if (!mmc_card_mmc(card) && !mmc_card_sd(card)) @@ -3013,20 +3017,22 @@ static int mmc_test_probe(struct mmc_card *card) return 0; } -static void mmc_test_remove(struct mmc_card *card) +static int mmc_test_remove(struct device *dev) { + struct mmc_card *card = mmc_dev_to_card(dev); + mmc_test_free_result(card); mmc_test_free_dbgfs_file(card); + + return 0; } -static void mmc_test_shutdown(struct mmc_card *card) +static void mmc_test_shutdown(struct device *dev) { } -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmc_test", - }, +static struct device_driver mmc_driver = { + .name = "mmc_test", .probe = mmc_test_probe, .remove = mmc_test_remove, .shutdown = mmc_test_shutdown, diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index cfa6110632c3..236d194c2883 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -232,13 +232,15 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, if (!mqrq_cur->bounce_buf) { pr_warn("%s: unable to allocate bounce cur buffer\n", mmc_card_name(card)); - } - mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); - if (!mqrq_prev->bounce_buf) { - pr_warn("%s: unable to allocate bounce prev buffer\n", - mmc_card_name(card)); - kfree(mqrq_cur->bounce_buf); - mqrq_cur->bounce_buf = NULL; + } else { + mqrq_prev->bounce_buf = + kmalloc(bouncesz, GFP_KERNEL); + if (!mqrq_prev->bounce_buf) { + pr_warn("%s: unable to allocate bounce prev buffer\n", + mmc_card_name(card)); + kfree(mqrq_cur->bounce_buf); + mqrq_cur->bounce_buf = NULL; + } } } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 8a1f1240e058..5ca562ccfcf3 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -25,8 +25,6 @@ #include "sdio_cis.h" #include "bus.h" -#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) - static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -106,33 +104,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) return retval; } -static int mmc_bus_probe(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = mmc_dev_to_card(dev); - - return drv->probe(card); -} - -static int mmc_bus_remove(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = mmc_dev_to_card(dev); - - drv->remove(card); - - return 0; -} - static void mmc_bus_shutdown(struct device *dev) { - struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; - if (dev->driver && drv->shutdown) - drv->shutdown(card); + if (dev->driver && dev->driver->shutdown) + dev->driver->shutdown(dev); if (host->bus_ops->shutdown) { ret = host->bus_ops->shutdown(host); @@ -145,16 +124,13 @@ static void mmc_bus_shutdown(struct device *dev) #ifdef CONFIG_PM_SLEEP static int mmc_bus_suspend(struct device *dev) { - struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; - if (dev->driver && drv->suspend) { - ret = drv->suspend(card); - if (ret) - return ret; - } + ret = pm_generic_suspend(dev); + if (ret) + return ret; ret = host->bus_ops->suspend(host); return ret; @@ -162,7 +138,6 @@ static int mmc_bus_suspend(struct device *dev) static int mmc_bus_resume(struct device *dev) { - struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; @@ -172,9 +147,7 @@ static int mmc_bus_resume(struct device *dev) pr_warn("%s: error %d during resume (card was removed?)\n", mmc_hostname(host), ret); - if (dev->driver && drv->resume) - ret = drv->resume(card); - + ret = pm_generic_resume(dev); return ret; } #endif @@ -207,8 +180,6 @@ static struct bus_type mmc_bus_type = { .dev_groups = mmc_dev_groups, .match = mmc_bus_match, .uevent = mmc_bus_uevent, - .probe = mmc_bus_probe, - .remove = mmc_bus_remove, .shutdown = mmc_bus_shutdown, .pm = &mmc_bus_pm_ops, }; @@ -227,24 +198,22 @@ void mmc_unregister_bus(void) * mmc_register_driver - register a media driver * @drv: MMC media driver */ -int mmc_register_driver(struct mmc_driver *drv) +int mmc_register_driver(struct device_driver *drv) { - drv->drv.bus = &mmc_bus_type; - return driver_register(&drv->drv); + drv->bus = &mmc_bus_type; + return driver_register(drv); } - EXPORT_SYMBOL(mmc_register_driver); /** * mmc_unregister_driver - unregister a media driver * @drv: MMC media driver */ -void mmc_unregister_driver(struct mmc_driver *drv) +void mmc_unregister_driver(struct device_driver *drv) { - drv->drv.bus = &mmc_bus_type; - driver_unregister(&drv->drv); + drv->bus = &mmc_bus_type; + driver_unregister(drv); } - EXPORT_SYMBOL(mmc_unregister_driver); static void mmc_release_card(struct device *dev) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f26a5f1d926d..9584bffa8b22 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -149,6 +149,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) led_trigger_event(host->led, LED_OFF); + if (mrq->sbc) { + pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n", + mmc_hostname(host), mrq->sbc->opcode, + mrq->sbc->error, + mrq->sbc->resp[0], mrq->sbc->resp[1], + mrq->sbc->resp[2], mrq->sbc->resp[3]); + } + pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", mmc_hostname(host), cmd->opcode, err, cmd->resp[0], cmd->resp[1], @@ -214,6 +222,10 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mrq->cmd->error = 0; mrq->cmd->mrq = mrq; + if (mrq->sbc) { + mrq->sbc->error = 0; + mrq->sbc->mrq = mrq; + } if (mrq->data) { BUG_ON(mrq->data->blksz > host->max_blk_size); BUG_ON(mrq->data->blocks > host->max_blk_count); @@ -538,8 +550,18 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (host->card && mmc_card_mmc(host->card) && ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) || (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) && - (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) + (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) { + + /* Cancel the prepared request */ + if (areq) + mmc_post_req(host, areq->mrq, -EINVAL); + mmc_start_bkops(host->card, true); + + /* prepare the request again */ + if (areq) + mmc_pre_req(host, areq->mrq, !host->areq); + } } if (!err && areq) @@ -709,27 +731,16 @@ int mmc_read_bkops_status(struct mmc_card *card) int err; u8 *ext_csd; - /* - * In future work, we should consider storing the entire ext_csd. - */ - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - pr_err("%s: could not allocate buffer to receive the ext_csd.\n", - mmc_hostname(card->host)); - return -ENOMEM; - } - mmc_claim_host(card->host); - err = mmc_send_ext_csd(card, ext_csd); + err = mmc_get_ext_csd(card, &ext_csd); mmc_release_host(card->host); if (err) - goto out; + return err; card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS]; -out: kfree(ext_csd); - return err; + return 0; } EXPORT_SYMBOL(mmc_read_bkops_status); @@ -1088,6 +1099,22 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) mmc_host_clk_release(host); } +/* + * Set initial state after a power cycle or a hw_reset. + */ +void mmc_set_initial_state(struct mmc_host *host) +{ + if (mmc_host_is_spi(host)) + host->ios.chip_select = MMC_CS_HIGH; + else + host->ios.chip_select = MMC_CS_DONTCARE; + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + + mmc_set_ios(host); +} + /** * mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number * @vdd: voltage (mV) @@ -1420,18 +1447,20 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) pr_warn("%s: cannot verify signal voltage switch\n", mmc_hostname(host)); + mmc_host_clk_hold(host); + cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(host, &cmd, 0); if (err) - return err; + goto err_command; - if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) - return -EIO; - - mmc_host_clk_hold(host); + if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) { + err = -EIO; + goto err_command; + } /* * The card should drive cmd and dat[0:3] low immediately * after the response of cmd11, but wait 1 ms to be sure @@ -1480,6 +1509,7 @@ power_cycle: mmc_power_cycle(host, ocr); } +err_command: mmc_host_clk_release(host); return err; @@ -1526,15 +1556,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) mmc_host_clk_hold(host); host->ios.vdd = fls(ocr) - 1; - if (mmc_host_is_spi(host)) - host->ios.chip_select = MMC_CS_HIGH; - else - host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ios.power_mode = MMC_POWER_UP; - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0) @@ -1574,14 +1598,9 @@ void mmc_power_off(struct mmc_host *host) host->ios.clock = 0; host->ios.vdd = 0; - if (!mmc_host_is_spi(host)) { - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - host->ios.chip_select = MMC_CS_DONTCARE; - } host->ios.power_mode = MMC_POWER_OFF; - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); /* * Some configurations, such as the 802.11 SDIO card in the OLPC @@ -2259,30 +2278,16 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check) /* If the reset has happened, then a status command will fail */ if (check) { - struct mmc_command cmd = {0}; - int err; + u32 status; - cmd.opcode = MMC_SEND_STATUS; - if (!mmc_host_is_spi(card->host)) - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 0); - if (!err) { + if (!mmc_send_status(card, &status)) { mmc_host_clk_release(host); return -ENOSYS; } } - if (mmc_host_is_spi(host)) { - host->ios.chip_select = MMC_CS_HIGH; - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; - } else { - host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - } - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); mmc_host_clk_release(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 443a584660f0..d76597c65e3a 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -49,6 +49,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); void mmc_power_up(struct mmc_host *host, u32 ocr); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host, u32 ocr); +void mmc_set_initial_state(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 91eb16223246..e9142108a6c6 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -291,14 +291,8 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) if (!buf) return -ENOMEM; - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - err = -ENOMEM; - goto out_free; - } - mmc_get_card(card); - err = mmc_send_ext_csd(card, ext_csd); + err = mmc_get_ext_csd(card, &ext_csd); mmc_put_card(card); if (err) goto out_free; @@ -314,7 +308,6 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) out_free: kfree(buf); - kfree(ext_csd); return err; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a301a78a2bd1..02ad79229f65 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -177,65 +177,6 @@ static int mmc_decode_csd(struct mmc_card *card) return 0; } -/* - * Read extended CSD. - */ -static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) -{ - int err; - u8 *ext_csd; - - BUG_ON(!card); - BUG_ON(!new_ext_csd); - - *new_ext_csd = NULL; - - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) - return 0; - - /* - * As the ext_csd is so large and mostly unused, we don't store the - * raw block in mmc_card. - */ - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - pr_err("%s: could not allocate a buffer to " - "receive the ext_csd.\n", mmc_hostname(card->host)); - return -ENOMEM; - } - - err = mmc_send_ext_csd(card, ext_csd); - if (err) { - kfree(ext_csd); - *new_ext_csd = NULL; - - /* If the host or the card can't do the switch, - * fail more gracefully. */ - if ((err != -EINVAL) - && (err != -ENOSYS) - && (err != -EFAULT)) - return err; - - /* - * High capacity cards should have this "magic" size - * stored in their CSD. - */ - if (card->csd.capacity == (4096 * 512)) { - pr_err("%s: unable to read EXT_CSD " - "on a possible high capacity card. " - "Card will be ignored.\n", - mmc_hostname(card->host)); - } else { - pr_warn("%s: unable to read EXT_CSD, performance might suffer\n", - mmc_hostname(card->host)); - err = 0; - } - } else - *new_ext_csd = ext_csd; - - return err; -} - static void mmc_select_card_type(struct mmc_card *card) { struct mmc_host *host = card->host; @@ -391,16 +332,11 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) /* * Decode extended CSD. */ -static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) +static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) { int err = 0, idx; unsigned int part_size; - BUG_ON(!card); - - if (!ext_csd) - return 0; - /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE]; if (card->csd.structure == 3) { @@ -628,16 +564,56 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.data_sector_size = 512; } + /* eMMC v5 or later */ + if (card->ext_csd.rev >= 7) { + memcpy(card->ext_csd.fwrev, &ext_csd[EXT_CSD_FIRMWARE_VERSION], + MMC_FIRMWARE_LEN); + card->ext_csd.ffu_capable = + (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) && + !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1); + } out: return err; } -static inline void mmc_free_ext_csd(u8 *ext_csd) +static int mmc_read_ext_csd(struct mmc_card *card) { + u8 *ext_csd; + int err; + + if (!mmc_can_ext_csd(card)) + return 0; + + err = mmc_get_ext_csd(card, &ext_csd); + if (err) { + /* If the host or the card can't do the switch, + * fail more gracefully. */ + if ((err != -EINVAL) + && (err != -ENOSYS) + && (err != -EFAULT)) + return err; + + /* + * High capacity cards should have this "magic" size + * stored in their CSD. + */ + if (card->csd.capacity == (4096 * 512)) { + pr_err("%s: unable to read EXT_CSD on a possible high capacity card. Card will be ignored.\n", + mmc_hostname(card->host)); + } else { + pr_warn("%s: unable to read EXT_CSD, performance might suffer\n", + mmc_hostname(card->host)); + err = 0; + } + + return err; + } + + err = mmc_decode_ext_csd(card, ext_csd); kfree(ext_csd); + return err; } - static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) { u8 *bw_ext_csd; @@ -647,11 +623,8 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) return 0; err = mmc_get_ext_csd(card, &bw_ext_csd); - - if (err || bw_ext_csd == NULL) { - err = -EINVAL; - goto out; - } + if (err) + return err; /* only compare read only fields */ err = !((card->ext_csd.raw_partition_support == @@ -710,8 +683,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) if (err) err = -EINVAL; -out: - mmc_free_ext_csd(bw_ext_csd); + kfree(bw_ext_csd); return err; } @@ -722,7 +694,7 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); -MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); +MMC_DEV_ATTR(ffu_capable, "%d\n", card->ext_csd.ffu_capable); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name); @@ -735,6 +707,22 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); +static ssize_t mmc_fwrev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + + if (card->ext_csd.rev < 7) { + return sprintf(buf, "0x%x\n", card->cid.fwrev); + } else { + return sprintf(buf, "0x%*phN\n", MMC_FIRMWARE_LEN, + card->ext_csd.fwrev); + } +} + +static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL); + static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, @@ -742,6 +730,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_erase_size.attr, &dev_attr_preferred_erase_size.attr, &dev_attr_fwrev.attr, + &dev_attr_ffu_capable.attr, &dev_attr_hwrev.attr, &dev_attr_manfid.attr, &dev_attr_name.attr, @@ -774,14 +763,6 @@ static int __mmc_select_powerclass(struct mmc_card *card, unsigned int pwrclass_val = 0; int err = 0; - /* Power class selection is supported for versions >= 4.0 */ - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) - return 0; - - /* Power class values are defined only for 4/8 bit bus */ - if (bus_width == EXT_CSD_BUS_WIDTH_1) - return 0; - switch (1 << host->ios.vdd) { case MMC_VDD_165_195: if (host->ios.clock <= MMC_HIGH_26_MAX_DTR) @@ -844,7 +825,7 @@ static int mmc_select_powerclass(struct mmc_card *card) int err, ddr; /* Power class selection is supported for versions >= 4.0 */ - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) + if (!mmc_can_ext_csd(card)) return 0; bus_width = host->ios.bus_width; @@ -905,7 +886,7 @@ static int mmc_select_bus_width(struct mmc_card *card) unsigned idx, bus_width = 0; int err = 0; - if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && + if (!mmc_can_ext_csd(card) && !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) return 0; @@ -998,7 +979,7 @@ static int mmc_select_hs_ddr(struct mmc_card *card) ext_csd_bits, card->ext_csd.generic_cmd6_time); if (err) { - pr_warn("%s: switch to bus width %d ddr failed\n", + pr_err("%s: switch to bus width %d ddr failed\n", mmc_hostname(host), 1 << bus_width); return err; } @@ -1069,7 +1050,7 @@ static int mmc_select_hs400(struct mmc_card *card) card->ext_csd.generic_cmd6_time, true, true, true); if (err) { - pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n", + pr_err("%s: switch to high-speed from hs200 failed, err:%d\n", mmc_hostname(host), err); return err; } @@ -1079,7 +1060,7 @@ static int mmc_select_hs400(struct mmc_card *card) EXT_CSD_DDR_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time); if (err) { - pr_warn("%s: switch to bus width for hs400 failed, err:%d\n", + pr_err("%s: switch to bus width for hs400 failed, err:%d\n", mmc_hostname(host), err); return err; } @@ -1089,7 +1070,7 @@ static int mmc_select_hs400(struct mmc_card *card) card->ext_csd.generic_cmd6_time, true, true, true); if (err) { - pr_warn("%s: switch to hs400 failed, err:%d\n", + pr_err("%s: switch to hs400 failed, err:%d\n", mmc_hostname(host), err); return err; } @@ -1146,8 +1127,7 @@ static int mmc_select_timing(struct mmc_card *card) { int err = 0; - if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && - card->ext_csd.hs_max_dtr == 0)) + if (!mmc_can_ext_csd(card)) goto bus_speed; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) @@ -1232,7 +1212,7 @@ static int mmc_hs200_tuning(struct mmc_card *card) mmc_host_clk_release(host); if (err) - pr_warn("%s: tuning execution failed\n", + pr_err("%s: tuning execution failed\n", mmc_hostname(host)); } @@ -1252,7 +1232,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, int err; u32 cid[4]; u32 rocr; - u8 *ext_csd = NULL; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1361,14 +1340,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } if (!oldcard) { - /* - * Fetch and process extended CSD. - */ - - err = mmc_get_ext_csd(card, &ext_csd); - if (err) - goto free_card; - err = mmc_read_ext_csd(card, ext_csd); + /* Read extended CSD. */ + err = mmc_read_ext_csd(card); if (err) goto free_card; @@ -1458,18 +1431,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (mmc_card_hs200(card)) { err = mmc_hs200_tuning(card); if (err) - goto err; + goto free_card; err = mmc_select_hs400(card); if (err) - goto err; + goto free_card; } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); if (!IS_ERR_VALUE(err)) { err = mmc_select_hs_ddr(card); if (err) - goto err; + goto free_card; } } @@ -1545,15 +1518,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!oldcard) host->card = card; - mmc_free_ext_csd(ext_csd); return 0; free_card: if (!oldcard) mmc_remove_card(card); err: - mmc_free_ext_csd(ext_csd); - return err; } diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 7911e0510a1d..3b044c5b029c 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -264,20 +264,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; - void *data_buf; - int is_on_stack; - - is_on_stack = object_is_on_stack(buf); - if (is_on_stack) { - /* - * dma onto stack is unsafe/nonportable, but callers to this - * routine normally provide temporary on-stack buffers ... - */ - data_buf = kmalloc(len, GFP_KERNEL); - if (!data_buf) - return -ENOMEM; - } else - data_buf = buf; mrq.cmd = &cmd; mrq.data = &data; @@ -298,7 +284,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, data.sg = &sg; data.sg_len = 1; - sg_init_one(&sg, data_buf, len); + sg_init_one(&sg, buf, len); if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) { /* @@ -312,11 +298,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, mmc_wait_for_req(host, &mrq); - if (is_on_stack) { - memcpy(buf, data_buf, len); - kfree(data_buf); - } - if (cmd.error) return cmd.error; if (data.error) @@ -334,7 +315,7 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd) return mmc_send_cxd_native(card->host, card->rca << 16, csd, MMC_SEND_CSD); - csd_tmp = kmalloc(16, GFP_KERNEL); + csd_tmp = kzalloc(16, GFP_KERNEL); if (!csd_tmp) return -ENOMEM; @@ -362,7 +343,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid) cid, MMC_SEND_CID); } - cid_tmp = kmalloc(16, GFP_KERNEL); + cid_tmp = kzalloc(16, GFP_KERNEL); if (!cid_tmp) return -ENOMEM; @@ -378,12 +359,35 @@ err: return ret; } -int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) +int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) { - return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, - ext_csd, 512); + int err; + u8 *ext_csd; + + if (!card || !new_ext_csd) + return -EINVAL; + + if (!mmc_can_ext_csd(card)) + return -EOPNOTSUPP; + + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + ext_csd = kzalloc(512, GFP_KERNEL); + if (!ext_csd) + return -ENOMEM; + + err = mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ext_csd, + 512); + if (err) + kfree(ext_csd); + else + *new_ext_csd = ext_csd; + + return err; } -EXPORT_SYMBOL_GPL(mmc_send_ext_csd); +EXPORT_SYMBOL_GPL(mmc_get_ext_csd); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) { @@ -543,6 +547,75 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, } EXPORT_SYMBOL_GPL(mmc_switch); +int mmc_send_tuning(struct mmc_host *host) +{ + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + struct mmc_ios *ios = &host->ios; + const u8 *tuning_block_pattern; + int size, err = 0; + u8 *data_buf; + u32 opcode; + + if (ios->bus_width == MMC_BUS_WIDTH_8) { + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); + opcode = MMC_SEND_TUNING_BLOCK_HS200; + } else if (ios->bus_width == MMC_BUS_WIDTH_4) { + tuning_block_pattern = tuning_blk_pattern_4bit; + size = sizeof(tuning_blk_pattern_4bit); + opcode = MMC_SEND_TUNING_BLOCK; + } else + return -EINVAL; + + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = opcode; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = size; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + /* + * According to the tuning specs, Tuning process + * is normally shorter 40 executions of CMD19, + * and timeout value should be shorter than 150 ms + */ + data.timeout_ns = 150 * NSEC_PER_MSEC; + + data.sg = &sg; + data.sg_len = 1; + sg_init_one(&sg, data_buf, size); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error) { + err = cmd.error; + goto out; + } + + if (data.error) { + err = data.error; + goto out; + } + + if (memcmp(data_buf, tuning_block_pattern, size)) + err = -EIO; + +out: + kfree(data_buf); + return err; +} +EXPORT_SYMBOL_GPL(mmc_send_tuning); + static int mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) @@ -675,3 +748,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) return 0; } + +int mmc_can_ext_csd(struct mmc_card *card) +{ + return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3); +} diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 390dac665b2a..6f4b00ed93de 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -20,13 +20,13 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_set_relative_addr(struct mmc_card *card); int mmc_send_csd(struct mmc_card *card, u32 *csd); -int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); +int mmc_can_ext_csd(struct mmc_card *card); #endif diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 2439e717655b..fd0750b5a634 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -980,8 +980,12 @@ static int mmc_sdio_resume(struct mmc_host *host) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { sdio_reset(host); mmc_go_idle(host); - err = mmc_sdio_init_card(host, host->card->ocr, host->card, - mmc_card_keep_power(host)); + mmc_send_if_cond(host, host->card->ocr); + err = mmc_send_io_op_cond(host, 0, NULL); + if (!err) + err = mmc_sdio_init_card(host, host->card->ocr, + host->card, + mmc_card_keep_power(host)); } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); @@ -1035,7 +1039,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host) sdio_reset(host); mmc_go_idle(host); - mmc_send_if_cond(host, host->ocr_avail); + mmc_send_if_cond(host, host->card->ocr); ret = mmc_send_io_op_cond(host, 0, NULL); if (ret) diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 6da97b170563..60885316afba 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -26,6 +26,8 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) + /* show configuration fields */ #define sdio_config_attr(field, format_string) \ static ssize_t \ @@ -196,8 +198,6 @@ static int sdio_bus_remove(struct device *dev) return ret; } -#ifdef CONFIG_PM - static const struct dev_pm_ops sdio_bus_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume) SET_RUNTIME_PM_OPS( @@ -207,14 +207,6 @@ static const struct dev_pm_ops sdio_bus_pm_ops = { ) }; -#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops) - -#else /* !CONFIG_PM */ - -#define SDIO_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM */ - static struct bus_type sdio_bus_type = { .name = "sdio", .dev_groups = sdio_dev_groups, @@ -222,7 +214,7 @@ static struct bus_type sdio_bus_type = { .uevent = sdio_bus_uevent, .probe = sdio_bus_probe, .remove = sdio_bus_remove, - .pm = SDIO_PM_OPS_PTR, + .pm = &sdio_bus_pm_ops, }; int sdio_register_bus(void) @@ -295,7 +287,7 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card) static void sdio_acpi_set_handle(struct sdio_func *func) { struct mmc_host *host = func->card->host; - u64 addr = (host->slotno << 16) | func->num; + u64 addr = ((u64)host->slotno << 16) | func->num; acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr); } diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 13860656104b..2d6fbdd11803 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -580,7 +580,7 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" depends on HAS_DMA - depends on ARC || ARM || MIPS || COMPILE_TEST + depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both @@ -748,3 +748,8 @@ config MMC_SUNXI help This selects support for the SD/MMC Host Controller on Allwinner sunxi SoCs. + +config MMC_TOSHIBA_PCI + tristate "Toshiba Type A SD/MMC Card Interface Driver" + depends on PCI + help diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index b09ecfb88269..f7b0a77cf419 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o +obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 77250d4b1979..62aba9af19f4 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -30,13 +30,16 @@ #include <linux/stat.h> #include <linux/types.h> #include <linux/platform_data/atmel.h> +#include <linux/platform_data/mmc-atmel-mci.h> #include <linux/mmc/host.h> #include <linux/mmc/sdio.h> -#include <mach/atmel-mci.h> #include <linux/atmel-mci.h> #include <linux/atmel_pdc.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/pinctrl/consumer.h> #include <asm/cacheflush.h> #include <asm/io.h> @@ -44,6 +47,8 @@ #include "atmel-mci-regs.h" +#define AUTOSUSPEND_DELAY 50 + #define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE) #define ATMCI_DMA_THRESHOLD 16 @@ -386,20 +391,19 @@ static int atmci_regs_show(struct seq_file *s, void *v) if (!buf) return -ENOMEM; + pm_runtime_get_sync(&host->pdev->dev); + /* * Grab a more or less consistent snapshot. Note that we're * not disabling interrupts, so IMR and SR may not be * consistent. */ - ret = clk_prepare_enable(host->mck); - if (ret) - goto out; - spin_lock_bh(&host->lock); memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE); spin_unlock_bh(&host->lock); - clk_disable_unprepare(host->mck); + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); seq_printf(s, "MR:\t0x%08x%s%s ", buf[ATMCI_MR / 4], @@ -449,7 +453,6 @@ static int atmci_regs_show(struct seq_file *s, void *v) val & ATMCI_CFG_LSYNC ? " LSYNC" : ""); } -out: kfree(buf); return ret; @@ -560,6 +563,9 @@ atmci_of_init(struct platform_device *pdev) pdata->slot[slot_id].detect_is_active_high = of_property_read_bool(cnp, "cd-inverted"); + pdata->slot[slot_id].non_removable = + of_property_read_bool(cnp, "non-removable"); + pdata->slot[slot_id].wp_pin = of_get_named_gpio(cnp, "wp-gpios", 0); } @@ -1252,6 +1258,8 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(slot->mrq); dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode); + pm_runtime_get_sync(&host->pdev->dev); + /* * We may "know" the card is gone even though there's still an * electrical connection. If so, we really need to communicate @@ -1281,7 +1289,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct atmel_mci_slot *slot = mmc_priv(mmc); struct atmel_mci *host = slot->host; unsigned int i; - bool unprepare_clk; + + pm_runtime_get_sync(&host->pdev->dev); slot->sdc_reg &= ~ATMCI_SDCBUS_MASK; switch (ios->bus_width) { @@ -1297,13 +1306,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned int clock_min = ~0U; u32 clkdiv; - clk_prepare(host->mck); - unprepare_clk = true; - spin_lock_bh(&host->lock); if (!host->mode_reg) { - clk_enable(host->mck); - unprepare_clk = false; atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN); if (host->caps.has_cfg_reg) @@ -1371,8 +1375,6 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } else { bool any_slot_active = false; - unprepare_clk = false; - spin_lock_bh(&host->lock); slot->clock = 0; for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { @@ -1385,17 +1387,12 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); if (host->mode_reg) { atmci_readl(host, ATMCI_MR); - clk_disable(host->mck); - unprepare_clk = true; } host->mode_reg = 0; } spin_unlock_bh(&host->lock); } - if (unprepare_clk) - clk_unprepare(host->mck); - switch (ios->power_mode) { case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) @@ -1421,6 +1418,9 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) */ break; } + + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); } static int atmci_get_ro(struct mmc_host *mmc) @@ -1512,6 +1512,9 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); + + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); } static void atmci_command_complete(struct atmel_mci *host, @@ -2137,7 +2140,7 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init atmci_init_slot(struct atmel_mci *host, +static int atmci_init_slot(struct atmel_mci *host, struct mci_slot_pdata *slot_data, unsigned int id, u32 sdc_reg, u32 sdio_irq) { @@ -2206,8 +2209,12 @@ static int __init atmci_init_slot(struct atmel_mci *host, } } - if (!gpio_is_valid(slot->detect_pin)) - mmc->caps |= MMC_CAP_NEEDS_POLL; + if (!gpio_is_valid(slot->detect_pin)) { + if (slot_data->non_removable) + mmc->caps |= MMC_CAP_NONREMOVABLE; + else + mmc->caps |= MMC_CAP_NEEDS_POLL; + } if (gpio_is_valid(slot->wp_pin)) { if (devm_gpio_request(&host->pdev->dev, slot->wp_pin, @@ -2265,55 +2272,25 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot, mmc_free_host(slot->mmc); } -static bool atmci_filter(struct dma_chan *chan, void *pdata) +static int atmci_configure_dma(struct atmel_mci *host) { - struct mci_platform_data *sl_pdata = pdata; - struct mci_dma_data *sl; - - if (!sl_pdata) - return false; + host->dma.chan = dma_request_slave_channel_reason(&host->pdev->dev, + "rxtx"); + if (IS_ERR(host->dma.chan)) + return PTR_ERR(host->dma.chan); + + dev_info(&host->pdev->dev, "using %s for DMA transfers\n", + dma_chan_name(host->dma.chan)); + + host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; + host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.src_maxburst = 1; + host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; + host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.dst_maxburst = 1; + host->dma_conf.device_fc = false; - sl = sl_pdata->dma_slave; - if (sl && find_slave_dev(sl) == chan->device->dev) { - chan->private = slave_data_ptr(sl); - return true; - } else { - return false; - } -} - -static bool atmci_configure_dma(struct atmel_mci *host) -{ - struct mci_platform_data *pdata; - dma_cap_mask_t mask; - - if (host == NULL) - return false; - - pdata = host->pdev->dev.platform_data; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma.chan = dma_request_slave_channel_compat(mask, atmci_filter, pdata, - &host->pdev->dev, "rxtx"); - if (!host->dma.chan) { - dev_warn(&host->pdev->dev, "no DMA channel available\n"); - return false; - } else { - dev_info(&host->pdev->dev, - "using %s for DMA transfers\n", - dma_chan_name(host->dma.chan)); - - host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; - host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_conf.src_maxburst = 1; - host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; - host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_conf.dst_maxburst = 1; - host->dma_conf.device_fc = false; - return true; - } + return 0; } /* @@ -2321,7 +2298,7 @@ static bool atmci_configure_dma(struct atmel_mci *host) * HSMCI provides DMA support and a new config register but no more supports * PDC. */ -static void __init atmci_get_cap(struct atmel_mci *host) +static void atmci_get_cap(struct atmel_mci *host) { unsigned int version; @@ -2370,7 +2347,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) } } -static int __init atmci_probe(struct platform_device *pdev) +static int atmci_probe(struct platform_device *pdev) { struct mci_platform_data *pdata; struct atmel_mci *host; @@ -2417,19 +2394,23 @@ static int __init atmci_probe(struct platform_device *pdev) atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); host->bus_hz = clk_get_rate(host->mck); - clk_disable_unprepare(host->mck); host->mapbase = regs->start; tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host); ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); - if (ret) + if (ret) { + clk_disable_unprepare(host->mck); return ret; + } /* Get MCI capabilities and set operations according to it */ atmci_get_cap(host); - if (atmci_configure_dma(host)) { + ret = atmci_configure_dma(host); + if (ret == -EPROBE_DEFER) + goto err_dma_probe_defer; + if (ret == 0) { host->prepare_data = &atmci_prepare_data_dma; host->submit_data = &atmci_submit_data_dma; host->stop_transfer = &atmci_stop_transfer_dma; @@ -2449,6 +2430,12 @@ static int __init atmci_probe(struct platform_device *pdev) setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + /* We need at least one slot to succeed */ nr_slots = 0; ret = -ENODEV; @@ -2491,6 +2478,9 @@ static int __init atmci_probe(struct platform_device *pdev) "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", host->mapbase, irq, nr_slots); + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; err_dma_alloc: @@ -2499,18 +2489,26 @@ err_dma_alloc: atmci_cleanup_slot(host->slot[i], i); } err_init_slot: + clk_disable_unprepare(host->mck); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + del_timer_sync(&host->timer); - if (host->dma.chan) + if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); +err_dma_probe_defer: free_irq(irq, host); return ret; } -static int __exit atmci_remove(struct platform_device *pdev) +static int atmci_remove(struct platform_device *pdev) { struct atmel_mci *host = platform_get_drvdata(pdev); unsigned int i; + pm_runtime_get_sync(&pdev->dev); + if (host->buffer) dma_free_coherent(&pdev->dev, host->buf_size, host->buffer, host->buf_phys_addr); @@ -2520,41 +2518,62 @@ static int __exit atmci_remove(struct platform_device *pdev) atmci_cleanup_slot(host->slot[i], i); } - clk_prepare_enable(host->mck); atmci_writel(host, ATMCI_IDR, ~0UL); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); atmci_readl(host, ATMCI_SR); - clk_disable_unprepare(host->mck); del_timer_sync(&host->timer); - if (host->dma.chan) + if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); free_irq(platform_get_irq(pdev, 0), host); + clk_disable_unprepare(host->mck); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; } -static struct platform_driver atmci_driver = { - .remove = __exit_p(atmci_remove), - .driver = { - .name = "atmel_mci", - .of_match_table = of_match_ptr(atmci_dt_ids), - }, -}; - -static int __init atmci_init(void) +#ifdef CONFIG_PM +static int atmci_runtime_suspend(struct device *dev) { - return platform_driver_probe(&atmci_driver, atmci_probe); + struct atmel_mci *host = dev_get_drvdata(dev); + + clk_disable_unprepare(host->mck); + + pinctrl_pm_select_sleep_state(dev); + + return 0; } -static void __exit atmci_exit(void) +static int atmci_runtime_resume(struct device *dev) { - platform_driver_unregister(&atmci_driver); + struct atmel_mci *host = dev_get_drvdata(dev); + + pinctrl_pm_select_default_state(dev); + + return clk_prepare_enable(host->mck); } +#endif -late_initcall(atmci_init); /* try to load after dma driver when built-in */ -module_exit(atmci_exit); +static const struct dev_pm_ops atmci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_PM_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL) +}; + +static struct platform_driver atmci_driver = { + .probe = atmci_probe, + .remove = atmci_remove, + .driver = { + .name = "atmel_mci", + .of_match_table = of_match_ptr(atmci_dt_ids), + .pm = &atmci_dev_pm_ops, + }, +}; +module_platform_driver(atmci_driver); MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 0fbc53ac7eae..509365cb22c6 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -25,6 +25,7 @@ #define NUM_PINS(x) (x + 2) #define SDMMC_CLKSEL 0x09C +#define SDMMC_CLKSEL64 0x0A8 #define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) #define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) #define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) @@ -65,6 +66,8 @@ enum dw_mci_exynos_type { DW_MCI_TYPE_EXYNOS5250, DW_MCI_TYPE_EXYNOS5420, DW_MCI_TYPE_EXYNOS5420_SMU, + DW_MCI_TYPE_EXYNOS7, + DW_MCI_TYPE_EXYNOS7_SMU, }; /* Exynos implementation specific driver private data */ @@ -95,6 +98,12 @@ static struct dw_mci_exynos_compatible { }, { .compatible = "samsung,exynos5420-dw-mshc-smu", .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, + }, { + .compatible = "samsung,exynos7-dw-mshc", + .ctrl_type = DW_MCI_TYPE_EXYNOS7, + }, { + .compatible = "samsung,exynos7-dw-mshc-smu", + .ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU, }, }; @@ -102,7 +111,8 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; - if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) { + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { mci_writel(host, MPSBEGIN0, 0); mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | @@ -153,11 +163,22 @@ static int dw_mci_exynos_resume(struct device *dev) static int dw_mci_exynos_resume_noirq(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_exynos_priv_data *priv = host->priv; u32 clksel; - clksel = mci_readl(host, CLKSEL); - if (clksel & SDMMC_CLKSEL_WAKEUP_INT) - mci_writel(host, CLKSEL, clksel); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); + + if (clksel & SDMMC_CLKSEL_WAKEUP_INT) { + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); + } return 0; } @@ -169,6 +190,7 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) { + struct dw_mci_exynos_priv_data *priv = host->priv; /* * Exynos4412 and Exynos5250 extends the use of CMD register with the * use of bit 29 (which is reserved on standard MSHC controllers) for @@ -176,8 +198,14 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) - *cmdr |= SDMMC_CMD_USE_HOLD_REG; + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { + if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64))) + *cmdr |= SDMMC_CMD_USE_HOLD_REG; + } else { + if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) + *cmdr |= SDMMC_CMD_USE_HOLD_REG; + } } static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) @@ -188,12 +216,20 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) u8 div = priv->ciu_div + 1; if (ios->timing == MMC_TIMING_MMC_DDR52) { - mci_writel(host, CLKSEL, priv->ddr_timing); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, priv->ddr_timing); + else + mci_writel(host, CLKSEL, priv->ddr_timing); /* Should be double rate for DDR mode */ if (ios->bus_width == MMC_BUS_WIDTH_8) wanted <<= 1; } else { - mci_writel(host, CLKSEL, priv->sdr_timing); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, priv->sdr_timing); + else + mci_writel(host, CLKSEL, priv->sdr_timing); } /* Don't care if wanted clock is zero */ @@ -265,26 +301,51 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) { - return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); + struct dw_mci_exynos_priv_data *priv = host->priv; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64)); + else + return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); } static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) { u32 clksel; - clksel = mci_readl(host, CLKSEL); + struct dw_mci_exynos_priv_data *priv = host->priv; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); - mci_writel(host, CLKSEL, clksel); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); } static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) { + struct dw_mci_exynos_priv_data *priv = host->priv; u32 clksel; u8 sample; - clksel = mci_readl(host, CLKSEL); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); sample = (clksel + 1) & 0x7; clksel = (clksel & ~0x7) | sample; - mci_writel(host, CLKSEL, clksel); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); return sample; } @@ -411,6 +472,10 @@ static const struct of_device_id dw_mci_exynos_match[] = { .data = &exynos_drv_data, }, { .compatible = "samsung,exynos5420-dw-mshc-smu", .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos7-dw-mshc", + .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos7-dw-mshc-smu", + .data = &exynos_drv_data, }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 8b6572162ed9..ec6dbcdec693 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -35,6 +35,10 @@ static const struct dw_mci_drv_data socfpga_drv_data = { .prepare_command = dw_mci_pltfm_prepare_command, }; +static const struct dw_mci_drv_data pistachio_drv_data = { + .prepare_command = dw_mci_pltfm_prepare_command, +}; + int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data) { @@ -90,6 +94,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data }, + { .compatible = "img,pistachio-dw-mshc", + .data = &pistachio_drv_data }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index f0c2cb1a210d..5650ac488cf3 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -37,6 +37,9 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) unsigned int cclkin; u32 bus_hz; + if (ios->clock == 0) + return; + /* * cclkin: source clock of mmc controller * bus_hz: card interface clock generated by CLKGEN @@ -65,14 +68,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rockchip_init(struct dw_mci *host) +{ + /* It is slot 8 on Rockchip SoCs */ + host->sdio_id0 = 8; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command = dw_mci_rockchip_prepare_command, + .init = dw_mci_rockchip_init, }; static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command = dw_mci_rockchip_prepare_command, .set_ios = dw_mci_rk3288_set_ios, .setup_clock = dw_mci_rk3288_setup_clock, + .init = dw_mci_rockchip_init, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc68d5b2..67c04518ec4c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -34,7 +34,6 @@ #include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> -#include <linux/workqueue.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/mmc/slot-gpio.h> @@ -62,6 +61,24 @@ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ SDMMC_IDMAC_INT_TI) +struct idmac_desc_64addr { + u32 des0; /* Control Descriptor */ + + u32 des1; /* Reserved */ + + u32 des2; /*Buffer sizes */ +#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \ + ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff)) + + u32 des3; /* Reserved */ + + u32 des4; /* Lower 32-bits of Buffer Address Pointer 1*/ + u32 des5; /* Upper 32-bits of Buffer Address Pointer 1*/ + + u32 des6; /* Lower 32-bits of Next Descriptor Address */ + u32 des7; /* Upper 32-bits of Next Descriptor Address */ +}; + struct idmac_desc { u32 des0; /* Control Descriptor */ #define IDMAC_DES0_DIC BIT(1) @@ -83,6 +100,7 @@ struct idmac_desc { #endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -414,30 +432,66 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { int i; - struct idmac_desc *desc = host->sg_cpu; + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *desc = host->sg_cpu; - for (i = 0; i < sg_len; i++, desc++) { - unsigned int length = sg_dma_len(&data->sg[i]); - u32 mem_addr = sg_dma_address(&data->sg[i]); + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u64 mem_addr = sg_dma_address(&data->sg[i]); - /* Set the OWN bit and disable interrupts for this descriptor */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + /* + * Set the OWN bit and disable interrupts for this + * descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + } - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, length); + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; - /* Physical address to DMA to/from */ - desc->des2 = mem_addr; - } + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * + sizeof(struct idmac_desc_64addr); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; - /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= IDMAC_DES0_FD; + } else { + struct idmac_desc *desc = host->sg_cpu; + + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); + + /* + * Set the OWN bit and disable interrupts for this + * descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, length); - /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc->des0 |= IDMAC_DES0_LD; + /* Physical address to DMA to/from */ + desc->des2 = mem_addr; + } + + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; + } wmb(); } @@ -448,6 +502,10 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) dw_mci_translate_sglist(host, host->data, sg_len); + /* Make sure to reset DMA in case we did PIO before this */ + dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET); + dw_mci_idmac_reset(host); + /* Select IDMAC interface */ temp = mci_readl(host, CTRL); temp |= SDMMC_CTRL_USE_IDMAC; @@ -466,29 +524,71 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) static int dw_mci_idmac_init(struct dw_mci *host) { - struct idmac_desc *p; int i; - /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr); + + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; + i++, p++) { + p->des6 = (host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) & 0xffffffff; + + p->des7 = (u64)(host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) >> 32; + /* Initialize reserved and buffer size fields to "0" */ + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des6 = host->sg_dma & 0xffffffff; + p->des7 = (u64)host->sg_dma >> 32; + p->des0 = IDMAC_DES0_ER; + + } else { + struct idmac_desc *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); - /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) - p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1)); + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) + p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * + (i + 1)); - /* Set the last descriptor as the end-of-ring descriptor */ - p->des3 = host->sg_dma; - p->des0 = IDMAC_DES0_ER; + /* Set the last descriptor as the end-of-ring descriptor */ + p->des3 = host->sg_dma; + p->des0 = IDMAC_DES0_ER; + } dw_mci_idmac_reset(host); - /* Mask out interrupts - get Tx & Rx complete only */ - mci_writel(host, IDSTS, IDMAC_INT_CLR); - mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI | - SDMMC_IDMAC_INT_TI); + if (host->dma_64bit_address == 1) { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS64, IDMAC_INT_CLR); + mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff); + mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32); + + } else { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); + mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDR, host->sg_dma); + } - /* Set the descriptor base address */ - mci_writel(host, DBADDR, host->sg_dma); return 0; } @@ -626,6 +726,13 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) WARN_ON(!(data->flags & MMC_DATA_READ)); + /* + * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is + * in the FIFO region, so we really shouldn't access it). + */ + if (host->verid < DW_MMC_240A) + return; + if (host->timing != MMC_TIMING_MMC_HS200 && host->timing != MMC_TIMING_UHS_SDR104) goto disable; @@ -819,7 +926,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id))) + if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1075,7 +1182,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); if (ret) { - dev_err(&mmc->class_dev, + dev_dbg(&mmc->class_dev, "Regulator set error %d: %d - %d\n", ret, min_uv, max_uv); return ret; @@ -1180,10 +1287,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot->id))); + (int_mask | SDMMC_INT_SDIO(slot->sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask & ~SDMMC_INT_SDIO(slot->id))); + (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id))); } } @@ -1954,6 +2061,23 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) tasklet_schedule(&host->tasklet); } +static void dw_mci_handle_cd(struct dw_mci *host) +{ + int i; + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot[i]; + + if (!slot) + continue; + + if (slot->mmc->ops->card_event) + slot->mmc->ops->card_event(slot->mmc); + mmc_detect_change(slot->mmc, + msecs_to_jiffies(host->pdata->detect_delay_ms)); + } +} + static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; @@ -2029,14 +2153,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_CD) { mci_writel(host, RINTSTS, SDMMC_INT_CD); - queue_work(host->card_workqueue, &host->card_work); + dw_mci_handle_cd(host); } /* Handle SDIO Interrupts */ for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; - if (pending & SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot->sdio_id)); mmc_signal_sdio_irq(slot->mmc); } } @@ -2045,99 +2170,28 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) #ifdef CONFIG_MMC_DW_IDMAC /* Handle DMA interrupts */ - pending = mci_readl(host, IDSTS); - if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { - mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); - mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete(host); + if (host->dma_64bit_address == 1) { + pending = mci_readl(host, IDSTS64); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI | + SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } else { + pending = mci_readl(host, IDSTS); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | + SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } } #endif return IRQ_HANDLED; } -static void dw_mci_work_routine_card(struct work_struct *work) -{ - struct dw_mci *host = container_of(work, struct dw_mci, card_work); - int i; - - for (i = 0; i < host->num_slots; i++) { - struct dw_mci_slot *slot = host->slot[i]; - struct mmc_host *mmc = slot->mmc; - struct mmc_request *mrq; - int present; - - present = dw_mci_get_cd(mmc); - while (present != slot->last_detect_state) { - dev_dbg(&slot->mmc->class_dev, "card %s\n", - present ? "inserted" : "removed"); - - spin_lock_bh(&host->lock); - - /* Card change detected */ - slot->last_detect_state = present; - - /* Clean up queue if present */ - mrq = slot->mrq; - if (mrq) { - if (mrq == host->mrq) { - host->data = NULL; - host->cmd = NULL; - - switch (host->state) { - case STATE_IDLE: - case STATE_WAITING_CMD11_DONE: - break; - case STATE_SENDING_CMD11: - case STATE_SENDING_CMD: - mrq->cmd->error = -ENOMEDIUM; - if (!mrq->data) - break; - /* fall through */ - case STATE_SENDING_DATA: - mrq->data->error = -ENOMEDIUM; - dw_mci_stop_dma(host); - break; - case STATE_DATA_BUSY: - case STATE_DATA_ERROR: - if (mrq->data->error == -EINPROGRESS) - mrq->data->error = -ENOMEDIUM; - /* fall through */ - case STATE_SENDING_STOP: - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - break; - } - - dw_mci_request_end(host, mrq); - } else { - list_del(&slot->queue_node); - mrq->cmd->error = -ENOMEDIUM; - if (mrq->data) - mrq->data->error = -ENOMEDIUM; - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - - spin_unlock(&host->lock); - mmc_request_done(slot->mmc, mrq); - spin_lock(&host->lock); - } - } - - /* Power down slot */ - if (present == 0) - dw_mci_reset(host); - - spin_unlock_bh(&host->lock); - - present = dw_mci_get_cd(mmc); - } - - mmc_detect_change(slot->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); - } -} - #ifdef CONFIG_OF /* given a slot id, find out the device node representing that slot */ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) @@ -2206,6 +2260,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot->id = id; + slot->sdio_id = host->sdio_id0 + id; slot->mmc = mmc; slot->host = host; host->slot[id] = slot; @@ -2289,9 +2344,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) dw_mci_init_debugfs(slot); #endif - /* Card initially undetected */ - slot->last_detect_state = 0; - return 0; err_host_allocated: @@ -2309,6 +2361,22 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { + int addr_config; + /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */ + addr_config = (mci_readl(host, HCON) >> 27) & 0x01; + + if (addr_config == 1) { + /* host supports IDMAC in 64-bit address mode */ + host->dma_64bit_address = 1; + dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); + if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) + dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64)); + } else { + /* host supports IDMAC in 32-bit address mode */ + host->dma_64bit_address = 0; + dev_info(host->dev, "IDMAC supports 32-bit address mode.\n"); + } + /* Alloc memory for sg translation */ host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); @@ -2672,17 +2740,10 @@ int dw_mci_probe(struct dw_mci *host) host->data_offset = DATA_240A_OFFSET; tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); - host->card_workqueue = alloc_workqueue("dw-mci-card", - WQ_MEM_RECLAIM, 1); - if (!host->card_workqueue) { - ret = -ENOMEM; - goto err_dmaunmap; - } - INIT_WORK(&host->card_work, dw_mci_work_routine_card); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); if (ret) - goto err_workqueue; + goto err_dmaunmap; if (host->pdata->num_slots) host->num_slots = host->pdata->num_slots; @@ -2718,7 +2779,7 @@ int dw_mci_probe(struct dw_mci *host) } else { dev_dbg(host->dev, "attempted to initialize %d slots, " "but failed on all\n", host->num_slots); - goto err_workqueue; + goto err_dmaunmap; } if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) @@ -2726,9 +2787,6 @@ int dw_mci_probe(struct dw_mci *host) return 0; -err_workqueue: - destroy_workqueue(host->card_workqueue); - err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); @@ -2762,8 +2820,6 @@ void dw_mci_remove(struct dw_mci *host) mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - destroy_workqueue(host->card_workqueue); - if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 01b99e8a9190..0d0f7a271d63 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -55,6 +55,17 @@ #define SDMMC_BUFADDR 0x098 #define SDMMC_CDTHRCTL 0x100 #define SDMMC_DATA(x) (x) +/* +* Registers to support idmac 64-bit address mode +*/ +#define SDMMC_DBADDRL 0x088 +#define SDMMC_DBADDRU 0x08c +#define SDMMC_IDSTS64 0x090 +#define SDMMC_IDINTEN64 0x094 +#define SDMMC_DSCADDRL 0x098 +#define SDMMC_DSCADDRU 0x09c +#define SDMMC_BUFADDRL 0x0A0 +#define SDMMC_BUFADDRU 0x0A4 /* * Data offset is difference according to Version @@ -214,7 +225,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. - * @last_detect_state: Most recently observed card detect state. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -234,7 +245,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 int id; - int last_detect_state; + int sdio_id; }; struct dw_mci_tuning_data { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 43af791e2e45..53bf7a4b5839 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -736,8 +736,15 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, chan = host->dma_tx_channel; dmaengine_terminate_all(chan); + if (host->dma_desc_current == next->dma_desc) + host->dma_desc_current = NULL; + + if (host->dma_current == next->dma_chan) + host->dma_current = NULL; + next->dma_desc = NULL; next->dma_chan = NULL; + data->host_cookie = 0; } } diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 9405ecdaf6cf..90c60fd4ff6e 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1360,7 +1360,7 @@ msmsdcc_probe(struct platform_device *pdev) if (ret) goto cmd_irq_free; - mmc_set_drvdata(pdev, mmc); + platform_set_drvdata(pdev, mmc); mmc_add_host(mmc); pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", @@ -1419,7 +1419,7 @@ ioremap_free: static int msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { - struct mmc_host *mmc = mmc_get_drvdata(dev); + struct mmc_host *mmc = platform_get_drvdata(dev); if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); @@ -1437,7 +1437,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) static int msmsdcc_resume(struct platform_device *dev) { - struct mmc_host *mmc = mmc_get_drvdata(dev); + struct mmc_host *mmc = platform_get_drvdata(dev); if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 6b4c5ad3b393..4f8618f4522d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -111,10 +111,15 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data) mvsd_write(MVSD_BLK_COUNT, data->blocks); mvsd_write(MVSD_BLK_SIZE, data->blksz); - if (nodma || (data->blksz | data->sg->offset) & 3) { + if (nodma || (data->blksz | data->sg->offset) & 3 || + ((!(data->flags & MMC_DATA_READ) && data->sg->offset & 0x3f))) { /* * We cannot do DMA on a buffer which offset or size * is not aligned on a 4-byte boundary. + * + * It also appears the host to card DMA can corrupt + * data when the buffer is not aligned on a 64 byte + * boundary. */ host->pio_size = data->blocks * data->blksz; host->pio_ptr = sg_virt(data->sg); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index ad111422ad55..5316d9b9e7b4 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -373,13 +373,9 @@ static void mxcmci_dma_callback(void *data) del_timer(&host->watchdog); stat = mxcmci_readl(host, MMC_REG_STATUS); - mxcmci_writel(host, stat & ~STATUS_DATA_TRANS_DONE, MMC_REG_STATUS); dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); - if (stat & STATUS_READ_OP_DONE) - mxcmci_writel(host, STATUS_READ_OP_DONE, MMC_REG_STATUS); - mxcmci_data_done(host, stat); } @@ -743,10 +739,8 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio; spin_unlock_irqrestore(&host->lock, flags); - if (mxcmci_use_dma(host) && - (stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE))) - mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE, - MMC_REG_STATUS); + if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE))) + mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS); if (sdio_irq) { mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS); @@ -756,8 +750,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) if (stat & STATUS_END_CMD_RESP) mxcmci_cmd_done(host, stat); - if (mxcmci_use_dma(host) && - (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) { + if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) { del_timer(&host->watchdog); mxcmci_data_done(host, stat); } @@ -1084,12 +1077,14 @@ static int mxcmci_probe(struct platform_device *pdev) dat3_card_detect = true; ret = mmc_regulator_get_supply(mmc); - if (ret) { - if (pdata && ret != -EPROBE_DEFER) - mmc->ocr_avail = pdata->ocr_avail ? : - MMC_VDD_32_33 | MMC_VDD_33_34; + if (ret == -EPROBE_DEFER) + goto out_free; + + if (!mmc->ocr_avail) { + if (pdata && pdata->ocr_avail) + mmc->ocr_avail = pdata->ocr_avail; else - goto out_free; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; } if (dat3_card_detect) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index cd74e5143c36..60c4ca97a727 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -581,10 +581,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) struct regulator *reg_vmmc; struct mxs_ssp *ssp; - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); - if (!iores || irq_err < 0) - return -EINVAL; + if (irq_err < 0) + return irq_err; mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev); if (!mmc) @@ -593,6 +592,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) host = mmc_priv(mmc); ssp = &host->ssp; ssp->dev = &pdev->dev; + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssp->base = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(ssp->base)) { ret = PTR_ERR(ssp->base); @@ -619,7 +619,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) ret = PTR_ERR(ssp->clk); goto out_mmc_free; } - clk_prepare_enable(ssp->clk); + ret = clk_prepare_enable(ssp->clk); + if (ret) + goto out_mmc_free; ret = mxs_mmc_reset(host); if (ret) { @@ -660,7 +662,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mmc); ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0, - DRIVER_NAME, host); + dev_name(&pdev->dev), host); if (ret) goto out_free_dma; @@ -702,7 +704,7 @@ static int mxs_mmc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mxs_mmc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); @@ -719,25 +721,19 @@ static int mxs_mmc_resume(struct device *dev) struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - clk_prepare_enable(ssp->clk); - return 0; + return clk_prepare_enable(ssp->clk); } - -static const struct dev_pm_ops mxs_mmc_pm_ops = { - .suspend = mxs_mmc_suspend, - .resume = mxs_mmc_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume); + static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, .remove = mxs_mmc_remove, .id_table = mxs_ssp_ids, .driver = { .name = DRIVER_NAME, -#ifdef CONFIG_PM .pm = &mxs_mmc_pm_ops, -#endif .of_match_table = mxs_mmc_dt_ids, }, }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index df27bb4fc098..7c71dcdcba8b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -42,7 +42,7 @@ #include <linux/regulator/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> -#include <linux/platform_data/mmc-omap.h> +#include <linux/platform_data/hsmmc-omap.h> /* OMAP HSMMC Host Controller Registers */ #define OMAP_HSMMC_SYSSTATUS 0x0014 @@ -155,7 +155,7 @@ * omap.c controller driver. Luckily this is not currently done on any known * omap_hsmmc.c device. */ -#define mmc_slot(host) (host->pdata->slots[host->slot_id]) +#define mmc_pdata(host) host->pdata /* * MMC Host controller read/write API's @@ -207,7 +207,6 @@ struct omap_hsmmc_host { int use_dma, dma_ch; struct dma_chan *tx_chan; struct dma_chan *rx_chan; - int slot_id; int response_busy; int context_loss; int protect_card; @@ -220,7 +219,26 @@ struct omap_hsmmc_host { #define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */ #define HSMMC_WAKE_IRQ_ENABLED (1 << 2) struct omap_hsmmc_next next_data; - struct omap_mmc_platform_data *pdata; + struct omap_hsmmc_platform_data *pdata; + + /* To handle board related suspend/resume functionality for MMC */ + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + + /* return MMC cover switch state, can be NULL if not supported. + * + * possible return values: + * 0 - closed + * 1 - open + */ + int (*get_cover_state)(struct device *dev); + + /* Card detection IRQs */ + int card_detect_irq; + + int (*card_detect)(struct device *dev); + int (*get_ro)(struct device *dev); + }; struct omap_mmc_of_data { @@ -230,50 +248,48 @@ struct omap_mmc_of_data { static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host); -static int omap_hsmmc_card_detect(struct device *dev, int slot) +static int omap_hsmmc_card_detect(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; + struct omap_hsmmc_platform_data *mmc = host->pdata; /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); + return !gpio_get_value_cansleep(mmc->switch_pin); } -static int omap_hsmmc_get_wp(struct device *dev, int slot) +static int omap_hsmmc_get_wp(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; + struct omap_hsmmc_platform_data *mmc = host->pdata; /* NOTE: assumes write protect signal is active-high */ - return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); + return gpio_get_value_cansleep(mmc->gpio_wp); } -static int omap_hsmmc_get_cover_state(struct device *dev, int slot) +static int omap_hsmmc_get_cover_state(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; + struct omap_hsmmc_platform_data *mmc = host->pdata; /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); + return !gpio_get_value_cansleep(mmc->switch_pin); } #ifdef CONFIG_PM -static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot) +static int omap_hsmmc_suspend_cdirq(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; - disable_irq(mmc->slots[0].card_detect_irq); + disable_irq(host->card_detect_irq); return 0; } -static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) +static int omap_hsmmc_resume_cdirq(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; - enable_irq(mmc->slots[0].card_detect_irq); + enable_irq(host->card_detect_irq); return 0; } @@ -286,8 +302,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) #ifdef CONFIG_REGULATOR -static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, - int vdd) +static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) { struct omap_hsmmc_host *host = platform_get_drvdata(to_platform_device(dev)); @@ -300,8 +315,8 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, if (!host->vcc) return 0; - if (mmc_slot(host).before_set_reg) - mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); + if (mmc_pdata(host)->before_set_reg) + mmc_pdata(host)->before_set_reg(dev, power_on, vdd); if (host->pbias) { if (host->pbias_enabled == 1) { @@ -363,8 +378,8 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, } } - if (mmc_slot(host).after_set_reg) - mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); + if (mmc_pdata(host)->after_set_reg) + mmc_pdata(host)->after_set_reg(dev, power_on, vdd); error_set_power: return ret; @@ -383,18 +398,18 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } else { host->vcc = reg; ocr_value = mmc_regulator_get_ocrmask(reg); - if (!mmc_slot(host).ocr_mask) { - mmc_slot(host).ocr_mask = ocr_value; + if (!mmc_pdata(host)->ocr_mask) { + mmc_pdata(host)->ocr_mask = ocr_value; } else { - if (!(mmc_slot(host).ocr_mask & ocr_value)) { + if (!(mmc_pdata(host)->ocr_mask & ocr_value)) { dev_err(host->dev, "ocrmask %x is not supported\n", - mmc_slot(host).ocr_mask); - mmc_slot(host).ocr_mask = 0; + mmc_pdata(host)->ocr_mask); + mmc_pdata(host)->ocr_mask = 0; return -EINVAL; } } } - mmc_slot(host).set_power = omap_hsmmc_set_power; + mmc_pdata(host)->set_power = omap_hsmmc_set_power; /* Allow an aux regulator */ reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); @@ -404,7 +419,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) host->pbias = IS_ERR(reg) ? NULL : reg; /* For eMMC do not power off when not in sleep state */ - if (mmc_slot(host).no_regulator_off_init) + if (mmc_pdata(host)->no_regulator_off_init) return 0; /* * To disable boot_on regulator, enable regulator @@ -412,10 +427,10 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) */ if ((host->vcc && regulator_is_enabled(host->vcc) > 0) || (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { - int vdd = ffs(mmc_slot(host).ocr_mask) - 1; + int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1; - mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); - mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + mmc_pdata(host)->set_power(host->dev, 1, vdd); + mmc_pdata(host)->set_power(host->dev, 0, 0); } return 0; @@ -423,7 +438,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) { - mmc_slot(host).set_power = NULL; + mmc_pdata(host)->set_power = NULL; } static inline int omap_hsmmc_have_reg(void) @@ -449,55 +464,59 @@ static inline int omap_hsmmc_have_reg(void) #endif -static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata) +static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host, + struct omap_hsmmc_platform_data *pdata) { int ret; - if (gpio_is_valid(pdata->slots[0].switch_pin)) { - if (pdata->slots[0].cover) - pdata->slots[0].get_cover_state = - omap_hsmmc_get_cover_state; + if (gpio_is_valid(pdata->switch_pin)) { + if (pdata->cover) + host->get_cover_state = + omap_hsmmc_get_cover_state; else - pdata->slots[0].card_detect = omap_hsmmc_card_detect; - pdata->slots[0].card_detect_irq = - gpio_to_irq(pdata->slots[0].switch_pin); - ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd"); + host->card_detect = omap_hsmmc_card_detect; + host->card_detect_irq = + gpio_to_irq(pdata->switch_pin); + ret = gpio_request(pdata->switch_pin, "mmc_cd"); if (ret) return ret; - ret = gpio_direction_input(pdata->slots[0].switch_pin); + ret = gpio_direction_input(pdata->switch_pin); if (ret) goto err_free_sp; - } else - pdata->slots[0].switch_pin = -EINVAL; + } else { + pdata->switch_pin = -EINVAL; + } - if (gpio_is_valid(pdata->slots[0].gpio_wp)) { - pdata->slots[0].get_ro = omap_hsmmc_get_wp; - ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp"); + if (gpio_is_valid(pdata->gpio_wp)) { + host->get_ro = omap_hsmmc_get_wp; + ret = gpio_request(pdata->gpio_wp, "mmc_wp"); if (ret) goto err_free_cd; - ret = gpio_direction_input(pdata->slots[0].gpio_wp); + ret = gpio_direction_input(pdata->gpio_wp); if (ret) goto err_free_wp; - } else - pdata->slots[0].gpio_wp = -EINVAL; + } else { + pdata->gpio_wp = -EINVAL; + } return 0; err_free_wp: - gpio_free(pdata->slots[0].gpio_wp); + gpio_free(pdata->gpio_wp); err_free_cd: - if (gpio_is_valid(pdata->slots[0].switch_pin)) + if (gpio_is_valid(pdata->switch_pin)) err_free_sp: - gpio_free(pdata->slots[0].switch_pin); + gpio_free(pdata->switch_pin); return ret; } -static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata) +static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host, + struct omap_hsmmc_platform_data *pdata) { - if (gpio_is_valid(pdata->slots[0].gpio_wp)) - gpio_free(pdata->slots[0].gpio_wp); - if (gpio_is_valid(pdata->slots[0].switch_pin)) - gpio_free(pdata->slots[0].switch_pin); + if (gpio_is_valid(pdata->gpio_wp)) + gpio_free(pdata->gpio_wp); + if (gpio_is_valid(pdata->switch_pin)) + gpio_free(pdata->switch_pin); } /* @@ -607,8 +626,9 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) * in capabilities register * - MMC/SD clock coming out of controller > 25MHz */ - if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && + if ((mmc_pdata(host)->features & HSMMC_HAS_HSPE_SUPPORT) && (ios->timing != MMC_TIMING_MMC_DDR52) && + (ios->timing != MMC_TIMING_UHS_DDR50) && ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { regval = OMAP_HSMMC_READ(host->base, HCTL); if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) @@ -628,7 +648,8 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host) u32 con; con = OMAP_HSMMC_READ(host->base, CON); - if (ios->timing == MMC_TIMING_MMC_DDR52) + if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_UHS_DDR50) con |= DDR; /* configure in DDR mode */ else con &= ~DDR; @@ -791,8 +812,8 @@ int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host) { int r = 1; - if (mmc_slot(host).get_cover_state) - r = mmc_slot(host).get_cover_state(host->dev, host->slot_id); + if (host->get_cover_state) + r = host->get_cover_state(host->dev); return r; } @@ -816,7 +837,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr, struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); struct omap_hsmmc_host *host = mmc_priv(mmc); - return sprintf(buf, "%s\n", mmc_slot(host).name); + return sprintf(buf, "%s\n", mmc_pdata(host)->name); } static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); @@ -1061,7 +1082,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, * OMAP4 ES2 and greater has an updated reset logic. * Monitor a 0->1 transition first */ - if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { + if (mmc_pdata(host)->features & HSMMC_HAS_UPDATED_RESET) { while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) && (i++ < limit)) udelay(1); @@ -1210,12 +1231,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) clk_disable_unprepare(host->dbclk); /* Turn the power off */ - ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + ret = mmc_pdata(host)->set_power(host->dev, 0, 0); /* Turn the power ON with given VDD 1.8 or 3.0v */ if (!ret) - ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, - vdd); + ret = mmc_pdata(host)->set_power(host->dev, 1, vdd); pm_runtime_get_sync(host->dev); if (host->dbclk) clk_prepare_enable(host->dbclk); @@ -1259,11 +1279,11 @@ err: /* Protect the card while the cover is open */ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) { - if (!mmc_slot(host).get_cover_state) + if (!host->get_cover_state) return; host->reqs_blocked = 0; - if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { + if (host->get_cover_state(host->dev)) { if (host->protect_card) { dev_info(host->dev, "%s: cover is closed, " "card is now accessible\n", @@ -1286,13 +1306,12 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) { struct omap_hsmmc_host *host = dev_id; - struct omap_mmc_slot_data *slot = &mmc_slot(host); int carddetect; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); - if (slot->card_detect) - carddetect = slot->card_detect(host->dev, host->slot_id); + if (host->card_detect) + carddetect = host->card_detect(host->dev); else { omap_hsmmc_protect_card(host); carddetect = -ENOSYS; @@ -1618,12 +1637,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode != host->power_mode) { switch (ios->power_mode) { case MMC_POWER_OFF: - mmc_slot(host).set_power(host->dev, host->slot_id, - 0, 0); + mmc_pdata(host)->set_power(host->dev, 0, 0); break; case MMC_POWER_UP: - mmc_slot(host).set_power(host->dev, host->slot_id, - 1, ios->vdd); + mmc_pdata(host)->set_power(host->dev, 1, ios->vdd); break; case MMC_POWER_ON: do_send_init_stream = 1; @@ -1668,26 +1685,26 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc) { struct omap_hsmmc_host *host = mmc_priv(mmc); - if (!mmc_slot(host).card_detect) + if (!host->card_detect) return -ENOSYS; - return mmc_slot(host).card_detect(host->dev, host->slot_id); + return host->card_detect(host->dev); } static int omap_hsmmc_get_ro(struct mmc_host *mmc) { struct omap_hsmmc_host *host = mmc_priv(mmc); - if (!mmc_slot(host).get_ro) + if (!host->get_ro) return -ENOSYS; - return mmc_slot(host).get_ro(host->dev, 0); + return host->get_ro(host->dev); } static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) { struct omap_hsmmc_host *host = mmc_priv(mmc); - if (mmc_slot(host).init_card) - mmc_slot(host).init_card(card); + if (mmc_pdata(host)->init_card) + mmc_pdata(host)->init_card(card); } static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1957,9 +1974,9 @@ static const struct of_device_id omap_mmc_of_match[] = { }; MODULE_DEVICE_TABLE(of, omap_mmc_of_match); -static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) +static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { - struct omap_mmc_platform_data *pdata; + struct omap_hsmmc_platform_data *pdata; struct device_node *np = dev->of_node; u32 bus_width, max_freq; int cd_gpio, wp_gpio; @@ -1976,40 +1993,38 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (of_find_property(np, "ti,dual-volt", NULL)) pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; - /* This driver only supports 1 slot */ - pdata->nr_slots = 1; - pdata->slots[0].switch_pin = cd_gpio; - pdata->slots[0].gpio_wp = wp_gpio; + pdata->switch_pin = cd_gpio; + pdata->gpio_wp = wp_gpio; if (of_find_property(np, "ti,non-removable", NULL)) { - pdata->slots[0].nonremovable = true; - pdata->slots[0].no_regulator_off_init = true; + pdata->nonremovable = true; + pdata->no_regulator_off_init = true; } of_property_read_u32(np, "bus-width", &bus_width); if (bus_width == 4) - pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA; + pdata->caps |= MMC_CAP_4_BIT_DATA; else if (bus_width == 8) - pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA; + pdata->caps |= MMC_CAP_8_BIT_DATA; if (of_find_property(np, "ti,needs-special-reset", NULL)) - pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET; + pdata->features |= HSMMC_HAS_UPDATED_RESET; if (!of_property_read_u32(np, "max-frequency", &max_freq)) pdata->max_freq = max_freq; if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) - pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; + pdata->features |= HSMMC_HAS_HSPE_SUPPORT; if (of_find_property(np, "keep-power-in-suspend", NULL)) - pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER; + pdata->pm_caps |= MMC_PM_KEEP_POWER; if (of_find_property(np, "enable-sdio-wakeup", NULL)) - pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; return pdata; } #else -static inline struct omap_mmc_platform_data +static inline struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { return ERR_PTR(-EINVAL); @@ -2018,7 +2033,7 @@ static inline struct omap_mmc_platform_data static int omap_hsmmc_probe(struct platform_device *pdev) { - struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; + struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_host *mmc; struct omap_hsmmc_host *host = NULL; struct resource *res; @@ -2048,11 +2063,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) return -ENXIO; } - if (pdata->nr_slots == 0) { - dev_err(&pdev->dev, "No Slots\n"); - return -ENXIO; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (res == NULL || irq < 0) @@ -2062,14 +2072,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - ret = omap_hsmmc_gpio_init(pdata); - if (ret) - goto err; - mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; - goto err_alloc; + goto err; } host = mmc_priv(mmc); @@ -2079,13 +2085,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->use_dma = 1; host->dma_ch = -1; host->irq = irq; - host->slot_id = 0; host->mapbase = res->start + pdata->reg_offset; host->base = base + pdata->reg_offset; host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; host->pbias_enabled = 0; + ret = omap_hsmmc_gpio_init(host, pdata); + if (ret) + goto err_gpio; + platform_set_drvdata(pdev, host); if (pdev->dev.of_node) @@ -2144,14 +2153,14 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - mmc->caps |= mmc_slot(host).caps; + mmc->caps |= mmc_pdata(host)->caps; if (mmc->caps & MMC_CAP_8_BIT_DATA) mmc->caps |= MMC_CAP_4_BIT_DATA; - if (mmc_slot(host).nonremovable) + if (mmc_pdata(host)->nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; - mmc->pm_caps = mmc_slot(host).pm_caps; + mmc->pm_caps = mmc_pdata(host)->pm_caps; omap_hsmmc_conf_bus_power(host); @@ -2204,27 +2213,19 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err_irq; } - if (pdata->init != NULL) { - if (pdata->init(&pdev->dev) != 0) { - dev_err(mmc_dev(host->mmc), - "Unable to configure MMC IRQs\n"); - goto err_irq; - } - } - - if (omap_hsmmc_have_reg() && !mmc_slot(host).set_power) { + if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) { ret = omap_hsmmc_reg_get(host); if (ret) - goto err_reg; + goto err_irq; host->use_reg = 1; } - mmc->ocr_avail = mmc_slot(host).ocr_mask; + mmc->ocr_avail = mmc_pdata(host)->ocr_mask; /* Request IRQ for card detect */ - if ((mmc_slot(host).card_detect_irq)) { + if (host->card_detect_irq) { ret = devm_request_threaded_irq(&pdev->dev, - mmc_slot(host).card_detect_irq, + host->card_detect_irq, NULL, omap_hsmmc_detect, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, mmc_hostname(mmc), host); @@ -2233,8 +2234,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev) "Unable to grab MMC CD IRQ\n"); goto err_irq_cd; } - pdata->suspend = omap_hsmmc_suspend_cdirq; - pdata->resume = omap_hsmmc_resume_cdirq; + host->suspend = omap_hsmmc_suspend_cdirq; + host->resume = omap_hsmmc_resume_cdirq; } omap_hsmmc_disable_irq(host); @@ -2255,12 +2256,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc_add_host(mmc); - if (mmc_slot(host).name != NULL) { + if (mmc_pdata(host)->name != NULL) { ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); if (ret < 0) goto err_slot_name; } - if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) { + if (host->card_detect_irq && host->get_cover_state) { ret = device_create_file(&mmc->class_dev, &dev_attr_cover_switch); if (ret < 0) @@ -2278,9 +2279,6 @@ err_slot_name: err_irq_cd: if (host->use_reg) omap_hsmmc_reg_put(host); -err_reg: - if (host->pdata->cleanup) - host->pdata->cleanup(&pdev->dev); err_irq: if (host->tx_chan) dma_release_channel(host->tx_chan); @@ -2291,9 +2289,9 @@ err_irq: if (host->dbclk) clk_disable_unprepare(host->dbclk); err1: + omap_hsmmc_gpio_free(host, pdata); +err_gpio: mmc_free_host(mmc); -err_alloc: - omap_hsmmc_gpio_free(pdata); err: return ret; } @@ -2306,8 +2304,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) mmc_remove_host(host->mmc); if (host->use_reg) omap_hsmmc_reg_put(host); - if (host->pdata->cleanup) - host->pdata->cleanup(&pdev->dev); if (host->tx_chan) dma_release_channel(host->tx_chan); @@ -2319,7 +2315,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) if (host->dbclk) clk_disable_unprepare(host->dbclk); - omap_hsmmc_gpio_free(host->pdata); + omap_hsmmc_gpio_free(host, host->pdata); mmc_free_host(host->mmc); return 0; @@ -2330,8 +2326,8 @@ static int omap_hsmmc_prepare(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - if (host->pdata->suspend) - return host->pdata->suspend(dev, host->slot_id); + if (host->suspend) + return host->suspend(dev); return 0; } @@ -2340,8 +2336,8 @@ static void omap_hsmmc_complete(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - if (host->pdata->resume) - host->pdata->resume(dev, host->slot_id); + if (host->resume) + host->resume(dev); } diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 9cccc0e89b04..daba49ac1242 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -76,6 +76,7 @@ struct sdhci_acpi_host { const struct sdhci_acpi_slot *slot; struct platform_device *pdev; bool use_runtime_pm; + bool dma_setup; }; static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) @@ -85,7 +86,29 @@ static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) static int sdhci_acpi_enable_dma(struct sdhci_host *host) { - return 0; + struct sdhci_acpi_host *c = sdhci_priv(host); + struct device *dev = &c->pdev->dev; + int err = -1; + + if (c->dma_setup) + return 0; + + if (host->flags & SDHCI_USE_64_BIT_DMA) { + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) { + host->flags &= ~SDHCI_USE_64_BIT_DMA; + } else { + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + dev_warn(dev, "Failed to set 64-bit DMA mask\n"); + } + } + + if (err) + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + + c->dma_setup = !err; + + return err; } static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) @@ -180,17 +203,21 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .chip = &sdhci_acpi_chip_int, .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR, + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | + MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, .probe_slot = sdhci_acpi_emmc_probe_slot, }; static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { - .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, - .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD, + .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD | + MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, .flags = SDHCI_ACPI_RUNTIME_PM, .pm_caps = MMC_PM_KEEP_POWER, .probe_slot = sdhci_acpi_sdio_probe_slot, @@ -199,8 +226,10 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL | SDHCI_ACPI_RUNTIME_PM, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_STOP_WITH_TC, + .caps = MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, .probe_slot = sdhci_acpi_sd_probe_slot, }; @@ -305,21 +334,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev) goto err_free; } - if (!dev->dma_mask) { - u64 dma_mask; - - if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) { - /* 64-bit DMA is not supported at present */ - dma_mask = DMA_BIT_MASK(32); - } else { - dma_mask = DMA_BIT_MASK(32); - } - - err = dma_coerce_mask_and_coherent(dev, dma_mask); - if (err) - goto err_free; - } - if (c->slot) { if (c->slot->probe_slot) { err = c->slot->probe_slot(pdev, hid, uid); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 587ee0edeb57..12711ab51aed 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -65,8 +65,6 @@ /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ #define ESDHC_TUNING_START_TAP 0x1 -#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64 - /* pinctrl state */ #define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" #define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz" @@ -692,8 +690,6 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) /* FIXME: delay a bit for card to be ready for next tuning due to errors */ mdelay(1); - /* This is balanced by the runtime put in sdhci_tasklet_finish */ - pm_runtime_get_sync(host->mmc->parent); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL; @@ -704,54 +700,6 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS)); } -static void esdhc_request_done(struct mmc_request *mrq) -{ - complete(&mrq->completion); -} - -static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, - struct scatterlist *sg) -{ - struct mmc_command cmd = {0}; - struct mmc_request mrq = {NULL}; - struct mmc_data data = {0}; - - cmd.opcode = opcode; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = sg; - data.sg_len = 1; - - mrq.cmd = &cmd; - mrq.cmd->mrq = &mrq; - mrq.data = &data; - mrq.data->mrq = &mrq; - mrq.cmd->data = mrq.data; - - mrq.done = esdhc_request_done; - init_completion(&(mrq.completion)); - - spin_lock_irq(&host->lock); - host->mrq = &mrq; - - sdhci_send_command(host, mrq.cmd); - - spin_unlock_irq(&host->lock); - - wait_for_completion(&mrq.completion); - - if (cmd.error) - return cmd.error; - if (data.error) - return data.error; - - return 0; -} - static void esdhc_post_tuning(struct sdhci_host *host) { u32 reg; @@ -763,21 +711,13 @@ static void esdhc_post_tuning(struct sdhci_host *host) static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) { - struct scatterlist sg; - char *tuning_pattern; int min, max, avg, ret; - tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL); - if (!tuning_pattern) - return -ENOMEM; - - sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN); - /* find the mininum delay first which can pass tuning */ min = ESDHC_TUNE_CTRL_MIN; while (min < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, min); - if (!esdhc_send_tuning_cmd(host, opcode, &sg)) + if (!mmc_send_tuning(host->mmc)) break; min += ESDHC_TUNE_CTRL_STEP; } @@ -786,7 +726,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) max = min + ESDHC_TUNE_CTRL_STEP; while (max < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, max); - if (esdhc_send_tuning_cmd(host, opcode, &sg)) { + if (mmc_send_tuning(host->mmc)) { max -= ESDHC_TUNE_CTRL_STEP; break; } @@ -796,11 +736,9 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) /* use average delay to get the best timing */ avg = (min + max) / 2; esdhc_prepare_tuning(host, avg); - ret = esdhc_send_tuning_cmd(host, opcode, &sg); + ret = mmc_send_tuning(host->mmc); esdhc_post_tuning(host); - kfree(tuning_pattern); - dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", ret ? "failed" : "passed", avg, ret); @@ -1031,11 +969,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl, PINCTRL_STATE_DEFAULT); - if (IS_ERR(imx_data->pins_default)) { - err = PTR_ERR(imx_data->pins_default); - dev_err(mmc_dev(host->mmc), "could not get default state\n"); - goto disable_clk; - } + if (IS_ERR(imx_data->pins_default)) + dev_warn(mmc_dev(host->mmc), "could not get default state\n"); host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; @@ -1123,7 +1058,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) } /* sdr50 and sdr104 needs work on 1.8v signal voltage */ - if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) { + if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) && + !IS_ERR(imx_data->pins_default)) { imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, ESDHC_PINCTRL_STATE_100MHZ); imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 30804385af6d..3d32ce896b09 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -339,9 +339,7 @@ static int msm_init_cm_dll(struct sdhci_host *host) static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) { int tuning_seq_cnt = 3; - u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; - const u8 *tuning_block_pattern = tuning_blk_pattern_4bit; - int size = sizeof(tuning_blk_pattern_4bit); + u8 phase, tuned_phases[16], tuned_phase_cnt = 0; int rc; struct mmc_host *mmc = host->mmc; struct mmc_ios ios = host->mmc->ios; @@ -355,53 +353,21 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) (ios.timing == MMC_TIMING_UHS_SDR104))) return 0; - if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && - (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { - tuning_block_pattern = tuning_blk_pattern_8bit; - size = sizeof(tuning_blk_pattern_8bit); - } - - data_buf = kmalloc(size, GFP_KERNEL); - if (!data_buf) - return -ENOMEM; - retry: /* First of all reset the tuning block */ rc = msm_init_cm_dll(host); if (rc) - goto out; + return rc; phase = 0; do { - struct mmc_command cmd = { 0 }; - struct mmc_data data = { 0 }; - struct mmc_request mrq = { - .cmd = &cmd, - .data = &data - }; - struct scatterlist sg; - /* Set the phase in delay line hw block */ rc = msm_config_cm_dll_phase(host, phase); if (rc) - goto out; + return rc; - cmd.opcode = opcode; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - data.blksz = size; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.timeout_ns = NSEC_PER_SEC; /* 1 second */ - - data.sg = &sg; - data.sg_len = 1; - sg_init_one(&sg, data_buf, size); - memset(data_buf, 0, size); - mmc_wait_for_req(mmc, &mrq); - - if (!cmd.error && !data.error && - !memcmp(data_buf, tuning_block_pattern, size)) { + rc = mmc_send_tuning(mmc); + if (!rc) { /* Tuning is successful at this tuning point */ tuned_phases[tuned_phase_cnt++] = phase; dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", @@ -413,7 +379,7 @@ retry: rc = msm_find_most_appropriate_phase(host, tuned_phases, tuned_phase_cnt); if (rc < 0) - goto out; + return rc; else phase = rc; @@ -423,7 +389,7 @@ retry: */ rc = msm_config_cm_dll_phase(host, phase); if (rc) - goto out; + return rc; dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", mmc_hostname(mmc), phase); } else { @@ -435,8 +401,6 @@ retry: rc = -EIO; } -out: - kfree(data_buf); return rc; } diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 981d66e5c023..bcb51e9dfdcd 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -165,7 +165,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0); if (IS_ERR(host)) { ret = PTR_ERR(host); - dev_err(&pdev->dev, "platform init failed (%u)\n", ret); goto clk_disable_all; } @@ -175,10 +174,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) pltfm_host->clk = clk_xin; ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "platform register failed (%u)\n", ret); + if (ret) goto err_pltfm_free; - } return 0; diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 5670e381b0cf..e2ec108dba0e 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -127,8 +127,6 @@ void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) return; scratch_32 &= ~((1 << 21) | (1 << 30)); - /* Set RTD3 function disabled */ - scratch_32 |= ((1 << 29) | (1 << 28)); pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); /* Set L1 Entrance Timer */ diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 61192973e7cb..95f73007fccf 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -269,7 +269,9 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host) static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR; + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | + MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->hw_reset = sdhci_pci_int_hw_reset; if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) @@ -279,12 +281,16 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE; + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE | + MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; return 0; } static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) { + slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; slot->cd_con_id = NULL; slot->cd_idx = 0; slot->cd_override_level = true; @@ -294,11 +300,13 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, }; static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, @@ -306,6 +314,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { }; static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, @@ -645,6 +654,25 @@ static const struct sdhci_pci_fixes sdhci_rtsx = { .probe_slot = rtsx_probe_slot, }; +static int amd_probe(struct sdhci_pci_chip *chip) +{ + struct pci_dev *smbus_dev; + + smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); + + if (smbus_dev && (smbus_dev->revision < 0x51)) { + chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; + chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; + } + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_amd = { + .probe = amd_probe, +}; + static const struct pci_device_id pci_ids[] = { { .vendor = PCI_VENDOR_ID_RICOH, @@ -1044,7 +1072,15 @@ static const struct pci_device_id pci_ids[] = { .subdevice = PCI_ANY_ID, .driver_data = (kernel_ulong_t)&sdhci_o2, }, - + { + .vendor = PCI_VENDOR_ID_AMD, + .device = PCI_ANY_ID, + .class = PCI_CLASS_SYSTEM_SDHCI << 8, + .class_mask = 0xFFFF00, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_amd, + }, { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, @@ -1064,7 +1100,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) { struct sdhci_pci_slot *slot; struct pci_dev *pdev; - int ret; + int ret = -1; slot = sdhci_priv(host); pdev = slot->chip->pdev; @@ -1076,7 +1112,17 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) "doesn't fully claim to support it.\n"); } - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (host->flags & SDHCI_USE_64_BIT_DMA) { + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) { + host->flags &= ~SDHCI_USE_64_BIT_DMA; + } else { + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (ret) + dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n"); + } + } + if (ret) + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) return ret; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index b4c23e983baf..f98008b5ea77 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -167,23 +167,17 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; - struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; int ret; struct clk *clk; - pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); - if (!pxa) - return -ENOMEM; - host = sdhci_pltfm_init(pdev, NULL, 0); - if (IS_ERR(host)) { - kfree(pxa); + if (IS_ERR(host)) return PTR_ERR(host); - } + pltfm_host = sdhci_priv(host); - pltfm_host->priv = pxa; + pltfm_host->priv = NULL; clk = clk_get(dev, "PXA-SDHCLK"); if (IS_ERR(clk)) { @@ -238,7 +232,6 @@ err_add_host: clk_put(clk); err_clk_get: sdhci_pltfm_free(pdev); - kfree(pxa); return ret; } @@ -246,14 +239,12 @@ static int sdhci_pxav2_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_pxa *pxa = pltfm_host->priv; sdhci_remove_host(host, 1); clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); sdhci_pltfm_free(pdev); - kfree(pxa); return 0; } diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 5036d7d39529..ad0badad0ebc 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -58,6 +58,12 @@ #define SDCE_MISC_INT (1<<2) #define SDCE_MISC_INT_EN (1<<1) +struct sdhci_pxa { + struct clk *clk_core; + struct clk *clk_io; + u8 power_mode; +}; + /* * These registers are relative to the second register region, for the * MBus bridge. @@ -211,6 +217,7 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) case MMC_TIMING_UHS_SDR104: ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180; break; + case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180; break; @@ -283,9 +290,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; - int ret; - struct clk *clk; pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) @@ -305,14 +310,20 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; - clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) { + pxa->clk_io = devm_clk_get(dev, "io"); + if (IS_ERR(pxa->clk_io)) + pxa->clk_io = devm_clk_get(dev, NULL); + if (IS_ERR(pxa->clk_io)) { dev_err(dev, "failed to get io clock\n"); - ret = PTR_ERR(clk); + ret = PTR_ERR(pxa->clk_io); goto err_clk_get; } - pltfm_host->clk = clk; - clk_prepare_enable(clk); + pltfm_host->clk = pxa->clk_io; + clk_prepare_enable(pxa->clk_io); + + pxa->clk_core = devm_clk_get(dev, "core"); + if (!IS_ERR(pxa->clk_core)) + clk_prepare_enable(pxa->clk_core); /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -385,7 +396,9 @@ err_add_host: pm_runtime_disable(&pdev->dev); err_of_parse: err_cd_req: - clk_disable_unprepare(clk); + clk_disable_unprepare(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_disable_unprepare(pxa->clk_core); err_clk_get: err_mbus_win: sdhci_pltfm_free(pdev); @@ -396,12 +409,15 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; pm_runtime_get_sync(&pdev->dev); sdhci_remove_host(host, 1); pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_disable_unprepare(pxa->clk_core); sdhci_pltfm_free(pdev); @@ -441,15 +457,16 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; unsigned long flags; - if (pltfm_host->clk) { - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = true; - spin_unlock_irqrestore(&host->lock, flags); + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = true; + spin_unlock_irqrestore(&host->lock, flags); - clk_disable_unprepare(pltfm_host->clk); - } + clk_disable_unprepare(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_disable_unprepare(pxa->clk_core); return 0; } @@ -458,15 +475,16 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; unsigned long flags; - if (pltfm_host->clk) { - clk_prepare_enable(pltfm_host->clk); + clk_prepare_enable(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_prepare_enable(pxa->clk_core); - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = false; - spin_unlock_irqrestore(&host->lock, flags); - } + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = false; + spin_unlock_irqrestore(&host->lock, flags); return 0; } diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 0ce6eb17deaf..4f7a63213b33 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -300,6 +300,7 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) struct device *dev = &ourhost->pdev->dev; unsigned long timeout; u16 clk = 0; + int ret; host->mmc->actual_clock = 0; @@ -311,7 +312,12 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_s3c_set_clock(host, clock); - clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + if (ret != 0) { + dev_err(dev, "%s: failed to set clock rate %uHz\n", + mmc_hostname(host->mmc), clock); + return; + } clk = SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ada1a3ea3a87..73de62a58d70 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -44,8 +44,6 @@ #define MAX_TUNING_LOOP 40 -#define ADMA_SIZE ((128 * 2 + 1) * 4) - static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -119,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host) pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", sdhci_readw(host, SDHCI_HOST_CONTROL2)); - if (host->flags & SDHCI_USE_ADMA) - pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", - readl(host->ioaddr + SDHCI_ADMA_ERROR), - readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + if (host->flags & SDHCI_USE_ADMA) { + if (host->flags & SDHCI_USE_64_BIT_DMA) + pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + else + pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + } pr_debug(DRIVER_NAME ": ===========================================\n"); } @@ -448,18 +453,26 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) local_irq_restore(*flags); } -static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd) +static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc, + dma_addr_t addr, int len, unsigned cmd) { - __le32 *dataddr = (__le32 __force *)(desc + 4); - __le16 *cmdlen = (__le16 __force *)desc; + struct sdhci_adma2_64_desc *dma_desc = desc; + + /* 32-bit and 64-bit descriptors have these members in same position */ + dma_desc->cmd = cpu_to_le16(cmd); + dma_desc->len = cpu_to_le16(len); + dma_desc->addr_lo = cpu_to_le32((u32)addr); - /* SDHCI specification says ADMA descriptors should be 4 byte - * aligned, so using 16 or 32bit operations should be safe. */ + if (host->flags & SDHCI_USE_64_BIT_DMA) + dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32); +} - cmdlen[0] = cpu_to_le16(cmd); - cmdlen[1] = cpu_to_le16(len); +static void sdhci_adma_mark_end(void *desc) +{ + struct sdhci_adma2_64_desc *dma_desc = desc; - dataddr[0] = cpu_to_le32(addr); + /* 32-bit and 64-bit descriptors have 'cmd' in same position */ + dma_desc->cmd |= cpu_to_le16(ADMA2_END); } static int sdhci_adma_table_pre(struct sdhci_host *host, @@ -467,8 +480,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, { int direction; - u8 *desc; - u8 *align; + void *desc; + void *align; dma_addr_t addr; dma_addr_t align_addr; int len, offset; @@ -489,17 +502,17 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, direction = DMA_TO_DEVICE; host->align_addr = dma_map_single(mmc_dev(host->mmc), - host->align_buffer, 128 * 4, direction); + host->align_buffer, host->align_buffer_sz, direction); if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto fail; - BUG_ON(host->align_addr & 0x3); + BUG_ON(host->align_addr & host->align_mask); host->sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction); if (host->sg_count == 0) goto unmap_align; - desc = host->adma_desc; + desc = host->adma_table; align = host->align_buffer; align_addr = host->align_addr; @@ -515,24 +528,27 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, * the (up to three) bytes that screw up the * alignment. */ - offset = (4 - (addr & 0x3)) & 0x3; + offset = (host->align_sz - (addr & host->align_mask)) & + host->align_mask; if (offset) { if (data->flags & MMC_DATA_WRITE) { buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); + WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > + (PAGE_SIZE - offset)); memcpy(align, buffer, offset); sdhci_kunmap_atomic(buffer, &flags); } /* tran, valid */ - sdhci_set_adma_desc(desc, align_addr, offset, 0x21); + sdhci_adma_write_desc(host, desc, align_addr, offset, + ADMA2_TRAN_VALID); BUG_ON(offset > 65536); - align += 4; - align_addr += 4; + align += host->align_sz; + align_addr += host->align_sz; - desc += 8; + desc += host->desc_sz; addr += offset; len -= offset; @@ -541,23 +557,23 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, BUG_ON(len > 65536); /* tran, valid */ - sdhci_set_adma_desc(desc, addr, len, 0x21); - desc += 8; + sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID); + desc += host->desc_sz; /* * If this triggers then we have a calculation bug * somewhere. :/ */ - WARN_ON((desc - host->adma_desc) > ADMA_SIZE); + WARN_ON((desc - host->adma_table) >= host->adma_table_sz); } if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { /* * Mark the last descriptor as the terminating descriptor */ - if (desc != host->adma_desc) { - desc -= 8; - desc[0] |= 0x2; /* end */ + if (desc != host->adma_table) { + desc -= host->desc_sz; + sdhci_adma_mark_end(desc); } } else { /* @@ -565,7 +581,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, */ /* nop, end, valid */ - sdhci_set_adma_desc(desc, 0, 0, 0x3); + sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID); } /* @@ -573,14 +589,14 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, */ if (data->flags & MMC_DATA_WRITE) { dma_sync_single_for_device(mmc_dev(host->mmc), - host->align_addr, 128 * 4, direction); + host->align_addr, host->align_buffer_sz, direction); } return 0; unmap_align: dma_unmap_single(mmc_dev(host->mmc), host->align_addr, - 128 * 4, direction); + host->align_buffer_sz, direction); fail: return -EINVAL; } @@ -592,7 +608,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host, struct scatterlist *sg; int i, size; - u8 *align; + void *align; char *buffer; unsigned long flags; bool has_unaligned; @@ -603,12 +619,12 @@ static void sdhci_adma_table_post(struct sdhci_host *host, direction = DMA_TO_DEVICE; dma_unmap_single(mmc_dev(host->mmc), host->align_addr, - 128 * 4, direction); + host->align_buffer_sz, direction); /* Do a quick scan of the SG list for any unaligned mappings */ has_unaligned = false; for_each_sg(data->sg, sg, host->sg_count, i) - if (sg_dma_address(sg) & 3) { + if (sg_dma_address(sg) & host->align_mask) { has_unaligned = true; break; } @@ -620,15 +636,17 @@ static void sdhci_adma_table_post(struct sdhci_host *host, align = host->align_buffer; for_each_sg(data->sg, sg, host->sg_count, i) { - if (sg_dma_address(sg) & 0x3) { - size = 4 - (sg_dma_address(sg) & 0x3); + if (sg_dma_address(sg) & host->align_mask) { + size = host->align_sz - + (sg_dma_address(sg) & host->align_mask); buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); + WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > + (PAGE_SIZE - size)); memcpy(buffer, align, size); sdhci_kunmap_atomic(buffer, &flags); - align += 4; + align += host->align_sz; } } } @@ -822,6 +840,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } else { sdhci_writel(host, host->adma_addr, SDHCI_ADMA_ADDRESS); + if (host->flags & SDHCI_USE_64_BIT_DMA) + sdhci_writel(host, + (u64)host->adma_addr >> 32, + SDHCI_ADMA_ADDRESS_HI); } } else { int sg_cnt; @@ -855,10 +877,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; if ((host->flags & SDHCI_REQ_USE_DMA) && - (host->flags & SDHCI_USE_ADMA)) - ctrl |= SDHCI_CTRL_ADMA32; - else + (host->flags & SDHCI_USE_ADMA)) { + if (host->flags & SDHCI_USE_64_BIT_DMA) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + } else { ctrl |= SDHCI_CTRL_SDMA; + } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } @@ -889,10 +915,15 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, struct mmc_data *data = cmd->data; if (data == NULL) { + if (host->quirks2 & + SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) { + sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); + } else { /* clear Auto CMD settings for no data CMDs */ - mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); - sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); + sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); + } return; } @@ -1117,6 +1148,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) case MMC_TIMING_UHS_DDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; + case MMC_TIMING_MMC_HS400: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400); + break; default: pr_warn("%s: Invalid UHS-I mode selected\n", mmc_hostname(host->mmc)); @@ -1444,6 +1478,8 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) else if ((timing == MMC_TIMING_UHS_DDR50) || (timing == MMC_TIMING_MMC_DDR52)) ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); @@ -1515,7 +1551,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) u16 clk, ctrl_2; /* In case of UHS-I modes, set High Speed Enable */ - if ((ios->timing == MMC_TIMING_MMC_HS200) || + if ((ios->timing == MMC_TIMING_MMC_HS400) || + (ios->timing == MMC_TIMING_MMC_HS200) || (ios->timing == MMC_TIMING_MMC_DDR52) || (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || @@ -1862,6 +1899,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * tuning function has to be executed. */ switch (host->timing) { + case MMC_TIMING_MMC_HS400: case MMC_TIMING_MMC_HS200: case MMC_TIMING_UHS_SDR104: break; @@ -2144,9 +2182,10 @@ static void sdhci_tasklet_finish(unsigned long param) */ if (!(host->flags & SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->error) || - (mrq->data && (mrq->data->error || - (mrq->data->stop && mrq->data->stop->error))) || - (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { + (mrq->sbc && mrq->sbc->error) || + (mrq->data && ((mrq->data->error && !mrq->data->stop) || + (mrq->data->stop && mrq->data->stop->error))) || + (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) @@ -2282,32 +2321,36 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) } #ifdef CONFIG_MMC_DEBUG -static void sdhci_show_adma_error(struct sdhci_host *host) +static void sdhci_adma_show_error(struct sdhci_host *host) { const char *name = mmc_hostname(host->mmc); - u8 *desc = host->adma_desc; - __le32 *dma; - __le16 *len; - u8 attr; + void *desc = host->adma_table; sdhci_dumpregs(host); while (true) { - dma = (__le32 *)(desc + 4); - len = (__le16 *)(desc + 2); - attr = *desc; - - DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", - name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr); + struct sdhci_adma2_64_desc *dma_desc = desc; + + if (host->flags & SDHCI_USE_64_BIT_DMA) + DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", + name, desc, le32_to_cpu(dma_desc->addr_hi), + le32_to_cpu(dma_desc->addr_lo), + le16_to_cpu(dma_desc->len), + le16_to_cpu(dma_desc->cmd)); + else + DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", + name, desc, le32_to_cpu(dma_desc->addr_lo), + le16_to_cpu(dma_desc->len), + le16_to_cpu(dma_desc->cmd)); - desc += 8; + desc += host->desc_sz; - if (attr & 2) + if (dma_desc->cmd & cpu_to_le16(ADMA2_END)) break; } } #else -static void sdhci_show_adma_error(struct sdhci_host *host) { } +static void sdhci_adma_show_error(struct sdhci_host *host) { } #endif static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) @@ -2370,7 +2413,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) host->data->error = -EILSEQ; else if (intmask & SDHCI_INT_ADMA_ERROR) { pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); - sdhci_show_adma_error(host); + sdhci_adma_show_error(host); host->data->error = -EIO; if (host->ops->adma_workaround) host->ops->adma_workaround(host, intmask); @@ -2849,6 +2892,16 @@ int sdhci_add_host(struct sdhci_host *host) host->flags &= ~SDHCI_USE_ADMA; } + /* + * It is assumed that a 64-bit capable device has set a 64-bit DMA mask + * and *must* do 64-bit DMA. A driver has the opportunity to change + * that during the first call to ->enable_dma(). Similarly + * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to + * implement. + */ + if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) + host->flags |= SDHCI_USE_64_BIT_DMA; + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) { if (host->ops->enable_dma(host)) { @@ -2860,33 +2913,56 @@ int sdhci_add_host(struct sdhci_host *host) } } + /* SDMA does not support 64-bit DMA */ + if (host->flags & SDHCI_USE_64_BIT_DMA) + host->flags &= ~SDHCI_USE_SDMA; + if (host->flags & SDHCI_USE_ADMA) { /* - * We need to allocate descriptors for all sg entries - * (128) and potentially one alignment transfer for - * each of those entries. + * The DMA descriptor table size is calculated as the maximum + * number of segments times 2, to allow for an alignment + * descriptor for each segment, plus 1 for a nop end descriptor, + * all multipled by the descriptor size. */ - host->adma_desc = dma_alloc_coherent(mmc_dev(mmc), - ADMA_SIZE, &host->adma_addr, - GFP_KERNEL); - host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); - if (!host->adma_desc || !host->align_buffer) { - dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, - host->adma_desc, host->adma_addr); + if (host->flags & SDHCI_USE_64_BIT_DMA) { + host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * + SDHCI_ADMA2_64_DESC_SZ; + host->align_buffer_sz = SDHCI_MAX_SEGS * + SDHCI_ADMA2_64_ALIGN; + host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; + host->align_sz = SDHCI_ADMA2_64_ALIGN; + host->align_mask = SDHCI_ADMA2_64_ALIGN - 1; + } else { + host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * + SDHCI_ADMA2_32_DESC_SZ; + host->align_buffer_sz = SDHCI_MAX_SEGS * + SDHCI_ADMA2_32_ALIGN; + host->desc_sz = SDHCI_ADMA2_32_DESC_SZ; + host->align_sz = SDHCI_ADMA2_32_ALIGN; + host->align_mask = SDHCI_ADMA2_32_ALIGN - 1; + } + host->adma_table = dma_alloc_coherent(mmc_dev(mmc), + host->adma_table_sz, + &host->adma_addr, + GFP_KERNEL); + host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL); + if (!host->adma_table || !host->align_buffer) { + dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, + host->adma_table, host->adma_addr); kfree(host->align_buffer); pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; - host->adma_desc = NULL; + host->adma_table = NULL; host->align_buffer = NULL; - } else if (host->adma_addr & 3) { + } else if (host->adma_addr & host->align_mask) { pr_warn("%s: unable to allocate aligned ADMA descriptor\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; - dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, - host->adma_desc, host->adma_addr); + dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, + host->adma_table, host->adma_addr); kfree(host->align_buffer); - host->adma_desc = NULL; + host->adma_table = NULL; host->align_buffer = NULL; } } @@ -3027,7 +3103,7 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); - mmc->supply.vqmmc = NULL; + mmc->supply.vqmmc = ERR_PTR(-EINVAL); } } @@ -3046,16 +3122,21 @@ int sdhci_add_host(struct sdhci_host *host) /* SD3.0: SDR104 is supported so (for eMMC) the caps2 * field can be promoted to support HS200. */ - if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) { + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) mmc->caps2 |= MMC_CAP2_HS200; - if (IS_ERR(mmc->supply.vqmmc) || - !regulator_is_supported_voltage - (mmc->supply.vqmmc, 1100000, 1300000)) - mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR; - } } else if (caps[1] & SDHCI_SUPPORT_SDR50) mmc->caps |= MMC_CAP_UHS_SDR50; + if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 && + (caps[1] & SDHCI_SUPPORT_HS400)) + mmc->caps2 |= MMC_CAP2_HS400; + + if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) && + (IS_ERR(mmc->supply.vqmmc) || + !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000, + 1300000))) + mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V; + if ((caps[1] & SDHCI_SUPPORT_DDR50) && !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50)) mmc->caps |= MMC_CAP_UHS_DDR50; @@ -3175,11 +3256,11 @@ int sdhci_add_host(struct sdhci_host *host) * can do scatter/gather or not. */ if (host->flags & SDHCI_USE_ADMA) - mmc->max_segs = 128; + mmc->max_segs = SDHCI_MAX_SEGS; else if (host->flags & SDHCI_USE_SDMA) mmc->max_segs = 1; else /* PIO */ - mmc->max_segs = 128; + mmc->max_segs = SDHCI_MAX_SEGS; /* * Maximum number of sectors in one transfer. Limited by DMA boundary @@ -3277,7 +3358,8 @@ int sdhci_add_host(struct sdhci_host *host) pr_info("%s: SDHCI controller on %s [%s] using %s\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), - (host->flags & SDHCI_USE_ADMA) ? "ADMA" : + (host->flags & SDHCI_USE_ADMA) ? + (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); sdhci_enable_card_detection(host); @@ -3339,18 +3421,15 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->finish_tasklet); - if (!IS_ERR(mmc->supply.vmmc)) - regulator_disable(mmc->supply.vmmc); - if (!IS_ERR(mmc->supply.vqmmc)) regulator_disable(mmc->supply.vqmmc); - if (host->adma_desc) - dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, - host->adma_desc, host->adma_addr); + if (host->adma_table) + dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, + host->adma_table, host->adma_addr); kfree(host->align_buffer); - host->adma_desc = NULL; + host->adma_table = NULL; host->align_buffer = NULL; } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 31896a779d4e..ddd31cda2370 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -161,7 +161,7 @@ #define SDHCI_CTRL_UHS_SDR50 0x0002 #define SDHCI_CTRL_UHS_SDR104 0x0003 #define SDHCI_CTRL_UHS_DDR50 0x0004 -#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */ +#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */ #define SDHCI_CTRL_VDD_180 0x0008 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_B 0x0000 @@ -204,6 +204,7 @@ #define SDHCI_RETUNING_MODE_SHIFT 14 #define SDHCI_CLOCK_MUL_MASK 0x00FF0000 #define SDHCI_CLOCK_MUL_SHIFT 16 +#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ #define SDHCI_CAPABILITIES_1 0x44 @@ -227,6 +228,7 @@ /* 55-57 reserved */ #define SDHCI_ADMA_ADDRESS 0x58 +#define SDHCI_ADMA_ADDRESS_HI 0x5C /* 60-FB reserved */ @@ -235,6 +237,7 @@ #define SDHCI_PRESET_FOR_SDR50 0x6A #define SDHCI_PRESET_FOR_SDR104 0x6C #define SDHCI_PRESET_FOR_DDR50 0x6E +#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ #define SDHCI_PRESET_DRV_MASK 0xC000 #define SDHCI_PRESET_DRV_SHIFT 14 #define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400 @@ -266,6 +269,46 @@ #define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) #define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) +/* ADMA2 32-bit DMA descriptor size */ +#define SDHCI_ADMA2_32_DESC_SZ 8 + +/* ADMA2 32-bit DMA alignment */ +#define SDHCI_ADMA2_32_ALIGN 4 + +/* ADMA2 32-bit descriptor */ +struct sdhci_adma2_32_desc { + __le16 cmd; + __le16 len; + __le32 addr; +} __packed __aligned(SDHCI_ADMA2_32_ALIGN); + +/* ADMA2 64-bit DMA descriptor size */ +#define SDHCI_ADMA2_64_DESC_SZ 12 + +/* ADMA2 64-bit DMA alignment */ +#define SDHCI_ADMA2_64_ALIGN 8 + +/* + * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte + * aligned. + */ +struct sdhci_adma2_64_desc { + __le16 cmd; + __le16 len; + __le32 addr_lo; + __le32 addr_hi; +} __packed __aligned(4); + +#define ADMA2_TRAN_VALID 0x21 +#define ADMA2_NOP_END_VALID 0x3 +#define ADMA2_END 0x2 + +/* + * Maximum segments assuming a 512KiB maximum requisition size and a minimum + * 4KiB page size. + */ +#define SDHCI_MAX_SEGS 128 + struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS u32 (*read_l)(struct sdhci_host *host, int reg); diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index d1663b3c4143..15cb8b7ffc34 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -21,7 +21,6 @@ #include <linux/err.h> #include <linux/clk.h> -#include <linux/clk-private.h> #include <linux/clk/sunxi.h> #include <linux/gpio.h> diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c new file mode 100644 index 000000000000..4666262edaca --- /dev/null +++ b/drivers/mmc/host/toshsd.c @@ -0,0 +1,717 @@ +/* + * Toshiba PCI Secure Digital Host Controller Interface driver + * + * Copyright (C) 2014 Ondrej Zary + * Copyright (C) 2007 Richard Betts, All Rights Reserved. + * + * Based on asic3_mmc.c, copyright (c) 2005 SDG Systems, LLC and, + * sdhci.c, copyright (C) 2005-2006 Pierre Ossman + * + * 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/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/scatterlist.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/pm.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> + +#include "toshsd.h" + +#define DRIVER_NAME "toshsd" + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA, 0x0805) }, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(pci, pci_ids); + +static void toshsd_init(struct toshsd_host *host) +{ + /* enable clock */ + pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, + SD_PCICFG_CLKSTOP_ENABLE_ALL); + pci_write_config_byte(host->pdev, SD_PCICFG_CARDDETECT, 2); + + /* reset */ + iowrite16(0, host->ioaddr + SD_SOFTWARERESET); /* assert */ + mdelay(2); + iowrite16(1, host->ioaddr + SD_SOFTWARERESET); /* deassert */ + mdelay(2); + + /* Clear card registers */ + iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); + iowrite32(0, host->ioaddr + SD_CARDSTATUS); + iowrite32(0, host->ioaddr + SD_ERRORSTATUS0); + iowrite16(0, host->ioaddr + SD_STOPINTERNAL); + + /* SDIO clock? */ + iowrite16(0x100, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL); + + /* enable LED */ + pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE1, + SD_PCICFG_LED_ENABLE1_START); + pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE2, + SD_PCICFG_LED_ENABLE2_START); + + /* set interrupt masks */ + iowrite32(~(u32)(SD_CARD_RESP_END | SD_CARD_RW_END + | SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0 + | SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE + | SD_BUF_CMD_TIMEOUT), + host->ioaddr + SD_INTMASKCARD); + + iowrite16(0x1000, host->ioaddr + SD_TRANSACTIONCTRL); +} + +/* Set MMC clock / power. + * Note: This controller uses a simple divider scheme therefore it cannot run + * SD/MMC cards at full speed (24/20MHz). HCLK (=33MHz PCI clock?) is too high + * and the next slowest is 16MHz (div=2). + */ +static void __toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct toshsd_host *host = mmc_priv(mmc); + + if (ios->clock) { + u16 clk; + int div = 1; + + while (ios->clock < HCLK / div) + div *= 2; + + clk = div >> 2; + + if (div == 1) { /* disable the divider */ + pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, + SD_PCICFG_CLKMODE_DIV_DISABLE); + clk |= SD_CARDCLK_DIV_DISABLE; + } else + pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, 0); + + clk |= SD_CARDCLK_ENABLE_CLOCK; + iowrite16(clk, host->ioaddr + SD_CARDCLOCKCTRL); + + mdelay(10); + } else + iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, + SD_PCICFG_PWR1_OFF); + mdelay(1); + break; + case MMC_POWER_UP: + break; + case MMC_POWER_ON: + pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, + SD_PCICFG_PWR1_33V); + pci_write_config_byte(host->pdev, SD_PCICFG_POWER2, + SD_PCICFG_PWR2_AUTO); + mdelay(20); + break; + } + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14) + | SD_CARDOPT_C2_MODULE_ABSENT + | SD_CARDOPT_DATA_XFR_WIDTH_1, + host->ioaddr + SD_CARDOPTIONSETUP); + break; + case MMC_BUS_WIDTH_4: + iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14) + | SD_CARDOPT_C2_MODULE_ABSENT + | SD_CARDOPT_DATA_XFR_WIDTH_4, + host->ioaddr + SD_CARDOPTIONSETUP); + break; + } +} + +static void toshsd_set_led(struct toshsd_host *host, unsigned char state) +{ + iowrite16(state, host->ioaddr + SDIO_BASE + SDIO_LEDCTRL); +} + +static void toshsd_finish_request(struct toshsd_host *host) +{ + struct mmc_request *mrq = host->mrq; + + /* Write something to end the command */ + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + + toshsd_set_led(host, 0); + mmc_request_done(host->mmc, mrq); +} + +static irqreturn_t toshsd_thread_irq(int irq, void *dev_id) +{ + struct toshsd_host *host = dev_id; + struct mmc_data *data = host->data; + struct sg_mapping_iter *sg_miter = &host->sg_miter; + unsigned short *buf; + int count; + unsigned long flags; + + if (!data) { + dev_warn(&host->pdev->dev, "Spurious Data IRQ\n"); + if (host->cmd) { + host->cmd->error = -EIO; + toshsd_finish_request(host); + } + return IRQ_NONE; + } + spin_lock_irqsave(&host->lock, flags); + + if (!sg_miter_next(sg_miter)) + return IRQ_HANDLED; + buf = sg_miter->addr; + + /* Ensure we dont read more than one block. The chip will interrupt us + * When the next block is available. + */ + count = sg_miter->length; + if (count > data->blksz) + count = data->blksz; + + dev_dbg(&host->pdev->dev, "count: %08x, flags %08x\n", count, + data->flags); + + /* Transfer the data */ + if (data->flags & MMC_DATA_READ) + ioread32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2); + else + iowrite32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2); + + sg_miter->consumed = count; + sg_miter_stop(sg_miter); + + spin_unlock_irqrestore(&host->lock, flags); + + return IRQ_HANDLED; +} + +static void toshsd_cmd_irq(struct toshsd_host *host) +{ + struct mmc_command *cmd = host->cmd; + u8 *buf; + u16 data; + + if (!host->cmd) { + dev_warn(&host->pdev->dev, "Spurious CMD irq\n"); + return; + } + buf = (u8 *)cmd->resp; + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) { + /* R2 */ + buf[12] = 0xff; + data = ioread16(host->ioaddr + SD_RESPONSE0); + buf[13] = data & 0xff; + buf[14] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE1); + buf[15] = data & 0xff; + buf[8] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE2); + buf[9] = data & 0xff; + buf[10] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE3); + buf[11] = data & 0xff; + buf[4] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE4); + buf[5] = data & 0xff; + buf[6] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE5); + buf[7] = data & 0xff; + buf[0] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE6); + buf[1] = data & 0xff; + buf[2] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE7); + buf[3] = data & 0xff; + } else if (cmd->flags & MMC_RSP_PRESENT) { + /* R1, R1B, R3, R6, R7 */ + data = ioread16(host->ioaddr + SD_RESPONSE0); + buf[0] = data & 0xff; + buf[1] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE1); + buf[2] = data & 0xff; + buf[3] = data >> 8; + } + + dev_dbg(&host->pdev->dev, "Command IRQ complete %d %d %x\n", + cmd->opcode, cmd->error, cmd->flags); + + /* If there is data to handle we will + * finish the request in the mmc_data_end_irq handler.*/ + if (host->data) + return; + + toshsd_finish_request(host); +} + +static void toshsd_data_end_irq(struct toshsd_host *host) +{ + struct mmc_data *data = host->data; + + host->data = NULL; + + if (!data) { + dev_warn(&host->pdev->dev, "Spurious data end IRQ\n"); + return; + } + + if (data->error == 0) + data->bytes_xfered = data->blocks * data->blksz; + else + data->bytes_xfered = 0; + + dev_dbg(&host->pdev->dev, "Completed data request xfr=%d\n", + data->bytes_xfered); + + iowrite16(0, host->ioaddr + SD_STOPINTERNAL); + + toshsd_finish_request(host); +} + +static irqreturn_t toshsd_irq(int irq, void *dev_id) +{ + struct toshsd_host *host = dev_id; + u32 int_reg, int_mask, int_status, detail; + int error = 0, ret = IRQ_HANDLED; + + spin_lock(&host->lock); + int_status = ioread32(host->ioaddr + SD_CARDSTATUS); + int_mask = ioread32(host->ioaddr + SD_INTMASKCARD); + int_reg = int_status & ~int_mask & ~IRQ_DONT_CARE_BITS; + + dev_dbg(&host->pdev->dev, "IRQ status:%x mask:%x\n", + int_status, int_mask); + + /* nothing to do: it's not our IRQ */ + if (!int_reg) { + ret = IRQ_NONE; + goto irq_end; + } + + if (int_reg & SD_BUF_CMD_TIMEOUT) { + error = -ETIMEDOUT; + dev_dbg(&host->pdev->dev, "Timeout\n"); + } else if (int_reg & SD_BUF_CRC_ERR) { + error = -EILSEQ; + dev_err(&host->pdev->dev, "BadCRC\n"); + } else if (int_reg & (SD_BUF_ILLEGAL_ACCESS + | SD_BUF_CMD_INDEX_ERR + | SD_BUF_STOP_BIT_END_ERR + | SD_BUF_OVERFLOW + | SD_BUF_UNDERFLOW + | SD_BUF_DATA_TIMEOUT)) { + dev_err(&host->pdev->dev, "Buffer status error: { %s%s%s%s%s%s}\n", + int_reg & SD_BUF_ILLEGAL_ACCESS ? "ILLEGAL_ACC " : "", + int_reg & SD_BUF_CMD_INDEX_ERR ? "CMD_INDEX " : "", + int_reg & SD_BUF_STOP_BIT_END_ERR ? "STOPBIT_END " : "", + int_reg & SD_BUF_OVERFLOW ? "OVERFLOW " : "", + int_reg & SD_BUF_UNDERFLOW ? "UNDERFLOW " : "", + int_reg & SD_BUF_DATA_TIMEOUT ? "DATA_TIMEOUT " : ""); + + detail = ioread32(host->ioaddr + SD_ERRORSTATUS0); + dev_err(&host->pdev->dev, "detail error status { %s%s%s%s%s%s%s%s%s%s%s%s%s}\n", + detail & SD_ERR0_RESP_CMD_ERR ? "RESP_CMD " : "", + detail & SD_ERR0_RESP_NON_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "", + detail & SD_ERR0_RESP_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "", + detail & SD_ERR0_READ_DATA_END_BIT_ERR ? "READ_DATA_END_BIT " : "", + detail & SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR ? "WRITE_CMD_END_BIT " : "", + detail & SD_ERR0_RESP_NON_CMD12_CRC_ERR ? "RESP_CRC " : "", + detail & SD_ERR0_RESP_CMD12_CRC_ERR ? "RESP_CRC " : "", + detail & SD_ERR0_READ_DATA_CRC_ERR ? "READ_DATA_CRC " : "", + detail & SD_ERR0_WRITE_CMD_CRC_ERR ? "WRITE_CMD_CRC " : "", + detail & SD_ERR1_NO_CMD_RESP ? "NO_CMD_RESP " : "", + detail & SD_ERR1_TIMEOUT_READ_DATA ? "READ_DATA_TIMEOUT " : "", + detail & SD_ERR1_TIMEOUT_CRS_STATUS ? "CRS_STATUS_TIMEOUT " : "", + detail & SD_ERR1_TIMEOUT_CRC_BUSY ? "CRC_BUSY_TIMEOUT " : ""); + error = -EIO; + } + + if (error) { + if (host->cmd) + host->cmd->error = error; + + if (error == -ETIMEDOUT) { + iowrite32(int_status & + ~(SD_BUF_CMD_TIMEOUT | SD_CARD_RESP_END), + host->ioaddr + SD_CARDSTATUS); + } else { + toshsd_init(host); + __toshsd_set_ios(host->mmc, &host->mmc->ios); + goto irq_end; + } + } + + /* Card insert/remove. The mmc controlling code is stateless. */ + if (int_reg & (SD_CARD_CARD_INSERTED_0 | SD_CARD_CARD_REMOVED_0)) { + iowrite32(int_status & + ~(SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0), + host->ioaddr + SD_CARDSTATUS); + + if (int_reg & SD_CARD_CARD_INSERTED_0) + toshsd_init(host); + + mmc_detect_change(host->mmc, 1); + } + + /* Data transfer */ + if (int_reg & (SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE)) { + iowrite32(int_status & + ~(SD_BUF_WRITE_ENABLE | SD_BUF_READ_ENABLE), + host->ioaddr + SD_CARDSTATUS); + + ret = IRQ_WAKE_THREAD; + goto irq_end; + } + + /* Command completion */ + if (int_reg & SD_CARD_RESP_END) { + iowrite32(int_status & ~(SD_CARD_RESP_END), + host->ioaddr + SD_CARDSTATUS); + toshsd_cmd_irq(host); + } + + /* Data transfer completion */ + if (int_reg & SD_CARD_RW_END) { + iowrite32(int_status & ~(SD_CARD_RW_END), + host->ioaddr + SD_CARDSTATUS); + toshsd_data_end_irq(host); + } +irq_end: + spin_unlock(&host->lock); + return ret; +} + +static void toshsd_start_cmd(struct toshsd_host *host, struct mmc_command *cmd) +{ + struct mmc_data *data = host->data; + int c = cmd->opcode; + + dev_dbg(&host->pdev->dev, "Command opcode: %d\n", cmd->opcode); + + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + iowrite16(SD_STOPINT_ISSUE_CMD12, + host->ioaddr + SD_STOPINTERNAL); + + cmd->resp[0] = cmd->opcode; + cmd->resp[1] = 0; + cmd->resp[2] = 0; + cmd->resp[3] = 0; + + toshsd_finish_request(host); + return; + } + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + c |= SD_CMD_RESP_TYPE_NONE; + break; + + case MMC_RSP_R1: + c |= SD_CMD_RESP_TYPE_EXT_R1; + break; + case MMC_RSP_R1B: + c |= SD_CMD_RESP_TYPE_EXT_R1B; + break; + case MMC_RSP_R2: + c |= SD_CMD_RESP_TYPE_EXT_R2; + break; + case MMC_RSP_R3: + c |= SD_CMD_RESP_TYPE_EXT_R3; + break; + + default: + dev_err(&host->pdev->dev, "Unknown response type %d\n", + mmc_resp_type(cmd)); + break; + } + + host->cmd = cmd; + + if (cmd->opcode == MMC_APP_CMD) + c |= SD_CMD_TYPE_ACMD; + + if (cmd->opcode == MMC_GO_IDLE_STATE) + c |= (3 << 8); /* removed from ipaq-asic3.h for some reason */ + + if (data) { + c |= SD_CMD_DATA_PRESENT; + + if (data->blocks > 1) { + iowrite16(SD_STOPINT_AUTO_ISSUE_CMD12, + host->ioaddr + SD_STOPINTERNAL); + c |= SD_CMD_MULTI_BLOCK; + } + + if (data->flags & MMC_DATA_READ) + c |= SD_CMD_TRANSFER_READ; + + /* MMC_DATA_WRITE does not require a bit to be set */ + } + + /* Send the command */ + iowrite32(cmd->arg, host->ioaddr + SD_ARG0); + iowrite16(c, host->ioaddr + SD_CMD); +} + +static void toshsd_start_data(struct toshsd_host *host, struct mmc_data *data) +{ + unsigned int flags = SG_MITER_ATOMIC; + + dev_dbg(&host->pdev->dev, "setup data transfer: blocksize %08x nr_blocks %d, offset: %08x\n", + data->blksz, data->blocks, data->sg->offset); + + host->data = data; + + if (data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); + + /* Set transfer length and blocksize */ + iowrite16(data->blocks, host->ioaddr + SD_BLOCKCOUNT); + iowrite16(data->blksz, host->ioaddr + SD_CARDXFERDATALEN); +} + +/* Process requests from the MMC layer */ +static void toshsd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct toshsd_host *host = mmc_priv(mmc); + unsigned long flags; + + /* abort if card not present */ + if (!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + } + + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); + + host->mrq = mrq; + + if (mrq->data) + toshsd_start_data(host, mrq->data); + + toshsd_set_led(host, 1); + + toshsd_start_cmd(host, mrq->cmd); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static void toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct toshsd_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + __toshsd_set_ios(mmc, ios); + spin_unlock_irqrestore(&host->lock, flags); +} + +static int toshsd_get_ro(struct mmc_host *mmc) +{ + struct toshsd_host *host = mmc_priv(mmc); + + /* active low */ + return !(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_WRITE_PROTECT); +} + +static int toshsd_get_cd(struct mmc_host *mmc) +{ + struct toshsd_host *host = mmc_priv(mmc); + + return !!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0); +} + +static struct mmc_host_ops toshsd_ops = { + .request = toshsd_request, + .set_ios = toshsd_set_ios, + .get_ro = toshsd_get_ro, + .get_cd = toshsd_get_cd, +}; + + +static void toshsd_powerdown(struct toshsd_host *host) +{ + /* mask all interrupts */ + iowrite32(0xffffffff, host->ioaddr + SD_INTMASKCARD); + /* disable card clock */ + iowrite16(0x000, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL); + iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); + /* power down card */ + pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, SD_PCICFG_PWR1_OFF); + /* disable clock */ + pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0); +} + +#ifdef CONFIG_PM_SLEEP +static int toshsd_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct toshsd_host *host = pci_get_drvdata(pdev); + + toshsd_powerdown(host); + + pci_save_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int toshsd_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct toshsd_host *host = pci_get_drvdata(pdev); + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + + toshsd_init(host); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + struct toshsd_host *host; + struct mmc_host *mmc; + resource_size_t base; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto err; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + + host->pdev = pdev; + pci_set_drvdata(pdev, host); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret) + goto free; + + host->ioaddr = pci_iomap(pdev, 0, 0); + if (!host->ioaddr) { + ret = -ENOMEM; + goto release; + } + + /* Set MMC host parameters */ + mmc->ops = &toshsd_ops; + mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->ocr_avail = MMC_VDD_32_33; + + mmc->f_min = HCLK / 512; + mmc->f_max = HCLK; + + spin_lock_init(&host->lock); + + toshsd_init(host); + + ret = request_threaded_irq(pdev->irq, toshsd_irq, toshsd_thread_irq, + IRQF_SHARED, DRIVER_NAME, host); + if (ret) + goto unmap; + + mmc_add_host(mmc); + + base = pci_resource_start(pdev, 0); + dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq); + + pm_suspend_ignore_children(&pdev->dev, 1); + + return 0; + +unmap: + pci_iounmap(pdev, host->ioaddr); +release: + pci_release_regions(pdev); +free: + mmc_free_host(mmc); + pci_set_drvdata(pdev, NULL); +err: + pci_disable_device(pdev); + return ret; +} + +static void toshsd_remove(struct pci_dev *pdev) +{ + struct toshsd_host *host = pci_get_drvdata(pdev); + + mmc_remove_host(host->mmc); + toshsd_powerdown(host); + free_irq(pdev->irq, host); + pci_iounmap(pdev, host->ioaddr); + pci_release_regions(pdev); + mmc_free_host(host->mmc); + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); +} + +static const struct dev_pm_ops toshsd_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume) +}; + +static struct pci_driver toshsd_driver = { + .name = DRIVER_NAME, + .id_table = pci_ids, + .probe = toshsd_probe, + .remove = toshsd_remove, + .driver.pm = &toshsd_pm_ops, +}; + +static int __init toshsd_drv_init(void) +{ + return pci_register_driver(&toshsd_driver); +} + +static void __exit toshsd_drv_exit(void) +{ + pci_unregister_driver(&toshsd_driver); +} + +module_init(toshsd_drv_init); +module_exit(toshsd_drv_exit); + +MODULE_AUTHOR("Ondrej Zary, Richard Betts"); +MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/toshsd.h b/drivers/mmc/host/toshsd.h new file mode 100644 index 000000000000..b6c0d89e53a6 --- /dev/null +++ b/drivers/mmc/host/toshsd.h @@ -0,0 +1,176 @@ +/* + * Toshiba PCI Secure Digital Host Controller Interface driver + * + * Copyright (C) 2014 Ondrej Zary + * Copyright (C) 2007 Richard Betts, All Rights Reserved. + * + * Based on asic3_mmc.c Copyright (c) 2005 SDG Systems, LLC + * + * 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. + */ + +#define HCLK 33000000 /* 33 MHz (PCI clock) */ + +#define SD_PCICFG_CLKSTOP 0x40 /* 0x1f = clock controller, 0 = stop */ +#define SD_PCICFG_GATEDCLK 0x41 /* Gated clock */ +#define SD_PCICFG_CLKMODE 0x42 /* Control clock of SD controller */ +#define SD_PCICFG_PINSTATUS 0x44 /* R/O: read status of SD pins */ +#define SD_PCICFG_POWER1 0x48 +#define SD_PCICFG_POWER2 0x49 +#define SD_PCICFG_POWER3 0x4a +#define SD_PCICFG_CARDDETECT 0x4c +#define SD_PCICFG_SLOTS 0x50 /* R/O: define support slot number */ +#define SD_PCICFG_EXTGATECLK1 0xf0 /* Could be used for gated clock */ +#define SD_PCICFG_EXTGATECLK2 0xf1 /* Could be used for gated clock */ +#define SD_PCICFG_EXTGATECLK3 0xf9 /* Bit 1: double buffer/single buffer */ +#define SD_PCICFG_SDLED_ENABLE1 0xfa +#define SD_PCICFG_SDLED_ENABLE2 0xfe + +#define SD_PCICFG_CLKMODE_DIV_DISABLE BIT(0) +#define SD_PCICFG_CLKSTOP_ENABLE_ALL 0x1f +#define SD_PCICFG_LED_ENABLE1_START 0x12 +#define SD_PCICFG_LED_ENABLE2_START 0x80 + +#define SD_PCICFG_PWR1_33V 0x08 /* Set for 3.3 volts */ +#define SD_PCICFG_PWR1_OFF 0x00 /* Turn off power */ +#define SD_PCICFG_PWR2_AUTO 0x02 + +#define SD_CMD 0x00 /* also for SDIO */ +#define SD_ARG0 0x04 /* also for SDIO */ +#define SD_ARG1 0x06 /* also for SDIO */ +#define SD_STOPINTERNAL 0x08 +#define SD_BLOCKCOUNT 0x0a /* also for SDIO */ +#define SD_RESPONSE0 0x0c /* also for SDIO */ +#define SD_RESPONSE1 0x0e /* also for SDIO */ +#define SD_RESPONSE2 0x10 /* also for SDIO */ +#define SD_RESPONSE3 0x12 /* also for SDIO */ +#define SD_RESPONSE4 0x14 /* also for SDIO */ +#define SD_RESPONSE5 0x16 /* also for SDIO */ +#define SD_RESPONSE6 0x18 /* also for SDIO */ +#define SD_RESPONSE7 0x1a /* also for SDIO */ +#define SD_CARDSTATUS 0x1c /* also for SDIO */ +#define SD_BUFFERCTRL 0x1e /* also for SDIO */ +#define SD_INTMASKCARD 0x20 /* also for SDIO */ +#define SD_INTMASKBUFFER 0x22 /* also for SDIO */ +#define SD_CARDCLOCKCTRL 0x24 +#define SD_CARDXFERDATALEN 0x26 /* also for SDIO */ +#define SD_CARDOPTIONSETUP 0x28 /* also for SDIO */ +#define SD_ERRORSTATUS0 0x2c /* also for SDIO */ +#define SD_ERRORSTATUS1 0x2e /* also for SDIO */ +#define SD_DATAPORT 0x30 /* also for SDIO */ +#define SD_TRANSACTIONCTRL 0x34 /* also for SDIO */ +#define SD_SOFTWARERESET 0xe0 /* also for SDIO */ + +/* registers above marked "also for SDIO" and all SDIO registers below can be + * accessed at SDIO_BASE + reg address */ +#define SDIO_BASE 0x100 + +#define SDIO_CARDPORTSEL 0x02 +#define SDIO_CARDINTCTRL 0x36 +#define SDIO_CLOCKNWAITCTRL 0x38 +#define SDIO_HOSTINFORMATION 0x3a +#define SDIO_ERRORCTRL 0x3c +#define SDIO_LEDCTRL 0x3e + +#define SD_TRANSCTL_SET BIT(8) + +#define SD_CARDCLK_DIV_DISABLE BIT(15) +#define SD_CARDCLK_ENABLE_CLOCK BIT(8) +#define SD_CARDCLK_CLK_DIV_512 BIT(7) +#define SD_CARDCLK_CLK_DIV_256 BIT(6) +#define SD_CARDCLK_CLK_DIV_128 BIT(5) +#define SD_CARDCLK_CLK_DIV_64 BIT(4) +#define SD_CARDCLK_CLK_DIV_32 BIT(3) +#define SD_CARDCLK_CLK_DIV_16 BIT(2) +#define SD_CARDCLK_CLK_DIV_8 BIT(1) +#define SD_CARDCLK_CLK_DIV_4 BIT(0) +#define SD_CARDCLK_CLK_DIV_2 0 + +#define SD_CARDOPT_REQUIRED 0x000e +#define SD_CARDOPT_DATA_RESP_TIMEOUT(x) (((x) & 0x0f) << 4) /* 4 bits */ +#define SD_CARDOPT_C2_MODULE_ABSENT BIT(14) +#define SD_CARDOPT_DATA_XFR_WIDTH_1 (1 << 15) +#define SD_CARDOPT_DATA_XFR_WIDTH_4 (0 << 15) + +#define SD_CMD_TYPE_CMD (0 << 6) +#define SD_CMD_TYPE_ACMD (1 << 6) +#define SD_CMD_TYPE_AUTHEN (2 << 6) +#define SD_CMD_RESP_TYPE_NONE (3 << 8) +#define SD_CMD_RESP_TYPE_EXT_R1 (4 << 8) +#define SD_CMD_RESP_TYPE_EXT_R1B (5 << 8) +#define SD_CMD_RESP_TYPE_EXT_R2 (6 << 8) +#define SD_CMD_RESP_TYPE_EXT_R3 (7 << 8) +#define SD_CMD_RESP_TYPE_EXT_R6 (4 << 8) +#define SD_CMD_RESP_TYPE_EXT_R7 (4 << 8) +#define SD_CMD_DATA_PRESENT BIT(11) +#define SD_CMD_TRANSFER_READ BIT(12) +#define SD_CMD_MULTI_BLOCK BIT(13) +#define SD_CMD_SECURITY_CMD BIT(14) + +#define SD_STOPINT_ISSUE_CMD12 BIT(0) +#define SD_STOPINT_AUTO_ISSUE_CMD12 BIT(8) + +#define SD_CARD_RESP_END BIT(0) +#define SD_CARD_RW_END BIT(2) +#define SD_CARD_CARD_REMOVED_0 BIT(3) +#define SD_CARD_CARD_INSERTED_0 BIT(4) +#define SD_CARD_PRESENT_0 BIT(5) +#define SD_CARD_UNK6 BIT(6) +#define SD_CARD_WRITE_PROTECT BIT(7) +#define SD_CARD_CARD_REMOVED_3 BIT(8) +#define SD_CARD_CARD_INSERTED_3 BIT(9) +#define SD_CARD_PRESENT_3 BIT(10) + +#define SD_BUF_CMD_INDEX_ERR BIT(16) +#define SD_BUF_CRC_ERR BIT(17) +#define SD_BUF_STOP_BIT_END_ERR BIT(18) +#define SD_BUF_DATA_TIMEOUT BIT(19) +#define SD_BUF_OVERFLOW BIT(20) +#define SD_BUF_UNDERFLOW BIT(21) +#define SD_BUF_CMD_TIMEOUT BIT(22) +#define SD_BUF_UNK7 BIT(23) +#define SD_BUF_READ_ENABLE BIT(24) +#define SD_BUF_WRITE_ENABLE BIT(25) +#define SD_BUF_ILLEGAL_FUNCTION BIT(29) +#define SD_BUF_CMD_BUSY BIT(30) +#define SD_BUF_ILLEGAL_ACCESS BIT(31) + +#define SD_ERR0_RESP_CMD_ERR BIT(0) +#define SD_ERR0_RESP_NON_CMD12_END_BIT_ERR BIT(2) +#define SD_ERR0_RESP_CMD12_END_BIT_ERR BIT(3) +#define SD_ERR0_READ_DATA_END_BIT_ERR BIT(4) +#define SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR BIT(5) +#define SD_ERR0_RESP_NON_CMD12_CRC_ERR BIT(8) +#define SD_ERR0_RESP_CMD12_CRC_ERR BIT(9) +#define SD_ERR0_READ_DATA_CRC_ERR BIT(10) +#define SD_ERR0_WRITE_CMD_CRC_ERR BIT(11) + +#define SD_ERR1_NO_CMD_RESP BIT(16) +#define SD_ERR1_TIMEOUT_READ_DATA BIT(20) +#define SD_ERR1_TIMEOUT_CRS_STATUS BIT(21) +#define SD_ERR1_TIMEOUT_CRC_BUSY BIT(22) + +#define IRQ_DONT_CARE_BITS (SD_CARD_PRESENT_3 \ + | SD_CARD_WRITE_PROTECT \ + | SD_CARD_UNK6 \ + | SD_CARD_PRESENT_0 \ + | SD_BUF_UNK7 \ + | SD_BUF_CMD_BUSY) + +struct toshsd_host { + struct pci_dev *pdev; + struct mmc_host *mmc; + + spinlock_t lock; + + struct mmc_request *mrq;/* Current request */ + struct mmc_command *cmd;/* Current command */ + struct mmc_data *data; /* Current data request */ + + struct sg_mapping_iter sg_miter; /* for PIO */ + + void __iomem *ioaddr; /* mapped address */ +}; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c13d83e15ace..45f09a66e6c9 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -225,7 +225,12 @@ static int bond_changelink(struct net_device *bond_dev, bond_option_arp_ip_targets_clear(bond); nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) { - __be32 target = nla_get_be32(attr); + __be32 target; + + if (nla_len(attr) < sizeof(target)) + return -EINVAL; + + target = nla_get_be32(attr); bond_opt_initval(&newval, (__force u64)target); err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 8520d5529df8..279873cb6e3a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2442,9 +2442,13 @@ static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps) SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full | SUPPORTED_10000baseKX4_Full; else if (type == FW_PORT_TYPE_FIBER_XFI || - type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) + type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) { v |= SUPPORTED_FIBRE; - else if (type == FW_PORT_TYPE_BP40_BA) + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_BP40_BA) v |= SUPPORTED_40000baseSR4_Full; if (caps & FW_PORT_CAP_ANEG) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 60e9c2cd051e..b5db6b3f939f 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -917,21 +917,13 @@ static int sh_eth_reset(struct net_device *ndev) return ret; } -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static void sh_eth_set_receive_align(struct sk_buff *skb) { - int reserve; + uintptr_t reserve = (uintptr_t)skb->data & (SH_ETH_RX_ALIGN - 1); - reserve = SH4_SKB_RX_ALIGN - ((u32)skb->data & (SH4_SKB_RX_ALIGN - 1)); if (reserve) - skb_reserve(skb, reserve); + skb_reserve(skb, SH_ETH_RX_ALIGN - reserve); } -#else -static void sh_eth_set_receive_align(struct sk_buff *skb) -{ - skb_reserve(skb, SH2_SH3_SKB_RX_ALIGN); -} -#endif /* CPU <-> EDMAC endian convert */ @@ -1119,6 +1111,7 @@ static void sh_eth_ring_format(struct net_device *ndev) struct sh_eth_txdesc *txdesc = NULL; int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring; int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring; + int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; mdp->cur_rx = 0; mdp->cur_tx = 0; @@ -1131,21 +1124,21 @@ static void sh_eth_ring_format(struct net_device *ndev) for (i = 0; i < mdp->num_rx_ring; i++) { /* skb */ mdp->rx_skbuff[i] = NULL; - skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); + skb = netdev_alloc_skb(ndev, skbuff_size); mdp->rx_skbuff[i] = skb; if (skb == NULL) break; - dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz, - DMA_FROM_DEVICE); sh_eth_set_receive_align(skb); /* RX descriptor */ rxdesc = &mdp->rx_ring[i]; + /* The size of the buffer is a multiple of 16 bytes. */ + rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); + dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length, + DMA_FROM_DEVICE); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP); - /* The size of the buffer is 16 byte boundary. */ - rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); /* Rx descriptor address set */ if (i == 0) { sh_eth_write(ndev, mdp->rx_desc_dma, RDLAR); @@ -1397,6 +1390,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) struct sk_buff *skb; u16 pkt_len = 0; u32 desc_status; + int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; rxdesc = &mdp->rx_ring[entry]; while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) { @@ -1448,7 +1442,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) if (mdp->cd->rpadir) skb_reserve(skb, NET_IP_ALIGN); dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr, - mdp->rx_buf_sz, + ALIGN(mdp->rx_buf_sz, 16), DMA_FROM_DEVICE); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); @@ -1468,13 +1462,13 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); if (mdp->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); + skb = netdev_alloc_skb(ndev, skbuff_size); mdp->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ - dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz, - DMA_FROM_DEVICE); sh_eth_set_receive_align(skb); + dma_map_single(&ndev->dev, skb->data, + rxdesc->buffer_length, DMA_FROM_DEVICE); skb_checksum_none_assert(skb); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); @@ -2042,6 +2036,8 @@ static int sh_eth_open(struct net_device *ndev) if (ret) goto out_free_irq; + mdp->is_opened = 1; + return ret; out_free_irq: @@ -2131,6 +2127,36 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_OK; } +static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + if (sh_eth_is_rz_fast_ether(mdp)) + return &ndev->stats; + + if (!mdp->is_opened) + return &ndev->stats; + + ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR); + sh_eth_write(ndev, 0, TROCR); /* (write clear) */ + ndev->stats.collisions += sh_eth_read(ndev, CDCR); + sh_eth_write(ndev, 0, CDCR); /* (write clear) */ + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR); + sh_eth_write(ndev, 0, LCCR); /* (write clear) */ + + if (sh_eth_is_gether(mdp)) { + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR); + sh_eth_write(ndev, 0, CERCR); /* (write clear) */ + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR); + sh_eth_write(ndev, 0, CEECR); /* (write clear) */ + } else { + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR); + sh_eth_write(ndev, 0, CNDCR); /* (write clear) */ + } + + return &ndev->stats; +} + /* device close function */ static int sh_eth_close(struct net_device *ndev) { @@ -2145,6 +2171,7 @@ static int sh_eth_close(struct net_device *ndev) sh_eth_write(ndev, 0, EDTRR); sh_eth_write(ndev, 0, EDRRR); + sh_eth_get_stats(ndev); /* PHY Disconnect */ if (mdp->phydev) { phy_stop(mdp->phydev); @@ -2163,36 +2190,9 @@ static int sh_eth_close(struct net_device *ndev) pm_runtime_put_sync(&mdp->pdev->dev); - return 0; -} - -static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (sh_eth_is_rz_fast_ether(mdp)) - return &ndev->stats; + mdp->is_opened = 0; - pm_runtime_get_sync(&mdp->pdev->dev); - - ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR); - sh_eth_write(ndev, 0, TROCR); /* (write clear) */ - ndev->stats.collisions += sh_eth_read(ndev, CDCR); - sh_eth_write(ndev, 0, CDCR); /* (write clear) */ - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR); - sh_eth_write(ndev, 0, LCCR); /* (write clear) */ - if (sh_eth_is_gether(mdp)) { - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR); - sh_eth_write(ndev, 0, CERCR); /* (write clear) */ - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR); - sh_eth_write(ndev, 0, CEECR); /* (write clear) */ - } else { - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR); - sh_eth_write(ndev, 0, CNDCR); /* (write clear) */ - } - pm_runtime_put_sync(&mdp->pdev->dev); - - return &ndev->stats; + return 0; } /* ioctl to device function */ diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index b37c427144ee..22301bf9c21d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -162,9 +162,9 @@ enum { /* Driver's parameters */ #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) -#define SH4_SKB_RX_ALIGN 32 +#define SH_ETH_RX_ALIGN 32 #else -#define SH2_SH3_SKB_RX_ALIGN 2 +#define SH_ETH_RX_ALIGN 2 #endif /* Register's bits @@ -522,6 +522,7 @@ struct sh_eth_private { unsigned no_ether_link:1; unsigned ether_link_active_low:1; + unsigned is_opened:1; }; static inline void sh_eth_soft_swap(char *src, int len) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 5b0da3986216..58a1a0a423d4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -265,6 +265,15 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) plat_dat = dev_get_platdata(&pdev->dev); + if (!plat_dat) + plat_dat = devm_kzalloc(&pdev->dev, + sizeof(struct plat_stmmacenet_data), + GFP_KERNEL); + if (!plat_dat) { + pr_err("%s: ERROR: no memory", __func__); + return -ENOMEM; + } + /* Set default value for multicast hash bins */ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; @@ -272,15 +281,6 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) plat_dat->unicast_filter_entries = 1; if (pdev->dev.of_node) { - if (!plat_dat) - plat_dat = devm_kzalloc(&pdev->dev, - sizeof(struct plat_stmmacenet_data), - GFP_KERNEL); - if (!plat_dat) { - pr_err("%s: ERROR: no memory", __func__); - return -ENOMEM; - } - ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); if (ret) { pr_err("%s: main dt probe failed", __func__); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index cca871346a0f..ece8d1804d13 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -496,9 +496,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, len = skb_frag_size(frag); offset = frag->page_offset; - /* Data must not cross a page boundary. */ - BUG_ON(len + offset > PAGE_SIZE<<compound_order(page)); - /* Skip unused frames from start of page */ page += offset >> PAGE_SHIFT; offset &= ~PAGE_MASK; @@ -506,8 +503,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, while (len > 0) { unsigned long bytes; - BUG_ON(offset >= PAGE_SIZE); - bytes = PAGE_SIZE - offset; if (bytes > len) bytes = len; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 30e97bcc4f88..d134710de96d 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -964,8 +964,6 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, bool nomap) { - if (memblock_is_region_reserved(base, size)) - return -EBUSY; if (nomap) return memblock_remove(base, size); return memblock_reserve(base, size); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 3d43874319be..19bb19c7db4a 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -276,6 +276,7 @@ struct tegra_pcie { struct resource all; struct resource io; + struct resource pio; struct resource mem; struct resource prefetch; struct resource busn; @@ -658,7 +659,6 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { struct tegra_pcie *pcie = sys_to_pcie(sys); int err; - phys_addr_t io_start; err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem); if (err < 0) @@ -668,14 +668,12 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) if (err) return err; - io_start = pci_pio_to_address(pcie->io.start); - pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource_offset(&sys->resources, &pcie->prefetch, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); - pci_ioremap_io(nr * SZ_64K, io_start); + pci_ioremap_io(pcie->pio.start, pcie->io.start); return 1; } @@ -786,7 +784,6 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) { u32 fpci_bar, size, axi_address; - phys_addr_t io_start = pci_pio_to_address(pcie->io.start); /* Bar 0: type 1 extended configuration space */ fpci_bar = 0xfe100000; @@ -799,7 +796,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) /* Bar 1: downstream IO bar */ fpci_bar = 0xfdfc0000; size = resource_size(&pcie->io); - axi_address = io_start; + axi_address = pcie->io.start; afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); @@ -1690,8 +1687,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) switch (res.flags & IORESOURCE_TYPE_BITS) { case IORESOURCE_IO: - memcpy(&pcie->io, &res, sizeof(res)); - pcie->io.name = np->full_name; + memcpy(&pcie->pio, &res, sizeof(res)); + pcie->pio.name = np->full_name; + + /* + * The Tegra PCIe host bridge uses this to program the + * mapping of the I/O space to the physical address, + * so we override the .start and .end fields here that + * of_pci_range_to_resource() converted to I/O space. + * We also set the IORESOURCE_MEM type to clarify that + * the resource is in the physical memory space. + */ + pcie->io.start = range.cpu_addr; + pcie->io.end = range.cpu_addr + range.size - 1; + pcie->io.flags = IORESOURCE_MEM; + pcie->io.name = "I/O"; + + memcpy(&res, &pcie->io, sizeof(res)); break; case IORESOURCE_MEM: diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 55d7b7b0f2e0..5e061347be93 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -529,13 +529,13 @@ config REGULATOR_S2MPA01 via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. config REGULATOR_S2MPS11 - tristate "Samsung S2MPS11/S2MPS14/S2MPU02 voltage regulator" + tristate "Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage regulator" depends on MFD_SEC_CORE help - This driver supports a Samsung S2MPS11/S2MPS14/S2MPU02 voltage output - regulator via I2C bus. The chip is comprised of high efficient Buck - converters including Dual-Phase Buck converter, Buck-Boost converter, - various LDOs. + This driver supports a Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage + output regulator via I2C bus. The chip is comprised of high efficient + Buck converters including Dual-Phase Buck converter, Buck-Boost + converter, various LDOs. config REGULATOR_S5M8767 tristate "Samsung S5M8767A voltage regulator" diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index adab82d5279f..738dc7763d47 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -30,6 +30,7 @@ #include <linux/of_gpio.h> #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mpu02.h> @@ -45,10 +46,10 @@ struct s2mps11_info { enum sec_device_type dev_type; /* - * One bit for each S2MPS14/S2MPU02 regulator whether the suspend mode - * was enabled. + * One bit for each S2MPS13/S2MPS14/S2MPU02 regulator whether + * the suspend mode was enabled. */ - unsigned long long s2mps14_suspend_state:35; + unsigned long long s2mps14_suspend_state:50; /* Array of size rdev_num with GPIO-s for external sleep control */ int *ext_control_gpio; @@ -369,12 +370,101 @@ static const struct regulator_desc s2mps11_regulators[] = { regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV), }; +static struct regulator_ops s2mps14_reg_ops; + +#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \ + .name = "LDO"#num, \ + .id = S2MPS13_LDO##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS13_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS14_LDO_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +#define regulator_desc_s2mps13_buck(num, min, step, min_sel) { \ + .name = "BUCK"#num, \ + .id = S2MPS13_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS13_REG_B1OUT + (num - 1) * 2, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +static const struct regulator_desc s2mps13_regulators[] = { + regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(3, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(4, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(5, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(6, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(7, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(8, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(9, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(10, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(11, MIN_800_MV, STEP_25_MV, 0x10), + regulator_desc_s2mps13_ldo(12, MIN_800_MV, STEP_25_MV, 0x10), + regulator_desc_s2mps13_ldo(13, MIN_800_MV, STEP_25_MV, 0x10), + regulator_desc_s2mps13_ldo(14, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(15, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(16, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(17, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(18, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(19, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(20, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(21, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(22, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(23, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(24, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(25, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(26, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(27, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(28, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(29, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(30, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(31, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(32, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(33, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(34, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(35, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(36, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(37, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(38, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(39, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(40, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_buck(1, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(2, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(3, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(7, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(8, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck(9, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck(10, MIN_500_MV, STEP_6_25_MV, 0x10), +}; + static int s2mps14_regulator_enable(struct regulator_dev *rdev) { struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); unsigned int val; switch (s2mps11->dev_type) { + case S2MPS13X: case S2MPS14X: if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) val = S2MPS14_ENABLE_SUSPEND; @@ -406,6 +496,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev) /* Below LDO should be always on or does not support suspend mode. */ switch (s2mps11->dev_type) { + case S2MPS13X: case S2MPS14X: switch (rdev_id) { case S2MPS14_LDO3: @@ -831,6 +922,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) s2mps11->rdev_num = ARRAY_SIZE(s2mps11_regulators); regulators = s2mps11_regulators; break; + case S2MPS13X: + s2mps11->rdev_num = ARRAY_SIZE(s2mps13_regulators); + regulators = s2mps13_regulators; + break; case S2MPS14X: s2mps11->rdev_num = ARRAY_SIZE(s2mps14_regulators); regulators = s2mps14_regulators; @@ -927,6 +1022,7 @@ out: static const struct platform_device_id s2mps11_pmic_id[] = { { "s2mps11-pmic", S2MPS11X}, + { "s2mps13-pmic", S2MPS13X}, { "s2mps14-pmic", S2MPS14X}, { "s2mpu02-pmic", S2MPU02}, { }, diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 8532c3e2aea7..1626dc66e763 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -161,7 +161,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { static const struct s3c2410_wdt_variant drv_data_exynos7 = { .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, - .mask_bit = 0, + .mask_bit = 23, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 23, /* A57 WDTRESET */ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 6df8d3d885e5..b8b92c2f9683 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -736,7 +736,12 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, } alias = d_find_alias(inode); - if (alias && !vfat_d_anon_disconn(alias)) { + /* + * Checking "alias->d_parent == dentry->d_parent" to make sure + * FS is not corrupted (especially double linked dir). + */ + if (alias && alias->d_parent == dentry->d_parent && + !vfat_d_anon_disconn(alias)) { /* * This inode has non anonymous-DCACHE_DISCONNECTED * dentry. This means, the user did ->lookup() by an @@ -755,12 +760,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, out: mutex_unlock(&MSDOS_SB(sb)->s_lock); - dentry->d_time = dentry->d_parent->d_inode->i_version; - dentry = d_splice_alias(inode, dentry); - if (dentry) - dentry->d_time = dentry->d_parent->d_inode->i_version; - return dentry; - + if (!inode) + dentry->d_time = dir->i_version; + return d_splice_alias(inode, dentry); error: mutex_unlock(&MSDOS_SB(sb)->s_lock); return ERR_PTR(err); @@ -793,7 +795,6 @@ static int vfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry, inode); out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -824,6 +825,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry) clear_nlink(inode); inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; fat_detach(inode); + dentry->d_time = dir->i_version; out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -849,6 +851,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry) clear_nlink(inode); inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; fat_detach(inode); + dentry->d_time = dir->i_version; out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -889,7 +892,6 @@ static int vfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry, inode); mutex_unlock(&MSDOS_SB(sb)->s_lock); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index e4dc74713a43..1df94fabe4eb 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1853,13 +1853,12 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, journal->j_chksum_driver = NULL; return 0; } - } - /* Precompute checksum seed for all metadata */ - if (jbd2_journal_has_csum_v2or3(journal)) + /* Precompute checksum seed for all metadata */ journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, sizeof(sb->s_uuid)); + } } /* If enabling v1 checksums, downgrade superblock */ diff --git a/include/linux/atmel-mci.h b/include/linux/atmel-mci.h index 91b77f8d495d..9177947bf032 100644 --- a/include/linux/atmel-mci.h +++ b/include/linux/atmel-mci.h @@ -11,6 +11,7 @@ * @detect_pin: GPIO pin wired to the card detect switch * @wp_pin: GPIO pin wired to the write protect sensor * @detect_is_active_high: The state of the detect pin when it is active + * @non_removable: The slot is not removable, only detect once * * If a given slot is not present on the board, @bus_width should be * set to 0. The other fields are ignored in this case. @@ -26,6 +27,7 @@ struct mci_slot_pdata { int detect_pin; int wp_pin; bool detect_is_active_high; + bool non_removable; }; /** diff --git a/include/linux/edac.h b/include/linux/edac.h index e1e68da6f35c..da3b72e95db3 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -194,7 +194,8 @@ static inline char *mc_event_error_type(const unsigned int err_type) * @MEM_DDR3: DDR3 RAM * @MEM_RDDR3: Registered DDR3 RAM * This is a variant of the DDR3 memories. - * @MEM_DDR4: DDR4 RAM + * @MEM_LRDDR3 Load-Reduced DDR3 memory. + * @MEM_DDR4: Unbuffered DDR4 RAM * @MEM_RDDR4: Registered DDR4 RAM * This is a variant of the DDR4 memories. */ @@ -216,6 +217,7 @@ enum mem_type { MEM_XDR, MEM_DDR3, MEM_RDDR3, + MEM_LRDDR3, MEM_DDR4, MEM_RDDR4, }; diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index adba89d9c660..689312745b2f 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -12,7 +12,6 @@ int ab8500_sysctrl_read(u16 reg, u8 *value); int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value); -void ab8500_restart(char mode, const char *cmd); #else diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index c0b075f6bc35..aacc10d7789c 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -125,6 +125,8 @@ #define ARIZONA_MIC_BIAS_CTRL_1 0x218 #define ARIZONA_MIC_BIAS_CTRL_2 0x219 #define ARIZONA_MIC_BIAS_CTRL_3 0x21A +#define ARIZONA_HP_CTRL_1L 0x225 +#define ARIZONA_HP_CTRL_1R 0x226 #define ARIZONA_ACCESSORY_DETECT_MODE_1 0x293 #define ARIZONA_HEADPHONE_DETECT_1 0x29B #define ARIZONA_HEADPHONE_DETECT_2 0x29C @@ -279,8 +281,16 @@ #define ARIZONA_AIF2_FRAME_CTRL_2 0x548 #define ARIZONA_AIF2_FRAME_CTRL_3 0x549 #define ARIZONA_AIF2_FRAME_CTRL_4 0x54A +#define ARIZONA_AIF2_FRAME_CTRL_5 0x54B +#define ARIZONA_AIF2_FRAME_CTRL_6 0x54C +#define ARIZONA_AIF2_FRAME_CTRL_7 0x54D +#define ARIZONA_AIF2_FRAME_CTRL_8 0x54E #define ARIZONA_AIF2_FRAME_CTRL_11 0x551 #define ARIZONA_AIF2_FRAME_CTRL_12 0x552 +#define ARIZONA_AIF2_FRAME_CTRL_13 0x553 +#define ARIZONA_AIF2_FRAME_CTRL_14 0x554 +#define ARIZONA_AIF2_FRAME_CTRL_15 0x555 +#define ARIZONA_AIF2_FRAME_CTRL_16 0x556 #define ARIZONA_AIF2_TX_ENABLES 0x559 #define ARIZONA_AIF2_RX_ENABLES 0x55A #define ARIZONA_AIF2_FORCE_WRITE 0x55B @@ -2245,6 +2255,46 @@ #define ARIZONA_MICB3_ENA_WIDTH 1 /* MICB3_ENA */ /* + * R549 (0x225) - HP Ctrl 1L + */ +#define ARIZONA_RMV_SHRT_HP1L 0x4000 /* RMV_SHRT_HP1L */ +#define ARIZONA_RMV_SHRT_HP1L_MASK 0x4000 /* RMV_SHRT_HP1L */ +#define ARIZONA_RMV_SHRT_HP1L_SHIFT 14 /* RMV_SHRT_HP1L */ +#define ARIZONA_RMV_SHRT_HP1L_WIDTH 1 /* RMV_SHRT_HP1L */ +#define ARIZONA_HP1L_FLWR 0x0004 /* HP1L_FLWR */ +#define ARIZONA_HP1L_FLWR_MASK 0x0004 /* HP1L_FLWR */ +#define ARIZONA_HP1L_FLWR_SHIFT 2 /* HP1L_FLWR */ +#define ARIZONA_HP1L_FLWR_WIDTH 1 /* HP1L_FLWR */ +#define ARIZONA_HP1L_SHRTI 0x0002 /* HP1L_SHRTI */ +#define ARIZONA_HP1L_SHRTI_MASK 0x0002 /* HP1L_SHRTI */ +#define ARIZONA_HP1L_SHRTI_SHIFT 1 /* HP1L_SHRTI */ +#define ARIZONA_HP1L_SHRTI_WIDTH 1 /* HP1L_SHRTI */ +#define ARIZONA_HP1L_SHRTO 0x0001 /* HP1L_SHRTO */ +#define ARIZONA_HP1L_SHRTO_MASK 0x0001 /* HP1L_SHRTO */ +#define ARIZONA_HP1L_SHRTO_SHIFT 0 /* HP1L_SHRTO */ +#define ARIZONA_HP1L_SHRTO_WIDTH 1 /* HP1L_SHRTO */ + +/* + * R550 (0x226) - HP Ctrl 1R + */ +#define ARIZONA_RMV_SHRT_HP1R 0x4000 /* RMV_SHRT_HP1R */ +#define ARIZONA_RMV_SHRT_HP1R_MASK 0x4000 /* RMV_SHRT_HP1R */ +#define ARIZONA_RMV_SHRT_HP1R_SHIFT 14 /* RMV_SHRT_HP1R */ +#define ARIZONA_RMV_SHRT_HP1R_WIDTH 1 /* RMV_SHRT_HP1R */ +#define ARIZONA_HP1R_FLWR 0x0004 /* HP1R_FLWR */ +#define ARIZONA_HP1R_FLWR_MASK 0x0004 /* HP1R_FLWR */ +#define ARIZONA_HP1R_FLWR_SHIFT 2 /* HP1R_FLWR */ +#define ARIZONA_HP1R_FLWR_WIDTH 1 /* HP1R_FLWR */ +#define ARIZONA_HP1R_SHRTI 0x0002 /* HP1R_SHRTI */ +#define ARIZONA_HP1R_SHRTI_MASK 0x0002 /* HP1R_SHRTI */ +#define ARIZONA_HP1R_SHRTI_SHIFT 1 /* HP1R_SHRTI */ +#define ARIZONA_HP1R_SHRTI_WIDTH 1 /* HP1R_SHRTI */ +#define ARIZONA_HP1R_SHRTO 0x0001 /* HP1R_SHRTO */ +#define ARIZONA_HP1R_SHRTO_MASK 0x0001 /* HP1R_SHRTO */ +#define ARIZONA_HP1R_SHRTO_SHIFT 0 /* HP1R_SHRTO */ +#define ARIZONA_HP1R_SHRTO_WIDTH 1 /* HP1R_SHRTO */ + +/* * R659 (0x293) - Accessory Detect Mode 1 */ #define ARIZONA_ACCDET_SRC 0x2000 /* ACCDET_SRC */ diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h new file mode 100644 index 000000000000..1279ab1644b5 --- /dev/null +++ b/include/linux/mfd/atmel-hlcdc.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 Free Electrons + * Copyright (C) 2014 Atmel + * + * Author: Boris BREZILLON <boris.brezillon@free-electrons.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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LINUX_MFD_HLCDC_H +#define __LINUX_MFD_HLCDC_H + +#include <linux/clk.h> +#include <linux/regmap.h> + +#define ATMEL_HLCDC_CFG(i) ((i) * 0x4) +#define ATMEL_HLCDC_SIG_CFG LCDCFG(5) +#define ATMEL_HLCDC_HSPOL BIT(0) +#define ATMEL_HLCDC_VSPOL BIT(1) +#define ATMEL_HLCDC_VSPDLYS BIT(2) +#define ATMEL_HLCDC_VSPDLYE BIT(3) +#define ATMEL_HLCDC_DISPPOL BIT(4) +#define ATMEL_HLCDC_DITHER BIT(6) +#define ATMEL_HLCDC_DISPDLY BIT(7) +#define ATMEL_HLCDC_MODE_MASK GENMASK(9, 8) +#define ATMEL_HLCDC_PP BIT(10) +#define ATMEL_HLCDC_VSPSU BIT(12) +#define ATMEL_HLCDC_VSPHO BIT(13) +#define ATMEL_HLCDC_GUARDTIME_MASK GENMASK(20, 16) + +#define ATMEL_HLCDC_EN 0x20 +#define ATMEL_HLCDC_DIS 0x24 +#define ATMEL_HLCDC_SR 0x28 +#define ATMEL_HLCDC_IER 0x2c +#define ATMEL_HLCDC_IDR 0x30 +#define ATMEL_HLCDC_IMR 0x34 +#define ATMEL_HLCDC_ISR 0x38 + +#define ATMEL_HLCDC_CLKPOL BIT(0) +#define ATMEL_HLCDC_CLKSEL BIT(2) +#define ATMEL_HLCDC_CLKPWMSEL BIT(3) +#define ATMEL_HLCDC_CGDIS(i) BIT(8 + (i)) +#define ATMEL_HLCDC_CLKDIV_SHFT 16 +#define ATMEL_HLCDC_CLKDIV_MASK GENMASK(23, 16) +#define ATMEL_HLCDC_CLKDIV(div) ((div - 2) << ATMEL_HLCDC_CLKDIV_SHFT) + +#define ATMEL_HLCDC_PIXEL_CLK BIT(0) +#define ATMEL_HLCDC_SYNC BIT(1) +#define ATMEL_HLCDC_DISP BIT(2) +#define ATMEL_HLCDC_PWM BIT(3) +#define ATMEL_HLCDC_SIP BIT(4) + +#define ATMEL_HLCDC_SOF BIT(0) +#define ATMEL_HLCDC_SYNCDIS BIT(1) +#define ATMEL_HLCDC_FIFOERR BIT(4) +#define ATMEL_HLCDC_LAYER_STATUS(x) BIT((x) + 8) + +/** + * Structure shared by the MFD device and its subdevices. + * + * @regmap: register map used to access HLCDC IP registers + * @periph_clk: the hlcdc peripheral clock + * @sys_clk: the hlcdc system clock + * @slow_clk: the system slow clk + * @irq: the hlcdc irq + */ +struct atmel_hlcdc { + struct regmap *regmap; + struct clk *periph_clk; + struct clk *sys_clk; + struct clk *slow_clk; + int irq; +}; + +#endif /* __LINUX_MFD_HLCDC_H */ diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index d0e31a2287ac..81589d176ae8 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -14,6 +14,8 @@ enum { AXP202_ID = 0, AXP209_ID, + AXP288_ID, + NR_AXP20X_VARIANTS, }; #define AXP20X_DATACACHE(m) (0x04 + (m)) @@ -49,11 +51,13 @@ enum { #define AXP20X_IRQ3_EN 0x42 #define AXP20X_IRQ4_EN 0x43 #define AXP20X_IRQ5_EN 0x44 +#define AXP20X_IRQ6_EN 0x45 #define AXP20X_IRQ1_STATE 0x48 #define AXP20X_IRQ2_STATE 0x49 #define AXP20X_IRQ3_STATE 0x4a #define AXP20X_IRQ4_STATE 0x4b #define AXP20X_IRQ5_STATE 0x4c +#define AXP20X_IRQ6_STATE 0x4d /* ADC */ #define AXP20X_ACIN_V_ADC_H 0x56 @@ -116,6 +120,15 @@ enum { #define AXP20X_CC_CTRL 0xb8 #define AXP20X_FG_RES 0xb9 +/* AXP288 specific registers */ +#define AXP288_PMIC_ADC_H 0x56 +#define AXP288_PMIC_ADC_L 0x57 +#define AXP288_ADC_TS_PIN_CTRL 0x84 + +#define AXP288_PMIC_ADC_EN 0x84 +#define AXP288_FG_TUNE5 0xed + + /* Regulators IDs */ enum { AXP20X_LDO1 = 0, @@ -169,12 +182,58 @@ enum { AXP20X_IRQ_GPIO0_INPUT, }; +enum axp288_irqs { + AXP288_IRQ_VBUS_FALL = 2, + AXP288_IRQ_VBUS_RISE, + AXP288_IRQ_OV, + AXP288_IRQ_FALLING_ALT, + AXP288_IRQ_RISING_ALT, + AXP288_IRQ_OV_ALT, + AXP288_IRQ_DONE = 10, + AXP288_IRQ_CHARGING, + AXP288_IRQ_SAFE_QUIT, + AXP288_IRQ_SAFE_ENTER, + AXP288_IRQ_ABSENT, + AXP288_IRQ_APPEND, + AXP288_IRQ_QWBTU, + AXP288_IRQ_WBTU, + AXP288_IRQ_QWBTO, + AXP288_IRQ_WBTO, + AXP288_IRQ_QCBTU, + AXP288_IRQ_CBTU, + AXP288_IRQ_QCBTO, + AXP288_IRQ_CBTO, + AXP288_IRQ_WL2, + AXP288_IRQ_WL1, + AXP288_IRQ_GPADC, + AXP288_IRQ_OT = 31, + AXP288_IRQ_GPIO0, + AXP288_IRQ_GPIO1, + AXP288_IRQ_POKO, + AXP288_IRQ_POKL, + AXP288_IRQ_POKS, + AXP288_IRQ_POKN, + AXP288_IRQ_POKP, + AXP288_IRQ_TIMER, + AXP288_IRQ_MV_CHNG, + AXP288_IRQ_BC_USB_CHNG, +}; + +#define AXP288_TS_ADC_H 0x58 +#define AXP288_TS_ADC_L 0x59 +#define AXP288_GP_ADC_H 0x5a +#define AXP288_GP_ADC_L 0x5b + struct axp20x_dev { struct device *dev; struct i2c_client *i2c_client; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; long variant; + int nr_cells; + struct mfd_cell *cells; + const struct regmap_config *regmap_cfg; + const struct regmap_irq_chip *regmap_irq_chip; }; #endif /* __LINUX_MFD_AXP20X_H */ diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 73e1709d4c09..a76bc100bf97 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -111,6 +111,13 @@ extern int mfd_add_devices(struct device *parent, int id, struct resource *mem_base, int irq_base, struct irq_domain *irq_domain); +static inline int mfd_add_hotplug_devices(struct device *parent, + const struct mfd_cell *cells, int n_devs) +{ + return mfd_add_devices(parent, PLATFORM_DEVID_AUTO, cells, n_devs, + NULL, 0, NULL); +} + extern void mfd_remove_devices(struct device *parent); #endif diff --git a/include/linux/mfd/dln2.h b/include/linux/mfd/dln2.h new file mode 100644 index 000000000000..004b24576da8 --- /dev/null +++ b/include/linux/mfd/dln2.h @@ -0,0 +1,103 @@ +#ifndef __LINUX_USB_DLN2_H +#define __LINUX_USB_DLN2_H + +#define DLN2_CMD(cmd, id) ((cmd) | ((id) << 8)) + +struct dln2_platform_data { + u16 handle; /* sub-driver handle (internally used only) */ + u8 port; /* I2C/SPI port */ +}; + +/** + * dln2_event_cb_t - event callback function signature + * + * @pdev - the sub-device that registered this callback + * @echo - the echo header field received in the message + * @data - the data payload + * @len - the data payload length + * + * The callback function is called in interrupt context and the data payload is + * only valid during the call. If the user needs later access of the data, it + * must copy it. + */ + +typedef void (*dln2_event_cb_t)(struct platform_device *pdev, u16 echo, + const void *data, int len); + +/** + * dl2n_register_event_cb - register a callback function for an event + * + * @pdev - the sub-device that registers the callback + * @event - the event for which to register a callback + * @event_cb - the callback function + * + * @return 0 in case of success, negative value in case of error + */ +int dln2_register_event_cb(struct platform_device *pdev, u16 event, + dln2_event_cb_t event_cb); + +/** + * dln2_unregister_event_cb - unregister the callback function for an event + * + * @pdev - the sub-device that registered the callback + * @event - the event for which to register a callback + */ +void dln2_unregister_event_cb(struct platform_device *pdev, u16 event); + +/** + * dln2_transfer - issue a DLN2 command and wait for a response and the + * associated data + * + * @pdev - the sub-device which is issuing this transfer + * @cmd - the command to be sent to the device + * @obuf - the buffer to be sent to the device; it can be NULL if the user + * doesn't need to transmit data with this command + * @obuf_len - the size of the buffer to be sent to the device + * @ibuf - any data associated with the response will be copied here; it can be + * NULL if the user doesn't need the response data + * @ibuf_len - must be initialized to the input buffer size; it will be modified + * to indicate the actual data transferred; + * + * @return 0 for success, negative value for errors + */ +int dln2_transfer(struct platform_device *pdev, u16 cmd, + const void *obuf, unsigned obuf_len, + void *ibuf, unsigned *ibuf_len); + +/** + * dln2_transfer_rx - variant of @dln2_transfer() where TX buffer is not needed + * + * @pdev - the sub-device which is issuing this transfer + * @cmd - the command to be sent to the device + * @ibuf - any data associated with the response will be copied here; it can be + * NULL if the user doesn't need the response data + * @ibuf_len - must be initialized to the input buffer size; it will be modified + * to indicate the actual data transferred; + * + * @return 0 for success, negative value for errors + */ + +static inline int dln2_transfer_rx(struct platform_device *pdev, u16 cmd, + void *ibuf, unsigned *ibuf_len) +{ + return dln2_transfer(pdev, cmd, NULL, 0, ibuf, ibuf_len); +} + +/** + * dln2_transfer_tx - variant of @dln2_transfer() where RX buffer is not needed + * + * @pdev - the sub-device which is issuing this transfer + * @cmd - the command to be sent to the device + * @obuf - the buffer to be sent to the device; it can be NULL if the + * user doesn't need to transmit data with this command + * @obuf_len - the size of the buffer to be sent to the device + * + * @return 0 for success, negative value for errors + */ +static inline int dln2_transfer_tx(struct platform_device *pdev, u16 cmd, + const void *obuf, unsigned obuf_len) +{ + return dln2_transfer(pdev, cmd, obuf, obuf_len, NULL, NULL); +} + +#endif diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 582e67f34054..08dae01258b9 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -26,7 +26,6 @@ #include <linux/i2c.h> -#define MAX77693_NUM_IRQ_MUIC_REGS 3 #define MAX77693_REG_INVALID (0xff) /* Slave addr = 0xCC: PMIC, Charger, Flash LED */ diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 74346d5e7899..0c12628e91c6 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -558,6 +558,7 @@ #define SD_SAMPLE_POINT_CTL 0xFDA7 #define SD_PUSH_POINT_CTL 0xFDA8 #define SD_CMD0 0xFDA9 +#define SD_CMD_START 0x40 #define SD_CMD1 0xFDAA #define SD_CMD2 0xFDAB #define SD_CMD3 0xFDAC @@ -707,6 +708,14 @@ #define PM_CTRL1 0xFF44 #define PM_CTRL2 0xFF45 #define PM_CTRL3 0xFF46 +#define SDIO_SEND_PME_EN 0x80 +#define FORCE_RC_MODE_ON 0x40 +#define FORCE_RX50_LINK_ON 0x20 +#define D3_DELINK_MODE_EN 0x10 +#define USE_PESRTB_CTL_DELINK 0x08 +#define DELAY_PIN_WAKE 0x04 +#define RESET_PIN_WAKE 0x02 +#define PM_WAKE_EN 0x01 #define PM_CTRL4 0xFF47 /* Memory mapping */ @@ -752,6 +761,14 @@ #define PHY_DUM_REG 0x1F #define LCTLR 0x80 +#define LCTLR_EXT_SYNC 0x80 +#define LCTLR_COMMON_CLOCK_CFG 0x40 +#define LCTLR_RETRAIN_LINK 0x20 +#define LCTLR_LINK_DISABLE 0x10 +#define LCTLR_RCB 0x08 +#define LCTLR_RESERVED 0x04 +#define LCTLR_ASPM_CTL_MASK 0x03 + #define PCR_SETTING_REG1 0x724 #define PCR_SETTING_REG2 0x814 #define PCR_SETTING_REG3 0x747 @@ -967,4 +984,24 @@ static inline u8 *rtsx_pci_get_cmd_data(struct rtsx_pcr *pcr) return (u8 *)(pcr->host_cmds_ptr); } +static inline int rtsx_pci_update_cfg_byte(struct rtsx_pcr *pcr, int addr, + u8 mask, u8 append) +{ + int err; + u8 val; + + err = pci_read_config_byte(pcr->pci, addr, &val); + if (err < 0) + return err; + return pci_write_config_byte(pcr->pci, addr, (val & mask) | append); +} + +static inline void rtsx_pci_write_be32(struct rtsx_pcr *pcr, u16 reg, u32 val) +{ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg, 0xFF, val >> 24); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 1, 0xFF, val >> 16); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 2, 0xFF, val >> 8); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 3, 0xFF, val); +} + #endif diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 1825edacbda7..3fdb7cfbffb3 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -28,6 +28,7 @@ #define MIN_800_MV 800000 #define MIN_750_MV 750000 #define MIN_600_MV 600000 +#define MIN_500_MV 500000 /* Macros to represent steps for LDO/BUCK */ #define STEP_50_MV 50000 @@ -41,6 +42,7 @@ enum sec_device_type { S5M8767X, S2MPA01, S2MPS11X, + S2MPS13X, S2MPS14X, S2MPU02, }; diff --git a/include/linux/mfd/samsung/s2mps13.h b/include/linux/mfd/samsung/s2mps13.h new file mode 100644 index 000000000000..ce5dda8958fe --- /dev/null +++ b/include/linux/mfd/samsung/s2mps13.h @@ -0,0 +1,186 @@ +/* + * s2mps13.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * http://www.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. + * + */ + +#ifndef __LINUX_MFD_S2MPS13_H +#define __LINUX_MFD_S2MPS13_H + +/* S2MPS13 registers */ +enum s2mps13_reg { + S2MPS13_REG_ID, + S2MPS13_REG_INT1, + S2MPS13_REG_INT2, + S2MPS13_REG_INT3, + S2MPS13_REG_INT1M, + S2MPS13_REG_INT2M, + S2MPS13_REG_INT3M, + S2MPS13_REG_ST1, + S2MPS13_REG_ST2, + S2MPS13_REG_PWRONSRC, + S2MPS13_REG_OFFSRC, + S2MPS13_REG_BU_CHG, + S2MPS13_REG_RTCCTRL, + S2MPS13_REG_CTRL1, + S2MPS13_REG_CTRL2, + S2MPS13_REG_RSVD1, + S2MPS13_REG_RSVD2, + S2MPS13_REG_RSVD3, + S2MPS13_REG_RSVD4, + S2MPS13_REG_RSVD5, + S2MPS13_REG_RSVD6, + S2MPS13_REG_CTRL3, + S2MPS13_REG_RSVD7, + S2MPS13_REG_RSVD8, + S2MPS13_REG_WRSTBI, + S2MPS13_REG_B1CTRL, + S2MPS13_REG_B1OUT, + S2MPS13_REG_B2CTRL, + S2MPS13_REG_B2OUT, + S2MPS13_REG_B3CTRL, + S2MPS13_REG_B3OUT, + S2MPS13_REG_B4CTRL, + S2MPS13_REG_B4OUT, + S2MPS13_REG_B5CTRL, + S2MPS13_REG_B5OUT, + S2MPS13_REG_B6CTRL, + S2MPS13_REG_B6OUT, + S2MPS13_REG_B7CTRL, + S2MPS13_REG_B7OUT, + S2MPS13_REG_B8CTRL, + S2MPS13_REG_B8OUT, + S2MPS13_REG_B9CTRL, + S2MPS13_REG_B9OUT, + S2MPS13_REG_B10CTRL, + S2MPS13_REG_B10OUT, + S2MPS13_REG_BB1CTRL, + S2MPS13_REG_BB1OUT, + S2MPS13_REG_BUCK_RAMP1, + S2MPS13_REG_BUCK_RAMP2, + S2MPS13_REG_LDO_DVS1, + S2MPS13_REG_LDO_DVS2, + S2MPS13_REG_LDO_DVS3, + S2MPS13_REG_B6OUT2, + S2MPS13_REG_L1CTRL, + S2MPS13_REG_L2CTRL, + S2MPS13_REG_L3CTRL, + S2MPS13_REG_L4CTRL, + S2MPS13_REG_L5CTRL, + S2MPS13_REG_L6CTRL, + S2MPS13_REG_L7CTRL, + S2MPS13_REG_L8CTRL, + S2MPS13_REG_L9CTRL, + S2MPS13_REG_L10CTRL, + S2MPS13_REG_L11CTRL, + S2MPS13_REG_L12CTRL, + S2MPS13_REG_L13CTRL, + S2MPS13_REG_L14CTRL, + S2MPS13_REG_L15CTRL, + S2MPS13_REG_L16CTRL, + S2MPS13_REG_L17CTRL, + S2MPS13_REG_L18CTRL, + S2MPS13_REG_L19CTRL, + S2MPS13_REG_L20CTRL, + S2MPS13_REG_L21CTRL, + S2MPS13_REG_L22CTRL, + S2MPS13_REG_L23CTRL, + S2MPS13_REG_L24CTRL, + S2MPS13_REG_L25CTRL, + S2MPS13_REG_L26CTRL, + S2MPS13_REG_L27CTRL, + S2MPS13_REG_L28CTRL, + S2MPS13_REG_L30CTRL, + S2MPS13_REG_L31CTRL, + S2MPS13_REG_L32CTRL, + S2MPS13_REG_L33CTRL, + S2MPS13_REG_L34CTRL, + S2MPS13_REG_L35CTRL, + S2MPS13_REG_L36CTRL, + S2MPS13_REG_L37CTRL, + S2MPS13_REG_L38CTRL, + S2MPS13_REG_L39CTRL, + S2MPS13_REG_L40CTRL, + S2MPS13_REG_LDODSCH1, + S2MPS13_REG_LDODSCH2, + S2MPS13_REG_LDODSCH3, + S2MPS13_REG_LDODSCH4, + S2MPS13_REG_LDODSCH5, +}; + +/* regulator ids */ +enum s2mps13_regulators { + S2MPS13_LDO1, + S2MPS13_LDO2, + S2MPS13_LDO3, + S2MPS13_LDO4, + S2MPS13_LDO5, + S2MPS13_LDO6, + S2MPS13_LDO7, + S2MPS13_LDO8, + S2MPS13_LDO9, + S2MPS13_LDO10, + S2MPS13_LDO11, + S2MPS13_LDO12, + S2MPS13_LDO13, + S2MPS13_LDO14, + S2MPS13_LDO15, + S2MPS13_LDO16, + S2MPS13_LDO17, + S2MPS13_LDO18, + S2MPS13_LDO19, + S2MPS13_LDO20, + S2MPS13_LDO21, + S2MPS13_LDO22, + S2MPS13_LDO23, + S2MPS13_LDO24, + S2MPS13_LDO25, + S2MPS13_LDO26, + S2MPS13_LDO27, + S2MPS13_LDO28, + S2MPS13_LDO29, + S2MPS13_LDO30, + S2MPS13_LDO31, + S2MPS13_LDO32, + S2MPS13_LDO33, + S2MPS13_LDO34, + S2MPS13_LDO35, + S2MPS13_LDO36, + S2MPS13_LDO37, + S2MPS13_LDO38, + S2MPS13_LDO39, + S2MPS13_LDO40, + S2MPS13_BUCK1, + S2MPS13_BUCK2, + S2MPS13_BUCK3, + S2MPS13_BUCK4, + S2MPS13_BUCK5, + S2MPS13_BUCK6, + S2MPS13_BUCK7, + S2MPS13_BUCK8, + S2MPS13_BUCK9, + S2MPS13_BUCK10, + + S2MPS13_REGULATOR_MAX, +}; + +/* + * Default ramp delay in uv/us. Datasheet says that ramp delay can be + * controlled however it does not specify which register is used for that. + * Let's assume that default value will be set. + */ +#define S2MPS13_BUCK_RAMP_DELAY 12500 + +#endif /* __LINUX_MFD_S2MPS13_H */ diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index e6088c2e2092..e1c12d84c26a 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -164,13 +164,10 @@ struct tc3589x_keypad_platform_data { /** * struct tc3589x_gpio_platform_data - TC3589x GPIO platform data - * @gpio_base: first gpio number assigned to TC3589x. A maximum of - * %TC3589x_NR_GPIOS GPIOs will be allocated. * @setup: callback for board-specific initialization * @remove: callback for board-specific teardown */ struct tc3589x_gpio_platform_data { - int gpio_base; void (*setup)(struct tc3589x *tc3589x, unsigned gpio_base); void (*remove)(struct tc3589x *tc3589x, unsigned gpio_base); }; @@ -178,18 +175,13 @@ struct tc3589x_gpio_platform_data { /** * struct tc3589x_platform_data - TC3589x platform data * @block: bitmask of blocks to enable (use TC3589x_BLOCK_*) - * @irq_base: base IRQ number. %TC3589x_NR_IRQS irqs will be used. * @gpio: GPIO-specific platform data * @keypad: keypad-specific platform data */ struct tc3589x_platform_data { unsigned int block; - int irq_base; struct tc3589x_gpio_platform_data *gpio; const struct tc3589x_keypad_platform_data *keypad; }; -#define TC3589x_NR_GPIOS 24 -#define TC3589x_NR_IRQS TC3589x_INT_GPIO(TC3589x_NR_GPIOS) - #endif diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index b0692d28f8e6..4d69c00497bd 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -88,6 +88,9 @@ struct mmc_ext_csd { unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ bool boot_ro_lockable; + bool ffu_capable; /* Firmware upgrade support */ +#define MMC_FIRMWARE_LEN 8 + u8 fwrev[MMC_FIRMWARE_LEN]; /* FW version */ u8 raw_exception_status; /* 54 */ u8 raw_partition_support; /* 160 */ u8 raw_rpmb_size_mult; /* 168 */ @@ -509,24 +512,8 @@ static inline int mmc_card_broken_irq_polling(const struct mmc_card *c) #define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev) -#define mmc_list_to_card(l) container_of(l, struct mmc_card, node) -#define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev) -#define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d) - -/* - * MMC device driver (e.g., Flash card, I/O card...) - */ -struct mmc_driver { - struct device_driver drv; - int (*probe)(struct mmc_card *); - void (*remove)(struct mmc_card *); - int (*suspend)(struct mmc_card *); - int (*resume)(struct mmc_card *); - void (*shutdown)(struct mmc_card *); -}; - -extern int mmc_register_driver(struct mmc_driver *); -extern void mmc_unregister_driver(struct mmc_driver *); +extern int mmc_register_driver(struct device_driver *); +extern void mmc_unregister_driver(struct device_driver *); extern void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index f206e29f94d7..cb2b0400d284 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -154,7 +154,8 @@ extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, bool, bool); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); -extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); +extern int mmc_send_tuning(struct mmc_host *host); +extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 001366927cf4..42b724e8d503 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -54,6 +54,7 @@ struct mmc_data; * transfer is in progress. * @use_dma: Whether DMA channel is initialized or not. * @using_dma: Whether DMA is in use for the current transfer. + * @dma_64bit_address: Whether DMA supports 64-bit address mode or not. * @sg_dma: Bus address of DMA buffer. * @sg_cpu: Virtual address of DMA buffer. * @dma_ops: Pointer to platform-specific DMA callbacks. @@ -96,6 +97,7 @@ struct mmc_data; * @quirks: Set of quirks that apply to specific versions of the IP. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. + * @sdio_id0: Number of slot0 in the SDIO interrupt registers. * * Locking * ======= @@ -135,11 +137,11 @@ struct dw_mci { struct mmc_command stop_abort; unsigned int prev_blksz; unsigned char timing; - struct workqueue_struct *card_workqueue; /* DMA interface members*/ int use_dma; int using_dma; + int dma_64bit_address; dma_addr_t sg_dma; void *sg_cpu; @@ -154,7 +156,6 @@ struct dw_mci { u32 stop_cmdr; u32 dir_status; struct tasklet_struct tasklet; - struct work_struct card_work; unsigned long pending_events; unsigned long completed_events; enum dw_mci_state state; @@ -193,6 +194,8 @@ struct dw_mci { bool vqmmc_enabled; unsigned long irq_flags; /* IRQ flags */ int irq; + + int sdio_id0; }; /* DMA ops for Internal/External DMAC interface */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index df0c15396bbf..9f322706f7cb 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -289,6 +289,7 @@ struct mmc_host { #define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ MMC_CAP2_HS400_1_2V) +#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V) #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) mmc_pm_flag_t pm_caps; /* supported pm features */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 1cd00b3a75b9..49ad7a943638 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -296,6 +296,7 @@ struct _mmc_csd { #define EXT_CSD_SANITIZE_START 165 /* W */ #define EXT_CSD_WR_REL_PARAM 166 /* RO */ #define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_FW_CONFIG 169 /* R/W */ #define EXT_CSD_BOOT_WP 173 /* R/W */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ #define EXT_CSD_PART_CONFIG 179 /* R/W */ @@ -332,6 +333,8 @@ struct _mmc_csd { #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ #define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */ +#define EXT_CSD_SUPPORTED_MODE 493 /* RO */ #define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ #define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ #define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index dba793e3a331..375af80bde7d 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -100,6 +100,12 @@ struct sdhci_host { #define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) /* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */ #define SDHCI_QUIRK2_STOP_WITH_TC (1<<8) +/* Controller does not support 64-bit DMA */ +#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9) +/* need clear transfer mode register before send cmd */ +#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10) +/* Capability register bit-63 indicates HS400 support */ +#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ @@ -130,6 +136,7 @@ struct sdhci_host { #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ #define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */ #define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ +#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ unsigned int version; /* SDHCI spec. version */ @@ -155,12 +162,19 @@ struct sdhci_host { int sg_count; /* Mapped sg entries */ - u8 *adma_desc; /* ADMA descriptor table */ - u8 *align_buffer; /* Bounce buffer */ + void *adma_table; /* ADMA descriptor table */ + void *align_buffer; /* Bounce buffer */ + + size_t adma_table_sz; /* ADMA descriptor table size */ + size_t align_buffer_sz; /* Bounce buffer size */ dma_addr_t adma_addr; /* Mapped ADMA descr. table */ dma_addr_t align_addr; /* Mapped bounce buffer */ + unsigned int desc_sz; /* ADMA descriptor size */ + unsigned int align_sz; /* ADMA alignment */ + unsigned int align_mask; /* ADMA alignment mask */ + struct tasklet_struct finish_tasklet; /* Tasklet structures */ struct timer_list timer; /* Timer for timeouts */ diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 50f0bc952328..aab032a6ae61 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -84,8 +84,6 @@ struct sdio_driver { struct device_driver drv; }; -#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) - /** * SDIO_DEVICE - macro used to describe a specific SDIO device * @vend: the 16 bit manufacturer code diff --git a/include/linux/of.h b/include/linux/of.h index 29f0adc5f3e4..364a15858746 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -922,4 +922,15 @@ static inline int of_changeset_update_property(struct of_changeset *ocs, /* CONFIG_OF_RESOLVE api */ extern int of_resolve_phandles(struct device_node *tree); +/** + * of_system_has_poweroff_source - Tells if poweroff-source is found for device_node + * @np: Pointer to the given device_node + * + * return true if present false otherwise + */ +static inline bool of_system_has_poweroff_source(const struct device_node *np) +{ + return of_property_read_bool(np, "poweroff-source"); +} + #endif /* _LINUX_OF_H */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1fa99a301817..97fb9f69aaed 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -522,6 +522,8 @@ #define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403 #define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d #define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e +#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573 +#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F4 0x1574 #define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600 #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h new file mode 100644 index 000000000000..67bbcf0785f6 --- /dev/null +++ b/include/linux/platform_data/hsmmc-omap.h @@ -0,0 +1,90 @@ +/* + * MMC definitions for OMAP2 + * + * Copyright (C) 2006 Nokia Corporation + * + * 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. + */ + +/* + * struct omap_hsmmc_dev_attr.flags possibilities + * + * OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can + * operate with either 1.8Vdc or 3.0Vdc card voltages; this flag + * should be set if this is the case. See for example Section 22.5.3 + * "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia + * Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R). + * + * OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers + * don't work correctly on some MMC controller instances on some + * OMAP3 SoCs; this flag should be set if this is the case. See + * for example Advisory 2.1.1.128 "MMC: Multiple Block Read + * Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_ + * Revision F (October 2010) (SPRZ278F). + */ +#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) +#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1) +#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2) + +struct omap_hsmmc_dev_attr { + u8 flags; +}; + +struct mmc_card; + +struct omap_hsmmc_platform_data { + /* back-link to device */ + struct device *dev; + + /* set if your board has components or wiring that limits the + * maximum frequency on the MMC bus */ + unsigned int max_freq; + + /* Integrating attributes from the omap_hwmod layer */ + u8 controller_flags; + + /* Register offset deviation */ + u16 reg_offset; + + /* + * 4/8 wires and any additional host capabilities + * need to OR'd all capabilities (ref. linux/mmc/host.h) + */ + u32 caps; /* Used for the MMC driver on 2430 and later */ + u32 pm_caps; /* PM capabilities of the mmc */ + + /* switch pin can be for card detect (default) or card cover */ + unsigned cover:1; + + /* use the internal clock */ + unsigned internal_clock:1; + + /* nonremovable e.g. eMMC */ + unsigned nonremovable:1; + + /* eMMC does not handle power off when not in sleep state */ + unsigned no_regulator_off_init:1; + + /* we can put the features above into this variable */ +#define HSMMC_HAS_PBIAS (1 << 0) +#define HSMMC_HAS_UPDATED_RESET (1 << 1) +#define HSMMC_HAS_HSPE_SUPPORT (1 << 2) + unsigned features; + + int switch_pin; /* gpio (card detect) */ + int gpio_wp; /* gpio (write protect) */ + + int (*set_power)(struct device *dev, int power_on, int vdd); + void (*remux)(struct device *dev, int power_on); + /* Call back before enabling / disabling regulators */ + void (*before_set_reg)(struct device *dev, int power_on, int vdd); + /* Call back after enabling / disabling regulators */ + void (*after_set_reg)(struct device *dev, int power_on, int vdd); + /* if we have special card, init it using this callback */ + void (*init_card)(struct mmc_card *card); + + const char *name; + u32 ocr_mask; +}; diff --git a/arch/arm/mach-at91/include/mach/atmel-mci.h b/include/linux/platform_data/mmc-atmel-mci.h index 3069e4135573..399a2d5a14bd 100644 --- a/arch/arm/mach-at91/include/mach/atmel-mci.h +++ b/include/linux/platform_data/mmc-atmel-mci.h @@ -1,17 +1,22 @@ -#ifndef __MACH_ATMEL_MCI_H -#define __MACH_ATMEL_MCI_H +#ifndef __MMC_ATMEL_MCI_H +#define __MMC_ATMEL_MCI_H #include <linux/platform_data/dma-atmel.h> +#include <linux/platform_data/dma-dw.h> /** * struct mci_dma_data - DMA data for MCI interface */ struct mci_dma_data { - struct at_dma_slave sdata; +#ifdef CONFIG_ARM + struct at_dma_slave sdata; +#else + struct dw_dma_slave sdata; +#endif }; /* accessor macros */ #define slave_data_ptr(s) (&(s)->sdata) #define find_slave_dev(s) ((s)->sdata.dma_dev) -#endif /* __MACH_ATMEL_MCI_H */ +#endif /* __MMC_ATMEL_MCI_H */ diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h index 51e70cf25cbc..5c188f4e9bec 100644 --- a/include/linux/platform_data/mmc-omap.h +++ b/include/linux/platform_data/mmc-omap.h @@ -10,32 +10,8 @@ #define OMAP_MMC_MAX_SLOTS 2 -/* - * struct omap_mmc_dev_attr.flags possibilities - * - * OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can - * operate with either 1.8Vdc or 3.0Vdc card voltages; this flag - * should be set if this is the case. See for example Section 22.5.3 - * "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia - * Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R). - * - * OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers - * don't work correctly on some MMC controller instances on some - * OMAP3 SoCs; this flag should be set if this is the case. See - * for example Advisory 2.1.1.128 "MMC: Multiple Block Read - * Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_ - * Revision F (October 2010) (SPRZ278F). - */ -#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) -#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1) -#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2) - struct mmc_card; -struct omap_mmc_dev_attr { - u8 flags; -}; - struct omap_mmc_platform_data { /* back-link to device */ struct device *dev; @@ -106,9 +82,6 @@ struct omap_mmc_platform_data { unsigned vcc_aux_disable_is_sleep:1; /* we can put the features above into this variable */ -#define HSMMC_HAS_PBIAS (1 << 0) -#define HSMMC_HAS_UPDATED_RESET (1 << 1) -#define HSMMC_HAS_HSPE_SUPPORT (1 << 2) #define MMC_OMAP7XX (1 << 3) #define MMC_OMAP15XX (1 << 4) #define MMC_OMAP16XX (1 << 5) diff --git a/include/linux/platform_data/pxa_sdhci.h b/include/linux/platform_data/pxa_sdhci.h index 27d3156d093a..9e20c2fb4ffd 100644 --- a/include/linux/platform_data/pxa_sdhci.h +++ b/include/linux/platform_data/pxa_sdhci.h @@ -55,9 +55,4 @@ struct sdhci_pxa_platdata { unsigned int quirks2; unsigned int pm_caps; }; - -struct sdhci_pxa { - u8 clk_enable; - u8 power_mode; -}; #endif /* _PXA_SDHCI_H_ */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 4c94f31a8c99..8523f9bb72f2 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -427,7 +427,7 @@ header-y += virtio_net.h header-y += virtio_pci.h header-y += virtio_ring.h header-y += virtio_rng.h -header=y += vm_sockets.h +header-y += vm_sockets.h header-y += vt.h header-y += wait.h header-y += wanrouter.h diff --git a/ipc/sem.c b/ipc/sem.c index 454f6c6020a8..53c3310f41c6 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -507,13 +507,6 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) return retval; } - id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); - if (id < 0) { - ipc_rcu_putref(sma, sem_rcu_free); - return id; - } - ns->used_sems += nsems; - sma->sem_base = (struct sem *) &sma[1]; for (i = 0; i < nsems; i++) { @@ -528,6 +521,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); + + id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); + if (id < 0) { + ipc_rcu_putref(sma, sem_rcu_free); + return id; + } + ns->used_sems += nsems; + sem_unlock(sma, -1); rcu_read_unlock(); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 24beb9bb4c3e..89e7283015a6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2874,10 +2874,14 @@ asmlinkage __visible void __sched schedule_user(void) * or we have been woken up remotely but the IPI has not yet arrived, * we haven't yet exited the RCU idle mode. Do it here manually until * we find a better solution. + * + * NB: There are buggy callers of this function. Ideally we + * should warn if prev_state != IN_USER, but that will trigger + * too frequently to make sense yet. */ - user_exit(); + enum ctx_state prev_state = exception_enter(); schedule(); - user_enter(); + exception_exit(prev_state); } #endif diff --git a/lib/genalloc.c b/lib/genalloc.c index cce4dd68c40d..2e65d206b01c 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -598,6 +598,7 @@ struct gen_pool *devm_gen_pool_create(struct device *dev, int min_alloc_order, return pool; } +EXPORT_SYMBOL(devm_gen_pool_create); /** * dev_get_gen_pool - Obtain the gen_pool (if any) for a device diff --git a/lib/show_mem.c b/lib/show_mem.c index 09225796991a..5e256271b47b 100644 --- a/lib/show_mem.c +++ b/lib/show_mem.c @@ -28,7 +28,7 @@ void show_mem(unsigned int filter) continue; total += zone->present_pages; - reserved = zone->present_pages - zone->managed_pages; + reserved += zone->present_pages - zone->managed_pages; if (is_highmem_idx(zoneid)) highmem += zone->present_pages; diff --git a/mm/frontswap.c b/mm/frontswap.c index c30eec536f03..f2a3571c6e22 100644 --- a/mm/frontswap.c +++ b/mm/frontswap.c @@ -244,8 +244,10 @@ int __frontswap_store(struct page *page) the (older) page from frontswap */ inc_frontswap_failed_stores(); - if (dup) + if (dup) { __frontswap_clear(sis, offset); + frontswap_ops->invalidate_page(type, offset); + } } if (frontswap_writethrough_enabled) /* report failure so swap also writes to swap device */ diff --git a/mm/memory.c b/mm/memory.c index 3e503831e042..d5f2ae9c4a23 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -815,20 +815,20 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm, if (!pte_file(pte)) { swp_entry_t entry = pte_to_swp_entry(pte); - if (swap_duplicate(entry) < 0) - return entry.val; - - /* make sure dst_mm is on swapoff's mmlist. */ - if (unlikely(list_empty(&dst_mm->mmlist))) { - spin_lock(&mmlist_lock); - if (list_empty(&dst_mm->mmlist)) - list_add(&dst_mm->mmlist, - &src_mm->mmlist); - spin_unlock(&mmlist_lock); - } - if (likely(!non_swap_entry(entry))) + if (likely(!non_swap_entry(entry))) { + if (swap_duplicate(entry) < 0) + return entry.val; + + /* make sure dst_mm is on swapoff's mmlist. */ + if (unlikely(list_empty(&dst_mm->mmlist))) { + spin_lock(&mmlist_lock); + if (list_empty(&dst_mm->mmlist)) + list_add(&dst_mm->mmlist, + &src_mm->mmlist); + spin_unlock(&mmlist_lock); + } rss[MM_SWAPENTS]++; - else if (is_migration_entry(entry)) { + } else if (is_migration_entry(entry)) { page = migration_entry_to_page(entry); if (PageAnon(page)) diff --git a/mm/mmap.c b/mm/mmap.c index 87e82b38453c..ae919891a087 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -776,8 +776,11 @@ again: remove_next = 1 + (end > next->vm_end); * shrinking vma had, to cover any anon pages imported. */ if (exporter && exporter->anon_vma && !importer->anon_vma) { - if (anon_vma_clone(importer, exporter)) - return -ENOMEM; + int error; + + error = anon_vma_clone(importer, exporter); + if (error) + return error; importer->anon_vma = exporter->anon_vma; } } @@ -2469,7 +2472,8 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (err) goto out_free_vma; - if (anon_vma_clone(new, vma)) + err = anon_vma_clone(new, vma); + if (err) goto out_free_mpol; if (new->vm_file) diff --git a/mm/rmap.c b/mm/rmap.c index 19886fb2f13a..3e4c7213210c 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -274,6 +274,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) { struct anon_vma_chain *avc; struct anon_vma *anon_vma; + int error; /* Don't bother if the parent process has no anon_vma here. */ if (!pvma->anon_vma) @@ -283,8 +284,9 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) * First, attach the new VMA to the parent VMA's anon_vmas, * so rmap can find non-COWed pages in child processes. */ - if (anon_vma_clone(vma, pvma)) - return -ENOMEM; + error = anon_vma_clone(vma, pvma); + if (error) + return error; /* Then add our own anon_vma. */ anon_vma = anon_vma_alloc(); diff --git a/mm/slab.c b/mm/slab.c index eb2b2ea30130..f34e053ec46e 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3076,7 +3076,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, void *obj; int x; - VM_BUG_ON(nodeid > num_online_nodes()); + VM_BUG_ON(nodeid < 0 || nodeid >= MAX_NUMNODES); n = get_node(cachep, nodeid); BUG_ON(!n); diff --git a/mm/vmpressure.c b/mm/vmpressure.c index d4042e75f7c7..c5afd573d7da 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -165,6 +165,7 @@ static void vmpressure_work_fn(struct work_struct *work) unsigned long scanned; unsigned long reclaimed; + spin_lock(&vmpr->sr_lock); /* * Several contexts might be calling vmpressure(), so it is * possible that the work was rescheduled again before the old @@ -173,11 +174,12 @@ static void vmpressure_work_fn(struct work_struct *work) * here. No need for any locks here since we don't care if * vmpr->reclaimed is in sync. */ - if (!vmpr->scanned) + scanned = vmpr->scanned; + if (!scanned) { + spin_unlock(&vmpr->sr_lock); return; + } - spin_lock(&vmpr->sr_lock); - scanned = vmpr->scanned; reclaimed = vmpr->reclaimed; vmpr->scanned = 0; vmpr->reclaimed = 0; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b9b7dfaf202b..76321ea442c3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1498,6 +1498,7 @@ static int do_setlink(const struct sk_buff *skb, goto errout; } if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + put_net(net); err = -EPERM; goto errout; } diff --git a/security/keys/internal.h b/security/keys/internal.h index b8960c4959a5..200e37867336 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -117,6 +117,7 @@ struct keyring_search_context { #define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */ #define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */ #define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */ +#define KEYRING_SEARCH_SKIP_EXPIRED 0x0020 /* Ignore expired keys (intention to replace) */ int (*iterator)(const void *object, void *iterator_data); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index eff88a5f5d40..4743d71e4aa6 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -26,6 +26,8 @@ #include <asm/uaccess.h> #include "internal.h" +#define KEY_MAX_DESC_SIZE 4096 + static int key_get_type_from_user(char *type, const char __user *_type, unsigned len) @@ -78,7 +80,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, description = NULL; if (_description) { - description = strndup_user(_description, PAGE_SIZE); + description = strndup_user(_description, KEY_MAX_DESC_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; @@ -177,7 +179,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, goto error; /* pull the description into kernel space */ - description = strndup_user(_description, PAGE_SIZE); + description = strndup_user(_description, KEY_MAX_DESC_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; @@ -287,7 +289,7 @@ long keyctl_join_session_keyring(const char __user *_name) /* fetch the name from userspace */ name = NULL; if (_name) { - name = strndup_user(_name, PAGE_SIZE); + name = strndup_user(_name, KEY_MAX_DESC_SIZE); if (IS_ERR(name)) { ret = PTR_ERR(name); goto error; @@ -562,8 +564,9 @@ long keyctl_describe_key(key_serial_t keyid, { struct key *key, *instkey; key_ref_t key_ref; - char *tmpbuf; + char *infobuf; long ret; + int desclen, infolen; key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); if (IS_ERR(key_ref)) { @@ -586,38 +589,31 @@ long keyctl_describe_key(key_serial_t keyid, } okay: - /* calculate how much description we're going to return */ - ret = -ENOMEM; - tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!tmpbuf) - goto error2; - key = key_ref_to_ptr(key_ref); + desclen = strlen(key->description); - ret = snprintf(tmpbuf, PAGE_SIZE - 1, - "%s;%d;%d;%08x;%s", - key->type->name, - from_kuid_munged(current_user_ns(), key->uid), - from_kgid_munged(current_user_ns(), key->gid), - key->perm, - key->description ?: ""); - - /* include a NUL char at the end of the data */ - if (ret > PAGE_SIZE - 1) - ret = PAGE_SIZE - 1; - tmpbuf[ret] = 0; - ret++; + /* calculate how much information we're going to return */ + ret = -ENOMEM; + infobuf = kasprintf(GFP_KERNEL, + "%s;%d;%d;%08x;", + key->type->name, + from_kuid_munged(current_user_ns(), key->uid), + from_kgid_munged(current_user_ns(), key->gid), + key->perm); + if (!infobuf) + goto error2; + infolen = strlen(infobuf); + ret = infolen + desclen + 1; /* consider returning the data */ - if (buffer && buflen > 0) { - if (buflen > ret) - buflen = ret; - - if (copy_to_user(buffer, tmpbuf, buflen) != 0) + if (buffer && buflen >= ret) { + if (copy_to_user(buffer, infobuf, infolen) != 0 || + copy_to_user(buffer + infolen, key->description, + desclen + 1) != 0) ret = -EFAULT; } - kfree(tmpbuf); + kfree(infobuf); error2: key_ref_put(key_ref); error: @@ -649,7 +645,7 @@ long keyctl_keyring_search(key_serial_t ringid, if (ret < 0) goto error; - description = strndup_user(_description, PAGE_SIZE); + description = strndup_user(_description, KEY_MAX_DESC_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 8177010174f7..e72548b5897e 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -546,7 +546,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) } if (key->expiry && ctx->now.tv_sec >= key->expiry) { - ctx->result = ERR_PTR(-EKEYEXPIRED); + if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED)) + ctx->result = ERR_PTR(-EKEYEXPIRED); kleave(" = %d [expire]", ctx->skipped_ret); goto skipped; } @@ -628,6 +629,10 @@ static bool search_nested_keyrings(struct key *keyring, ctx->index_key.type->name, ctx->index_key.description); +#define STATE_CHECKS (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_DO_STATE_CHECK) + BUG_ON((ctx->flags & STATE_CHECKS) == 0 || + (ctx->flags & STATE_CHECKS) == STATE_CHECKS); + if (ctx->index_key.description) ctx->index_key.desc_len = strlen(ctx->index_key.description); @@ -637,7 +642,6 @@ static bool search_nested_keyrings(struct key *keyring, if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE || keyring_compare_object(keyring, &ctx->index_key)) { ctx->skipped_ret = 2; - ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) { case 1: goto found; @@ -649,8 +653,6 @@ static bool search_nested_keyrings(struct key *keyring, } ctx->skipped_ret = 0; - if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK) - ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK; /* Start processing a new keyring */ descend_to_keyring: diff --git a/security/keys/request_key.c b/security/keys/request_key.c index bb4337c7ae1b..0c7aea4dea54 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -516,6 +516,8 @@ struct key *request_key_and_link(struct key_type *type, .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = (KEYRING_SEARCH_DO_STATE_CHECK | + KEYRING_SEARCH_SKIP_EXPIRED), }; struct key *key; key_ref_t key_ref; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 6639e2cb8853..5d672f7580dd 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -249,6 +249,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; struct key *authkey; key_ref_t authkey_ref; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 14f16be3f374..b118a5be18df 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4790,6 +4790,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), |