mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
MMC host:
- Convert from using tasklet to the BH workqueue - dw_mmc-bluefield: Add support for eMMC HW reset - mmc_spi: Allow spi controllers incapable of lower than 400kHz - sdhci: Rework code to eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT - sdhci-brcmstb: Add support for the BCM2712 variant - sdhci-esdhc-imx: Disable card-detect as system wakeup on S32G platforms - sdhci-msm: Add support for the SDX75 variant - sdhci-of-dwcmshc: Enable CQE support for some Rockchip variants - sdhci-of-esdhc: Convert DT-bindings to yaml - sdhci-sprd: Convert DT-bindings to yaml MEMSTICK: - rtsx_pci_ms: Remove the unused Realtek PCI memstick driver -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmaU/CMXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCnXrxAA0qSVIuFN/BwEGKMke0BTmzRS KX4qFLqi+IfOyKfFqD0+trE2LJ2oh7CnfV/FSmW8+45rlsaoTVVRZPyrllYuwDC0 OC3PeWBpzgYvmlilqHxsgl9McWG1F4mItgBvo0MxQFy26pwFiiQY3dBHjVchVwoZ VUq83rZr+cCgkriMrT31k2K0wwODTVEDxdKbmSwBSbYozPLyJkFediP64KH4sTF2 gDe1mM++ZQgn6IC+sDpia6SH0WFnZKuUZlf08WU4l2TjgcNVAeIxsllh6nYZSgj8 +nxEuICXyO8qYleCRBWb9s5EgNUl1P0dZmfDviHs+4ORbrie+4lngoDdqVraXz4g uy2QFYjZqfptmt4rJpUwtp2HbbX5eslV6rfS1uSkoYzcsTjQNv74Fgniek1qSXGh 9aF7U1lliwWs0W/Aquom3vw+6edFrTjJ3TC/la5dZwkx4qRndAVFpFiyvxKCp5yZ FtpjUn7s80CAhxVTCVgdl4jCRgwZzG0j+t5/YrSwEw1h8vWYY/WwioyFDjoxkY+N gv63vr+CfzKCcCyWN3ROaYnSVn9GDQkwaX2zJJTi+7ozRj6t09/Jp+Yi5GuXpn3/ Zktk35dSxCIF/DL8C1qwV1WWkOLQCLY7JGSWF41VDMkHMcw4+Xkp5YeynuktFEVt 33AfOn/ul139Xd1MrXg= =0Oa4 -----END PGP SIGNATURE----- Merge tag 'mmc-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC host: - Convert from using tasklet to the BH workqueue - dw_mmc-bluefield: Add support for eMMC HW reset - mmc_spi: Allow spi controllers incapable of lower than 400kHz - sdhci: Rework code to eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT - sdhci-brcmstb: Add support for the BCM2712 variant - sdhci-esdhc-imx: Disable card-detect as system wakeup on S32G platforms - sdhci-msm: Add support for the SDX75 variant - sdhci-of-dwcmshc: Enable CQE support for some Rockchip variants - sdhci-of-esdhc: Convert DT-bindings to yaml - sdhci-sprd: Convert DT-bindings to yaml MEMSTICK: - rtsx_pci_ms: Remove the unused Realtek PCI memstick driver" * tag 'mmc-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (26 commits) MAINTAINERS: add 's32@nxp.com' as relevant mailing list for 'sdhci-esdhc-imx' driver mmc: sdhci-esdhc-imx: obtain the 'per' clock rate after its enablement mmc: sdhci-esdhc-imx: disable card detect wake for S32G based platforms dt-bindings: mmc: sdhci-sprd: convert to YAML mmc: davinci_mmc: report all possible bus widths mmc: dw_mmc-bluefield: Add support for eMMC HW reset mmc: dw_mmc: Add support for platform specific eMMC HW reset mmc: sdhci_am654: Constify struct regmap_config mmc: Convert from tasklet to BH workqueue mmc: sdhi: Convert from tasklet to BH workqueue mmc: mmc_spi: allow for spi controllers incapable of getting as low as 400k memstick: rtsx_pci_ms: Remove Realtek PCI memstick driver MAINTAINERS: drop entry for VIA SD/MMC controller mmc: tmio: Remove obsolete .set_pwr() callback() mfd: tmio: Remove obsolete .set_clk_div() callback mmc: sdhci-brcmstb: Add ARCH_BCM2835 option mmc: sdhci: Eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT dt-bindings: mmc: Convert fsl-esdhc.txt to yaml dt-bindings: mmc: mmc-spi-slot: Change voltage-ranges to uint32-matrix mmc: add missing MODULE_DESCRIPTION() macros ...
This commit is contained in:
commit
3f32ab146c
@ -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:
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,7 @@ properties:
|
||||
- const: brcm,sdhci-brcmstb
|
||||
- items:
|
||||
- enum:
|
||||
- brcm,bcm2712-sdhci
|
||||
- brcm,bcm74165b0-sdhci
|
||||
- brcm,bcm7445-sdhci
|
||||
- brcm,bcm7425-sdhci
|
||||
|
105
Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml
Normal file
105
Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml
Normal file
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
@ -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: |
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
};
|
112
Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml
Normal file
112
Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml
Normal file
@ -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>;
|
||||
};
|
||||
...
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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)
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user