mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-08 07:38:10 +00:00
MMC core:
- Decrease polling rate for erase/trim/discard - Allow non-sleeping GPIOs for card detect - Improve mmc block removal path - Enable support for mmc_sw_reset() for SDIO cards - Add mmc_sw_reset() to allow users to do a soft reset of the card - Allow power delay to be tunable via DT - Allow card detect debounce delay to be tunable via DT - Enable new quirk to limit clock rate for Marvell 8887 chip - Don't show eMMC RPMB and BOOT areas in /proc/partitions - Add capability to avoid 3.3V signaling for fragile HWs MMC host: - Improve/fixup support for handle highmem pages - Remove depends on HAS_DMA in case of platform dependency - mvsdio: Enable support for erase/trim/discard - rtsx_usb: Enable support for erase/trim/discard - renesas_sdhi: Fix WP logic regressions - renesas_sdhi: Add r8a77965 support - renesas_sdhi: Add R8A77980 to whitelist - meson: Add optional support for device reset - meson: Add support for the Meson-AXG platform - dw_mmc: Add new driver for BlueField DW variant - mediatek: Add support for 64G DRAM DMA - sunxi: Deploy runtime PM support - jz4740: Add support for JZ4780 - jz4740: Enable support for DT based platforms - sdhci: Various improvement to timeout handling - sdhci: Disable support for HS200/HS400/UHS when no 1.8V support - sdhci-omap: Add support for controller in k2g SoC - sdhci-omap: Add workarounds for a couple of Erratas - sdhci-omap: Enable support for generic sdhci DT properties - sdhci-cadence: Re-send tune request to deal with errata - sdhci-pci: Fix 3.3V voltage switch for some BYT-based Intel controllers - sdhci-pci: Avoid 3.3V signaling on some NI 904x - sdhci-esdhc-imx: Use watermark levels for PIO access - sdhci-msm: Improve card detection handling - sdhci-msm: Add support voltage pad switching -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAlsWMJUXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCkWhw/9GdsKWGAezGYcBC9fBKVbZgvl Mu7kTebyQy1N7kbIVD7sBz2ila1O2DhVI9SVS7lVhzFwrG4Hup0szb/mjEQgiOAi +S2EjKRD3+9sA612fi1uWY+2cXoQiwCHhFZsCS5deL4pYspebzuYRNO5vZD7QlN8 vLHZ85yxBtMSiysDUTRm9MH2EXb9j6PfrHnVKj0ih112U/ip4PZVc7zHGBZq9zzy 6WSUMYBe2d1jajJ6MS3gf5Vrp53mz2OLkxKRCtMgut7YOwLZ8biKVa3G69ruQTm8 jqrMoNrZnhO3aT/1kassCE4ygoXewMZPhYq8k24PcaihR3o/tVI1dlb4Zz70cBb8 AGAdmdzu1sDEzY6KkSVTZ5IaxQo9SNANste/O/LfsOxHWkWxBQ6H0EeqMdt+W7of A0zGKTQrcTFhxzA9OeX3aZwZhjcipQlf8TWnI5+ayPY/UKwMhvWNCTMQtTJAhqBh mS7Uk5qd7OSJZTzVYrfLZ2ZOQKhtMbD9lAza/9Ldy5j5QDscLmxmhLOP0yuKI2Zp 92N3JPIGzfK4aKGCLKSA3KhYwuwaicwsXqfJV+cPz9fFtNs8WdHqyU+nslD0Oxm4 v3QZKPJa3LWCezPvWGSSTGLEgOvJtLQtJBefX1Hm6sxLbY12aBgu/iNp5UckGlzW 7zaWXtH8nzYlBKsXf/8= =MwCT -----END PGP SIGNATURE----- Merge tag 'mmc-v4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Decrease polling rate for erase/trim/discard - Allow non-sleeping GPIOs for card detect - Improve mmc block removal path - Enable support for mmc_sw_reset() for SDIO cards - Add mmc_sw_reset() to allow users to do a soft reset of the card - Allow power delay to be tunable via DT - Allow card detect debounce delay to be tunable via DT - Enable new quirk to limit clock rate for Marvell 8887 chip - Don't show eMMC RPMB and BOOT areas in /proc/partitions - Add capability to avoid 3.3V signaling for fragile HWs MMC host: - Improve/fixup support for handle highmem pages - Remove depends on HAS_DMA in case of platform dependency - mvsdio: Enable support for erase/trim/discard - rtsx_usb: Enable support for erase/trim/discard - renesas_sdhi: Fix WP logic regressions - renesas_sdhi: Add r8a77965 support - renesas_sdhi: Add R8A77980 to whitelist - meson: Add optional support for device reset - meson: Add support for the Meson-AXG platform - dw_mmc: Add new driver for BlueField DW variant - mediatek: Add support for 64G DRAM DMA - sunxi: Deploy runtime PM support - jz4740: Add support for JZ4780 - jz4740: Enable support for DT based platforms - sdhci: Various improvement to timeout handling - sdhci: Disable support for HS200/HS400/UHS when no 1.8V support - sdhci-omap: Add support for controller in k2g SoC - sdhci-omap: Add workarounds for a couple of Erratas - sdhci-omap: Enable support for generic sdhci DT properties - sdhci-cadence: Re-send tune request to deal with errata - sdhci-pci: Fix 3.3V voltage switch for some BYT-based Intel controllers - sdhci-pci: Avoid 3.3V signaling on some NI 904x - sdhci-esdhc-imx: Use watermark levels for PIO access - sdhci-msm: Improve card detection handling - sdhci-msm: Add support voltage pad switching" * tag 'mmc-v4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (104 commits) mmc: renesas_sdhi: really fix WP logic regressions mmc: mvsdio: Enable MMC_CAP_ERASE mmc: mvsdio: Respect card busy time out from mmc core mmc: sdhci-msm: Remove NO_CARD_NO_RESET quirk mmc: sunxi: Use ifdef rather than __maybe_unused mmc: mxmmc: Use ifdef rather than __maybe_unused mmc: mxmmc: include linux/highmem.h mmc: sunxi: mark PM functions as __maybe_unused mmc: Throttle calls to MMC_SEND_STATUS during mmc_do_erase() mmc: au1xmmc: handle highmem pages mmc: Allow non-sleeping GPIO cd mmc: sdhci-*: Don't emit error msg if sdhci_add_host() fails mmc: sd: Define name for default speed dtr mmc: core: Move calls to ->prepare_hs400_tuning() closer to mmc code mmc: sdhci-xenon: use match_string() helper mmc: wbsd: handle highmem pages mmc: ushc: handle highmem pages mmc: mxcmmc: handle highmem pages mmc: atmel-mci: use sg_copy_{from,to}_buffer mmc: android-goldfish: use sg_copy_{from,to}_buffer ...
This commit is contained in:
commit
f60342fac9
66 changed files with 1381 additions and 430 deletions
|
@ -12,6 +12,7 @@ Required properties:
|
||||||
- "amlogic,meson-gxbb-mmc"
|
- "amlogic,meson-gxbb-mmc"
|
||||||
- "amlogic,meson-gxl-mmc"
|
- "amlogic,meson-gxl-mmc"
|
||||||
- "amlogic,meson-gxm-mmc"
|
- "amlogic,meson-gxm-mmc"
|
||||||
|
- "amlogic,meson-axg-mmc"
|
||||||
- clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
|
- clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
|
||||||
- clock-names: Should contain the following:
|
- clock-names: Should contain the following:
|
||||||
"core" - Main peripheral bus clock
|
"core" - Main peripheral bus clock
|
||||||
|
@ -19,6 +20,7 @@ Required properties:
|
||||||
"clkin1" - Other parent clock of internal mux
|
"clkin1" - Other parent clock of internal mux
|
||||||
The driver has an internal mux clock which switches between clkin0 and clkin1 depending on the
|
The driver has an internal mux clock which switches between clkin0 and clkin1 depending on the
|
||||||
clock rate requested by the MMC core.
|
clock rate requested by the MMC core.
|
||||||
|
- resets : phandle of the internal reset line
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -29,4 +31,5 @@ Example:
|
||||||
clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>;
|
clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>;
|
||||||
clock-names = "core", "clkin0", "clkin1";
|
clock-names = "core", "clkin0", "clkin1";
|
||||||
pinctrl-0 = <&emmc_pins>;
|
pinctrl-0 = <&emmc_pins>;
|
||||||
|
resets = <&reset RESET_SD_EMMC_A>;
|
||||||
};
|
};
|
||||||
|
|
29
Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt
Normal file
29
Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
* Mellanox Bluefield SoC specific extensions to the Synopsys Designware
|
||||||
|
Mobile Storage Host Controller
|
||||||
|
|
||||||
|
Read synopsys-dw-mshc.txt for more details
|
||||||
|
|
||||||
|
The Synopsys designware mobile storage host controller is used to interface
|
||||||
|
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||||
|
differences between the core Synopsys dw mshc controller properties described
|
||||||
|
by synopsys-dw-mshc.txt and the properties used by the Mellanox Bluefield SoC
|
||||||
|
specific extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
|
||||||
|
* compatible: should be one of the following.
|
||||||
|
- "mellanox,bluefield-dw-mshc": for controllers with Mellanox Bluefield SoC
|
||||||
|
specific extensions.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
/* Mellanox Bluefield SoC MMC */
|
||||||
|
mmc@6008000 {
|
||||||
|
compatible = "mellanox,bluefield-dw-mshc";
|
||||||
|
reg = <0x6008000 0x400>;
|
||||||
|
interrupts = <32>;
|
||||||
|
fifo-depth = <0x100>;
|
||||||
|
clock-frequency = <24000000>;
|
||||||
|
bus-width = <8>;
|
||||||
|
cap-mmc-highspeed;
|
||||||
|
};
|
38
Documentation/devicetree/bindings/mmc/jz4740.txt
Normal file
38
Documentation/devicetree/bindings/mmc/jz4740.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
* Ingenic JZ47xx MMC controllers
|
||||||
|
|
||||||
|
This file documents the device tree properties used for the MMC controller in
|
||||||
|
Ingenic JZ4740/JZ4780 SoCs. These are in addition to the core MMC properties
|
||||||
|
described in mmc.txt.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be one of the following:
|
||||||
|
- "ingenic,jz4740-mmc" for the JZ4740
|
||||||
|
- "ingenic,jz4780-mmc" for the JZ4780
|
||||||
|
- reg: Should contain the MMC controller registers location and length.
|
||||||
|
- interrupts: Should contain the interrupt specifier of the MMC controller.
|
||||||
|
- clocks: Clock for the MMC controller.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- dmas: List of DMA specifiers with the controller specific format
|
||||||
|
as described in the generic DMA client binding. A tx and rx
|
||||||
|
specifier is required.
|
||||||
|
- dma-names: RX and TX DMA request names.
|
||||||
|
Should be "rx" and "tx", in that order.
|
||||||
|
|
||||||
|
For additional details on DMA client bindings see ../dma/dma.txt.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mmc0: mmc@13450000 {
|
||||||
|
compatible = "ingenic,jz4780-mmc";
|
||||||
|
reg = <0x13450000 0x1000>;
|
||||||
|
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
interrupts = <37>;
|
||||||
|
|
||||||
|
clocks = <&cgu JZ4780_CLK_MSC0>;
|
||||||
|
clock-names = "mmc";
|
||||||
|
|
||||||
|
dmas = <&dma JZ4780_DMA_MSC0_RX 0xffffffff>, <&dma JZ4780_DMA_MSC0_TX 0xffffffff>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
};
|
|
@ -19,6 +19,8 @@ Optional properties:
|
||||||
- wp-gpios: Specify GPIOs for write protection, see gpio binding
|
- wp-gpios: Specify GPIOs for write protection, see gpio binding
|
||||||
- cd-inverted: when present, polarity on the CD line is inverted. See the note
|
- cd-inverted: when present, polarity on the CD line is inverted. See the note
|
||||||
below for the case, when a GPIO is used for the CD line
|
below for the case, when a GPIO is used for the CD line
|
||||||
|
- cd-debounce-delay-ms: Set delay time before detecting card after card insert interrupt.
|
||||||
|
It's only valid when cd-gpios is present.
|
||||||
- wp-inverted: when present, polarity on the WP line is inverted. See the note
|
- wp-inverted: when present, polarity on the WP line is inverted. See the note
|
||||||
below for the case, when a GPIO is used for the WP line
|
below for the case, when a GPIO is used for the WP line
|
||||||
- disable-wp: When set no physical WP line is present. This property should
|
- disable-wp: When set no physical WP line is present. This property should
|
||||||
|
@ -56,6 +58,10 @@ Optional properties:
|
||||||
- fixed-emmc-driver-type: for non-removable eMMC, enforce this driver type.
|
- fixed-emmc-driver-type: for non-removable eMMC, enforce this driver type.
|
||||||
The value <n> is the driver type as specified in the eMMC specification
|
The value <n> is the driver type as specified in the eMMC specification
|
||||||
(table 206 in spec version 5.1).
|
(table 206 in spec version 5.1).
|
||||||
|
- post-power-on-delay-ms : It was invented for MMC pwrseq-simple which could
|
||||||
|
be referred to mmc-pwrseq-simple.txt. But now it's reused as a tunable delay
|
||||||
|
waiting for I/O signalling and card power supply to be stable, regardless of
|
||||||
|
whether pwrseq-simple is used. Default to 10ms if no available.
|
||||||
|
|
||||||
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
||||||
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
||||||
|
|
|
@ -4,7 +4,14 @@ Refer to mmc.txt for standard MMC bindings.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
|
- compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
|
||||||
|
Should be "ti,k2g-sdhci" for K2G
|
||||||
- ti,hwmods: Must be "mmc<n>", <n> is controller instance starting 1
|
- ti,hwmods: Must be "mmc<n>", <n> is controller instance starting 1
|
||||||
|
(Not required for K2G).
|
||||||
|
- pinctrl-names: Should be subset of "default", "hs", "sdr12", "sdr25", "sdr50",
|
||||||
|
"ddr50-rev11", "sdr104-rev11", "ddr50", "sdr104",
|
||||||
|
"ddr_1_8v-rev11", "ddr_1_8v" or "ddr_3_3v", "hs200_1_8v-rev11",
|
||||||
|
"hs200_1_8v",
|
||||||
|
- pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
mmc1: mmc@4809c000 {
|
mmc1: mmc@4809c000 {
|
||||||
|
|
|
@ -26,6 +26,8 @@ Required properties:
|
||||||
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
|
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
|
||||||
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
|
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
|
||||||
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
|
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
|
||||||
|
"renesas,sdhi-r8a77965" - SDHI IP on R8A77965 SoC
|
||||||
|
"renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC
|
||||||
"renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC
|
"renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC
|
||||||
"renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller
|
"renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller
|
||||||
"renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller
|
"renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller
|
||||||
|
|
|
@ -36,6 +36,28 @@
|
||||||
clock-frequency = <48000000>;
|
clock-frequency = <48000000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&mmc0 {
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
bus-width = <4>;
|
||||||
|
max-frequency = <50000000>;
|
||||||
|
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&pins_mmc0>;
|
||||||
|
|
||||||
|
cd-gpios = <&gpf 20 GPIO_ACTIVE_LOW>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&mmc1 {
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
bus-width = <4>;
|
||||||
|
max-frequency = <50000000>;
|
||||||
|
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&pins_mmc1>;
|
||||||
|
};
|
||||||
|
|
||||||
&uart0 {
|
&uart0 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
|
@ -203,4 +225,16 @@
|
||||||
groups = "nemc-cs6";
|
groups = "nemc-cs6";
|
||||||
bias-disable;
|
bias-disable;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pins_mmc0: mmc0 {
|
||||||
|
function = "mmc0";
|
||||||
|
groups = "mmc0-1bit-e", "mmc0-4bit-e";
|
||||||
|
bias-disable;
|
||||||
|
};
|
||||||
|
|
||||||
|
pins_mmc1: mmc1 {
|
||||||
|
function = "mmc1";
|
||||||
|
groups = "mmc1-1bit-d", "mmc1-4bit-d";
|
||||||
|
bias-disable;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <dt-bindings/clock/jz4780-cgu.h>
|
#include <dt-bindings/clock/jz4780-cgu.h>
|
||||||
|
#include <dt-bindings/dma/jz4780-dma.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
|
@ -241,6 +242,57 @@
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dma: dma@13420000 {
|
||||||
|
compatible = "ingenic,jz4780-dma";
|
||||||
|
reg = <0x13420000 0x10000>;
|
||||||
|
#dma-cells = <2>;
|
||||||
|
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
interrupts = <10>;
|
||||||
|
|
||||||
|
clocks = <&cgu JZ4780_CLK_PDMA>;
|
||||||
|
};
|
||||||
|
|
||||||
|
mmc0: mmc@13450000 {
|
||||||
|
compatible = "ingenic,jz4780-mmc";
|
||||||
|
reg = <0x13450000 0x1000>;
|
||||||
|
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
interrupts = <37>;
|
||||||
|
|
||||||
|
clocks = <&cgu JZ4780_CLK_MSC0>;
|
||||||
|
clock-names = "mmc";
|
||||||
|
|
||||||
|
cap-sd-highspeed;
|
||||||
|
cap-mmc-highspeed;
|
||||||
|
cap-sdio-irq;
|
||||||
|
dmas = <&dma JZ4780_DMA_MSC0_RX 0xffffffff>,
|
||||||
|
<&dma JZ4780_DMA_MSC0_TX 0xffffffff>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
mmc1: mmc@13460000 {
|
||||||
|
compatible = "ingenic,jz4780-mmc";
|
||||||
|
reg = <0x13460000 0x1000>;
|
||||||
|
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
interrupts = <36>;
|
||||||
|
|
||||||
|
clocks = <&cgu JZ4780_CLK_MSC1>;
|
||||||
|
clock-names = "mmc";
|
||||||
|
|
||||||
|
cap-sd-highspeed;
|
||||||
|
cap-mmc-highspeed;
|
||||||
|
cap-sdio-irq;
|
||||||
|
dmas = <&dma JZ4780_DMA_MSC1_RX 0xffffffff>,
|
||||||
|
<&dma JZ4780_DMA_MSC1_TX 0xffffffff>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
bch: bch@134d0000 {
|
bch: bch@134d0000 {
|
||||||
compatible = "ingenic,jz4780-bch";
|
compatible = "ingenic,jz4780-bch";
|
||||||
reg = <0x134d0000 0x10000>;
|
reg = <0x134d0000 0x10000>;
|
||||||
|
|
|
@ -104,10 +104,14 @@ CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||||
# CONFIG_HID is not set
|
# CONFIG_HID is not set
|
||||||
# CONFIG_USB_SUPPORT is not set
|
# CONFIG_USB_SUPPORT is not set
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_JZ4740=y
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
CONFIG_RTC_DRV_JZ4740=y
|
CONFIG_RTC_DRV_JZ4740=y
|
||||||
|
CONFIG_DMADEVICES=y
|
||||||
|
CONFIG_DMA_JZ4780=y
|
||||||
# CONFIG_IOMMU_SUPPORT is not set
|
# CONFIG_IOMMU_SUPPORT is not set
|
||||||
CONFIG_MEMORY=y
|
CONFIG_MEMORY=y
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_PROC_KCORE=y
|
CONFIG_PROC_KCORE=y
|
||||||
# CONFIG_PROC_PAGE_MONITOR is not set
|
# CONFIG_PROC_PAGE_MONITOR is not set
|
||||||
|
|
|
@ -2351,7 +2351,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||||
set_disk_ro(md->disk, md->read_only || default_ro);
|
set_disk_ro(md->disk, md->read_only || default_ro);
|
||||||
md->disk->flags = GENHD_FL_EXT_DEVT;
|
md->disk->flags = GENHD_FL_EXT_DEVT;
|
||||||
if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
|
if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
|
||||||
md->disk->flags |= GENHD_FL_NO_PART_SCAN;
|
md->disk->flags |= GENHD_FL_NO_PART_SCAN
|
||||||
|
| GENHD_FL_SUPPRESS_PARTITION_INFO;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As discussed on lkml, GENHD_FL_REMOVABLE should:
|
* As discussed on lkml, GENHD_FL_REMOVABLE should:
|
||||||
|
@ -2965,9 +2966,11 @@ static void mmc_blk_remove(struct mmc_card *card)
|
||||||
mmc_blk_remove_debugfs(card, md);
|
mmc_blk_remove_debugfs(card, md);
|
||||||
mmc_blk_remove_parts(card, md);
|
mmc_blk_remove_parts(card, md);
|
||||||
pm_runtime_get_sync(&card->dev);
|
pm_runtime_get_sync(&card->dev);
|
||||||
|
if (md->part_curr != md->part_type) {
|
||||||
mmc_claim_host(card->host);
|
mmc_claim_host(card->host);
|
||||||
mmc_blk_part_switch(card, md->part_type);
|
mmc_blk_part_switch(card, md->part_type);
|
||||||
mmc_release_host(card->host);
|
mmc_release_host(card->host);
|
||||||
|
}
|
||||||
if (card->type != MMC_TYPE_SD_COMBO)
|
if (card->type != MMC_TYPE_SD_COMBO)
|
||||||
pm_runtime_disable(&card->dev);
|
pm_runtime_disable(&card->dev);
|
||||||
pm_runtime_put_noidle(&card->dev);
|
pm_runtime_put_noidle(&card->dev);
|
||||||
|
|
|
@ -149,6 +149,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||||
card->quirks &= ~data;
|
card->quirks &= ~data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void __maybe_unused add_limit_rate_quirk(struct mmc_card *card,
|
||||||
|
int data)
|
||||||
|
{
|
||||||
|
card->quirk_max_rate = data;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Quirk add/remove for MMC products.
|
* Quirk add/remove for MMC products.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -50,9 +50,6 @@
|
||||||
#include "sd_ops.h"
|
#include "sd_ops.h"
|
||||||
#include "sdio_ops.h"
|
#include "sdio_ops.h"
|
||||||
|
|
||||||
/* If the device is not responding */
|
|
||||||
#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
|
||||||
|
|
||||||
/* The max erase timeout, used when host->max_busy_timeout isn't specified */
|
/* The max erase timeout, used when host->max_busy_timeout isn't specified */
|
||||||
#define MMC_ERASE_TIMEOUT_MS (60 * 1000) /* 60 s */
|
#define MMC_ERASE_TIMEOUT_MS (60 * 1000) /* 60 s */
|
||||||
|
|
||||||
|
@ -1484,6 +1481,17 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mmc_set_initial_signal_voltage(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
|
||||||
|
if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330))
|
||||||
|
dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");
|
||||||
|
else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))
|
||||||
|
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");
|
||||||
|
else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))
|
||||||
|
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
|
||||||
|
}
|
||||||
|
|
||||||
int mmc_host_set_uhs_voltage(struct mmc_host *host)
|
int mmc_host_set_uhs_voltage(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
u32 clock;
|
u32 clock;
|
||||||
|
@ -1646,19 +1654,13 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
||||||
/* Set initial state and call mmc_set_ios */
|
/* Set initial state and call mmc_set_ios */
|
||||||
mmc_set_initial_state(host);
|
mmc_set_initial_state(host);
|
||||||
|
|
||||||
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
|
mmc_set_initial_signal_voltage(host);
|
||||||
if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330))
|
|
||||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");
|
|
||||||
else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))
|
|
||||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");
|
|
||||||
else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))
|
|
||||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This delay should be sufficient to allow the power supply
|
* This delay should be sufficient to allow the power supply
|
||||||
* to reach the minimum voltage.
|
* to reach the minimum voltage.
|
||||||
*/
|
*/
|
||||||
mmc_delay(10);
|
mmc_delay(host->ios.power_delay_ms);
|
||||||
|
|
||||||
mmc_pwrseq_post_power_on(host);
|
mmc_pwrseq_post_power_on(host);
|
||||||
|
|
||||||
|
@ -1671,7 +1673,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
||||||
* This delay must be at least 74 clock sizes, or 1 ms, or the
|
* This delay must be at least 74 clock sizes, or 1 ms, or the
|
||||||
* time required to reach a stable voltage.
|
* time required to reach a stable voltage.
|
||||||
*/
|
*/
|
||||||
mmc_delay(10);
|
mmc_delay(host->ios.power_delay_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mmc_power_off(struct mmc_host *host)
|
void mmc_power_off(struct mmc_host *host)
|
||||||
|
@ -1967,6 +1969,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
unsigned int qty = 0, busy_timeout = 0;
|
unsigned int qty = 0, busy_timeout = 0;
|
||||||
bool use_r1b_resp = false;
|
bool use_r1b_resp = false;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
int loop_udelay=64, udelay_max=32768;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mmc_retune_hold(card->host);
|
mmc_retune_hold(card->host);
|
||||||
|
@ -2091,9 +2094,15 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((cmd.resp[0] & R1_READY_FOR_DATA) &&
|
||||||
|
R1_CURRENT_STATE(cmd.resp[0]) != R1_STATE_PRG)
|
||||||
|
break;
|
||||||
|
|
||||||
|
usleep_range(loop_udelay, loop_udelay*2);
|
||||||
|
if (loop_udelay < udelay_max)
|
||||||
|
loop_udelay *= 2;
|
||||||
|
} while (1);
|
||||||
|
|
||||||
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
|
|
||||||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
|
|
||||||
out:
|
out:
|
||||||
mmc_retune_release(card->host);
|
mmc_retune_release(card->host);
|
||||||
return err;
|
return err;
|
||||||
|
@ -2435,22 +2444,46 @@ int mmc_hw_reset(struct mmc_host *host)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mmc_bus_get(host);
|
mmc_bus_get(host);
|
||||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->reset) {
|
if (!host->bus_ops || host->bus_dead || !host->bus_ops->hw_reset) {
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = host->bus_ops->reset(host);
|
ret = host->bus_ops->hw_reset(host);
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_warn("%s: tried to reset card, got error %d\n",
|
pr_warn("%s: tried to HW reset card, got error %d\n",
|
||||||
mmc_hostname(host), ret);
|
mmc_hostname(host), ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_hw_reset);
|
EXPORT_SYMBOL(mmc_hw_reset);
|
||||||
|
|
||||||
|
int mmc_sw_reset(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!host->card)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mmc_bus_get(host);
|
||||||
|
if (!host->bus_ops || host->bus_dead || !host->bus_ops->sw_reset) {
|
||||||
|
mmc_bus_put(host);
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = host->bus_ops->sw_reset(host);
|
||||||
|
mmc_bus_put(host);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
pr_warn("%s: tried to SW reset card, got error %d\n",
|
||||||
|
mmc_hostname(host), ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_sw_reset);
|
||||||
|
|
||||||
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||||
{
|
{
|
||||||
host->f_init = freq;
|
host->f_init = freq;
|
||||||
|
|
|
@ -32,7 +32,8 @@ struct mmc_bus_ops {
|
||||||
int (*power_restore)(struct mmc_host *);
|
int (*power_restore)(struct mmc_host *);
|
||||||
int (*alive)(struct mmc_host *);
|
int (*alive)(struct mmc_host *);
|
||||||
int (*shutdown)(struct mmc_host *);
|
int (*shutdown)(struct mmc_host *);
|
||||||
int (*reset)(struct mmc_host *);
|
int (*hw_reset)(struct mmc_host *);
|
||||||
|
int (*sw_reset)(struct mmc_host *);
|
||||||
};
|
};
|
||||||
|
|
||||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||||
|
@ -51,6 +52,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
|
||||||
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr);
|
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr);
|
||||||
int mmc_host_set_uhs_voltage(struct mmc_host *host);
|
int mmc_host_set_uhs_voltage(struct mmc_host *host);
|
||||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||||
|
void mmc_set_initial_signal_voltage(struct mmc_host *host);
|
||||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||||
int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
|
int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
|
||||||
|
|
|
@ -143,9 +143,6 @@ int mmc_retune(struct mmc_host *host)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
return_to_hs400 = true;
|
return_to_hs400 = true;
|
||||||
|
|
||||||
if (host->ops->prepare_hs400_tuning)
|
|
||||||
host->ops->prepare_hs400_tuning(host, &host->ios);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mmc_execute_tuning(host->card);
|
err = mmc_execute_tuning(host->card);
|
||||||
|
@ -179,7 +176,7 @@ static void mmc_retune_timer(struct timer_list *t)
|
||||||
int mmc_of_parse(struct mmc_host *host)
|
int mmc_of_parse(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
struct device *dev = host->parent;
|
struct device *dev = host->parent;
|
||||||
u32 bus_width, drv_type;
|
u32 bus_width, drv_type, cd_debounce_delay_ms;
|
||||||
int ret;
|
int ret;
|
||||||
bool cd_cap_invert, cd_gpio_invert = false;
|
bool cd_cap_invert, cd_gpio_invert = false;
|
||||||
bool ro_cap_invert, ro_gpio_invert = false;
|
bool ro_cap_invert, ro_gpio_invert = false;
|
||||||
|
@ -230,11 +227,16 @@ int mmc_of_parse(struct mmc_host *host)
|
||||||
} else {
|
} else {
|
||||||
cd_cap_invert = device_property_read_bool(dev, "cd-inverted");
|
cd_cap_invert = device_property_read_bool(dev, "cd-inverted");
|
||||||
|
|
||||||
|
if (device_property_read_u32(dev, "cd-debounce-delay-ms",
|
||||||
|
&cd_debounce_delay_ms))
|
||||||
|
cd_debounce_delay_ms = 200;
|
||||||
|
|
||||||
if (device_property_read_bool(dev, "broken-cd"))
|
if (device_property_read_bool(dev, "broken-cd"))
|
||||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||||
|
|
||||||
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
|
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
|
||||||
0, &cd_gpio_invert);
|
cd_debounce_delay_ms,
|
||||||
|
&cd_gpio_invert);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
dev_info(host->parent, "Got CD GPIO\n");
|
dev_info(host->parent, "Got CD GPIO\n");
|
||||||
else if (ret != -ENOENT && ret != -ENOSYS)
|
else if (ret != -ENOENT && ret != -ENOSYS)
|
||||||
|
@ -338,6 +340,9 @@ int mmc_of_parse(struct mmc_host *host)
|
||||||
host->dsr_req = 0;
|
host->dsr_req = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device_property_read_u32(dev, "post-power-on-delay-ms",
|
||||||
|
&host->ios.power_delay_ms);
|
||||||
|
|
||||||
return mmc_pwrseq_alloc(host);
|
return mmc_pwrseq_alloc(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,6 +408,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
host->max_blk_count = PAGE_SIZE / 512;
|
host->max_blk_count = PAGE_SIZE / 512;
|
||||||
|
|
||||||
host->fixed_drv_type = -EINVAL;
|
host->fixed_drv_type = -EINVAL;
|
||||||
|
host->ios.power_delay_ms = 10;
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1282,6 +1282,10 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||||
|
|
||||||
mmc_set_bus_speed(card);
|
mmc_set_bus_speed(card);
|
||||||
|
|
||||||
|
/* Prepare tuning for HS400 mode. */
|
||||||
|
if (host->ops->prepare_hs400_tuning)
|
||||||
|
host->ops->prepare_hs400_tuning(host, &host->ios);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_err:
|
out_err:
|
||||||
|
@ -1830,6 +1834,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
|
||||||
|
host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||||
|
pr_err("%s: Host failed to negotiate down from 3.3V\n",
|
||||||
|
mmc_hostname(host));
|
||||||
|
err = -EINVAL;
|
||||||
|
goto free_card;
|
||||||
|
}
|
||||||
|
|
||||||
if (!oldcard)
|
if (!oldcard)
|
||||||
host->card = card;
|
host->card = card;
|
||||||
|
|
||||||
|
@ -2117,7 +2129,7 @@ static int mmc_can_reset(struct mmc_card *card)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_reset(struct mmc_host *host)
|
static int _mmc_hw_reset(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_card *card = host->card;
|
struct mmc_card *card = host->card;
|
||||||
|
|
||||||
|
@ -2151,7 +2163,7 @@ static const struct mmc_bus_ops mmc_ops = {
|
||||||
.runtime_resume = mmc_runtime_resume,
|
.runtime_resume = mmc_runtime_resume,
|
||||||
.alive = mmc_alive,
|
.alive = mmc_alive,
|
||||||
.shutdown = mmc_shutdown,
|
.shutdown = mmc_shutdown,
|
||||||
.reset = mmc_reset,
|
.hw_reset = _mmc_hw_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -40,14 +40,18 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
|
||||||
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
|
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
|
||||||
|
|
||||||
if (!IS_ERR(reset_gpios)) {
|
if (!IS_ERR(reset_gpios)) {
|
||||||
int i;
|
int i, *values;
|
||||||
int values[reset_gpios->ndescs];
|
int nvalues = reset_gpios->ndescs;
|
||||||
|
|
||||||
for (i = 0; i < reset_gpios->ndescs; i++)
|
values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
|
||||||
|
if (!values)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < nvalues; i++)
|
||||||
values[i] = value;
|
values[i] = value;
|
||||||
|
|
||||||
gpiod_set_array_value_cansleep(
|
gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
|
||||||
reset_gpios->ndescs, reset_gpios->desc, values);
|
kfree(values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,9 @@ static const struct mmc_fixup sdio_fixup_methods[] = {
|
||||||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
||||||
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
||||||
|
|
||||||
|
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887WLAN,
|
||||||
|
add_limit_rate_quirk, 150000000),
|
||||||
|
|
||||||
END_FIXUP
|
END_FIXUP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1058,6 +1058,14 @@ retry:
|
||||||
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
|
||||||
|
host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||||
|
pr_err("%s: Host failed to negotiate down from 3.3V\n",
|
||||||
|
mmc_hostname(host));
|
||||||
|
err = -EINVAL;
|
||||||
|
goto free_card;
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
host->card = card;
|
host->card = card;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1214,7 +1222,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_sd_reset(struct mmc_host *host)
|
static int mmc_sd_hw_reset(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
mmc_power_cycle(host, host->card->ocr);
|
mmc_power_cycle(host, host->card->ocr);
|
||||||
return mmc_sd_init_card(host, host->card->ocr, host->card);
|
return mmc_sd_init_card(host, host->card->ocr, host->card);
|
||||||
|
@ -1229,7 +1237,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
||||||
.resume = mmc_sd_resume,
|
.resume = mmc_sd_resume,
|
||||||
.alive = mmc_sd_alive,
|
.alive = mmc_sd_alive,
|
||||||
.shutdown = mmc_sd_suspend,
|
.shutdown = mmc_sd_suspend,
|
||||||
.reset = mmc_sd_reset,
|
.hw_reset = mmc_sd_hw_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -444,6 +444,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
|
||||||
unsigned int bus_speed, timing;
|
unsigned int bus_speed, timing;
|
||||||
int err;
|
int err;
|
||||||
unsigned char speed;
|
unsigned char speed;
|
||||||
|
unsigned int max_rate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the host doesn't support any of the UHS-I modes, fallback on
|
* If the host doesn't support any of the UHS-I modes, fallback on
|
||||||
|
@ -500,9 +501,12 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
max_rate = min_not_zero(card->quirk_max_rate,
|
||||||
|
card->sw_caps.uhs_max_dtr);
|
||||||
|
|
||||||
if (bus_speed) {
|
if (bus_speed) {
|
||||||
mmc_set_timing(card->host, timing);
|
mmc_set_timing(card->host, timing);
|
||||||
mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
|
mmc_set_clock(card->host, max_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -788,6 +792,14 @@ try_again:
|
||||||
if (err)
|
if (err)
|
||||||
goto remove;
|
goto remove;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
|
||||||
|
host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||||
|
pr_err("%s: Host failed to negotiate down from 3.3V\n",
|
||||||
|
mmc_hostname(host));
|
||||||
|
err = -EINVAL;
|
||||||
|
goto remove;
|
||||||
|
}
|
||||||
finish:
|
finish:
|
||||||
if (!oldcard)
|
if (!oldcard)
|
||||||
host->card = card;
|
host->card = card;
|
||||||
|
@ -801,6 +813,22 @@ err:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_sdio_reinit_card(struct mmc_host *host, bool powered_resume)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sdio_reset(host);
|
||||||
|
mmc_go_idle(host);
|
||||||
|
mmc_send_if_cond(host, host->card->ocr);
|
||||||
|
|
||||||
|
ret = mmc_send_io_op_cond(host, 0, NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return mmc_sdio_init_card(host, host->card->ocr, host->card,
|
||||||
|
powered_resume);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Host is being removed. Free up the current card.
|
* Host is being removed. Free up the current card.
|
||||||
*/
|
*/
|
||||||
|
@ -948,14 +976,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||||
|
|
||||||
/* No need to reinitialize powered-resumed nonremovable cards */
|
/* No need to reinitialize powered-resumed nonremovable cards */
|
||||||
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
|
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
|
||||||
sdio_reset(host);
|
err = mmc_sdio_reinit_card(host, mmc_card_keep_power(host));
|
||||||
mmc_go_idle(host);
|
|
||||||
mmc_send_if_cond(host, host->card->ocr);
|
|
||||||
err = mmc_send_io_op_cond(host, 0, NULL);
|
|
||||||
if (!err)
|
|
||||||
err = mmc_sdio_init_card(host, host->card->ocr,
|
|
||||||
host->card,
|
|
||||||
mmc_card_keep_power(host));
|
|
||||||
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||||
/* We may have switched to 1-bit mode during suspend */
|
/* We may have switched to 1-bit mode during suspend */
|
||||||
err = sdio_enable_4bit_bus(host->card);
|
err = sdio_enable_4bit_bus(host->card);
|
||||||
|
@ -978,8 +999,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mmc_claim_host(host);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset the card by performing the same steps that are taken by
|
* Reset the card by performing the same steps that are taken by
|
||||||
* mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
|
* mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
|
||||||
|
@ -997,20 +1016,12 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sdio_reset(host);
|
mmc_claim_host(host);
|
||||||
mmc_go_idle(host);
|
|
||||||
mmc_send_if_cond(host, host->card->ocr);
|
|
||||||
|
|
||||||
ret = mmc_send_io_op_cond(host, 0, NULL);
|
ret = mmc_sdio_reinit_card(host, mmc_card_keep_power(host));
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
ret = mmc_sdio_init_card(host, host->card->ocr, host->card,
|
|
||||||
mmc_card_keep_power(host));
|
|
||||||
if (!ret && host->sdio_irqs)
|
if (!ret && host->sdio_irqs)
|
||||||
mmc_signal_sdio_irq(host);
|
mmc_signal_sdio_irq(host);
|
||||||
|
|
||||||
out:
|
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1039,12 +1050,24 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_sdio_reset(struct mmc_host *host)
|
static int mmc_sdio_hw_reset(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
mmc_power_cycle(host, host->card->ocr);
|
mmc_power_cycle(host, host->card->ocr);
|
||||||
return mmc_sdio_power_restore(host);
|
return mmc_sdio_power_restore(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_sdio_sw_reset(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
mmc_set_clock(host, host->f_init);
|
||||||
|
sdio_reset(host);
|
||||||
|
mmc_go_idle(host);
|
||||||
|
|
||||||
|
mmc_set_initial_state(host);
|
||||||
|
mmc_set_initial_signal_voltage(host);
|
||||||
|
|
||||||
|
return mmc_sdio_reinit_card(host, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_bus_ops mmc_sdio_ops = {
|
static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||||
.remove = mmc_sdio_remove,
|
.remove = mmc_sdio_remove,
|
||||||
.detect = mmc_sdio_detect,
|
.detect = mmc_sdio_detect,
|
||||||
|
@ -1055,7 +1078,8 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||||
.runtime_resume = mmc_sdio_runtime_resume,
|
.runtime_resume = mmc_sdio_runtime_resume,
|
||||||
.power_restore = mmc_sdio_power_restore,
|
.power_restore = mmc_sdio_power_restore,
|
||||||
.alive = mmc_sdio_alive,
|
.alive = mmc_sdio_alive,
|
||||||
.reset = mmc_sdio_reset,
|
.hw_reset = mmc_sdio_hw_reset,
|
||||||
|
.sw_reset = mmc_sdio_sw_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,17 @@ struct mmc_gpio {
|
||||||
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||||
char *ro_label;
|
char *ro_label;
|
||||||
char cd_label[0];
|
char cd_label[0];
|
||||||
|
u32 cd_debounce_delay_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
/* Schedule a card detection after a debounce timeout */
|
/* Schedule a card detection after a debounce timeout */
|
||||||
struct mmc_host *host = dev_id;
|
struct mmc_host *host = dev_id;
|
||||||
|
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||||
|
|
||||||
host->trigger_card_event = true;
|
host->trigger_card_event = true;
|
||||||
mmc_detect_change(host, msecs_to_jiffies(200));
|
mmc_detect_change(host, msecs_to_jiffies(ctx->cd_debounce_delay_ms));
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +51,7 @@ int mmc_gpio_alloc(struct mmc_host *host)
|
||||||
|
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
ctx->ro_label = ctx->cd_label + len;
|
ctx->ro_label = ctx->cd_label + len;
|
||||||
|
ctx->cd_debounce_delay_ms = 200;
|
||||||
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
||||||
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
||||||
host->slot.handler_priv = ctx;
|
host->slot.handler_priv = ctx;
|
||||||
|
@ -76,15 +79,22 @@ EXPORT_SYMBOL(mmc_gpio_get_ro);
|
||||||
int mmc_gpio_get_cd(struct mmc_host *host)
|
int mmc_gpio_get_cd(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||||
|
int cansleep;
|
||||||
|
|
||||||
if (!ctx || !ctx->cd_gpio)
|
if (!ctx || !ctx->cd_gpio)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
|
||||||
if (ctx->override_cd_active_level)
|
cansleep = gpiod_cansleep(ctx->cd_gpio);
|
||||||
return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^
|
if (ctx->override_cd_active_level) {
|
||||||
!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
|
int value = cansleep ?
|
||||||
|
gpiod_get_raw_value_cansleep(ctx->cd_gpio) :
|
||||||
|
gpiod_get_raw_value(ctx->cd_gpio);
|
||||||
|
return !value ^ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
return gpiod_get_value_cansleep(ctx->cd_gpio);
|
return cansleep ?
|
||||||
|
gpiod_get_value_cansleep(ctx->cd_gpio) :
|
||||||
|
gpiod_get_value(ctx->cd_gpio);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_gpio_get_cd);
|
EXPORT_SYMBOL(mmc_gpio_get_cd);
|
||||||
|
|
||||||
|
@ -261,7 +271,7 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||||
if (debounce) {
|
if (debounce) {
|
||||||
ret = gpiod_set_debounce(desc, debounce);
|
ret = gpiod_set_debounce(desc, debounce);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
ctx->cd_debounce_delay_ms = debounce;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpio_invert)
|
if (gpio_invert)
|
||||||
|
|
|
@ -345,11 +345,11 @@ config MMC_SDHCI_IPROC
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
config MMC_MESON_GX
|
config MMC_MESON_GX
|
||||||
tristate "Amlogic S905/GX* SD/MMC Host Controller support"
|
tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support"
|
||||||
depends on ARCH_MESON && MMC
|
depends on ARCH_MESON && MMC
|
||||||
help
|
help
|
||||||
This selects support for the Amlogic SD/MMC Host Controller
|
This selects support for the Amlogic SD/MMC Host Controller
|
||||||
found on the S905/GX* family of SoCs. This controller is
|
found on the S905/GX*/AXG family of SoCs. This controller is
|
||||||
MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces.
|
MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces.
|
||||||
|
|
||||||
If you have a controller with this interface, say Y here.
|
If you have a controller with this interface, say Y here.
|
||||||
|
@ -358,7 +358,6 @@ config MMC_MESON_MX_SDIO
|
||||||
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
|
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
|
||||||
depends on ARCH_MESON || COMPILE_TEST
|
depends on ARCH_MESON || COMPILE_TEST
|
||||||
depends on COMMON_CLK
|
depends on COMMON_CLK
|
||||||
depends on HAS_DMA
|
|
||||||
depends on OF
|
depends on OF
|
||||||
help
|
help
|
||||||
This selects support for the SD/MMC Host Controller on
|
This selects support for the SD/MMC Host Controller on
|
||||||
|
@ -401,7 +400,6 @@ config MMC_OMAP
|
||||||
|
|
||||||
config MMC_OMAP_HS
|
config MMC_OMAP_HS
|
||||||
tristate "TI OMAP High Speed Multimedia Card Interface support"
|
tristate "TI OMAP High Speed Multimedia Card Interface support"
|
||||||
depends on HAS_DMA
|
|
||||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the TI OMAP High Speed Multimedia card Interface.
|
This selects the TI OMAP High Speed Multimedia card Interface.
|
||||||
|
@ -511,7 +509,6 @@ config MMC_DAVINCI
|
||||||
|
|
||||||
config MMC_GOLDFISH
|
config MMC_GOLDFISH
|
||||||
tristate "goldfish qemu Multimedia Card Interface support"
|
tristate "goldfish qemu Multimedia Card Interface support"
|
||||||
depends on HAS_DMA
|
|
||||||
depends on GOLDFISH || COMPILE_TEST
|
depends on GOLDFISH || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the Goldfish Multimedia card Interface emulation
|
This selects the Goldfish Multimedia card Interface emulation
|
||||||
|
@ -605,7 +602,7 @@ config MMC_SDHI
|
||||||
|
|
||||||
config MMC_SDHI_SYS_DMAC
|
config MMC_SDHI_SYS_DMAC
|
||||||
tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC"
|
tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC"
|
||||||
depends on MMC_SDHI && HAS_DMA
|
depends on MMC_SDHI
|
||||||
default MMC_SDHI if (SUPERH || ARM)
|
default MMC_SDHI if (SUPERH || ARM)
|
||||||
help
|
help
|
||||||
This provides DMA support for SDHI SD/SDIO controllers
|
This provides DMA support for SDHI SD/SDIO controllers
|
||||||
|
@ -615,7 +612,7 @@ config MMC_SDHI_SYS_DMAC
|
||||||
config MMC_SDHI_INTERNAL_DMAC
|
config MMC_SDHI_INTERNAL_DMAC
|
||||||
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
|
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
|
||||||
depends on ARM64 || COMPILE_TEST
|
depends on ARM64 || COMPILE_TEST
|
||||||
depends on MMC_SDHI && HAS_DMA
|
depends on MMC_SDHI
|
||||||
default MMC_SDHI if ARM64
|
default MMC_SDHI if ARM64
|
||||||
help
|
help
|
||||||
This provides DMA support for SDHI SD/SDIO controllers
|
This provides DMA support for SDHI SD/SDIO controllers
|
||||||
|
@ -669,7 +666,6 @@ config MMC_CAVIUM_THUNDERX
|
||||||
|
|
||||||
config MMC_DW
|
config MMC_DW
|
||||||
tristate "Synopsys DesignWare Memory Card Interface"
|
tristate "Synopsys DesignWare Memory Card Interface"
|
||||||
depends on HAS_DMA
|
|
||||||
depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
|
depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects support for the Synopsys DesignWare Mobile Storage IP
|
This selects support for the Synopsys DesignWare Mobile Storage IP
|
||||||
|
@ -690,6 +686,15 @@ config MMC_DW_PLTFM
|
||||||
|
|
||||||
If unsure, say Y.
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config MMC_DW_BLUEFIELD
|
||||||
|
tristate "BlueField specific extensions for Synopsys DW Memory Card Interface"
|
||||||
|
depends on MMC_DW
|
||||||
|
select MMC_DW_PLTFM
|
||||||
|
help
|
||||||
|
This selects support for Mellanox BlueField SoC specific extensions to
|
||||||
|
the Synopsys DesignWare Memory Card Interface driver. Select this
|
||||||
|
option for platforms based on Mellanox BlueField SoC's.
|
||||||
|
|
||||||
config MMC_DW_EXYNOS
|
config MMC_DW_EXYNOS
|
||||||
tristate "Exynos specific extensions for Synopsys DW Memory Card Interface"
|
tristate "Exynos specific extensions for Synopsys DW Memory Card Interface"
|
||||||
depends on MMC_DW
|
depends on MMC_DW
|
||||||
|
@ -748,7 +753,6 @@ config MMC_DW_ZX
|
||||||
|
|
||||||
config MMC_SH_MMCIF
|
config MMC_SH_MMCIF
|
||||||
tristate "SuperH Internal MMCIF support"
|
tristate "SuperH Internal MMCIF support"
|
||||||
depends on HAS_DMA
|
|
||||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the MMC Host Interface controller (MMCIF) found in various
|
This selects the MMC Host Interface controller (MMCIF) found in various
|
||||||
|
@ -756,11 +760,12 @@ config MMC_SH_MMCIF
|
||||||
|
|
||||||
|
|
||||||
config MMC_JZ4740
|
config MMC_JZ4740
|
||||||
tristate "JZ4740 SD/Multimedia Card Interface support"
|
tristate "Ingenic JZ47xx SD/Multimedia Card Interface support"
|
||||||
depends on MACH_JZ4740
|
depends on MACH_JZ4740 || MACH_JZ4780
|
||||||
help
|
help
|
||||||
This selects support for the SD/MMC controller on Ingenic JZ4740
|
This selects support for the SD/MMC controller on Ingenic
|
||||||
SoCs.
|
JZ4740, JZ4750, JZ4770 and JZ4780 SoCs.
|
||||||
|
|
||||||
If you have a board based on such a SoC and with a SD/MMC slot,
|
If you have a board based on such a SoC and with a SD/MMC slot,
|
||||||
say Y or M here.
|
say Y or M here.
|
||||||
|
|
||||||
|
@ -868,7 +873,6 @@ config MMC_TOSHIBA_PCI
|
||||||
config MMC_BCM2835
|
config MMC_BCM2835
|
||||||
tristate "Broadcom BCM2835 SDHOST MMC Controller support"
|
tristate "Broadcom BCM2835 SDHOST MMC Controller support"
|
||||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||||
depends on HAS_DMA
|
|
||||||
help
|
help
|
||||||
This selects the BCM2835 SDHOST MMC controller. If you have
|
This selects the BCM2835 SDHOST MMC controller. If you have
|
||||||
a BCM2835 platform with SD or MMC devices, say Y or M here.
|
a BCM2835 platform with SD or MMC devices, say Y or M here.
|
||||||
|
|
|
@ -49,6 +49,7 @@ thunderx-mmc-objs := cavium.o cavium-thunderx.o
|
||||||
obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
|
obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
|
||||||
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
||||||
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
||||||
|
obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o
|
||||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
||||||
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
|
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
|
||||||
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
|
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
|
||||||
|
|
|
@ -217,8 +217,8 @@ static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
|
||||||
* We don't really have DMA, so we need
|
* We don't really have DMA, so we need
|
||||||
* to copy from our platform driver buffer
|
* to copy from our platform driver buffer
|
||||||
*/
|
*/
|
||||||
uint8_t *dest = (uint8_t *)sg_virt(data->sg);
|
sg_copy_to_buffer(data->sg, 1, host->virt_base,
|
||||||
memcpy(dest, host->virt_base, data->sg->length);
|
data->sg->length);
|
||||||
}
|
}
|
||||||
host->data->bytes_xfered += data->sg->length;
|
host->data->bytes_xfered += data->sg->length;
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
|
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
|
||||||
|
@ -393,8 +393,8 @@ static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
|
||||||
* We don't really have DMA, so we need to copy to our
|
* We don't really have DMA, so we need to copy to our
|
||||||
* platform driver buffer
|
* platform driver buffer
|
||||||
*/
|
*/
|
||||||
const uint8_t *src = (uint8_t *)sg_virt(data->sg);
|
sg_copy_from_buffer(data->sg, 1, host->virt_base,
|
||||||
memcpy(host->virt_base, src, data->sg->length);
|
data->sg->length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1967,7 +1967,6 @@ static void atmci_tasklet_func(unsigned long priv)
|
||||||
static void atmci_read_data_pio(struct atmel_mci *host)
|
static void atmci_read_data_pio(struct atmel_mci *host)
|
||||||
{
|
{
|
||||||
struct scatterlist *sg = host->sg;
|
struct scatterlist *sg = host->sg;
|
||||||
void *buf = sg_virt(sg);
|
|
||||||
unsigned int offset = host->pio_offset;
|
unsigned int offset = host->pio_offset;
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
@ -1977,7 +1976,7 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
||||||
do {
|
do {
|
||||||
value = atmci_readl(host, ATMCI_RDR);
|
value = atmci_readl(host, ATMCI_RDR);
|
||||||
if (likely(offset + 4 <= sg->length)) {
|
if (likely(offset + 4 <= sg->length)) {
|
||||||
put_unaligned(value, (u32 *)(buf + offset));
|
sg_pcopy_to_buffer(sg, 1, &value, sizeof(u32), offset);
|
||||||
|
|
||||||
offset += 4;
|
offset += 4;
|
||||||
nbytes += 4;
|
nbytes += 4;
|
||||||
|
@ -1990,11 +1989,11 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
offset = 0;
|
offset = 0;
|
||||||
buf = sg_virt(sg);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unsigned int remaining = sg->length - offset;
|
unsigned int remaining = sg->length - offset;
|
||||||
memcpy(buf + offset, &value, remaining);
|
|
||||||
|
sg_pcopy_to_buffer(sg, 1, &value, remaining, offset);
|
||||||
nbytes += remaining;
|
nbytes += remaining;
|
||||||
|
|
||||||
flush_dcache_page(sg_page(sg));
|
flush_dcache_page(sg_page(sg));
|
||||||
|
@ -2004,8 +2003,8 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
offset = 4 - remaining;
|
offset = 4 - remaining;
|
||||||
buf = sg_virt(sg);
|
sg_pcopy_to_buffer(sg, 1, (u8 *)&value + remaining,
|
||||||
memcpy(buf, (u8 *)&value + remaining, offset);
|
offset, 0);
|
||||||
nbytes += offset;
|
nbytes += offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2035,7 +2034,6 @@ done:
|
||||||
static void atmci_write_data_pio(struct atmel_mci *host)
|
static void atmci_write_data_pio(struct atmel_mci *host)
|
||||||
{
|
{
|
||||||
struct scatterlist *sg = host->sg;
|
struct scatterlist *sg = host->sg;
|
||||||
void *buf = sg_virt(sg);
|
|
||||||
unsigned int offset = host->pio_offset;
|
unsigned int offset = host->pio_offset;
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
@ -2044,7 +2042,7 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (likely(offset + 4 <= sg->length)) {
|
if (likely(offset + 4 <= sg->length)) {
|
||||||
value = get_unaligned((u32 *)(buf + offset));
|
sg_pcopy_from_buffer(sg, 1, &value, sizeof(u32), offset);
|
||||||
atmci_writel(host, ATMCI_TDR, value);
|
atmci_writel(host, ATMCI_TDR, value);
|
||||||
|
|
||||||
offset += 4;
|
offset += 4;
|
||||||
|
@ -2056,13 +2054,12 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
offset = 0;
|
offset = 0;
|
||||||
buf = sg_virt(sg);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unsigned int remaining = sg->length - offset;
|
unsigned int remaining = sg->length - offset;
|
||||||
|
|
||||||
value = 0;
|
value = 0;
|
||||||
memcpy(&value, buf + offset, remaining);
|
sg_pcopy_from_buffer(sg, 1, &value, remaining, offset);
|
||||||
nbytes += remaining;
|
nbytes += remaining;
|
||||||
|
|
||||||
host->sg = sg = sg_next(sg);
|
host->sg = sg = sg_next(sg);
|
||||||
|
@ -2073,8 +2070,8 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = 4 - remaining;
|
offset = 4 - remaining;
|
||||||
buf = sg_virt(sg);
|
sg_pcopy_from_buffer(sg, 1, (u8 *)&value + remaining,
|
||||||
memcpy((u8 *)&value + remaining, buf, offset);
|
offset, 0);
|
||||||
atmci_writel(host, ATMCI_TDR, value);
|
atmci_writel(host, ATMCI_TDR, value);
|
||||||
nbytes += offset;
|
nbytes += offset;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -405,7 +406,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
|
||||||
|
|
||||||
/* This is the pointer to the data buffer */
|
/* This is the pointer to the data buffer */
|
||||||
sg = &data->sg[host->pio.index];
|
sg = &data->sg[host->pio.index];
|
||||||
sg_ptr = sg_virt(sg) + host->pio.offset;
|
sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset;
|
||||||
|
|
||||||
/* This is the space left inside the buffer */
|
/* This is the space left inside the buffer */
|
||||||
sg_len = data->sg[host->pio.index].length - host->pio.offset;
|
sg_len = data->sg[host->pio.index].length - host->pio.offset;
|
||||||
|
@ -421,11 +422,12 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
|
||||||
if (!(status & SD_STATUS_TH))
|
if (!(status & SD_STATUS_TH))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
val = *sg_ptr++;
|
val = sg_ptr[count];
|
||||||
|
|
||||||
__raw_writel((unsigned long)val, HOST_TXPORT(host));
|
__raw_writel((unsigned long)val, HOST_TXPORT(host));
|
||||||
wmb(); /* drain writebuffer */
|
wmb(); /* drain writebuffer */
|
||||||
}
|
}
|
||||||
|
kunmap_atomic(sg_ptr);
|
||||||
|
|
||||||
host->pio.len -= count;
|
host->pio.len -= count;
|
||||||
host->pio.offset += count;
|
host->pio.offset += count;
|
||||||
|
@ -462,7 +464,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
|
||||||
|
|
||||||
if (host->pio.index < host->dma.len) {
|
if (host->pio.index < host->dma.len) {
|
||||||
sg = &data->sg[host->pio.index];
|
sg = &data->sg[host->pio.index];
|
||||||
sg_ptr = sg_virt(sg) + host->pio.offset;
|
sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset;
|
||||||
|
|
||||||
/* This is the space left inside the buffer */
|
/* This is the space left inside the buffer */
|
||||||
sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
|
sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
|
||||||
|
@ -501,8 +503,10 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
|
||||||
val = __raw_readl(HOST_RXPORT(host));
|
val = __raw_readl(HOST_RXPORT(host));
|
||||||
|
|
||||||
if (sg_ptr)
|
if (sg_ptr)
|
||||||
*sg_ptr++ = (unsigned char)(val & 0xFF);
|
sg_ptr[count] = (unsigned char)(val & 0xFF);
|
||||||
}
|
}
|
||||||
|
if (sg_ptr)
|
||||||
|
kunmap_atomic(sg_ptr);
|
||||||
|
|
||||||
host->pio.len -= count;
|
host->pio.len -= count;
|
||||||
host->pio.offset += count;
|
host->pio.offset += count;
|
||||||
|
|
|
@ -1377,8 +1377,7 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int davinci_mmcsd_suspend(struct device *dev)
|
static int davinci_mmcsd_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct mmc_davinci_host *host = dev_get_drvdata(dev);
|
||||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
writel(0, host->base + DAVINCI_MMCIM);
|
writel(0, host->base + DAVINCI_MMCIM);
|
||||||
mmc_davinci_reset_ctrl(host, 1);
|
mmc_davinci_reset_ctrl(host, 1);
|
||||||
|
@ -1389,8 +1388,7 @@ static int davinci_mmcsd_suspend(struct device *dev)
|
||||||
|
|
||||||
static int davinci_mmcsd_resume(struct device *dev)
|
static int davinci_mmcsd_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct mmc_davinci_host *host = dev_get_drvdata(dev);
|
||||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
clk_enable(host->clk);
|
clk_enable(host->clk);
|
||||||
mmc_davinci_reset_ctrl(host, 0);
|
mmc_davinci_reset_ctrl(host, 0);
|
||||||
|
|
81
drivers/mmc/host/dw_mmc-bluefield.c
Normal file
81
drivers/mmc/host/dw_mmc-bluefield.c
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Mellanox Technologies.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
|
#include "dw_mmc.h"
|
||||||
|
#include "dw_mmc-pltfm.h"
|
||||||
|
|
||||||
|
#define UHS_REG_EXT_SAMPLE_MASK GENMASK(22, 16)
|
||||||
|
#define UHS_REG_EXT_DRIVE_MASK GENMASK(29, 23)
|
||||||
|
#define BLUEFIELD_UHS_REG_EXT_SAMPLE 2
|
||||||
|
#define BLUEFIELD_UHS_REG_EXT_DRIVE 4
|
||||||
|
|
||||||
|
static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* Update the Drive and Sample fields in register UHS_REG_EXT. */
|
||||||
|
reg = mci_readl(host, UHS_REG_EXT);
|
||||||
|
reg &= ~UHS_REG_EXT_SAMPLE_MASK;
|
||||||
|
reg |= FIELD_PREP(UHS_REG_EXT_SAMPLE_MASK,
|
||||||
|
BLUEFIELD_UHS_REG_EXT_SAMPLE);
|
||||||
|
reg &= ~UHS_REG_EXT_DRIVE_MASK;
|
||||||
|
reg |= FIELD_PREP(UHS_REG_EXT_DRIVE_MASK, BLUEFIELD_UHS_REG_EXT_DRIVE);
|
||||||
|
mci_writel(host, UHS_REG_EXT, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dw_mci_drv_data bluefield_drv_data = {
|
||||||
|
.set_ios = dw_mci_bluefield_set_ios
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id dw_mci_bluefield_match[] = {
|
||||||
|
{ .compatible = "mellanox,bluefield-dw-mshc",
|
||||||
|
.data = &bluefield_drv_data },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, dw_mci_bluefield_match);
|
||||||
|
|
||||||
|
static int dw_mci_bluefield_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
const struct dw_mci_drv_data *drv_data = NULL;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
|
||||||
|
if (pdev->dev.of_node) {
|
||||||
|
match = of_match_node(dw_mci_bluefield_match,
|
||||||
|
pdev->dev.of_node);
|
||||||
|
drv_data = match->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dw_mci_pltfm_register(pdev, drv_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver dw_mci_bluefield_pltfm_driver = {
|
||||||
|
.probe = dw_mci_bluefield_probe,
|
||||||
|
.remove = dw_mci_pltfm_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "dwmmc_bluefield",
|
||||||
|
.of_match_table = dw_mci_bluefield_match,
|
||||||
|
.pm = &dw_mci_pltfm_pmops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(dw_mci_bluefield_pltfm_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("BlueField DW Multimedia Card driver");
|
||||||
|
MODULE_AUTHOR("Mellanox Technologies");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -44,9 +44,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||||
* bus_hz = cclkin / RK3288_CLKGEN_DIV
|
* bus_hz = cclkin / RK3288_CLKGEN_DIV
|
||||||
* ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div))
|
* ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div))
|
||||||
*
|
*
|
||||||
* Note: div can only be 0 or 1
|
* Note: div can only be 0 or 1, but div must be set to 1 for eMMC
|
||||||
* if DDR50 8bit mode(only emmc work in 8bit mode),
|
* DDR52 8-bit mode.
|
||||||
* div must be set 1
|
|
||||||
*/
|
*/
|
||||||
if (ios->bus_width == MMC_BUS_WIDTH_8 &&
|
if (ios->bus_width == MMC_BUS_WIDTH_8 &&
|
||||||
ios->timing == MMC_TIMING_MMC_DDR52)
|
ios->timing == MMC_TIMING_MMC_DDR52)
|
||||||
|
|
|
@ -1230,6 +1230,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
||||||
if (host->state == STATE_WAITING_CMD11_DONE)
|
if (host->state == STATE_WAITING_CMD11_DONE)
|
||||||
sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
|
sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
|
||||||
|
|
||||||
|
slot->mmc->actual_clock = 0;
|
||||||
|
|
||||||
if (!clock) {
|
if (!clock) {
|
||||||
mci_writel(host, CLKENA, 0);
|
mci_writel(host, CLKENA, 0);
|
||||||
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
||||||
|
@ -1288,6 +1290,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
||||||
|
|
||||||
/* keep the last clock value that was requested from core */
|
/* keep the last clock value that was requested from core */
|
||||||
slot->__clk_old = clock;
|
slot->__clk_old = clock;
|
||||||
|
slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) :
|
||||||
|
host->bus_hz;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->current_speed = clock;
|
host->current_speed = clock;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
|
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
|
||||||
|
* Copyright (C) 2013, Imagination Technologies
|
||||||
|
*
|
||||||
* JZ4740 SD/MMC controller driver
|
* JZ4740 SD/MMC controller driver
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -13,24 +15,25 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/clk.h>
|
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <linux/dma-mapping.h>
|
|
||||||
#include <linux/dmaengine.h>
|
|
||||||
|
|
||||||
#include <asm/mach-jz4740/dma.h>
|
#include <asm/mach-jz4740/dma.h>
|
||||||
#include <asm/mach-jz4740/jz4740_mmc.h>
|
#include <asm/mach-jz4740/jz4740_mmc.h>
|
||||||
|
@ -51,6 +54,7 @@
|
||||||
#define JZ_REG_MMC_RESP_FIFO 0x34
|
#define JZ_REG_MMC_RESP_FIFO 0x34
|
||||||
#define JZ_REG_MMC_RXFIFO 0x38
|
#define JZ_REG_MMC_RXFIFO 0x38
|
||||||
#define JZ_REG_MMC_TXFIFO 0x3C
|
#define JZ_REG_MMC_TXFIFO 0x3C
|
||||||
|
#define JZ_REG_MMC_DMAC 0x44
|
||||||
|
|
||||||
#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
|
#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
|
||||||
#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6)
|
#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6)
|
||||||
|
@ -104,9 +108,17 @@
|
||||||
#define JZ_MMC_IRQ_PRG_DONE BIT(1)
|
#define JZ_MMC_IRQ_PRG_DONE BIT(1)
|
||||||
#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0)
|
#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0)
|
||||||
|
|
||||||
|
#define JZ_MMC_DMAC_DMA_SEL BIT(1)
|
||||||
|
#define JZ_MMC_DMAC_DMA_EN BIT(0)
|
||||||
|
|
||||||
#define JZ_MMC_CLK_RATE 24000000
|
#define JZ_MMC_CLK_RATE 24000000
|
||||||
|
|
||||||
|
enum jz4740_mmc_version {
|
||||||
|
JZ_MMC_JZ4740,
|
||||||
|
JZ_MMC_JZ4750,
|
||||||
|
JZ_MMC_JZ4780,
|
||||||
|
};
|
||||||
|
|
||||||
enum jz4740_mmc_state {
|
enum jz4740_mmc_state {
|
||||||
JZ4740_MMC_STATE_READ_RESPONSE,
|
JZ4740_MMC_STATE_READ_RESPONSE,
|
||||||
JZ4740_MMC_STATE_TRANSFER_DATA,
|
JZ4740_MMC_STATE_TRANSFER_DATA,
|
||||||
|
@ -125,6 +137,8 @@ struct jz4740_mmc_host {
|
||||||
struct jz4740_mmc_platform_data *pdata;
|
struct jz4740_mmc_platform_data *pdata;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
|
enum jz4740_mmc_version version;
|
||||||
|
|
||||||
int irq;
|
int irq;
|
||||||
int card_detect_irq;
|
int card_detect_irq;
|
||||||
|
|
||||||
|
@ -137,7 +151,7 @@ struct jz4740_mmc_host {
|
||||||
|
|
||||||
uint32_t cmdat;
|
uint32_t cmdat;
|
||||||
|
|
||||||
uint16_t irq_mask;
|
uint32_t irq_mask;
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
|
@ -159,6 +173,32 @@ struct jz4740_mmc_host {
|
||||||
#define JZ4740_MMC_FIFO_HALF_SIZE 8
|
#define JZ4740_MMC_FIFO_HALF_SIZE 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host,
|
||||||
|
uint32_t val)
|
||||||
|
{
|
||||||
|
if (host->version >= JZ_MMC_JZ4750)
|
||||||
|
return writel(val, host->base + JZ_REG_MMC_IMASK);
|
||||||
|
else
|
||||||
|
return writew(val, host->base + JZ_REG_MMC_IMASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host,
|
||||||
|
uint32_t val)
|
||||||
|
{
|
||||||
|
if (host->version >= JZ_MMC_JZ4780)
|
||||||
|
return writel(val, host->base + JZ_REG_MMC_IREG);
|
||||||
|
else
|
||||||
|
return writew(val, host->base + JZ_REG_MMC_IREG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host)
|
||||||
|
{
|
||||||
|
if (host->version >= JZ_MMC_JZ4780)
|
||||||
|
return readl(host->base + JZ_REG_MMC_IREG);
|
||||||
|
else
|
||||||
|
return readw(host->base + JZ_REG_MMC_IREG);
|
||||||
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
/* DMA infrastructure */
|
/* DMA infrastructure */
|
||||||
|
|
||||||
|
@ -173,31 +213,23 @@ static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
|
||||||
|
|
||||||
static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
|
static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
|
||||||
{
|
{
|
||||||
dma_cap_mask_t mask;
|
host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx");
|
||||||
|
if (IS_ERR(host->dma_tx)) {
|
||||||
dma_cap_zero(mask);
|
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
|
||||||
|
|
||||||
host->dma_tx = dma_request_channel(mask, NULL, host);
|
|
||||||
if (!host->dma_tx) {
|
|
||||||
dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
|
dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
|
||||||
return -ENODEV;
|
return PTR_ERR(host->dma_tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
host->dma_rx = dma_request_channel(mask, NULL, host);
|
host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx");
|
||||||
if (!host->dma_rx) {
|
if (IS_ERR(host->dma_rx)) {
|
||||||
dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
|
dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
|
||||||
goto free_master_write;
|
dma_release_channel(host->dma_tx);
|
||||||
|
return PTR_ERR(host->dma_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize DMA pre request cookie */
|
/* Initialize DMA pre request cookie */
|
||||||
host->next_data.cookie = 1;
|
host->next_data.cookie = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_master_write:
|
|
||||||
dma_release_channel(host->dma_tx);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
|
static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
|
||||||
|
@ -363,7 +395,7 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
|
||||||
else
|
else
|
||||||
host->irq_mask |= irq;
|
host->irq_mask |= irq;
|
||||||
|
|
||||||
writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
|
jz4740_mmc_write_irq_mask(host, host->irq_mask);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,10 +447,10 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
|
||||||
unsigned int irq)
|
unsigned int irq)
|
||||||
{
|
{
|
||||||
unsigned int timeout = 0x800;
|
unsigned int timeout = 0x800;
|
||||||
uint16_t status;
|
uint32_t status;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
status = readw(host->base + JZ_REG_MMC_IREG);
|
status = jz4740_mmc_read_irq_reg(host);
|
||||||
} while (!(status & irq) && --timeout);
|
} while (!(status & irq) && --timeout);
|
||||||
|
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
|
@ -518,7 +550,7 @@ static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host,
|
||||||
void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO;
|
void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO;
|
||||||
uint32_t *buf;
|
uint32_t *buf;
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
uint16_t status;
|
uint32_t status;
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
unsigned int timeout;
|
unsigned int timeout;
|
||||||
|
|
||||||
|
@ -654,8 +686,25 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
|
||||||
cmdat |= JZ_MMC_CMDAT_DATA_EN;
|
cmdat |= JZ_MMC_CMDAT_DATA_EN;
|
||||||
if (cmd->data->flags & MMC_DATA_WRITE)
|
if (cmd->data->flags & MMC_DATA_WRITE)
|
||||||
cmdat |= JZ_MMC_CMDAT_WRITE;
|
cmdat |= JZ_MMC_CMDAT_WRITE;
|
||||||
if (host->use_dma)
|
if (host->use_dma) {
|
||||||
|
/*
|
||||||
|
* The 4780's MMC controller has integrated DMA ability
|
||||||
|
* in addition to being able to use the external DMA
|
||||||
|
* controller. It moves DMA control bits to a separate
|
||||||
|
* register. The DMA_SEL bit chooses the external
|
||||||
|
* controller over the integrated one. Earlier SoCs
|
||||||
|
* can only use the external controller, and have a
|
||||||
|
* single DMA enable bit in CMDAT.
|
||||||
|
*/
|
||||||
|
if (host->version >= JZ_MMC_JZ4780) {
|
||||||
|
writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL,
|
||||||
|
host->base + JZ_REG_MMC_DMAC);
|
||||||
|
} else {
|
||||||
cmdat |= JZ_MMC_CMDAT_DMA_EN;
|
cmdat |= JZ_MMC_CMDAT_DMA_EN;
|
||||||
|
}
|
||||||
|
} else if (host->version >= JZ_MMC_JZ4780) {
|
||||||
|
writel(0, host->base + JZ_REG_MMC_DMAC);
|
||||||
|
}
|
||||||
|
|
||||||
writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
|
writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
|
||||||
writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
|
writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
|
||||||
|
@ -736,7 +785,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
|
||||||
host->state = JZ4740_MMC_STATE_SEND_STOP;
|
host->state = JZ4740_MMC_STATE_SEND_STOP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG);
|
jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
|
||||||
|
|
||||||
case JZ4740_MMC_STATE_SEND_STOP:
|
case JZ4740_MMC_STATE_SEND_STOP:
|
||||||
if (!req->stop)
|
if (!req->stop)
|
||||||
|
@ -766,9 +815,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
||||||
{
|
{
|
||||||
struct jz4740_mmc_host *host = devid;
|
struct jz4740_mmc_host *host = devid;
|
||||||
struct mmc_command *cmd = host->cmd;
|
struct mmc_command *cmd = host->cmd;
|
||||||
uint16_t irq_reg, status, tmp;
|
uint32_t irq_reg, status, tmp;
|
||||||
|
|
||||||
irq_reg = readw(host->base + JZ_REG_MMC_IREG);
|
status = readl(host->base + JZ_REG_MMC_STATUS);
|
||||||
|
irq_reg = jz4740_mmc_read_irq_reg(host);
|
||||||
|
|
||||||
tmp = irq_reg;
|
tmp = irq_reg;
|
||||||
irq_reg &= ~host->irq_mask;
|
irq_reg &= ~host->irq_mask;
|
||||||
|
@ -777,10 +827,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
||||||
JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE);
|
JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE);
|
||||||
|
|
||||||
if (tmp != irq_reg)
|
if (tmp != irq_reg)
|
||||||
writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG);
|
jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg);
|
||||||
|
|
||||||
if (irq_reg & JZ_MMC_IRQ_SDIO) {
|
if (irq_reg & JZ_MMC_IRQ_SDIO) {
|
||||||
writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG);
|
jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO);
|
||||||
mmc_signal_sdio_irq(host->mmc);
|
mmc_signal_sdio_irq(host->mmc);
|
||||||
irq_reg &= ~JZ_MMC_IRQ_SDIO;
|
irq_reg &= ~JZ_MMC_IRQ_SDIO;
|
||||||
}
|
}
|
||||||
|
@ -789,8 +839,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
||||||
if (test_and_clear_bit(0, &host->waiting)) {
|
if (test_and_clear_bit(0, &host->waiting)) {
|
||||||
del_timer(&host->timeout_timer);
|
del_timer(&host->timeout_timer);
|
||||||
|
|
||||||
status = readl(host->base + JZ_REG_MMC_STATUS);
|
|
||||||
|
|
||||||
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
|
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
|
||||||
cmd->error = -ETIMEDOUT;
|
cmd->error = -ETIMEDOUT;
|
||||||
} else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
|
} else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
|
||||||
|
@ -803,7 +851,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
||||||
}
|
}
|
||||||
|
|
||||||
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
|
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
|
||||||
writew(irq_reg, host->base + JZ_REG_MMC_IREG);
|
jz4740_mmc_write_irq_reg(host, irq_reg);
|
||||||
|
|
||||||
return IRQ_WAKE_THREAD;
|
return IRQ_WAKE_THREAD;
|
||||||
}
|
}
|
||||||
|
@ -818,7 +866,7 @@ static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate)
|
||||||
int real_rate;
|
int real_rate;
|
||||||
|
|
||||||
jz4740_mmc_clock_disable(host);
|
jz4740_mmc_clock_disable(host);
|
||||||
clk_set_rate(host->clk, JZ_MMC_CLK_RATE);
|
clk_set_rate(host->clk, host->mmc->f_max);
|
||||||
|
|
||||||
real_rate = clk_get_rate(host->clk);
|
real_rate = clk_get_rate(host->clk);
|
||||||
|
|
||||||
|
@ -837,9 +885,7 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||||
|
|
||||||
host->req = req;
|
host->req = req;
|
||||||
|
|
||||||
writew(0xffff, host->base + JZ_REG_MMC_IREG);
|
jz4740_mmc_write_irq_reg(host, ~0);
|
||||||
|
|
||||||
writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG);
|
|
||||||
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true);
|
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true);
|
||||||
|
|
||||||
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
|
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
|
||||||
|
@ -857,7 +903,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
switch (ios->power_mode) {
|
switch (ios->power_mode) {
|
||||||
case MMC_POWER_UP:
|
case MMC_POWER_UP:
|
||||||
jz4740_mmc_reset(host);
|
jz4740_mmc_reset(host);
|
||||||
if (gpio_is_valid(host->pdata->gpio_power))
|
if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
|
||||||
gpio_set_value(host->pdata->gpio_power,
|
gpio_set_value(host->pdata->gpio_power,
|
||||||
!host->pdata->power_active_low);
|
!host->pdata->power_active_low);
|
||||||
host->cmdat |= JZ_MMC_CMDAT_INIT;
|
host->cmdat |= JZ_MMC_CMDAT_INIT;
|
||||||
|
@ -866,7 +912,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
case MMC_POWER_ON:
|
case MMC_POWER_ON:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (gpio_is_valid(host->pdata->gpio_power))
|
if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
|
||||||
gpio_set_value(host->pdata->gpio_power,
|
gpio_set_value(host->pdata->gpio_power,
|
||||||
host->pdata->power_active_low);
|
host->pdata->power_active_low);
|
||||||
clk_disable_unprepare(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
|
@ -926,7 +972,7 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
|
||||||
static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
|
struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
|
@ -955,7 +1001,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
||||||
|
|
||||||
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
|
struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return;
|
return;
|
||||||
|
@ -964,14 +1010,22 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
||||||
gpio_free(pdata->gpio_power);
|
gpio_free(pdata->gpio_power);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id jz4740_mmc_of_match[] = {
|
||||||
|
{ .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 },
|
||||||
|
{ .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match);
|
||||||
|
|
||||||
static int jz4740_mmc_probe(struct platform_device* pdev)
|
static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct jz4740_mmc_host *host;
|
struct jz4740_mmc_host *host;
|
||||||
|
const struct of_device_id *match;
|
||||||
struct jz4740_mmc_platform_data *pdata;
|
struct jz4740_mmc_platform_data *pdata;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
|
mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
|
||||||
if (!mmc) {
|
if (!mmc) {
|
||||||
|
@ -982,6 +1036,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->pdata = pdata;
|
host->pdata = pdata;
|
||||||
|
|
||||||
|
match = of_match_device(jz4740_mmc_of_match, &pdev->dev);
|
||||||
|
if (match) {
|
||||||
|
host->version = (enum jz4740_mmc_version)match->data;
|
||||||
|
ret = mmc_of_parse(mmc);
|
||||||
|
if (ret) {
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"could not parse of data: %d\n", ret);
|
||||||
|
goto err_free_host;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* JZ4740 should be the only one using legacy probe */
|
||||||
|
host->version = JZ_MMC_JZ4740;
|
||||||
|
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||||
|
if (!(pdata && pdata->data_1bit))
|
||||||
|
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||||
|
ret = jz4740_mmc_request_gpios(mmc, pdev);
|
||||||
|
if (ret)
|
||||||
|
goto err_free_host;
|
||||||
|
}
|
||||||
|
|
||||||
host->irq = platform_get_irq(pdev, 0);
|
host->irq = platform_get_irq(pdev, 0);
|
||||||
if (host->irq < 0) {
|
if (host->irq < 0) {
|
||||||
ret = host->irq;
|
ret = host->irq;
|
||||||
|
@ -1004,16 +1079,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
goto err_free_host;
|
goto err_free_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = jz4740_mmc_request_gpios(mmc, pdev);
|
|
||||||
if (ret)
|
|
||||||
goto err_release_dma;
|
|
||||||
|
|
||||||
mmc->ops = &jz4740_mmc_ops;
|
mmc->ops = &jz4740_mmc_ops;
|
||||||
mmc->f_min = JZ_MMC_CLK_RATE / 128;
|
if (!mmc->f_max)
|
||||||
mmc->f_max = JZ_MMC_CLK_RATE;
|
mmc->f_max = JZ_MMC_CLK_RATE;
|
||||||
|
mmc->f_min = mmc->f_max / 128;
|
||||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||||
mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA;
|
|
||||||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
|
||||||
|
|
||||||
mmc->max_blk_size = (1 << 10) - 1;
|
mmc->max_blk_size = (1 << 10) - 1;
|
||||||
mmc->max_blk_count = (1 << 15) - 1;
|
mmc->max_blk_count = (1 << 15) - 1;
|
||||||
|
@ -1025,7 +1095,9 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->pdev = pdev;
|
host->pdev = pdev;
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
host->irq_mask = 0xffff;
|
host->irq_mask = ~0;
|
||||||
|
|
||||||
|
jz4740_mmc_reset(host);
|
||||||
|
|
||||||
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
|
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
|
||||||
dev_name(&pdev->dev), host);
|
dev_name(&pdev->dev), host);
|
||||||
|
@ -1034,20 +1106,20 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
goto err_free_gpios;
|
goto err_free_gpios;
|
||||||
}
|
}
|
||||||
|
|
||||||
jz4740_mmc_reset(host);
|
|
||||||
jz4740_mmc_clock_disable(host);
|
jz4740_mmc_clock_disable(host);
|
||||||
timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
|
timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
|
||||||
|
|
||||||
host->use_dma = true;
|
ret = jz4740_mmc_acquire_dma_channels(host);
|
||||||
if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
|
if (ret == -EPROBE_DEFER)
|
||||||
host->use_dma = false;
|
goto err_free_irq;
|
||||||
|
host->use_dma = !ret;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
ret = mmc_add_host(mmc);
|
ret = mmc_add_host(mmc);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
|
||||||
goto err_free_irq;
|
goto err_release_dma;
|
||||||
}
|
}
|
||||||
dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
|
dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
|
||||||
|
|
||||||
|
@ -1057,13 +1129,13 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_release_dma:
|
||||||
|
if (host->use_dma)
|
||||||
|
jz4740_mmc_release_dma_channels(host);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
free_irq(host->irq, host);
|
free_irq(host->irq, host);
|
||||||
err_free_gpios:
|
err_free_gpios:
|
||||||
jz4740_mmc_free_gpios(pdev);
|
jz4740_mmc_free_gpios(pdev);
|
||||||
err_release_dma:
|
|
||||||
if (host->use_dma)
|
|
||||||
jz4740_mmc_release_dma_channels(host);
|
|
||||||
err_free_host:
|
err_free_host:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
|
||||||
|
@ -1116,6 +1188,7 @@ static struct platform_driver jz4740_mmc_driver = {
|
||||||
.remove = jz4740_mmc_remove,
|
.remove = jz4740_mmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "jz4740-mmc",
|
.name = "jz4740-mmc",
|
||||||
|
.of_match_table = of_match_ptr(jz4740_mmc_of_match),
|
||||||
.pm = JZ4740_MMC_PM_OPS,
|
.pm = JZ4740_MMC_PM_OPS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
@ -47,15 +48,29 @@
|
||||||
#define CLK_CORE_PHASE_MASK GENMASK(9, 8)
|
#define CLK_CORE_PHASE_MASK GENMASK(9, 8)
|
||||||
#define CLK_TX_PHASE_MASK GENMASK(11, 10)
|
#define CLK_TX_PHASE_MASK GENMASK(11, 10)
|
||||||
#define CLK_RX_PHASE_MASK GENMASK(13, 12)
|
#define CLK_RX_PHASE_MASK GENMASK(13, 12)
|
||||||
#define CLK_TX_DELAY_MASK GENMASK(19, 16)
|
#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16)
|
||||||
#define CLK_RX_DELAY_MASK GENMASK(23, 20)
|
#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20)
|
||||||
|
#define CLK_V2_ALWAYS_ON BIT(24)
|
||||||
|
|
||||||
|
#define CLK_V3_TX_DELAY_MASK GENMASK(21, 16)
|
||||||
|
#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22)
|
||||||
|
#define CLK_V3_ALWAYS_ON BIT(28)
|
||||||
|
|
||||||
#define CLK_DELAY_STEP_PS 200
|
#define CLK_DELAY_STEP_PS 200
|
||||||
#define CLK_PHASE_STEP 30
|
#define CLK_PHASE_STEP 30
|
||||||
#define CLK_PHASE_POINT_NUM (360 / CLK_PHASE_STEP)
|
#define CLK_PHASE_POINT_NUM (360 / CLK_PHASE_STEP)
|
||||||
#define CLK_ALWAYS_ON BIT(24)
|
|
||||||
|
#define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask)
|
||||||
|
#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask)
|
||||||
|
#define CLK_ALWAYS_ON(h) (h->data->always_on)
|
||||||
|
|
||||||
#define SD_EMMC_DELAY 0x4
|
#define SD_EMMC_DELAY 0x4
|
||||||
#define SD_EMMC_ADJUST 0x8
|
#define SD_EMMC_ADJUST 0x8
|
||||||
|
|
||||||
|
#define SD_EMMC_DELAY1 0x4
|
||||||
|
#define SD_EMMC_DELAY2 0x8
|
||||||
|
#define SD_EMMC_V3_ADJUST 0xc
|
||||||
|
|
||||||
#define SD_EMMC_CALOUT 0x10
|
#define SD_EMMC_CALOUT 0x10
|
||||||
#define SD_EMMC_START 0x40
|
#define SD_EMMC_START 0x40
|
||||||
#define START_DESC_INIT BIT(0)
|
#define START_DESC_INIT BIT(0)
|
||||||
|
@ -122,6 +137,12 @@
|
||||||
|
|
||||||
#define MUX_CLK_NUM_PARENTS 2
|
#define MUX_CLK_NUM_PARENTS 2
|
||||||
|
|
||||||
|
struct meson_mmc_data {
|
||||||
|
unsigned int tx_delay_mask;
|
||||||
|
unsigned int rx_delay_mask;
|
||||||
|
unsigned int always_on;
|
||||||
|
};
|
||||||
|
|
||||||
struct sd_emmc_desc {
|
struct sd_emmc_desc {
|
||||||
u32 cmd_cfg;
|
u32 cmd_cfg;
|
||||||
u32 cmd_arg;
|
u32 cmd_arg;
|
||||||
|
@ -131,6 +152,7 @@ struct sd_emmc_desc {
|
||||||
|
|
||||||
struct meson_host {
|
struct meson_host {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct meson_mmc_data *data;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct mmc_command *cmd;
|
struct mmc_command *cmd;
|
||||||
|
|
||||||
|
@ -474,7 +496,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
||||||
|
|
||||||
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
|
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
|
||||||
clk_reg = 0;
|
clk_reg = 0;
|
||||||
clk_reg |= CLK_ALWAYS_ON;
|
clk_reg |= CLK_ALWAYS_ON(host);
|
||||||
clk_reg |= CLK_DIV_MASK;
|
clk_reg |= CLK_DIV_MASK;
|
||||||
writel(clk_reg, host->regs + SD_EMMC_CLOCK);
|
writel(clk_reg, host->regs + SD_EMMC_CLOCK);
|
||||||
|
|
||||||
|
@ -574,7 +596,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
||||||
|
|
||||||
tx->reg = host->regs + SD_EMMC_CLOCK;
|
tx->reg = host->regs + SD_EMMC_CLOCK;
|
||||||
tx->phase_mask = CLK_TX_PHASE_MASK;
|
tx->phase_mask = CLK_TX_PHASE_MASK;
|
||||||
tx->delay_mask = CLK_TX_DELAY_MASK;
|
tx->delay_mask = CLK_TX_DELAY_MASK(host);
|
||||||
tx->delay_step_ps = CLK_DELAY_STEP_PS;
|
tx->delay_step_ps = CLK_DELAY_STEP_PS;
|
||||||
tx->hw.init = &init;
|
tx->hw.init = &init;
|
||||||
|
|
||||||
|
@ -597,7 +619,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
||||||
|
|
||||||
rx->reg = host->regs + SD_EMMC_CLOCK;
|
rx->reg = host->regs + SD_EMMC_CLOCK;
|
||||||
rx->phase_mask = CLK_RX_PHASE_MASK;
|
rx->phase_mask = CLK_RX_PHASE_MASK;
|
||||||
rx->delay_mask = CLK_RX_DELAY_MASK;
|
rx->delay_mask = CLK_RX_DELAY_MASK(host);
|
||||||
rx->delay_step_ps = CLK_DELAY_STEP_PS;
|
rx->delay_step_ps = CLK_DELAY_STEP_PS;
|
||||||
rx->hw.init = &init;
|
rx->hw.init = &init;
|
||||||
|
|
||||||
|
@ -1184,6 +1206,21 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
||||||
goto free_host;
|
goto free_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host->data = (struct meson_mmc_data *)
|
||||||
|
of_device_get_match_data(&pdev->dev);
|
||||||
|
if (!host->data) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = device_reset_optional(&pdev->dev);
|
||||||
|
if (ret) {
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "device reset failed: %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
host->regs = devm_ioremap_resource(&pdev->dev, res);
|
host->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->regs)) {
|
if (IS_ERR(host->regs)) {
|
||||||
|
@ -1315,11 +1352,24 @@ static int meson_mmc_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct meson_mmc_data meson_gx_data = {
|
||||||
|
.tx_delay_mask = CLK_V2_TX_DELAY_MASK,
|
||||||
|
.rx_delay_mask = CLK_V2_RX_DELAY_MASK,
|
||||||
|
.always_on = CLK_V2_ALWAYS_ON,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct meson_mmc_data meson_axg_data = {
|
||||||
|
.tx_delay_mask = CLK_V3_TX_DELAY_MASK,
|
||||||
|
.rx_delay_mask = CLK_V3_RX_DELAY_MASK,
|
||||||
|
.always_on = CLK_V3_ALWAYS_ON,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id meson_mmc_of_match[] = {
|
static const struct of_device_id meson_mmc_of_match[] = {
|
||||||
{ .compatible = "amlogic,meson-gx-mmc", },
|
{ .compatible = "amlogic,meson-gx-mmc", .data = &meson_gx_data },
|
||||||
{ .compatible = "amlogic,meson-gxbb-mmc", },
|
{ .compatible = "amlogic,meson-gxbb-mmc", .data = &meson_gx_data },
|
||||||
{ .compatible = "amlogic,meson-gxl-mmc", },
|
{ .compatible = "amlogic,meson-gxl-mmc", .data = &meson_gx_data },
|
||||||
{ .compatible = "amlogic,meson-gxm-mmc", },
|
{ .compatible = "amlogic,meson-gxm-mmc", .data = &meson_gx_data },
|
||||||
|
{ .compatible = "amlogic,meson-axg-mmc", .data = &meson_axg_data },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, meson_mmc_of_match);
|
MODULE_DEVICE_TABLE(of, meson_mmc_of_match);
|
||||||
|
@ -1335,6 +1385,6 @@ static struct platform_driver meson_mmc_driver = {
|
||||||
|
|
||||||
module_platform_driver(meson_mmc_driver);
|
module_platform_driver(meson_mmc_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Amlogic S905*/GX* SD/eMMC driver");
|
MODULE_DESCRIPTION("Amlogic S905*/GX*/AXG SD/eMMC driver");
|
||||||
MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
|
MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -1253,15 +1253,12 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
|
||||||
struct sg_mapping_iter *sg_miter = &host->sg_miter;
|
struct sg_mapping_iter *sg_miter = &host->sg_miter;
|
||||||
struct variant_data *variant = host->variant;
|
struct variant_data *variant = host->variant;
|
||||||
void __iomem *base = host->base;
|
void __iomem *base = host->base;
|
||||||
unsigned long flags;
|
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
status = readl(base + MMCISTATUS);
|
status = readl(base + MMCISTATUS);
|
||||||
|
|
||||||
dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
|
dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
|
||||||
|
|
||||||
local_irq_save(flags);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
unsigned int remain, len;
|
unsigned int remain, len;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
@ -1301,8 +1298,6 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
|
||||||
|
|
||||||
sg_miter_stop(sg_miter);
|
sg_miter_stop(sg_miter);
|
||||||
|
|
||||||
local_irq_restore(flags);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have less than the fifo 'half-full' threshold to transfer,
|
* If we have less than the fifo 'half-full' threshold to transfer,
|
||||||
* trigger a PIO interrupt as soon as any data is available.
|
* trigger a PIO interrupt as soon as any data is available.
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
@ -70,6 +71,7 @@
|
||||||
#define SDC_ADV_CFG0 0x64
|
#define SDC_ADV_CFG0 0x64
|
||||||
#define EMMC_IOCON 0x7c
|
#define EMMC_IOCON 0x7c
|
||||||
#define SDC_ACMD_RESP 0x80
|
#define SDC_ACMD_RESP 0x80
|
||||||
|
#define DMA_SA_H4BIT 0x8c
|
||||||
#define MSDC_DMA_SA 0x90
|
#define MSDC_DMA_SA 0x90
|
||||||
#define MSDC_DMA_CTRL 0x98
|
#define MSDC_DMA_CTRL 0x98
|
||||||
#define MSDC_DMA_CFG 0x9c
|
#define MSDC_DMA_CFG 0x9c
|
||||||
|
@ -194,6 +196,9 @@
|
||||||
/* SDC_ADV_CFG0 mask */
|
/* SDC_ADV_CFG0 mask */
|
||||||
#define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */
|
#define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */
|
||||||
|
|
||||||
|
/* DMA_SA_H4BIT mask */
|
||||||
|
#define DMA_ADDR_HIGH_4BIT (0xf << 0) /* RW */
|
||||||
|
|
||||||
/* MSDC_DMA_CTRL mask */
|
/* MSDC_DMA_CTRL mask */
|
||||||
#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
|
#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
|
||||||
#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
|
#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
|
||||||
|
@ -227,6 +232,7 @@
|
||||||
|
|
||||||
#define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */
|
#define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */
|
||||||
#define MSDC_PATCH_BIT2_CFGCRCSTS (0x1 << 28) /* RW */
|
#define MSDC_PATCH_BIT2_CFGCRCSTS (0x1 << 28) /* RW */
|
||||||
|
#define MSDC_PB2_SUPPORT_64G (0x1 << 1) /* RW */
|
||||||
#define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */
|
#define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */
|
||||||
#define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* RW */
|
#define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* RW */
|
||||||
#define MSDC_PB2_CRCSTSENSEL (0x7 << 29) /* RW */
|
#define MSDC_PB2_CRCSTSENSEL (0x7 << 29) /* RW */
|
||||||
|
@ -280,6 +286,8 @@ struct mt_gpdma_desc {
|
||||||
#define GPDMA_DESC_BDP (0x1 << 1)
|
#define GPDMA_DESC_BDP (0x1 << 1)
|
||||||
#define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
|
#define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
|
||||||
#define GPDMA_DESC_INT (0x1 << 16)
|
#define GPDMA_DESC_INT (0x1 << 16)
|
||||||
|
#define GPDMA_DESC_NEXT_H4 (0xf << 24)
|
||||||
|
#define GPDMA_DESC_PTR_H4 (0xf << 28)
|
||||||
u32 next;
|
u32 next;
|
||||||
u32 ptr;
|
u32 ptr;
|
||||||
u32 gpd_data_len;
|
u32 gpd_data_len;
|
||||||
|
@ -296,6 +304,8 @@ struct mt_bdma_desc {
|
||||||
#define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
|
#define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
|
||||||
#define BDMA_DESC_BLKPAD (0x1 << 17)
|
#define BDMA_DESC_BLKPAD (0x1 << 17)
|
||||||
#define BDMA_DESC_DWPAD (0x1 << 18)
|
#define BDMA_DESC_DWPAD (0x1 << 18)
|
||||||
|
#define BDMA_DESC_NEXT_H4 (0xf << 24)
|
||||||
|
#define BDMA_DESC_PTR_H4 (0xf << 28)
|
||||||
u32 next;
|
u32 next;
|
||||||
u32 ptr;
|
u32 ptr;
|
||||||
u32 bd_data_len;
|
u32 bd_data_len;
|
||||||
|
@ -334,6 +344,7 @@ struct mtk_mmc_compatible {
|
||||||
bool busy_check;
|
bool busy_check;
|
||||||
bool stop_clk_fix;
|
bool stop_clk_fix;
|
||||||
bool enhance_rx;
|
bool enhance_rx;
|
||||||
|
bool support_64g;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msdc_tune_para {
|
struct msdc_tune_para {
|
||||||
|
@ -403,6 +414,7 @@ static const struct mtk_mmc_compatible mt8135_compat = {
|
||||||
.busy_check = false,
|
.busy_check = false,
|
||||||
.stop_clk_fix = false,
|
.stop_clk_fix = false,
|
||||||
.enhance_rx = false,
|
.enhance_rx = false,
|
||||||
|
.support_64g = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_mmc_compatible mt8173_compat = {
|
static const struct mtk_mmc_compatible mt8173_compat = {
|
||||||
|
@ -414,6 +426,7 @@ static const struct mtk_mmc_compatible mt8173_compat = {
|
||||||
.busy_check = false,
|
.busy_check = false,
|
||||||
.stop_clk_fix = false,
|
.stop_clk_fix = false,
|
||||||
.enhance_rx = false,
|
.enhance_rx = false,
|
||||||
|
.support_64g = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_mmc_compatible mt2701_compat = {
|
static const struct mtk_mmc_compatible mt2701_compat = {
|
||||||
|
@ -425,6 +438,7 @@ static const struct mtk_mmc_compatible mt2701_compat = {
|
||||||
.busy_check = false,
|
.busy_check = false,
|
||||||
.stop_clk_fix = false,
|
.stop_clk_fix = false,
|
||||||
.enhance_rx = false,
|
.enhance_rx = false,
|
||||||
|
.support_64g = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_mmc_compatible mt2712_compat = {
|
static const struct mtk_mmc_compatible mt2712_compat = {
|
||||||
|
@ -436,6 +450,7 @@ static const struct mtk_mmc_compatible mt2712_compat = {
|
||||||
.busy_check = true,
|
.busy_check = true,
|
||||||
.stop_clk_fix = true,
|
.stop_clk_fix = true,
|
||||||
.enhance_rx = true,
|
.enhance_rx = true,
|
||||||
|
.support_64g = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_mmc_compatible mt7622_compat = {
|
static const struct mtk_mmc_compatible mt7622_compat = {
|
||||||
|
@ -447,6 +462,7 @@ static const struct mtk_mmc_compatible mt7622_compat = {
|
||||||
.busy_check = true,
|
.busy_check = true,
|
||||||
.stop_clk_fix = true,
|
.stop_clk_fix = true,
|
||||||
.enhance_rx = true,
|
.enhance_rx = true,
|
||||||
|
.support_64g = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id msdc_of_ids[] = {
|
static const struct of_device_id msdc_of_ids[] = {
|
||||||
|
@ -556,7 +572,12 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
|
||||||
/* init bd */
|
/* init bd */
|
||||||
bd[j].bd_info &= ~BDMA_DESC_BLKPAD;
|
bd[j].bd_info &= ~BDMA_DESC_BLKPAD;
|
||||||
bd[j].bd_info &= ~BDMA_DESC_DWPAD;
|
bd[j].bd_info &= ~BDMA_DESC_DWPAD;
|
||||||
bd[j].ptr = (u32)dma_address;
|
bd[j].ptr = lower_32_bits(dma_address);
|
||||||
|
if (host->dev_comp->support_64g) {
|
||||||
|
bd[j].bd_info &= ~BDMA_DESC_PTR_H4;
|
||||||
|
bd[j].bd_info |= (upper_32_bits(dma_address) & 0xf)
|
||||||
|
<< 28;
|
||||||
|
}
|
||||||
bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
|
bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
|
||||||
bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
|
bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
|
||||||
|
|
||||||
|
@ -575,7 +596,10 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
|
||||||
dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE);
|
dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE);
|
||||||
dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8);
|
dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8);
|
||||||
writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL);
|
writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL);
|
||||||
writel((u32)dma->gpd_addr, host->base + MSDC_DMA_SA);
|
if (host->dev_comp->support_64g)
|
||||||
|
sdr_set_field(host->base + DMA_SA_H4BIT, DMA_ADDR_HIGH_4BIT,
|
||||||
|
upper_32_bits(dma->gpd_addr) & 0xf);
|
||||||
|
writel(lower_32_bits(dma->gpd_addr), host->base + MSDC_DMA_SA);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq)
|
static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq)
|
||||||
|
@ -1366,6 +1390,9 @@ static void msdc_init_hw(struct msdc_host *host)
|
||||||
MSDC_PATCH_BIT2_CFGCRCSTS);
|
MSDC_PATCH_BIT2_CFGCRCSTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->dev_comp->support_64g)
|
||||||
|
sdr_set_bits(host->base + MSDC_PATCH_BIT2,
|
||||||
|
MSDC_PB2_SUPPORT_64G);
|
||||||
if (host->dev_comp->data_tune) {
|
if (host->dev_comp->data_tune) {
|
||||||
sdr_set_bits(host->base + tune_reg,
|
sdr_set_bits(host->base + tune_reg,
|
||||||
MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL);
|
MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL);
|
||||||
|
@ -1407,19 +1434,32 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
|
||||||
{
|
{
|
||||||
struct mt_gpdma_desc *gpd = dma->gpd;
|
struct mt_gpdma_desc *gpd = dma->gpd;
|
||||||
struct mt_bdma_desc *bd = dma->bd;
|
struct mt_bdma_desc *bd = dma->bd;
|
||||||
|
dma_addr_t dma_addr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2);
|
memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2);
|
||||||
|
|
||||||
|
dma_addr = dma->gpd_addr + sizeof(struct mt_gpdma_desc);
|
||||||
gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
|
gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
|
||||||
gpd->ptr = (u32)dma->bd_addr; /* physical address */
|
|
||||||
/* gpd->next is must set for desc DMA
|
/* gpd->next is must set for desc DMA
|
||||||
* That's why must alloc 2 gpd structure.
|
* That's why must alloc 2 gpd structure.
|
||||||
*/
|
*/
|
||||||
gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc);
|
gpd->next = lower_32_bits(dma_addr);
|
||||||
|
if (host->dev_comp->support_64g)
|
||||||
|
gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 24;
|
||||||
|
|
||||||
|
dma_addr = dma->bd_addr;
|
||||||
|
gpd->ptr = lower_32_bits(dma->bd_addr); /* physical address */
|
||||||
|
if (host->dev_comp->support_64g)
|
||||||
|
gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 28;
|
||||||
|
|
||||||
memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
|
memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
|
||||||
for (i = 0; i < (MAX_BD_NUM - 1); i++)
|
for (i = 0; i < (MAX_BD_NUM - 1); i++) {
|
||||||
bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
|
dma_addr = dma->bd_addr + sizeof(*bd) * (i + 1);
|
||||||
|
bd[i].next = lower_32_bits(dma_addr);
|
||||||
|
if (host->dev_comp->support_64g)
|
||||||
|
bd[i].bd_info |= (upper_32_bits(dma_addr) & 0xf) << 24;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
@ -1820,7 +1860,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct msdc_host *host;
|
struct msdc_host *host;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
const struct of_device_id *of_id;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!pdev->dev.of_node) {
|
if (!pdev->dev.of_node) {
|
||||||
|
@ -1828,9 +1867,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
of_id = of_match_node(msdc_of_ids, pdev->dev.of_node);
|
|
||||||
if (!of_id)
|
|
||||||
return -EINVAL;
|
|
||||||
/* Allocate MMC host for this device */
|
/* Allocate MMC host for this device */
|
||||||
mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
|
mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
|
||||||
if (!mmc)
|
if (!mmc)
|
||||||
|
@ -1899,7 +1935,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||||
msdc_of_property_parse(pdev, host);
|
msdc_of_property_parse(pdev, host);
|
||||||
|
|
||||||
host->dev = &pdev->dev;
|
host->dev = &pdev->dev;
|
||||||
host->dev_comp = of_id->data;
|
host->dev_comp = of_device_get_match_data(&pdev->dev);
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->src_clk_freq = clk_get_rate(host->src_clk);
|
host->src_clk_freq = clk_get_rate(host->src_clk);
|
||||||
/* Set host parameters to mmc */
|
/* Set host parameters to mmc */
|
||||||
|
@ -1916,6 +1952,9 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||||
mmc->max_blk_size = 2048;
|
mmc->max_blk_size = 2048;
|
||||||
mmc->max_req_size = 512 * 1024;
|
mmc->max_req_size = 512 * 1024;
|
||||||
mmc->max_blk_count = mmc->max_req_size / 512;
|
mmc->max_blk_count = mmc->max_req_size / 512;
|
||||||
|
if (host->dev_comp->support_64g)
|
||||||
|
host->dma_mask = DMA_BIT_MASK(36);
|
||||||
|
else
|
||||||
host->dma_mask = DMA_BIT_MASK(32);
|
host->dma_mask = DMA_BIT_MASK(32);
|
||||||
mmc_dev(mmc)->dma_mask = &host->dma_mask;
|
mmc_dev(mmc)->dma_mask = &host->dma_mask;
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,7 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
struct mmc_command *cmd = mrq->cmd;
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
u32 cmdreg = 0, xfer = 0, intr = 0;
|
u32 cmdreg = 0, xfer = 0, intr = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned int timeout;
|
||||||
|
|
||||||
BUG_ON(host->mrq != NULL);
|
BUG_ON(host->mrq != NULL);
|
||||||
host->mrq = mrq;
|
host->mrq = mrq;
|
||||||
|
@ -234,7 +235,8 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
|
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
|
||||||
mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
|
mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
|
||||||
|
|
||||||
mod_timer(&host->timer, jiffies + 5 * HZ);
|
timeout = cmd->busy_timeout ? cmd->busy_timeout : 5000;
|
||||||
|
mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout));
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -755,6 +757,8 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||||
if (maxfreq)
|
if (maxfreq)
|
||||||
mmc->f_max = maxfreq;
|
mmc->f_max = maxfreq;
|
||||||
|
|
||||||
|
mmc->caps |= MMC_CAP_ERASE;
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
|
|
||||||
host->base = devm_ioremap_resource(&pdev->dev, r);
|
host->base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
|
@ -291,8 +292,10 @@ static void mxcmci_swap_buffers(struct mmc_data *data)
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i)
|
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||||
buffer_swap32(sg_virt(sg), sg->length);
|
void *buf = kmap_atomic(sg_page(sg) + sg->offset;
|
||||||
|
buffer_swap32(buf, sg->length);
|
||||||
|
kunmap_atomic(buf);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
|
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
|
||||||
|
@ -609,6 +612,7 @@ static int mxcmci_transfer_data(struct mxcmci_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_data *data = host->req->data;
|
struct mmc_data *data = host->req->data;
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
|
void *buf;
|
||||||
int stat, i;
|
int stat, i;
|
||||||
|
|
||||||
host->data = data;
|
host->data = data;
|
||||||
|
@ -616,14 +620,18 @@ static int mxcmci_transfer_data(struct mxcmci_host *host)
|
||||||
|
|
||||||
if (data->flags & MMC_DATA_READ) {
|
if (data->flags & MMC_DATA_READ) {
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||||
stat = mxcmci_pull(host, sg_virt(sg), sg->length);
|
buf = kmap_atomic(sg_page(sg) + sg->offset);
|
||||||
|
stat = mxcmci_pull(host, buf, sg->length);
|
||||||
|
kunmap(buf);
|
||||||
if (stat)
|
if (stat)
|
||||||
return stat;
|
return stat;
|
||||||
host->datasize += sg->length;
|
host->datasize += sg->length;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||||
stat = mxcmci_push(host, sg_virt(sg), sg->length);
|
buf = kmap_atomic(sg_page(sg) + sg->offset);
|
||||||
|
stat = mxcmci_push(host, buf, sg->length);
|
||||||
|
kunmap(buf);
|
||||||
if (stat)
|
if (stat)
|
||||||
return stat;
|
return stat;
|
||||||
host->datasize += sg->length;
|
host->datasize += sg->length;
|
||||||
|
@ -1206,7 +1214,8 @@ static int mxcmci_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused mxcmci_suspend(struct device *dev)
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int mxcmci_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
struct mxcmci_host *host = mmc_priv(mmc);
|
struct mxcmci_host *host = mmc_priv(mmc);
|
||||||
|
@ -1216,7 +1225,7 @@ static int __maybe_unused mxcmci_suspend(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused mxcmci_resume(struct device *dev)
|
static int mxcmci_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
struct mxcmci_host *host = mmc_priv(mmc);
|
struct mxcmci_host *host = mmc_priv(mmc);
|
||||||
|
@ -1232,6 +1241,7 @@ static int __maybe_unused mxcmci_resume(struct device *dev)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);
|
static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/mfd/tmio.h>
|
#include <linux/mfd/tmio.h>
|
||||||
#include <linux/sh_dma.h>
|
#include <linux/sh_dma.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -534,6 +535,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||||
host->multi_io_quirk = renesas_sdhi_multi_io_quirk;
|
host->multi_io_quirk = renesas_sdhi_multi_io_quirk;
|
||||||
host->dma_ops = dma_ops;
|
host->dma_ops = dma_ops;
|
||||||
|
|
||||||
|
/* For some SoC, we disable internal WP. GPIO may override this */
|
||||||
|
if (mmc_can_gpio_ro(host->mmc))
|
||||||
|
mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
|
||||||
|
|
||||||
/* SDR speeds are only available on Gen2+ */
|
/* SDR speeds are only available on Gen2+ */
|
||||||
if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) {
|
if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) {
|
||||||
/* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */
|
/* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */
|
||||||
|
|
|
@ -87,11 +87,12 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
||||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||||
.bus_shift = 2,
|
.bus_shift = 2,
|
||||||
.scc_offset = 0x1000,
|
.scc_offset = 0x1000,
|
||||||
.taps = rcar_gen3_scc_taps,
|
.taps = rcar_gen3_scc_taps,
|
||||||
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
|
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
|
||||||
/* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */
|
/* DMAC can handle 0xffffffff blk count but only 1 segment */
|
||||||
.max_blk_count = 0xffffffff,
|
.max_blk_count = 0xffffffff,
|
||||||
.max_segs = 1,
|
.max_segs = 1,
|
||||||
};
|
};
|
||||||
|
@ -157,38 +158,34 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
|
||||||
{
|
{
|
||||||
struct scatterlist *sg = host->sg_ptr;
|
struct scatterlist *sg = host->sg_ptr;
|
||||||
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
|
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
|
||||||
enum dma_data_direction dir;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* This DMAC cannot handle if sg_len is not 1 */
|
if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
|
||||||
WARN_ON(host->sg_len > 1);
|
mmc_get_dma_dir(data)))
|
||||||
|
goto force_pio;
|
||||||
|
|
||||||
/* This DMAC cannot handle if buffer is not 8-bytes alignment */
|
/* This DMAC cannot handle if buffer is not 8-bytes alignment */
|
||||||
if (!IS_ALIGNED(sg->offset, 8))
|
if (!IS_ALIGNED(sg_dma_address(sg), 8)) {
|
||||||
|
dma_unmap_sg(&host->pdev->dev, sg, host->sg_len,
|
||||||
|
mmc_get_dma_dir(data));
|
||||||
goto force_pio;
|
goto force_pio;
|
||||||
|
}
|
||||||
|
|
||||||
if (data->flags & MMC_DATA_READ) {
|
if (data->flags & MMC_DATA_READ) {
|
||||||
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
|
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
|
||||||
dir = DMA_FROM_DEVICE;
|
|
||||||
if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
|
if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
|
||||||
test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
|
test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
|
||||||
goto force_pio;
|
goto force_pio;
|
||||||
} else {
|
} else {
|
||||||
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
|
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
|
||||||
dir = DMA_TO_DEVICE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
|
|
||||||
if (ret == 0)
|
|
||||||
goto force_pio;
|
|
||||||
|
|
||||||
renesas_sdhi_internal_dmac_enable_dma(host, true);
|
renesas_sdhi_internal_dmac_enable_dma(host, true);
|
||||||
|
|
||||||
/* set dma parameters */
|
/* set dma parameters */
|
||||||
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
|
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
|
||||||
dtran_mode);
|
dtran_mode);
|
||||||
renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
|
renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
|
||||||
sg->dma_address);
|
sg_dma_address(sg));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -272,12 +269,17 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
|
||||||
* implementation as others may use a different implementation.
|
* implementation as others may use a different implementation.
|
||||||
*/
|
*/
|
||||||
static const struct soc_device_attribute gen3_soc_whitelist[] = {
|
static const struct soc_device_attribute gen3_soc_whitelist[] = {
|
||||||
|
/* specific ones */
|
||||||
{ .soc_id = "r8a7795", .revision = "ES1.*",
|
{ .soc_id = "r8a7795", .revision = "ES1.*",
|
||||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
||||||
{ .soc_id = "r8a7795", .revision = "ES2.0" },
|
|
||||||
{ .soc_id = "r8a7796", .revision = "ES1.0",
|
{ .soc_id = "r8a7796", .revision = "ES1.0",
|
||||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
||||||
{ .soc_id = "r8a77995", .revision = "ES1.0" },
|
/* generic ones */
|
||||||
|
{ .soc_id = "r8a7795" },
|
||||||
|
{ .soc_id = "r8a7796" },
|
||||||
|
{ .soc_id = "r8a77965" },
|
||||||
|
{ .soc_id = "r8a77980" },
|
||||||
|
{ .soc_id = "r8a77995" },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ static const struct renesas_sdhi_of_data of_rz_compatible = {
|
||||||
static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
|
static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
||||||
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Definitions for sampling clocks */
|
/* Definitions for sampling clocks */
|
||||||
|
@ -61,6 +62,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
|
||||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||||
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||||
.dma_rx_offset = 0x2000,
|
.dma_rx_offset = 0x2000,
|
||||||
.scc_offset = 0x0300,
|
.scc_offset = 0x0300,
|
||||||
|
@ -81,6 +83,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
||||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||||
.bus_shift = 2,
|
.bus_shift = 2,
|
||||||
.scc_offset = 0x1000,
|
.scc_offset = 0x1000,
|
||||||
.taps = rcar_gen3_scc_taps,
|
.taps = rcar_gen3_scc_taps,
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/sd.h>
|
#include <linux/mmc/sd.h>
|
||||||
#include <linux/mmc/sdio.h>
|
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
@ -343,7 +342,7 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsp_type == SD_RSP_TYPE_R1b)
|
if (rsp_type == SD_RSP_TYPE_R1b)
|
||||||
timeout = 3000;
|
timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000;
|
||||||
|
|
||||||
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
||||||
err = rtsx_usb_write_register(ucr, SD_BUS_STAT,
|
err = rtsx_usb_write_register(ucr, SD_BUS_STAT,
|
||||||
|
@ -839,17 +838,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
goto finish_detect_card;
|
goto finish_detect_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Reject SDIO CMDs to speed up card identification
|
|
||||||
* since unsupported
|
|
||||||
*/
|
|
||||||
if (cmd->opcode == SD_IO_SEND_OP_COND ||
|
|
||||||
cmd->opcode == SD_IO_RW_DIRECT ||
|
|
||||||
cmd->opcode == SD_IO_RW_EXTENDED) {
|
|
||||||
cmd->error = -EINVAL;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&ucr->dev_mutex);
|
mutex_lock(&ucr->dev_mutex);
|
||||||
|
|
||||||
mutex_lock(&host->host_mutex);
|
mutex_lock(&host->host_mutex);
|
||||||
|
@ -1332,8 +1320,9 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
|
||||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
|
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
|
||||||
MMC_CAP_NEEDS_POLL;
|
MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE;
|
||||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
|
||||||
|
MMC_CAP2_NO_SDIO;
|
||||||
|
|
||||||
mmc->max_current_330 = 400;
|
mmc->max_current_330 = 400;
|
||||||
mmc->max_current_180 = 800;
|
mmc->max_current_180 = 800;
|
||||||
|
|
|
@ -284,10 +284,8 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
||||||
sdhci_bcm_kona_sd_init(host);
|
sdhci_bcm_kona_sd_init(host);
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(dev, "Failed sdhci_add_host\n");
|
|
||||||
goto err_reset;
|
goto err_reset;
|
||||||
}
|
|
||||||
|
|
||||||
/* if device is eMMC, emulate card insert right here */
|
/* if device is eMMC, emulate card insert right here */
|
||||||
if (!mmc_card_is_removable(host->mmc)) {
|
if (!mmc_card_is_removable(host->mmc)) {
|
||||||
|
|
|
@ -253,6 +253,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
|
||||||
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
|
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
|
||||||
void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
|
void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
|
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -260,11 +261,24 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
|
||||||
tmp = readl(reg);
|
tmp = readl(reg);
|
||||||
tmp &= ~SDHCI_CDNS_HRS06_TUNE;
|
tmp &= ~SDHCI_CDNS_HRS06_TUNE;
|
||||||
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
|
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workaround for IP errata:
|
||||||
|
* The IP6116 SD/eMMC PHY design has a timing issue on receive data
|
||||||
|
* path. Send tune request twice.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
|
tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
|
||||||
writel(tmp, reg);
|
writel(tmp, reg);
|
||||||
|
|
||||||
return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
|
ret = readl_poll_timeout(reg, tmp,
|
||||||
|
!(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
|
||||||
0, 1);
|
0, 1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
|
|
|
@ -41,6 +41,12 @@
|
||||||
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
|
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
|
||||||
#define ESDHC_WTMK_LVL 0x44
|
#define ESDHC_WTMK_LVL 0x44
|
||||||
#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
|
#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
|
||||||
|
#define ESDHC_WTMK_LVL_RD_WML_MASK 0x000000FF
|
||||||
|
#define ESDHC_WTMK_LVL_RD_WML_SHIFT 0
|
||||||
|
#define ESDHC_WTMK_LVL_WR_WML_MASK 0x00FF0000
|
||||||
|
#define ESDHC_WTMK_LVL_WR_WML_SHIFT 16
|
||||||
|
#define ESDHC_WTMK_LVL_WML_VAL_DEF 64
|
||||||
|
#define ESDHC_WTMK_LVL_WML_VAL_MAX 128
|
||||||
#define ESDHC_MIX_CTRL 0x48
|
#define ESDHC_MIX_CTRL 0x48
|
||||||
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
|
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
|
||||||
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
|
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
|
||||||
|
@ -516,6 +522,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esdhc_is_usdhc(imx_data)) {
|
if (esdhc_is_usdhc(imx_data)) {
|
||||||
|
u32 wml;
|
||||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
/* Swap AC23 bit */
|
/* Swap AC23 bit */
|
||||||
if (val & SDHCI_TRNS_AUTO_CMD23) {
|
if (val & SDHCI_TRNS_AUTO_CMD23) {
|
||||||
|
@ -524,6 +531,21 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
||||||
}
|
}
|
||||||
m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
|
m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
|
||||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
|
|
||||||
|
/* Set watermark levels for PIO access to maximum value
|
||||||
|
* (128 words) to accommodate full 512 bytes buffer.
|
||||||
|
* For DMA access restore the levels to default value.
|
||||||
|
*/
|
||||||
|
m = readl(host->ioaddr + ESDHC_WTMK_LVL);
|
||||||
|
if (val & SDHCI_TRNS_DMA)
|
||||||
|
wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
|
||||||
|
else
|
||||||
|
wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
|
||||||
|
m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
|
||||||
|
ESDHC_WTMK_LVL_WR_WML_MASK);
|
||||||
|
m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
|
||||||
|
(wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
|
||||||
|
writel(m, host->ioaddr + ESDHC_WTMK_LVL);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Postpone this write, we must do it together with a
|
* Postpone this write, we must do it together with a
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
@ -77,10 +78,16 @@
|
||||||
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
||||||
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
||||||
#define CORE_HC_MCLK_SEL_MASK (3 << 8)
|
#define CORE_HC_MCLK_SEL_MASK (3 << 8)
|
||||||
|
#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15)
|
||||||
|
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
|
||||||
#define CORE_HC_SELECT_IN_EN BIT(18)
|
#define CORE_HC_SELECT_IN_EN BIT(18)
|
||||||
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
||||||
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
||||||
|
|
||||||
|
#define CORE_3_0V_SUPPORT (1 << 25)
|
||||||
|
#define CORE_1_8V_SUPPORT (1 << 26)
|
||||||
|
#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
|
||||||
|
|
||||||
#define CORE_CSR_CDC_CTLR_CFG0 0x130
|
#define CORE_CSR_CDC_CTLR_CFG0 0x130
|
||||||
#define CORE_SW_TRIG_FULL_CALIB BIT(16)
|
#define CORE_SW_TRIG_FULL_CALIB BIT(16)
|
||||||
#define CORE_HW_AUTOCAL_ENA BIT(17)
|
#define CORE_HW_AUTOCAL_ENA BIT(17)
|
||||||
|
@ -148,6 +155,7 @@ struct sdhci_msm_host {
|
||||||
u32 curr_io_level;
|
u32 curr_io_level;
|
||||||
wait_queue_head_t pwr_irq_wait;
|
wait_queue_head_t pwr_irq_wait;
|
||||||
bool pwr_irq_flag;
|
bool pwr_irq_flag;
|
||||||
|
u32 caps_0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
|
static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||||
|
@ -1103,8 +1111,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
|
||||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
u32 irq_status, irq_ack = 0;
|
u32 irq_status, irq_ack = 0;
|
||||||
int retry = 10;
|
int retry = 10;
|
||||||
int pwr_state = 0, io_level = 0;
|
u32 pwr_state = 0, io_level = 0;
|
||||||
|
u32 config;
|
||||||
|
|
||||||
irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
|
irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
|
||||||
irq_status &= INT_MASK;
|
irq_status &= INT_MASK;
|
||||||
|
@ -1161,6 +1169,38 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
|
||||||
*/
|
*/
|
||||||
writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
|
writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we don't have info regarding the voltage levels supported by
|
||||||
|
* regulators, don't change the IO PAD PWR SWITCH.
|
||||||
|
*/
|
||||||
|
if (msm_host->caps_0 & CORE_VOLT_SUPPORT) {
|
||||||
|
u32 new_config;
|
||||||
|
/*
|
||||||
|
* We should unset IO PAD PWR switch only if the register write
|
||||||
|
* can set IO lines high and the regulator also switches to 3 V.
|
||||||
|
* Else, we should keep the IO PAD PWR switch set.
|
||||||
|
* This is applicable to certain targets where eMMC vccq supply
|
||||||
|
* is only 1.8V. In such targets, even during REQ_IO_HIGH, the
|
||||||
|
* IO PAD PWR switch must be kept set to reflect actual
|
||||||
|
* regulator voltage. This way, during initialization of
|
||||||
|
* controllers with only 1.8V, we will set the IO PAD bit
|
||||||
|
* without waiting for a REQ_IO_LOW.
|
||||||
|
*/
|
||||||
|
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||||
|
new_config = config;
|
||||||
|
|
||||||
|
if ((io_level & REQ_IO_HIGH) &&
|
||||||
|
(msm_host->caps_0 & CORE_3_0V_SUPPORT))
|
||||||
|
new_config &= ~CORE_IO_PAD_PWR_SWITCH;
|
||||||
|
else if ((io_level & REQ_IO_LOW) ||
|
||||||
|
(msm_host->caps_0 & CORE_1_8V_SUPPORT))
|
||||||
|
new_config |= CORE_IO_PAD_PWR_SWITCH;
|
||||||
|
|
||||||
|
if (config ^ new_config)
|
||||||
|
writel_relaxed(new_config,
|
||||||
|
host->ioaddr + CORE_VENDOR_SPEC);
|
||||||
|
}
|
||||||
|
|
||||||
if (pwr_state)
|
if (pwr_state)
|
||||||
msm_host->curr_pwr_state = pwr_state;
|
msm_host->curr_pwr_state = pwr_state;
|
||||||
if (io_level)
|
if (io_level)
|
||||||
|
@ -1313,6 +1353,45 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||||
sdhci_msm_check_power_status(host, req_type);
|
sdhci_msm_check_power_status(host, req_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host)
|
||||||
|
{
|
||||||
|
struct mmc_host *mmc = msm_host->mmc;
|
||||||
|
struct regulator *supply = mmc->supply.vqmmc;
|
||||||
|
u32 caps = 0, config;
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
|
if (regulator_is_supported_voltage(supply, 1700000, 1950000))
|
||||||
|
caps |= CORE_1_8V_SUPPORT;
|
||||||
|
if (regulator_is_supported_voltage(supply, 2700000, 3600000))
|
||||||
|
caps |= CORE_3_0V_SUPPORT;
|
||||||
|
|
||||||
|
if (!caps)
|
||||||
|
pr_warn("%s: 1.8/3V not supported for vqmmc\n",
|
||||||
|
mmc_hostname(mmc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caps) {
|
||||||
|
/*
|
||||||
|
* Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH
|
||||||
|
* bit can be used as required later on.
|
||||||
|
*/
|
||||||
|
u32 io_level = msm_host->curr_io_level;
|
||||||
|
|
||||||
|
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||||
|
config |= CORE_IO_PAD_PWR_SWITCH_EN;
|
||||||
|
|
||||||
|
if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT))
|
||||||
|
config &= ~CORE_IO_PAD_PWR_SWITCH;
|
||||||
|
else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT))
|
||||||
|
config |= CORE_IO_PAD_PWR_SWITCH;
|
||||||
|
|
||||||
|
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||||
|
}
|
||||||
|
msm_host->caps_0 |= caps;
|
||||||
|
pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||||
{ .compatible = "qcom,sdhci-msm-v4" },
|
{ .compatible = "qcom,sdhci-msm-v4" },
|
||||||
{},
|
{},
|
||||||
|
@ -1333,7 +1412,6 @@ static const struct sdhci_ops sdhci_msm_ops = {
|
||||||
|
|
||||||
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
||||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||||
SDHCI_QUIRK_NO_CARD_NO_RESET |
|
|
||||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
|
@ -1530,6 +1608,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto pm_runtime_disable;
|
goto pm_runtime_disable;
|
||||||
|
sdhci_msm_set_regulator_caps(msm_host);
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(&pdev->dev);
|
pm_runtime_mark_last_busy(&pdev->dev);
|
||||||
pm_runtime_put_autosuspend(&pdev->dev);
|
pm_runtime_put_autosuspend(&pdev->dev);
|
||||||
|
|
|
@ -290,7 +290,8 @@ static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||||
.ops = &sdhci_arasan_ops,
|
.ops = &sdhci_arasan_ops,
|
||||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||||
|
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||||
};
|
};
|
||||||
|
|
||||||
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||||
|
@ -359,8 +360,7 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
|
||||||
*/
|
*/
|
||||||
static int sdhci_arasan_suspend(struct device *dev)
|
static int sdhci_arasan_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -403,8 +403,7 @@ static int sdhci_arasan_suspend(struct device *dev)
|
||||||
*/
|
*/
|
||||||
static int sdhci_arasan_resume(struct device *dev)
|
static int sdhci_arasan_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/sys_soc.h>
|
||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
#define CON_DDR BIT(19)
|
#define CON_DDR BIT(19)
|
||||||
#define CON_CLKEXTFREE BIT(16)
|
#define CON_CLKEXTFREE BIT(16)
|
||||||
#define CON_PADEN BIT(15)
|
#define CON_PADEN BIT(15)
|
||||||
|
#define CON_CTPL BIT(11)
|
||||||
#define CON_INIT BIT(1)
|
#define CON_INIT BIT(1)
|
||||||
#define CON_OD BIT(0)
|
#define CON_OD BIT(0)
|
||||||
|
|
||||||
|
@ -100,6 +102,7 @@ struct sdhci_omap_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_omap_host {
|
struct sdhci_omap_host {
|
||||||
|
char *version;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct regulator *pbias;
|
struct regulator *pbias;
|
||||||
|
@ -224,6 +227,23 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_omap_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
|
||||||
|
if (enable)
|
||||||
|
reg |= (CON_CTPL | CON_CLKEXTFREE);
|
||||||
|
else
|
||||||
|
reg &= ~(CON_CTPL | CON_CLKEXTFREE);
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
|
||||||
|
|
||||||
|
sdhci_enable_sdio_irq(mmc, enable);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
|
static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
|
||||||
int count)
|
int count)
|
||||||
{
|
{
|
||||||
|
@ -713,10 +733,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
|
||||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
|
||||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_RSP_136_HAS_CRC,
|
SDHCI_QUIRK2_RSP_136_HAS_CRC |
|
||||||
|
SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
|
||||||
.ops = &sdhci_omap_ops,
|
.ops = &sdhci_omap_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_omap_data k2g_data = {
|
||||||
|
.offset = 0x200,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct sdhci_omap_data dra7_data = {
|
static const struct sdhci_omap_data dra7_data = {
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
||||||
|
@ -724,6 +749,7 @@ static const struct sdhci_omap_data dra7_data = {
|
||||||
|
|
||||||
static const struct of_device_id omap_sdhci_match[] = {
|
static const struct of_device_id omap_sdhci_match[] = {
|
||||||
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
|
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
|
||||||
|
{ .compatible = "ti,k2g-sdhci", .data = &k2g_data },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
|
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
|
||||||
|
@ -733,12 +759,21 @@ static struct pinctrl_state
|
||||||
u32 *caps, u32 capmask)
|
u32 *caps, u32 capmask)
|
||||||
{
|
{
|
||||||
struct device *dev = omap_host->dev;
|
struct device *dev = omap_host->dev;
|
||||||
|
char *version = omap_host->version;
|
||||||
struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
|
struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
|
||||||
|
char str[20];
|
||||||
|
|
||||||
if (!(*caps & capmask))
|
if (!(*caps & capmask))
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
snprintf(str, 20, "%s-%s", mode, version);
|
||||||
|
pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ERR(pinctrl_state))
|
||||||
pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
|
pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
|
||||||
|
|
||||||
if (IS_ERR(pinctrl_state)) {
|
if (IS_ERR(pinctrl_state)) {
|
||||||
dev_err(dev, "no pinctrl state for %s mode", mode);
|
dev_err(dev, "no pinctrl state for %s mode", mode);
|
||||||
*caps &= ~capmask;
|
*caps &= ~capmask;
|
||||||
|
@ -807,8 +842,15 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
|
||||||
|
|
||||||
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
|
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
|
||||||
MMC_CAP_1_8V_DDR);
|
MMC_CAP_1_8V_DDR);
|
||||||
|
if (!IS_ERR(state)) {
|
||||||
|
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
|
||||||
|
} else {
|
||||||
|
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_3_3v",
|
||||||
|
caps,
|
||||||
|
MMC_CAP_3_3V_DDR);
|
||||||
if (!IS_ERR(state))
|
if (!IS_ERR(state))
|
||||||
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
|
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
|
||||||
|
}
|
||||||
|
|
||||||
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
|
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
|
||||||
MMC_CAP_SD_HIGHSPEED);
|
MMC_CAP_SD_HIGHSPEED);
|
||||||
|
@ -830,6 +872,16 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct soc_device_attribute sdhci_omap_soc_devices[] = {
|
||||||
|
{
|
||||||
|
.machine = "DRA7[45]*",
|
||||||
|
.revision = "ES1.[01]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* sentinel */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static int sdhci_omap_probe(struct platform_device *pdev)
|
static int sdhci_omap_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -841,6 +893,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
struct sdhci_omap_data *data;
|
struct sdhci_omap_data *data;
|
||||||
|
const struct soc_device_attribute *soc;
|
||||||
|
|
||||||
match = of_match_device(omap_sdhci_match, dev);
|
match = of_match_device(omap_sdhci_match, dev);
|
||||||
if (!match)
|
if (!match)
|
||||||
|
@ -871,10 +924,22 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
||||||
host->ioaddr += offset;
|
host->ioaddr += offset;
|
||||||
|
|
||||||
mmc = host->mmc;
|
mmc = host->mmc;
|
||||||
|
sdhci_get_of_property(pdev);
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_pltfm_free;
|
goto err_pltfm_free;
|
||||||
|
|
||||||
|
soc = soc_device_match(sdhci_omap_soc_devices);
|
||||||
|
if (soc) {
|
||||||
|
omap_host->version = "rev11";
|
||||||
|
if (!strcmp(dev_name(dev), "4809c000.mmc"))
|
||||||
|
mmc->f_max = 96000000;
|
||||||
|
if (!strcmp(dev_name(dev), "480b4000.mmc"))
|
||||||
|
mmc->f_max = 48000000;
|
||||||
|
if (!strcmp(dev_name(dev), "480ad000.mmc"))
|
||||||
|
mmc->f_max = 48000000;
|
||||||
|
}
|
||||||
|
|
||||||
pltfm_host->clk = devm_clk_get(dev, "fck");
|
pltfm_host->clk = devm_clk_get(dev, "fck");
|
||||||
if (IS_ERR(pltfm_host->clk)) {
|
if (IS_ERR(pltfm_host->clk)) {
|
||||||
ret = PTR_ERR(pltfm_host->clk);
|
ret = PTR_ERR(pltfm_host->clk);
|
||||||
|
@ -916,26 +981,31 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
||||||
goto err_put_sync;
|
goto err_put_sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
|
|
||||||
if (ret)
|
|
||||||
goto err_put_sync;
|
|
||||||
|
|
||||||
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
|
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
|
||||||
host->mmc_host_ops.start_signal_voltage_switch =
|
host->mmc_host_ops.start_signal_voltage_switch =
|
||||||
sdhci_omap_start_signal_voltage_switch;
|
sdhci_omap_start_signal_voltage_switch;
|
||||||
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
|
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
|
||||||
host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
|
host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
|
||||||
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
|
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
|
||||||
|
host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
|
||||||
|
|
||||||
sdhci_read_caps(host);
|
ret = sdhci_setup_host(host);
|
||||||
host->caps |= SDHCI_CAN_DO_ADMA2;
|
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_put_sync;
|
goto err_put_sync;
|
||||||
|
|
||||||
|
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
|
||||||
|
if (ret)
|
||||||
|
goto err_cleanup_host;
|
||||||
|
|
||||||
|
ret = __sdhci_add_host(host);
|
||||||
|
if (ret)
|
||||||
|
goto err_cleanup_host;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_cleanup_host:
|
||||||
|
sdhci_cleanup_host(host);
|
||||||
|
|
||||||
err_put_sync:
|
err_put_sync:
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
|
||||||
|
|
|
@ -453,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
|
||||||
enum {
|
enum {
|
||||||
INTEL_DSM_FNS = 0,
|
INTEL_DSM_FNS = 0,
|
||||||
INTEL_DSM_V18_SWITCH = 3,
|
INTEL_DSM_V18_SWITCH = 3,
|
||||||
|
INTEL_DSM_V33_SWITCH = 4,
|
||||||
INTEL_DSM_DRV_STRENGTH = 9,
|
INTEL_DSM_DRV_STRENGTH = 9,
|
||||||
INTEL_DSM_D3_RETUNE = 10,
|
INTEL_DSM_D3_RETUNE = 10,
|
||||||
};
|
};
|
||||||
|
@ -620,17 +621,37 @@ static void intel_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||||
sdhci_writel(host, val, INTEL_HS400_ES_REG);
|
sdhci_writel(host, val, INTEL_HS400_ES_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_intel_voltage_switch(struct sdhci_host *host)
|
static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
|
struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
|
struct device *dev = mmc_dev(mmc);
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||||
struct intel_host *intel_host = sdhci_pci_priv(slot);
|
struct intel_host *intel_host = sdhci_pci_priv(slot);
|
||||||
struct device *dev = &slot->chip->pdev->dev;
|
unsigned int fn;
|
||||||
u32 result = 0;
|
u32 result = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = intel_dsm(intel_host, dev, INTEL_DSM_V18_SWITCH, &result);
|
err = sdhci_start_signal_voltage_switch(mmc, ios);
|
||||||
pr_debug("%s: %s DSM error %d result %u\n",
|
if (err)
|
||||||
mmc_hostname(host->mmc), __func__, err, result);
|
return err;
|
||||||
|
|
||||||
|
switch (ios->signal_voltage) {
|
||||||
|
case MMC_SIGNAL_VOLTAGE_330:
|
||||||
|
fn = INTEL_DSM_V33_SWITCH;
|
||||||
|
break;
|
||||||
|
case MMC_SIGNAL_VOLTAGE_180:
|
||||||
|
fn = INTEL_DSM_V18_SWITCH;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = intel_dsm(intel_host, dev, fn, &result);
|
||||||
|
pr_debug("%s: %s DSM fn %u error %d result %u\n",
|
||||||
|
mmc_hostname(mmc), __func__, fn, err, result);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sdhci_ops sdhci_intel_byt_ops = {
|
static const struct sdhci_ops sdhci_intel_byt_ops = {
|
||||||
|
@ -641,7 +662,6 @@ static const struct sdhci_ops sdhci_intel_byt_ops = {
|
||||||
.reset = sdhci_reset,
|
.reset = sdhci_reset,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
.hw_reset = sdhci_pci_hw_reset,
|
.hw_reset = sdhci_pci_hw_reset,
|
||||||
.voltage_switch = sdhci_intel_voltage_switch,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_ops sdhci_intel_glk_ops = {
|
static const struct sdhci_ops sdhci_intel_glk_ops = {
|
||||||
|
@ -652,7 +672,6 @@ static const struct sdhci_ops sdhci_intel_glk_ops = {
|
||||||
.reset = sdhci_reset,
|
.reset = sdhci_reset,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
.hw_reset = sdhci_pci_hw_reset,
|
.hw_reset = sdhci_pci_hw_reset,
|
||||||
.voltage_switch = sdhci_intel_voltage_switch,
|
|
||||||
.irq = sdhci_cqhci_irq,
|
.irq = sdhci_cqhci_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -691,6 +710,7 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
byt_read_dsm(slot);
|
byt_read_dsm(slot);
|
||||||
|
|
||||||
ops->execute_tuning = intel_execute_tuning;
|
ops->execute_tuning = intel_execute_tuning;
|
||||||
|
ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
|
@ -832,6 +852,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_SD)
|
slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_SD)
|
||||||
slot->host->mmc_host_ops.get_cd = bxt_get_cd;
|
slot->host->mmc_host_ops.get_cd = bxt_get_cd;
|
||||||
|
|
||||||
|
if (slot->chip->pdev->subsystem_vendor == PCI_VENDOR_ID_NI &&
|
||||||
|
slot->chip->pdev->subsystem_device == PCI_SUBDEVICE_ID_NI_78E3)
|
||||||
|
slot->host->mmc->caps2 |= MMC_CAP2_AVOID_3_3V;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#define PCI_DEVICE_ID_REALTEK_5250 0x5250
|
#define PCI_DEVICE_ID_REALTEK_5250 0x5250
|
||||||
|
|
||||||
#define PCI_SUBDEVICE_ID_NI_7884 0x7884
|
#define PCI_SUBDEVICE_ID_NI_7884 0x7884
|
||||||
|
#define PCI_SUBDEVICE_ID_NI_78E3 0x78e3
|
||||||
|
|
||||||
#define PCI_VENDOR_ID_ARASAN 0x16e6
|
#define PCI_VENDOR_ID_ARASAN 0x16e6
|
||||||
#define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670
|
#define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670
|
||||||
|
|
|
@ -200,10 +200,8 @@ static int pic32_sdhci_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "error adding host\n");
|
|
||||||
goto err_base_clk;
|
goto err_base_clk;
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(&pdev->dev, "Successfully added sdhci host\n");
|
dev_info(&pdev->dev, "Successfully added sdhci host\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -221,10 +221,8 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
|
||||||
host->ops = &pxav2_sdhci_ops;
|
host->ops = &pxav2_sdhci_ops;
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "failed to add host\n");
|
|
||||||
goto disable_clk;
|
goto disable_clk;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -472,10 +472,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "failed to add host\n");
|
|
||||||
goto err_add_host;
|
goto err_add_host;
|
||||||
}
|
|
||||||
|
|
||||||
if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
|
if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
|
|
|
@ -655,10 +655,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||||
goto err_req_regs;
|
goto err_req_regs;
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(dev, "sdhci_add_host() failed\n");
|
|
||||||
goto err_req_regs;
|
goto err_req_regs;
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
|
if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
|
||||||
|
|
|
@ -126,10 +126,8 @@ static int sdhci_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_dbg(&pdev->dev, "error adding host\n");
|
|
||||||
goto disable_clk;
|
goto disable_clk;
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
|
|
|
@ -422,10 +422,8 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||||
st_mmcss_cconfig(np, host);
|
st_mmcss_cconfig(np, host);
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "Failed sdhci_add_host\n");
|
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
|
||||||
|
|
||||||
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
|
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
|
||||||
if (timing == MMC_TIMING_UHS_DDR50)
|
if (timing == MMC_TIMING_UHS_DDR50)
|
||||||
tegra_host->ddr_signaling = true;
|
tegra_host->ddr_signaling = true;
|
||||||
|
|
||||||
return sdhci_set_uhs_signaling(host, timing);
|
sdhci_set_uhs_signaling(host, timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
|
static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
|
||||||
|
|
|
@ -814,15 +814,10 @@ static int xenon_add_phy(struct device_node *np, struct sdhci_host *host,
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
int i, ret;
|
int ret;
|
||||||
|
|
||||||
for (i = 0; i < NR_PHY_TYPES; i++) {
|
priv->phy_type = match_string(phy_types, NR_PHY_TYPES, phy_name);
|
||||||
if (!strcmp(phy_name, phy_types[i])) {
|
if (priv->phy_type < 0) {
|
||||||
priv->phy_type = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == NR_PHY_TYPES) {
|
|
||||||
dev_err(mmc_dev(host->mmc),
|
dev_err(mmc_dev(host->mmc),
|
||||||
"Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n",
|
"Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n",
|
||||||
phy_name);
|
phy_name);
|
||||||
|
|
|
@ -709,29 +709,16 @@ static u32 sdhci_sdma_address(struct sdhci_host *host)
|
||||||
return sg_dma_address(host->data->sg);
|
return sg_dma_address(host->data->sg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
static unsigned int sdhci_target_timeout(struct sdhci_host *host,
|
||||||
|
struct mmc_command *cmd,
|
||||||
|
struct mmc_data *data)
|
||||||
{
|
{
|
||||||
u8 count;
|
unsigned int target_timeout;
|
||||||
struct mmc_data *data = cmd->data;
|
|
||||||
unsigned target_timeout, current_timeout;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the host controller provides us with an incorrect timeout
|
|
||||||
* value, just skip the check and use 0xE. The hardware may take
|
|
||||||
* longer to time out, but that's much better than having a too-short
|
|
||||||
* timeout value.
|
|
||||||
*/
|
|
||||||
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
|
|
||||||
return 0xE;
|
|
||||||
|
|
||||||
/* Unspecified timeout, assume max */
|
|
||||||
if (!data && !cmd->busy_timeout)
|
|
||||||
return 0xE;
|
|
||||||
|
|
||||||
/* timeout in us */
|
/* timeout in us */
|
||||||
if (!data)
|
if (!data) {
|
||||||
target_timeout = cmd->busy_timeout * 1000;
|
target_timeout = cmd->busy_timeout * 1000;
|
||||||
else {
|
} else {
|
||||||
target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000);
|
target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000);
|
||||||
if (host->clock && data->timeout_clks) {
|
if (host->clock && data->timeout_clks) {
|
||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
|
@ -748,6 +735,67 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return target_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_calc_sw_timeout(struct sdhci_host *host,
|
||||||
|
struct mmc_command *cmd)
|
||||||
|
{
|
||||||
|
struct mmc_data *data = cmd->data;
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
struct mmc_ios *ios = &mmc->ios;
|
||||||
|
unsigned char bus_width = 1 << ios->bus_width;
|
||||||
|
unsigned int blksz;
|
||||||
|
unsigned int freq;
|
||||||
|
u64 target_timeout;
|
||||||
|
u64 transfer_time;
|
||||||
|
|
||||||
|
target_timeout = sdhci_target_timeout(host, cmd, data);
|
||||||
|
target_timeout *= NSEC_PER_USEC;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
blksz = data->blksz;
|
||||||
|
freq = host->mmc->actual_clock ? : host->clock;
|
||||||
|
transfer_time = (u64)blksz * NSEC_PER_SEC * (8 / bus_width);
|
||||||
|
do_div(transfer_time, freq);
|
||||||
|
/* multiply by '2' to account for any unknowns */
|
||||||
|
transfer_time = transfer_time * 2;
|
||||||
|
/* calculate timeout for the entire data */
|
||||||
|
host->data_timeout = data->blocks * target_timeout +
|
||||||
|
transfer_time;
|
||||||
|
} else {
|
||||||
|
host->data_timeout = target_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host->data_timeout)
|
||||||
|
host->data_timeout += MMC_CMD_TRANSFER_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
||||||
|
bool *too_big)
|
||||||
|
{
|
||||||
|
u8 count;
|
||||||
|
struct mmc_data *data = cmd->data;
|
||||||
|
unsigned target_timeout, current_timeout;
|
||||||
|
|
||||||
|
*too_big = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the host controller provides us with an incorrect timeout
|
||||||
|
* value, just skip the check and use 0xE. The hardware may take
|
||||||
|
* longer to time out, but that's much better than having a too-short
|
||||||
|
* timeout value.
|
||||||
|
*/
|
||||||
|
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
|
||||||
|
return 0xE;
|
||||||
|
|
||||||
|
/* Unspecified timeout, assume max */
|
||||||
|
if (!data && !cmd->busy_timeout)
|
||||||
|
return 0xE;
|
||||||
|
|
||||||
|
/* timeout in us */
|
||||||
|
target_timeout = sdhci_target_timeout(host, cmd, data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Figure out needed cycles.
|
* Figure out needed cycles.
|
||||||
* We do this in steps in order to fit inside a 32 bit int.
|
* We do this in steps in order to fit inside a 32 bit int.
|
||||||
|
@ -768,9 +816,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count >= 0xF) {
|
if (count >= 0xF) {
|
||||||
|
if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
|
||||||
DBG("Too large timeout 0x%x requested for CMD%d!\n",
|
DBG("Too large timeout 0x%x requested for CMD%d!\n",
|
||||||
count, cmd->opcode);
|
count, cmd->opcode);
|
||||||
count = 0xE;
|
count = 0xE;
|
||||||
|
} else {
|
||||||
|
*too_big = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
@ -790,6 +841,16 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
|
||||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable)
|
||||||
|
{
|
||||||
|
if (enable)
|
||||||
|
host->ier |= SDHCI_INT_DATA_TIMEOUT;
|
||||||
|
else
|
||||||
|
host->ier &= ~SDHCI_INT_DATA_TIMEOUT;
|
||||||
|
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||||
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
u8 count;
|
u8 count;
|
||||||
|
@ -797,7 +858,18 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
if (host->ops->set_timeout) {
|
if (host->ops->set_timeout) {
|
||||||
host->ops->set_timeout(host, cmd);
|
host->ops->set_timeout(host, cmd);
|
||||||
} else {
|
} else {
|
||||||
count = sdhci_calc_timeout(host, cmd);
|
bool too_big = false;
|
||||||
|
|
||||||
|
count = sdhci_calc_timeout(host, cmd, &too_big);
|
||||||
|
|
||||||
|
if (too_big &&
|
||||||
|
host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) {
|
||||||
|
sdhci_calc_sw_timeout(host, cmd);
|
||||||
|
sdhci_set_data_timeout_irq(host, false);
|
||||||
|
} else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) {
|
||||||
|
sdhci_set_data_timeout_irq(host, true);
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
|
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -807,6 +879,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
u8 ctrl;
|
u8 ctrl;
|
||||||
struct mmc_data *data = cmd->data;
|
struct mmc_data *data = cmd->data;
|
||||||
|
|
||||||
|
host->data_timeout = 0;
|
||||||
|
|
||||||
if (sdhci_data_line_cmd(cmd))
|
if (sdhci_data_line_cmd(cmd))
|
||||||
sdhci_set_timeout(host, cmd);
|
sdhci_set_timeout(host, cmd);
|
||||||
|
|
||||||
|
@ -1160,13 +1234,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = jiffies;
|
|
||||||
if (!cmd->data && cmd->busy_timeout > 9000)
|
|
||||||
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
|
|
||||||
else
|
|
||||||
timeout += 10 * HZ;
|
|
||||||
sdhci_mod_timer(host, cmd->mrq, timeout);
|
|
||||||
|
|
||||||
host->cmd = cmd;
|
host->cmd = cmd;
|
||||||
if (sdhci_data_line_cmd(cmd)) {
|
if (sdhci_data_line_cmd(cmd)) {
|
||||||
WARN_ON(host->data_cmd);
|
WARN_ON(host->data_cmd);
|
||||||
|
@ -1206,6 +1273,15 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
|
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
|
||||||
flags |= SDHCI_CMD_DATA;
|
flags |= SDHCI_CMD_DATA;
|
||||||
|
|
||||||
|
timeout = jiffies;
|
||||||
|
if (host->data_timeout)
|
||||||
|
timeout += nsecs_to_jiffies(host->data_timeout);
|
||||||
|
else if (!cmd->data && cmd->busy_timeout > 9000)
|
||||||
|
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
|
||||||
|
else
|
||||||
|
timeout += 10 * HZ;
|
||||||
|
sdhci_mod_timer(host, cmd->mrq, timeout);
|
||||||
|
|
||||||
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_send_command);
|
EXPORT_SYMBOL_GPL(sdhci_send_command);
|
||||||
|
@ -3616,6 +3692,10 @@ int sdhci_setup_host(struct sdhci_host *host)
|
||||||
mmc->max_busy_timeout /= host->timeout_clk;
|
mmc->max_busy_timeout /= host->timeout_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT &&
|
||||||
|
!host->ops->get_max_timeout_count)
|
||||||
|
mmc->max_busy_timeout = 0;
|
||||||
|
|
||||||
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||||
|
|
||||||
|
@ -3672,6 +3752,16 @@ int sdhci_setup_host(struct sdhci_host *host)
|
||||||
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
|
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
|
||||||
host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
||||||
SDHCI_SUPPORT_DDR50);
|
SDHCI_SUPPORT_DDR50);
|
||||||
|
/*
|
||||||
|
* The SDHCI controller in a SoC might support HS200/HS400
|
||||||
|
* (indicated using mmc-hs200-1_8v/mmc-hs400-1_8v dt property),
|
||||||
|
* but if the board is modeled such that the IO lines are not
|
||||||
|
* connected to 1.8v then HS200/HS400 cannot be supported.
|
||||||
|
* Disable HS200/HS400 if the board does not have 1.8v connected
|
||||||
|
* to the IO lines. (Applicable for other modes in 1.8v)
|
||||||
|
*/
|
||||||
|
mmc->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES);
|
||||||
|
mmc->caps &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
|
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
|
||||||
|
|
|
@ -332,6 +332,14 @@ struct sdhci_adma2_64_desc {
|
||||||
/* Allow for a a command request and a data request at the same time */
|
/* Allow for a a command request and a data request at the same time */
|
||||||
#define SDHCI_MAX_MRQS 2
|
#define SDHCI_MAX_MRQS 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 48bit command and 136 bit response in 100KHz clock could take upto 2.48ms.
|
||||||
|
* However since the start time of the command, the time between
|
||||||
|
* command and response, and the time between response and start of data is
|
||||||
|
* not known, set the command transfer time to 10ms.
|
||||||
|
*/
|
||||||
|
#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
|
||||||
|
|
||||||
enum sdhci_cookie {
|
enum sdhci_cookie {
|
||||||
COOKIE_UNMAPPED,
|
COOKIE_UNMAPPED,
|
||||||
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
||||||
|
@ -437,6 +445,11 @@ struct sdhci_host {
|
||||||
#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15)
|
#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15)
|
||||||
/* Controller has CRC in 136 bit Command Response */
|
/* Controller has CRC in 136 bit Command Response */
|
||||||
#define SDHCI_QUIRK2_RSP_136_HAS_CRC (1<<16)
|
#define SDHCI_QUIRK2_RSP_136_HAS_CRC (1<<16)
|
||||||
|
/*
|
||||||
|
* Disable HW timeout if the requested timeout is more than the maximum
|
||||||
|
* obtainable timeout.
|
||||||
|
*/
|
||||||
|
#define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1<<17)
|
||||||
|
|
||||||
int irq; /* Device IRQ */
|
int irq; /* Device IRQ */
|
||||||
void __iomem *ioaddr; /* Mapped address */
|
void __iomem *ioaddr; /* Mapped address */
|
||||||
|
@ -550,6 +563,8 @@ struct sdhci_host {
|
||||||
/* Host SDMA buffer boundary. */
|
/* Host SDMA buffer boundary. */
|
||||||
u32 sdma_boundary;
|
u32 sdma_boundary;
|
||||||
|
|
||||||
|
u64 data_timeout;
|
||||||
|
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,36 +13,34 @@
|
||||||
* the License, or (at your option) any later version.
|
* the License, or (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clk/sunxi-ng.h>
|
#include <linux/clk/sunxi-ng.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/scatterlist.h>
|
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/err.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mmc/card.h>
|
||||||
|
#include <linux/mmc/core.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
|
#include <linux/mmc/sd.h>
|
||||||
|
#include <linux/mmc/sdio.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/mmc/sd.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/mmc/sdio.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/mmc/core.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
|
||||||
|
|
||||||
/* register offset definitions */
|
/* register offset definitions */
|
||||||
#define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */
|
#define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */
|
||||||
|
@ -322,10 +320,9 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sunxi_mmc_init_host(struct mmc_host *mmc)
|
static int sunxi_mmc_init_host(struct sunxi_mmc_host *host)
|
||||||
{
|
{
|
||||||
u32 rval;
|
u32 rval;
|
||||||
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
if (sunxi_mmc_reset_host(host))
|
if (sunxi_mmc_reset_host(host))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -859,17 +856,48 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
static void sunxi_mmc_set_bus_width(struct sunxi_mmc_host *host,
|
||||||
|
unsigned char width)
|
||||||
|
{
|
||||||
|
switch (width) {
|
||||||
|
case MMC_BUS_WIDTH_1:
|
||||||
|
mmc_writel(host, REG_WIDTH, SDXC_WIDTH1);
|
||||||
|
break;
|
||||||
|
case MMC_BUS_WIDTH_4:
|
||||||
|
mmc_writel(host, REG_WIDTH, SDXC_WIDTH4);
|
||||||
|
break;
|
||||||
|
case MMC_BUS_WIDTH_8:
|
||||||
|
mmc_writel(host, REG_WIDTH, SDXC_WIDTH8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_mmc_set_clk(struct sunxi_mmc_host *host, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
|
||||||
u32 rval;
|
u32 rval;
|
||||||
|
|
||||||
/* Set the power state */
|
/* set ddr mode */
|
||||||
switch (ios->power_mode) {
|
rval = mmc_readl(host, REG_GCTRL);
|
||||||
case MMC_POWER_ON:
|
if (ios->timing == MMC_TIMING_UHS_DDR50 ||
|
||||||
break;
|
ios->timing == MMC_TIMING_MMC_DDR52)
|
||||||
|
rval |= SDXC_DDR_MODE;
|
||||||
|
else
|
||||||
|
rval &= ~SDXC_DDR_MODE;
|
||||||
|
mmc_writel(host, REG_GCTRL, rval);
|
||||||
|
|
||||||
|
host->ferror = sunxi_mmc_clk_set_rate(host, ios);
|
||||||
|
/* Android code had a usleep_range(50000, 55000); here */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_mmc_card_power(struct sunxi_mmc_host *host,
|
||||||
|
struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
|
||||||
|
switch (ios->power_mode) {
|
||||||
case MMC_POWER_UP:
|
case MMC_POWER_UP:
|
||||||
|
dev_dbg(mmc_dev(mmc), "Powering card up\n");
|
||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vmmc)) {
|
if (!IS_ERR(mmc->supply.vmmc)) {
|
||||||
host->ferror = mmc_regulator_set_ocr(mmc,
|
host->ferror = mmc_regulator_set_ocr(mmc,
|
||||||
mmc->supply.vmmc,
|
mmc->supply.vmmc,
|
||||||
|
@ -887,53 +915,33 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
}
|
}
|
||||||
host->vqmmc_enabled = true;
|
host->vqmmc_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->ferror = sunxi_mmc_init_host(mmc);
|
|
||||||
if (host->ferror)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dev_dbg(mmc_dev(mmc), "power on!\n");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MMC_POWER_OFF:
|
case MMC_POWER_OFF:
|
||||||
dev_dbg(mmc_dev(mmc), "power off!\n");
|
dev_dbg(mmc_dev(mmc), "Powering card off\n");
|
||||||
sunxi_mmc_reset_host(host);
|
|
||||||
if (!IS_ERR(mmc->supply.vmmc))
|
if (!IS_ERR(mmc->supply.vmmc))
|
||||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
|
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
|
||||||
regulator_disable(mmc->supply.vqmmc);
|
regulator_disable(mmc->supply.vqmmc);
|
||||||
|
|
||||||
host->vqmmc_enabled = false;
|
host->vqmmc_enabled = false;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* set bus width */
|
default:
|
||||||
switch (ios->bus_width) {
|
dev_dbg(mmc_dev(mmc), "Ignoring unknown card power state\n");
|
||||||
case MMC_BUS_WIDTH_1:
|
|
||||||
mmc_writel(host, REG_WIDTH, SDXC_WIDTH1);
|
|
||||||
break;
|
|
||||||
case MMC_BUS_WIDTH_4:
|
|
||||||
mmc_writel(host, REG_WIDTH, SDXC_WIDTH4);
|
|
||||||
break;
|
|
||||||
case MMC_BUS_WIDTH_8:
|
|
||||||
mmc_writel(host, REG_WIDTH, SDXC_WIDTH8);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* set ddr mode */
|
static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
rval = mmc_readl(host, REG_GCTRL);
|
{
|
||||||
if (ios->timing == MMC_TIMING_UHS_DDR50 ||
|
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
||||||
ios->timing == MMC_TIMING_MMC_DDR52)
|
|
||||||
rval |= SDXC_DDR_MODE;
|
|
||||||
else
|
|
||||||
rval &= ~SDXC_DDR_MODE;
|
|
||||||
mmc_writel(host, REG_GCTRL, rval);
|
|
||||||
|
|
||||||
/* set up clock */
|
sunxi_mmc_card_power(host, ios);
|
||||||
if (ios->power_mode) {
|
sunxi_mmc_set_bus_width(host, ios->bus_width);
|
||||||
host->ferror = sunxi_mmc_clk_set_rate(host, ios);
|
sunxi_mmc_set_clk(host, ios);
|
||||||
/* Android code had a usleep_range(50000, 55000); here */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
@ -955,6 +963,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 imask;
|
u32 imask;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
pm_runtime_get_noresume(host->dev);
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
imask = mmc_readl(host, REG_IMASK);
|
imask = mmc_readl(host, REG_IMASK);
|
||||||
|
@ -967,6 +978,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
}
|
}
|
||||||
mmc_writel(host, REG_IMASK, imask);
|
mmc_writel(host, REG_IMASK, imask);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
pm_runtime_put_noidle(host->mmc->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sunxi_mmc_hw_reset(struct mmc_host *mmc)
|
static void sunxi_mmc_hw_reset(struct mmc_host *mmc)
|
||||||
|
@ -1380,6 +1394,15 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_free_dma;
|
goto error_free_dma;
|
||||||
|
|
||||||
|
ret = sunxi_mmc_init_host(host);
|
||||||
|
if (ret)
|
||||||
|
goto error_free_dma;
|
||||||
|
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
||||||
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
ret = mmc_add_host(mmc);
|
ret = mmc_add_host(mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_free_dma;
|
goto error_free_dma;
|
||||||
|
@ -1400,6 +1423,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
|
||||||
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
|
pm_runtime_force_suspend(&pdev->dev);
|
||||||
disable_irq(host->irq);
|
disable_irq(host->irq);
|
||||||
sunxi_mmc_disable(host);
|
sunxi_mmc_disable(host);
|
||||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||||
|
@ -1408,10 +1432,47 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int sunxi_mmc_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
|
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sunxi_mmc_enable(host);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
sunxi_mmc_init_host(host);
|
||||||
|
sunxi_mmc_set_bus_width(host, mmc->ios.bus_width);
|
||||||
|
sunxi_mmc_set_clk(host, &mmc->ios);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sunxi_mmc_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
|
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
sunxi_mmc_reset_host(host);
|
||||||
|
sunxi_mmc_disable(host);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sunxi_mmc_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
|
||||||
|
sunxi_mmc_runtime_resume,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver sunxi_mmc_driver = {
|
static struct platform_driver sunxi_mmc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sunxi-mmc",
|
.name = "sunxi-mmc",
|
||||||
.of_match_table = of_match_ptr(sunxi_mmc_of_match),
|
.of_match_table = of_match_ptr(sunxi_mmc_of_match),
|
||||||
|
.pm = &sunxi_mmc_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = sunxi_mmc_probe,
|
.probe = sunxi_mmc_probe,
|
||||||
.remove = sunxi_mmc_remove,
|
.remove = sunxi_mmc_remove,
|
||||||
|
|
|
@ -300,8 +300,10 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||||
pipe = usb_sndbulkpipe(ushc->usb_dev, 2);
|
pipe = usb_sndbulkpipe(ushc->usb_dev, 2);
|
||||||
|
|
||||||
usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe,
|
usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe,
|
||||||
sg_virt(data->sg), data->sg->length,
|
NULL, data->sg->length,
|
||||||
data_callback, ushc);
|
data_callback, ushc);
|
||||||
|
ushc->data_urb->num_sgs = 1;
|
||||||
|
ushc->data_urb->sg = data->sg;
|
||||||
ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC);
|
ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -268,43 +268,29 @@ static inline int wbsd_next_sg(struct wbsd_host *host)
|
||||||
return host->num_sg;
|
return host->num_sg;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
|
static inline char *wbsd_map_sg(struct wbsd_host *host)
|
||||||
{
|
{
|
||||||
return sg_virt(host->cur_sg);
|
return kmap_atomic(sg_page(host->cur_sg)) + host->cur_sg->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
|
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
unsigned int len, i;
|
size_t len = 0;
|
||||||
struct scatterlist *sg;
|
int i;
|
||||||
char *dmabuf = host->dma_buffer;
|
|
||||||
char *sgbuf;
|
|
||||||
|
|
||||||
sg = data->sg;
|
for (i = 0; i < data->sg_len; i++)
|
||||||
len = data->sg_len;
|
len += data->sg[i].length;
|
||||||
|
sg_copy_to_buffer(data->sg, data->sg_len, host->dma_buffer, len);
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
sgbuf = sg_virt(&sg[i]);
|
|
||||||
memcpy(dmabuf, sgbuf, sg[i].length);
|
|
||||||
dmabuf += sg[i].length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
|
static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
unsigned int len, i;
|
size_t len = 0;
|
||||||
struct scatterlist *sg;
|
int i;
|
||||||
char *dmabuf = host->dma_buffer;
|
|
||||||
char *sgbuf;
|
|
||||||
|
|
||||||
sg = data->sg;
|
for (i = 0; i < data->sg_len; i++)
|
||||||
len = data->sg_len;
|
len += data->sg[i].length;
|
||||||
|
sg_copy_from_buffer(data->sg, data->sg_len, host->dma_buffer, len);
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
sgbuf = sg_virt(&sg[i]);
|
|
||||||
memcpy(sgbuf, dmabuf, sg[i].length);
|
|
||||||
dmabuf += sg[i].length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -418,7 +404,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_data *data = host->mrq->cmd->data;
|
struct mmc_data *data = host->mrq->cmd->data;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int i, fsr, fifo;
|
int i, idx, fsr, fifo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle excessive data.
|
* Handle excessive data.
|
||||||
|
@ -426,7 +412,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
||||||
if (host->num_sg == 0)
|
if (host->num_sg == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer = wbsd_sg_to_buffer(host) + host->offset;
|
buffer = wbsd_map_sg(host) + host->offset;
|
||||||
|
idx = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drain the fifo. This has a tendency to loop longer
|
* Drain the fifo. This has a tendency to loop longer
|
||||||
|
@ -445,8 +432,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
||||||
fifo = 1;
|
fifo = 1;
|
||||||
|
|
||||||
for (i = 0; i < fifo; i++) {
|
for (i = 0; i < fifo; i++) {
|
||||||
*buffer = inb(host->base + WBSD_DFR);
|
buffer[idx++] = inb(host->base + WBSD_DFR);
|
||||||
buffer++;
|
|
||||||
host->offset++;
|
host->offset++;
|
||||||
host->remain--;
|
host->remain--;
|
||||||
|
|
||||||
|
@ -456,16 +442,19 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
||||||
* End of scatter list entry?
|
* End of scatter list entry?
|
||||||
*/
|
*/
|
||||||
if (host->remain == 0) {
|
if (host->remain == 0) {
|
||||||
|
kunmap_atomic(buffer);
|
||||||
/*
|
/*
|
||||||
* Get next entry. Check if last.
|
* Get next entry. Check if last.
|
||||||
*/
|
*/
|
||||||
if (!wbsd_next_sg(host))
|
if (!wbsd_next_sg(host))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer = wbsd_sg_to_buffer(host);
|
buffer = wbsd_map_sg(host);
|
||||||
|
idx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kunmap_atomic(buffer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a very dirty hack to solve a
|
* This is a very dirty hack to solve a
|
||||||
|
@ -480,7 +469,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_data *data = host->mrq->cmd->data;
|
struct mmc_data *data = host->mrq->cmd->data;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int i, fsr, fifo;
|
int i, idx, fsr, fifo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that we aren't being called after the
|
* Check that we aren't being called after the
|
||||||
|
@ -489,7 +478,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
||||||
if (host->num_sg == 0)
|
if (host->num_sg == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer = wbsd_sg_to_buffer(host) + host->offset;
|
buffer = wbsd_map_sg(host) + host->offset;
|
||||||
|
idx = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill the fifo. This has a tendency to loop longer
|
* Fill the fifo. This has a tendency to loop longer
|
||||||
|
@ -508,8 +498,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
||||||
fifo = 15;
|
fifo = 15;
|
||||||
|
|
||||||
for (i = 16; i > fifo; i--) {
|
for (i = 16; i > fifo; i--) {
|
||||||
outb(*buffer, host->base + WBSD_DFR);
|
outb(buffer[idx], host->base + WBSD_DFR);
|
||||||
buffer++;
|
|
||||||
host->offset++;
|
host->offset++;
|
||||||
host->remain--;
|
host->remain--;
|
||||||
|
|
||||||
|
@ -519,16 +508,19 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
||||||
* End of scatter list entry?
|
* End of scatter list entry?
|
||||||
*/
|
*/
|
||||||
if (host->remain == 0) {
|
if (host->remain == 0) {
|
||||||
|
kunmap_atomic(buffer);
|
||||||
/*
|
/*
|
||||||
* Get next entry. Check if last.
|
* Get next entry. Check if last.
|
||||||
*/
|
*/
|
||||||
if (!wbsd_next_sg(host))
|
if (!wbsd_next_sg(host))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer = wbsd_sg_to_buffer(host);
|
buffer = wbsd_map_sg(host);
|
||||||
|
idx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kunmap_atomic(buffer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The controller stops sending interrupts for
|
* The controller stops sending interrupts for
|
||||||
|
|
|
@ -928,8 +928,7 @@ static int wmt_mci_remove(struct platform_device *pdev)
|
||||||
static int wmt_mci_suspend(struct device *dev)
|
static int wmt_mci_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
u32 reg_tmp;
|
u32 reg_tmp;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
|
||||||
struct wmt_mci_priv *priv;
|
struct wmt_mci_priv *priv;
|
||||||
|
|
||||||
if (!mmc)
|
if (!mmc)
|
||||||
|
@ -953,8 +952,7 @@ static int wmt_mci_suspend(struct device *dev)
|
||||||
static int wmt_mci_resume(struct device *dev)
|
static int wmt_mci_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
u32 reg_tmp;
|
u32 reg_tmp;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
|
||||||
struct wmt_mci_priv *priv;
|
struct wmt_mci_priv *priv;
|
||||||
|
|
||||||
if (mmc) {
|
if (mmc) {
|
||||||
|
|
49
include/dt-bindings/dma/jz4780-dma.h
Normal file
49
include/dt-bindings/dma/jz4780-dma.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef __DT_BINDINGS_DMA_JZ4780_DMA_H__
|
||||||
|
#define __DT_BINDINGS_DMA_JZ4780_DMA_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request type numbers for the JZ4780 DMA controller (written to the DRTn
|
||||||
|
* register for the channel).
|
||||||
|
*/
|
||||||
|
#define JZ4780_DMA_I2S1_TX 0x4
|
||||||
|
#define JZ4780_DMA_I2S1_RX 0x5
|
||||||
|
#define JZ4780_DMA_I2S0_TX 0x6
|
||||||
|
#define JZ4780_DMA_I2S0_RX 0x7
|
||||||
|
#define JZ4780_DMA_AUTO 0x8
|
||||||
|
#define JZ4780_DMA_SADC_RX 0x9
|
||||||
|
#define JZ4780_DMA_UART4_TX 0xc
|
||||||
|
#define JZ4780_DMA_UART4_RX 0xd
|
||||||
|
#define JZ4780_DMA_UART3_TX 0xe
|
||||||
|
#define JZ4780_DMA_UART3_RX 0xf
|
||||||
|
#define JZ4780_DMA_UART2_TX 0x10
|
||||||
|
#define JZ4780_DMA_UART2_RX 0x11
|
||||||
|
#define JZ4780_DMA_UART1_TX 0x12
|
||||||
|
#define JZ4780_DMA_UART1_RX 0x13
|
||||||
|
#define JZ4780_DMA_UART0_TX 0x14
|
||||||
|
#define JZ4780_DMA_UART0_RX 0x15
|
||||||
|
#define JZ4780_DMA_SSI0_TX 0x16
|
||||||
|
#define JZ4780_DMA_SSI0_RX 0x17
|
||||||
|
#define JZ4780_DMA_SSI1_TX 0x18
|
||||||
|
#define JZ4780_DMA_SSI1_RX 0x19
|
||||||
|
#define JZ4780_DMA_MSC0_TX 0x1a
|
||||||
|
#define JZ4780_DMA_MSC0_RX 0x1b
|
||||||
|
#define JZ4780_DMA_MSC1_TX 0x1c
|
||||||
|
#define JZ4780_DMA_MSC1_RX 0x1d
|
||||||
|
#define JZ4780_DMA_MSC2_TX 0x1e
|
||||||
|
#define JZ4780_DMA_MSC2_RX 0x1f
|
||||||
|
#define JZ4780_DMA_PCM0_TX 0x20
|
||||||
|
#define JZ4780_DMA_PCM0_RX 0x21
|
||||||
|
#define JZ4780_DMA_SMB0_TX 0x24
|
||||||
|
#define JZ4780_DMA_SMB0_RX 0x25
|
||||||
|
#define JZ4780_DMA_SMB1_TX 0x26
|
||||||
|
#define JZ4780_DMA_SMB1_RX 0x27
|
||||||
|
#define JZ4780_DMA_SMB2_TX 0x28
|
||||||
|
#define JZ4780_DMA_SMB2_RX 0x29
|
||||||
|
#define JZ4780_DMA_SMB3_TX 0x2a
|
||||||
|
#define JZ4780_DMA_SMB3_RX 0x2b
|
||||||
|
#define JZ4780_DMA_SMB4_TX 0x2c
|
||||||
|
#define JZ4780_DMA_SMB4_RX 0x2d
|
||||||
|
#define JZ4780_DMA_DES_TX 0x2e
|
||||||
|
#define JZ4780_DMA_DES_RX 0x2f
|
||||||
|
|
||||||
|
#endif /* __DT_BINDINGS_DMA_JZ4780_DMA_H__ */
|
|
@ -156,6 +156,7 @@ struct sd_switch_caps {
|
||||||
#define UHS_DDR50_MAX_DTR 50000000
|
#define UHS_DDR50_MAX_DTR 50000000
|
||||||
#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR
|
#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR
|
||||||
#define UHS_SDR12_MAX_DTR 25000000
|
#define UHS_SDR12_MAX_DTR 25000000
|
||||||
|
#define DEFAULT_SPEED_MAX_DTR UHS_SDR12_MAX_DTR
|
||||||
unsigned int sd3_bus_mode;
|
unsigned int sd3_bus_mode;
|
||||||
#define UHS_SDR12_BUS_SPEED 0
|
#define UHS_SDR12_BUS_SPEED 0
|
||||||
#define HIGH_SPEED_BUS_SPEED 1
|
#define HIGH_SPEED_BUS_SPEED 1
|
||||||
|
@ -252,6 +253,7 @@ struct mmc_card {
|
||||||
#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */
|
#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */
|
||||||
unsigned int state; /* (our) card state */
|
unsigned int state; /* (our) card state */
|
||||||
unsigned int quirks; /* card quirks */
|
unsigned int quirks; /* card quirks */
|
||||||
|
unsigned int quirk_max_rate; /* max rate set by quirks */
|
||||||
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
|
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
|
||||||
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
|
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
|
||||||
/* for byte mode */
|
/* for byte mode */
|
||||||
|
|
|
@ -177,6 +177,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd,
|
||||||
int retries);
|
int retries);
|
||||||
|
|
||||||
int mmc_hw_reset(struct mmc_host *host);
|
int mmc_hw_reset(struct mmc_host *host);
|
||||||
|
int mmc_sw_reset(struct mmc_host *host);
|
||||||
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card);
|
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card);
|
||||||
|
|
||||||
#endif /* LINUX_MMC_CORE_H */
|
#endif /* LINUX_MMC_CORE_H */
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
struct mmc_ios {
|
struct mmc_ios {
|
||||||
unsigned int clock; /* clock rate */
|
unsigned int clock; /* clock rate */
|
||||||
unsigned short vdd;
|
unsigned short vdd;
|
||||||
|
unsigned int power_delay_ms; /* waiting for stable power */
|
||||||
|
|
||||||
/* vdd stores the bit number of the selected voltage range from below. */
|
/* vdd stores the bit number of the selected voltage range from below. */
|
||||||
|
|
||||||
|
@ -320,6 +321,9 @@ struct mmc_host {
|
||||||
#define MMC_CAP_UHS_SDR50 (1 << 18) /* Host supports UHS SDR50 mode */
|
#define MMC_CAP_UHS_SDR50 (1 << 18) /* Host supports UHS SDR50 mode */
|
||||||
#define MMC_CAP_UHS_SDR104 (1 << 19) /* Host supports UHS SDR104 mode */
|
#define MMC_CAP_UHS_SDR104 (1 << 19) /* Host supports UHS SDR104 mode */
|
||||||
#define MMC_CAP_UHS_DDR50 (1 << 20) /* Host supports UHS DDR50 mode */
|
#define MMC_CAP_UHS_DDR50 (1 << 20) /* Host supports UHS DDR50 mode */
|
||||||
|
#define MMC_CAP_UHS (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | \
|
||||||
|
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | \
|
||||||
|
MMC_CAP_UHS_DDR50)
|
||||||
/* (1 << 21) is free for reuse */
|
/* (1 << 21) is free for reuse */
|
||||||
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
|
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
|
||||||
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
|
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
|
||||||
|
@ -345,6 +349,7 @@ struct mmc_host {
|
||||||
#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */
|
#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */
|
||||||
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
|
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
|
||||||
MMC_CAP2_HS400_1_2V)
|
MMC_CAP2_HS400_1_2V)
|
||||||
|
#define MMC_CAP2_HSX00_1_8V (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)
|
||||||
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
|
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
|
||||||
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
||||||
#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */
|
#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */
|
||||||
|
@ -354,6 +359,7 @@ struct mmc_host {
|
||||||
#define MMC_CAP2_NO_MMC (1 << 22) /* Do not send (e)MMC commands during initialization */
|
#define MMC_CAP2_NO_MMC (1 << 22) /* Do not send (e)MMC commands during initialization */
|
||||||
#define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */
|
#define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */
|
||||||
#define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */
|
#define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */
|
||||||
|
#define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */
|
||||||
|
|
||||||
int fixed_drv_type; /* fixed driver type for non-removable media */
|
int fixed_drv_type; /* fixed driver type for non-removable media */
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
|
#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
|
||||||
#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
|
#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
|
||||||
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
|
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
|
||||||
|
#define SDIO_DEVICE_ID_MARVELL_8887WLAN 0x9134
|
||||||
|
|
||||||
#define SDIO_VENDOR_ID_SIANO 0x039a
|
#define SDIO_VENDOR_ID_SIANO 0x039a
|
||||||
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
|
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
|
||||||
|
|
Loading…
Add table
Reference in a new issue