diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 02:11:43 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 02:11:43 +0300 |
commit | f60342fac9fae20ada2cd5faadbc2a1337cae03f (patch) | |
tree | fe64b1cb3ea699d819e5e808264903aee2d8dc9a | |
parent | 5231804cf9e584f3e7e763a0d6d2fffe011c1bce (diff) | |
parent | ef5332c10d4f332a2ac79e9ad5452f4e89d1815a (diff) | |
download | linux-f60342fac9fae20ada2cd5faadbc2a1337cae03f.tar.xz |
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
...
66 files changed, 1382 insertions, 431 deletions
diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt index 50bf611a4d2c..13e70409e8ac 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt @@ -12,6 +12,7 @@ Required properties: - "amlogic,meson-gxbb-mmc" - "amlogic,meson-gxl-mmc" - "amlogic,meson-gxm-mmc" + - "amlogic,meson-axg-mmc" - clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names. - clock-names: Should contain the following: "core" - Main peripheral bus clock @@ -19,6 +20,7 @@ Required properties: "clkin1" - Other parent clock of internal mux The driver has an internal mux clock which switches between clkin0 and clkin1 depending on the clock rate requested by the MMC core. +- resets : phandle of the internal reset line Example: @@ -29,4 +31,5 @@ Example: clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>; clock-names = "core", "clkin0", "clkin1"; pinctrl-0 = <&emmc_pins>; + resets = <&reset RESET_SD_EMMC_A>; }; diff --git a/Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt new file mode 100644 index 000000000000..b0f0999ea1a9 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt @@ -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; + }; diff --git a/Documentation/devicetree/bindings/mmc/jz4740.txt b/Documentation/devicetree/bindings/mmc/jz4740.txt new file mode 100644 index 000000000000..7cd8c432d7c8 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/jz4740.txt @@ -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"; +}; diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 467cd7b147ce..f5a0923b34ca 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -19,6 +19,8 @@ Optional properties: - wp-gpios: Specify GPIOs for write protection, see gpio binding - 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 +- 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 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 @@ -56,6 +58,10 @@ Optional properties: - 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 (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 polarity properties, we have to fix the meaning of the "normal" and "inverted" diff --git a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt index 51775a372c06..393848c2138e 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt @@ -4,7 +4,14 @@ Refer to mmc.txt for standard MMC bindings. Required properties: - 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 + (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: mmc1: mmc@4809c000 { diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index 2d5287eeed95..ee978c95189d 100644 --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -26,6 +26,8 @@ Required properties: "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC "renesas,sdhi-r8a7795" - SDHI IP on R8A7795 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-shmobile" - a generic sh-mobile SDHI controller "renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index 38078594cf97..50cff3cbcc6d 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts @@ -36,6 +36,28 @@ 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 { status = "okay"; @@ -203,4 +225,16 @@ groups = "nemc-cs6"; 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; + }; }; diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index 9b5794667aee..b72e53bb7292 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <dt-bindings/clock/jz4780-cgu.h> +#include <dt-bindings/dma/jz4780-dma.h> / { #address-cells = <1>; @@ -241,6 +242,57 @@ 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 { compatible = "ingenic,jz4780-bch"; reg = <0x134d0000 0x10000>; diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig index b5f4ad8f2c45..be23fd25eeaa 100644 --- a/arch/mips/configs/ci20_defconfig +++ b/arch/mips/configs/ci20_defconfig @@ -104,10 +104,14 @@ CONFIG_REGULATOR_FIXED_VOLTAGE=y # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set CONFIG_MMC=y +CONFIG_MMC_JZ4740=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_JZ4740=y +CONFIG_DMADEVICES=y +CONFIG_DMA_JZ4780=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_MEMORY=y +CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set CONFIG_PROC_KCORE=y # CONFIG_PROC_PAGE_MONITOR is not set diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index d89e17829527..a0b9102c4c6e 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -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); md->disk->flags = GENHD_FL_EXT_DEVT; 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: @@ -2965,9 +2966,11 @@ static void mmc_blk_remove(struct mmc_card *card) mmc_blk_remove_debugfs(card, md); mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); - mmc_claim_host(card->host); - mmc_blk_part_switch(card, md->part_type); - mmc_release_host(card->host); + if (md->part_curr != md->part_type) { + mmc_claim_host(card->host); + mmc_blk_part_switch(card, md->part_type); + mmc_release_host(card->host); + } if (card->type != MMC_TYPE_SD_COMBO) pm_runtime_disable(&card->dev); pm_runtime_put_noidle(&card->dev); diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index 9c821eedd156..1170feb8f969 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -149,6 +149,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int 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. */ diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 121ce50b6d5e..281826d1fcca 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -50,9 +50,6 @@ #include "sd_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 */ #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) { u32 clock; @@ -1646,19 +1654,13 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); - /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ - if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330)) - 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"); + mmc_set_initial_signal_voltage(host); /* * This delay should be sufficient to allow the power supply * to reach the minimum voltage. */ - mmc_delay(10); + mmc_delay(host->ios.power_delay_ms); 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 * 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) @@ -1967,6 +1969,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, unsigned int qty = 0, busy_timeout = 0; bool use_r1b_resp = false; unsigned long timeout; + int loop_udelay=64, udelay_max=32768; int err; mmc_retune_hold(card->host); @@ -2091,9 +2094,15 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, err = -EIO; 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: mmc_retune_release(card->host); return err; @@ -2435,22 +2444,46 @@ int mmc_hw_reset(struct mmc_host *host) return -EINVAL; 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); return -EOPNOTSUPP; } - ret = host->bus_ops->reset(host); + ret = host->bus_ops->hw_reset(host); mmc_bus_put(host); 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); return ret; } 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) { host->f_init = freq; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index d6303d69071b..9d8f09ac0821 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -32,7 +32,8 @@ struct mmc_bus_ops { int (*power_restore)(struct mmc_host *); int (*alive)(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); @@ -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_host_set_uhs_voltage(struct mmc_host *host); 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_driver_type(struct mmc_host *host, unsigned int drv_type); int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 64b03d6eaf18..abf9e884386c 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -143,9 +143,6 @@ int mmc_retune(struct mmc_host *host) goto out; return_to_hs400 = true; - - if (host->ops->prepare_hs400_tuning) - host->ops->prepare_hs400_tuning(host, &host->ios); } 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) { struct device *dev = host->parent; - u32 bus_width, drv_type; + u32 bus_width, drv_type, cd_debounce_delay_ms; int ret; bool cd_cap_invert, cd_gpio_invert = false; bool ro_cap_invert, ro_gpio_invert = false; @@ -230,11 +227,16 @@ int mmc_of_parse(struct mmc_host *host) } else { 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")) host->caps |= MMC_CAP_NEEDS_POLL; ret = mmc_gpiod_request_cd(host, "cd", 0, true, - 0, &cd_gpio_invert); + cd_debounce_delay_ms, + &cd_gpio_invert); if (!ret) dev_info(host->parent, "Got CD GPIO\n"); else if (ret != -ENOENT && ret != -ENOSYS) @@ -338,6 +340,9 @@ int mmc_of_parse(struct mmc_host *host) host->dsr_req = 0; } + device_property_read_u32(dev, "post-power-on-delay-ms", + &host->ios.power_delay_ms); + 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->fixed_drv_type = -EINVAL; + host->ios.power_delay_ms = 10; return host; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6f8ebd6caa4c..4466f5de54d4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1282,6 +1282,10 @@ int mmc_hs400_to_hs200(struct mmc_card *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; 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) host->card = card; @@ -2117,7 +2129,7 @@ static int mmc_can_reset(struct mmc_card *card) 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; @@ -2151,7 +2163,7 @@ static const struct mmc_bus_ops mmc_ops = { .runtime_resume = mmc_runtime_resume, .alive = mmc_alive, .shutdown = mmc_shutdown, - .reset = mmc_reset, + .hw_reset = _mmc_hw_reset, }; /* diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 13ef162cf066..a8b9fee4d62a 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -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; if (!IS_ERR(reset_gpios)) { - int i; - int values[reset_gpios->ndescs]; + int i, *values; + 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; - gpiod_set_array_value_cansleep( - reset_gpios->ndescs, reset_gpios->desc, values); + gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values); + kfree(values); } } diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 5153577754f0..dd2f73af8f2c 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -132,6 +132,9 @@ static const struct mmc_fixup sdio_fixup_methods[] = { SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0, 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 }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index baf3d5da4ccb..d0d9f90e7cdf 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1058,6 +1058,14 @@ retry: 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: host->card = card; return 0; @@ -1214,7 +1222,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) 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); 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, .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, - .reset = mmc_sd_reset, + .hw_reset = mmc_sd_hw_reset, }; /* diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c599a628a387..a86490dbca70 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -444,6 +444,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) unsigned int bus_speed, timing; int err; unsigned char speed; + unsigned int max_rate; /* * 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) return err; + max_rate = min_not_zero(card->quirk_max_rate, + card->sw_caps.uhs_max_dtr); + if (bus_speed) { 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; @@ -788,6 +792,14 @@ try_again: if (err) 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: if (!oldcard) host->card = card; @@ -801,6 +813,22 @@ 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. */ @@ -948,14 +976,7 @@ static int mmc_sdio_resume(struct mmc_host *host) /* No need to reinitialize powered-resumed nonremovable cards */ if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { - sdio_reset(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)); + err = mmc_sdio_reinit_card(host, mmc_card_keep_power(host)); } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); @@ -978,8 +999,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host) { int ret; - mmc_claim_host(host); - /* * Reset the card by performing the same steps that are taken by * 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_go_idle(host); - mmc_send_if_cond(host, host->card->ocr); - - ret = mmc_send_io_op_cond(host, 0, NULL); - if (ret) - goto out; + mmc_claim_host(host); - ret = mmc_sdio_init_card(host, host->card->ocr, host->card, - mmc_card_keep_power(host)); + ret = mmc_sdio_reinit_card(host, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); -out: mmc_release_host(host); return ret; @@ -1039,12 +1050,24 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host) 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); 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 = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, @@ -1055,7 +1078,8 @@ static const struct mmc_bus_ops mmc_sdio_ops = { .runtime_resume = mmc_sdio_runtime_resume, .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, - .reset = mmc_sdio_reset, + .hw_reset = mmc_sdio_hw_reset, + .sw_reset = mmc_sdio_sw_reset, }; diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 31f7dbb15668..ef05e0039378 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -28,15 +28,17 @@ struct mmc_gpio { irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); char *ro_label; char cd_label[0]; + u32 cd_debounce_delay_ms; }; static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) { /* Schedule a card detection after a debounce timeout */ struct mmc_host *host = dev_id; + struct mmc_gpio *ctx = host->slot.handler_priv; 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; } @@ -49,6 +51,7 @@ int mmc_gpio_alloc(struct mmc_host *host) if (ctx) { 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->ro_label, len, "%s ro", dev_name(host->parent)); host->slot.handler_priv = ctx; @@ -76,15 +79,22 @@ EXPORT_SYMBOL(mmc_gpio_get_ro); int mmc_gpio_get_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; + int cansleep; if (!ctx || !ctx->cd_gpio) return -ENOSYS; - if (ctx->override_cd_active_level) - return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^ - !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); + cansleep = gpiod_cansleep(ctx->cd_gpio); + if (ctx->override_cd_active_level) { + 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); @@ -261,7 +271,7 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, if (debounce) { ret = gpiod_set_debounce(desc, debounce); if (ret < 0) - return ret; + ctx->cd_debounce_delay_ms = debounce; } if (gpio_invert) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9589f9c9046f..0581c199c996 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -345,11 +345,11 @@ config MMC_SDHCI_IPROC If unsure, say N. 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 help 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. 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" depends on ARCH_MESON || COMPILE_TEST depends on COMMON_CLK - depends on HAS_DMA depends on OF help This selects support for the SD/MMC Host Controller on @@ -401,7 +400,6 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" - depends on HAS_DMA depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST help This selects the TI OMAP High Speed Multimedia card Interface. @@ -511,7 +509,6 @@ config MMC_DAVINCI config MMC_GOLDFISH tristate "goldfish qemu Multimedia Card Interface support" - depends on HAS_DMA depends on GOLDFISH || COMPILE_TEST help This selects the Goldfish Multimedia card Interface emulation @@ -605,7 +602,7 @@ config MMC_SDHI config MMC_SDHI_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) help This provides DMA support for SDHI SD/SDIO controllers @@ -615,7 +612,7 @@ config MMC_SDHI_SYS_DMAC config MMC_SDHI_INTERNAL_DMAC tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering" depends on ARM64 || COMPILE_TEST - depends on MMC_SDHI && HAS_DMA + depends on MMC_SDHI default MMC_SDHI if ARM64 help This provides DMA support for SDHI SD/SDIO controllers @@ -669,7 +666,6 @@ config MMC_CAVIUM_THUNDERX config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on HAS_DMA depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP @@ -690,6 +686,15 @@ config MMC_DW_PLTFM 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 tristate "Exynos specific extensions for Synopsys DW Memory Card Interface" depends on MMC_DW @@ -748,7 +753,6 @@ config MMC_DW_ZX config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" - depends on HAS_DMA depends on SUPERH || ARCH_RENESAS || COMPILE_TEST help This selects the MMC Host Interface controller (MMCIF) found in various @@ -756,11 +760,12 @@ config MMC_SH_MMCIF config MMC_JZ4740 - tristate "JZ4740 SD/Multimedia Card Interface support" - depends on MACH_JZ4740 + tristate "Ingenic JZ47xx SD/Multimedia Card Interface support" + depends on MACH_JZ4740 || MACH_JZ4780 help - This selects support for the SD/MMC controller on Ingenic JZ4740 - SoCs. + This selects support for the SD/MMC controller on Ingenic + JZ4740, JZ4750, JZ4770 and JZ4780 SoCs. + If you have a board based on such a SoC and with a SD/MMC slot, say Y or M here. @@ -868,7 +873,6 @@ config MMC_TOSHIBA_PCI config MMC_BCM2835 tristate "Broadcom BCM2835 SDHOST MMC Controller support" depends on ARCH_BCM2835 || COMPILE_TEST - depends on HAS_DMA help This selects the BCM2835 SDHOST MMC controller. If you have a BCM2835 platform with SD or MMC devices, say Y or M here. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 6aead24879b4..85dc1322c3de 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -49,6 +49,7 @@ thunderx-mmc-objs := cavium.o cavium-thunderx.o obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.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_HI3798CV200) += dw_mmc-hi3798cv200.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c index 63d27589cd89..294de177632c 100644 --- a/drivers/mmc/host/android-goldfish.c +++ b/drivers/mmc/host/android-goldfish.c @@ -217,8 +217,8 @@ static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host, * We don't really have DMA, so we need * to copy from our platform driver buffer */ - uint8_t *dest = (uint8_t *)sg_virt(data->sg); - memcpy(dest, host->virt_base, data->sg->length); + sg_copy_to_buffer(data->sg, 1, host->virt_base, + data->sg->length); } host->data->bytes_xfered += data->sg->length; 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 * platform driver buffer */ - const uint8_t *src = (uint8_t *)sg_virt(data->sg); - memcpy(host->virt_base, src, data->sg->length); + sg_copy_from_buffer(data->sg, 1, host->virt_base, + data->sg->length); } } diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index e55f3932d580..5aa2c9404e92 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1967,7 +1967,6 @@ static void atmci_tasklet_func(unsigned long priv) static void atmci_read_data_pio(struct atmel_mci *host) { struct scatterlist *sg = host->sg; - void *buf = sg_virt(sg); unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; u32 value; @@ -1977,7 +1976,7 @@ static void atmci_read_data_pio(struct atmel_mci *host) do { value = atmci_readl(host, ATMCI_RDR); if (likely(offset + 4 <= sg->length)) { - put_unaligned(value, (u32 *)(buf + offset)); + sg_pcopy_to_buffer(sg, 1, &value, sizeof(u32), offset); offset += 4; nbytes += 4; @@ -1990,11 +1989,11 @@ static void atmci_read_data_pio(struct atmel_mci *host) goto done; offset = 0; - buf = sg_virt(sg); } } else { unsigned int remaining = sg->length - offset; - memcpy(buf + offset, &value, remaining); + + sg_pcopy_to_buffer(sg, 1, &value, remaining, offset); nbytes += remaining; flush_dcache_page(sg_page(sg)); @@ -2004,8 +2003,8 @@ static void atmci_read_data_pio(struct atmel_mci *host) goto done; offset = 4 - remaining; - buf = sg_virt(sg); - memcpy(buf, (u8 *)&value + remaining, offset); + sg_pcopy_to_buffer(sg, 1, (u8 *)&value + remaining, + offset, 0); nbytes += offset; } @@ -2035,7 +2034,6 @@ done: static void atmci_write_data_pio(struct atmel_mci *host) { struct scatterlist *sg = host->sg; - void *buf = sg_virt(sg); unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; u32 value; @@ -2044,7 +2042,7 @@ static void atmci_write_data_pio(struct atmel_mci *host) do { 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); offset += 4; @@ -2056,13 +2054,12 @@ static void atmci_write_data_pio(struct atmel_mci *host) goto done; offset = 0; - buf = sg_virt(sg); } } else { unsigned int remaining = sg->length - offset; value = 0; - memcpy(&value, buf + offset, remaining); + sg_pcopy_from_buffer(sg, 1, &value, remaining, offset); nbytes += remaining; host->sg = sg = sg_next(sg); @@ -2073,8 +2070,8 @@ static void atmci_write_data_pio(struct atmel_mci *host) } offset = 4 - remaining; - buf = sg_virt(sg); - memcpy((u8 *)&value + remaining, buf, offset); + sg_pcopy_from_buffer(sg, 1, (u8 *)&value + remaining, + offset, 0); atmci_writel(host, ATMCI_TDR, value); nbytes += offset; } diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index ed77fbfa4774..9b4be67330dd 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -40,6 +40,7 @@ #include <linux/interrupt.h> #include <linux/dma-mapping.h> #include <linux/scatterlist.h> +#include <linux/highmem.h> #include <linux/leds.h> #include <linux/mmc/host.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 */ 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 */ 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)) break; - val = *sg_ptr++; + val = sg_ptr[count]; __raw_writel((unsigned long)val, HOST_TXPORT(host)); wmb(); /* drain writebuffer */ } + kunmap_atomic(sg_ptr); host->pio.len -= 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) { 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 */ 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)); 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.offset += count; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 8e363174f9d6..9e68c3645e22 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1377,8 +1377,7 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int davinci_mmcsd_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mmc_davinci_host *host = platform_get_drvdata(pdev); + struct mmc_davinci_host *host = dev_get_drvdata(dev); writel(0, host->base + DAVINCI_MMCIM); 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) { - struct platform_device *pdev = to_platform_device(dev); - struct mmc_davinci_host *host = platform_get_drvdata(pdev); + struct mmc_davinci_host *host = dev_get_drvdata(dev); clk_enable(host->clk); mmc_davinci_reset_ctrl(host, 0); diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c new file mode 100644 index 000000000000..54c3fbb4a391 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -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"); diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 40d7de2eea12..8c86a800a8fd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -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 * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) * - * Note: div can only be 0 or 1 - * if DDR50 8bit mode(only emmc work in 8bit mode), - * div must be set 1 + * Note: div can only be 0 or 1, but div must be set to 1 for eMMC + * DDR52 8-bit mode. */ if (ios->bus_width == MMC_BUS_WIDTH_8 && ios->timing == MMC_TIMING_MMC_DDR52) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 29a1afa81f66..623f4d27fa01 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -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) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + slot->mmc->actual_clock = 0; + if (!clock) { mci_writel(host, CLKENA, 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 */ slot->__clk_old = clock; + slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : + host->bus_hz; } host->current_speed = clock; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index a0168e9e4fce..993386c9ea50 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2013, Imagination Technologies + * * JZ4740 SD/MMC controller driver * * This program is free software; you can redistribute it and/or modify it @@ -13,24 +15,25 @@ * */ -#include <linux/mmc/host.h> -#include <linux/mmc/slot-gpio.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> #include <linux/io.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/of_device.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/delay.h> #include <linux/scatterlist.h> -#include <linux/clk.h> -#include <linux/bitops.h> -#include <linux/gpio.h> #include <asm/cacheflush.h> -#include <linux/dma-mapping.h> -#include <linux/dmaengine.h> #include <asm/mach-jz4740/dma.h> #include <asm/mach-jz4740/jz4740_mmc.h> @@ -51,6 +54,7 @@ #define JZ_REG_MMC_RESP_FIFO 0x34 #define JZ_REG_MMC_RXFIFO 0x38 #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_TRANSFER BIT(6) @@ -104,9 +108,17 @@ #define JZ_MMC_IRQ_PRG_DONE BIT(1) #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 +enum jz4740_mmc_version { + JZ_MMC_JZ4740, + JZ_MMC_JZ4750, + JZ_MMC_JZ4780, +}; + enum jz4740_mmc_state { JZ4740_MMC_STATE_READ_RESPONSE, JZ4740_MMC_STATE_TRANSFER_DATA, @@ -125,6 +137,8 @@ struct jz4740_mmc_host { struct jz4740_mmc_platform_data *pdata; struct clk *clk; + enum jz4740_mmc_version version; + int irq; int card_detect_irq; @@ -137,7 +151,7 @@ struct jz4740_mmc_host { uint32_t cmdat; - uint16_t irq_mask; + uint32_t irq_mask; spinlock_t lock; @@ -159,6 +173,32 @@ struct jz4740_mmc_host { #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 */ @@ -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) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma_tx = dma_request_channel(mask, NULL, host); - if (!host->dma_tx) { + host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); + if (IS_ERR(host->dma_tx)) { 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); - if (!host->dma_rx) { + host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); + if (IS_ERR(host->dma_rx)) { 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 */ host->next_data.cookie = 1; 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, @@ -363,7 +395,7 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, else 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); } @@ -415,10 +447,10 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, unsigned int irq) { unsigned int timeout = 0x800; - uint16_t status; + uint32_t status; do { - status = readw(host->base + JZ_REG_MMC_IREG); + status = jz4740_mmc_read_irq_reg(host); } while (!(status & irq) && --timeout); 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; uint32_t *buf; uint32_t d; - uint16_t status; + uint32_t status; size_t i, j; unsigned int timeout; @@ -654,8 +686,25 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, cmdat |= JZ_MMC_CMDAT_DATA_EN; if (cmd->data->flags & MMC_DATA_WRITE) cmdat |= JZ_MMC_CMDAT_WRITE; - if (host->use_dma) - cmdat |= JZ_MMC_CMDAT_DMA_EN; + 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; + } + } 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->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; 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: if (!req->stop) @@ -766,9 +815,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) { struct jz4740_mmc_host *host = devid; 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; 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); 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) { - 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); 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)) { del_timer(&host->timeout_timer); - status = readl(host->base + JZ_REG_MMC_STATUS); - if (status & JZ_MMC_STATUS_TIMEOUT_RES) { cmd->error = -ETIMEDOUT; } 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); - writew(irq_reg, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, irq_reg); 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; 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); @@ -837,9 +885,7 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) host->req = req; - writew(0xffff, host->base + JZ_REG_MMC_IREG); - - writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, ~0); jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 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) { case MMC_POWER_UP: 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, !host->pdata->power_active_low); 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: break; 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, host->pdata->power_active_low); 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, 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; 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) { - struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); if (!pdata) return; @@ -964,14 +1010,22 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev) 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) { int ret; struct mmc_host *mmc; struct jz4740_mmc_host *host; + const struct of_device_id *match; 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); if (!mmc) { @@ -982,6 +1036,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); 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); if (host->irq < 0) { ret = host->irq; @@ -1004,16 +1079,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_host; } - ret = jz4740_mmc_request_gpios(mmc, pdev); - if (ret) - goto err_release_dma; - mmc->ops = &jz4740_mmc_ops; - mmc->f_min = JZ_MMC_CLK_RATE / 128; - mmc->f_max = JZ_MMC_CLK_RATE; + if (!mmc->f_max) + 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->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_count = (1 << 15) - 1; @@ -1025,7 +1095,9 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host->mmc = mmc; host->pdev = pdev; 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, dev_name(&pdev->dev), host); @@ -1034,20 +1106,20 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_gpios; } - jz4740_mmc_reset(host); jz4740_mmc_clock_disable(host); timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); - host->use_dma = true; - if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0) - host->use_dma = false; + ret = jz4740_mmc_acquire_dma_channels(host); + if (ret == -EPROBE_DEFER) + goto err_free_irq; + host->use_dma = !ret; platform_set_drvdata(pdev, host); ret = mmc_add_host(mmc); if (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"); @@ -1057,13 +1129,13 @@ static int jz4740_mmc_probe(struct platform_device* pdev) return 0; +err_release_dma: + if (host->use_dma) + jz4740_mmc_release_dma_channels(host); err_free_irq: free_irq(host->irq, host); err_free_gpios: jz4740_mmc_free_gpios(pdev); -err_release_dma: - if (host->use_dma) - jz4740_mmc_release_dma_channels(host); err_free_host: mmc_free_host(mmc); @@ -1116,6 +1188,7 @@ static struct platform_driver jz4740_mmc_driver = { .remove = jz4740_mmc_remove, .driver = { .name = "jz4740-mmc", + .of_match_table = of_match_ptr(jz4740_mmc_of_match), .pm = JZ4740_MMC_PM_OPS, }, }; diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 4f972b879fe6..c201c378537e 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -35,6 +35,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/interrupt.h> #include <linux/bitfield.h> #include <linux/pinctrl/consumer.h> @@ -47,15 +48,29 @@ #define CLK_CORE_PHASE_MASK GENMASK(9, 8) #define CLK_TX_PHASE_MASK GENMASK(11, 10) #define CLK_RX_PHASE_MASK GENMASK(13, 12) -#define CLK_TX_DELAY_MASK GENMASK(19, 16) -#define CLK_RX_DELAY_MASK GENMASK(23, 20) +#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16) +#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_PHASE_STEP 30 #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_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_START 0x40 #define START_DESC_INIT BIT(0) @@ -122,6 +137,12 @@ #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 { u32 cmd_cfg; u32 cmd_arg; @@ -131,6 +152,7 @@ struct sd_emmc_desc { struct meson_host { struct device *dev; + struct meson_mmc_data *data; struct mmc_host *mmc; 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 */ clk_reg = 0; - clk_reg |= CLK_ALWAYS_ON; + clk_reg |= CLK_ALWAYS_ON(host); clk_reg |= CLK_DIV_MASK; 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->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->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->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->hw.init = &init; @@ -1184,6 +1206,21 @@ static int meson_mmc_probe(struct platform_device *pdev) 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); host->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs)) { @@ -1315,11 +1352,24 @@ static int meson_mmc_remove(struct platform_device *pdev) 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[] = { - { .compatible = "amlogic,meson-gx-mmc", }, - { .compatible = "amlogic,meson-gxbb-mmc", }, - { .compatible = "amlogic,meson-gxl-mmc", }, - { .compatible = "amlogic,meson-gxm-mmc", }, + { .compatible = "amlogic,meson-gx-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxbb-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxl-mmc", .data = &meson_gx_data }, + { .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); @@ -1335,6 +1385,6 @@ static struct 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_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 70b0df8b9c78..f1849775e47e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -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 variant_data *variant = host->variant; void __iomem *base = host->base; - unsigned long flags; u32 status; status = readl(base + MMCISTATUS); dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); - local_irq_save(flags); - do { unsigned int remain, len; char *buffer; @@ -1301,8 +1298,6 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) sg_miter_stop(sg_miter); - local_irq_restore(flags); - /* * If we have less than the fifo 'half-full' threshold to transfer, * trigger a PIO interrupt as soon as any data is available. diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index cb274e822293..04841386b65d 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -19,6 +19,7 @@ #include <linux/ioport.h> #include <linux/irq.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> @@ -70,6 +71,7 @@ #define SDC_ADV_CFG0 0x64 #define EMMC_IOCON 0x7c #define SDC_ACMD_RESP 0x80 +#define DMA_SA_H4BIT 0x8c #define MSDC_DMA_SA 0x90 #define MSDC_DMA_CTRL 0x98 #define MSDC_DMA_CFG 0x9c @@ -194,6 +196,9 @@ /* SDC_ADV_CFG0 mask */ #define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */ +/* DMA_SA_H4BIT mask */ +#define DMA_ADDR_HIGH_4BIT (0xf << 0) /* RW */ + /* MSDC_DMA_CTRL mask */ #define MSDC_DMA_CTRL_START (0x1 << 0) /* 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_CFGCRCSTS (0x1 << 28) /* RW */ +#define MSDC_PB2_SUPPORT_64G (0x1 << 1) /* RW */ #define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */ #define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* 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_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ #define GPDMA_DESC_INT (0x1 << 16) +#define GPDMA_DESC_NEXT_H4 (0xf << 24) +#define GPDMA_DESC_PTR_H4 (0xf << 28) u32 next; u32 ptr; u32 gpd_data_len; @@ -296,6 +304,8 @@ struct mt_bdma_desc { #define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ #define BDMA_DESC_BLKPAD (0x1 << 17) #define BDMA_DESC_DWPAD (0x1 << 18) +#define BDMA_DESC_NEXT_H4 (0xf << 24) +#define BDMA_DESC_PTR_H4 (0xf << 28) u32 next; u32 ptr; u32 bd_data_len; @@ -334,6 +344,7 @@ struct mtk_mmc_compatible { bool busy_check; bool stop_clk_fix; bool enhance_rx; + bool support_64g; }; struct msdc_tune_para { @@ -403,6 +414,7 @@ static const struct mtk_mmc_compatible mt8135_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt8173_compat = { @@ -414,6 +426,7 @@ static const struct mtk_mmc_compatible mt8173_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt2701_compat = { @@ -425,6 +438,7 @@ static const struct mtk_mmc_compatible mt2701_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt2712_compat = { @@ -436,6 +450,7 @@ static const struct mtk_mmc_compatible mt2712_compat = { .busy_check = true, .stop_clk_fix = true, .enhance_rx = true, + .support_64g = true, }; static const struct mtk_mmc_compatible mt7622_compat = { @@ -447,6 +462,7 @@ static const struct mtk_mmc_compatible mt7622_compat = { .busy_check = true, .stop_clk_fix = true, .enhance_rx = true, + .support_64g = false, }; 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 */ bd[j].bd_info &= ~BDMA_DESC_BLKPAD; 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 |= (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_BURST_64B << 12 | 1 << 8); 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) @@ -1366,6 +1390,9 @@ static void msdc_init_hw(struct msdc_host *host) 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) { sdr_set_bits(host->base + tune_reg, 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_bdma_desc *bd = dma->bd; + dma_addr_t dma_addr; int i; 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->ptr = (u32)dma->bd_addr; /* physical address */ /* gpd->next is must set for desc DMA * 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); - for (i = 0; i < (MAX_BD_NUM - 1); i++) - bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1); + for (i = 0; i < (MAX_BD_NUM - 1); i++) { + 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) @@ -1820,7 +1860,6 @@ static int msdc_drv_probe(struct platform_device *pdev) struct mmc_host *mmc; struct msdc_host *host; struct resource *res; - const struct of_device_id *of_id; int ret; if (!pdev->dev.of_node) { @@ -1828,9 +1867,6 @@ static int msdc_drv_probe(struct platform_device *pdev) 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 */ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev); if (!mmc) @@ -1899,7 +1935,7 @@ static int msdc_drv_probe(struct platform_device *pdev) msdc_of_property_parse(pdev, host); host->dev = &pdev->dev; - host->dev_comp = of_id->data; + host->dev_comp = of_device_get_match_data(&pdev->dev); host->mmc = mmc; host->src_clk_freq = clk_get_rate(host->src_clk); /* Set host parameters to mmc */ @@ -1916,7 +1952,10 @@ static int msdc_drv_probe(struct platform_device *pdev) mmc->max_blk_size = 2048; mmc->max_req_size = 512 * 1024; mmc->max_blk_count = mmc->max_req_size / 512; - host->dma_mask = DMA_BIT_MASK(32); + if (host->dev_comp->support_64g) + host->dma_mask = DMA_BIT_MASK(36); + else + host->dma_mask = DMA_BIT_MASK(32); mmc_dev(mmc)->dma_mask = &host->dma_mask; host->timeout_clks = 3 * 1048576; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 210247b3d11a..e22bbff89c8d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -143,6 +143,7 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; u32 cmdreg = 0, xfer = 0, intr = 0; unsigned long flags; + unsigned int timeout; BUG_ON(host->mrq != NULL); 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_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); } @@ -755,6 +757,8 @@ static int mvsd_probe(struct platform_device *pdev) if (maxfreq) mmc->f_max = maxfreq; + mmc->caps |= MMC_CAP_ERASE; + spin_lock_init(&host->lock); host->base = devm_ioremap_resource(&pdev->dev, r); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 5ff8ef7223cc..75f781c11e89 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/ioport.h> #include <linux/platform_device.h> +#include <linux/highmem.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/blkdev.h> @@ -291,8 +292,10 @@ static void mxcmci_swap_buffers(struct mmc_data *data) struct scatterlist *sg; int i; - for_each_sg(data->sg, sg, data->sg_len, i) - buffer_swap32(sg_virt(sg), sg->length); + for_each_sg(data->sg, sg, data->sg_len, i) { + void *buf = kmap_atomic(sg_page(sg) + sg->offset; + buffer_swap32(buf, sg->length); + kunmap_atomic(buf); } #else 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 scatterlist *sg; + void *buf; int stat, i; host->data = data; @@ -616,14 +620,18 @@ static int mxcmci_transfer_data(struct mxcmci_host *host) if (data->flags & MMC_DATA_READ) { 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) return stat; host->datasize += sg->length; } } else { 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) return stat; host->datasize += sg->length; @@ -1206,7 +1214,8 @@ static int mxcmci_remove(struct platform_device *pdev) 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 mxcmci_host *host = mmc_priv(mmc); @@ -1216,7 +1225,7 @@ static int __maybe_unused mxcmci_suspend(struct device *dev) 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 mxcmci_host *host = mmc_priv(mmc); @@ -1232,6 +1241,7 @@ static int __maybe_unused mxcmci_resume(struct device *dev) return ret; } +#endif static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume); diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 51e01f03fb99..45c015da2e75 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -28,6 +28,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/mmc/host.h> +#include <linux/mmc/slot-gpio.h> #include <linux/mfd/tmio.h> #include <linux/sh_dma.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->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+ */ if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) { /* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */ diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 6af946d16d24..f7f9773d161f 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -87,11 +87,12 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .bus_shift = 2, .scc_offset = 0x1000, .taps = 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_segs = 1, }; @@ -157,38 +158,34 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, { struct scatterlist *sg = host->sg_ptr; 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 */ - WARN_ON(host->sg_len > 1); + if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len, + mmc_get_dma_dir(data))) + goto force_pio; /* 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; + } if (data->flags & MMC_DATA_READ) { dtran_mode |= DTRAN_MODE_CH_NUM_CH1; - dir = DMA_FROM_DEVICE; if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) && test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags)) goto force_pio; } else { 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); /* set dma parameters */ renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE, dtran_mode); renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR, - sg->dma_address); + sg_dma_address(sg)); 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. */ static const struct soc_device_attribute gen3_soc_whitelist[] = { + /* specific ones */ { .soc_id = "r8a7795", .revision = "ES1.*", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, - { .soc_id = "r8a7795", .revision = "ES2.0" }, { .soc_id = "r8a7796", .revision = "ES1.0", .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 */ } }; diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 848e50c1638a..4bb46c489d71 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -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 = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, }; /* 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, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, .dma_rx_offset = 0x2000, .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, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .bus_shift = 2, .scc_offset = 0x1000, .taps = rcar_gen3_scc_taps, diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 78422079ecfa..9a3ff22dd0fe 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -26,7 +26,6 @@ #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> -#include <linux/mmc/sdio.h> #include <linux/mmc/card.h> #include <linux/scatterlist.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) - timeout = 3000; + timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000; if (cmd->opcode == SD_SWITCH_VOLTAGE) { 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; } - /* - * 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(&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_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | - MMC_CAP_NEEDS_POLL; - mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE; + MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE; + mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | + MMC_CAP2_NO_SDIO; mmc->max_current_330 = 400; mmc->max_current_180 = 800; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 11ca95c60bcf..bdbd4897c0f7 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -284,10 +284,8 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) sdhci_bcm_kona_sd_init(host); ret = sdhci_add_host(host); - if (ret) { - dev_err(dev, "Failed sdhci_add_host\n"); + if (ret) goto err_reset; - } /* if device is eMMC, emulate card insert right here */ if (!mmc_card_is_removable(host->mmc)) { diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 0f589e26ee63..7a343b87b5e5 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -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); void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06; u32 tmp; + int i, ret; if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) return -EINVAL; @@ -260,11 +261,24 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) tmp = readl(reg); tmp &= ~SDHCI_CDNS_HRS06_TUNE; tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val); - tmp |= SDHCI_CDNS_HRS06_TUNE_UP; - writel(tmp, reg); - return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), - 0, 1); + /* + * 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; + writel(tmp, reg); + + ret = readl_poll_timeout(reg, tmp, + !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), + 0, 1); + if (ret) + return ret; + } + + return 0; } static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index cd2b5f643a15..d6aef70d34fa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -41,6 +41,12 @@ #define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) #define ESDHC_WTMK_LVL 0x44 #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_DDREN (1 << 3) #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)) { + u32 wml; u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); /* Swap AC23 bit */ 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); 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 { /* * Postpone this write, we must do it together with a diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291db705..646bf377ba77 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/iopoll.h> +#include <linux/regulator/consumer.h> #include "sdhci-pltfm.h" @@ -77,10 +78,16 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (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_HS400 (6 << 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_SW_TRIG_FULL_CALIB BIT(16) #define CORE_HW_AUTOCAL_ENA BIT(17) @@ -148,6 +155,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; 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); u32 irq_status, irq_ack = 0; 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 &= 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); + /* + * 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) msm_host->curr_pwr_state = pwr_state; 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); } +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[] = { { .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 = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_NO_CARD_NO_RESET | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_CAP_CLOCK_BASE_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); if (ret) goto pm_runtime_disable; + sdhci_msm_set_regulator_caps(msm_host); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index c33a5f7393bd..e3332a522a5d 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -290,7 +290,8 @@ static const struct sdhci_pltfm_data sdhci_arasan_pdata = { .ops = &sdhci_arasan_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_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) @@ -359,8 +360,7 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { */ static int sdhci_arasan_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; @@ -403,8 +403,7 @@ static int sdhci_arasan_suspend(struct device *dev) */ static int sdhci_arasan_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 1456abd5eeb9..f3a7c8ece4be 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/pinctrl/consumer.h> +#include <linux/sys_soc.h> #include "sdhci-pltfm.h" @@ -35,6 +36,7 @@ #define CON_DDR BIT(19) #define CON_CLKEXTFREE BIT(16) #define CON_PADEN BIT(15) +#define CON_CTPL BIT(11) #define CON_INIT BIT(1) #define CON_OD BIT(0) @@ -100,6 +102,7 @@ struct sdhci_omap_data { }; struct sdhci_omap_host { + char *version; void __iomem *base; struct device *dev; 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, int count) { @@ -713,10 +733,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = { SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, .quirks2 = SDHCI_QUIRK2_ACMD23_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, }; +static const struct sdhci_omap_data k2g_data = { + .offset = 0x200, +}; + static const struct sdhci_omap_data dra7_data = { .offset = 0x200, .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[] = { { .compatible = "ti,dra7-sdhci", .data = &dra7_data }, + { .compatible = "ti,k2g-sdhci", .data = &k2g_data }, {}, }; MODULE_DEVICE_TABLE(of, omap_sdhci_match); @@ -733,12 +759,21 @@ static struct pinctrl_state u32 *caps, u32 capmask) { struct device *dev = omap_host->dev; + char *version = omap_host->version; struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV); + char str[20]; if (!(*caps & capmask)) goto ret; - pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode); + 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); + if (IS_ERR(pinctrl_state)) { dev_err(dev, "no pinctrl state for %s mode", mode); *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, MMC_CAP_1_8V_DDR); - if (!IS_ERR(state)) + 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)) + pinctrl_state[MMC_TIMING_MMC_DDR52] = state; + } state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps, MMC_CAP_SD_HIGHSPEED); @@ -830,6 +872,16 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host 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) { int ret; @@ -841,6 +893,7 @@ static int sdhci_omap_probe(struct platform_device *pdev) struct mmc_host *mmc; const struct of_device_id *match; struct sdhci_omap_data *data; + const struct soc_device_attribute *soc; match = of_match_device(omap_sdhci_match, dev); if (!match) @@ -871,10 +924,22 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->ioaddr += offset; mmc = host->mmc; + sdhci_get_of_property(pdev); ret = mmc_of_parse(mmc); if (ret) 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"); if (IS_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; } - 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.start_signal_voltage_switch = sdhci_omap_start_signal_voltage_switch; host->mmc_host_ops.set_ios = sdhci_omap_set_ios; host->mmc_host_ops.card_busy = sdhci_omap_card_busy; 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); - host->caps |= SDHCI_CAN_DO_ADMA2; - - ret = sdhci_add_host(host); + ret = sdhci_setup_host(host); if (ret) 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; +err_cleanup_host: + sdhci_cleanup_host(host); + err_put_sync: pm_runtime_put_sync(dev); diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 78c25ad35fd2..77dd3521daae 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -453,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { enum { INTEL_DSM_FNS = 0, INTEL_DSM_V18_SWITCH = 3, + INTEL_DSM_V33_SWITCH = 4, INTEL_DSM_DRV_STRENGTH = 9, 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); } -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 intel_host *intel_host = sdhci_pci_priv(slot); - struct device *dev = &slot->chip->pdev->dev; + unsigned int fn; u32 result = 0; int err; - err = intel_dsm(intel_host, dev, INTEL_DSM_V18_SWITCH, &result); - pr_debug("%s: %s DSM error %d result %u\n", - mmc_hostname(host->mmc), __func__, err, result); + err = sdhci_start_signal_voltage_switch(mmc, ios); + if (err) + 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 = { @@ -641,7 +662,6 @@ static const struct sdhci_ops sdhci_intel_byt_ops = { .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, - .voltage_switch = sdhci_intel_voltage_switch, }; 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, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, - .voltage_switch = sdhci_intel_voltage_switch, .irq = sdhci_cqhci_irq, }; @@ -691,6 +710,7 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot) byt_read_dsm(slot); 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) @@ -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->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; } diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 5cbcdc448f98..db9cb54ef700 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -54,6 +54,7 @@ #define PCI_DEVICE_ID_REALTEK_5250 0x5250 #define PCI_SUBDEVICE_ID_NI_7884 0x7884 +#define PCI_SUBDEVICE_ID_NI_78E3 0x78e3 #define PCI_VENDOR_ID_ARASAN 0x16e6 #define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670 diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c index a6caa49ca25a..a11e6397d4ff 100644 --- a/drivers/mmc/host/sdhci-pic32.c +++ b/drivers/mmc/host/sdhci-pic32.c @@ -200,10 +200,8 @@ static int pic32_sdhci_probe(struct platform_device *pdev) } ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "error adding host\n"); + if (ret) goto err_base_clk; - } dev_info(&pdev->dev, "Successfully added sdhci host\n"); return 0; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 8986f9d9cf98..2c3827f54927 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -221,10 +221,8 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) host->ops = &pxav2_sdhci_ops; ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "failed to add host\n"); + if (ret) goto disable_clk; - } return 0; diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index a34434166ca7..b8e96f392428 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -472,10 +472,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "failed to add host\n"); + if (ret) goto err_add_host; - } if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) device_init_wakeup(&pdev->dev, 1); diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index cda83ccb2702..9ef89d00970e 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -655,10 +655,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev) goto err_req_regs; ret = sdhci_add_host(host); - if (ret) { - dev_err(dev, "sdhci_add_host() failed\n"); + if (ret) goto err_req_regs; - } #ifdef CONFIG_PM if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 14511526a3a8..9247d51f2eed 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -126,10 +126,8 @@ static int sdhci_probe(struct platform_device *pdev) } ret = sdhci_add_host(host); - if (ret) { - dev_dbg(&pdev->dev, "error adding host\n"); + if (ret) goto disable_clk; - } platform_set_drvdata(pdev, host); diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index c32daed0d418..8f95647195d9 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -422,10 +422,8 @@ static int sdhci_st_probe(struct platform_device *pdev) st_mmcss_cconfig(np, host); ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "Failed sdhci_add_host\n"); + if (ret) goto err_out; - } host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b877c13184c2..970d38f68939 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -231,7 +231,7 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, if (timing == MMC_TIMING_UHS_DDR50) 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) diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c index ec8794335241..a35804b203a7 100644 --- a/drivers/mmc/host/sdhci-xenon-phy.c +++ b/drivers/mmc/host/sdhci-xenon-phy.c @@ -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 xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); - int i, ret; + int ret; - for (i = 0; i < NR_PHY_TYPES; i++) { - if (!strcmp(phy_name, phy_types[i])) { - priv->phy_type = i; - break; - } - } - if (i == NR_PHY_TYPES) { + priv->phy_type = match_string(phy_types, NR_PHY_TYPES, phy_name); + if (priv->phy_type < 0) { dev_err(mmc_dev(host->mmc), "Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n", phy_name); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2ededa7f43df..1c828e0e9905 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -709,29 +709,16 @@ static u32 sdhci_sdma_address(struct sdhci_host *host) 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; - 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; + unsigned int target_timeout; /* timeout in us */ - if (!data) + if (!data) { target_timeout = cmd->busy_timeout * 1000; - else { + } else { target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); if (host->clock && data->timeout_clks) { 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. * 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) { - DBG("Too large timeout 0x%x requested for CMD%d!\n", - count, cmd->opcode); + if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) + DBG("Too large timeout 0x%x requested for CMD%d!\n", + count, cmd->opcode); count = 0xE; + } else { + *too_big = false; } return count; @@ -790,6 +841,16 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) 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) { u8 count; @@ -797,7 +858,18 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) if (host->ops->set_timeout) { host->ops->set_timeout(host, cmd); } 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); } } @@ -807,6 +879,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) u8 ctrl; struct mmc_data *data = cmd->data; + host->data_timeout = 0; + if (sdhci_data_line_cmd(cmd)) sdhci_set_timeout(host, cmd); @@ -1160,13 +1234,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) 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; if (sdhci_data_line_cmd(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) 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); } 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; } + 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->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) { host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | 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. */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c95b0a4a7594..23966f887da6 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -332,6 +332,14 @@ struct sdhci_adma2_64_desc { /* Allow for a a command request and a data request at the same time */ #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 { COOKIE_UNMAPPED, COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */ @@ -437,6 +445,11 @@ struct sdhci_host { #define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15) /* Controller has CRC in 136 bit Command Response */ #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 */ void __iomem *ioaddr; /* Mapped address */ @@ -550,6 +563,8 @@ struct sdhci_host { /* Host SDMA buffer boundary. */ u32 sdma_boundary; + u64 data_timeout; + unsigned long private[0] ____cacheline_aligned; }; diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 20cfb20418f3..e7472590f2ed 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -13,36 +13,34 @@ * 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/sunxi-ng.h> -#include <linux/gpio.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/scatterlist.h> +#include <linux/delay.h> +#include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/slab.h> -#include <linux/reset.h> -#include <linux/regulator/consumer.h> - -#include <linux/of_address.h> -#include <linux/of_gpio.h> -#include <linux/of_platform.h> - +#include <linux/err.h> +#include <linux/gpio.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/mmc.h> -#include <linux/mmc/core.h> -#include <linux/mmc/card.h> #include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/spinlock.h> /* register offset definitions */ #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; } -static int sunxi_mmc_init_host(struct mmc_host *mmc) +static int sunxi_mmc_init_host(struct sunxi_mmc_host *host) { u32 rval; - struct sunxi_mmc_host *host = mmc_priv(mmc); if (sunxi_mmc_reset_host(host)) return -EIO; @@ -859,17 +856,48 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, 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; - /* Set the power state */ - switch (ios->power_mode) { - case MMC_POWER_ON: - break; + /* set ddr mode */ + rval = mmc_readl(host, REG_GCTRL); + if (ios->timing == MMC_TIMING_UHS_DDR50 || + 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: + dev_dbg(mmc_dev(mmc), "Powering card up\n"); + if (!IS_ERR(mmc->supply.vmmc)) { host->ferror = mmc_regulator_set_ocr(mmc, 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->ferror = sunxi_mmc_init_host(mmc); - if (host->ferror) - return; - - dev_dbg(mmc_dev(mmc), "power on!\n"); break; case MMC_POWER_OFF: - dev_dbg(mmc_dev(mmc), "power off!\n"); - sunxi_mmc_reset_host(host); + dev_dbg(mmc_dev(mmc), "Powering card off\n"); + if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; break; - } - /* set bus width */ - switch (ios->bus_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); + default: + dev_dbg(mmc_dev(mmc), "Ignoring unknown card power state\n"); break; } +} - /* set ddr mode */ - rval = mmc_readl(host, REG_GCTRL); - if (ios->timing == MMC_TIMING_UHS_DDR50 || - ios->timing == MMC_TIMING_MMC_DDR52) - rval |= SDXC_DDR_MODE; - else - rval &= ~SDXC_DDR_MODE; - mmc_writel(host, REG_GCTRL, rval); +static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sunxi_mmc_host *host = mmc_priv(mmc); - /* set up clock */ - if (ios->power_mode) { - host->ferror = sunxi_mmc_clk_set_rate(host, ios); - /* Android code had a usleep_range(50000, 55000); here */ - } + sunxi_mmc_card_power(host, ios); + sunxi_mmc_set_bus_width(host, ios->bus_width); + sunxi_mmc_set_clk(host, 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; u32 imask; + if (enable) + pm_runtime_get_noresume(host->dev); + spin_lock_irqsave(&host->lock, flags); 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); 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) @@ -1380,6 +1394,15 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) 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); if (ret) 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); mmc_remove_host(mmc); + pm_runtime_force_suspend(&pdev->dev); disable_irq(host->irq); sunxi_mmc_disable(host); 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; } +#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 = { .driver = { .name = "sunxi-mmc", .of_match_table = of_match_ptr(sunxi_mmc_of_match), + .pm = &sunxi_mmc_pm_ops, }, .probe = sunxi_mmc_probe, .remove = sunxi_mmc_remove, diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index 81dac17064d7..b2b379b10dfa 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -300,8 +300,10 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req) pipe = usb_sndbulkpipe(ushc->usb_dev, 2); 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); + ushc->data_urb->num_sgs = 1; + ushc->data_urb->sg = data->sg; ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC); if (ret < 0) goto out; diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index f4233576153b..1e54bbf13d75 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -268,43 +268,29 @@ static inline int wbsd_next_sg(struct wbsd_host *host) 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) { - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = sg_virt(&sg[i]); - memcpy(dmabuf, sgbuf, sg[i].length); - dmabuf += sg[i].length; - } + size_t len = 0; + int i; + + for (i = 0; i < data->sg_len; i++) + len += data->sg[i].length; + sg_copy_to_buffer(data->sg, data->sg_len, host->dma_buffer, len); } static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) { - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = sg_virt(&sg[i]); - memcpy(sgbuf, dmabuf, sg[i].length); - dmabuf += sg[i].length; - } + size_t len = 0; + int i; + + for (i = 0; i < data->sg_len; i++) + len += data->sg[i].length; + sg_copy_from_buffer(data->sg, data->sg_len, host->dma_buffer, len); } /* @@ -418,7 +404,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) { struct mmc_data *data = host->mrq->cmd->data; char *buffer; - int i, fsr, fifo; + int i, idx, fsr, fifo; /* * Handle excessive data. @@ -426,7 +412,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host) if (host->num_sg == 0) 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 @@ -445,8 +432,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) fifo = 1; for (i = 0; i < fifo; i++) { - *buffer = inb(host->base + WBSD_DFR); - buffer++; + buffer[idx++] = inb(host->base + WBSD_DFR); host->offset++; host->remain--; @@ -456,16 +442,19 @@ static void wbsd_empty_fifo(struct wbsd_host *host) * End of scatter list entry? */ if (host->remain == 0) { + kunmap_atomic(buffer); /* * Get next entry. Check if last. */ if (!wbsd_next_sg(host)) 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 @@ -480,7 +469,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) { struct mmc_data *data = host->mrq->cmd->data; char *buffer; - int i, fsr, fifo; + int i, idx, fsr, fifo; /* * 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) 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 @@ -508,8 +498,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) fifo = 15; for (i = 16; i > fifo; i--) { - outb(*buffer, host->base + WBSD_DFR); - buffer++; + outb(buffer[idx], host->base + WBSD_DFR); host->offset++; host->remain--; @@ -519,16 +508,19 @@ static void wbsd_fill_fifo(struct wbsd_host *host) * End of scatter list entry? */ if (host->remain == 0) { + kunmap_atomic(buffer); /* * Get next entry. Check if last. */ if (!wbsd_next_sg(host)) return; - buffer = wbsd_sg_to_buffer(host); + buffer = wbsd_map_sg(host); + idx = 0; } } } + kunmap_atomic(buffer); /* * The controller stops sending interrupts for diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index fd30ac7da5e5..3ba42f508014 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -928,8 +928,7 @@ static int wmt_mci_remove(struct platform_device *pdev) static int wmt_mci_suspend(struct device *dev) { u32 reg_tmp; - struct platform_device *pdev = to_platform_device(dev); - struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mmc_host *mmc = dev_get_drvdata(dev); struct wmt_mci_priv *priv; if (!mmc) @@ -953,8 +952,7 @@ static int wmt_mci_suspend(struct device *dev) static int wmt_mci_resume(struct device *dev) { u32 reg_tmp; - struct platform_device *pdev = to_platform_device(dev); - struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mmc_host *mmc = dev_get_drvdata(dev); struct wmt_mci_priv *priv; if (mmc) { diff --git a/include/dt-bindings/dma/jz4780-dma.h b/include/dt-bindings/dma/jz4780-dma.h new file mode 100644 index 000000000000..df017fdfb44e --- /dev/null +++ b/include/dt-bindings/dma/jz4780-dma.h @@ -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__ */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 279b39008a33..de7377815b6b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -156,6 +156,7 @@ struct sd_switch_caps { #define UHS_DDR50_MAX_DTR 50000000 #define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR #define UHS_SDR12_MAX_DTR 25000000 +#define DEFAULT_SPEED_MAX_DTR UHS_SDR12_MAX_DTR unsigned int sd3_bus_mode; #define UHS_SDR12_BUS_SPEED 0 #define HIGH_SPEED_BUS_SPEED 1 @@ -252,6 +253,7 @@ struct mmc_card { #define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */ unsigned int state; /* (our) card state */ 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_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ /* for byte mode */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 927519385482..134a6483347a 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -177,6 +177,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries); 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); #endif /* LINUX_MMC_CORE_H */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 85146235231e..64300a48dcce 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -22,6 +22,7 @@ struct mmc_ios { unsigned int clock; /* clock rate */ unsigned short vdd; + unsigned int power_delay_ms; /* waiting for stable power */ /* 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_SDR104 (1 << 19) /* Host supports UHS SDR104 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 */ #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 */ @@ -345,6 +349,7 @@ struct mmc_host { #define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ MMC_CAP2_HS400_1_2V) +#define MMC_CAP2_HSX00_1_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_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 */ @@ -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_CQE (1 << 23) /* Has eMMC command queue engine */ #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 */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index cdd66a5fbd5e..2836a96e014a 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -55,6 +55,7 @@ #define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104 #define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105 #define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128 +#define SDIO_DEVICE_ID_MARVELL_8887WLAN 0x9134 #define SDIO_VENDOR_ID_SIANO 0x039a #define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201 |