You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'mmc-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Continue to re-factor code to prepare for eMMC CMDQ and blkmq support - Introduce queue semantics to prepare for eMMC CMDQ and blkmq support - Add helper functions to manage temporary enable/disable of eMMC CMDQ - Improve wait-busy detection for SDIO MMC host: - cavium: Add driver to support Cavium controllers - cavium: Extend Cavium driver to support Octeon and ThunderX SOCs - bcm2835: Add new driver for Broadcom BCM2835 controller - sdhci-xenon: Add driver to support Marvell Xenon SDHCI controller - sdhci-tegra: Add support for the Tegra186 variant - sdhci-of-esdhc: Support for UHS-I SD cards - sdhci-of-esdhc: Support for eMMC HS200 cards - sdhci-cadence: Add eMMC HS400 enhanced strobe support - sdhci-esdhc-imx: Reset tuning circuit when needed - sdhci-pci: Modernize and clean-up some PM related code - sdhci-pci: Avoid re-tuning at runtime PM for some Intel devices - sdhci-pci|acpi: Use aggressive PM for some Intel BYT controllers - sdhci: Re-factoring and modernizations - sdhci: Optimize delay loops - sdhci: Improve register dump print format - sdhci: Add support for the Command Queue Engine - meson-gx: Various improvements and clean-ups - meson-gx: Add support for CMD23 - meson-gx: Basic tuning support to avoid CRC errors - s3cmci: Enable probing via DT - mediatek: Improve tuning support for eMMC HS200 and HS400 mode - tmio: Improve DMA support - tmio: Use correct response for CMD12 - dw_mmc: Minor improvements and clean-ups" * tag 'mmc-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (148 commits) mmc: sdhci-of-esdhc: limit SD clock for ls1012a/ls1046a mmc: sdhci-of-esdhc: poll ESDHC_CLOCK_STABLE bit with udelay mmc: sdhci-xenon: Fix default value of LOGIC_TIMING_ADJUST for eMMC5.0 PHY mmc: sdhci-xenon: Fix the work flow in xenon_remove(). MIPS: Octeon: cavium_octeon_defconfig: Enable Octeon MMC mmc: sdhci-xenon: Remove redundant dev_err call in get_dt_pad_ctrl_data() mmc: cavium: Use module_pci_driver to simplify the code mmc: cavium: Add MMC support for Octeon SOCs. mmc: cavium: Fix detection of block or byte addressing. mmc: core: Export API to allow hosts to get the card address mmc: sdio: Fix sdio wait busy implement limitation mmc: sdhci-esdhc-imx: reset tuning circuit when power on mmc card clk: apn806: fix spelling mistake: "mising" -> "missing" mmc: sdhci-of-esdhc: add delay between tuning cycles mmc: sdhci: Control the delay between tuning commands mmc: sdhci-of-esdhc: add tuning support mmc: sdhci-of-esdhc: add support for signal voltage switch mmc: sdhci-of-esdhc: add peripheral clock support mmc: sdhci-pci: Allow for 3 bytes from Intel DSM mmc: cavium: Fix a shift wrapping bug ...
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
Broadcom BCM2835 SDHOST controller
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties that represent the BCM2835 controller.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "brcm,bcm2835-sdhost".
|
||||
- clocks: The clock feeding the SDHOST controller.
|
||||
|
||||
Optional properties:
|
||||
- dmas: DMA channel for read and write.
|
||||
See Documentation/devicetree/bindings/dma/dma.txt for details
|
||||
|
||||
Example:
|
||||
|
||||
sdhost: mmc@7e202000 {
|
||||
compatible = "brcm,bcm2835-sdhost";
|
||||
reg = <0x7e202000 0x100>;
|
||||
interrupts = <2 24>;
|
||||
clocks = <&clocks BCM2835_CLOCK_VPU>;
|
||||
dmas = <&dma 13>;
|
||||
dma-names = "rx-tx";
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
* Cavium Octeon & ThunderX MMC controller
|
||||
|
||||
The highspeed MMC host controller on Caviums SoCs provides an interface
|
||||
for MMC and SD types of memory cards.
|
||||
|
||||
Supported maximum speeds are the ones of the eMMC standard 4.41 as well
|
||||
as the speed of SD standard 4.0. Only 3.3 Volt is supported.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of:
|
||||
cavium,octeon-6130-mmc
|
||||
cavium,octeon-7890-mmc
|
||||
cavium,thunder-8190-mmc
|
||||
cavium,thunder-8390-mmc
|
||||
mmc-slot
|
||||
- reg : mmc controller base registers
|
||||
- clocks : phandle
|
||||
|
||||
Optional properties:
|
||||
- for cd, bus-width and additional generic mmc parameters
|
||||
please refer to mmc.txt within this directory
|
||||
- cavium,cmd-clk-skew : number of coprocessor clocks before sampling command
|
||||
- cavium,dat-clk-skew : number of coprocessor clocks before sampling data
|
||||
|
||||
Deprecated properties:
|
||||
- spi-max-frequency : use max-frequency instead
|
||||
- cavium,bus-max-width : use bus-width instead
|
||||
- power-gpios : use vmmc-supply instead
|
||||
- cavium,octeon-6130-mmc-slot : use mmc-slot instead
|
||||
|
||||
Examples:
|
||||
mmc_1_4: mmc@1,4 {
|
||||
compatible = "cavium,thunder-8390-mmc";
|
||||
reg = <0x0c00 0 0 0 0>; /* DEVFN = 0x0c (1:4) */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&sclk>;
|
||||
|
||||
mmc-slot@0 {
|
||||
compatible = "mmc-slot";
|
||||
reg = <0>;
|
||||
vmmc-supply = <&mmc_supply_3v3>;
|
||||
max-frequency = <42000000>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
};
|
||||
|
||||
mmc-slot@1 {
|
||||
compatible = "mmc-slot";
|
||||
reg = <1>;
|
||||
vmmc-supply = <&mmc_supply_3v3>;
|
||||
max-frequency = <42000000>;
|
||||
bus-width = <8>;
|
||||
cap-mmc-highspeed;
|
||||
non-removable;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,170 @@
|
||||
Marvell Xenon SDHCI Controller device tree bindings
|
||||
This file documents differences between the core mmc properties
|
||||
described by mmc.txt and the properties used by the Xenon implementation.
|
||||
|
||||
Multiple SDHCs might be put into a single Xenon IP, to save size and cost.
|
||||
Each SDHC is independent and owns independent resources, such as register sets,
|
||||
clock and PHY.
|
||||
Each SDHC should have an independent device tree node.
|
||||
|
||||
Required Properties:
|
||||
- compatible: should be one of the following
|
||||
- "marvell,armada-3700-sdhci": For controllers on Armada-3700 SoC.
|
||||
Must provide a second register area and marvell,pad-type.
|
||||
- "marvell,armada-ap806-sdhci": For controllers on Armada AP806.
|
||||
- "marvell,armada-cp110-sdhci": For controllers on Armada CP110.
|
||||
|
||||
- clocks:
|
||||
Array of clocks required for SDHC.
|
||||
Require at least input clock for Xenon IP core.
|
||||
|
||||
- clock-names:
|
||||
Array of names corresponding to clocks property.
|
||||
The input clock for Xenon IP core should be named as "core".
|
||||
|
||||
- reg:
|
||||
* For "marvell,armada-3700-sdhci", two register areas.
|
||||
The first one for Xenon IP register. The second one for the Armada 3700 SoC
|
||||
PHY PAD Voltage Control register.
|
||||
Please follow the examples with compatible "marvell,armada-3700-sdhci"
|
||||
in below.
|
||||
Please also check property marvell,pad-type in below.
|
||||
|
||||
* For other compatible strings, one register area for Xenon IP.
|
||||
|
||||
Optional Properties:
|
||||
- marvell,xenon-sdhc-id:
|
||||
Indicate the corresponding bit index of current SDHC in
|
||||
SDHC System Operation Control Register Bit[7:0].
|
||||
Set/clear the corresponding bit to enable/disable current SDHC.
|
||||
If Xenon IP contains only one SDHC, this property is optional.
|
||||
|
||||
- marvell,xenon-phy-type:
|
||||
Xenon support multiple types of PHYs.
|
||||
To select eMMC 5.1 PHY, set:
|
||||
marvell,xenon-phy-type = "emmc 5.1 phy"
|
||||
eMMC 5.1 PHY is the default choice if this property is not provided.
|
||||
To select eMMC 5.0 PHY, set:
|
||||
marvell,xenon-phy-type = "emmc 5.0 phy"
|
||||
|
||||
All those types of PHYs can support eMMC, SD and SDIO.
|
||||
Please note that this property only presents the type of PHY.
|
||||
It doesn't stand for the entire SDHC type or property.
|
||||
For example, "emmc 5.1 phy" doesn't mean that this Xenon SDHC only
|
||||
supports eMMC 5.1.
|
||||
|
||||
- marvell,xenon-phy-znr:
|
||||
Set PHY ZNR value.
|
||||
Only available for eMMC PHY.
|
||||
Valid range = [0:0x1F].
|
||||
ZNR is set as 0xF by default if this property is not provided.
|
||||
|
||||
- marvell,xenon-phy-zpr:
|
||||
Set PHY ZPR value.
|
||||
Only available for eMMC PHY.
|
||||
Valid range = [0:0x1F].
|
||||
ZPR is set as 0xF by default if this property is not provided.
|
||||
|
||||
- marvell,xenon-phy-nr-success-tun:
|
||||
Set the number of required consecutive successful sampling points
|
||||
used to identify a valid sampling window, in tuning process.
|
||||
Valid range = [1:7].
|
||||
Set as 0x4 by default if this property is not provided.
|
||||
|
||||
- marvell,xenon-phy-tun-step-divider:
|
||||
Set the divider for calculating TUN_STEP.
|
||||
Set as 64 by default if this property is not provided.
|
||||
|
||||
- marvell,xenon-phy-slow-mode:
|
||||
If this property is selected, transfers will bypass PHY.
|
||||
Only available when bus frequency lower than 55MHz in SDR mode.
|
||||
Disabled by default. Please only try this property if timing issues
|
||||
always occur with PHY enabled in eMMC HS SDR, SD SDR12, SD SDR25,
|
||||
SD Default Speed and HS mode and eMMC legacy speed mode.
|
||||
|
||||
- marvell,xenon-tun-count:
|
||||
Xenon SDHC SoC usually doesn't provide re-tuning counter in
|
||||
Capabilities Register 3 Bit[11:8].
|
||||
This property provides the re-tuning counter.
|
||||
If this property is not set, default re-tuning counter will
|
||||
be set as 0x9 in driver.
|
||||
|
||||
- marvell,pad-type:
|
||||
Type of Armada 3700 SoC PHY PAD Voltage Controller register.
|
||||
Only valid when "marvell,armada-3700-sdhci" is selected.
|
||||
Two types: "sd" and "fixed-1-8v".
|
||||
If "sd" is selected, SoC PHY PAD is set as 3.3V at the beginning and is
|
||||
switched to 1.8V when later in higher speed mode.
|
||||
If "fixed-1-8v" is selected, SoC PHY PAD is fixed 1.8V, such as for eMMC.
|
||||
Please follow the examples with compatible "marvell,armada-3700-sdhci"
|
||||
in below.
|
||||
|
||||
Example:
|
||||
- For eMMC:
|
||||
|
||||
sdhci@aa0000 {
|
||||
compatible = "marvell,armada-ap806-sdhci";
|
||||
reg = <0xaa0000 0x1000>;
|
||||
interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>
|
||||
clocks = <&emmc_clk>;
|
||||
clock-names = "core";
|
||||
bus-width = <4>;
|
||||
marvell,xenon-phy-slow-mode;
|
||||
marvell,xenon-tun-count = <11>;
|
||||
non-removable;
|
||||
no-sd;
|
||||
no-sdio;
|
||||
|
||||
/* Vmmc and Vqmmc are both fixed */
|
||||
};
|
||||
|
||||
- For SD/SDIO:
|
||||
|
||||
sdhci@ab0000 {
|
||||
compatible = "marvell,armada-cp110-sdhci";
|
||||
reg = <0xab0000 0x1000>;
|
||||
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>
|
||||
vqmmc-supply = <&sd_vqmmc_regulator>;
|
||||
vmmc-supply = <&sd_vmmc_regulator>;
|
||||
clocks = <&sdclk>;
|
||||
clock-names = "core";
|
||||
bus-width = <4>;
|
||||
marvell,xenon-tun-count = <9>;
|
||||
};
|
||||
|
||||
- For eMMC with compatible "marvell,armada-3700-sdhci":
|
||||
|
||||
sdhci@aa0000 {
|
||||
compatible = "marvell,armada-3700-sdhci";
|
||||
reg = <0xaa0000 0x1000>,
|
||||
<phy_addr 0x4>;
|
||||
interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>
|
||||
clocks = <&emmcclk>;
|
||||
clock-names = "core";
|
||||
bus-width = <8>;
|
||||
mmc-ddr-1_8v;
|
||||
mmc-hs400-1_8v;
|
||||
non-removable;
|
||||
no-sd;
|
||||
no-sdio;
|
||||
|
||||
/* Vmmc and Vqmmc are both fixed */
|
||||
|
||||
marvell,pad-type = "fixed-1-8v";
|
||||
};
|
||||
|
||||
- For SD/SDIO with compatible "marvell,armada-3700-sdhci":
|
||||
|
||||
sdhci@ab0000 {
|
||||
compatible = "marvell,armada-3700-sdhci";
|
||||
reg = <0xab0000 0x1000>,
|
||||
<phy_addr 0x4>;
|
||||
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>
|
||||
vqmmc-supply = <&sd_regulator>;
|
||||
/* Vmmc is fixed */
|
||||
clocks = <&sdclk>;
|
||||
clock-names = "core";
|
||||
bus-width = <4>;
|
||||
|
||||
marvell,pad-type = "sd";
|
||||
};
|
||||
@@ -21,6 +21,15 @@ Optional properties:
|
||||
- assigned-clocks: PLL of the source clock
|
||||
- assigned-clock-parents: parent of source clock, used for HS400 mode to get 400Mhz source clock
|
||||
- hs400-ds-delay: HS400 DS delay setting
|
||||
- mediatek,hs200-cmd-int-delay: HS200 command internal delay setting.
|
||||
This field has total 32 stages.
|
||||
The value is an integer from 0 to 31.
|
||||
- mediatek,hs400-cmd-int-delay: HS400 command internal delay setting
|
||||
This field has total 32 stages.
|
||||
The value is an integer from 0 to 31.
|
||||
- mediatek,hs400-cmd-resp-sel-rising: HS400 command response sample selection
|
||||
If present,HS400 command responses are sampled on rising edges.
|
||||
If not present,HS400 command responses are sampled on falling edges.
|
||||
|
||||
Examples:
|
||||
mmc0: mmc@11230000 {
|
||||
@@ -38,4 +47,7 @@ mmc0: mmc@11230000 {
|
||||
assigned-clocks = <&topckgen CLK_TOP_MSDC50_0_SEL>;
|
||||
assigned-clock-parents = <&topckgen CLK_TOP_MSDCPLL_D2>;
|
||||
hs400-ds-delay = <0x14015>;
|
||||
mediatek,hs200-cmd-int-delay = <26>;
|
||||
mediatek,hs400-cmd-int-delay = <14>;
|
||||
mediatek,hs400-cmd-resp-sel-rising;
|
||||
};
|
||||
|
||||
@@ -7,11 +7,13 @@ This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the sdhci-tegra driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : For Tegra20, must contain "nvidia,tegra20-sdhci".
|
||||
For Tegra30, must contain "nvidia,tegra30-sdhci". For Tegra114,
|
||||
must contain "nvidia,tegra114-sdhci". For Tegra124, must contain
|
||||
"nvidia,tegra124-sdhci". Otherwise, must contain "nvidia,<chip>-sdhci",
|
||||
plus one of the above, where <chip> is tegra132 or tegra210.
|
||||
- compatible : should be one of:
|
||||
- "nvidia,tegra20-sdhci": for Tegra20
|
||||
- "nvidia,tegra30-sdhci": for Tegra30
|
||||
- "nvidia,tegra114-sdhci": for Tegra114
|
||||
- "nvidia,tegra124-sdhci": for Tegra124 and Tegra132
|
||||
- "nvidia,tegra210-sdhci": for Tegra210
|
||||
- "nvidia,tegra186-sdhci": for Tegra186
|
||||
- clocks : Must contain one entry, for the module clock.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- resets : Must contain an entry for each entry in reset-names.
|
||||
|
||||
@@ -8,6 +8,7 @@ Required properties:
|
||||
|
||||
- compatible: should be "renesas,mmcif-<soctype>", "renesas,sh-mmcif" as a
|
||||
fallback. Examples with <soctype> are:
|
||||
- "renesas,mmcif-r7s72100" for the MMCIF found in r7s72100 SoCs
|
||||
- "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
|
||||
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
|
||||
- "renesas,mmcif-r8a7778" for the MMCIF found in r8a7778 SoCs
|
||||
@@ -17,6 +18,13 @@ Required properties:
|
||||
- "renesas,mmcif-r8a7794" for the MMCIF found in r8a7794 SoCs
|
||||
- "renesas,mmcif-sh73a0" for the MMCIF found in sh73a0 SoCs
|
||||
|
||||
- interrupts: Some SoCs have only 1 shared interrupt, while others have either
|
||||
2 or 3 individual interrupts (error, int, card detect). Below is the number
|
||||
of interrupts for each SoC:
|
||||
1: r8a73a4, r8a7778, r8a7790, r8a7791, r8a7793, r8a7794
|
||||
2: r8a7740, sh73a0
|
||||
3: r7s72100
|
||||
|
||||
- clocks: reference to the functional clock
|
||||
|
||||
- dmas: reference to the DMA channels, one per channel name listed in the
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
* Samsung's S3C24XX MMC/SD/SDIO controller device tree bindings
|
||||
|
||||
Samsung's S3C24XX MMC/SD/SDIO controller is used as a connectivity interface
|
||||
with external MMC, SD and SDIO storage mediums.
|
||||
|
||||
This file documents differences between the core mmc properties described by
|
||||
mmc.txt and the properties used by the Samsung S3C24XX MMC/SD/SDIO controller
|
||||
implementation.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be one of the following
|
||||
- "samsung,s3c2410-sdi": for controllers compatible with s3c2410
|
||||
- "samsung,s3c2412-sdi": for controllers compatible with s3c2412
|
||||
- "samsung,s3c2440-sdi": for controllers compatible with s3c2440
|
||||
- reg: register location and length
|
||||
- interrupts: mmc controller interrupt
|
||||
- clocks: Should reference the controller clock
|
||||
- clock-names: Should contain "sdi"
|
||||
|
||||
Required Board Specific Properties:
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Optional Properties:
|
||||
- bus-width: number of data lines (see mmc.txt)
|
||||
- cd-gpios: gpio for card detection (see mmc.txt)
|
||||
- wp-gpios: gpio for write protection (see mmc.txt)
|
||||
|
||||
Example:
|
||||
|
||||
mmc0: mmc@5a000000 {
|
||||
compatible = "samsung,s3c2440-sdi";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sdi_pins>;
|
||||
reg = <0x5a000000 0x100000>;
|
||||
interrupts = <0 0 21 3>;
|
||||
clocks = <&clocks PCLK_SDI>;
|
||||
clock-names = "sdi";
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpg 8 GPIO_ACTIVE_LOW>;
|
||||
wp-gpios = <&gph 8 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
@@ -19,6 +19,53 @@ if supported. See mmc.txt for details.
|
||||
- mmc-hs400-1_8v
|
||||
- mmc-hs400-1_2v
|
||||
|
||||
Some PHY delays can be configured by following properties.
|
||||
PHY DLL input delays:
|
||||
They are used to delay the data valid window, and align the window
|
||||
to sampling clock. The delay starts from 5ns (for delay parameter equal to 0)
|
||||
and it is increased by 2.5ns in each step.
|
||||
- cdns,phy-input-delay-sd-highspeed:
|
||||
Value of the delay in the input path for SD high-speed timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-legacy:
|
||||
Value of the delay in the input path for legacy timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-sd-uhs-sdr12:
|
||||
Value of the delay in the input path for SD UHS SDR12 timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-sd-uhs-sdr25:
|
||||
Value of the delay in the input path for SD UHS SDR25 timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-sd-uhs-sdr50:
|
||||
Value of the delay in the input path for SD UHS SDR50 timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-sd-uhs-ddr50:
|
||||
Value of the delay in the input path for SD UHS DDR50 timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-mmc-highspeed:
|
||||
Value of the delay in the input path for MMC high-speed timing
|
||||
Valid range = [0:0x1F].
|
||||
- cdns,phy-input-delay-mmc-ddr:
|
||||
Value of the delay in the input path for eMMC high-speed DDR timing
|
||||
Valid range = [0:0x1F].
|
||||
|
||||
PHY DLL clock delays:
|
||||
Each delay property represents the fraction of the clock period.
|
||||
The approximate delay value will be
|
||||
(<delay property value>/128)*sdmclk_clock_period.
|
||||
- cdns,phy-dll-delay-sdclk:
|
||||
Value of the delay introduced on the sdclk output
|
||||
for all modes except HS200, HS400 and HS400_ES.
|
||||
Valid range = [0:0x7F].
|
||||
- cdns,phy-dll-delay-sdclk-hsmmc:
|
||||
Value of the delay introduced on the sdclk output
|
||||
for HS200, HS400 and HS400_ES speed modes.
|
||||
Valid range = [0:0x7F].
|
||||
- cdns,phy-dll-delay-strobe:
|
||||
Value of the delay introduced on the dat_strobe input
|
||||
used in HS400 / HS400_ES speed modes.
|
||||
Valid range = [0:0x7F].
|
||||
|
||||
Example:
|
||||
emmc: sdhci@5a000000 {
|
||||
compatible = "socionext,uniphier-sd4hc", "cdns,sd4hc";
|
||||
@@ -29,4 +76,5 @@ Example:
|
||||
mmc-ddr-1_8v;
|
||||
mmc-hs200-1_8v;
|
||||
mmc-hs400-1_8v;
|
||||
cdns,phy-dll-delay-sdclk = <0>;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ All attributes are read-only.
|
||||
rel_sectors Reliable write sector count
|
||||
ocr Operation Conditions Register
|
||||
dsr Driver Stage Register
|
||||
cmdq_en Command Queue enabled: 1 => enabled, 0 => not enabled
|
||||
|
||||
Note on Erase Size and Preferred Erase Size:
|
||||
|
||||
|
||||
+15
@@ -3064,6 +3064,14 @@ S: Supported
|
||||
F: drivers/i2c/busses/i2c-octeon*
|
||||
F: drivers/i2c/busses/i2c-thunderx*
|
||||
|
||||
CAVIUM MMC DRIVER
|
||||
M: Jan Glauber <jglauber@cavium.com>
|
||||
M: David Daney <david.daney@cavium.com>
|
||||
M: Steven J. Hill <Steven.Hill@cavium.com>
|
||||
W: http://www.cavium.com
|
||||
S: Supported
|
||||
F: drivers/mmc/host/cavium*
|
||||
|
||||
CAVIUM LIQUIDIO NETWORK DRIVER
|
||||
M: Derek Chickles <derek.chickles@caviumnetworks.com>
|
||||
M: Satanand Burla <satananda.burla@caviumnetworks.com>
|
||||
@@ -7919,6 +7927,13 @@ M: Nicolas Pitre <nico@fluxnic.net>
|
||||
S: Odd Fixes
|
||||
F: drivers/mmc/host/mvsdio.*
|
||||
|
||||
MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
|
||||
M: Hu Ziji <huziji@marvell.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/mmc/host/sdhci-xenon*
|
||||
F: Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt
|
||||
|
||||
MATROX FRAMEBUFFER DRIVER
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
S: Orphan
|
||||
|
||||
@@ -235,7 +235,8 @@
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "ap-cpu-cluster-0",
|
||||
"ap-cpu-cluster-1",
|
||||
"ap-fixed", "ap-mss";
|
||||
"ap-fixed", "ap-mss",
|
||||
"ap-emmc";
|
||||
reg = <0x6f4000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -127,6 +127,11 @@ CONFIG_USB_EHCI_HCD=m
|
||||
CONFIG_USB_EHCI_HCD_PLATFORM=m
|
||||
CONFIG_USB_OHCI_HCD=m
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=m
|
||||
CONFIG_MMC=y
|
||||
# CONFIG_PWRSEQ_EMMC is not set
|
||||
# CONFIG_PWRSEQ_SIMPLE is not set
|
||||
# CONFIG_MMC_BLOCK_BOUNCE is not set
|
||||
CONFIG_MMC_CAVIUM_OCTEON=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_DS1307=y
|
||||
CONFIG_STAGING=y
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#define AP806_SAR_REG 0x400
|
||||
#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f
|
||||
|
||||
#define AP806_CLK_NUM 4
|
||||
#define AP806_CLK_NUM 5
|
||||
|
||||
static struct clk *ap806_clks[AP806_CLK_NUM];
|
||||
|
||||
@@ -135,6 +135,23 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev)
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* eMMC Clock is fixed clock divided by 3 */
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
4, &name)) {
|
||||
ap806_clk_data.clk_num--;
|
||||
dev_warn(&pdev->dev,
|
||||
"eMMC clock missing: update the device tree!\n");
|
||||
} else {
|
||||
ap806_clks[4] = clk_register_fixed_factor(NULL, name,
|
||||
fixedclk_name,
|
||||
0, 1, 3);
|
||||
if (IS_ERR(ap806_clks[4])) {
|
||||
ret = PTR_ERR(ap806_clks[4]);
|
||||
goto fail4;
|
||||
}
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
|
||||
ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
|
||||
if (ret)
|
||||
goto fail_clk_add;
|
||||
@@ -142,6 +159,8 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
fail_clk_add:
|
||||
clk_unregister_fixed_factor(ap806_clks[4]);
|
||||
fail4:
|
||||
clk_unregister_fixed_factor(ap806_clks[3]);
|
||||
fail3:
|
||||
clk_unregister_fixed_rate(ap806_clks[2]);
|
||||
|
||||
+185
-115
File diff suppressed because it is too large
Load Diff
+102
-91
@@ -172,14 +172,16 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
|
||||
trace_mmc_request_done(host, mrq);
|
||||
|
||||
if (err && cmd->retries && !mmc_card_removed(host->card)) {
|
||||
/*
|
||||
* Request starter must handle retries - see
|
||||
* mmc_wait_for_req_done().
|
||||
*/
|
||||
if (mrq->done)
|
||||
mrq->done(mrq);
|
||||
} else {
|
||||
/*
|
||||
* We list various conditions for the command to be considered
|
||||
* properly done:
|
||||
*
|
||||
* - There was no error, OK fine then
|
||||
* - We are not doing some kind of retry
|
||||
* - The card was removed (...so just complete everything no matter
|
||||
* if there are errors or retries)
|
||||
*/
|
||||
if (!err || !cmd->retries || mmc_card_removed(host->card)) {
|
||||
mmc_should_fail_request(host, mrq);
|
||||
|
||||
if (!host->ongoing_mrq)
|
||||
@@ -211,10 +213,13 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
mrq->stop->resp[0], mrq->stop->resp[1],
|
||||
mrq->stop->resp[2], mrq->stop->resp[3]);
|
||||
}
|
||||
|
||||
if (mrq->done)
|
||||
mrq->done(mrq);
|
||||
}
|
||||
/*
|
||||
* Request starter must handle retries - see
|
||||
* mmc_wait_for_req_done().
|
||||
*/
|
||||
if (mrq->done)
|
||||
mrq->done(mrq);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_request_done);
|
||||
@@ -234,8 +239,10 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
/*
|
||||
* For sdio rw commands we must wait for card busy otherwise some
|
||||
* sdio devices won't work properly.
|
||||
* And bypass I/O abort, reset and bus suspend operations.
|
||||
*/
|
||||
if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) {
|
||||
if (sdio_is_io_busy(mrq->cmd->opcode, mrq->cmd->arg) &&
|
||||
host->ops->card_busy) {
|
||||
int tries = 500; /* Wait aprox 500ms at maximum */
|
||||
|
||||
while (host->ops->card_busy(host) && --tries)
|
||||
@@ -262,26 +269,19 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
host->ops->request(host, mrq);
|
||||
}
|
||||
|
||||
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
static void mmc_mrq_pr_debug(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned int i, sz;
|
||||
struct scatterlist *sg;
|
||||
#endif
|
||||
mmc_retune_hold(host);
|
||||
|
||||
if (mmc_card_removed(host->card))
|
||||
return -ENOMEDIUM;
|
||||
|
||||
if (mrq->sbc) {
|
||||
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
|
||||
mmc_hostname(host), mrq->sbc->opcode,
|
||||
mrq->sbc->arg, mrq->sbc->flags);
|
||||
}
|
||||
|
||||
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
|
||||
mmc_hostname(host), mrq->cmd->opcode,
|
||||
mrq->cmd->arg, mrq->cmd->flags);
|
||||
if (mrq->cmd) {
|
||||
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
|
||||
mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg,
|
||||
mrq->cmd->flags);
|
||||
}
|
||||
|
||||
if (mrq->data) {
|
||||
pr_debug("%s: blksz %d blocks %d flags %08x "
|
||||
@@ -297,11 +297,20 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
mmc_hostname(host), mrq->stop->opcode,
|
||||
mrq->stop->arg, mrq->stop->flags);
|
||||
}
|
||||
}
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned int i, sz;
|
||||
struct scatterlist *sg;
|
||||
#endif
|
||||
|
||||
mrq->cmd->error = 0;
|
||||
mrq->cmd->mrq = mrq;
|
||||
if (mrq->cmd) {
|
||||
mrq->cmd->error = 0;
|
||||
mrq->cmd->mrq = mrq;
|
||||
mrq->cmd->data = mrq->data;
|
||||
}
|
||||
if (mrq->sbc) {
|
||||
mrq->sbc->error = 0;
|
||||
mrq->sbc->mrq = mrq;
|
||||
@@ -318,8 +327,6 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
if (sz != mrq->data->blocks * mrq->data->blksz)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
mrq->cmd->data = mrq->data;
|
||||
mrq->data->error = 0;
|
||||
mrq->data->mrq = mrq;
|
||||
if (mrq->stop) {
|
||||
@@ -328,6 +335,27 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
mrq->stop->mrq = mrq;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
int err;
|
||||
|
||||
mmc_retune_hold(host);
|
||||
|
||||
if (mmc_card_removed(host->card))
|
||||
return -ENOMEDIUM;
|
||||
|
||||
mmc_mrq_pr_debug(host, mrq);
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
err = mmc_mrq_prep(host, mrq);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
led_trigger_event(host->led, LED_FULL);
|
||||
__mmc_start_request(host, mrq);
|
||||
|
||||
@@ -485,56 +513,6 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* mmc_wait_for_data_req_done() - wait for request completed
|
||||
* @host: MMC host to prepare the command.
|
||||
* @mrq: MMC request to wait for
|
||||
*
|
||||
* Blocks MMC context till host controller will ack end of data request
|
||||
* execution or new request notification arrives from the block layer.
|
||||
* Handles command retries.
|
||||
*
|
||||
* Returns enum mmc_blk_status after checking errors.
|
||||
*/
|
||||
static enum mmc_blk_status mmc_wait_for_data_req_done(struct mmc_host *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_context_info *context_info = &host->context_info;
|
||||
enum mmc_blk_status status;
|
||||
|
||||
while (1) {
|
||||
wait_event_interruptible(context_info->wait,
|
||||
(context_info->is_done_rcv ||
|
||||
context_info->is_new_req));
|
||||
|
||||
if (context_info->is_done_rcv) {
|
||||
context_info->is_done_rcv = false;
|
||||
cmd = mrq->cmd;
|
||||
|
||||
if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card)) {
|
||||
status = host->areq->err_check(host->card,
|
||||
host->areq);
|
||||
break; /* return status */
|
||||
} else {
|
||||
mmc_retune_recheck(host);
|
||||
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
|
||||
mmc_hostname(host),
|
||||
cmd->opcode, cmd->error);
|
||||
cmd->retries--;
|
||||
cmd->error = 0;
|
||||
__mmc_start_request(host, mrq);
|
||||
continue; /* wait for done/new event again */
|
||||
}
|
||||
}
|
||||
|
||||
return MMC_BLK_NEW_REQUEST;
|
||||
}
|
||||
mmc_retune_release(host);
|
||||
return status;
|
||||
}
|
||||
|
||||
void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
struct mmc_command *cmd;
|
||||
@@ -639,14 +617,44 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
|
||||
*/
|
||||
static enum mmc_blk_status mmc_finalize_areq(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_context_info *context_info = &host->context_info;
|
||||
enum mmc_blk_status status;
|
||||
|
||||
if (!host->areq)
|
||||
return MMC_BLK_SUCCESS;
|
||||
|
||||
status = mmc_wait_for_data_req_done(host, host->areq->mrq);
|
||||
if (status == MMC_BLK_NEW_REQUEST)
|
||||
return status;
|
||||
while (1) {
|
||||
wait_event_interruptible(context_info->wait,
|
||||
(context_info->is_done_rcv ||
|
||||
context_info->is_new_req));
|
||||
|
||||
if (context_info->is_done_rcv) {
|
||||
struct mmc_command *cmd;
|
||||
|
||||
context_info->is_done_rcv = false;
|
||||
cmd = host->areq->mrq->cmd;
|
||||
|
||||
if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card)) {
|
||||
status = host->areq->err_check(host->card,
|
||||
host->areq);
|
||||
break; /* return status */
|
||||
} else {
|
||||
mmc_retune_recheck(host);
|
||||
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
|
||||
mmc_hostname(host),
|
||||
cmd->opcode, cmd->error);
|
||||
cmd->retries--;
|
||||
cmd->error = 0;
|
||||
__mmc_start_request(host, host->areq->mrq);
|
||||
continue; /* wait for done/new event again */
|
||||
}
|
||||
}
|
||||
|
||||
return MMC_BLK_NEW_REQUEST;
|
||||
}
|
||||
|
||||
mmc_retune_release(host);
|
||||
|
||||
/*
|
||||
* Check BKOPS urgency for each R1 response
|
||||
@@ -683,7 +691,7 @@ struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
|
||||
{
|
||||
enum mmc_blk_status status;
|
||||
int start_err = 0;
|
||||
struct mmc_async_req *data = host->areq;
|
||||
struct mmc_async_req *previous = host->areq;
|
||||
|
||||
/* Prepare a new request */
|
||||
if (areq)
|
||||
@@ -691,13 +699,12 @@ struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
|
||||
|
||||
/* Finalize previous request */
|
||||
status = mmc_finalize_areq(host);
|
||||
if (ret_stat)
|
||||
*ret_stat = status;
|
||||
|
||||
/* The previous request is still going on... */
|
||||
if (status == MMC_BLK_NEW_REQUEST) {
|
||||
if (ret_stat)
|
||||
*ret_stat = status;
|
||||
if (status == MMC_BLK_NEW_REQUEST)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fine so far, start the new request! */
|
||||
if (status == MMC_BLK_SUCCESS && areq)
|
||||
@@ -716,9 +723,7 @@ struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
|
||||
else
|
||||
host->areq = areq;
|
||||
|
||||
if (ret_stat)
|
||||
*ret_stat = status;
|
||||
return data;
|
||||
return previous;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_start_areq);
|
||||
|
||||
@@ -2555,6 +2560,12 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_calc_max_discard);
|
||||
|
||||
bool mmc_card_is_blockaddr(struct mmc_card *card)
|
||||
{
|
||||
return card ? mmc_card_blockaddr(card) : false;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_card_is_blockaddr);
|
||||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
||||
{
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
@@ -790,6 +790,7 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
||||
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
||||
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||
MMC_DEV_ATTR(ocr, "%08x\n", card->ocr);
|
||||
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
|
||||
|
||||
static ssize_t mmc_fwrev_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@@ -845,6 +846,7 @@ static struct attribute *mmc_std_attrs[] = {
|
||||
&dev_attr_rel_sectors.attr,
|
||||
&dev_attr_ocr.attr,
|
||||
&dev_attr_dsr.attr,
|
||||
&dev_attr_cmdq_en.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(mmc_std);
|
||||
@@ -1787,6 +1789,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In some cases (e.g. RPMB or mmc_test), the Command Queue must be
|
||||
* disabled for a time, so a flag is needed to indicate to re-enable the
|
||||
* Command Queue.
|
||||
*/
|
||||
card->reenable_cmdq = card->ext_csd.cmdq_en;
|
||||
|
||||
/*
|
||||
* The mandatory minimum values are defined for packed command.
|
||||
* read: 5, write: 3
|
||||
|
||||
@@ -305,7 +305,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
||||
int mmc_send_csd(struct mmc_card *card, u32 *csd)
|
||||
{
|
||||
int ret, i;
|
||||
u32 *csd_tmp;
|
||||
__be32 *csd_tmp;
|
||||
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
return mmc_send_cxd_native(card->host, card->rca << 16,
|
||||
@@ -319,7 +319,7 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 0;i < 4;i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
csd[i] = be32_to_cpu(csd_tmp[i]);
|
||||
|
||||
err:
|
||||
@@ -330,7 +330,7 @@ err:
|
||||
int mmc_send_cid(struct mmc_host *host, u32 *cid)
|
||||
{
|
||||
int ret, i;
|
||||
u32 *cid_tmp;
|
||||
__be32 *cid_tmp;
|
||||
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
if (!host->card)
|
||||
@@ -347,7 +347,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 0;i < 4;i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
cid[i] = be32_to_cpu(cid_tmp[i]);
|
||||
|
||||
err:
|
||||
@@ -838,3 +838,31 @@ int mmc_can_ext_csd(struct mmc_card *card)
|
||||
{
|
||||
return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
|
||||
}
|
||||
|
||||
static int mmc_cmdq_switch(struct mmc_card *card, bool enable)
|
||||
{
|
||||
u8 val = enable ? EXT_CSD_CMDQ_MODE_ENABLED : 0;
|
||||
int err;
|
||||
|
||||
if (!card->ext_csd.cmdq_support)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CMDQ_MODE_EN,
|
||||
val, card->ext_csd.generic_cmd6_time);
|
||||
if (!err)
|
||||
card->ext_csd.cmdq_en = enable;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mmc_cmdq_enable(struct mmc_card *card)
|
||||
{
|
||||
return mmc_cmdq_switch(card, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_cmdq_enable);
|
||||
|
||||
int mmc_cmdq_disable(struct mmc_card *card)
|
||||
{
|
||||
return mmc_cmdq_switch(card, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_cmdq_disable);
|
||||
|
||||
@@ -46,6 +46,8 @@ int mmc_read_bkops_status(struct mmc_card *card);
|
||||
void mmc_start_bkops(struct mmc_card *card, bool from_exception);
|
||||
int mmc_can_reset(struct mmc_card *card);
|
||||
int mmc_flush_cache(struct mmc_card *card);
|
||||
int mmc_cmdq_enable(struct mmc_card *card);
|
||||
int mmc_cmdq_disable(struct mmc_card *card);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
#include "mmc_ops.h"
|
||||
|
||||
#define RESULT_OK 0
|
||||
#define RESULT_FAIL 1
|
||||
@@ -3264,6 +3265,14 @@ static int mmc_test_probe(struct mmc_card *card)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (card->ext_csd.cmdq_en) {
|
||||
mmc_claim_host(card->host);
|
||||
ret = mmc_cmdq_disable(card);
|
||||
mmc_release_host(card->host);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&card->dev, "Card claimed for testing.\n");
|
||||
|
||||
return 0;
|
||||
@@ -3271,6 +3280,11 @@ static int mmc_test_probe(struct mmc_card *card)
|
||||
|
||||
static void mmc_test_remove(struct mmc_card *card)
|
||||
{
|
||||
if (card->reenable_cmdq) {
|
||||
mmc_claim_host(card->host);
|
||||
mmc_cmdq_enable(card);
|
||||
mmc_release_host(card->host);
|
||||
}
|
||||
mmc_test_free_result(card);
|
||||
mmc_test_free_dbgfs_file(card);
|
||||
}
|
||||
|
||||
+200
-137
@@ -40,6 +40,35 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
|
||||
return BLKPREP_OK;
|
||||
}
|
||||
|
||||
struct mmc_queue_req *mmc_queue_req_find(struct mmc_queue *mq,
|
||||
struct request *req)
|
||||
{
|
||||
struct mmc_queue_req *mqrq;
|
||||
int i = ffz(mq->qslots);
|
||||
|
||||
if (i >= mq->qdepth)
|
||||
return NULL;
|
||||
|
||||
mqrq = &mq->mqrq[i];
|
||||
WARN_ON(mqrq->req || mq->qcnt >= mq->qdepth ||
|
||||
test_bit(mqrq->task_id, &mq->qslots));
|
||||
mqrq->req = req;
|
||||
mq->qcnt += 1;
|
||||
__set_bit(mqrq->task_id, &mq->qslots);
|
||||
|
||||
return mqrq;
|
||||
}
|
||||
|
||||
void mmc_queue_req_free(struct mmc_queue *mq,
|
||||
struct mmc_queue_req *mqrq)
|
||||
{
|
||||
WARN_ON(!mqrq->req || mq->qcnt < 1 ||
|
||||
!test_bit(mqrq->task_id, &mq->qslots));
|
||||
mqrq->req = NULL;
|
||||
mq->qcnt -= 1;
|
||||
__clear_bit(mqrq->task_id, &mq->qslots);
|
||||
}
|
||||
|
||||
static int mmc_queue_thread(void *d)
|
||||
{
|
||||
struct mmc_queue *mq = d;
|
||||
@@ -50,7 +79,7 @@ static int mmc_queue_thread(void *d)
|
||||
|
||||
down(&mq->thread_sem);
|
||||
do {
|
||||
struct request *req = NULL;
|
||||
struct request *req;
|
||||
|
||||
spin_lock_irq(q->queue_lock);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
@@ -63,38 +92,17 @@ static int mmc_queue_thread(void *d)
|
||||
* Dispatch queue is empty so set flags for
|
||||
* mmc_request_fn() to wake us up.
|
||||
*/
|
||||
if (mq->mqrq_prev->req)
|
||||
if (mq->qcnt)
|
||||
cntx->is_waiting_last_req = true;
|
||||
else
|
||||
mq->asleep = true;
|
||||
}
|
||||
mq->mqrq_cur->req = req;
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
|
||||
if (req || mq->mqrq_prev->req) {
|
||||
bool req_is_special = mmc_req_is_special(req);
|
||||
|
||||
if (req || mq->qcnt) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
mmc_blk_issue_rq(mq, req);
|
||||
cond_resched();
|
||||
if (mq->new_request) {
|
||||
mq->new_request = false;
|
||||
continue; /* fetch again */
|
||||
}
|
||||
|
||||
/*
|
||||
* Current request becomes previous request
|
||||
* and vice versa.
|
||||
* In case of special requests, current request
|
||||
* has been finished. Do not assign it to previous
|
||||
* request.
|
||||
*/
|
||||
if (req_is_special)
|
||||
mq->mqrq_cur->req = NULL;
|
||||
|
||||
mq->mqrq_prev->brq.mrq.data = NULL;
|
||||
mq->mqrq_prev->req = NULL;
|
||||
swap(mq->mqrq_prev, mq->mqrq_cur);
|
||||
} else {
|
||||
if (kthread_should_stop()) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
@@ -141,17 +149,13 @@ static void mmc_request_fn(struct request_queue *q)
|
||||
wake_up_process(mq->thread);
|
||||
}
|
||||
|
||||
static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
|
||||
static struct scatterlist *mmc_alloc_sg(int sg_len)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
|
||||
sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
|
||||
if (!sg)
|
||||
*err = -ENOMEM;
|
||||
else {
|
||||
*err = 0;
|
||||
if (sg)
|
||||
sg_init_table(sg, sg_len);
|
||||
}
|
||||
|
||||
return sg;
|
||||
}
|
||||
@@ -175,62 +179,6 @@ static void mmc_queue_setup_discard(struct request_queue *q,
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_SECERASE, q);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMC_BLOCK_BOUNCE
|
||||
static bool mmc_queue_alloc_bounce_bufs(struct mmc_queue *mq,
|
||||
unsigned int bouncesz)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mq->qdepth; i++) {
|
||||
mq->mqrq[i].bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
||||
if (!mq->mqrq[i].bounce_buf)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
out_err:
|
||||
while (--i >= 0) {
|
||||
kfree(mq->mqrq[i].bounce_buf);
|
||||
mq->mqrq[i].bounce_buf = NULL;
|
||||
}
|
||||
pr_warn("%s: unable to allocate bounce buffers\n",
|
||||
mmc_card_name(mq->card));
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mmc_queue_alloc_bounce_sgs(struct mmc_queue *mq,
|
||||
unsigned int bouncesz)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < mq->qdepth; i++) {
|
||||
mq->mqrq[i].sg = mmc_alloc_sg(1, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mq->mqrq[i].bounce_sg = mmc_alloc_sg(bouncesz / 512, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mmc_queue_alloc_sgs(struct mmc_queue *mq, int max_segs)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < mq->qdepth; i++) {
|
||||
mq->mqrq[i].sg = mmc_alloc_sg(max_segs, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmc_queue_req_free_bufs(struct mmc_queue_req *mqrq)
|
||||
{
|
||||
kfree(mqrq->bounce_sg);
|
||||
@@ -243,12 +191,166 @@ static void mmc_queue_req_free_bufs(struct mmc_queue_req *mqrq)
|
||||
mqrq->bounce_buf = NULL;
|
||||
}
|
||||
|
||||
static void mmc_queue_reqs_free_bufs(struct mmc_queue *mq)
|
||||
static void mmc_queue_reqs_free_bufs(struct mmc_queue_req *mqrq, int qdepth)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mq->qdepth; i++)
|
||||
mmc_queue_req_free_bufs(&mq->mqrq[i]);
|
||||
for (i = 0; i < qdepth; i++)
|
||||
mmc_queue_req_free_bufs(&mqrq[i]);
|
||||
}
|
||||
|
||||
static void mmc_queue_free_mqrqs(struct mmc_queue_req *mqrq, int qdepth)
|
||||
{
|
||||
mmc_queue_reqs_free_bufs(mqrq, qdepth);
|
||||
kfree(mqrq);
|
||||
}
|
||||
|
||||
static struct mmc_queue_req *mmc_queue_alloc_mqrqs(int qdepth)
|
||||
{
|
||||
struct mmc_queue_req *mqrq;
|
||||
int i;
|
||||
|
||||
mqrq = kcalloc(qdepth, sizeof(*mqrq), GFP_KERNEL);
|
||||
if (mqrq) {
|
||||
for (i = 0; i < qdepth; i++)
|
||||
mqrq[i].task_id = i;
|
||||
}
|
||||
|
||||
return mqrq;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMC_BLOCK_BOUNCE
|
||||
static int mmc_queue_alloc_bounce_bufs(struct mmc_queue_req *mqrq, int qdepth,
|
||||
unsigned int bouncesz)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qdepth; i++) {
|
||||
mqrq[i].bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
||||
if (!mqrq[i].bounce_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
mqrq[i].sg = mmc_alloc_sg(1);
|
||||
if (!mqrq[i].sg)
|
||||
return -ENOMEM;
|
||||
|
||||
mqrq[i].bounce_sg = mmc_alloc_sg(bouncesz / 512);
|
||||
if (!mqrq[i].bounce_sg)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mmc_queue_alloc_bounce(struct mmc_queue_req *mqrq, int qdepth,
|
||||
unsigned int bouncesz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mmc_queue_alloc_bounce_bufs(mqrq, qdepth, bouncesz);
|
||||
if (ret)
|
||||
mmc_queue_reqs_free_bufs(mqrq, qdepth);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
static unsigned int mmc_queue_calc_bouncesz(struct mmc_host *host)
|
||||
{
|
||||
unsigned int bouncesz = MMC_QUEUE_BOUNCESZ;
|
||||
|
||||
if (host->max_segs != 1)
|
||||
return 0;
|
||||
|
||||
if (bouncesz > host->max_req_size)
|
||||
bouncesz = host->max_req_size;
|
||||
if (bouncesz > host->max_seg_size)
|
||||
bouncesz = host->max_seg_size;
|
||||
if (bouncesz > host->max_blk_count * 512)
|
||||
bouncesz = host->max_blk_count * 512;
|
||||
|
||||
if (bouncesz <= 512)
|
||||
return 0;
|
||||
|
||||
return bouncesz;
|
||||
}
|
||||
#else
|
||||
static inline bool mmc_queue_alloc_bounce(struct mmc_queue_req *mqrq,
|
||||
int qdepth, unsigned int bouncesz)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned int mmc_queue_calc_bouncesz(struct mmc_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mmc_queue_alloc_sgs(struct mmc_queue_req *mqrq, int qdepth,
|
||||
int max_segs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qdepth; i++) {
|
||||
mqrq[i].sg = mmc_alloc_sg(max_segs);
|
||||
if (!mqrq[i].sg)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mmc_queue_free_shared_queue(struct mmc_card *card)
|
||||
{
|
||||
if (card->mqrq) {
|
||||
mmc_queue_free_mqrqs(card->mqrq, card->qdepth);
|
||||
card->mqrq = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int __mmc_queue_alloc_shared_queue(struct mmc_card *card, int qdepth)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
struct mmc_queue_req *mqrq;
|
||||
unsigned int bouncesz;
|
||||
int ret = 0;
|
||||
|
||||
if (card->mqrq)
|
||||
return -EINVAL;
|
||||
|
||||
mqrq = mmc_queue_alloc_mqrqs(qdepth);
|
||||
if (!mqrq)
|
||||
return -ENOMEM;
|
||||
|
||||
card->mqrq = mqrq;
|
||||
card->qdepth = qdepth;
|
||||
|
||||
bouncesz = mmc_queue_calc_bouncesz(host);
|
||||
|
||||
if (bouncesz && !mmc_queue_alloc_bounce(mqrq, qdepth, bouncesz)) {
|
||||
bouncesz = 0;
|
||||
pr_warn("%s: unable to allocate bounce buffers\n",
|
||||
mmc_card_name(card));
|
||||
}
|
||||
|
||||
card->bouncesz = bouncesz;
|
||||
|
||||
if (!bouncesz) {
|
||||
ret = mmc_queue_alloc_sgs(mqrq, qdepth, host->max_segs);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
out_err:
|
||||
mmc_queue_free_shared_queue(card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mmc_queue_alloc_shared_queue(struct mmc_card *card)
|
||||
{
|
||||
return __mmc_queue_alloc_shared_queue(card, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,7 +367,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
u64 limit = BLK_BOUNCE_HIGH;
|
||||
bool bounce = false;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
|
||||
@@ -276,13 +377,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
||||
if (!mq->queue)
|
||||
return -ENOMEM;
|
||||
|
||||
mq->qdepth = 2;
|
||||
mq->mqrq = kcalloc(mq->qdepth, sizeof(struct mmc_queue_req),
|
||||
GFP_KERNEL);
|
||||
if (!mq->mqrq)
|
||||
goto blk_cleanup;
|
||||
mq->mqrq_cur = &mq->mqrq[0];
|
||||
mq->mqrq_prev = &mq->mqrq[1];
|
||||
mq->mqrq = card->mqrq;
|
||||
mq->qdepth = card->qdepth;
|
||||
mq->queue->queuedata = mq;
|
||||
|
||||
blk_queue_prep_rq(mq->queue, mmc_prep_request);
|
||||
@@ -291,44 +387,17 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
||||
if (mmc_can_erase(card))
|
||||
mmc_queue_setup_discard(mq->queue, card);
|
||||
|
||||
#ifdef CONFIG_MMC_BLOCK_BOUNCE
|
||||
if (host->max_segs == 1) {
|
||||
unsigned int bouncesz;
|
||||
|
||||
bouncesz = MMC_QUEUE_BOUNCESZ;
|
||||
|
||||
if (bouncesz > host->max_req_size)
|
||||
bouncesz = host->max_req_size;
|
||||
if (bouncesz > host->max_seg_size)
|
||||
bouncesz = host->max_seg_size;
|
||||
if (bouncesz > (host->max_blk_count * 512))
|
||||
bouncesz = host->max_blk_count * 512;
|
||||
|
||||
if (bouncesz > 512 &&
|
||||
mmc_queue_alloc_bounce_bufs(mq, bouncesz)) {
|
||||
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
|
||||
blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
|
||||
blk_queue_max_segments(mq->queue, bouncesz / 512);
|
||||
blk_queue_max_segment_size(mq->queue, bouncesz);
|
||||
|
||||
ret = mmc_queue_alloc_bounce_sgs(mq, bouncesz);
|
||||
if (ret)
|
||||
goto cleanup_queue;
|
||||
bounce = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!bounce) {
|
||||
if (card->bouncesz) {
|
||||
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
|
||||
blk_queue_max_hw_sectors(mq->queue, card->bouncesz / 512);
|
||||
blk_queue_max_segments(mq->queue, card->bouncesz / 512);
|
||||
blk_queue_max_segment_size(mq->queue, card->bouncesz);
|
||||
} else {
|
||||
blk_queue_bounce_limit(mq->queue, limit);
|
||||
blk_queue_max_hw_sectors(mq->queue,
|
||||
min(host->max_blk_count, host->max_req_size / 512));
|
||||
blk_queue_max_segments(mq->queue, host->max_segs);
|
||||
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
|
||||
|
||||
ret = mmc_queue_alloc_sgs(mq, host->max_segs);
|
||||
if (ret)
|
||||
goto cleanup_queue;
|
||||
}
|
||||
|
||||
sema_init(&mq->thread_sem, 1);
|
||||
@@ -343,11 +412,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_queue:
|
||||
mmc_queue_reqs_free_bufs(mq);
|
||||
kfree(mq->mqrq);
|
||||
cleanup_queue:
|
||||
mq->mqrq = NULL;
|
||||
blk_cleanup:
|
||||
blk_cleanup_queue(mq->queue);
|
||||
return ret;
|
||||
}
|
||||
@@ -369,10 +435,7 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
|
||||
blk_start_queue(q);
|
||||
spin_unlock_irqrestore(q->queue_lock, flags);
|
||||
|
||||
mmc_queue_reqs_free_bufs(mq);
|
||||
kfree(mq->mqrq);
|
||||
mq->mqrq = NULL;
|
||||
|
||||
mq->card = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_cleanup_queue);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user