summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml6
-rw-r--r--Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml3
-rw-r--r--Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml1
-rw-r--r--Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml105
-rw-r--r--Documentation/devicetree/bindings/mmc/fsl-esdhc.txt52
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml16
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-msm.yaml1
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-sprd.txt67
-rw-r--r--Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml112
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/memstick/host/Kconfig10
-rw-r--r--drivers/memstick/host/Makefile1
-rw-r--r--drivers/memstick/host/rtsx_pci_ms.c638
-rw-r--r--drivers/mmc/core/core.c1
-rw-r--r--drivers/mmc/core/pwrseq_emmc.c1
-rw-r--r--drivers/mmc/core/pwrseq_sd8787.c1
-rw-r--r--drivers/mmc/core/pwrseq_simple.c1
-rw-r--r--drivers/mmc/core/sdio_uart.c1
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mmc/host/atmel-mci.c35
-rw-r--r--drivers/mmc/host/au1xmmc.c37
-rw-r--r--drivers/mmc/host/cb710-mmc.c14
-rw-r--r--drivers/mmc/host/cb710-mmc.h3
-rw-r--r--drivers/mmc/host/davinci_mmc.c11
-rw-r--r--drivers/mmc/host/dw_mmc-bluefield.c18
-rw-r--r--drivers/mmc/host/dw_mmc.c30
-rw-r--r--drivers/mmc/host/dw_mmc.h11
-rw-r--r--drivers/mmc/host/mmc_spi.c5
-rw-r--r--drivers/mmc/host/of_mmc_spi.c1
-rw-r--r--drivers/mmc/host/omap.c17
-rw-r--r--drivers/mmc/host/renesas_sdhi.h4
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c3
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c26
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c9
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c2
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c60
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c16
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c1
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c27
-rw-r--r--drivers/mmc/host/sdhci.c31
-rw-r--r--drivers/mmc/host/sdhci.h3
-rw-r--r--drivers/mmc/host/sdhci_am654.c2
-rw-r--r--drivers/mmc/host/tifm_sd.c15
-rw-r--r--drivers/mmc/host/tmio_mmc.h6
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c13
-rw-r--r--drivers/mmc/host/uniphier-sd.c12
-rw-r--r--drivers/mmc/host/via-sdmmc.c25
-rw-r--r--drivers/mmc/host/wbsd.c74
-rw-r--r--drivers/mmc/host/wbsd.h10
-rw-r--r--include/linux/mfd/tmio.h2
50 files changed, 556 insertions, 993 deletions
diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml b/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml
index 162a39dab218..e4ff71f006b8 100644
--- a/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml
+++ b/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml
@@ -23,6 +23,12 @@ properties:
- raspberrypi,4-model-b
- const: brcm,bcm2711
+ - description: BCM2712 based Boards
+ items:
+ - enum:
+ - raspberrypi,5-model-b
+ - const: brcm,bcm2712
+
- description: BCM2835 based Boards
items:
- enum:
diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
index bc403ae9e5d9..57646575a13f 100644
--- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
+++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
@@ -51,6 +51,9 @@ properties:
set when controller's internal DMA engine cannot access the DRAM memory,
like on the G12A dedicated SDIO controller.
+ power-domains:
+ maxItems: 1
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml
index cbd3d6c6c77f..eee6be7a7867 100644
--- a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml
+++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml
@@ -20,6 +20,7 @@ properties:
- const: brcm,sdhci-brcmstb
- items:
- enum:
+ - brcm,bcm2712-sdhci
- brcm,bcm74165b0-sdhci
- brcm,bcm7445-sdhci
- brcm,bcm7425-sdhci
diff --git a/Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml
new file mode 100644
index 000000000000..b86ffb53b18b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/fsl,esdhc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale Enhanced Secure Digital Host Controller (eSDHC)
+
+description:
+ The Enhanced Secure Digital Host Controller provides an interface
+ for MMC, SD, and SDIO types of memory cards.
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - fsl,mpc8536-esdhc
+ - fsl,mpc8378-esdhc
+ - fsl,p2020-esdhc
+ - fsl,p4080-esdhc
+ - fsl,t1040-esdhc
+ - fsl,t4240-esdhc
+ - fsl,ls1012a-esdhc
+ - fsl,ls1028a-esdhc
+ - fsl,ls1088a-esdhc
+ - fsl,ls1043a-esdhc
+ - fsl,ls1046a-esdhc
+ - fsl,ls2080a-esdhc
+ - const: fsl,esdhc
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-frequency:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: specifies eSDHC base clock frequency.
+
+ sdhci,wp-inverted:
+ $ref: /schemas/types.yaml#/definitions/flag
+ deprecated: true
+ description:
+ specifies that eSDHC controller reports
+ inverted write-protect state; New devices should use the generic
+ "wp-inverted" property.
+
+ sdhci,1-bit-only:
+ $ref: /schemas/types.yaml#/definitions/flag
+ deprecated: true
+ description:
+ specifies that a controller can only handle
+ 1-bit data transfers. New devices should use the generic
+ "bus-width = <1>" property.
+
+ sdhci,auto-cmd12:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ specifies that a controller can only handle auto CMD12.
+
+ voltage-ranges:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ items:
+ items:
+ - description: specifies minimum slot voltage (mV).
+ - description: specifies maximum slot voltage (mV).
+ minItems: 1
+ maxItems: 8
+
+ dma-coherent: true
+
+ little-endian:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ If the host controller is little-endian mode, specify
+ this property. The default endian mode is big-endian.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+allOf:
+ - $ref: sdhci-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ mmc@2e000 {
+ compatible = "fsl,mpc8378-esdhc", "fsl,esdhc";
+ reg = <0x2e000 0x1000>;
+ interrupts = <42 0x8>;
+ interrupt-parent = <&ipic>;
+ /* Filled in by U-Boot */
+ clock-frequency = <100000000>;
+ voltage-ranges = <3300 3300>;
+ };
diff --git a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt
deleted file mode 100644
index edb8cadb9541..000000000000
--- a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-* Freescale Enhanced Secure Digital Host Controller (eSDHC)
-
-The Enhanced Secure Digital Host Controller provides an interface
-for MMC, SD, and SDIO types of memory cards.
-
-This file documents differences between the core properties described
-by mmc.txt and the properties used by the sdhci-esdhc driver.
-
-Required properties:
- - compatible : should be "fsl,esdhc", or "fsl,<chip>-esdhc".
- Possible compatibles for PowerPC:
- "fsl,mpc8536-esdhc"
- "fsl,mpc8378-esdhc"
- "fsl,p2020-esdhc"
- "fsl,p4080-esdhc"
- "fsl,t1040-esdhc"
- "fsl,t4240-esdhc"
- Possible compatibles for ARM:
- "fsl,ls1012a-esdhc"
- "fsl,ls1028a-esdhc"
- "fsl,ls1088a-esdhc"
- "fsl,ls1043a-esdhc"
- "fsl,ls1046a-esdhc"
- "fsl,ls2080a-esdhc"
- - clock-frequency : specifies eSDHC base clock frequency.
-
-Optional properties:
- - sdhci,wp-inverted : specifies that eSDHC controller reports
- inverted write-protect state; New devices should use the generic
- "wp-inverted" property.
- - sdhci,1-bit-only : specifies that a controller can only handle
- 1-bit data transfers. New devices should use the generic
- "bus-width = <1>" property.
- - sdhci,auto-cmd12: specifies that a controller can only handle auto
- CMD12.
- - voltage-ranges : two cells are required, first cell specifies minimum
- slot voltage (mV), second cell specifies maximum slot voltage (mV).
- Several ranges could be specified.
- - little-endian : If the host controller is little-endian mode, specify
- this property. The default endian mode is big-endian.
-
-Example:
-
-sdhci@2e000 {
- compatible = "fsl,mpc8378-esdhc", "fsl,esdhc";
- reg = <0x2e000 0x1000>;
- interrupts = <42 0x8>;
- interrupt-parent = <&ipic>;
- /* Filled in by U-Boot */
- clock-frequency = <0>;
- voltage-ranges = <3300 3300>;
-};
diff --git a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
index 36acc40c7d18..6e2cdac6a85d 100644
--- a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
+++ b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
@@ -27,17 +27,19 @@ properties:
maxItems: 1
voltage-ranges:
- $ref: /schemas/types.yaml#/definitions/uint32-array
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
description: |
Two cells are required, first cell specifies minimum slot voltage (mV),
second cell specifies maximum slot voltage (mV).
items:
- - description: |
- value for minimum slot voltage in mV
- default: 3200
- - description: |
- value for maximum slot voltage in mV
- default: 3400
+ items:
+ - description: |
+ value for minimum slot voltage in mV
+ default: 3200
+ - description: |
+ value for maximum slot voltage in mV
+ default: 3400
+ maxItems: 1
gpios:
description: |
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
index c24c537f62b1..11979b026d21 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
@@ -51,6 +51,7 @@ properties:
- qcom,sdm845-sdhci
- qcom,sdx55-sdhci
- qcom,sdx65-sdhci
+ - qcom,sdx75-sdhci
- qcom,sm6115-sdhci
- qcom,sm6125-sdhci
- qcom,sm6350-sdhci
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt b/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt
deleted file mode 100644
index eb7eb1b529f0..000000000000
--- a/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-* Spreadtrum SDHCI controller (sdhci-sprd)
-
-The Secure Digital (SD) Host controller on Spreadtrum SoCs provides an interface
-for MMC, SD and SDIO types of cards.
-
-This file documents differences between the core properties in mmc.txt
-and the properties used by the sdhci-sprd driver.
-
-Required properties:
-- compatible: Should contain "sprd,sdhci-r11".
-- reg: physical base address of the controller and length.
-- interrupts: Interrupts used by the SDHCI controller.
-- clocks: Should contain phandle for the clock feeding the SDHCI controller
-- clock-names: Should contain the following:
- "sdio" - SDIO source clock (required)
- "enable" - gate clock which used for enabling/disabling the device (required)
- "2x_enable" - gate clock controlling the device for some special platforms (optional)
-
-Optional properties:
-- assigned-clocks: the same with "sdio" clock
-- assigned-clock-parents: the default parent of "sdio" clock
-- pinctrl-names: should be "default", "state_uhs"
-- pinctrl-0: should contain default/high speed pin control
-- pinctrl-1: should contain uhs mode pin control
-
-PHY DLL delays are used to delay the data valid window, and align the window
-to sampling clock. PHY DLL delays can be configured by following properties,
-and each property contains 4 cells which are used to configure the clock data
-write line delay value, clock read command line delay value, clock read data
-positive edge delay value and clock read data negative edge delay value.
-Each cell's delay value unit is cycle of the PHY clock.
-
-- sprd,phy-delay-legacy: Delay value for legacy timing.
-- sprd,phy-delay-sd-highspeed: Delay value for SD high-speed timing.
-- sprd,phy-delay-sd-uhs-sdr50: Delay value for SD UHS SDR50 timing.
-- sprd,phy-delay-sd-uhs-sdr104: Delay value for SD UHS SDR50 timing.
-- sprd,phy-delay-mmc-highspeed: Delay value for MMC high-speed timing.
-- sprd,phy-delay-mmc-ddr52: Delay value for MMC DDR52 timing.
-- sprd,phy-delay-mmc-hs200: Delay value for MMC HS200 timing.
-- sprd,phy-delay-mmc-hs400: Delay value for MMC HS400 timing.
-- sprd,phy-delay-mmc-hs400es: Delay value for MMC HS400 enhanced strobe timing.
-
-Examples:
-
-sdio0: sdio@20600000 {
- compatible = "sprd,sdhci-r11";
- reg = <0 0x20600000 0 0x1000>;
- interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
-
- clock-names = "sdio", "enable";
- clocks = <&ap_clk CLK_EMMC_2X>,
- <&apahb_gate CLK_EMMC_EB>;
- assigned-clocks = <&ap_clk CLK_EMMC_2X>;
- assigned-clock-parents = <&rpll CLK_RPLL_390M>;
-
- pinctrl-names = "default", "state_uhs";
- pinctrl-0 = <&sd0_pins_default>;
- pinctrl-1 = <&sd0_pins_uhs>;
-
- sprd,phy-delay-sd-uhs-sdr104 = <0x3f 0x7f 0x2e 0x2e>;
- bus-width = <8>;
- non-removable;
- no-sdio;
- no-sd;
- cap-mmc-hw-reset;
- status = "okay";
-};
diff --git a/Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml b/Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml
new file mode 100644
index 000000000000..b08081bc018b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/sprd,sdhci-r11.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Spreadtrum SDHCI controller
+
+maintainers:
+ - Orson Zhai <orsonzhai@gmail.com>
+ - Baolin Wang <baolin.wang7@gmail.com>
+ - Chunyan Zhang <zhang.lyra@gmail.com>
+
+properties:
+ compatible:
+ const: sprd,sdhci-r11
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ minItems: 2
+ items:
+ - description: SDIO source clock
+ - description: gate clock for enabling/disabling the device
+ - description: gate clock controlling the device for some special platforms (optional)
+
+ clock-names:
+ minItems: 2
+ items:
+ - const: sdio
+ - const: enable
+ - const: 2x_enable
+
+ pinctrl-0:
+ description: default/high speed pin control
+ maxItems: 1
+
+ pinctrl-1:
+ description: UHS mode pin control
+ maxItems: 1
+
+ pinctrl-names:
+ minItems: 1
+ items:
+ - const: default
+ - const: state_uhs
+
+patternProperties:
+ "^sprd,phy-delay-(legacy|mmc-(ddr52|highspeed|hs[24]00|hs400es)|sd-(highspeed|uhs-sdr(50|104)))$":
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: clock data write line delay value
+ - description: clock read command line delay value
+ - description: clock read data positive edge delay value
+ - description: clock read data negative edge delay value
+ description:
+ PHY DLL delays are used to delay the data valid window, and align
+ the window to the sampling clock. Each cell's delay value unit is
+ cycle of the PHY clock.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+
+allOf:
+ - $ref: sdhci-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/sprd,sc9860-clk.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ mmc@50430000 {
+ compatible = "sprd,sdhci-r11";
+ reg = <0x50430000 0x1000>;
+ interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&aon_prediv CLK_EMMC_2X>,
+ <&apahb_gate CLK_EMMC_EB>,
+ <&aon_gate CLK_EMMC_2X_EN>;
+ clock-names = "sdio", "enable", "2x_enable";
+
+ pinctrl-0 = <&sd0_pins_default>;
+ pinctrl-1 = <&sd0_pins_uhs>;
+ pinctrl-names = "default", "state_uhs";
+
+ bus-width = <8>;
+ cap-mmc-hw-reset;
+ mmc-hs400-enhanced-strobe;
+ mmc-hs400-1_8v;
+ mmc-hs200-1_8v;
+ mmc-ddr-1_8v;
+ non-removable;
+ no-sdio;
+ no-sd;
+
+ sprd,phy-delay-mmc-ddr52 = <0x3f 0x75 0x14 0x14>;
+ sprd,phy-delay-mmc-hs200 = <0x0 0x8c 0x8c 0x8c>;
+ sprd,phy-delay-mmc-hs400 = <0x44 0x7f 0x2e 0x2e>;
+ sprd,phy-delay-mmc-hs400es = <0x3f 0x3f 0x2e 0x2e>;
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 65bdae824587..81d05219bfc4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20156,6 +20156,7 @@ SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) NXP i.MX DRIVER
M: Haibo Chen <haibo.chen@nxp.com>
L: imx@lists.linux.dev
L: linux-mmc@vger.kernel.org
+L: s32@nxp.com
S: Maintained
F: drivers/mmc/host/sdhci-esdhc-imx.c
@@ -23702,12 +23703,6 @@ M: Kevin Brace <kevinbrace@bracecomputerlab.com>
S: Maintained
F: drivers/net/ethernet/via/via-rhine.c
-VIA SD/MMC CARD CONTROLLER DRIVER
-M: Bruce Chang <brucechang@via.com.tw>
-M: Harald Welte <HaraldWelte@viatech.com>
-S: Maintained
-F: drivers/mmc/host/via-sdmmc.c
-
VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER
M: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
L: linux-fbdev@vger.kernel.org
diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig
index 4113343da056..fcd2c2cc3cb4 100644
--- a/drivers/memstick/host/Kconfig
+++ b/drivers/memstick/host/Kconfig
@@ -44,16 +44,6 @@ config MEMSTICK_R592
To compile this driver as a module, choose M here: the module will
be called r592.
-config MEMSTICK_REALTEK_PCI
- tristate "Realtek PCI-E Memstick Card Interface Driver"
- depends on MISC_RTSX_PCI
- help
- Say Y here to include driver code to support Memstick card interface
- of Realtek PCI-E card reader
-
- To compile this driver as a module, choose M here: the module will
- be called rtsx_pci_ms.
-
config MEMSTICK_REALTEK_USB
tristate "Realtek USB Memstick Card Interface Driver"
depends on MISC_RTSX_USB
diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile
index 1abaa03ee68c..0c90df33165d 100644
--- a/drivers/memstick/host/Makefile
+++ b/drivers/memstick/host/Makefile
@@ -6,5 +6,4 @@
obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o
obj-$(CONFIG_MEMSTICK_R592) += r592.o
-obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o
obj-$(CONFIG_MEMSTICK_REALTEK_USB) += rtsx_usb_ms.o
diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c
deleted file mode 100644
index 980a54513e6c..000000000000
--- a/drivers/memstick/host/rtsx_pci_ms.c
+++ /dev/null
@@ -1,638 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Realtek PCI-Express Memstick Card Interface driver
- *
- * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
- *
- * Author:
- * Wei WANG <wei_wang@realsil.com.cn>
- */
-
-#include <linux/module.h>
-#include <linux/highmem.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/memstick.h>
-#include <linux/rtsx_pci.h>
-#include <asm/unaligned.h>
-
-struct realtek_pci_ms {
- struct platform_device *pdev;
- struct rtsx_pcr *pcr;
- struct memstick_host *msh;
- struct memstick_request *req;
-
- struct mutex host_mutex;
- struct work_struct handle_req;
-
- u8 ssc_depth;
- unsigned int clock;
- unsigned char ifmode;
- bool eject;
-};
-
-static inline struct device *ms_dev(struct realtek_pci_ms *host)
-{
- return &(host->pdev->dev);
-}
-
-static inline void ms_clear_error(struct realtek_pci_ms *host)
-{
- rtsx_pci_write_register(host->pcr, CARD_STOP,
- MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR);
-}
-
-#ifdef DEBUG
-
-static void ms_print_debug_regs(struct realtek_pci_ms *host)
-{
- struct rtsx_pcr *pcr = host->pcr;
- u16 i;
- u8 *ptr;
-
- /* Print MS host internal registers */
- rtsx_pci_init_cmd(pcr);
- for (i = 0xFD40; i <= 0xFD44; i++)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
- for (i = 0xFD52; i <= 0xFD69; i++)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
- rtsx_pci_send_cmd(pcr, 100);
-
- ptr = rtsx_pci_get_cmd_data(pcr);
- for (i = 0xFD40; i <= 0xFD44; i++)
- dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
- for (i = 0xFD52; i <= 0xFD69; i++)
- dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
-}
-
-#else
-
-#define ms_print_debug_regs(host)
-
-#endif
-
-static int ms_power_on(struct realtek_pci_ms *host)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err;
-
- rtsx_pci_init_cmd(pcr);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
- CARD_SHARE_MASK, CARD_SHARE_48_MS);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN,
- MS_CLK_EN, MS_CLK_EN);
- err = rtsx_pci_send_cmd(pcr, 100);
- if (err < 0)
- return err;
-
- err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD);
- if (err < 0)
- return err;
-
- err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD);
- if (err < 0)
- return err;
-
- /* Wait ms power stable */
- msleep(150);
-
- err = rtsx_pci_write_register(pcr, CARD_OE,
- MS_OUTPUT_EN, MS_OUTPUT_EN);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int ms_power_off(struct realtek_pci_ms *host)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err;
-
- rtsx_pci_init_cmd(pcr);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
-
- err = rtsx_pci_send_cmd(pcr, 100);
- if (err < 0)
- return err;
-
- err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD);
- if (err < 0)
- return err;
-
- return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD);
-}
-
-static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir,
- u8 tpc, u8 cfg, struct scatterlist *sg)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err;
- unsigned int length = sg->length;
- u16 sec_cnt = (u16)(length / 512);
- u8 val, trans_mode, dma_dir;
- struct memstick_dev *card = host->msh->card;
- bool pro_card = card->id.type == MEMSTICK_TYPE_PRO;
-
- dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n",
- __func__, tpc, (data_dir == READ) ? "READ" : "WRITE",
- length);
-
- if (data_dir == READ) {
- dma_dir = DMA_DIR_FROM_CARD;
- trans_mode = pro_card ? MS_TM_AUTO_READ : MS_TM_NORMAL_READ;
- } else {
- dma_dir = DMA_DIR_TO_CARD;
- trans_mode = pro_card ? MS_TM_AUTO_WRITE : MS_TM_NORMAL_WRITE;
- }
-
- rtsx_pci_init_cmd(pcr);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
- if (pro_card) {
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H,
- 0xFF, (u8)(sec_cnt >> 8));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L,
- 0xFF, (u8)sec_cnt);
- }
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
- DMA_DONE_INT, DMA_DONE_INT);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
- 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
- 0x01, RING_BUFFER);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
- 0xFF, MS_TRANSFER_START | trans_mode);
- rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
- MS_TRANSFER_END, MS_TRANSFER_END);
-
- rtsx_pci_send_cmd_no_wait(pcr);
-
- err = rtsx_pci_transfer_data(pcr, sg, 1, data_dir == READ, 10000);
- if (err < 0) {
- ms_clear_error(host);
- return err;
- }
-
- rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
- if (pro_card) {
- if (val & (MS_INT_CMDNK | MS_INT_ERR |
- MS_CRC16_ERR | MS_RDY_TIMEOUT))
- return -EIO;
- } else {
- if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT))
- return -EIO;
- }
-
- return 0;
-}
-
-static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc,
- u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err, i;
-
- dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
-
- if (!data)
- return -EINVAL;
-
- rtsx_pci_init_cmd(pcr);
-
- for (i = 0; i < cnt; i++)
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
- PPBUF_BASE2 + i, 0xFF, data[i]);
- if (cnt % 2)
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
- PPBUF_BASE2 + i, 0xFF, 0xFF);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
- 0x01, PINGPONG_BUFFER);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
- 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
- rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
- MS_TRANSFER_END, MS_TRANSFER_END);
- if (int_reg)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
-
- err = rtsx_pci_send_cmd(pcr, 5000);
- if (err < 0) {
- u8 val;
-
- rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
- dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
-
- if (int_reg)
- *int_reg = val & 0x0F;
-
- ms_print_debug_regs(host);
-
- ms_clear_error(host);
-
- if (!(tpc & 0x08)) {
- if (val & MS_CRC16_ERR)
- return -EIO;
- } else {
- if (!(val & 0x80)) {
- if (val & (MS_INT_ERR | MS_INT_CMDNK))
- return -EIO;
- }
- }
-
- return -ETIMEDOUT;
- }
-
- if (int_reg) {
- u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1;
- *int_reg = *ptr & 0x0F;
- }
-
- return 0;
-}
-
-static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc,
- u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err, i;
- u8 *ptr;
-
- dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
-
- if (!data)
- return -EINVAL;
-
- rtsx_pci_init_cmd(pcr);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
- 0x01, PINGPONG_BUFFER);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
- 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
- rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
- MS_TRANSFER_END, MS_TRANSFER_END);
- for (i = 0; i < cnt - 1; i++)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
- if (cnt % 2)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0);
- else
- rtsx_pci_add_cmd(pcr, READ_REG_CMD,
- PPBUF_BASE2 + cnt - 1, 0, 0);
- if (int_reg)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
-
- err = rtsx_pci_send_cmd(pcr, 5000);
- if (err < 0) {
- u8 val;
-
- rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
- dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
-
- if (int_reg)
- *int_reg = val & 0x0F;
-
- ms_print_debug_regs(host);
-
- ms_clear_error(host);
-
- if (!(tpc & 0x08)) {
- if (val & MS_CRC16_ERR)
- return -EIO;
- } else {
- if (!(val & 0x80)) {
- if (val & (MS_INT_ERR | MS_INT_CMDNK))
- return -EIO;
- }
- }
-
- return -ETIMEDOUT;
- }
-
- ptr = rtsx_pci_get_cmd_data(pcr) + 1;
- for (i = 0; i < cnt; i++)
- data[i] = *ptr++;
-
- if (int_reg)
- *int_reg = *ptr & 0x0F;
-
- return 0;
-}
-
-static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host)
-{
- struct memstick_request *req = host->req;
- int err = 0;
- u8 cfg = 0, int_reg;
-
- dev_dbg(ms_dev(host), "%s\n", __func__);
-
- if (req->need_card_int) {
- if (host->ifmode != MEMSTICK_SERIAL)
- cfg = WAIT_INT;
- }
-
- if (req->long_data) {
- err = ms_transfer_data(host, req->data_dir,
- req->tpc, cfg, &(req->sg));
- } else {
- if (req->data_dir == READ) {
- err = ms_read_bytes(host, req->tpc, cfg,
- req->data_len, req->data, &int_reg);
- } else {
- err = ms_write_bytes(host, req->tpc, cfg,
- req->data_len, req->data, &int_reg);
- }
- }
- if (err < 0)
- return err;
-
- if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) {
- err = ms_read_bytes(host, MS_TPC_GET_INT,
- NO_WAIT_INT, 1, &int_reg, NULL);
- if (err < 0)
- return err;
- }
-
- if (req->need_card_int) {
- dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", int_reg);
-
- if (int_reg & MS_INT_CMDNK)
- req->int_reg |= MEMSTICK_INT_CMDNAK;
- if (int_reg & MS_INT_BREQ)
- req->int_reg |= MEMSTICK_INT_BREQ;
- if (int_reg & MS_INT_ERR)
- req->int_reg |= MEMSTICK_INT_ERR;
- if (int_reg & MS_INT_CED)
- req->int_reg |= MEMSTICK_INT_CED;
- }
-
- return 0;
-}
-
-static void rtsx_pci_ms_handle_req(struct work_struct *work)
-{
- struct realtek_pci_ms *host = container_of(work,
- struct realtek_pci_ms, handle_req);
- struct rtsx_pcr *pcr = host->pcr;
- struct memstick_host *msh = host->msh;
- int rc;
-
- mutex_lock(&pcr->pcr_mutex);
-
- rtsx_pci_start_run(pcr);
-
- rtsx_pci_switch_clock(host->pcr, host->clock, host->ssc_depth,
- false, true, false);
- rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, MS_MOD_SEL);
- rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
- CARD_SHARE_MASK, CARD_SHARE_48_MS);
-
- if (!host->req) {
- do {
- rc = memstick_next_req(msh, &host->req);
- dev_dbg(ms_dev(host), "next req %d\n", rc);
-
- if (!rc)
- host->req->error = rtsx_pci_ms_issue_cmd(host);
- } while (!rc);
- }
-
- mutex_unlock(&pcr->pcr_mutex);
-}
-
-static void rtsx_pci_ms_request(struct memstick_host *msh)
-{
- struct realtek_pci_ms *host = memstick_priv(msh);
-
- dev_dbg(ms_dev(host), "--> %s\n", __func__);
-
- if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD))
- return;
-
- schedule_work(&host->handle_req);
-}
-
-static int rtsx_pci_ms_set_param(struct memstick_host *msh,
- enum memstick_param param, int value)
-{
- struct realtek_pci_ms *host = memstick_priv(msh);
- struct rtsx_pcr *pcr = host->pcr;
- unsigned int clock = 0;
- u8 ssc_depth = 0;
- int err;
-
- dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
- __func__, param, value);
-
- err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD);
- if (err)
- return err;
-
- switch (param) {
- case MEMSTICK_POWER:
- if (value == MEMSTICK_POWER_ON)
- err = ms_power_on(host);
- else if (value == MEMSTICK_POWER_OFF)
- err = ms_power_off(host);
- else
- return -EINVAL;
- break;
-
- case MEMSTICK_INTERFACE:
- if (value == MEMSTICK_SERIAL) {
- clock = 19000000;
- ssc_depth = RTSX_SSC_DEPTH_500K;
-
- err = rtsx_pci_write_register(pcr, MS_CFG, 0x58,
- MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT);
- if (err < 0)
- return err;
- } else if (value == MEMSTICK_PAR4) {
- clock = 39000000;
- ssc_depth = RTSX_SSC_DEPTH_1M;
-
- err = rtsx_pci_write_register(pcr, MS_CFG,
- 0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD);
- if (err < 0)
- return err;
- } else {
- return -EINVAL;
- }
-
- err = rtsx_pci_switch_clock(pcr, clock,
- ssc_depth, false, true, false);
- if (err < 0)
- return err;
-
- host->ssc_depth = ssc_depth;
- host->clock = clock;
- host->ifmode = value;
- break;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct realtek_pci_ms *host = platform_get_drvdata(pdev);
- struct memstick_host *msh = host->msh;
-
- dev_dbg(ms_dev(host), "--> %s\n", __func__);
-
- memstick_suspend_host(msh);
- return 0;
-}
-
-static int rtsx_pci_ms_resume(struct platform_device *pdev)
-{
- struct realtek_pci_ms *host = platform_get_drvdata(pdev);
- struct memstick_host *msh = host->msh;
-
- dev_dbg(ms_dev(host), "--> %s\n", __func__);
-
- memstick_resume_host(msh);
- return 0;
-}
-
-#else /* CONFIG_PM */
-
-#define rtsx_pci_ms_suspend NULL
-#define rtsx_pci_ms_resume NULL
-
-#endif /* CONFIG_PM */
-
-static void rtsx_pci_ms_card_event(struct platform_device *pdev)
-{
- struct realtek_pci_ms *host = platform_get_drvdata(pdev);
-
- memstick_detect_change(host->msh);
-}
-
-static int rtsx_pci_ms_drv_probe(struct platform_device *pdev)
-{
- struct memstick_host *msh;
- struct realtek_pci_ms *host;
- struct rtsx_pcr *pcr;
- struct pcr_handle *handle = pdev->dev.platform_data;
- int rc;
-
- if (!handle)
- return -ENXIO;
-
- pcr = handle->pcr;
- if (!pcr)
- return -ENXIO;
-
- dev_dbg(&(pdev->dev),
- ": Realtek PCI-E Memstick controller found\n");
-
- msh = memstick_alloc_host(sizeof(*host), &pdev->dev);
- if (!msh)
- return -ENOMEM;
-
- host = memstick_priv(msh);
- host->pcr = pcr;
- host->msh = msh;
- host->pdev = pdev;
- platform_set_drvdata(pdev, host);
- pcr->slots[RTSX_MS_CARD].p_dev = pdev;
- pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event;
-
- mutex_init(&host->host_mutex);
-
- INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req);
- msh->request = rtsx_pci_ms_request;
- msh->set_param = rtsx_pci_ms_set_param;
- msh->caps = MEMSTICK_CAP_PAR4;
-
- rc = memstick_add_host(msh);
- if (rc) {
- memstick_free_host(msh);
- return rc;
- }
-
- return 0;
-}
-
-static void rtsx_pci_ms_drv_remove(struct platform_device *pdev)
-{
- struct realtek_pci_ms *host = platform_get_drvdata(pdev);
- struct rtsx_pcr *pcr;
- struct memstick_host *msh;
- int rc;
-
- pcr = host->pcr;
- pcr->slots[RTSX_MS_CARD].p_dev = NULL;
- pcr->slots[RTSX_MS_CARD].card_event = NULL;
- msh = host->msh;
- host->eject = true;
- cancel_work_sync(&host->handle_req);
-
- mutex_lock(&host->host_mutex);
- if (host->req) {
- dev_dbg(&(pdev->dev),
- "%s: Controller removed during transfer\n",
- dev_name(&msh->dev));
-
- rtsx_pci_complete_unfinished_transfer(pcr);
-
- host->req->error = -ENOMEDIUM;
- do {
- rc = memstick_next_req(msh, &host->req);
- if (!rc)
- host->req->error = -ENOMEDIUM;
- } while (!rc);
- }
- mutex_unlock(&host->host_mutex);
-
- memstick_remove_host(msh);
- memstick_free_host(msh);
-
- dev_dbg(&(pdev->dev),
- ": Realtek PCI-E Memstick controller has been removed\n");
-}
-
-static struct platform_device_id rtsx_pci_ms_ids[] = {
- {
- .name = DRV_NAME_RTSX_PCI_MS,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids);
-
-static struct platform_driver rtsx_pci_ms_driver = {
- .probe = rtsx_pci_ms_drv_probe,
- .remove_new = rtsx_pci_ms_drv_remove,
- .id_table = rtsx_pci_ms_ids,
- .suspend = rtsx_pci_ms_suspend,
- .resume = rtsx_pci_ms_resume,
- .driver = {
- .name = DRV_NAME_RTSX_PCI_MS,
- },
-};
-module_platform_driver(rtsx_pci_ms_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
-MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver");
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index a8c17b4cd737..d6c819dd68ed 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2362,4 +2362,5 @@ static void __exit mmc_exit(void)
subsys_initcall(mmc_init);
module_exit(mmc_exit);
+MODULE_DESCRIPTION("MMC core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
index 3b6d69cefb4e..96fa4c508900 100644
--- a/drivers/mmc/core/pwrseq_emmc.c
+++ b/drivers/mmc/core/pwrseq_emmc.c
@@ -115,4 +115,5 @@ static struct platform_driver mmc_pwrseq_emmc_driver = {
};
module_platform_driver(mmc_pwrseq_emmc_driver);
+MODULE_DESCRIPTION("Hardware reset support for eMMC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/pwrseq_sd8787.c b/drivers/mmc/core/pwrseq_sd8787.c
index 0c5808fc3206..f24bbd68e251 100644
--- a/drivers/mmc/core/pwrseq_sd8787.c
+++ b/drivers/mmc/core/pwrseq_sd8787.c
@@ -130,4 +130,5 @@ static struct platform_driver mmc_pwrseq_sd8787_driver = {
};
module_platform_driver(mmc_pwrseq_sd8787_driver);
+MODULE_DESCRIPTION("Power sequence support for Marvell SD8787 BT + Wifi chip");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index df9588503ad0..154a8921ae75 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -159,4 +159,5 @@ static struct platform_driver mmc_pwrseq_simple_driver = {
};
module_platform_driver(mmc_pwrseq_simple_driver);
+MODULE_DESCRIPTION("Simple power sequence management for MMC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/sdio_uart.c b/drivers/mmc/core/sdio_uart.c
index 575ebbce378e..6b7471dba3bf 100644
--- a/drivers/mmc/core/sdio_uart.c
+++ b/drivers/mmc/core/sdio_uart.c
@@ -1162,4 +1162,5 @@ module_init(sdio_uart_init);
module_exit(sdio_uart_exit);
MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("SDIO UART/GPS driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index bb0d4fb0892a..eb3ecfe05591 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -1016,7 +1016,7 @@ config MMC_SDHCI_MICROCHIP_PIC32
config MMC_SDHCI_BRCMSTB
tristate "Broadcom SDIO/SD/MMC support"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
+ depends on ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST
depends on MMC_SDHCI_PLTFM
select MMC_CQHCI
default ARCH_BRCMSTB || BMIPS_GENERIC
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 8199d9620075..6490df54a6f5 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -33,6 +33,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/workqueue.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
@@ -272,12 +273,12 @@ struct atmel_mci_dma {
* EVENT_DATA_ERROR is pending.
* @stop_cmdr: Value to be loaded into CMDR when the stop command is
* to be sent.
- * @tasklet: Tasklet running the request state machine.
+ * @bh_work: Work running the request state machine.
* @pending_events: Bitmask of events flagged by the interrupt handler
- * to be processed by the tasklet.
+ * to be processed by the work.
* @completed_events: Bitmask of events which the state machine has
* processed.
- * @state: Tasklet state.
+ * @state: Work state.
* @queue: List of slots waiting for access to the controller.
* @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request.
@@ -352,7 +353,7 @@ struct atmel_mci {
u32 data_status;
u32 stop_cmdr;
- struct tasklet_struct tasklet;
+ struct work_struct bh_work;
unsigned long pending_events;
unsigned long completed_events;
enum atmel_mci_state state;
@@ -735,7 +736,7 @@ static void atmci_timeout_timer(struct timer_list *t)
host->need_reset = 1;
host->state = STATE_END_REQUEST;
smp_wmb();
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
@@ -958,7 +959,7 @@ static void atmci_pdc_complete(struct atmel_mci *host)
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
static void atmci_dma_cleanup(struct atmel_mci *host)
@@ -972,7 +973,7 @@ static void atmci_dma_cleanup(struct atmel_mci *host)
}
/*
- * This function is called by the DMA driver from tasklet context.
+ * This function is called by the DMA driver from bh context.
*/
static void atmci_dma_complete(void *arg)
{
@@ -995,7 +996,7 @@ static void atmci_dma_complete(void *arg)
if (data) {
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
/*
* Regardless of what the documentation says, we have
@@ -1008,7 +1009,7 @@ static void atmci_dma_complete(void *arg)
* haven't seen all the potential error bits yet.
*
* The interrupt handler will schedule a different
- * tasklet to finish things up when the data transfer
+ * bh work to finish things up when the data transfer
* is completely done.
*
* We may not complete the mmc request here anyway
@@ -1745,9 +1746,9 @@ static void atmci_detect_change(struct timer_list *t)
}
}
-static void atmci_tasklet_func(struct tasklet_struct *t)
+static void atmci_work_func(struct work_struct *t)
{
- struct atmel_mci *host = from_tasklet(host, t, tasklet);
+ struct atmel_mci *host = from_work(host, t, bh_work);
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
struct device *dev = host->dev;
@@ -1759,7 +1760,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
state = host->state;
- dev_vdbg(dev, "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
+ dev_vdbg(dev, "bh_work: state %u pending/completed/mask %lx/%lx/%x\n",
state, host->pending_events, host->completed_events,
atmci_readl(host, ATMCI_IMR));
@@ -2118,7 +2119,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
dev_dbg(dev, "set pending data error\n");
smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
if (pending & ATMCI_TXBUFE) {
@@ -2187,7 +2188,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
smp_wmb();
dev_dbg(dev, "set pending notbusy\n");
atmci_set_pending(host, EVENT_NOTBUSY);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
if (pending & ATMCI_NOTBUSY) {
@@ -2196,7 +2197,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
smp_wmb();
dev_dbg(dev, "set pending notbusy\n");
atmci_set_pending(host, EVENT_NOTBUSY);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
if (pending & ATMCI_RXRDY)
@@ -2211,7 +2212,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
smp_wmb();
dev_dbg(dev, "set pending cmd rdy\n");
atmci_set_pending(host, EVENT_CMD_RDY);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
@@ -2487,7 +2488,7 @@ static int atmci_probe(struct platform_device *pdev)
host->mapbase = regs->start;
- tasklet_setup(&host->tasklet, atmci_tasklet_func);
+ INIT_WORK(&host->bh_work, atmci_work_func);
ret = request_irq(irq, atmci_interrupt, 0, dev_name(dev), host);
if (ret) {
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index b5a5c6a2fe8b..6e80bcb668ec 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -42,6 +42,7 @@
#include <linux/leds.h>
#include <linux/mmc/host.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/mach-au1x00/au1000.h>
@@ -113,8 +114,8 @@ struct au1xmmc_host {
int irq;
- struct tasklet_struct finish_task;
- struct tasklet_struct data_task;
+ struct work_struct finish_bh_work;
+ struct work_struct data_bh_work;
struct au1xmmc_platform_data *platdata;
struct platform_device *pdev;
struct resource *ioarea;
@@ -253,9 +254,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)
mmc_request_done(host->mmc, mrq);
}
-static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
+static void au1xmmc_finish_bh_work(struct work_struct *t)
{
- struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
+ struct au1xmmc_host *host = from_work(host, t, finish_bh_work);
au1xmmc_finish_request(host);
}
@@ -363,9 +364,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
au1xmmc_finish_request(host);
}
-static void au1xmmc_tasklet_data(struct tasklet_struct *t)
+static void au1xmmc_data_bh_work(struct work_struct *t)
{
- struct au1xmmc_host *host = from_tasklet(host, t, data_task);
+ struct au1xmmc_host *host = from_work(host, t, data_bh_work);
u32 status = __raw_readl(HOST_STATUS(host));
au1xmmc_data_complete(host, status);
@@ -425,7 +426,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
if (host->flags & HOST_F_STOP)
SEND_STOP(host);
- tasklet_schedule(&host->data_task);
+ queue_work(system_bh_wq, &host->data_bh_work);
}
}
@@ -505,7 +506,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
if (host->flags & HOST_F_STOP)
SEND_STOP(host);
- tasklet_schedule(&host->data_task);
+ queue_work(system_bh_wq, &host->data_bh_work);
}
}
@@ -561,7 +562,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
if (!trans || cmd->error) {
IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
- tasklet_schedule(&host->finish_task);
+ queue_work(system_bh_wq, &host->finish_bh_work);
return;
}
@@ -797,7 +798,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
/* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */
- tasklet_schedule(&host->finish_task);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
#if 0
else if (status & SD_STATUS_DD) {
@@ -806,7 +807,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
au1xmmc_receive_pio(host);
else {
au1xmmc_data_complete(host, status);
- /* tasklet_schedule(&host->data_task); */
+ /* queue_work(system_bh_wq, &host->data_bh_work); */
}
}
#endif
@@ -854,7 +855,7 @@ static void au1xmmc_dbdma_callback(int irq, void *dev_id)
if (host->flags & HOST_F_STOP)
SEND_STOP(host);
- tasklet_schedule(&host->data_task);
+ queue_work(system_bh_wq, &host->data_bh_work);
}
static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
@@ -1039,9 +1040,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
if (host->platdata)
mmc->caps &= ~(host->platdata->mask_host_caps);
- tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
+ INIT_WORK(&host->data_bh_work, au1xmmc_data_bh_work);
- tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
+ INIT_WORK(&host->finish_bh_work, au1xmmc_finish_bh_work);
if (has_dbdma()) {
ret = au1xmmc_dbdma_init(host);
@@ -1091,8 +1092,8 @@ out5:
if (host->flags & HOST_F_DBDMA)
au1xmmc_dbdma_shutdown(host);
- tasklet_kill(&host->data_task);
- tasklet_kill(&host->finish_task);
+ cancel_work_sync(&host->data_bh_work);
+ cancel_work_sync(&host->finish_bh_work);
if (host->platdata && host->platdata->cd_setup &&
!(mmc->caps & MMC_CAP_NEEDS_POLL))
@@ -1135,8 +1136,8 @@ static void au1xmmc_remove(struct platform_device *pdev)
__raw_writel(0, HOST_CONFIG2(host));
wmb(); /* drain writebuffer */
- tasklet_kill(&host->data_task);
- tasklet_kill(&host->finish_task);
+ cancel_work_sync(&host->data_bh_work);
+ cancel_work_sync(&host->finish_bh_work);
if (host->flags & HOST_F_DBDMA)
au1xmmc_dbdma_shutdown(host);
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index 0aec33b88bef..902f7f20abaa 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -493,7 +493,7 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
cb710_mmc_command(mmc, mrq->stop);
- tasklet_schedule(&reader->finish_req_tasklet);
+ queue_work(system_bh_wq, &reader->finish_req_bh_work);
}
static int cb710_mmc_powerup(struct cb710_slot *slot)
@@ -646,10 +646,10 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot)
return 1;
}
-static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
+static void cb710_mmc_finish_request_bh_work(struct work_struct *t)
{
- struct cb710_mmc_reader *reader = from_tasklet(reader, t,
- finish_req_tasklet);
+ struct cb710_mmc_reader *reader = from_work(reader, t,
+ finish_req_bh_work);
struct mmc_request *mrq = reader->mrq;
reader->mrq = NULL;
@@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev)
reader = mmc_priv(mmc);
- tasklet_setup(&reader->finish_req_tasklet,
- cb710_mmc_finish_request_tasklet);
+ INIT_WORK(&reader->finish_req_bh_work,
+ cb710_mmc_finish_request_bh_work);
spin_lock_init(&reader->irq_lock);
cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
@@ -763,7 +763,7 @@ static void cb710_mmc_exit(struct platform_device *pdev)
cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0);
cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0);
- tasklet_kill(&reader->finish_req_tasklet);
+ cancel_work_sync(&reader->finish_req_bh_work);
mmc_free_host(mmc);
}
diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h
index 5e053077dbed..59abaccaad10 100644
--- a/drivers/mmc/host/cb710-mmc.h
+++ b/drivers/mmc/host/cb710-mmc.h
@@ -8,10 +8,11 @@
#define LINUX_CB710_MMC_H
#include <linux/cb710.h>
+#include <linux/workqueue.h>
/* per-MMC-reader structure */
struct cb710_mmc_reader {
- struct tasklet_struct finish_req_tasklet;
+ struct work_struct finish_req_bh_work;
struct mmc_request *mrq;
spinlock_t irq_lock;
unsigned char last_power_mode;
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index c302eb380e42..9cbde800685d 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1187,7 +1187,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
struct mmc_davinci_host *host = NULL;
struct mmc_host *mmc = NULL;
struct resource *r, *mem = NULL;
- int ret, irq;
+ int ret, irq, bus_width;
size_t mem_size;
const struct platform_device_id *id_entry;
@@ -1317,9 +1317,14 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
rename_region(mem, mmc_hostname(mmc));
+ if (mmc->caps & MMC_CAP_8_BIT_DATA)
+ bus_width = 8;
+ else if (mmc->caps & MMC_CAP_4_BIT_DATA)
+ bus_width = 4;
+ else
+ bus_width = 1;
dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
- host->use_dma ? "DMA" : "PIO",
- (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
+ host->use_dma ? "DMA" : "PIO", bus_width);
return 0;
diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c
index 4747e5698f48..24e0b604b405 100644
--- a/drivers/mmc/host/dw_mmc-bluefield.c
+++ b/drivers/mmc/host/dw_mmc-bluefield.c
@@ -3,6 +3,7 @@
* Copyright (C) 2018 Mellanox Technologies.
*/
+#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/mmc/host.h>
@@ -20,6 +21,9 @@
#define BLUEFIELD_UHS_REG_EXT_SAMPLE 2
#define BLUEFIELD_UHS_REG_EXT_DRIVE 4
+/* SMC call for RST_N */
+#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
+
static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
u32 reg;
@@ -34,8 +38,20 @@ static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
mci_writel(host, UHS_REG_EXT, reg);
}
+static void dw_mci_bluefield_hw_reset(struct dw_mci *host)
+{
+ struct arm_smccc_res res = { 0 };
+
+ arm_smccc_smc(BLUEFIELD_SMC_SET_EMMC_RST_N, 0, 0, 0, 0, 0, 0, 0,
+ &res);
+
+ if (res.a0)
+ pr_err("RST_N failed.\n");
+}
+
static const struct dw_mci_drv_data bluefield_drv_data = {
- .set_ios = dw_mci_bluefield_set_ios
+ .set_ios = dw_mci_bluefield_set_ios,
+ .hw_reset = dw_mci_bluefield_hw_reset
};
static const struct of_device_id dw_mci_bluefield_match[] = {
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 8e2d676b9239..2333ef4893ee 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -493,7 +493,7 @@ static void dw_mci_dmac_complete_dma(void *arg)
*/
if (data) {
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
}
@@ -1617,6 +1617,7 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
+ const struct dw_mci_drv_data *drv_data = host->drv_data;
int reset;
if (host->use_dma == TRANS_MODE_IDMAC)
@@ -1626,6 +1627,11 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
SDMMC_CTRL_FIFO_RESET))
return;
+ if (drv_data && drv_data->hw_reset) {
+ drv_data->hw_reset(host);
+ return;
+ }
+
/*
* According to eMMC spec, card reset procedure:
* tRstW >= 1us: RST_n pulse width
@@ -1834,7 +1840,7 @@ static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t)
if (!host->data_status) {
host->data_status = SDMMC_INT_DCRC;
set_bit(EVENT_DATA_ERROR, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
spin_unlock_irqrestore(&host->irq_lock, flags);
@@ -2056,9 +2062,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
return true;
}
-static void dw_mci_tasklet_func(struct tasklet_struct *t)
+static void dw_mci_work_func(struct work_struct *t)
{
- struct dw_mci *host = from_tasklet(host, t, tasklet);
+ struct dw_mci *host = from_work(host, t, bh_work);
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
@@ -2113,7 +2119,7 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
* will waste a bit of time (we already know
* the command was bad), it can't cause any
* errors since it's possible it would have
- * taken place anyway if this tasklet got
+ * taken place anyway if this bh work got
* delayed. Allowing the transfer to take place
* avoids races and keeps things simple.
*/
@@ -2706,7 +2712,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
smp_wmb(); /* drain writebuffer */
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
dw_mci_start_fault_timer(host);
}
@@ -2774,7 +2780,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
set_bit(EVENT_DATA_COMPLETE,
&host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
spin_unlock(&host->irq_lock);
}
@@ -2793,7 +2799,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
dw_mci_read_data_pio(host, true);
}
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
spin_unlock(&host->irq_lock);
}
@@ -3098,7 +3104,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t)
host->cmd_status = SDMMC_INT_RTO;
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
}
static void dw_mci_cto_timer(struct timer_list *t)
@@ -3144,7 +3150,7 @@ static void dw_mci_cto_timer(struct timer_list *t)
*/
host->cmd_status = SDMMC_INT_RTO;
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
break;
default:
dev_warn(host->dev, "Unexpected command timeout, state %d\n",
@@ -3195,7 +3201,7 @@ static void dw_mci_dto_timer(struct timer_list *t)
host->data_status = SDMMC_INT_DRTO;
set_bit(EVENT_DATA_ERROR, &host->pending_events);
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
- tasklet_schedule(&host->tasklet);
+ queue_work(system_bh_wq, &host->bh_work);
break;
default:
dev_warn(host->dev, "Unexpected data timeout, state %d\n",
@@ -3435,7 +3441,7 @@ int dw_mci_probe(struct dw_mci *host)
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
- tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
+ INIT_WORK(&host->bh_work, dw_mci_work_func);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
if (ret)
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 4ed81f94f7ca..6447b916990d 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -17,6 +17,7 @@
#include <linux/fault-inject.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
+#include <linux/workqueue.h>
enum dw_mci_state {
STATE_IDLE = 0,
@@ -89,12 +90,12 @@ struct dw_mci_dma_slave {
* @stop_cmdr: Value to be loaded into CMDR when the stop command is
* to be sent.
* @dir_status: Direction of current transfer.
- * @tasklet: Tasklet running the request state machine.
+ * @bh_work: Work running the request state machine.
* @pending_events: Bitmask of events flagged by the interrupt handler
- * to be processed by the tasklet.
+ * to be processed by bh work.
* @completed_events: Bitmask of events which the state machine has
* processed.
- * @state: Tasklet state.
+ * @state: BH work state.
* @queue: List of slots waiting for access to the controller.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
@@ -194,7 +195,7 @@ struct dw_mci {
u32 data_status;
u32 stop_cmdr;
u32 dir_status;
- struct tasklet_struct tasklet;
+ struct work_struct bh_work;
unsigned long pending_events;
unsigned long completed_events;
enum dw_mci_state state;
@@ -565,6 +566,7 @@ struct dw_mci_slot {
* @execute_tuning: implementation specific tuning procedure.
* @set_data_timeout: implementation specific timeout.
* @get_drto_clks: implementation specific cycle count for data read timeout.
+ * @hw_reset: implementation specific HW reset.
*
* Provide controller implementation specific extensions. The usage of this
* data structure is fully optional and usage of each member in this structure
@@ -585,5 +587,6 @@ struct dw_mci_drv_data {
void (*set_data_timeout)(struct dw_mci *host,
unsigned int timeout_ns);
u32 (*get_drto_clks)(struct dw_mci *host);
+ void (*hw_reset)(struct dw_mci *host);
};
#endif /* _DW_MMC_H_ */
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 09d7a6a0dc1a..c9caa1ece7ef 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1208,7 +1208,10 @@ static int mmc_spi_probe(struct spi_device *spi)
* that's the only reason not to use a few MHz for f_min (until
* the upper layer reads the target frequency from the CSD).
*/
- mmc->f_min = 400000;
+ if (spi->controller->min_speed_hz > 400000)
+ dev_warn(&spi->dev,"Controller unable to reduce bus clock to 400 KHz\n");
+
+ mmc->f_min = max(spi->controller->min_speed_hz, 400000);
mmc->f_max = spi->max_speed_hz;
host = mmc_priv(mmc);
diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c
index bf54776fb26c..05939f30a5ae 100644
--- a/drivers/mmc/host/of_mmc_spi.c
+++ b/drivers/mmc/host/of_mmc_spi.c
@@ -19,6 +19,7 @@
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
+MODULE_DESCRIPTION("OpenFirmware bindings for the MMC-over-SPI driver");
MODULE_LICENSE("GPL");
struct of_mmc_spi {
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index a8ee0df47148..335350a4e99a 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_data/mmc-omap.h>
+#include <linux/workqueue.h>
#define OMAP_MMC_REG_CMD 0x00
@@ -105,7 +106,7 @@ struct mmc_omap_slot {
u16 power_mode;
unsigned int fclk_freq;
- struct tasklet_struct cover_tasklet;
+ struct work_struct cover_bh_work;
struct timer_list cover_timer;
unsigned cover_open;
@@ -873,18 +874,18 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
}
- tasklet_hi_schedule(&slot->cover_tasklet);
+ queue_work(system_bh_highpri_wq, &slot->cover_bh_work);
}
static void mmc_omap_cover_timer(struct timer_list *t)
{
struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
- tasklet_schedule(&slot->cover_tasklet);
+ queue_work(system_bh_wq, &slot->cover_bh_work);
}
-static void mmc_omap_cover_handler(struct tasklet_struct *t)
+static void mmc_omap_cover_bh_handler(struct work_struct *t)
{
- struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
+ struct mmc_omap_slot *slot = from_work(slot, t, cover_bh_work);
int cover_open = mmc_omap_cover_is_open(slot);
mmc_detect_change(slot->mmc, 0);
@@ -1314,7 +1315,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
if (slot->pdata->get_cover_state != NULL) {
timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
- tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
+ INIT_WORK(&slot->cover_bh_work, mmc_omap_cover_bh_handler);
}
r = mmc_add_host(mmc);
@@ -1333,7 +1334,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
&dev_attr_cover_switch);
if (r < 0)
goto err_remove_slot_name;
- tasklet_schedule(&slot->cover_tasklet);
+ queue_work(system_bh_wq, &slot->cover_bh_work);
}
return 0;
@@ -1356,7 +1357,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
if (slot->pdata->get_cover_state != NULL)
device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
- tasklet_kill(&slot->cover_tasklet);
+ cancel_work_sync(&slot->cover_bh_work);
del_timer_sync(&slot->cover_timer);
flush_workqueue(slot->host->mmc_omap_wq);
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index 586f94d4dbfd..f12a87442338 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -11,6 +11,7 @@
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
+#include <linux/workqueue.h>
#include "tmio_mmc.h"
struct renesas_sdhi_scc {
@@ -67,7 +68,7 @@ struct renesas_sdhi_dma {
dma_filter_fn filter;
void (*enable)(struct tmio_mmc_host *host, bool enable);
struct completion dma_dataend;
- struct tasklet_struct dma_complete;
+ struct work_struct dma_complete;
};
struct renesas_sdhi {
@@ -93,6 +94,7 @@ struct renesas_sdhi {
unsigned int tap_set;
struct reset_control *rstc;
+ struct tmio_mmc_host *host;
};
#define host_to_priv(host) \
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 12f4faaaf4ee..04874791541f 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -970,6 +970,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (IS_ERR(host))
return PTR_ERR(host);
+ priv->host = host;
+
if (of_data) {
mmc_data->flags |= of_data->tmio_flags;
mmc_data->ocr_mask = of_data->tmio_ocr_mask;
@@ -1162,4 +1164,5 @@ void renesas_sdhi_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
+MODULE_DESCRIPTION("Renesas SDHI core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 422fa63a2e99..d4b66daeda66 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -337,7 +337,7 @@ static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host)
writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1);
set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags);
if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags))
- tasklet_schedule(&dma_priv->dma_complete);
+ queue_work(system_bh_wq, &dma_priv->dma_complete);
}
return status & dma_irqs;
@@ -352,7 +352,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags);
if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) ||
host->data->error)
- tasklet_schedule(&dma_priv->dma_complete);
+ queue_work(system_bh_wq, &dma_priv->dma_complete);
}
/*
@@ -440,9 +440,9 @@ force_pio:
renesas_sdhi_internal_dmac_enable_dma(host, false);
}
-static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
+static void renesas_sdhi_internal_dmac_issue_work_fn(struct work_struct *work)
{
- struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+ struct tmio_mmc_host *host = from_work(host, work, dma_issue);
struct renesas_sdhi *priv = host_to_priv(host);
tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
@@ -454,7 +454,7 @@ static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
/* on CMD errors, simulate DMA end immediately */
set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags);
if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags))
- tasklet_schedule(&priv->dma_priv.dma_complete);
+ queue_work(system_bh_wq, &priv->dma_priv.dma_complete);
}
}
@@ -484,9 +484,11 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
return true;
}
-static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
+static void renesas_sdhi_internal_dmac_complete_work_fn(struct work_struct *work)
{
- struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+ struct renesas_sdhi_dma *dma_priv = from_work(dma_priv, work, dma_complete);
+ struct renesas_sdhi *priv = container_of(dma_priv, typeof(*priv), dma_priv);
+ struct tmio_mmc_host *host = priv->host;
spin_lock_irq(&host->lock);
if (!renesas_sdhi_internal_dmac_complete(host))
@@ -544,12 +546,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
/* Each value is set to non-zero to assume "enabling" each DMA */
host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
- tasklet_init(&priv->dma_priv.dma_complete,
- renesas_sdhi_internal_dmac_complete_tasklet_fn,
- (unsigned long)host);
- tasklet_init(&host->dma_issue,
- renesas_sdhi_internal_dmac_issue_tasklet_fn,
- (unsigned long)host);
+ INIT_WORK(&priv->dma_priv.dma_complete,
+ renesas_sdhi_internal_dmac_complete_work_fn);
+ INIT_WORK(&host->dma_issue,
+ renesas_sdhi_internal_dmac_issue_work_fn);
/* Add pre_req and post_req */
host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 9cf7f9feab72..5a6f41318645 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -312,9 +312,9 @@ static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host,
}
}
-static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv)
+static void renesas_sdhi_sys_dmac_issue_work_fn(struct work_struct *work)
{
- struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv;
+ struct tmio_mmc_host *host = from_work(host, work, dma_issue);
struct dma_chan *chan = NULL;
spin_lock_irq(&host->lock);
@@ -401,9 +401,8 @@ static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host,
goto ebouncebuf;
init_completion(&priv->dma_priv.dma_dataend);
- tasklet_init(&host->dma_issue,
- renesas_sdhi_sys_dmac_issue_tasklet_fn,
- (unsigned long)host);
+ INIT_WORK(&host->dma_issue,
+ renesas_sdhi_sys_dmac_issue_work_fn);
}
renesas_sdhi_sys_dmac_enable_dma(host, true);
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index cb9152c6a65d..e067c7f5c537 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -107,7 +107,7 @@ static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
* Software emulation of the SD card insertion/removal. Set insert=1 for insert
* and insert=0 for removal. The card detection is done by GPIO. For Broadcom
* IP to function properly the bit 0 of CORESTAT register needs to be set/reset
- * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
+* to generate the CD IRQ handled in sdhci.c
*/
static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
{
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index 150fb477b7cc..031a4b514d16 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -31,6 +31,21 @@
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
+#define SDIO_CFG_CQ_CAPABILITY 0x4c
+#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
+
+#define SDIO_CFG_CTRL 0x0
+#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
+#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
+
+#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
+#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
+#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
+
+#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
+/* Select all SD UHS type I SDR speed above 50MB/s */
+#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
+
struct sdhci_brcmstb_priv {
void __iomem *cfg_regs;
unsigned int flags;
@@ -39,6 +54,7 @@ struct sdhci_brcmstb_priv {
};
struct brcmstb_match_priv {
+ void (*cfginit)(struct sdhci_host *host);
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
struct sdhci_ops *ops;
const unsigned int flags;
@@ -169,6 +185,33 @@ static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
+static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /*
+ * If we support a speed that requires tuning,
+ * then select the delay line PHY as the clock source.
+ */
+ if ((host->mmc->caps & MMC_CAP_UHS_I_SDR_MASK) || (host->mmc->caps2 & MMC_CAP_HSE_MASK)) {
+ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
+ reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE;
+ reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE;
+ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
+ }
+
+ if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ (host->mmc->caps & MMC_CAP_NEEDS_POLL)) {
+ /* Force presence */
+ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
+ reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
+ reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN;
+ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
+ }
+}
+
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{
sdhci_dumpregs(mmc_priv(mmc));
@@ -201,6 +244,14 @@ static struct sdhci_ops sdhci_brcmstb_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
+static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
+ .set_clock = sdhci_set_clock,
+ .set_power = sdhci_set_power_and_bus_voltage,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width,
@@ -215,6 +266,11 @@ static struct sdhci_ops sdhci_brcmstb_ops_74165b0 = {
.set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling,
};
+static const struct brcmstb_match_priv match_priv_2712 = {
+ .cfginit = sdhci_brcmstb_cfginit_2712,
+ .ops = &sdhci_brcmstb_ops_2712,
+};
+
static struct brcmstb_match_priv match_priv_7425 = {
.flags = BRCMSTB_MATCH_FLAGS_NO_64BIT |
BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
@@ -239,6 +295,7 @@ static struct brcmstb_match_priv match_priv_74165b0 = {
};
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
+ { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
@@ -371,6 +428,9 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
(host->mmc->caps2 & MMC_CAP2_HS400_ES))
host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
+ if (match_priv->cfginit)
+ match_priv->cfginit(host);
+
/*
* Supply the existing CAPS, but clear the UHS modes. This
* will allow these modes to be specified by device tree
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 40a6e2f8145a..8f0bc6dca2b0 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -201,6 +201,9 @@
/* ERR004536 is not applicable for the IP */
#define ESDHC_FLAG_SKIP_ERR004536 BIT(17)
+/* The IP does not have GPIO CD wake capabilities */
+#define ESDHC_FLAG_SKIP_CD_WAKE BIT(18)
+
enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
@@ -298,7 +301,7 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
- | ESDHC_FLAG_SKIP_ERR004536,
+ | ESDHC_FLAG_SKIP_ERR004536 | ESDHC_FLAG_SKIP_CD_WAKE,
};
static struct esdhc_soc_data usdhc_imx7ulp_data = {
@@ -1706,7 +1709,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
}
pltfm_host->clk = imx_data->clk_per;
- pltfm_host->clock = clk_get_rate(pltfm_host->clk);
err = clk_prepare_enable(imx_data->clk_per);
if (err)
goto free_sdhci;
@@ -1717,6 +1719,13 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (err)
goto disable_ipg_clk;
+ pltfm_host->clock = clk_get_rate(pltfm_host->clk);
+ if (!pltfm_host->clock) {
+ dev_err(mmc_dev(host->mmc), "could not get clk rate\n");
+ err = -EINVAL;
+ goto disable_ahb_clk;
+ }
+
imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(imx_data->pinctrl))
dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
@@ -1726,7 +1735,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
/* GPIO CD can be set as a wakeup source */
- host->mmc->caps |= MMC_CAP_CD_WAKE;
+ if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_CD_WAKE))
+ host->mmc->caps |= MMC_CAP_CD_WAKE;
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 39edf04fedcf..e79aa4b3b6c3 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -908,6 +908,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
.get_max_clock = rk35xx_get_max_clock,
.reset = rk35xx_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
+ .irq = dwcmshc_cqe_irq_handler,
};
static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 23e6ba70144c..ed45ed0bdafd 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -1319,6 +1319,23 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
.probe_slot = intel_mrfld_mmc_probe_slot,
};
+#define JMB388_SAMPLE_COUNT 5
+
+static int jmicron_jmb388_get_ro(struct mmc_host *mmc)
+{
+ int i, ro_count;
+
+ ro_count = 0;
+ for (i = 0; i < JMB388_SAMPLE_COUNT; i++) {
+ if (sdhci_get_ro(mmc) > 0) {
+ if (++ro_count > JMB388_SAMPLE_COUNT / 2)
+ return 1;
+ }
+ msleep(30);
+ }
+ return 0;
+}
+
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
{
u8 scratch;
@@ -1403,11 +1420,6 @@ static int jmicron_probe(struct sdhci_pci_chip *chip)
return ret;
}
- /* quirk for unsable RO-detection on JM388 chips */
- if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD ||
- chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD)
- chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT;
-
return 0;
}
@@ -1462,6 +1474,11 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST;
+ /* Handle unstable RO-detection on JM388 chips */
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD ||
+ slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD)
+ slot->host->mmc_host_ops.get_ro = jmicron_jmb388_get_ro;
+
return 0;
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fbf7a91bed35..4b91c9e96635 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2513,8 +2513,9 @@ out:
}
EXPORT_SYMBOL_GPL(sdhci_get_cd_nogpio);
-static int sdhci_check_ro(struct sdhci_host *host)
+int sdhci_get_ro(struct mmc_host *mmc)
{
+ struct sdhci_host *host = mmc_priv(mmc);
bool allow_invert = false;
int is_readonly;
@@ -2522,10 +2523,10 @@ static int sdhci_check_ro(struct sdhci_host *host)
is_readonly = 0;
} else if (host->ops->get_ro) {
is_readonly = host->ops->get_ro(host);
- } else if (mmc_can_gpio_ro(host->mmc)) {
- is_readonly = mmc_gpio_get_ro(host->mmc);
+ } else if (mmc_can_gpio_ro(mmc)) {
+ is_readonly = mmc_gpio_get_ro(mmc);
/* Do not invert twice */
- allow_invert = !(host->mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
+ allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
} else {
is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
& SDHCI_WRITE_PROTECT);
@@ -2539,27 +2540,7 @@ static int sdhci_check_ro(struct sdhci_host *host)
return is_readonly;
}
-
-#define SAMPLE_COUNT 5
-
-static int sdhci_get_ro(struct mmc_host *mmc)
-{
- struct sdhci_host *host = mmc_priv(mmc);
- int i, ro_count;
-
- if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
- return sdhci_check_ro(host);
-
- ro_count = 0;
- for (i = 0; i < SAMPLE_COUNT; i++) {
- if (sdhci_check_ro(host)) {
- if (++ro_count > SAMPLE_COUNT / 2)
- return 1;
- }
- msleep(30);
- }
- return 0;
-}
+EXPORT_SYMBOL_GPL(sdhci_get_ro);
static void sdhci_hw_reset(struct mmc_host *mmc)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 957c7a917ffb..f531b617f28d 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -437,8 +437,6 @@ struct sdhci_host {
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
/* Controller treats ADMA descriptors with length 0000h incorrectly */
#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
-/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
-#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
unsigned int quirks2; /* More deviations from spec. */
@@ -788,6 +786,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
int sdhci_get_cd_nogpio(struct mmc_host *mmc);
+int sdhci_get_ro(struct mmc_host *mmc);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
void sdhci_set_bus_width(struct sdhci_host *host, int width);
diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index 17ad32cfc0c3..64e10f7c9faa 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -90,7 +90,7 @@
/* Command Queue Host Controller Interface Base address */
#define SDHCI_AM654_CQE_BASE_ADDR 0x200
-static struct regmap_config sdhci_am654_regmap_config = {
+static const struct regmap_config sdhci_am654_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index b5a2f2f25ad9..aea14bf3e2e8 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -13,6 +13,7 @@
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
+#include <linux/workqueue.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_sd"
@@ -97,7 +98,7 @@ struct tifm_sd {
unsigned int clk_div;
unsigned long timeout_jiffies;
- struct tasklet_struct finish_tasklet;
+ struct work_struct finish_bh_work;
struct timer_list timer;
struct mmc_request *req;
@@ -463,7 +464,7 @@ static void tifm_sd_check_status(struct tifm_sd *host)
}
}
finish_request:
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
/* Called from interrupt handler */
@@ -723,9 +724,9 @@ err_out:
mmc_request_done(mmc, mrq);
}
-static void tifm_sd_end_cmd(struct tasklet_struct *t)
+static void tifm_sd_end_cmd(struct work_struct *t)
{
- struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
+ struct tifm_sd *host = from_work(host, t, finish_bh_work);
struct tifm_dev *sock = host->dev;
struct mmc_host *mmc = tifm_get_drvdata(sock);
struct mmc_request *mrq;
@@ -960,7 +961,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
*/
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
- tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
+ INIT_WORK(&host->finish_bh_work, tifm_sd_end_cmd);
timer_setup(&host->timer, tifm_sd_abort, 0);
mmc->ops = &tifm_sd_ops;
@@ -999,7 +1000,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
spin_unlock_irqrestore(&sock->lock, flags);
- tasklet_kill(&host->finish_tasklet);
+ cancel_work_sync(&host->finish_bh_work);
spin_lock_irqsave(&sock->lock, flags);
if (host->req) {
@@ -1009,7 +1010,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
host->req->cmd->error = -ENOMEDIUM;
if (host->req->stop)
host->req->stop->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
spin_unlock_irqrestore(&sock->lock, flags);
mmc_remove_host(mmc);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index de56e6534aea..a75755f31d31 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -21,6 +21,7 @@
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
+#include <linux/workqueue.h>
#define CTL_SD_CMD 0x00
#define CTL_ARG_REG 0x04
@@ -139,9 +140,6 @@ struct tmio_mmc_host {
struct mmc_host *mmc;
struct mmc_host_ops ops;
- /* Callbacks for clock / power control */
- void (*set_pwr)(struct platform_device *host, int state);
-
/* pio related stuff */
struct scatterlist *sg_ptr;
struct scatterlist *sg_orig;
@@ -156,7 +154,7 @@ struct tmio_mmc_host {
bool dma_on;
struct dma_chan *chan_rx;
struct dma_chan *chan_tx;
- struct tasklet_struct dma_issue;
+ struct work_struct dma_issue;
struct scatterlist bounce_sg;
u8 *bounce_buf;
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 93e912afd3ae..b61a6310311d 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -608,7 +608,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
} else {
tmio_mmc_disable_mmc_irqs(host,
TMIO_MASK_READOP);
- tasklet_schedule(&host->dma_issue);
+ queue_work(system_bh_wq, &host->dma_issue);
}
} else {
if (!host->dma_on) {
@@ -616,7 +616,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
} else {
tmio_mmc_disable_mmc_irqs(host,
TMIO_MASK_WRITEOP);
- tasklet_schedule(&host->dma_issue);
+ queue_work(system_bh_wq, &host->dma_issue);
}
}
} else {
@@ -880,9 +880,6 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
/* .set_ios() is returning void, so, no chance to report an error */
- if (host->set_pwr)
- host->set_pwr(host->pdev, 1);
-
if (!IS_ERR(mmc->supply.vmmc)) {
ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
/*
@@ -916,9 +913,6 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
-
- if (host->set_pwr)
- host->set_pwr(host->pdev, 0);
}
static unsigned int tmio_mmc_get_timeout_cycles(struct tmio_mmc_host *host)
@@ -1160,8 +1154,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
if (pdata->flags & TMIO_MMC_USE_BUSY_TIMEOUT && !_host->get_timeout_cycles)
_host->get_timeout_cycles = tmio_mmc_get_timeout_cycles;
- _host->set_pwr = pdata->set_pwr;
-
ret = tmio_mmc_init_ocr(_host);
if (ret < 0)
return ret;
@@ -1319,4 +1311,5 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
EXPORT_SYMBOL_GPL(tmio_mmc_host_runtime_resume);
#endif
+MODULE_DESCRIPTION("TMIO MMC core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c
index 1404989e6151..417fd13efdfd 100644
--- a/drivers/mmc/host/uniphier-sd.c
+++ b/drivers/mmc/host/uniphier-sd.c
@@ -90,9 +90,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable)
}
/* external DMA engine */
-static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
+static void uniphier_sd_external_dma_issue(struct work_struct *t)
{
- struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
+ struct tmio_mmc_host *host = from_work(host, t, dma_issue);
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
uniphier_sd_dma_endisable(host, 1);
@@ -199,7 +199,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host,
host->chan_rx = chan;
host->chan_tx = chan;
- tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
+ INIT_WORK(&host->dma_issue, uniphier_sd_external_dma_issue);
}
static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
@@ -236,9 +236,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = {
.dataend = uniphier_sd_external_dma_dataend,
};
-static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
+static void uniphier_sd_internal_dma_issue(struct work_struct *t)
{
- struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
+ struct tmio_mmc_host *host = from_work(host, t, dma_issue);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
@@ -317,7 +317,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host,
host->chan_tx = (void *)0xdeadbeaf;
- tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
+ INIT_WORK(&host->dma_issue, uniphier_sd_internal_dma_issue);
}
static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index ba6044b16e07..f77457105ec3 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/mmc/host.h>
+#include <linux/workqueue.h>
#define DRV_NAME "via_sdmmc"
@@ -307,7 +308,7 @@ struct via_crdr_mmc_host {
struct sdhcreg pm_sdhc_reg;
struct work_struct carddet_work;
- struct tasklet_struct finish_tasklet;
+ struct work_struct finish_bh_work;
struct timer_list timer;
spinlock_t lock;
@@ -643,7 +644,7 @@ static void via_sdc_finish_data(struct via_crdr_mmc_host *host)
if (data->stop)
via_sdc_send_command(host, data->stop);
else
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
@@ -653,7 +654,7 @@ static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
host->cmd->error = 0;
if (!host->cmd->data)
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
host->cmd = NULL;
}
@@ -682,7 +683,7 @@ static void via_sdc_request(struct mmc_host *mmc, struct mmc_request *mrq)
status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS);
if (!(status & VIA_CRDR_SDSTS_SLOTG) || host->reject) {
host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
} else {
via_sdc_send_command(host, mrq->cmd);
}
@@ -848,7 +849,7 @@ static void via_sdc_cmd_isr(struct via_crdr_mmc_host *host, u16 intmask)
host->cmd->error = -EILSEQ;
if (host->cmd->error)
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
else if (intmask & VIA_CRDR_SDSTS_CRD)
via_sdc_finish_command(host);
}
@@ -955,16 +956,16 @@ static void via_sdc_timeout(struct timer_list *t)
sdhost->cmd->error = -ETIMEDOUT;
else
sdhost->mrq->cmd->error = -ETIMEDOUT;
- tasklet_schedule(&sdhost->finish_tasklet);
+ queue_work(system_bh_wq, &sdhost->finish_bh_work);
}
}
spin_unlock_irqrestore(&sdhost->lock, flags);
}
-static void via_sdc_tasklet_finish(struct tasklet_struct *t)
+static void via_sdc_finish_bh_work(struct work_struct *t)
{
- struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
+ struct via_crdr_mmc_host *host = from_work(host, t, finish_bh_work);
unsigned long flags;
struct mmc_request *mrq;
@@ -1005,7 +1006,7 @@ static void via_sdc_card_detect(struct work_struct *work)
pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -1051,7 +1052,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
INIT_WORK(&host->carddet_work, via_sdc_card_detect);
- tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
+ INIT_WORK(&host->finish_bh_work, via_sdc_finish_bh_work);
addrbase = host->sdhc_mmiobase;
writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
@@ -1193,7 +1194,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
sdhost->mrq->cmd->error = -ENOMEDIUM;
if (sdhost->mrq->stop)
sdhost->mrq->stop->error = -ENOMEDIUM;
- tasklet_schedule(&sdhost->finish_tasklet);
+ queue_work(system_bh_wq, &sdhost->finish_bh_work);
}
spin_unlock_irqrestore(&sdhost->lock, flags);
@@ -1203,7 +1204,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
del_timer_sync(&sdhost->timer);
- tasklet_kill(&sdhost->finish_tasklet);
+ cancel_work_sync(&sdhost->finish_bh_work);
/* switch off power */
gatt = readb(sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT);
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index f0562f712d98..6e20405d0430 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -459,7 +459,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
* FIFO threshold interrupts properly.
*/
if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
- tasklet_schedule(&host->fifo_tasklet);
+ queue_work(system_bh_wq, &host->fifo_bh_work);
}
static void wbsd_fill_fifo(struct wbsd_host *host)
@@ -524,7 +524,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
* 'FIFO empty' under certain conditions. So we
* need to be a bit more pro-active.
*/
- tasklet_schedule(&host->fifo_tasklet);
+ queue_work(system_bh_wq, &host->fifo_bh_work);
}
static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
@@ -746,7 +746,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_command *cmd;
/*
- * Disable tasklets to avoid a deadlock.
+ * Disable bh works to avoid a deadlock.
*/
spin_lock_bh(&host->lock);
@@ -821,7 +821,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
* Dirty fix for hardware bug.
*/
if (host->dma == -1)
- tasklet_schedule(&host->fifo_tasklet);
+ queue_work(system_bh_wq, &host->fifo_bh_work);
spin_unlock_bh(&host->lock);
@@ -961,13 +961,13 @@ static void wbsd_reset_ignore(struct timer_list *t)
* Card status might have changed during the
* blackout.
*/
- tasklet_schedule(&host->card_tasklet);
+ queue_work(system_bh_wq, &host->card_bh_work);
spin_unlock_bh(&host->lock);
}
/*
- * Tasklets
+ * BH Works
*/
static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
@@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
return host->mrq->cmd->data;
}
-static void wbsd_tasklet_card(struct tasklet_struct *t)
+static void wbsd_card_bh_work(struct work_struct *t)
{
- struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
+ struct wbsd_host *host = from_work(host, t, card_bh_work);
u8 csr;
int delay = -1;
@@ -1020,7 +1020,7 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
wbsd_reset(host);
host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
delay = 0;
@@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
}
-static void wbsd_tasklet_fifo(struct tasklet_struct *t)
+static void wbsd_fifo_bh_work(struct work_struct *t)
{
- struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
+ struct wbsd_host *host = from_work(host, t, fifo_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1060,16 +1060,16 @@ static void wbsd_tasklet_fifo(struct tasklet_struct *t)
*/
if (host->num_sg == 0) {
wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
}
end:
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_crc(struct tasklet_struct *t)
+static void wbsd_crc_bh_work(struct work_struct *t)
{
- struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
+ struct wbsd_host *host = from_work(host, t, crc_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1085,15 +1085,15 @@ static void wbsd_tasklet_crc(struct tasklet_struct *t)
data->error = -EILSEQ;
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
end:
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_timeout(struct tasklet_struct *t)
+static void wbsd_timeout_bh_work(struct work_struct *t)
{
- struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
+ struct wbsd_host *host = from_work(host, t, timeout_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1109,15 +1109,15 @@ static void wbsd_tasklet_timeout(struct tasklet_struct *t)
data->error = -ETIMEDOUT;
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
end:
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_finish(struct tasklet_struct *t)
+static void wbsd_finish_bh_work(struct work_struct *t)
{
- struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
+ struct wbsd_host *host = from_work(host, t, finish_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1156,18 +1156,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
host->isr |= isr;
/*
- * Schedule tasklets as needed.
+ * Schedule bh work as needed.
*/
if (isr & WBSD_INT_CARD)
- tasklet_schedule(&host->card_tasklet);
+ queue_work(system_bh_wq, &host->card_bh_work);
if (isr & WBSD_INT_FIFO_THRE)
- tasklet_schedule(&host->fifo_tasklet);
+ queue_work(system_bh_wq, &host->fifo_bh_work);
if (isr & WBSD_INT_CRC)
- tasklet_hi_schedule(&host->crc_tasklet);
+ queue_work(system_bh_highpri_wq, &host->crc_bh_work);
if (isr & WBSD_INT_TIMEOUT)
- tasklet_hi_schedule(&host->timeout_tasklet);
+ queue_work(system_bh_highpri_wq, &host->timeout_bh_work);
if (isr & WBSD_INT_TC)
- tasklet_schedule(&host->finish_tasklet);
+ queue_work(system_bh_wq, &host->finish_bh_work);
return IRQ_HANDLED;
}
@@ -1443,13 +1443,13 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq)
int ret;
/*
- * Set up tasklets. Must be done before requesting interrupt.
+ * Set up bh works. Must be done before requesting interrupt.
*/
- tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
- tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
- tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
- tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
- tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
+ INIT_WORK(&host->card_bh_work, wbsd_card_bh_work);
+ INIT_WORK(&host->fifo_bh_work, wbsd_fifo_bh_work);
+ INIT_WORK(&host->crc_bh_work, wbsd_crc_bh_work);
+ INIT_WORK(&host->timeout_bh_work, wbsd_timeout_bh_work);
+ INIT_WORK(&host->finish_bh_work, wbsd_finish_bh_work);
/*
* Allocate interrupt.
@@ -1472,11 +1472,11 @@ static void wbsd_release_irq(struct wbsd_host *host)
host->irq = 0;
- tasklet_kill(&host->card_tasklet);
- tasklet_kill(&host->fifo_tasklet);
- tasklet_kill(&host->crc_tasklet);
- tasklet_kill(&host->timeout_tasklet);
- tasklet_kill(&host->finish_tasklet);
+ cancel_work_sync(&host->card_bh_work);
+ cancel_work_sync(&host->fifo_bh_work);
+ cancel_work_sync(&host->crc_bh_work);
+ cancel_work_sync(&host->timeout_bh_work);
+ cancel_work_sync(&host->finish_bh_work);
}
/*
diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h
index be30b4d8ce4c..970886831305 100644
--- a/drivers/mmc/host/wbsd.h
+++ b/drivers/mmc/host/wbsd.h
@@ -171,11 +171,11 @@ struct wbsd_host
int irq; /* Interrupt */
int dma; /* DMA channel */
- struct tasklet_struct card_tasklet; /* Tasklet structures */
- struct tasklet_struct fifo_tasklet;
- struct tasklet_struct crc_tasklet;
- struct tasklet_struct timeout_tasklet;
- struct tasklet_struct finish_tasklet;
+ struct work_struct card_bh_work; /* Work structures */
+ struct work_struct fifo_bh_work;
+ struct work_struct crc_bh_work;
+ struct work_struct timeout_bh_work;
+ struct work_struct finish_bh_work;
struct timer_list ignore_timer; /* Ignore detection timer */
};
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index eace8ea6cda0..8c09d14a3a28 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -100,8 +100,6 @@ struct tmio_mmc_data {
dma_addr_t dma_rx_offset;
unsigned int max_blk_count;
unsigned short max_segs;
- void (*set_pwr)(struct platform_device *host, int state);
- void (*set_clk_div)(struct platform_device *host, int state);
};
/*