MMC core:
- Decrease polling rate for erase/trim/discard - Allow non-sleeping GPIOs for card detect - Improve mmc block removal path - Enable support for mmc_sw_reset() for SDIO cards - Add mmc_sw_reset() to allow users to do a soft reset of the card - Allow power delay to be tunable via DT - Allow card detect debounce delay to be tunable via DT - Enable new quirk to limit clock rate for Marvell 8887 chip - Don't show eMMC RPMB and BOOT areas in /proc/partitions - Add capability to avoid 3.3V signaling for fragile HWs MMC host: - Improve/fixup support for handle highmem pages - Remove depends on HAS_DMA in case of platform dependency - mvsdio: Enable support for erase/trim/discard - rtsx_usb: Enable support for erase/trim/discard - renesas_sdhi: Fix WP logic regressions - renesas_sdhi: Add r8a77965 support - renesas_sdhi: Add R8A77980 to whitelist - meson: Add optional support for device reset - meson: Add support for the Meson-AXG platform - dw_mmc: Add new driver for BlueField DW variant - mediatek: Add support for 64G DRAM DMA - sunxi: Deploy runtime PM support - jz4740: Add support for JZ4780 - jz4740: Enable support for DT based platforms - sdhci: Various improvement to timeout handling - sdhci: Disable support for HS200/HS400/UHS when no 1.8V support - sdhci-omap: Add support for controller in k2g SoC - sdhci-omap: Add workarounds for a couple of Erratas - sdhci-omap: Enable support for generic sdhci DT properties - sdhci-cadence: Re-send tune request to deal with errata - sdhci-pci: Fix 3.3V voltage switch for some BYT-based Intel controllers - sdhci-pci: Avoid 3.3V signaling on some NI 904x - sdhci-esdhc-imx: Use watermark levels for PIO access - sdhci-msm: Improve card detection handling - sdhci-msm: Add support voltage pad switching -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAlsWMJUXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCkWhw/9GdsKWGAezGYcBC9fBKVbZgvl Mu7kTebyQy1N7kbIVD7sBz2ila1O2DhVI9SVS7lVhzFwrG4Hup0szb/mjEQgiOAi +S2EjKRD3+9sA612fi1uWY+2cXoQiwCHhFZsCS5deL4pYspebzuYRNO5vZD7QlN8 vLHZ85yxBtMSiysDUTRm9MH2EXb9j6PfrHnVKj0ih112U/ip4PZVc7zHGBZq9zzy 6WSUMYBe2d1jajJ6MS3gf5Vrp53mz2OLkxKRCtMgut7YOwLZ8biKVa3G69ruQTm8 jqrMoNrZnhO3aT/1kassCE4ygoXewMZPhYq8k24PcaihR3o/tVI1dlb4Zz70cBb8 AGAdmdzu1sDEzY6KkSVTZ5IaxQo9SNANste/O/LfsOxHWkWxBQ6H0EeqMdt+W7of A0zGKTQrcTFhxzA9OeX3aZwZhjcipQlf8TWnI5+ayPY/UKwMhvWNCTMQtTJAhqBh mS7Uk5qd7OSJZTzVYrfLZ2ZOQKhtMbD9lAza/9Ldy5j5QDscLmxmhLOP0yuKI2Zp 92N3JPIGzfK4aKGCLKSA3KhYwuwaicwsXqfJV+cPz9fFtNs8WdHqyU+nslD0Oxm4 v3QZKPJa3LWCezPvWGSSTGLEgOvJtLQtJBefX1Hm6sxLbY12aBgu/iNp5UckGlzW 7zaWXtH8nzYlBKsXf/8= =MwCT -----END PGP SIGNATURE----- Merge tag 'mmc-v4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Decrease polling rate for erase/trim/discard - Allow non-sleeping GPIOs for card detect - Improve mmc block removal path - Enable support for mmc_sw_reset() for SDIO cards - Add mmc_sw_reset() to allow users to do a soft reset of the card - Allow power delay to be tunable via DT - Allow card detect debounce delay to be tunable via DT - Enable new quirk to limit clock rate for Marvell 8887 chip - Don't show eMMC RPMB and BOOT areas in /proc/partitions - Add capability to avoid 3.3V signaling for fragile HWs MMC host: - Improve/fixup support for handle highmem pages - Remove depends on HAS_DMA in case of platform dependency - mvsdio: Enable support for erase/trim/discard - rtsx_usb: Enable support for erase/trim/discard - renesas_sdhi: Fix WP logic regressions - renesas_sdhi: Add r8a77965 support - renesas_sdhi: Add R8A77980 to whitelist - meson: Add optional support for device reset - meson: Add support for the Meson-AXG platform - dw_mmc: Add new driver for BlueField DW variant - mediatek: Add support for 64G DRAM DMA - sunxi: Deploy runtime PM support - jz4740: Add support for JZ4780 - jz4740: Enable support for DT based platforms - sdhci: Various improvement to timeout handling - sdhci: Disable support for HS200/HS400/UHS when no 1.8V support - sdhci-omap: Add support for controller in k2g SoC - sdhci-omap: Add workarounds for a couple of Erratas - sdhci-omap: Enable support for generic sdhci DT properties - sdhci-cadence: Re-send tune request to deal with errata - sdhci-pci: Fix 3.3V voltage switch for some BYT-based Intel controllers - sdhci-pci: Avoid 3.3V signaling on some NI 904x - sdhci-esdhc-imx: Use watermark levels for PIO access - sdhci-msm: Improve card detection handling - sdhci-msm: Add support voltage pad switching" * tag 'mmc-v4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (104 commits) mmc: renesas_sdhi: really fix WP logic regressions mmc: mvsdio: Enable MMC_CAP_ERASE mmc: mvsdio: Respect card busy time out from mmc core mmc: sdhci-msm: Remove NO_CARD_NO_RESET quirk mmc: sunxi: Use ifdef rather than __maybe_unused mmc: mxmmc: Use ifdef rather than __maybe_unused mmc: mxmmc: include linux/highmem.h mmc: sunxi: mark PM functions as __maybe_unused mmc: Throttle calls to MMC_SEND_STATUS during mmc_do_erase() mmc: au1xmmc: handle highmem pages mmc: Allow non-sleeping GPIO cd mmc: sdhci-*: Don't emit error msg if sdhci_add_host() fails mmc: sd: Define name for default speed dtr mmc: core: Move calls to ->prepare_hs400_tuning() closer to mmc code mmc: sdhci-xenon: use match_string() helper mmc: wbsd: handle highmem pages mmc: ushc: handle highmem pages mmc: mxcmmc: handle highmem pages mmc: atmel-mci: use sg_copy_{from,to}_buffer mmc: android-goldfish: use sg_copy_{from,to}_buffer ...
This commit is contained in:
commit
f60342fac9
|
@ -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>;
|
||||
};
|
||||
|
|
29
Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt
Normal file
29
Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
* Mellanox Bluefield SoC specific extensions to the Synopsys Designware
|
||||
Mobile Storage Host Controller
|
||||
|
||||
Read synopsys-dw-mshc.txt for more details
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsys dw mshc controller properties described
|
||||
by synopsys-dw-mshc.txt and the properties used by the Mellanox Bluefield SoC
|
||||
specific extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be one of the following.
|
||||
- "mellanox,bluefield-dw-mshc": for controllers with Mellanox Bluefield SoC
|
||||
specific extensions.
|
||||
|
||||
Example:
|
||||
|
||||
/* Mellanox Bluefield SoC MMC */
|
||||
mmc@6008000 {
|
||||
compatible = "mellanox,bluefield-dw-mshc";
|
||||
reg = <0x6008000 0x400>;
|
||||
interrupts = <32>;
|
||||
fifo-depth = <0x100>;
|
||||
clock-frequency = <24000000>;
|
||||
bus-width = <8>;
|
||||
cap-mmc-highspeed;
|
||||
};
|
38
Documentation/devicetree/bindings/mmc/jz4740.txt
Normal file
38
Documentation/devicetree/bindings/mmc/jz4740.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
* Ingenic JZ47xx MMC controllers
|
||||
|
||||
This file documents the device tree properties used for the MMC controller in
|
||||
Ingenic JZ4740/JZ4780 SoCs. These are in addition to the core MMC properties
|
||||
described in mmc.txt.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of the following:
|
||||
- "ingenic,jz4740-mmc" for the JZ4740
|
||||
- "ingenic,jz4780-mmc" for the JZ4780
|
||||
- reg: Should contain the MMC controller registers location and length.
|
||||
- interrupts: Should contain the interrupt specifier of the MMC controller.
|
||||
- clocks: Clock for the MMC controller.
|
||||
|
||||
Optional properties:
|
||||
- dmas: List of DMA specifiers with the controller specific format
|
||||
as described in the generic DMA client binding. A tx and rx
|
||||
specifier is required.
|
||||
- dma-names: RX and TX DMA request names.
|
||||
Should be "rx" and "tx", in that order.
|
||||
|
||||
For additional details on DMA client bindings see ../dma/dma.txt.
|
||||
|
||||
Example:
|
||||
|
||||
mmc0: mmc@13450000 {
|
||||
compatible = "ingenic,jz4780-mmc";
|
||||
reg = <0x13450000 0x1000>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <37>;
|
||||
|
||||
clocks = <&cgu JZ4780_CLK_MSC0>;
|
||||
clock-names = "mmc";
|
||||
|
||||
dmas = <&dma JZ4780_DMA_MSC0_RX 0xffffffff>, <&dma JZ4780_DMA_MSC0_TX 0xffffffff>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
|
@ -19,6 +19,8 @@ Optional properties:
|
|||
- wp-gpios: Specify GPIOs for write protection, see gpio binding
|
||||
- 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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
mmc_claim_host(host);
|
||||
|
||||
ret = mmc_send_io_op_cond(host, 0, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
81
drivers/mmc/host/dw_mmc-bluefield.c
Normal file
81
drivers/mmc/host/dw_mmc-bluefield.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Mellanox Technologies.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
#define UHS_REG_EXT_SAMPLE_MASK GENMASK(22, 16)
|
||||
#define UHS_REG_EXT_DRIVE_MASK GENMASK(29, 23)
|
||||
#define BLUEFIELD_UHS_REG_EXT_SAMPLE 2
|
||||
#define BLUEFIELD_UHS_REG_EXT_DRIVE 4
|
||||
|
||||
static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Update the Drive and Sample fields in register UHS_REG_EXT. */
|
||||
reg = mci_readl(host, UHS_REG_EXT);
|
||||
reg &= ~UHS_REG_EXT_SAMPLE_MASK;
|
||||
reg |= FIELD_PREP(UHS_REG_EXT_SAMPLE_MASK,
|
||||
BLUEFIELD_UHS_REG_EXT_SAMPLE);
|
||||
reg &= ~UHS_REG_EXT_DRIVE_MASK;
|
||||
reg |= FIELD_PREP(UHS_REG_EXT_DRIVE_MASK, BLUEFIELD_UHS_REG_EXT_DRIVE);
|
||||
mci_writel(host, UHS_REG_EXT, reg);
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data bluefield_drv_data = {
|
||||
.set_ios = dw_mci_bluefield_set_ios
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_bluefield_match[] = {
|
||||
{ .compatible = "mellanox,bluefield-dw-mshc",
|
||||
.data = &bluefield_drv_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_bluefield_match);
|
||||
|
||||
static int dw_mci_bluefield_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(dw_mci_bluefield_match,
|
||||
pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
}
|
||||
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static struct platform_driver dw_mci_bluefield_pltfm_driver = {
|
||||
.probe = dw_mci_bluefield_probe,
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dwmmc_bluefield",
|
||||
.of_match_table = dw_mci_bluefield_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_mci_bluefield_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("BlueField DW Multimedia Card driver");
|
||||
MODULE_AUTHOR("Mellanox Technologies");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -44,9 +44,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||
* bus_hz = cclkin / RK3288_CLKGEN_DIV
|
||||
* 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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */ }
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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/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/slot-gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <linux/mmc/host.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/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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
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;
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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) {
|
||||
|
|
49
include/dt-bindings/dma/jz4780-dma.h
Normal file
49
include/dt-bindings/dma/jz4780-dma.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef __DT_BINDINGS_DMA_JZ4780_DMA_H__
|
||||
#define __DT_BINDINGS_DMA_JZ4780_DMA_H__
|
||||
|
||||
/*
|
||||
* Request type numbers for the JZ4780 DMA controller (written to the DRTn
|
||||
* register for the channel).
|
||||
*/
|
||||
#define JZ4780_DMA_I2S1_TX 0x4
|
||||
#define JZ4780_DMA_I2S1_RX 0x5
|
||||
#define JZ4780_DMA_I2S0_TX 0x6
|
||||
#define JZ4780_DMA_I2S0_RX 0x7
|
||||
#define JZ4780_DMA_AUTO 0x8
|
||||
#define JZ4780_DMA_SADC_RX 0x9
|
||||
#define JZ4780_DMA_UART4_TX 0xc
|
||||
#define JZ4780_DMA_UART4_RX 0xd
|
||||
#define JZ4780_DMA_UART3_TX 0xe
|
||||
#define JZ4780_DMA_UART3_RX 0xf
|
||||
#define JZ4780_DMA_UART2_TX 0x10
|
||||
#define JZ4780_DMA_UART2_RX 0x11
|
||||
#define JZ4780_DMA_UART1_TX 0x12
|
||||
#define JZ4780_DMA_UART1_RX 0x13
|
||||
#define JZ4780_DMA_UART0_TX 0x14
|
||||
#define JZ4780_DMA_UART0_RX 0x15
|
||||
#define JZ4780_DMA_SSI0_TX 0x16
|
||||
#define JZ4780_DMA_SSI0_RX 0x17
|
||||
#define JZ4780_DMA_SSI1_TX 0x18
|
||||
#define JZ4780_DMA_SSI1_RX 0x19
|
||||
#define JZ4780_DMA_MSC0_TX 0x1a
|
||||
#define JZ4780_DMA_MSC0_RX 0x1b
|
||||
#define JZ4780_DMA_MSC1_TX 0x1c
|
||||
#define JZ4780_DMA_MSC1_RX 0x1d
|
||||
#define JZ4780_DMA_MSC2_TX 0x1e
|
||||
#define JZ4780_DMA_MSC2_RX 0x1f
|
||||
#define JZ4780_DMA_PCM0_TX 0x20
|
||||
#define JZ4780_DMA_PCM0_RX 0x21
|
||||
#define JZ4780_DMA_SMB0_TX 0x24
|
||||
#define JZ4780_DMA_SMB0_RX 0x25
|
||||
#define JZ4780_DMA_SMB1_TX 0x26
|
||||
#define JZ4780_DMA_SMB1_RX 0x27
|
||||
#define JZ4780_DMA_SMB2_TX 0x28
|
||||
#define JZ4780_DMA_SMB2_RX 0x29
|
||||
#define JZ4780_DMA_SMB3_TX 0x2a
|
||||
#define JZ4780_DMA_SMB3_RX 0x2b
|
||||
#define JZ4780_DMA_SMB4_TX 0x2c
|
||||
#define JZ4780_DMA_SMB4_RX 0x2d
|
||||
#define JZ4780_DMA_DES_TX 0x2e
|
||||
#define JZ4780_DMA_DES_RX 0x2f
|
||||
|
||||
#endif /* __DT_BINDINGS_DMA_JZ4780_DMA_H__ */
|
|
@ -156,6 +156,7 @@ struct sd_switch_caps {
|
|||
#define UHS_DDR50_MAX_DTR 50000000
|
||||
#define UHS_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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue