mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
MMC core:
- Add support for eMMC inline encryption - Add a helper function to parse DT properties for clock phases - Some improvements and cleanups for the mmc_test module MMC host: - android-goldfish: Remove driver - cqhci: Add support for eMMC inline encryption - dw_mmc-zx: Remove driver - meson-gx: Extend support for scatter-gather to allow SD_IO_RW_EXTENDED - mmci: Add support for probing bus voltage level translator - mtk-sd: Address race condition for request timeouts - sdhci_am654: Add Support for the variant on TI's AM64 SoC - sdhci-esdhc-imx: Prevent kernel panic at ->remove() - sdhci-iproc: Add ACPI bindings for the RPi to enable SD and WiFi on RPi4 - sdhci-msm: Add Inline Crypto Engine support - sdhci-msm: Use actual_clock to improve timeout calculations - sdhci-of-aspeed: Add Andrew Jeffery as maintainer - sdhci-of-aspeed: Extend clock support for the AST2600 variant - sdhci-pci-gli: Increase idle period for low power state for GL9763E - sdhci-pci-o2micro: Make tuning for SDR104 HW more robust - sdhci-sirf: Remove driver - sdhci-xenon: Add support for the AP807 variant - sunxi-mmc: Add support for the A100 variant - sunxi-mmc: Ensure host is suspended during system sleep - tmio: Add detection of data timeout errors - tmio/renesas_sdhi: Extend support for retuning - renesas_sdhi_internal_dmac: Add support for the ->pre|post_req() ops -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmAqZ+YXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmB8RAAwIwsLZXLQhNfzFGZvs+gbihd hgHnPXCHxLge2VDph1KKCCkxnjKTKHfB3McZOrlNZI0wiCBMSI+ZuoxIT0UlWqsy IyZ1s3u1YT30tPpyZ8UqYDzft/9/vX1TiZlLqsYnN05ykIe7siaeBb/w/8py0ip3 rVptRn49V2TRHiu/J8FvVF1diPMKNn1S063Xyrxu4yWnQX040xHC+tyDCl9xclWR oUDl1eqJmZRomijPu+AKte3uDppcr4ejDxfvzPrx4bpJNS7ZgJ5TG5MtDm+j+0Vu aJT0bPcfn/jvHahCcpcKkYcuesKIw2CFbglv9aIxbvOfEUkTSL4zO+VCvKD9r+wk WSXrPZ27ukTJmZIA6JqdUfs3/4oi5/80uA2kQkhfyYmlA7sLJcdRmBzSgltpJWp5 bmno/grpEXUgN59F5xe3/gINQNgAt319vmOPQg2LFF/uiOWfRytunNgXCCYMJQX8 1U9q6RHQWfdcasCOhAA9U9NxM1zecuIYb/2ecDhmovSmpElxdUFuN+TW1Om/xKHh o0xxu+/654dcehyHdW8/3kq9Oz9fhBoorC3F/OUm0k0DBL50G+476hl1rbhTMVl+ SlPIUvDxCu2GRwAuprQ9vu+jUGUSvC8mfxswQkrcLak/iPeOyYNrLDFN9nA80Kve G40UQwsDC3u7X1Z7rkY= =rwB7 -----END PGP SIGNATURE----- Merge tag 'mmc-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Add support for eMMC inline encryption - Add a helper function to parse DT properties for clock phases - Some improvements and cleanups for the mmc_test module MMC host: - android-goldfish: Remove driver - cqhci: Add support for eMMC inline encryption - dw_mmc-zx: Remove driver - meson-gx: Extend support for scatter-gather to allow SD_IO_RW_EXTENDED - mmci: Add support for probing bus voltage level translator - mtk-sd: Address race condition for request timeouts - sdhci_am654: Add Support for the variant on TI's AM64 SoC - sdhci-esdhc-imx: Prevent kernel panic at ->remove() - sdhci-iproc: Add ACPI bindings for the RPi to enable SD and WiFi on RPi4 - sdhci-msm: Add Inline Crypto Engine support - sdhci-msm: Use actual_clock to improve timeout calculations - sdhci-of-aspeed: Add Andrew Jeffery as maintainer - sdhci-of-aspeed: Extend clock support for the AST2600 variant - sdhci-pci-gli: Increase idle period for low power state for GL9763E - sdhci-pci-o2micro: Make tuning for SDR104 HW more robust - sdhci-sirf: Remove driver - sdhci-xenon: Add support for the AP807 variant - sunxi-mmc: Add support for the A100 variant - sunxi-mmc: Ensure host is suspended during system sleep - tmio: Add detection of data timeout errors - tmio/renesas_sdhi: Extend support for retuning - renesas_sdhi_internal_dmac: Add support for the ->pre|post_req() ops" * tag 'mmc-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (86 commits) mmc: sdhci-esdhc-imx: fix kernel panic when remove module mmc: host: Retire MMC_GOLDFISH mmc: cb710: Use new tasklet API mmc: sdhci-pci-o2micro: Bug fix for SDR104 HW tuning failure mmc: mmc_test: use erase_arg for mmc_erase command mmc: wbsd: Use new tasklet API mmc: via-sdmmc: Use new tasklet API mmc: uniphier-sd: Use new tasklet API mmc: tifm_sd: Use new tasklet API mmc: s3cmci: Use new tasklet API mmc: omap: Use new tasklet API mmc: dw_mmc: Use new tasklet API mmc: au1xmmc: Use new tasklet API mmc: atmel-mci: Use new tasklet API mmc: cavium: Replace spin_lock_irqsave with spin_lock in hard IRQ mmc: queue: Remove unused define mmc: core: Drop redundant bouncesz from struct mmc_card mmc: core: Drop redundant member in struct mmc host mmc: core: Use host instead of card argument to mmc_spi_send_csd() mmc: core: Exclude unnecessary header file ...
This commit is contained in:
commit
19472481bf
@ -182,8 +182,9 @@ API presented to device drivers
|
||||
|
||||
A :c:type:``struct blk_keyslot_manager`` should be set up by device drivers in
|
||||
the ``request_queue`` of the device. The device driver needs to call
|
||||
``blk_ksm_init`` on the ``blk_keyslot_manager``, which specifying the number of
|
||||
keyslots supported by the hardware.
|
||||
``blk_ksm_init`` (or its resource-managed variant ``devm_blk_ksm_init``) on the
|
||||
``blk_keyslot_manager``, while specifying the number of keyslots supported by
|
||||
the hardware.
|
||||
|
||||
The device driver also needs to tell the KSM how to actually manipulate the
|
||||
IE hardware in the device to do things like programming the crypto key into
|
||||
@ -202,10 +203,9 @@ needs each and every of its keyslots to be reprogrammed with the key it
|
||||
"should have" at the point in time when the function is called. This is useful
|
||||
e.g. if a device loses all its keys on runtime power down/up.
|
||||
|
||||
``blk_ksm_destroy`` should be called to free up all resources used by a keyslot
|
||||
manager upon ``blk_ksm_init``, once the ``blk_keyslot_manager`` is no longer
|
||||
needed.
|
||||
|
||||
If the driver used ``blk_ksm_init`` instead of ``devm_blk_ksm_init``, then
|
||||
``blk_ksm_destroy`` should be called to free up all resources used by a
|
||||
``blk_keyslot_manager`` once it is no longer needed.
|
||||
|
||||
Layered Devices
|
||||
===============
|
||||
|
@ -26,6 +26,8 @@ properties:
|
||||
- const: allwinner,sun9i-a80-mmc
|
||||
- const: allwinner,sun50i-a64-emmc
|
||||
- const: allwinner,sun50i-a64-mmc
|
||||
- const: allwinner,sun50i-a100-emmc
|
||||
- const: allwinner,sun50i-a100-mmc
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-mmc
|
||||
- const: allwinner,sun7i-a20-mmc
|
||||
@ -47,6 +49,12 @@ properties:
|
||||
- items:
|
||||
- const: allwinner,sun50i-h6-mmc
|
||||
- const: allwinner,sun50i-a64-mmc
|
||||
- items:
|
||||
- const: allwinner,sun50i-h616-emmc
|
||||
- const: allwinner,sun50i-a100-emmc
|
||||
- items:
|
||||
- const: allwinner,sun50i-h616-mmc
|
||||
- const: allwinner,sun50i-a100-mmc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
223
Documentation/devicetree/bindings/mmc/arm,pl18x.yaml
Normal file
223
Documentation/devicetree/bindings/mmc/arm,pl18x.yaml
Normal file
@ -0,0 +1,223 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/arm,pl18x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM PrimeCell MultiMedia Card Interface (MMCI) PL180 and PL181
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
description:
|
||||
The ARM PrimeCells MMCI PL180 and PL181 provides an interface for
|
||||
reading and writing to MultiMedia and SD cards alike. Over the years
|
||||
vendors have use the VHDL code from ARM to create derivative MMC/SD/SDIO
|
||||
host controllers with very similar characteristics.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/arm/primecell.yaml#
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
# We need a select here so we don't match all nodes with 'arm,primecell'
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- arm,pl180
|
||||
- arm,pl181
|
||||
- arm,pl18x
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- description: The first version of the block, simply called
|
||||
PL180 and found in the ARM Integrator IM/PD1 logic module.
|
||||
items:
|
||||
- const: arm,pl180
|
||||
- const: arm,primecell
|
||||
- description: The improved version of the block, found in the
|
||||
ARM Versatile and later reference designs. Further revisions
|
||||
exist but get detected at runtime by reading some magic numbers
|
||||
in the PrimeCell ID registers.
|
||||
items:
|
||||
- const: arm,pl181
|
||||
- const: arm,primecell
|
||||
- description: Wildcard entry that will let the operating system
|
||||
inspect the PrimeCell ID registers to determine which hardware
|
||||
variant of PL180 or PL181 this is.
|
||||
items:
|
||||
- const: arm,pl18x
|
||||
- const: arm,primecell
|
||||
|
||||
clocks:
|
||||
description: One or two clocks, the "apb_pclk" and the "MCLK"
|
||||
which is the core block clock. The names are not compulsory.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
power-domains: true
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
description: the MMIO memory window must be exactly 4KB (0x1000) and the
|
||||
layout should provide the PrimeCell ID registers so that the device can
|
||||
be discovered. On ST Micro variants, a second register window may be
|
||||
defined if a delay block is present and used for tuning.
|
||||
|
||||
interrupts:
|
||||
description: The first interrupt is the command interrupt and corresponds
|
||||
to the event at the end of a command. The second interrupt is the
|
||||
PIO (polled I/O) interrupt and occurs when the FIFO needs to be
|
||||
emptied as part of a bulk read from the card. Some variants have these
|
||||
two interrupts wired into the same line (logic OR) and in that case
|
||||
only one interrupt may be provided.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
st,sig-dir-dat0:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[0].
|
||||
|
||||
st,sig-dir-dat2:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[2].
|
||||
|
||||
st,sig-dir-dat31:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[3] and DAT[1].
|
||||
|
||||
st,sig-dir-dat74:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[7] and DAT[4].
|
||||
|
||||
st,sig-dir-cmd:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, CMD signal direction used for
|
||||
pin CMD.
|
||||
|
||||
st,sig-pin-fbclk:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, feedback clock FBCLK signal pin
|
||||
in use.
|
||||
|
||||
st,sig-dir:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, signal direction polarity used for
|
||||
pins CMD, DAT[0], DAT[1], DAT[2] and DAT[3].
|
||||
|
||||
st,neg-edge:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, data and command phase relation,
|
||||
generated on the sd clock falling edge.
|
||||
|
||||
st,use-ckin:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, use CKIN pin from an external
|
||||
driver to sample the receive data (for example with a voltage switch
|
||||
transceiver).
|
||||
|
||||
st,cmd-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
The GPIO matching the CMD pin.
|
||||
|
||||
st,ck-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
The GPIO matching the CK pin.
|
||||
|
||||
st,ckin-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
The GPIO matching the CKIN pin.
|
||||
|
||||
dependencies:
|
||||
st,cmd-gpios: [ "st,use-ckin" ]
|
||||
st,ck-gpios: [ "st,use-ckin" ]
|
||||
st,ckin-gpios: [ "st,use-ckin" ]
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
mmc@5000 {
|
||||
compatible = "arm,pl180", "arm,primecell";
|
||||
reg = <0x5000 0x1000>;
|
||||
interrupts-extended = <&vic 22 &sic 1>;
|
||||
clocks = <&xtal24mhz>, <&pclk>;
|
||||
clock-names = "mclk", "apb_pclk";
|
||||
};
|
||||
|
||||
mmc@80126000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
reg = <0x80126000 0x1000>;
|
||||
interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&dma 29 0 0x2>, <&dma 29 0 0x0>;
|
||||
dma-names = "rx", "tx";
|
||||
clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>;
|
||||
clock-names = "sdi", "apb_pclk";
|
||||
max-frequency = <100000000>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
cap-mmc-highspeed;
|
||||
cd-gpios = <&gpio2 31 0x4>;
|
||||
st,sig-dir-dat0;
|
||||
st,sig-dir-dat2;
|
||||
st,sig-dir-cmd;
|
||||
st,sig-pin-fbclk;
|
||||
vmmc-supply = <&ab8500_ldo_aux3_reg>;
|
||||
vqmmc-supply = <&vmmci>;
|
||||
};
|
||||
|
||||
mmc@101f6000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
reg = <0x101f6000 0x1000>;
|
||||
clocks = <&sdiclk>, <&pclksdi>;
|
||||
clock-names = "mclk", "apb_pclk";
|
||||
interrupt-parent = <&vica>;
|
||||
interrupts = <22>;
|
||||
max-frequency = <400000>;
|
||||
bus-width = <4>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
full-pwr-cycle;
|
||||
st,sig-dir-dat0;
|
||||
st,sig-dir-dat2;
|
||||
st,sig-dir-dat31;
|
||||
st,sig-dir-cmd;
|
||||
st,sig-pin-fbclk;
|
||||
vmmc-supply = <&vmmc_regulator>;
|
||||
};
|
||||
|
||||
mmc@52007000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
arm,primecell-periphid = <0x10153180>;
|
||||
reg = <0x52007000 0x1000>;
|
||||
interrupts = <49>;
|
||||
interrupt-names = "cmd_irq";
|
||||
clocks = <&rcc 0>;
|
||||
clock-names = "apb_pclk";
|
||||
resets = <&rcc 1>;
|
||||
cap-sd-highspeed;
|
||||
cap-mmc-highspeed;
|
||||
max-frequency = <120000000>;
|
||||
};
|
@ -12,6 +12,7 @@ Required Properties:
|
||||
- "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-ap807-sdhci": For controllers on Armada AP807.
|
||||
- "marvell,armada-cp110-sdhci": For controllers on Armada CP110.
|
||||
|
||||
- clocks:
|
||||
|
@ -1,74 +0,0 @@
|
||||
* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1
|
||||
|
||||
The ARM PrimeCell MMCI PL180 and PL181 provides an interface for
|
||||
reading and writing to MultiMedia and SD cards alike.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the mmci driver. Using "st" as
|
||||
the prefix for a property, indicates support by the ST Micro variant.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains "arm,pl18x", "arm,primecell".
|
||||
- vmmc-supply : phandle to the regulator device tree node, mentioned
|
||||
as the VCC/VDD supply in the eMMC/SD specs.
|
||||
|
||||
Optional properties:
|
||||
- arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides
|
||||
the ID provided by the HW
|
||||
- resets : phandle to internal reset line.
|
||||
Should be defined for sdmmc variant.
|
||||
- vqmmc-supply : phandle to the regulator device tree node, mentioned
|
||||
as the VCCQ/VDD_IO supply in the eMMC/SD specs.
|
||||
specific for ux500 variant:
|
||||
- st,sig-dir-dat0 : bus signal direction pin used for DAT[0].
|
||||
- st,sig-dir-dat2 : bus signal direction pin used for DAT[2].
|
||||
- st,sig-dir-dat31 : bus signal direction pin used for DAT[3] and DAT[1].
|
||||
- st,sig-dir-dat74 : bus signal direction pin used for DAT[4] to DAT[7].
|
||||
- st,sig-dir-cmd : cmd signal direction pin used for CMD.
|
||||
- st,sig-pin-fbclk : feedback clock signal pin used.
|
||||
|
||||
specific for sdmmc variant:
|
||||
- reg : a second base register may be defined if a delay
|
||||
block is present and used for tuning.
|
||||
- st,sig-dir : signal direction polarity used for cmd, dat0 dat123.
|
||||
- st,neg-edge : data & command phase relation, generated on
|
||||
sd clock falling edge.
|
||||
- st,use-ckin : use ckin pin from an external driver to sample
|
||||
the receive data (example: with voltage
|
||||
switch transceiver).
|
||||
|
||||
Deprecated properties:
|
||||
- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable.
|
||||
- mmc-cap-sd-highspeed : indicates whether SD is high speed capable.
|
||||
|
||||
Example:
|
||||
|
||||
sdi0_per1@80126000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
reg = <0x80126000 0x1000>;
|
||||
interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
dmas = <&dma 29 0 0x2>, /* Logical - DevToMem */
|
||||
<&dma 29 0 0x0>; /* Logical - MemToDev */
|
||||
dma-names = "rx", "tx";
|
||||
|
||||
clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>;
|
||||
clock-names = "sdi", "apb_pclk";
|
||||
|
||||
max-frequency = <100000000>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
cap-mmc-highspeed;
|
||||
cd-gpios = <&gpio2 31 0x4>; // 95
|
||||
st,sig-dir-dat0;
|
||||
st,sig-dir-dat2;
|
||||
st,sig-dir-cmd;
|
||||
st,sig-pin-fbclk;
|
||||
|
||||
vmmc-supply = <&ab8500_ldo_aux3_reg>;
|
||||
vqmmc-supply = <&vmmci>;
|
||||
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&sdi0_default_mode>;
|
||||
pinctrl-1 = <&sdi0_sleep_mode>;
|
||||
};
|
@ -59,6 +59,7 @@ properties:
|
||||
- renesas,sdhi-r8a77980 # R-Car V3H
|
||||
- renesas,sdhi-r8a77990 # R-Car E3
|
||||
- renesas,sdhi-r8a77995 # R-Car D3
|
||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
|
||||
|
||||
reg:
|
||||
|
@ -15,12 +15,19 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,am654-sdhci-5.1
|
||||
- ti,j721e-sdhci-8bit
|
||||
- ti,j721e-sdhci-4bit
|
||||
- ti,j7200-sdhci-8bit
|
||||
- ti,j721e-sdhci-4bit
|
||||
oneOf:
|
||||
- const: ti,am654-sdhci-5.1
|
||||
- const: ti,j721e-sdhci-8bit
|
||||
- const: ti,j721e-sdhci-4bit
|
||||
- const: ti,j721e-sdhci-4bit
|
||||
- const: ti,am64-sdhci-8bit
|
||||
- const: ti,am64-sdhci-4bit
|
||||
- items:
|
||||
- const: ti,j7200-sdhci-8bit
|
||||
- const: ti,j721e-sdhci-8bit
|
||||
- items:
|
||||
- const: ti,j7200-sdhci-4bit
|
||||
- const: ti,j721e-sdhci-4bit
|
||||
|
||||
reg:
|
||||
maxItems: 2
|
||||
|
@ -17,10 +17,11 @@ Required properties:
|
||||
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
|
||||
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sdx55-sdhci", "qcom,sdhci-msm-v5";
|
||||
"qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
|
||||
NOTE that some old device tree files may be floating around that only
|
||||
have the string "qcom,sdhci-msm-v4" without the SoC compatible string
|
||||
but doing that should be considered a deprecated practice.
|
||||
@ -30,10 +31,12 @@ Required properties:
|
||||
- SD Core register map (required for controllers earlier than msm-v5)
|
||||
- CQE register map (Optional, CQE support is present on SDHC instance meant
|
||||
for eMMC and version v4.2 and above)
|
||||
- Inline Crypto Engine register map (optional)
|
||||
- reg-names: When CQE register map is supplied, below reg-names are required
|
||||
- "hc" for Host controller register map
|
||||
- "core" for SD core register map
|
||||
- "cqhci" for CQE register map
|
||||
- "ice" for Inline Crypto Engine register map (optional)
|
||||
- interrupts: Should contain an interrupt-specifiers for the interrupts:
|
||||
- Host controller interrupt (required)
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
@ -46,6 +49,7 @@ Required properties:
|
||||
"xo" - TCXO clock (optional)
|
||||
"cal" - reference clock for RCLK delay calibration (optional)
|
||||
"sleep" - sleep clock for RCLK delay calibration (optional)
|
||||
"ice" - clock for Inline Crypto Engine (optional)
|
||||
|
||||
- qcom,ddr-config: Certain chipsets and platforms require particular settings
|
||||
for the DDR_CONFIG register. Use this field to specify the register
|
||||
|
@ -1,18 +0,0 @@
|
||||
* SiRFprimII/marco/atlas6 SDHCI Controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-sirf driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: sirf,prima2-sdhc
|
||||
|
||||
Optional properties:
|
||||
- cd-gpios: card detect gpio, with zero flags.
|
||||
|
||||
Example:
|
||||
|
||||
sd0: sdhci@56000000 {
|
||||
compatible = "sirf,prima2-sdhc";
|
||||
reg = <0xcd000000 0x100000>;
|
||||
cd-gpios = <&gpio 6 0>;
|
||||
};
|
@ -1,31 +0,0 @@
|
||||
* ZTE specific extensions to the Synopsys Designware Mobile Storage
|
||||
Host Controller
|
||||
|
||||
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 ZTE specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "zte,zx296718-dw-mshc": for ZX SoCs
|
||||
|
||||
Example:
|
||||
|
||||
mmc1: mmc@1110000 {
|
||||
compatible = "zte,zx296718-dw-mshc";
|
||||
reg = <0x01110000 0x1000>;
|
||||
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
|
||||
fifo-depth = <32>;
|
||||
data-addr = <0x200>;
|
||||
fifo-watermark-aligned;
|
||||
bus-width = <4>;
|
||||
clock-frequency = <50000000>;
|
||||
clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>;
|
||||
clock-names = "biu", "ciu";
|
||||
max-frequency = <50000000>;
|
||||
cap-sdio-irq;
|
||||
cap-sd-highspeed;
|
||||
};
|
@ -2775,6 +2775,15 @@ F: Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2xxx-scu-ic.
|
||||
F: drivers/irqchip/irq-aspeed-scu-ic.c
|
||||
F: include/dt-bindings/interrupt-controller/aspeed-scu-ic.h
|
||||
|
||||
ASPEED SD/MMC DRIVER
|
||||
M: Andrew Jeffery <andrew@aj.id.au>
|
||||
L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
|
||||
L: openbmc@lists.ozlabs.org (moderated for non-subscribers)
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
|
||||
F: drivers/mmc/host/sdhci-of-aspeed*
|
||||
|
||||
ASPEED VIDEO ENGINE DRIVER
|
||||
M: Eddie James <eajames@linux.ibm.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -29,6 +29,7 @@
|
||||
#define pr_fmt(fmt) "blk-crypto: " fmt
|
||||
|
||||
#include <linux/keyslot-manager.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -127,6 +128,34 @@ err_destroy_ksm:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_ksm_init);
|
||||
|
||||
static void blk_ksm_destroy_callback(void *ksm)
|
||||
{
|
||||
blk_ksm_destroy(ksm);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_blk_ksm_init() - Resource-managed blk_ksm_init()
|
||||
* @dev: The device which owns the blk_keyslot_manager.
|
||||
* @ksm: The blk_keyslot_manager to initialize.
|
||||
* @num_slots: The number of key slots to manage.
|
||||
*
|
||||
* Like blk_ksm_init(), but causes blk_ksm_destroy() to be called automatically
|
||||
* on driver detach.
|
||||
*
|
||||
* Return: 0 on success, or else a negative error code.
|
||||
*/
|
||||
int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm,
|
||||
unsigned int num_slots)
|
||||
{
|
||||
int err = blk_ksm_init(ksm, num_slots);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return devm_add_action_or_reset(dev, blk_ksm_destroy_callback, ksm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_blk_ksm_init);
|
||||
|
||||
static inline struct hlist_head *
|
||||
blk_ksm_hash_bucket_for_key(struct blk_keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key)
|
||||
|
@ -965,8 +965,11 @@ EXPORT_SYMBOL(qcom_scm_ice_available);
|
||||
* qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
|
||||
* @index: the keyslot to invalidate
|
||||
*
|
||||
* The UFSHCI standard defines a standard way to do this, but it doesn't work on
|
||||
* these SoCs; only this SCM call does.
|
||||
* The UFSHCI and eMMC standards define a standard way to do this, but it
|
||||
* doesn't work on these SoCs; only this SCM call does.
|
||||
*
|
||||
* It is assumed that the SoC has only one ICE instance being used, as this SCM
|
||||
* call doesn't specify which ICE instance the keyslot belongs to.
|
||||
*
|
||||
* Return: 0 on success; -errno on failure.
|
||||
*/
|
||||
@ -995,10 +998,13 @@ EXPORT_SYMBOL(qcom_scm_ice_invalidate_key);
|
||||
* units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc.
|
||||
*
|
||||
* Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it
|
||||
* can then be used to encrypt/decrypt UFS I/O requests inline.
|
||||
* can then be used to encrypt/decrypt UFS or eMMC I/O requests inline.
|
||||
*
|
||||
* The UFSHCI standard defines a standard way to do this, but it doesn't work on
|
||||
* these SoCs; only this SCM call does.
|
||||
* The UFSHCI and eMMC standards define a standard way to do this, but it
|
||||
* doesn't work on these SoCs; only this SCM call does.
|
||||
*
|
||||
* It is assumed that the SoC has only one ICE instance being used, as this SCM
|
||||
* call doesn't specify which ICE instance the keyslot belongs to.
|
||||
*
|
||||
* Return: 0 on success; -errno on failure.
|
||||
*/
|
||||
|
@ -81,3 +81,11 @@ config MMC_TEST
|
||||
This driver is only of interest to those developing or
|
||||
testing a host driver. Most people should say N here.
|
||||
|
||||
config MMC_CRYPTO
|
||||
bool "MMC Crypto Engine Support"
|
||||
depends on BLK_INLINE_ENCRYPTION
|
||||
help
|
||||
Enable Crypto Engine Support in MMC.
|
||||
Enabling this makes it possible for the kernel to use the crypto
|
||||
capabilities of the MMC device (if present) to perform crypto
|
||||
operations on data being transferred to/from the device.
|
||||
|
@ -18,3 +18,4 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
|
||||
mmc_block-objs := block.o queue.o
|
||||
obj-$(CONFIG_MMC_TEST) += mmc_test.o
|
||||
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
|
||||
mmc_core-$(CONFIG_MMC_CRYPTO) += crypto.o
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "block.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "crypto.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
#include "mmc_ops.h"
|
||||
@ -1247,6 +1248,8 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
|
||||
memset(brq, 0, sizeof(struct mmc_blk_request));
|
||||
|
||||
mmc_crypto_prepare_req(mqrq);
|
||||
|
||||
brq->mrq.data = &brq->data;
|
||||
brq->mrq.tag = req->tag;
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "crypto.h"
|
||||
#include "bus.h"
|
||||
#include "host.h"
|
||||
#include "sdio_bus.h"
|
||||
@ -547,10 +548,10 @@ int mmc_cqe_recovery(struct mmc_host *host)
|
||||
host->cqe_ops->cqe_recovery_start(host);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.opcode = MMC_STOP_TRANSMISSION,
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC,
|
||||
cmd.opcode = MMC_STOP_TRANSMISSION;
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
|
||||
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT,
|
||||
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
|
||||
mmc_wait_for_cmd(host, &cmd, 0);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
@ -558,7 +559,7 @@ int mmc_cqe_recovery(struct mmc_host *host)
|
||||
cmd.arg = 1; /* Discard entire queue */
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
|
||||
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT,
|
||||
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
|
||||
host->cqe_ops->cqe_recovery_finish(host);
|
||||
@ -992,6 +993,8 @@ void mmc_set_initial_state(struct mmc_host *host)
|
||||
host->ops->hs400_enhanced_strobe(host, &host->ios);
|
||||
|
||||
mmc_set_ios(host);
|
||||
|
||||
mmc_crypto_set_initial_state(host);
|
||||
}
|
||||
|
||||
/**
|
||||
|
48
drivers/mmc/core/crypto.c
Normal file
48
drivers/mmc/core/crypto.c
Normal file
@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* MMC crypto engine (inline encryption) support
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/blk-crypto.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "crypto.h"
|
||||
#include "queue.h"
|
||||
|
||||
void mmc_crypto_set_initial_state(struct mmc_host *host)
|
||||
{
|
||||
/* Reset might clear all keys, so reprogram all the keys. */
|
||||
if (host->caps2 & MMC_CAP2_CRYPTO)
|
||||
blk_ksm_reprogram_all_keys(&host->ksm);
|
||||
}
|
||||
|
||||
void mmc_crypto_setup_queue(struct request_queue *q, struct mmc_host *host)
|
||||
{
|
||||
if (host->caps2 & MMC_CAP2_CRYPTO)
|
||||
blk_ksm_register(&host->ksm, q);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_crypto_setup_queue);
|
||||
|
||||
void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq)
|
||||
{
|
||||
struct request *req = mmc_queue_req_to_req(mqrq);
|
||||
struct mmc_request *mrq = &mqrq->brq.mrq;
|
||||
|
||||
if (!req->crypt_keyslot)
|
||||
return;
|
||||
|
||||
mrq->crypto_enabled = true;
|
||||
mrq->crypto_key_slot = blk_ksm_get_slot_idx(req->crypt_keyslot);
|
||||
|
||||
/*
|
||||
* For now we assume that all MMC drivers set max_dun_bytes_supported=4,
|
||||
* which is the limit for CQHCI crypto. So all DUNs should be 32-bit.
|
||||
*/
|
||||
WARN_ON_ONCE(req->crypt_ctx->bc_dun[0] > U32_MAX);
|
||||
|
||||
mrq->data_unit_num = req->crypt_ctx->bc_dun[0];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_crypto_prepare_req);
|
40
drivers/mmc/core/crypto.h
Normal file
40
drivers/mmc/core/crypto.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* MMC crypto engine (inline encryption) support
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
*/
|
||||
|
||||
#ifndef _MMC_CORE_CRYPTO_H
|
||||
#define _MMC_CORE_CRYPTO_H
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_queue_req;
|
||||
struct request_queue;
|
||||
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
|
||||
void mmc_crypto_set_initial_state(struct mmc_host *host);
|
||||
|
||||
void mmc_crypto_setup_queue(struct request_queue *q, struct mmc_host *host);
|
||||
|
||||
void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq);
|
||||
|
||||
#else /* CONFIG_MMC_CRYPTO */
|
||||
|
||||
static inline void mmc_crypto_set_initial_state(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mmc_crypto_setup_queue(struct request_queue *q,
|
||||
struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_MMC_CRYPTO */
|
||||
|
||||
#endif /* _MMC_CORE_CRYPTO_H */
|
@ -25,6 +25,7 @@
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "crypto.h"
|
||||
#include "host.h"
|
||||
#include "slot-gpio.h"
|
||||
#include "pwrseq.h"
|
||||
@ -163,6 +164,50 @@ static void mmc_retune_timer(struct timer_list *t)
|
||||
mmc_retune_needed(host);
|
||||
}
|
||||
|
||||
static void mmc_of_parse_timing_phase(struct device *dev, const char *prop,
|
||||
struct mmc_clk_phase *phase)
|
||||
{
|
||||
int degrees[2] = {0};
|
||||
int rc;
|
||||
|
||||
rc = device_property_read_u32_array(dev, prop, degrees, 2);
|
||||
phase->valid = !rc;
|
||||
if (phase->valid) {
|
||||
phase->in_deg = degrees[0];
|
||||
phase->out_deg = degrees[1];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map)
|
||||
{
|
||||
struct device *dev = host->parent;
|
||||
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-legacy",
|
||||
&map->phase[MMC_TIMING_LEGACY]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs",
|
||||
&map->phase[MMC_TIMING_MMC_HS]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-sd-hs",
|
||||
&map->phase[MMC_TIMING_SD_HS]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr12",
|
||||
&map->phase[MMC_TIMING_UHS_SDR12]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr25",
|
||||
&map->phase[MMC_TIMING_UHS_SDR25]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr50",
|
||||
&map->phase[MMC_TIMING_UHS_SDR50]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr104",
|
||||
&map->phase[MMC_TIMING_UHS_SDR104]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-uhs-ddr50",
|
||||
&map->phase[MMC_TIMING_UHS_DDR50]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-mmc-ddr52",
|
||||
&map->phase[MMC_TIMING_MMC_DDR52]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs200",
|
||||
&map->phase[MMC_TIMING_MMC_HS200]);
|
||||
mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs400",
|
||||
&map->phase[MMC_TIMING_MMC_HS400]);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_of_parse_clk_phase);
|
||||
|
||||
/**
|
||||
* mmc_of_parse() - parse host's device-tree node
|
||||
* @host: host whose node should be parsed.
|
||||
|
@ -1697,7 +1697,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
goto free_card;
|
||||
|
||||
if (err) {
|
||||
err = 0;
|
||||
/*
|
||||
* Just disable enhanced area off & sz
|
||||
* will try to enable ERASE_GROUP_DEF
|
||||
@ -1802,7 +1801,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
pr_warn("%s: Enabling HPI failed\n",
|
||||
mmc_hostname(card->host));
|
||||
card->ext_csd.hpi_en = 0;
|
||||
err = 0;
|
||||
} else {
|
||||
card->ext_csd.hpi_en = 1;
|
||||
}
|
||||
@ -1831,7 +1829,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",
|
||||
mmc_hostname(card->host), err);
|
||||
card->ext_csd.cache_ctrl = 0;
|
||||
err = 0;
|
||||
} else {
|
||||
card->ext_csd.cache_ctrl = 1;
|
||||
}
|
||||
@ -1851,7 +1848,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
mmc_hostname(card->host));
|
||||
card->ext_csd.cmdq_support = false;
|
||||
card->ext_csd.cmdq_depth = 0;
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
@ -1899,7 +1895,7 @@ err:
|
||||
|
||||
static int mmc_can_sleep(struct mmc_card *card)
|
||||
{
|
||||
return (card && card->ext_csd.rev >= 3);
|
||||
return card->ext_csd.rev >= 3;
|
||||
}
|
||||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
|
@ -296,7 +296,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_spi_send_csd(struct mmc_card *card, u32 *csd)
|
||||
static int mmc_spi_send_csd(struct mmc_host *host, u32 *csd)
|
||||
{
|
||||
int ret, i;
|
||||
__be32 *csd_tmp;
|
||||
@ -305,7 +305,7 @@ static int mmc_spi_send_csd(struct mmc_card *card, u32 *csd)
|
||||
if (!csd_tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd_tmp, 16);
|
||||
ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CSD, csd_tmp, 16);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -320,7 +320,7 @@ err:
|
||||
int mmc_send_csd(struct mmc_card *card, u32 *csd)
|
||||
{
|
||||
if (mmc_host_is_spi(card->host))
|
||||
return mmc_spi_send_csd(card, csd);
|
||||
return mmc_spi_send_csd(card->host, csd);
|
||||
|
||||
return mmc_send_cxd_native(card->host, card->rca << 16, csd,
|
||||
MMC_SEND_CSD);
|
||||
|
@ -624,7 +624,7 @@ static unsigned int mmc_test_capacity(struct mmc_card *card)
|
||||
* Fill the first couple of sectors of the card with known data
|
||||
* so that bad reads/writes can be detected
|
||||
*/
|
||||
static int __mmc_test_prepare(struct mmc_test_card *test, int write)
|
||||
static int __mmc_test_prepare(struct mmc_test_card *test, int write, int val)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
@ -633,7 +633,7 @@ static int __mmc_test_prepare(struct mmc_test_card *test, int write)
|
||||
return ret;
|
||||
|
||||
if (write)
|
||||
memset(test->buffer, 0xDF, 512);
|
||||
memset(test->buffer, val, 512);
|
||||
else {
|
||||
for (i = 0; i < 512; i++)
|
||||
test->buffer[i] = i;
|
||||
@ -650,31 +650,17 @@ static int __mmc_test_prepare(struct mmc_test_card *test, int write)
|
||||
|
||||
static int mmc_test_prepare_write(struct mmc_test_card *test)
|
||||
{
|
||||
return __mmc_test_prepare(test, 1);
|
||||
return __mmc_test_prepare(test, 1, 0xDF);
|
||||
}
|
||||
|
||||
static int mmc_test_prepare_read(struct mmc_test_card *test)
|
||||
{
|
||||
return __mmc_test_prepare(test, 0);
|
||||
return __mmc_test_prepare(test, 0, 0);
|
||||
}
|
||||
|
||||
static int mmc_test_cleanup(struct mmc_test_card *test)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
ret = mmc_test_set_blksize(test, 512);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset(test->buffer, 0, 512);
|
||||
|
||||
for (i = 0; i < BUFFER_SIZE / 512; i++) {
|
||||
ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return __mmc_test_prepare(test, 1, 0);
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
@ -2124,7 +2110,7 @@ static int mmc_test_rw_multiple(struct mmc_test_card *test,
|
||||
if (mmc_can_erase(test->card) &&
|
||||
tdata->prepare & MMC_TEST_PREP_ERASE) {
|
||||
ret = mmc_erase(test->card, dev_addr,
|
||||
size / 512, MMC_SECURE_ERASE_ARG);
|
||||
size / 512, test->card->erase_arg);
|
||||
if (ret)
|
||||
ret = mmc_erase(test->card, dev_addr,
|
||||
size / 512, MMC_ERASE_ARG);
|
||||
@ -3267,17 +3253,12 @@ static void mmc_test_remove(struct mmc_card *card)
|
||||
mmc_test_free_dbgfs_file(card);
|
||||
}
|
||||
|
||||
static void mmc_test_shutdown(struct mmc_card *card)
|
||||
{
|
||||
}
|
||||
|
||||
static struct mmc_driver mmc_driver = {
|
||||
.drv = {
|
||||
.name = "mmc_test",
|
||||
},
|
||||
.probe = mmc_test_probe,
|
||||
.remove = mmc_test_remove,
|
||||
.shutdown = mmc_test_shutdown,
|
||||
};
|
||||
|
||||
static int __init mmc_test_init(void)
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/backing-dev.h>
|
||||
@ -19,6 +18,7 @@
|
||||
#include "block.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "crypto.h"
|
||||
#include "host.h"
|
||||
|
||||
#define MMC_DMA_MAP_MERGE_SEGMENTS 512
|
||||
@ -33,8 +33,6 @@ void mmc_cqe_check_busy(struct mmc_queue *mq)
|
||||
{
|
||||
if ((mq->cqe_busy & MMC_CQE_DCMD_BUSY) && !mmc_cqe_dcmd_busy(mq))
|
||||
mq->cqe_busy &= ~MMC_CQE_DCMD_BUSY;
|
||||
|
||||
mq->cqe_busy &= ~MMC_CQE_QUEUE_FULL;
|
||||
}
|
||||
|
||||
static inline bool mmc_cqe_can_dcmd(struct mmc_host *host)
|
||||
@ -407,6 +405,8 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
|
||||
mutex_init(&mq->complete_lock);
|
||||
|
||||
init_waitqueue_head(&mq->wait);
|
||||
|
||||
mmc_crypto_setup_queue(mq->queue, host);
|
||||
}
|
||||
|
||||
static inline bool mmc_merge_capable(struct mmc_host *host)
|
||||
|
@ -81,7 +81,6 @@ struct mmc_queue {
|
||||
int in_flight[MMC_ISSUE_MAX];
|
||||
unsigned int cqe_busy;
|
||||
#define MMC_CQE_DCMD_BUSY BIT(0)
|
||||
#define MMC_CQE_QUEUE_FULL BIT(1)
|
||||
bool busy;
|
||||
bool use_cqe;
|
||||
bool recovery_needed;
|
||||
|
@ -860,7 +860,7 @@ try_again:
|
||||
return err;
|
||||
}
|
||||
|
||||
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
|
||||
int mmc_sd_get_csd(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -1046,7 +1046,7 @@ retry:
|
||||
}
|
||||
|
||||
if (!oldcard) {
|
||||
err = mmc_sd_get_csd(host, card);
|
||||
err = mmc_sd_get_csd(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
|
@ -10,7 +10,7 @@ struct mmc_host;
|
||||
struct mmc_card;
|
||||
|
||||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
|
||||
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
|
||||
int mmc_sd_get_csd(struct mmc_card *card);
|
||||
void mmc_decode_cid(struct mmc_card *card);
|
||||
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
||||
bool reinit);
|
||||
|
@ -751,7 +751,7 @@ try_again:
|
||||
* Read CSD, before selecting the card
|
||||
*/
|
||||
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
||||
err = mmc_sd_get_csd(host, card);
|
||||
err = mmc_sd_get_csd(card);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
|
@ -168,6 +168,20 @@ config MMC_SDHCI_OF_ASPEED
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_ASPEED_TEST
|
||||
bool "Tests for the ASPEED SDHCI driver"
|
||||
depends on MMC_SDHCI_OF_ASPEED && KUNIT=y
|
||||
help
|
||||
Enable KUnit tests for the ASPEED SDHCI driver. Select this
|
||||
option only if you will boot the kernel for the purpose of running
|
||||
unit tests (e.g. under UML or qemu).
|
||||
|
||||
The KUnit tests generally exercise parts of the driver that do not
|
||||
directly touch the hardware, for example, the phase correction
|
||||
calculations.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_AT91
|
||||
tristate "SDHCI OF support for the Atmel SDMMC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
@ -312,18 +326,6 @@ config MMC_SDHCI_S3C
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_SIRF
|
||||
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
|
||||
depends on ARCH_SIRF || COMPILE_TEST
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the SDHCI support for SiRF System-on-Chip devices.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_PXAV3
|
||||
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
|
||||
depends on CLKDEV_LOOKUP
|
||||
@ -544,6 +546,7 @@ config MMC_SDHCI_MSM
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
select MMC_CQHCI
|
||||
select QCOM_SCM if MMC_CRYPTO && ARCH_QCOM
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
support present in Qualcomm SOCs. The controller supports
|
||||
@ -608,13 +611,6 @@ config MMC_DAVINCI
|
||||
If you have an DAVINCI board with a Multimedia Card slot,
|
||||
say Y or M here. If unsure, say N.
|
||||
|
||||
config MMC_GOLDFISH
|
||||
tristate "goldfish qemu Multimedia Card Interface support"
|
||||
depends on GOLDFISH || COMPILE_TEST
|
||||
help
|
||||
This selects the Goldfish Multimedia card Interface emulation
|
||||
found on the Goldfish Android virtual device emulation.
|
||||
|
||||
config MMC_SPI
|
||||
tristate "MMC/SD/SDIO over SPI"
|
||||
depends on SPI_MASTER
|
||||
@ -868,15 +864,6 @@ config MMC_DW_ROCKCHIP
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on RK3066, RK3188 and RK3288 SoC's.
|
||||
|
||||
config MMC_DW_ZX
|
||||
tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
|
||||
depends on MMC_DW && ARCH_ZX
|
||||
select MMC_DW_PLTFM
|
||||
help
|
||||
This selects support for ZTE SoC specific extensions to the
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on ZX296718 SoC's.
|
||||
|
||||
config MMC_SH_MMCIF
|
||||
tristate "SuperH Internal MMCIF support"
|
||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||
|
@ -19,7 +19,6 @@ obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
|
||||
obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
|
||||
obj-$(CONFIG_MMC_SDHCI_MILBEAUT) += sdhci-milbeaut.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
||||
@ -34,7 +33,6 @@ obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
|
||||
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
|
||||
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
|
||||
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
|
||||
obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o
|
||||
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
|
||||
ifeq ($(CONFIG_OF),y)
|
||||
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
|
||||
@ -61,7 +59,6 @@ obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
|
||||
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
|
||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
|
||||
obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
obj-$(CONFIG_MMC_VUB300) += vub300.o
|
||||
@ -104,6 +101,8 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o
|
||||
obj-$(CONFIG_MMC_CQHCI) += cqhci.o
|
||||
cqhci-y += cqhci-core.o
|
||||
cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o
|
||||
obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
|
@ -1,545 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2007, Google Inc.
|
||||
* Copyright 2012, Intel Inc.
|
||||
*
|
||||
* based on omap.c driver, which was
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
|
||||
* Misc hacks here and there by Tony Lindgren <tony@atomide.com>
|
||||
* Other hacks (DMA, SD, etc) by David Brownell
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/major.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define DRIVER_NAME "goldfish_mmc"
|
||||
|
||||
#define BUFFER_SIZE 16384
|
||||
|
||||
#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr))
|
||||
#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr))
|
||||
|
||||
enum {
|
||||
/* status register */
|
||||
MMC_INT_STATUS = 0x00,
|
||||
/* set this to enable IRQ */
|
||||
MMC_INT_ENABLE = 0x04,
|
||||
/* set this to specify buffer address */
|
||||
MMC_SET_BUFFER = 0x08,
|
||||
|
||||
/* MMC command number */
|
||||
MMC_CMD = 0x0C,
|
||||
|
||||
/* MMC argument */
|
||||
MMC_ARG = 0x10,
|
||||
|
||||
/* MMC response (or R2 bits 0 - 31) */
|
||||
MMC_RESP_0 = 0x14,
|
||||
|
||||
/* MMC R2 response bits 32 - 63 */
|
||||
MMC_RESP_1 = 0x18,
|
||||
|
||||
/* MMC R2 response bits 64 - 95 */
|
||||
MMC_RESP_2 = 0x1C,
|
||||
|
||||
/* MMC R2 response bits 96 - 127 */
|
||||
MMC_RESP_3 = 0x20,
|
||||
|
||||
MMC_BLOCK_LENGTH = 0x24,
|
||||
MMC_BLOCK_COUNT = 0x28,
|
||||
|
||||
/* MMC state flags */
|
||||
MMC_STATE = 0x2C,
|
||||
|
||||
/* MMC_INT_STATUS bits */
|
||||
|
||||
MMC_STAT_END_OF_CMD = 1U << 0,
|
||||
MMC_STAT_END_OF_DATA = 1U << 1,
|
||||
MMC_STAT_STATE_CHANGE = 1U << 2,
|
||||
MMC_STAT_CMD_TIMEOUT = 1U << 3,
|
||||
|
||||
/* MMC_STATE bits */
|
||||
MMC_STATE_INSERTED = 1U << 0,
|
||||
MMC_STATE_READ_ONLY = 1U << 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Command types
|
||||
*/
|
||||
#define OMAP_MMC_CMDTYPE_BC 0
|
||||
#define OMAP_MMC_CMDTYPE_BCR 1
|
||||
#define OMAP_MMC_CMDTYPE_AC 2
|
||||
#define OMAP_MMC_CMDTYPE_ADTC 3
|
||||
|
||||
|
||||
struct goldfish_mmc_host {
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
struct device *dev;
|
||||
unsigned char id; /* 16xx chips have 2 MMC blocks */
|
||||
void *virt_base;
|
||||
unsigned int phys_base;
|
||||
int irq;
|
||||
unsigned char bus_mode;
|
||||
unsigned char hw_bus_mode;
|
||||
|
||||
unsigned int sg_len;
|
||||
unsigned dma_done:1;
|
||||
unsigned dma_in_use:1;
|
||||
|
||||
void __iomem *reg_base;
|
||||
};
|
||||
|
||||
static inline int
|
||||
goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
goldfish_mmc_show_cover_switch(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct goldfish_mmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" :
|
||||
"closed");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL);
|
||||
|
||||
static void
|
||||
goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
u32 cmdreg;
|
||||
u32 resptype;
|
||||
u32 cmdtype;
|
||||
|
||||
host->cmd = cmd;
|
||||
|
||||
resptype = 0;
|
||||
cmdtype = 0;
|
||||
|
||||
/* Our hardware needs to know exact type */
|
||||
switch (mmc_resp_type(cmd)) {
|
||||
case MMC_RSP_NONE:
|
||||
break;
|
||||
case MMC_RSP_R1:
|
||||
case MMC_RSP_R1B:
|
||||
/* resp 1, 1b, 6, 7 */
|
||||
resptype = 1;
|
||||
break;
|
||||
case MMC_RSP_R2:
|
||||
resptype = 2;
|
||||
break;
|
||||
case MMC_RSP_R3:
|
||||
resptype = 3;
|
||||
break;
|
||||
default:
|
||||
dev_err(mmc_dev(mmc_from_priv(host)),
|
||||
"Invalid response type: %04x\n", mmc_resp_type(cmd));
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
|
||||
cmdtype = OMAP_MMC_CMDTYPE_ADTC;
|
||||
else if (mmc_cmd_type(cmd) == MMC_CMD_BC)
|
||||
cmdtype = OMAP_MMC_CMDTYPE_BC;
|
||||
else if (mmc_cmd_type(cmd) == MMC_CMD_BCR)
|
||||
cmdtype = OMAP_MMC_CMDTYPE_BCR;
|
||||
else
|
||||
cmdtype = OMAP_MMC_CMDTYPE_AC;
|
||||
|
||||
cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
|
||||
|
||||
if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
||||
cmdreg |= 1 << 6;
|
||||
|
||||
if (cmd->flags & MMC_RSP_BUSY)
|
||||
cmdreg |= 1 << 11;
|
||||
|
||||
if (host->data && !(host->data->flags & MMC_DATA_WRITE))
|
||||
cmdreg |= 1 << 15;
|
||||
|
||||
GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg);
|
||||
GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);
|
||||
}
|
||||
|
||||
static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
if (host->dma_in_use) {
|
||||
enum dma_data_direction dma_data_dir;
|
||||
|
||||
dma_data_dir = mmc_get_dma_dir(data);
|
||||
|
||||
if (dma_data_dir == DMA_FROM_DEVICE) {
|
||||
/*
|
||||
* We don't really have DMA, so we need
|
||||
* to copy from our platform driver buffer
|
||||
*/
|
||||
sg_copy_from_buffer(data->sg, 1, host->virt_base,
|
||||
data->sg->length);
|
||||
}
|
||||
host->data->bytes_xfered += data->sg->length;
|
||||
dma_unmap_sg(mmc_dev(mmc_from_priv(host)), data->sg,
|
||||
host->sg_len, dma_data_dir);
|
||||
}
|
||||
|
||||
host->data = NULL;
|
||||
host->sg_len = 0;
|
||||
|
||||
/*
|
||||
* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
|
||||
* dozens of requests until the card finishes writing data.
|
||||
* It'd be cheaper to just wait till an EOFB interrupt arrives...
|
||||
*/
|
||||
|
||||
if (!data->stop) {
|
||||
host->mrq = NULL;
|
||||
mmc_request_done(mmc_from_priv(host), data->mrq);
|
||||
return;
|
||||
}
|
||||
|
||||
goldfish_mmc_start_command(host, data->stop);
|
||||
}
|
||||
|
||||
static void goldfish_mmc_end_of_data(struct goldfish_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
if (!host->dma_in_use) {
|
||||
goldfish_mmc_xfer_done(host, data);
|
||||
return;
|
||||
}
|
||||
if (host->dma_done)
|
||||
goldfish_mmc_xfer_done(host, data);
|
||||
}
|
||||
|
||||
static void goldfish_mmc_cmd_done(struct goldfish_mmc_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
host->cmd = NULL;
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
/* response type 2 */
|
||||
cmd->resp[3] =
|
||||
GOLDFISH_MMC_READ(host, MMC_RESP_0);
|
||||
cmd->resp[2] =
|
||||
GOLDFISH_MMC_READ(host, MMC_RESP_1);
|
||||
cmd->resp[1] =
|
||||
GOLDFISH_MMC_READ(host, MMC_RESP_2);
|
||||
cmd->resp[0] =
|
||||
GOLDFISH_MMC_READ(host, MMC_RESP_3);
|
||||
} else {
|
||||
/* response types 1, 1b, 3, 4, 5, 6 */
|
||||
cmd->resp[0] =
|
||||
GOLDFISH_MMC_READ(host, MMC_RESP_0);
|
||||
}
|
||||
}
|
||||
|
||||
if (host->data == NULL || cmd->error) {
|
||||
host->mrq = NULL;
|
||||
mmc_request_done(mmc_from_priv(host), cmd->mrq);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct goldfish_mmc_host *host = (struct goldfish_mmc_host *)dev_id;
|
||||
u16 status;
|
||||
int end_command = 0;
|
||||
int end_transfer = 0;
|
||||
int state_changed = 0;
|
||||
int cmd_timeout = 0;
|
||||
|
||||
while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
|
||||
GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
|
||||
|
||||
if (status & MMC_STAT_END_OF_CMD)
|
||||
end_command = 1;
|
||||
|
||||
if (status & MMC_STAT_END_OF_DATA)
|
||||
end_transfer = 1;
|
||||
|
||||
if (status & MMC_STAT_STATE_CHANGE)
|
||||
state_changed = 1;
|
||||
|
||||
if (status & MMC_STAT_CMD_TIMEOUT) {
|
||||
end_command = 0;
|
||||
cmd_timeout = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_timeout) {
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
host->mrq = NULL;
|
||||
mmc_request_done(mmc_from_priv(host), mrq);
|
||||
}
|
||||
|
||||
if (end_command)
|
||||
goldfish_mmc_cmd_done(host, host->cmd);
|
||||
|
||||
if (end_transfer) {
|
||||
host->dma_done = 1;
|
||||
goldfish_mmc_end_of_data(host, host->data);
|
||||
} else if (host->data != NULL) {
|
||||
/*
|
||||
* WORKAROUND -- after porting this driver from 2.6 to 3.4,
|
||||
* during device initialization, cases where host->data is
|
||||
* non-null but end_transfer is false would occur. Doing
|
||||
* nothing in such cases results in no further interrupts,
|
||||
* and initialization failure.
|
||||
* TODO -- find the real cause.
|
||||
*/
|
||||
host->dma_done = 1;
|
||||
goldfish_mmc_end_of_data(host, host->data);
|
||||
}
|
||||
|
||||
if (state_changed) {
|
||||
u32 state = GOLDFISH_MMC_READ(host, MMC_STATE);
|
||||
pr_info("%s: Card detect now %d\n", __func__,
|
||||
(state & MMC_STATE_INSERTED));
|
||||
mmc_detect_change(mmc_from_priv(host), 0);
|
||||
}
|
||||
|
||||
if (!end_command && !end_transfer && !state_changed && !cmd_timeout) {
|
||||
status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
|
||||
dev_info(mmc_dev(mmc_from_priv(host)), "spurious irq 0x%04x\n",
|
||||
status);
|
||||
if (status != 0) {
|
||||
GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
|
||||
GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
|
||||
struct mmc_request *req)
|
||||
{
|
||||
struct mmc_data *data = req->data;
|
||||
int block_size;
|
||||
unsigned sg_len;
|
||||
enum dma_data_direction dma_data_dir;
|
||||
|
||||
host->data = data;
|
||||
if (data == NULL) {
|
||||
GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
|
||||
GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
|
||||
host->dma_in_use = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
block_size = data->blksz;
|
||||
|
||||
GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
|
||||
GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
|
||||
|
||||
/*
|
||||
* Cope with calling layer confusion; it issues "single
|
||||
* block" writes using multi-block scatterlists.
|
||||
*/
|
||||
sg_len = (data->blocks == 1) ? 1 : data->sg_len;
|
||||
|
||||
dma_data_dir = mmc_get_dma_dir(data);
|
||||
|
||||
host->sg_len = dma_map_sg(mmc_dev(mmc_from_priv(host)), data->sg,
|
||||
sg_len, dma_data_dir);
|
||||
host->dma_done = 0;
|
||||
host->dma_in_use = 1;
|
||||
|
||||
if (dma_data_dir == DMA_TO_DEVICE) {
|
||||
/*
|
||||
* We don't really have DMA, so we need to copy to our
|
||||
* platform driver buffer
|
||||
*/
|
||||
sg_copy_to_buffer(data->sg, 1, host->virt_base,
|
||||
data->sg->length);
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||
{
|
||||
struct goldfish_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
WARN_ON(host->mrq != NULL);
|
||||
|
||||
host->mrq = req;
|
||||
goldfish_mmc_prepare_data(host, req);
|
||||
goldfish_mmc_start_command(host, req->cmd);
|
||||
}
|
||||
|
||||
static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct goldfish_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
host->bus_mode = ios->bus_mode;
|
||||
host->hw_bus_mode = host->bus_mode;
|
||||
}
|
||||
|
||||
static int goldfish_mmc_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
uint32_t state;
|
||||
struct goldfish_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
state = GOLDFISH_MMC_READ(host, MMC_STATE);
|
||||
return ((state & MMC_STATE_READ_ONLY) != 0);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops goldfish_mmc_ops = {
|
||||
.request = goldfish_mmc_request,
|
||||
.set_ios = goldfish_mmc_set_ios,
|
||||
.get_ro = goldfish_mmc_get_ro,
|
||||
};
|
||||
|
||||
static int goldfish_mmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
struct goldfish_mmc_host *host = NULL;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
int irq;
|
||||
dma_addr_t buf_addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (res == NULL || irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
|
||||
if (mmc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc_host_failed;
|
||||
}
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end);
|
||||
host->reg_base = ioremap(res->start, resource_size(res));
|
||||
if (host->reg_base == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto ioremap_failed;
|
||||
}
|
||||
host->virt_base = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
|
||||
&buf_addr, GFP_KERNEL);
|
||||
|
||||
if (host->virt_base == 0) {
|
||||
ret = -ENOMEM;
|
||||
goto dma_alloc_failed;
|
||||
}
|
||||
host->phys_base = buf_addr;
|
||||
|
||||
host->id = pdev->id;
|
||||
host->irq = irq;
|
||||
|
||||
mmc->ops = &goldfish_mmc_ops;
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 24000000;
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA;
|
||||
mmc->caps2 = MMC_CAP2_NO_SDIO;
|
||||
|
||||
/* Use scatterlist DMA to reduce per-transfer costs.
|
||||
* NOTE max_seg_size assumption that small blocks aren't
|
||||
* normally used (except e.g. for reading SD registers).
|
||||
*/
|
||||
mmc->max_segs = 32;
|
||||
mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
|
||||
mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
|
||||
mmc->max_req_size = BUFFER_SIZE;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed IRQ Adding goldfish MMC\n");
|
||||
goto err_request_irq_failed;
|
||||
}
|
||||
|
||||
host->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
|
||||
if (ret)
|
||||
dev_warn(mmc_dev(mmc), "Unable to create sysfs attributes\n");
|
||||
|
||||
GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
|
||||
GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
|
||||
MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA |
|
||||
MMC_STAT_STATE_CHANGE | MMC_STAT_CMD_TIMEOUT);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
return 0;
|
||||
|
||||
err_request_irq_failed:
|
||||
dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base,
|
||||
host->phys_base);
|
||||
dma_alloc_failed:
|
||||
iounmap(host->reg_base);
|
||||
ioremap_failed:
|
||||
mmc_free_host(mmc);
|
||||
err_alloc_host_failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int goldfish_mmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
|
||||
struct mmc_host *mmc = mmc_from_priv(host);
|
||||
|
||||
BUG_ON(host == NULL);
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
free_irq(host->irq, host);
|
||||
dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
|
||||
iounmap(host->reg_base);
|
||||
mmc_free_host(mmc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver goldfish_mmc_driver = {
|
||||
.probe = goldfish_mmc_probe,
|
||||
.remove = goldfish_mmc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(goldfish_mmc_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1719,9 +1719,9 @@ static void atmci_detect_change(struct timer_list *t)
|
||||
}
|
||||
}
|
||||
|
||||
static void atmci_tasklet_func(unsigned long priv)
|
||||
static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
{
|
||||
struct atmel_mci *host = (struct atmel_mci *)priv;
|
||||
struct atmel_mci *host = from_tasklet(host, t, tasklet);
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mmc_data *data = host->data;
|
||||
enum atmel_mci_state state = host->state;
|
||||
@ -2401,45 +2401,45 @@ static void atmci_get_cap(struct atmel_mci *host)
|
||||
dev_info(&host->pdev->dev,
|
||||
"version: 0x%x\n", version);
|
||||
|
||||
host->caps.has_dma_conf_reg = 0;
|
||||
host->caps.has_pdc = 1;
|
||||
host->caps.has_cfg_reg = 0;
|
||||
host->caps.has_cstor_reg = 0;
|
||||
host->caps.has_highspeed = 0;
|
||||
host->caps.has_rwproof = 0;
|
||||
host->caps.has_odd_clk_div = 0;
|
||||
host->caps.has_bad_data_ordering = 1;
|
||||
host->caps.need_reset_after_xfer = 1;
|
||||
host->caps.need_blksz_mul_4 = 1;
|
||||
host->caps.need_notbusy_for_read_ops = 0;
|
||||
host->caps.has_dma_conf_reg = false;
|
||||
host->caps.has_pdc = true;
|
||||
host->caps.has_cfg_reg = false;
|
||||
host->caps.has_cstor_reg = false;
|
||||
host->caps.has_highspeed = false;
|
||||
host->caps.has_rwproof = false;
|
||||
host->caps.has_odd_clk_div = false;
|
||||
host->caps.has_bad_data_ordering = true;
|
||||
host->caps.need_reset_after_xfer = true;
|
||||
host->caps.need_blksz_mul_4 = true;
|
||||
host->caps.need_notbusy_for_read_ops = false;
|
||||
|
||||
/* keep only major version number */
|
||||
switch (version & 0xf00) {
|
||||
case 0x600:
|
||||
case 0x500:
|
||||
host->caps.has_odd_clk_div = 1;
|
||||
host->caps.has_odd_clk_div = true;
|
||||
fallthrough;
|
||||
case 0x400:
|
||||
case 0x300:
|
||||
host->caps.has_dma_conf_reg = 1;
|
||||
host->caps.has_pdc = 0;
|
||||
host->caps.has_cfg_reg = 1;
|
||||
host->caps.has_cstor_reg = 1;
|
||||
host->caps.has_highspeed = 1;
|
||||
host->caps.has_dma_conf_reg = true;
|
||||
host->caps.has_pdc = false;
|
||||
host->caps.has_cfg_reg = true;
|
||||
host->caps.has_cstor_reg = true;
|
||||
host->caps.has_highspeed = true;
|
||||
fallthrough;
|
||||
case 0x200:
|
||||
host->caps.has_rwproof = 1;
|
||||
host->caps.need_blksz_mul_4 = 0;
|
||||
host->caps.need_notbusy_for_read_ops = 1;
|
||||
host->caps.has_rwproof = true;
|
||||
host->caps.need_blksz_mul_4 = false;
|
||||
host->caps.need_notbusy_for_read_ops = true;
|
||||
fallthrough;
|
||||
case 0x100:
|
||||
host->caps.has_bad_data_ordering = 0;
|
||||
host->caps.need_reset_after_xfer = 0;
|
||||
host->caps.has_bad_data_ordering = false;
|
||||
host->caps.need_reset_after_xfer = false;
|
||||
fallthrough;
|
||||
case 0x0:
|
||||
break;
|
||||
default:
|
||||
host->caps.has_pdc = 0;
|
||||
host->caps.has_pdc = false;
|
||||
dev_warn(&host->pdev->dev,
|
||||
"Unmanaged mci version, set minimum capabilities\n");
|
||||
break;
|
||||
@ -2496,7 +2496,7 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
|
||||
host->mapbase = regs->start;
|
||||
|
||||
tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
|
||||
tasklet_setup(&host->tasklet, atmci_tasklet_func);
|
||||
|
||||
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
|
||||
if (ret) {
|
||||
|
@ -253,9 +253,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
static void au1xmmc_tasklet_finish(unsigned long param)
|
||||
static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
|
||||
{
|
||||
struct au1xmmc_host *host = (struct au1xmmc_host *) param;
|
||||
struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
|
||||
au1xmmc_finish_request(host);
|
||||
}
|
||||
|
||||
@ -363,9 +363,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
|
||||
au1xmmc_finish_request(host);
|
||||
}
|
||||
|
||||
static void au1xmmc_tasklet_data(unsigned long param)
|
||||
static void au1xmmc_tasklet_data(struct tasklet_struct *t)
|
||||
{
|
||||
struct au1xmmc_host *host = (struct au1xmmc_host *)param;
|
||||
struct au1xmmc_host *host = from_tasklet(host, t, data_task);
|
||||
|
||||
u32 status = __raw_readl(HOST_STATUS(host));
|
||||
au1xmmc_data_complete(host, status);
|
||||
@ -1037,11 +1037,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
|
||||
if (host->platdata)
|
||||
mmc->caps &= ~(host->platdata->mask_host_caps);
|
||||
|
||||
tasklet_init(&host->data_task, au1xmmc_tasklet_data,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
|
||||
|
||||
tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
|
||||
|
||||
if (has_dbdma()) {
|
||||
ret = au1xmmc_dbdma_init(host);
|
||||
|
@ -436,12 +436,11 @@ irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct cvm_mmc_host *host = dev_id;
|
||||
struct mmc_request *req;
|
||||
unsigned long flags = 0;
|
||||
u64 emm_int, rsp_sts;
|
||||
bool host_done;
|
||||
|
||||
if (host->need_irq_handler_lock)
|
||||
spin_lock_irqsave(&host->irq_handler_lock, flags);
|
||||
spin_lock(&host->irq_handler_lock);
|
||||
else
|
||||
__acquire(&host->irq_handler_lock);
|
||||
|
||||
@ -504,7 +503,7 @@ no_req_done:
|
||||
host->release_bus(host);
|
||||
out:
|
||||
if (host->need_irq_handler_lock)
|
||||
spin_unlock_irqrestore(&host->irq_handler_lock, flags);
|
||||
spin_unlock(&host->irq_handler_lock);
|
||||
else
|
||||
__release(&host->irq_handler_lock);
|
||||
return IRQ_RETVAL(emm_int != 0);
|
||||
|
@ -646,14 +646,14 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cb710_mmc_finish_request_tasklet(unsigned long data)
|
||||
static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
|
||||
{
|
||||
struct mmc_host *mmc = (void *)data;
|
||||
struct cb710_mmc_reader *reader = mmc_priv(mmc);
|
||||
struct cb710_mmc_reader *reader = from_tasklet(reader, t,
|
||||
finish_req_tasklet);
|
||||
struct mmc_request *mrq = reader->mrq;
|
||||
|
||||
reader->mrq = NULL;
|
||||
mmc_request_done(mmc, mrq);
|
||||
mmc_request_done(mmc_from_priv(reader), mrq);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops cb710_mmc_host = {
|
||||
@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev)
|
||||
|
||||
reader = mmc_priv(mmc);
|
||||
|
||||
tasklet_init(&reader->finish_req_tasklet,
|
||||
cb710_mmc_finish_request_tasklet, (unsigned long)mmc);
|
||||
tasklet_setup(&reader->finish_req_tasklet,
|
||||
cb710_mmc_finish_request_tasklet);
|
||||
spin_lock_init(&reader->irq_lock);
|
||||
cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/mmc/card.h>
|
||||
|
||||
#include "cqhci.h"
|
||||
#include "cqhci-crypto.h"
|
||||
|
||||
#define DCMD_SLOT 31
|
||||
#define NUM_SLOTS 32
|
||||
@ -258,6 +259,9 @@ static void __cqhci_enable(struct cqhci_host *cq_host)
|
||||
if (cq_host->caps & CQHCI_TASK_DESC_SZ_128)
|
||||
cqcfg |= CQHCI_TASK_DESC_SZ;
|
||||
|
||||
if (mmc->caps2 & MMC_CAP2_CRYPTO)
|
||||
cqcfg |= CQHCI_CRYPTO_GENERAL_ENABLE;
|
||||
|
||||
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
|
||||
|
||||
cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base),
|
||||
@ -408,13 +412,15 @@ static void cqhci_disable(struct mmc_host *mmc)
|
||||
}
|
||||
|
||||
static void cqhci_prep_task_desc(struct mmc_request *mrq,
|
||||
u64 *data, bool intr)
|
||||
struct cqhci_host *cq_host, int tag)
|
||||
{
|
||||
__le64 *task_desc = (__le64 __force *)get_desc(cq_host, tag);
|
||||
u32 req_flags = mrq->data->flags;
|
||||
u64 desc0;
|
||||
|
||||
*data = CQHCI_VALID(1) |
|
||||
desc0 = CQHCI_VALID(1) |
|
||||
CQHCI_END(1) |
|
||||
CQHCI_INT(intr) |
|
||||
CQHCI_INT(1) |
|
||||
CQHCI_ACT(0x5) |
|
||||
CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) |
|
||||
CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) |
|
||||
@ -425,8 +431,19 @@ static void cqhci_prep_task_desc(struct mmc_request *mrq,
|
||||
CQHCI_BLK_COUNT(mrq->data->blocks) |
|
||||
CQHCI_BLK_ADDR((u64)mrq->data->blk_addr);
|
||||
|
||||
pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n",
|
||||
mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data);
|
||||
task_desc[0] = cpu_to_le64(desc0);
|
||||
|
||||
if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) {
|
||||
u64 desc1 = cqhci_crypto_prep_task_desc(mrq);
|
||||
|
||||
task_desc[1] = cpu_to_le64(desc1);
|
||||
|
||||
pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx%016llx\n",
|
||||
mmc_hostname(mrq->host), mrq->tag, desc1, desc0);
|
||||
} else {
|
||||
pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n",
|
||||
mmc_hostname(mrq->host), mrq->tag, desc0);
|
||||
}
|
||||
}
|
||||
|
||||
static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq)
|
||||
@ -567,8 +584,6 @@ static inline int cqhci_tag(struct mmc_request *mrq)
|
||||
static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
int err = 0;
|
||||
u64 data = 0;
|
||||
u64 *task_desc = NULL;
|
||||
int tag = cqhci_tag(mrq);
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
unsigned long flags;
|
||||
@ -598,9 +613,8 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
}
|
||||
|
||||
if (mrq->data) {
|
||||
task_desc = (__le64 __force *)get_desc(cq_host, tag);
|
||||
cqhci_prep_task_desc(mrq, &data, 1);
|
||||
*task_desc = cpu_to_le64(data);
|
||||
cqhci_prep_task_desc(mrq, cq_host, tag);
|
||||
|
||||
err = cqhci_prep_tran_desc(mrq, cq_host, tag);
|
||||
if (err) {
|
||||
pr_err("%s: cqhci: failed to setup tx desc: %d\n",
|
||||
@ -671,6 +685,7 @@ static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error,
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
struct cqhci_slot *slot;
|
||||
u32 terri;
|
||||
u32 tdpe;
|
||||
int tag;
|
||||
|
||||
spin_lock(&cq_host->lock);
|
||||
@ -709,6 +724,30 @@ static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ICCE ("Invalid Crypto Configuration Error"). This should
|
||||
* never happen, since the block layer ensures that all crypto-enabled
|
||||
* I/O requests have a valid keyslot before they reach the driver.
|
||||
*
|
||||
* Note that GCE ("General Crypto Error") is different; it already got
|
||||
* handled above by checking TERRI.
|
||||
*/
|
||||
if (status & CQHCI_IS_ICCE) {
|
||||
tdpe = cqhci_readl(cq_host, CQHCI_TDPE);
|
||||
WARN_ONCE(1,
|
||||
"%s: cqhci: invalid crypto configuration error. IRQ status: 0x%08x TDPE: 0x%08x\n",
|
||||
mmc_hostname(mmc), status, tdpe);
|
||||
while (tdpe != 0) {
|
||||
tag = __ffs(tdpe);
|
||||
tdpe &= ~(1 << tag);
|
||||
slot = &cq_host->slot[tag];
|
||||
if (!slot->mrq)
|
||||
continue;
|
||||
slot->flags = cqhci_error_flags(data_error, cmd_error);
|
||||
cqhci_recovery_needed(mmc, slot->mrq, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cq_host->recovery_halt) {
|
||||
/*
|
||||
* The only way to guarantee forward progress is to mark at
|
||||
@ -774,7 +813,8 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
|
||||
|
||||
pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status);
|
||||
|
||||
if ((status & CQHCI_IS_RED) || cmd_error || data_error)
|
||||
if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) ||
|
||||
cmd_error || data_error)
|
||||
cqhci_error_irq(mmc, status, cmd_error, data_error);
|
||||
|
||||
if (status & CQHCI_IS_TCC) {
|
||||
@ -1141,6 +1181,13 @@ int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc,
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = cqhci_crypto_init(cq_host);
|
||||
if (err) {
|
||||
pr_err("%s: CQHCI crypto initialization failed\n",
|
||||
mmc_hostname(mmc));
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
spin_lock_init(&cq_host->lock);
|
||||
|
||||
init_completion(&cq_host->halt_comp);
|
242
drivers/mmc/host/cqhci-crypto.c
Normal file
242
drivers/mmc/host/cqhci-crypto.c
Normal file
@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* CQHCI crypto engine (inline encryption) support
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/blk-crypto.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "cqhci-crypto.h"
|
||||
|
||||
/* Map from blk-crypto modes to CQHCI crypto algorithm IDs and key sizes */
|
||||
static const struct cqhci_crypto_alg_entry {
|
||||
enum cqhci_crypto_alg alg;
|
||||
enum cqhci_crypto_key_size key_size;
|
||||
} cqhci_crypto_algs[BLK_ENCRYPTION_MODE_MAX] = {
|
||||
[BLK_ENCRYPTION_MODE_AES_256_XTS] = {
|
||||
.alg = CQHCI_CRYPTO_ALG_AES_XTS,
|
||||
.key_size = CQHCI_CRYPTO_KEY_SIZE_256,
|
||||
},
|
||||
};
|
||||
|
||||
static inline struct cqhci_host *
|
||||
cqhci_host_from_ksm(struct blk_keyslot_manager *ksm)
|
||||
{
|
||||
struct mmc_host *mmc = container_of(ksm, struct mmc_host, ksm);
|
||||
|
||||
return mmc->cqe_private;
|
||||
}
|
||||
|
||||
static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
|
||||
const union cqhci_crypto_cfg_entry *cfg,
|
||||
int slot)
|
||||
{
|
||||
u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg);
|
||||
int i;
|
||||
|
||||
if (cq_host->ops->program_key)
|
||||
return cq_host->ops->program_key(cq_host, cfg, slot);
|
||||
|
||||
/* Clear CFGE */
|
||||
cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0]));
|
||||
|
||||
/* Write the key */
|
||||
for (i = 0; i < 16; i++) {
|
||||
cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[i]),
|
||||
slot_offset + i * sizeof(cfg->reg_val[0]));
|
||||
}
|
||||
/* Write dword 17 */
|
||||
cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[17]),
|
||||
slot_offset + 17 * sizeof(cfg->reg_val[0]));
|
||||
/* Write dword 16, which includes the new value of CFGE */
|
||||
cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]),
|
||||
slot_offset + 16 * sizeof(cfg->reg_val[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
unsigned int slot)
|
||||
|
||||
{
|
||||
struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm);
|
||||
const union cqhci_crypto_cap_entry *ccap_array =
|
||||
cq_host->crypto_cap_array;
|
||||
const struct cqhci_crypto_alg_entry *alg =
|
||||
&cqhci_crypto_algs[key->crypto_cfg.crypto_mode];
|
||||
u8 data_unit_mask = key->crypto_cfg.data_unit_size / 512;
|
||||
int i;
|
||||
int cap_idx = -1;
|
||||
union cqhci_crypto_cfg_entry cfg = {};
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
|
||||
for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) {
|
||||
if (ccap_array[i].algorithm_id == alg->alg &&
|
||||
ccap_array[i].key_size == alg->key_size &&
|
||||
(ccap_array[i].sdus_mask & data_unit_mask)) {
|
||||
cap_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (WARN_ON(cap_idx < 0))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
cfg.data_unit_size = data_unit_mask;
|
||||
cfg.crypto_cap_idx = cap_idx;
|
||||
cfg.config_enable = CQHCI_CRYPTO_CONFIGURATION_ENABLE;
|
||||
|
||||
if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) {
|
||||
/* In XTS mode, the blk_crypto_key's size is already doubled */
|
||||
memcpy(cfg.crypto_key, key->raw, key->size/2);
|
||||
memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2,
|
||||
key->raw + key->size/2, key->size/2);
|
||||
} else {
|
||||
memcpy(cfg.crypto_key, key->raw, key->size);
|
||||
}
|
||||
|
||||
err = cqhci_crypto_program_key(cq_host, &cfg, slot);
|
||||
|
||||
memzero_explicit(&cfg, sizeof(cfg));
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
|
||||
{
|
||||
/*
|
||||
* Clear the crypto cfg on the device. Clearing CFGE
|
||||
* might not be sufficient, so just clear the entire cfg.
|
||||
*/
|
||||
union cqhci_crypto_cfg_entry cfg = {};
|
||||
|
||||
return cqhci_crypto_program_key(cq_host, &cfg, slot);
|
||||
}
|
||||
|
||||
static int cqhci_crypto_keyslot_evict(struct blk_keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
unsigned int slot)
|
||||
{
|
||||
struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm);
|
||||
|
||||
return cqhci_crypto_clear_keyslot(cq_host, slot);
|
||||
}
|
||||
|
||||
/*
|
||||
* The keyslot management operations for CQHCI crypto.
|
||||
*
|
||||
* Note that the block layer ensures that these are never called while the host
|
||||
* controller is runtime-suspended. However, the CQE won't necessarily be
|
||||
* "enabled" when these are called, i.e. CQHCI_ENABLE might not be set in the
|
||||
* CQHCI_CFG register. But the hardware allows that.
|
||||
*/
|
||||
static const struct blk_ksm_ll_ops cqhci_ksm_ops = {
|
||||
.keyslot_program = cqhci_crypto_keyslot_program,
|
||||
.keyslot_evict = cqhci_crypto_keyslot_evict,
|
||||
};
|
||||
|
||||
static enum blk_crypto_mode_num
|
||||
cqhci_find_blk_crypto_mode(union cqhci_crypto_cap_entry cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cqhci_crypto_algs); i++) {
|
||||
BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
|
||||
if (cqhci_crypto_algs[i].alg == cap.algorithm_id &&
|
||||
cqhci_crypto_algs[i].key_size == cap.key_size)
|
||||
return i;
|
||||
}
|
||||
return BLK_ENCRYPTION_MODE_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* cqhci_crypto_init - initialize CQHCI crypto support
|
||||
* @cq_host: a cqhci host
|
||||
*
|
||||
* If the driver previously set MMC_CAP2_CRYPTO and the CQE declares
|
||||
* CQHCI_CAP_CS, initialize the crypto support. This involves reading the
|
||||
* crypto capability registers, initializing the keyslot manager, clearing all
|
||||
* keyslots, and enabling 128-bit task descriptors.
|
||||
*
|
||||
* Return: 0 if crypto was initialized or isn't supported; whether
|
||||
* MMC_CAP2_CRYPTO remains set indicates which one of those cases it is.
|
||||
* Also can return a negative errno value on unexpected error.
|
||||
*/
|
||||
int cqhci_crypto_init(struct cqhci_host *cq_host)
|
||||
{
|
||||
struct mmc_host *mmc = cq_host->mmc;
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
struct blk_keyslot_manager *ksm = &mmc->ksm;
|
||||
unsigned int num_keyslots;
|
||||
unsigned int cap_idx;
|
||||
enum blk_crypto_mode_num blk_mode_num;
|
||||
unsigned int slot;
|
||||
int err = 0;
|
||||
|
||||
if (!(mmc->caps2 & MMC_CAP2_CRYPTO) ||
|
||||
!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
|
||||
goto out;
|
||||
|
||||
cq_host->crypto_capabilities.reg_val =
|
||||
cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
|
||||
|
||||
cq_host->crypto_cfg_register =
|
||||
(u32)cq_host->crypto_capabilities.config_array_ptr * 0x100;
|
||||
|
||||
cq_host->crypto_cap_array =
|
||||
devm_kcalloc(dev, cq_host->crypto_capabilities.num_crypto_cap,
|
||||
sizeof(cq_host->crypto_cap_array[0]), GFP_KERNEL);
|
||||
if (!cq_host->crypto_cap_array) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* CCAP.CFGC is off by one, so the actual number of crypto
|
||||
* configurations (a.k.a. keyslots) is CCAP.CFGC + 1.
|
||||
*/
|
||||
num_keyslots = cq_host->crypto_capabilities.config_count + 1;
|
||||
|
||||
err = devm_blk_ksm_init(dev, ksm, num_keyslots);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
ksm->ksm_ll_ops = cqhci_ksm_ops;
|
||||
ksm->dev = dev;
|
||||
|
||||
/* Unfortunately, CQHCI crypto only supports 32 DUN bits. */
|
||||
ksm->max_dun_bytes_supported = 4;
|
||||
|
||||
/*
|
||||
* Cache all the crypto capabilities and advertise the supported crypto
|
||||
* modes and data unit sizes to the block layer.
|
||||
*/
|
||||
for (cap_idx = 0; cap_idx < cq_host->crypto_capabilities.num_crypto_cap;
|
||||
cap_idx++) {
|
||||
cq_host->crypto_cap_array[cap_idx].reg_val =
|
||||
cpu_to_le32(cqhci_readl(cq_host,
|
||||
CQHCI_CRYPTOCAP +
|
||||
cap_idx * sizeof(__le32)));
|
||||
blk_mode_num = cqhci_find_blk_crypto_mode(
|
||||
cq_host->crypto_cap_array[cap_idx]);
|
||||
if (blk_mode_num == BLK_ENCRYPTION_MODE_INVALID)
|
||||
continue;
|
||||
ksm->crypto_modes_supported[blk_mode_num] |=
|
||||
cq_host->crypto_cap_array[cap_idx].sdus_mask * 512;
|
||||
}
|
||||
|
||||
/* Clear all the keyslots so that we start in a known state. */
|
||||
for (slot = 0; slot < num_keyslots; slot++)
|
||||
cqhci_crypto_clear_keyslot(cq_host, slot);
|
||||
|
||||
/* CQHCI crypto requires the use of 128-bit task descriptors. */
|
||||
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mmc->caps2 &= ~MMC_CAP2_CRYPTO;
|
||||
return err;
|
||||
}
|
47
drivers/mmc/host/cqhci-crypto.h
Normal file
47
drivers/mmc/host/cqhci-crypto.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* CQHCI crypto engine (inline encryption) support
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
*/
|
||||
|
||||
#ifndef LINUX_MMC_CQHCI_CRYPTO_H
|
||||
#define LINUX_MMC_CQHCI_CRYPTO_H
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "cqhci.h"
|
||||
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
|
||||
int cqhci_crypto_init(struct cqhci_host *host);
|
||||
|
||||
/*
|
||||
* Returns the crypto bits that should be set in bits 64-127 of the
|
||||
* task descriptor.
|
||||
*/
|
||||
static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)
|
||||
{
|
||||
if (!mrq->crypto_enabled)
|
||||
return 0;
|
||||
|
||||
return CQHCI_CRYPTO_ENABLE_BIT |
|
||||
CQHCI_CRYPTO_KEYSLOT(mrq->crypto_key_slot) |
|
||||
mrq->data_unit_num;
|
||||
}
|
||||
|
||||
#else /* CONFIG_MMC_CRYPTO */
|
||||
|
||||
static inline int cqhci_crypto_init(struct cqhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_MMC_CRYPTO */
|
||||
|
||||
#endif /* LINUX_MMC_CQHCI_CRYPTO_H */
|
@ -22,10 +22,13 @@
|
||||
|
||||
/* capabilities */
|
||||
#define CQHCI_CAP 0x04
|
||||
#define CQHCI_CAP_CS 0x10000000 /* Crypto Support */
|
||||
|
||||
/* configuration */
|
||||
#define CQHCI_CFG 0x08
|
||||
#define CQHCI_DCMD 0x00001000
|
||||
#define CQHCI_TASK_DESC_SZ 0x00000100
|
||||
#define CQHCI_CRYPTO_GENERAL_ENABLE 0x00000002
|
||||
#define CQHCI_ENABLE 0x00000001
|
||||
|
||||
/* control */
|
||||
@ -39,8 +42,11 @@
|
||||
#define CQHCI_IS_TCC BIT(1)
|
||||
#define CQHCI_IS_RED BIT(2)
|
||||
#define CQHCI_IS_TCL BIT(3)
|
||||
#define CQHCI_IS_GCE BIT(4) /* General Crypto Error */
|
||||
#define CQHCI_IS_ICCE BIT(5) /* Invalid Crypto Config Error */
|
||||
|
||||
#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED)
|
||||
#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED | \
|
||||
CQHCI_IS_GCE | CQHCI_IS_ICCE)
|
||||
|
||||
/* interrupt status enable */
|
||||
#define CQHCI_ISTE 0x14
|
||||
@ -78,6 +84,9 @@
|
||||
/* task clear */
|
||||
#define CQHCI_TCLR 0x38
|
||||
|
||||
/* task descriptor processing error */
|
||||
#define CQHCI_TDPE 0x3c
|
||||
|
||||
/* send status config 1 */
|
||||
#define CQHCI_SSC1 0x40
|
||||
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
|
||||
@ -107,6 +116,10 @@
|
||||
/* command response argument */
|
||||
#define CQHCI_CRA 0x5C
|
||||
|
||||
/* crypto capabilities */
|
||||
#define CQHCI_CCAP 0x100
|
||||
#define CQHCI_CRYPTOCAP 0x104
|
||||
|
||||
#define CQHCI_INT_ALL 0xF
|
||||
#define CQHCI_IC_DEFAULT_ICCTH 31
|
||||
#define CQHCI_IC_DEFAULT_ICTOVAL 1
|
||||
@ -133,11 +146,70 @@
|
||||
#define CQHCI_CMD_TIMING(x) (((x) & 1) << 22)
|
||||
#define CQHCI_RESP_TYPE(x) (((x) & 0x3) << 23)
|
||||
|
||||
/* crypto task descriptor fields (for bits 64-127 of task descriptor) */
|
||||
#define CQHCI_CRYPTO_ENABLE_BIT (1ULL << 47)
|
||||
#define CQHCI_CRYPTO_KEYSLOT(x) ((u64)(x) << 32)
|
||||
|
||||
/* transfer descriptor fields */
|
||||
#define CQHCI_DAT_LENGTH(x) (((x) & 0xFFFF) << 16)
|
||||
#define CQHCI_DAT_ADDR_LO(x) (((x) & 0xFFFFFFFF) << 32)
|
||||
#define CQHCI_DAT_ADDR_HI(x) (((x) & 0xFFFFFFFF) << 0)
|
||||
|
||||
/* CCAP - Crypto Capability 100h */
|
||||
union cqhci_crypto_capabilities {
|
||||
__le32 reg_val;
|
||||
struct {
|
||||
u8 num_crypto_cap;
|
||||
u8 config_count;
|
||||
u8 reserved;
|
||||
u8 config_array_ptr;
|
||||
};
|
||||
};
|
||||
|
||||
enum cqhci_crypto_key_size {
|
||||
CQHCI_CRYPTO_KEY_SIZE_INVALID = 0,
|
||||
CQHCI_CRYPTO_KEY_SIZE_128 = 1,
|
||||
CQHCI_CRYPTO_KEY_SIZE_192 = 2,
|
||||
CQHCI_CRYPTO_KEY_SIZE_256 = 3,
|
||||
CQHCI_CRYPTO_KEY_SIZE_512 = 4,
|
||||
};
|
||||
|
||||
enum cqhci_crypto_alg {
|
||||
CQHCI_CRYPTO_ALG_AES_XTS = 0,
|
||||
CQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC = 1,
|
||||
CQHCI_CRYPTO_ALG_AES_ECB = 2,
|
||||
CQHCI_CRYPTO_ALG_ESSIV_AES_CBC = 3,
|
||||
};
|
||||
|
||||
/* x-CRYPTOCAP - Crypto Capability X */
|
||||
union cqhci_crypto_cap_entry {
|
||||
__le32 reg_val;
|
||||
struct {
|
||||
u8 algorithm_id;
|
||||
u8 sdus_mask; /* Supported data unit size mask */
|
||||
u8 key_size;
|
||||
u8 reserved;
|
||||
};
|
||||
};
|
||||
|
||||
#define CQHCI_CRYPTO_CONFIGURATION_ENABLE (1 << 7)
|
||||
#define CQHCI_CRYPTO_KEY_MAX_SIZE 64
|
||||
/* x-CRYPTOCFG - Crypto Configuration X */
|
||||
union cqhci_crypto_cfg_entry {
|
||||
__le32 reg_val[32];
|
||||
struct {
|
||||
u8 crypto_key[CQHCI_CRYPTO_KEY_MAX_SIZE];
|
||||
u8 data_unit_size;
|
||||
u8 crypto_cap_idx;
|
||||
u8 reserved_1;
|
||||
u8 config_enable;
|
||||
u8 reserved_multi_host;
|
||||
u8 reserved_2;
|
||||
u8 vsb[2];
|
||||
u8 reserved_3[56];
|
||||
};
|
||||
};
|
||||
|
||||
struct cqhci_host_ops;
|
||||
struct mmc_host;
|
||||
struct mmc_request;
|
||||
@ -196,6 +268,12 @@ struct cqhci_host {
|
||||
struct completion halt_comp;
|
||||
wait_queue_head_t wait_queue;
|
||||
struct cqhci_slot *slot;
|
||||
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
union cqhci_crypto_capabilities crypto_capabilities;
|
||||
union cqhci_crypto_cap_entry *crypto_cap_array;
|
||||
u32 crypto_cfg_register;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct cqhci_host_ops {
|
||||
@ -208,6 +286,10 @@ struct cqhci_host_ops {
|
||||
u64 *data);
|
||||
void (*pre_enable)(struct mmc_host *mmc);
|
||||
void (*post_disable)(struct mmc_host *mmc);
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
int (*program_key)(struct cqhci_host *cq_host,
|
||||
const union cqhci_crypto_cfg_entry *cfg, int slot);
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
|
||||
|
@ -1,234 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
|
||||
*
|
||||
* Copyright (C) 2016, Linaro Ltd.
|
||||
* Copyright (C) 2016, ZTE Corp.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.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 <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
#include "dw_mmc-zx.h"
|
||||
|
||||
struct dw_mci_zx_priv_data {
|
||||
struct regmap *sysc_base;
|
||||
};
|
||||
|
||||
enum delay_type {
|
||||
DELAY_TYPE_READ, /* read dqs delay */
|
||||
DELAY_TYPE_CLK, /* clk sample delay */
|
||||
};
|
||||
|
||||
static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
|
||||
enum delay_type dflag)
|
||||
{
|
||||
struct dw_mci_zx_priv_data *priv = host->priv;
|
||||
struct regmap *sysc_base = priv->sysc_base;
|
||||
unsigned int clksel;
|
||||
unsigned int loop = 1000;
|
||||
int ret;
|
||||
|
||||
if (!sysc_base)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
|
||||
PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
|
||||
PARA_PHASE_DET_SEL_MASK |
|
||||
PARA_DLL_LOCK_NUM_MASK |
|
||||
DLL_REG_SET | PARA_DLL_START_MASK,
|
||||
PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dflag == DELAY_TYPE_CLK) {
|
||||
clksel &= ~CLK_SAMP_DELAY_MASK;
|
||||
clksel |= CLK_SAMP_DELAY(delay);
|
||||
} else {
|
||||
clksel &= ~READ_DQS_DELAY_MASK;
|
||||
clksel |= READ_DQS_DELAY(delay);
|
||||
}
|
||||
|
||||
regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
|
||||
regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
|
||||
PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
|
||||
DLL_REG_SET,
|
||||
PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) |
|
||||
DLL_REG_SET);
|
||||
|
||||
do {
|
||||
ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} while (--loop && !(clksel & ZX_DLL_LOCKED));
|
||||
|
||||
if (!loop) {
|
||||
dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
int ret, len = 0, start = 0, end = 0, delay, best = 0;
|
||||
|
||||
for (delay = 1; delay < 128; delay++) {
|
||||
ret = dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK);
|
||||
if (!ret && mmc_send_tuning(mmc, opcode, NULL)) {
|
||||
if (start >= 0) {
|
||||
end = delay - 1;
|
||||
/* check and update longest good range */
|
||||
if ((end - start) > len) {
|
||||
best = (start + end) >> 1;
|
||||
len = end - start;
|
||||
}
|
||||
}
|
||||
start = -1;
|
||||
end = 0;
|
||||
continue;
|
||||
}
|
||||
if (start < 0)
|
||||
start = delay;
|
||||
}
|
||||
|
||||
if (start >= 0) {
|
||||
end = delay - 1;
|
||||
if ((end - start) > len) {
|
||||
best = (start + end) >> 1;
|
||||
len = end - start;
|
||||
}
|
||||
}
|
||||
if (best < 0)
|
||||
return -EIO;
|
||||
|
||||
dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
|
||||
start, end);
|
||||
return dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK);
|
||||
}
|
||||
|
||||
static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* config phase shift as 90 degree */
|
||||
ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
|
||||
if (host->verid == 0x290a) /* only for emmc */
|
||||
return dw_mci_zx_emmc_execute_tuning(slot, opcode);
|
||||
/* TODO: Add 0x210a dedicated tuning for sd/sdio */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_zx_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
struct device_node *node;
|
||||
struct dw_mci_zx_priv_data *priv;
|
||||
struct regmap *sysc_base;
|
||||
|
||||
/* syscon is needed only by emmc */
|
||||
node = of_parse_phandle(np, "zte,aon-syscon", 0);
|
||||
if (node) {
|
||||
sysc_base = syscon_node_to_regmap(node);
|
||||
of_node_put(node);
|
||||
|
||||
if (IS_ERR(sysc_base))
|
||||
return dev_err_probe(host->dev, PTR_ERR(sysc_base),
|
||||
"Can't get syscon\n");
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
priv->sysc_base = sysc_base;
|
||||
host->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long zx_dwmmc_caps[3] = {
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data zx_drv_data = {
|
||||
.caps = zx_dwmmc_caps,
|
||||
.num_caps = ARRAY_SIZE(zx_dwmmc_caps),
|
||||
.execute_tuning = dw_mci_zx_execute_tuning,
|
||||
.prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
|
||||
.parse_dt = dw_mci_zx_parse_dt,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_zx_match[] = {
|
||||
{ .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
|
||||
|
||||
static int dw_mci_zx_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
|
||||
dw_mci_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_mci_zx_pltfm_driver = {
|
||||
.probe = dw_mci_zx_probe,
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dwmmc_zx",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = dw_mci_zx_match,
|
||||
.pm = &dw_mci_zx_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_mci_zx_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ZTE emmc/sd driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,32 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _DW_MMC_ZX_H_
|
||||
#define _DW_MMC_ZX_H_
|
||||
|
||||
/* ZX296718 SoC specific DLL register offset. */
|
||||
#define LB_AON_EMMC_CFG_REG0 0x1B0
|
||||
#define LB_AON_EMMC_CFG_REG1 0x1B4
|
||||
#define LB_AON_EMMC_CFG_REG2 0x1B8
|
||||
|
||||
/* LB_AON_EMMC_CFG_REG0 register defines */
|
||||
#define PARA_DLL_START(x) ((x) & 0xFF)
|
||||
#define PARA_DLL_START_MASK 0xFF
|
||||
#define DLL_REG_SET BIT(8)
|
||||
#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
|
||||
#define PARA_DLL_LOCK_NUM_MASK (7 << 16)
|
||||
#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
|
||||
#define PARA_PHASE_DET_SEL_MASK (7 << 20)
|
||||
#define PARA_DLL_BYPASS_MODE BIT(23)
|
||||
#define PARA_HALF_CLK_MODE BIT(24)
|
||||
|
||||
/* LB_AON_EMMC_CFG_REG1 register defines */
|
||||
#define READ_DQS_DELAY(x) ((x) & 0x7F)
|
||||
#define READ_DQS_DELAY_MASK (0x7F)
|
||||
#define READ_DQS_BYPASS_MODE BIT(7)
|
||||
#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
|
||||
#define CLK_SAMP_DELAY_MASK (0x7F << 8)
|
||||
#define CLK_SAMP_BYPASS_MODE BIT(15)
|
||||
|
||||
/* LB_AON_EMMC_CFG_REG2 register defines */
|
||||
#define ZX_DLL_LOCKED BIT(2)
|
||||
|
||||
#endif /* _DW_MMC_ZX_H_ */
|
@ -1952,9 +1952,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dw_mci_tasklet_func(unsigned long priv)
|
||||
static void dw_mci_tasklet_func(struct tasklet_struct *t)
|
||||
{
|
||||
struct dw_mci *host = (struct dw_mci *)priv;
|
||||
struct dw_mci *host = from_tasklet(host, t, tasklet);
|
||||
struct mmc_data *data;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_request *mrq;
|
||||
@ -3308,7 +3308,7 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
else
|
||||
host->fifo_reg = host->regs + DATA_240A_OFFSET;
|
||||
|
||||
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
|
||||
tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
|
||||
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
|
||||
host->irq_flags, "dw-mci", host);
|
||||
if (ret)
|
||||
|
@ -152,7 +152,6 @@ struct jz4740_mmc_host {
|
||||
enum jz4740_mmc_version version;
|
||||
|
||||
int irq;
|
||||
int card_detect_irq;
|
||||
|
||||
void __iomem *base;
|
||||
struct resource *mem_res;
|
||||
|
@ -227,7 +227,6 @@ static void meson_mmc_get_transfer_mode(struct mmc_host *mmc,
|
||||
struct mmc_data *data = mrq->data;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
bool use_desc_chain_mode = true;
|
||||
|
||||
/*
|
||||
* When Controller DMA cannot directly access DDR memory, disable
|
||||
@ -237,25 +236,33 @@ static void meson_mmc_get_transfer_mode(struct mmc_host *mmc,
|
||||
if (host->dram_access_quirk)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Broken SDIO with AP6255-based WiFi on Khadas VIM Pro has been
|
||||
* reported. For some strange reason this occurs in descriptor
|
||||
* chain mode only. So let's fall back to bounce buffer mode
|
||||
* for command SD_IO_RW_EXTENDED.
|
||||
*/
|
||||
if (mrq->cmd->opcode == SD_IO_RW_EXTENDED)
|
||||
return;
|
||||
|
||||
for_each_sg(data->sg, sg, data->sg_len, i)
|
||||
/* check for 8 byte alignment */
|
||||
if (sg->offset & 7) {
|
||||
WARN_ONCE(1, "unaligned scatterlist buffer\n");
|
||||
use_desc_chain_mode = false;
|
||||
break;
|
||||
if (data->blocks > 1) {
|
||||
/*
|
||||
* In block mode DMA descriptor format, "length" field indicates
|
||||
* number of blocks and there is no way to pass DMA size that
|
||||
* is not multiple of SDIO block size, making it impossible to
|
||||
* tie more than one memory buffer with single SDIO block.
|
||||
* Block mode sg buffer size should be aligned with SDIO block
|
||||
* size, otherwise chain mode could not be used.
|
||||
*/
|
||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||
if (sg->length % data->blksz) {
|
||||
WARN_ONCE(1, "unaligned sg len %u blksize %u\n",
|
||||
sg->length, data->blksz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_desc_chain_mode)
|
||||
data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE;
|
||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||
/* check for 8 byte alignment */
|
||||
if (sg->offset % 8) {
|
||||
WARN_ONCE(1, "unaligned scatterlist buffer\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE;
|
||||
}
|
||||
|
||||
static inline bool meson_mmc_desc_chain_mode(const struct mmc_data *data)
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
#include <asm/io.h>
|
||||
@ -1888,6 +1889,65 @@ static struct mmc_host_ops mmci_ops = {
|
||||
.start_signal_voltage_switch = mmci_sig_volt_switch,
|
||||
};
|
||||
|
||||
static void mmci_probe_level_translator(struct mmc_host *mmc)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
struct gpio_desc *cmd_gpio;
|
||||
struct gpio_desc *ck_gpio;
|
||||
struct gpio_desc *ckin_gpio;
|
||||
int clk_hi, clk_lo;
|
||||
|
||||
/*
|
||||
* Assume the level translator is present if st,use-ckin is set.
|
||||
* This is to cater for DTs which do not implement this test.
|
||||
*/
|
||||
host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
|
||||
|
||||
cmd_gpio = gpiod_get(dev, "st,cmd", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(cmd_gpio))
|
||||
goto exit_cmd;
|
||||
|
||||
ck_gpio = gpiod_get(dev, "st,ck", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ck_gpio))
|
||||
goto exit_ck;
|
||||
|
||||
ckin_gpio = gpiod_get(dev, "st,ckin", GPIOD_IN);
|
||||
if (IS_ERR(ckin_gpio))
|
||||
goto exit_ckin;
|
||||
|
||||
/* All GPIOs are valid, test whether level translator works */
|
||||
|
||||
/* Sample CKIN */
|
||||
clk_hi = !!gpiod_get_value(ckin_gpio);
|
||||
|
||||
/* Set CK low */
|
||||
gpiod_set_value(ck_gpio, 0);
|
||||
|
||||
/* Sample CKIN */
|
||||
clk_lo = !!gpiod_get_value(ckin_gpio);
|
||||
|
||||
/* Tristate all */
|
||||
gpiod_direction_input(cmd_gpio);
|
||||
gpiod_direction_input(ck_gpio);
|
||||
|
||||
/* Level translator is present if CK signal is propagated to CKIN */
|
||||
if (!clk_hi || clk_lo) {
|
||||
host->clk_reg_add &= ~MCI_STM32_CLK_SELCKIN;
|
||||
dev_warn(dev,
|
||||
"Level translator inoperable, CK signal not detected on CKIN, disabling.\n");
|
||||
}
|
||||
|
||||
gpiod_put(ckin_gpio);
|
||||
|
||||
exit_ckin:
|
||||
gpiod_put(ck_gpio);
|
||||
exit_ck:
|
||||
gpiod_put(cmd_gpio);
|
||||
exit_cmd:
|
||||
pinctrl_select_default_state(dev);
|
||||
}
|
||||
|
||||
static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
|
||||
{
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
@ -1913,7 +1973,7 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
|
||||
if (of_get_property(np, "st,neg-edge", NULL))
|
||||
host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;
|
||||
if (of_get_property(np, "st,use-ckin", NULL))
|
||||
host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
|
||||
mmci_probe_level_translator(mmc);
|
||||
|
||||
if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
|
||||
@ -1949,15 +2009,15 @@ static int mmci_probe(struct amba_device *dev,
|
||||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmci_of_parse(np, mmc);
|
||||
if (ret)
|
||||
goto host_free;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->mmc_ops = &mmci_ops;
|
||||
mmc->ops = &mmci_ops;
|
||||
|
||||
ret = mmci_of_parse(np, mmc);
|
||||
if (ret)
|
||||
goto host_free;
|
||||
|
||||
/*
|
||||
* Some variant (STM32) doesn't have opendrain bit, nevertheless
|
||||
* pins can be set accordingly using pinctrl
|
||||
|
@ -1127,13 +1127,13 @@ static void msdc_track_cmd_data(struct msdc_host *host,
|
||||
static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool ret;
|
||||
|
||||
ret = cancel_delayed_work(&host->req_timeout);
|
||||
if (!ret) {
|
||||
/* delay work already running */
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* No need check the return value of cancel_delayed_work, as only ONE
|
||||
* path will go here!
|
||||
*/
|
||||
cancel_delayed_work(&host->req_timeout);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->mrq = NULL;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
@ -1155,7 +1155,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
|
||||
bool done = false;
|
||||
bool sbc_error;
|
||||
unsigned long flags;
|
||||
u32 *rsp = cmd->resp;
|
||||
u32 *rsp;
|
||||
|
||||
if (mrq->sbc && cmd == mrq->cmd &&
|
||||
(events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR
|
||||
@ -1176,6 +1176,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
|
||||
|
||||
if (done)
|
||||
return true;
|
||||
rsp = cmd->resp;
|
||||
|
||||
sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
|
||||
|
||||
@ -1363,7 +1364,7 @@ static void msdc_data_xfer_next(struct msdc_host *host,
|
||||
static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
|
||||
struct mmc_request *mrq, struct mmc_data *data)
|
||||
{
|
||||
struct mmc_command *stop = data->stop;
|
||||
struct mmc_command *stop;
|
||||
unsigned long flags;
|
||||
bool done;
|
||||
unsigned int check_data = events &
|
||||
@ -1379,6 +1380,7 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
|
||||
|
||||
if (done)
|
||||
return true;
|
||||
stop = data->stop;
|
||||
|
||||
if (check_data || (stop && stop->error)) {
|
||||
dev_dbg(host->dev, "DMA status: 0x%8X\n",
|
||||
|
@ -628,7 +628,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto out_clk_disable;
|
||||
goto out_free_dma;
|
||||
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
|
@ -878,9 +878,9 @@ static void mmc_omap_cover_timer(struct timer_list *t)
|
||||
tasklet_schedule(&slot->cover_tasklet);
|
||||
}
|
||||
|
||||
static void mmc_omap_cover_handler(unsigned long param)
|
||||
static void mmc_omap_cover_handler(struct tasklet_struct *t)
|
||||
{
|
||||
struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
|
||||
struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
|
||||
int cover_open = mmc_omap_cover_is_open(slot);
|
||||
|
||||
mmc_detect_change(slot->mmc, 0);
|
||||
@ -1269,8 +1269,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
|
||||
|
||||
if (slot->pdata->get_cover_state != NULL) {
|
||||
timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
|
||||
tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
|
||||
(unsigned long)slot);
|
||||
tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
|
||||
}
|
||||
|
||||
r = mmc_add_host(mmc);
|
||||
|
@ -177,7 +177,7 @@ struct omap_hsmmc_host {
|
||||
struct regulator *pbias;
|
||||
bool pbias_enabled;
|
||||
void __iomem *base;
|
||||
int vqmmc_enabled;
|
||||
bool vqmmc_enabled;
|
||||
resource_size_t mapbase;
|
||||
spinlock_t irq_lock; /* Prevent races with irq handler */
|
||||
unsigned int dma_len;
|
||||
@ -232,7 +232,7 @@ static int omap_hsmmc_enable_supply(struct mmc_host *mmc)
|
||||
dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n");
|
||||
goto err_vqmmc;
|
||||
}
|
||||
host->vqmmc_enabled = 1;
|
||||
host->vqmmc_enabled = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -256,7 +256,7 @@ static int omap_hsmmc_disable_supply(struct mmc_host *mmc)
|
||||
dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n");
|
||||
return ret;
|
||||
}
|
||||
host->vqmmc_enabled = 0;
|
||||
host->vqmmc_enabled = false;
|
||||
}
|
||||
|
||||
if (!IS_ERR(mmc->supply.vmmc)) {
|
||||
@ -285,22 +285,22 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on)
|
||||
return 0;
|
||||
|
||||
if (power_on) {
|
||||
if (host->pbias_enabled == 0) {
|
||||
if (!host->pbias_enabled) {
|
||||
ret = regulator_enable(host->pbias);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "pbias reg enable fail\n");
|
||||
return ret;
|
||||
}
|
||||
host->pbias_enabled = 1;
|
||||
host->pbias_enabled = true;
|
||||
}
|
||||
} else {
|
||||
if (host->pbias_enabled == 1) {
|
||||
if (host->pbias_enabled) {
|
||||
ret = regulator_disable(host->pbias);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "pbias reg disable fail\n");
|
||||
return ret;
|
||||
}
|
||||
host->pbias_enabled = 0;
|
||||
host->pbias_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1861,8 +1861,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
host->base = base + pdata->reg_offset;
|
||||
host->power_mode = MMC_POWER_OFF;
|
||||
host->next_data.cookie = 1;
|
||||
host->pbias_enabled = 0;
|
||||
host->vqmmc_enabled = 0;
|
||||
host->pbias_enabled = false;
|
||||
host->vqmmc_enabled = false;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
|
@ -640,7 +640,7 @@ static int owl_mmc_probe(struct platform_device *pdev)
|
||||
owl_host->irq = platform_get_irq(pdev, 0);
|
||||
if (owl_host->irq < 0) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_host;
|
||||
goto err_release_channel;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, owl_host->irq, owl_irq_handler,
|
||||
@ -648,19 +648,21 @@ static int owl_mmc_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq %d\n",
|
||||
owl_host->irq);
|
||||
goto err_free_host;
|
||||
goto err_release_channel;
|
||||
}
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to add host\n");
|
||||
goto err_free_host;
|
||||
goto err_release_channel;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "Owl MMC Controller Initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_release_channel:
|
||||
dma_release_channel(owl_host->dma);
|
||||
err_free_host:
|
||||
mmc_free_host(mmc);
|
||||
|
||||
@ -674,6 +676,7 @@ static int owl_mmc_remove(struct platform_device *pdev)
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
disable_irq(owl_host->irq);
|
||||
dma_release_channel(owl_host->dma);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
return 0;
|
||||
|
@ -768,10 +768,12 @@ static bool renesas_sdhi_auto_correction(struct tmio_mmc_host *host)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
|
||||
static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
|
||||
bool ret = false;
|
||||
|
||||
/*
|
||||
* Skip checking SCC errors when running on 4 taps in HS400 mode as
|
||||
@ -785,11 +787,19 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
|
||||
if (mmc_doing_tune(host->mmc))
|
||||
return false;
|
||||
|
||||
if (((mrq->cmd->error == -ETIMEDOUT) ||
|
||||
(mrq->data && mrq->data->error == -ETIMEDOUT)) &&
|
||||
((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
|
||||
(host->ops.get_cd && host->ops.get_cd(host->mmc))))
|
||||
ret |= true;
|
||||
|
||||
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
|
||||
SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN)
|
||||
return renesas_sdhi_auto_correction(host);
|
||||
ret |= renesas_sdhi_auto_correction(host);
|
||||
else
|
||||
ret |= renesas_sdhi_manual_correction(host, use_4tap);
|
||||
|
||||
return renesas_sdhi_manual_correction(host, use_4tap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
|
||||
|
@ -56,6 +56,12 @@
|
||||
#define INFO2_DTRANERR1 BIT(17)
|
||||
#define INFO2_DTRANERR0 BIT(16)
|
||||
|
||||
enum renesas_sdhi_dma_cookie {
|
||||
COOKIE_UNMAPPED,
|
||||
COOKIE_PRE_MAPPED,
|
||||
COOKIE_MAPPED,
|
||||
};
|
||||
|
||||
/*
|
||||
* Specification of this driver:
|
||||
* - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
|
||||
@ -172,6 +178,50 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) {
|
||||
tasklet_schedule(&priv->dma_priv.dma_complete);
|
||||
}
|
||||
|
||||
/*
|
||||
* renesas_sdhi_internal_dmac_map() will be called with two difference
|
||||
* sg pointers in two mmc_data by .pre_req(), but tmio host can have a single
|
||||
* sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg
|
||||
* pointer in a mmc_data instead of host->sg_ptr.
|
||||
*/
|
||||
static void
|
||||
renesas_sdhi_internal_dmac_unmap(struct tmio_mmc_host *host,
|
||||
struct mmc_data *data,
|
||||
enum renesas_sdhi_dma_cookie cookie)
|
||||
{
|
||||
bool unmap = cookie == COOKIE_UNMAPPED ? (data->host_cookie != cookie) :
|
||||
(data->host_cookie == cookie);
|
||||
|
||||
if (unmap) {
|
||||
dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data));
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host,
|
||||
struct mmc_data *data,
|
||||
enum renesas_sdhi_dma_cookie cookie)
|
||||
{
|
||||
if (data->host_cookie == COOKIE_PRE_MAPPED)
|
||||
return true;
|
||||
|
||||
if (!dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data)))
|
||||
return false;
|
||||
|
||||
data->host_cookie = cookie;
|
||||
|
||||
/* This DMAC cannot handle if buffer is not 128-bytes alignment */
|
||||
if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) {
|
||||
renesas_sdhi_internal_dmac_unmap(host, data, cookie);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
@ -182,14 +232,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
|
||||
if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags))
|
||||
dtran_mode |= DTRAN_MODE_ADDR_MODE;
|
||||
|
||||
if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
|
||||
mmc_get_dma_dir(data)))
|
||||
if (!renesas_sdhi_internal_dmac_map(host, data, COOKIE_MAPPED))
|
||||
goto force_pio;
|
||||
|
||||
/* This DMAC cannot handle if buffer is not 8-bytes alignment */
|
||||
if (!IS_ALIGNED(sg_dma_address(sg), 8))
|
||||
goto force_pio_with_unmap;
|
||||
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
|
||||
if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
|
||||
@ -212,7 +257,7 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
|
||||
return;
|
||||
|
||||
force_pio_with_unmap:
|
||||
dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data));
|
||||
renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED);
|
||||
|
||||
force_pio:
|
||||
renesas_sdhi_internal_dmac_enable_dma(host, false);
|
||||
@ -245,7 +290,7 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
|
||||
dir = DMA_TO_DEVICE;
|
||||
|
||||
renesas_sdhi_internal_dmac_enable_dma(host, false);
|
||||
dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
|
||||
renesas_sdhi_internal_dmac_unmap(host, host->data, COOKIE_MAPPED);
|
||||
|
||||
if (dir == DMA_FROM_DEVICE)
|
||||
clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
|
||||
@ -274,6 +319,32 @@ static void renesas_sdhi_internal_dmac_end_dma(struct tmio_mmc_host *host)
|
||||
renesas_sdhi_internal_dmac_complete(host);
|
||||
}
|
||||
|
||||
static void renesas_sdhi_internal_dmac_post_req(struct mmc_host *mmc,
|
||||
struct mmc_request *mrq,
|
||||
int err)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED);
|
||||
}
|
||||
|
||||
static void renesas_sdhi_internal_dmac_pre_req(struct mmc_host *mmc,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
renesas_sdhi_internal_dmac_map(host, data, COOKIE_PRE_MAPPED);
|
||||
}
|
||||
|
||||
static void
|
||||
renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
|
||||
struct tmio_mmc_data *pdata)
|
||||
@ -295,6 +366,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
|
||||
tasklet_init(&host->dma_issue,
|
||||
renesas_sdhi_internal_dmac_issue_tasklet_fn,
|
||||
(unsigned long)host);
|
||||
|
||||
/* Add pre_req and post_req */
|
||||
host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;
|
||||
host->ops.post_req = renesas_sdhi_internal_dmac_post_req;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -906,6 +906,8 @@ static int sd_power_on(struct realtek_pci_sdmmc *host)
|
||||
if (host->power_state == SDMMC_POWER_ON)
|
||||
return 0;
|
||||
|
||||
msleep(100);
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
|
||||
@ -1425,7 +1427,8 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
||||
if (pcr->rtd3_en)
|
||||
mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM;
|
||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
|
||||
MMC_CAP2_NO_SDIO;
|
||||
mmc->max_current_330 = 400;
|
||||
mmc->max_current_180 = 800;
|
||||
mmc->ops = &realtek_pci_sdmmc_ops;
|
||||
|
@ -540,9 +540,9 @@ static void do_pio_write(struct s3cmci_host *host)
|
||||
enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
|
||||
}
|
||||
|
||||
static void pio_tasklet(unsigned long data)
|
||||
static void pio_tasklet(struct tasklet_struct *t)
|
||||
{
|
||||
struct s3cmci_host *host = (struct s3cmci_host *) data;
|
||||
struct s3cmci_host *host = from_tasklet(host, t, pio_tasklet);
|
||||
|
||||
s3cmci_disable_irq(host, true);
|
||||
|
||||
@ -1532,7 +1532,7 @@ static int s3cmci_probe(struct platform_device *pdev)
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
|
||||
spin_lock_init(&host->complete_lock);
|
||||
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
|
||||
tasklet_setup(&host->pio_tasklet, pio_tasklet);
|
||||
|
||||
if (host->is2440) {
|
||||
host->sdiimsk = S3C2440_SDIIMSK;
|
||||
|
@ -1666,9 +1666,10 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
int dead;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
|
@ -296,9 +296,27 @@ static const struct of_device_id sdhci_iproc_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/*
|
||||
* This is a duplicate of bcm2835_(pltfrm_)data without caps quirks
|
||||
* which are provided by the ACPI table.
|
||||
*/
|
||||
static const struct sdhci_pltfm_data sdhci_bcm_arasan_data = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
.ops = &sdhci_iproc_32only_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_iproc_data bcm_arasan_data = {
|
||||
.pdata = &sdhci_bcm_arasan_data,
|
||||
};
|
||||
|
||||
static const struct acpi_device_id sdhci_iproc_acpi_ids[] = {
|
||||
{ .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data },
|
||||
{ .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data },
|
||||
{ .id = "BCM2847", .driver_data = (kernel_ulong_t)&bcm_arasan_data },
|
||||
{ .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
@ -255,10 +256,12 @@ struct sdhci_msm_variant_info {
|
||||
struct sdhci_msm_host {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *core_mem; /* MSM SDCC mapped address */
|
||||
void __iomem *ice_mem; /* MSM ICE mapped address (if available) */
|
||||
int pwr_irq; /* power irq */
|
||||
struct clk *bus_clk; /* SDHC bus voter clock */
|
||||
struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/
|
||||
struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
|
||||
/* core, iface, cal, sleep, and ice clocks */
|
||||
struct clk_bulk_data bulk_clks[5];
|
||||
unsigned long clk_rate;
|
||||
struct mmc_host *mmc;
|
||||
struct opp_table *opp_table;
|
||||
@ -327,8 +330,7 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val,
|
||||
writel_relaxed(val, host->ioaddr + offset);
|
||||
}
|
||||
|
||||
static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
/*
|
||||
@ -341,8 +343,8 @@ static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||
ios.timing == MMC_TIMING_MMC_DDR52 ||
|
||||
ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
host->flags & SDHCI_HS400_TUNING)
|
||||
clock *= 2;
|
||||
return clock;
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||
@ -352,20 +354,36 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_ios curr_ios = host->mmc->ios;
|
||||
struct clk *core_clk = msm_host->bulk_clks[0].clk;
|
||||
unsigned long achieved_rate;
|
||||
unsigned int desired_rate;
|
||||
unsigned int mult;
|
||||
int rc;
|
||||
|
||||
clock = msm_get_clock_rate_for_bus_mode(host, clock);
|
||||
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock);
|
||||
mult = msm_get_clock_mult_for_bus_mode(host);
|
||||
desired_rate = clock * mult;
|
||||
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
|
||||
if (rc) {
|
||||
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
||||
mmc_hostname(host->mmc), clock,
|
||||
curr_ios.timing);
|
||||
mmc_hostname(host->mmc), desired_rate, curr_ios.timing);
|
||||
return;
|
||||
}
|
||||
msm_host->clk_rate = clock;
|
||||
|
||||
/*
|
||||
* Qualcomm clock drivers by default round clock _up_ if they can't
|
||||
* make the requested rate. This is not good for SD. Yell if we
|
||||
* encounter it.
|
||||
*/
|
||||
achieved_rate = clk_get_rate(core_clk);
|
||||
if (achieved_rate > desired_rate)
|
||||
pr_warn("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n",
|
||||
mmc_hostname(host->mmc), desired_rate, achieved_rate);
|
||||
host->mmc->actual_clock = achieved_rate / mult;
|
||||
|
||||
/* Stash the rate we requested to use in sdhci_msm_runtime_resume() */
|
||||
msm_host->clk_rate = desired_rate;
|
||||
|
||||
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
|
||||
mmc_hostname(host->mmc), clk_get_rate(core_clk),
|
||||
curr_ios.timing);
|
||||
mmc_hostname(host->mmc), achieved_rate, curr_ios.timing);
|
||||
}
|
||||
|
||||
/* Platform specific tuning */
|
||||
@ -1744,13 +1762,6 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
|
||||
static void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
u16 clk;
|
||||
/*
|
||||
* Keep actual_clock as zero -
|
||||
* - since there is no divider used so no need of having actual_clock.
|
||||
* - MSM controller uses SDCLK for data timeout calculation. If
|
||||
* actual_clock is zero, host->clock is taken for calculation.
|
||||
*/
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
@ -1773,7 +1784,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (!clock) {
|
||||
msm_host->clk_rate = clock;
|
||||
host->mmc->actual_clock = msm_host->clk_rate = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1784,6 +1795,246 @@ out:
|
||||
__sdhci_msm_set_clock(host, clock);
|
||||
}
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Inline Crypto Engine (ICE) support *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
|
||||
#define AES_256_XTS_KEY_SIZE 64
|
||||
|
||||
/* QCOM ICE registers */
|
||||
|
||||
#define QCOM_ICE_REG_VERSION 0x0008
|
||||
|
||||
#define QCOM_ICE_REG_FUSE_SETTING 0x0010
|
||||
#define QCOM_ICE_FUSE_SETTING_MASK 0x1
|
||||
#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2
|
||||
#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4
|
||||
|
||||
#define QCOM_ICE_REG_BIST_STATUS 0x0070
|
||||
#define QCOM_ICE_BIST_STATUS_MASK 0xF0000000
|
||||
|
||||
#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000
|
||||
|
||||
#define sdhci_msm_ice_writel(host, val, reg) \
|
||||
writel((val), (host)->ice_mem + (reg))
|
||||
#define sdhci_msm_ice_readl(host, reg) \
|
||||
readl((host)->ice_mem + (reg))
|
||||
|
||||
static bool sdhci_msm_ice_supported(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
struct device *dev = mmc_dev(msm_host->mmc);
|
||||
u32 regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_VERSION);
|
||||
int major = regval >> 24;
|
||||
int minor = (regval >> 16) & 0xFF;
|
||||
int step = regval & 0xFFFF;
|
||||
|
||||
/* For now this driver only supports ICE version 3. */
|
||||
if (major != 3) {
|
||||
dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
|
||||
major, minor, step);
|
||||
return false;
|
||||
}
|
||||
|
||||
dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n",
|
||||
major, minor, step);
|
||||
|
||||
/* If fuses are blown, ICE might not work in the standard way. */
|
||||
regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_FUSE_SETTING);
|
||||
if (regval & (QCOM_ICE_FUSE_SETTING_MASK |
|
||||
QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK |
|
||||
QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
|
||||
dev_warn(dev, "Fuses are blown; ICE is unusable!\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
|
||||
{
|
||||
return devm_clk_get(dev, "ice");
|
||||
}
|
||||
|
||||
static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
|
||||
struct cqhci_host *cq_host)
|
||||
{
|
||||
struct mmc_host *mmc = msm_host->mmc;
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
|
||||
return 0;
|
||||
|
||||
res = platform_get_resource_byname(msm_host->pdev, IORESOURCE_MEM,
|
||||
"ice");
|
||||
if (!res) {
|
||||
dev_warn(dev, "ICE registers not found\n");
|
||||
goto disable;
|
||||
}
|
||||
|
||||
if (!qcom_scm_ice_available()) {
|
||||
dev_warn(dev, "ICE SCM interface not found\n");
|
||||
goto disable;
|
||||
}
|
||||
|
||||
msm_host->ice_mem = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(msm_host->ice_mem)) {
|
||||
err = PTR_ERR(msm_host->ice_mem);
|
||||
dev_err(dev, "Failed to map ICE registers; err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!sdhci_msm_ice_supported(msm_host))
|
||||
goto disable;
|
||||
|
||||
mmc->caps2 |= MMC_CAP2_CRYPTO;
|
||||
return 0;
|
||||
|
||||
disable:
|
||||
dev_warn(dev, "Disabling inline encryption support\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_msm_ice_low_power_mode_enable(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
|
||||
/*
|
||||
* Enable low power mode sequence
|
||||
* [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0
|
||||
*/
|
||||
regval |= 0x7000;
|
||||
sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
|
||||
}
|
||||
|
||||
static void sdhci_msm_ice_optimization_enable(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
/* ICE Optimizations Enable Sequence */
|
||||
regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
|
||||
regval |= 0xD807100;
|
||||
/* ICE HPG requires delay before writing */
|
||||
udelay(5);
|
||||
sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait until the ICE BIST (built-in self-test) has completed.
|
||||
*
|
||||
* This may be necessary before ICE can be used.
|
||||
*
|
||||
* Note that we don't really care whether the BIST passed or failed; we really
|
||||
* just want to make sure that it isn't still running. This is because (a) the
|
||||
* BIST is a FIPS compliance thing that never fails in practice, (b) ICE is
|
||||
* documented to reject crypto requests if the BIST fails, so we needn't do it
|
||||
* in software too, and (c) properly testing storage encryption requires testing
|
||||
* the full storage stack anyway, and not relying on hardware-level self-tests.
|
||||
*/
|
||||
static int sdhci_msm_ice_wait_bist_status(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = readl_poll_timeout(msm_host->ice_mem + QCOM_ICE_REG_BIST_STATUS,
|
||||
regval, !(regval & QCOM_ICE_BIST_STATUS_MASK),
|
||||
50, 5000);
|
||||
if (err)
|
||||
dev_err(mmc_dev(msm_host->mmc),
|
||||
"Timed out waiting for ICE self-test to complete\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO))
|
||||
return;
|
||||
sdhci_msm_ice_low_power_mode_enable(msm_host);
|
||||
sdhci_msm_ice_optimization_enable(msm_host);
|
||||
sdhci_msm_ice_wait_bist_status(msm_host);
|
||||
}
|
||||
|
||||
static int __maybe_unused sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO))
|
||||
return 0;
|
||||
return sdhci_msm_ice_wait_bist_status(msm_host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires
|
||||
* vendor-specific SCM calls for this; it doesn't support the standard way.
|
||||
*/
|
||||
static int sdhci_msm_program_key(struct cqhci_host *cq_host,
|
||||
const union cqhci_crypto_cfg_entry *cfg,
|
||||
int slot)
|
||||
{
|
||||
struct device *dev = mmc_dev(cq_host->mmc);
|
||||
union cqhci_crypto_cap_entry cap;
|
||||
union {
|
||||
u8 bytes[AES_256_XTS_KEY_SIZE];
|
||||
u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
|
||||
} key;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
if (!(cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE))
|
||||
return qcom_scm_ice_invalidate_key(slot);
|
||||
|
||||
/* Only AES-256-XTS has been tested so far. */
|
||||
cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx];
|
||||
if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS ||
|
||||
cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256) {
|
||||
dev_err_ratelimited(dev,
|
||||
"Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
|
||||
cap.algorithm_id, cap.key_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE);
|
||||
|
||||
/*
|
||||
* The SCM call byte-swaps the 32-bit words of the key. So we have to
|
||||
* do the same, in order for the final key be correct.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(key.words); i++)
|
||||
__cpu_to_be32s(&key.words[i]);
|
||||
|
||||
err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
|
||||
QCOM_SCM_ICE_CIPHER_AES_256_XTS,
|
||||
cfg->data_unit_size);
|
||||
memzero_explicit(&key, sizeof(key));
|
||||
return err;
|
||||
}
|
||||
#else /* CONFIG_MMC_CRYPTO */
|
||||
static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
|
||||
struct cqhci_host *cq_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !CONFIG_MMC_CRYPTO */
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* MSM Command Queue Engine (CQE) *
|
||||
@ -1802,6 +2053,16 @@ static u32 sdhci_msm_cqe_irq(struct sdhci_host *host, u32 intmask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_msm_cqe_enable(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhci_cqe_enable(mmc);
|
||||
sdhci_msm_ice_enable(msm_host);
|
||||
}
|
||||
|
||||
static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
@ -1834,8 +2095,11 @@ static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
|
||||
}
|
||||
|
||||
static const struct cqhci_host_ops sdhci_msm_cqhci_ops = {
|
||||
.enable = sdhci_cqe_enable,
|
||||
.enable = sdhci_msm_cqe_enable,
|
||||
.disable = sdhci_msm_cqe_disable,
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
.program_key = sdhci_msm_program_key,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
|
||||
@ -1871,6 +2135,10 @@ static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
|
||||
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
|
||||
ret = sdhci_msm_ice_init(msm_host, cq_host);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
ret = cqhci_init(cq_host, host->mmc, dma64);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n",
|
||||
@ -2311,6 +2579,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
clk = NULL;
|
||||
msm_host->bulk_clks[3].clk = clk;
|
||||
|
||||
clk = sdhci_msm_ice_get_clk(&pdev->dev);
|
||||
if (IS_ERR(clk))
|
||||
clk = NULL;
|
||||
msm_host->bulk_clks[4].clk = clk;
|
||||
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
|
||||
msm_host->bulk_clks);
|
||||
if (ret)
|
||||
@ -2524,12 +2797,15 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
|
||||
* Whenever core-clock is gated dynamically, it's needed to
|
||||
* restore the SDR DLL settings when the clock is ungated.
|
||||
*/
|
||||
if (msm_host->restore_dll_config && msm_host->clk_rate)
|
||||
if (msm_host->restore_dll_config && msm_host->clk_rate) {
|
||||
ret = sdhci_msm_restore_sdr_dll_config(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_pm_opp_set_rate(dev, msm_host->clk_rate);
|
||||
|
||||
return ret;
|
||||
return sdhci_msm_ice_resume(msm_host);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sdhci_msm_pm_ops = {
|
||||
|
@ -1380,26 +1380,25 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
||||
|
||||
/**
|
||||
* sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support)
|
||||
* @host: The sdhci_host
|
||||
* @value: The value to write
|
||||
*
|
||||
* This should be set based on the System Address Bus.
|
||||
* 0: the Core supports only 32-bit System Address Bus.
|
||||
* 1: the Core supports 64-bit System Address Bus.
|
||||
*
|
||||
* NOTES:
|
||||
* - For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
|
||||
* Keem Bay does not support 64-bit access.
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
* @value: The value to write
|
||||
* NOTE:
|
||||
* For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
|
||||
* Keem Bay does not support 64-bit access.
|
||||
*/
|
||||
static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
|
||||
sdhci_arasan->soc_ctl_map;
|
||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
||||
|
||||
/* Having a map is optional */
|
||||
soc_ctl_map = sdhci_arasan->soc_ctl_map;
|
||||
if (!soc_ctl_map)
|
||||
return;
|
||||
|
||||
@ -1508,17 +1507,16 @@ cleanup:
|
||||
static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *node;
|
||||
struct clk *clk_xin;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sdhci_arasan_data *sdhci_arasan;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct sdhci_arasan_of_data *data;
|
||||
|
||||
match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
|
||||
data = match->data;
|
||||
data = of_device_get_match_data(dev);
|
||||
host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));
|
||||
|
||||
if (IS_ERR(host))
|
||||
@ -1531,42 +1529,41 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
|
||||
sdhci_arasan->clk_ops = data->clk_ops;
|
||||
|
||||
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
|
||||
node = of_parse_phandle(np, "arasan,soc-ctl-syscon", 0);
|
||||
if (node) {
|
||||
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
|
||||
of_node_put(node);
|
||||
|
||||
if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
|
||||
ret = dev_err_probe(&pdev->dev,
|
||||
ret = dev_err_probe(dev,
|
||||
PTR_ERR(sdhci_arasan->soc_ctl_base),
|
||||
"Can't get syscon\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
}
|
||||
|
||||
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
|
||||
sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb");
|
||||
if (IS_ERR(sdhci_arasan->clk_ahb)) {
|
||||
dev_err(&pdev->dev, "clk_ahb clock not found.\n");
|
||||
ret = PTR_ERR(sdhci_arasan->clk_ahb);
|
||||
ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb),
|
||||
"clk_ahb clock not found.\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
|
||||
clk_xin = devm_clk_get(dev, "clk_xin");
|
||||
if (IS_ERR(clk_xin)) {
|
||||
dev_err(&pdev->dev, "clk_xin clock not found.\n");
|
||||
ret = PTR_ERR(clk_xin);
|
||||
ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
|
||||
dev_err(dev, "Unable to enable AHB clock.\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_xin);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable SD clock.\n");
|
||||
dev_err(dev, "Unable to enable SD clock.\n");
|
||||
goto clk_dis_ahb;
|
||||
}
|
||||
|
||||
@ -1580,8 +1577,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"rockchip,rk3399-sdhci-5.1"))
|
||||
if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1"))
|
||||
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
||||
|
||||
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
|
||||
@ -1595,7 +1591,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
|
||||
sdhci_arasan_update_baseclkfreq(host);
|
||||
|
||||
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
|
||||
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, dev);
|
||||
if (ret)
|
||||
goto clk_disable_all;
|
||||
|
||||
@ -1604,29 +1600,26 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
arasan_zynqmp_execute_tuning;
|
||||
}
|
||||
|
||||
arasan_dt_parse_clk_phases(&pdev->dev, &sdhci_arasan->clk_data);
|
||||
arasan_dt_parse_clk_phases(dev, &sdhci_arasan->clk_data);
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "parsing dt failed (%d)\n", ret);
|
||||
ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
sdhci_arasan->phy = ERR_PTR(-ENODEV);
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"arasan,sdhci-5.1")) {
|
||||
sdhci_arasan->phy = devm_phy_get(&pdev->dev,
|
||||
"phy_arasan");
|
||||
if (of_device_is_compatible(np, "arasan,sdhci-5.1")) {
|
||||
sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan");
|
||||
if (IS_ERR(sdhci_arasan->phy)) {
|
||||
ret = PTR_ERR(sdhci_arasan->phy);
|
||||
dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
|
||||
ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->phy),
|
||||
"No phy for arasan,sdhci-5.1.\n");
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
ret = phy_init(sdhci_arasan->phy);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "phy_init err.\n");
|
||||
dev_err(dev, "phy_init err.\n");
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
@ -1651,7 +1644,7 @@ err_add_host:
|
||||
if (!IS_ERR(sdhci_arasan->phy))
|
||||
phy_exit(sdhci_arasan->phy);
|
||||
unreg_clk:
|
||||
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||
sdhci_arasan_unregister_sdclk(dev);
|
||||
clk_disable_all:
|
||||
clk_disable_unprepare(clk_xin);
|
||||
clk_dis_ahb:
|
||||
|
105
drivers/mmc/host/sdhci-of-aspeed-test.c
Normal file
105
drivers/mmc/host/sdhci-of-aspeed-test.c
Normal file
@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* Copyright (C) 2020 IBM Corp. */
|
||||
|
||||
#include <kunit/test.h>
|
||||
|
||||
static void aspeed_sdhci_phase_ddr52(struct kunit *test)
|
||||
{
|
||||
int rate = 52000000;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 0));
|
||||
KUNIT_EXPECT_EQ(test, 0,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 1));
|
||||
KUNIT_EXPECT_EQ(test, 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 2));
|
||||
KUNIT_EXPECT_EQ(test, 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 3));
|
||||
KUNIT_EXPECT_EQ(test, 2,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 4));
|
||||
KUNIT_EXPECT_EQ(test, 3,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 5));
|
||||
KUNIT_EXPECT_EQ(test, 14,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 23));
|
||||
KUNIT_EXPECT_EQ(test, 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 24));
|
||||
KUNIT_EXPECT_EQ(test, 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 25));
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 180));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 181));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 182));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 183));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 2,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 184));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 3,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 185));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 203));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 204));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 205));
|
||||
}
|
||||
|
||||
static void aspeed_sdhci_phase_hs200(struct kunit *test)
|
||||
{
|
||||
int rate = 200000000;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 0));
|
||||
KUNIT_EXPECT_EQ(test, 0,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 5));
|
||||
KUNIT_EXPECT_EQ(test, 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 6));
|
||||
KUNIT_EXPECT_EQ(test, 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 7));
|
||||
KUNIT_EXPECT_EQ(test, 14,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 89));
|
||||
KUNIT_EXPECT_EQ(test, 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 90));
|
||||
KUNIT_EXPECT_EQ(test, 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 91));
|
||||
KUNIT_EXPECT_EQ(test, 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 96));
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 180));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 185));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 186));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 187));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 269));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 270));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 271));
|
||||
KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
|
||||
aspeed_sdhci_phase_to_tap(NULL, rate, 276));
|
||||
}
|
||||
|
||||
static struct kunit_case aspeed_sdhci_test_cases[] = {
|
||||
KUNIT_CASE(aspeed_sdhci_phase_ddr52),
|
||||
KUNIT_CASE(aspeed_sdhci_phase_hs200),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite aspeed_sdhci_test_suite = {
|
||||
.name = "sdhci-of-aspeed",
|
||||
.test_cases = aspeed_sdhci_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite *aspeed_sdc_test_suite_array[] = {
|
||||
&aspeed_sdhci_test_suite,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct kunit_suite **aspeed_sdc_test_suites
|
||||
__used __section(".kunit_test_suites") = aspeed_sdc_test_suite_array;
|
@ -6,6 +6,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
@ -16,9 +17,19 @@
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define ASPEED_SDC_INFO 0x00
|
||||
#define ASPEED_SDC_S1MMC8 BIT(25)
|
||||
#define ASPEED_SDC_S0MMC8 BIT(24)
|
||||
#define ASPEED_SDC_INFO 0x00
|
||||
#define ASPEED_SDC_S1_MMC8 BIT(25)
|
||||
#define ASPEED_SDC_S0_MMC8 BIT(24)
|
||||
#define ASPEED_SDC_PHASE 0xf4
|
||||
#define ASPEED_SDC_S1_PHASE_IN GENMASK(25, 21)
|
||||
#define ASPEED_SDC_S0_PHASE_IN GENMASK(20, 16)
|
||||
#define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11)
|
||||
#define ASPEED_SDC_S1_PHASE_IN_EN BIT(10)
|
||||
#define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8)
|
||||
#define ASPEED_SDC_S0_PHASE_OUT GENMASK(7, 3)
|
||||
#define ASPEED_SDC_S0_PHASE_IN_EN BIT(2)
|
||||
#define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0)
|
||||
#define ASPEED_SDC_PHASE_MAX 31
|
||||
|
||||
struct aspeed_sdc {
|
||||
struct clk *clk;
|
||||
@ -28,9 +39,37 @@ struct aspeed_sdc {
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
struct aspeed_sdhci_tap_param {
|
||||
bool valid;
|
||||
|
||||
#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4)
|
||||
u8 in;
|
||||
u8 out;
|
||||
};
|
||||
|
||||
struct aspeed_sdhci_tap_desc {
|
||||
u32 tap_mask;
|
||||
u32 enable_mask;
|
||||
u8 enable_value;
|
||||
};
|
||||
|
||||
struct aspeed_sdhci_phase_desc {
|
||||
struct aspeed_sdhci_tap_desc in;
|
||||
struct aspeed_sdhci_tap_desc out;
|
||||
};
|
||||
|
||||
struct aspeed_sdhci_pdata {
|
||||
unsigned int clk_div_start;
|
||||
const struct aspeed_sdhci_phase_desc *phase_desc;
|
||||
size_t nr_phase_descs;
|
||||
};
|
||||
|
||||
struct aspeed_sdhci {
|
||||
const struct aspeed_sdhci_pdata *pdata;
|
||||
struct aspeed_sdc *parent;
|
||||
u32 width_mask;
|
||||
struct mmc_clk_phase_map phase_map;
|
||||
const struct aspeed_sdhci_phase_desc *phase_desc;
|
||||
};
|
||||
|
||||
static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
|
||||
@ -50,14 +89,125 @@ static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
|
||||
spin_unlock(&sdc->lock);
|
||||
}
|
||||
|
||||
static u32
|
||||
aspeed_sdc_set_phase_tap(const struct aspeed_sdhci_tap_desc *desc,
|
||||
u8 tap, bool enable, u32 reg)
|
||||
{
|
||||
reg &= ~(desc->enable_mask | desc->tap_mask);
|
||||
if (enable) {
|
||||
reg |= tap << __ffs(desc->tap_mask);
|
||||
reg |= desc->enable_value << __ffs(desc->enable_mask);
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void
|
||||
aspeed_sdc_set_phase_taps(struct aspeed_sdc *sdc,
|
||||
const struct aspeed_sdhci_phase_desc *desc,
|
||||
const struct aspeed_sdhci_tap_param *taps)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
spin_lock(&sdc->lock);
|
||||
reg = readl(sdc->regs + ASPEED_SDC_PHASE);
|
||||
|
||||
reg = aspeed_sdc_set_phase_tap(&desc->in, taps->in, taps->valid, reg);
|
||||
reg = aspeed_sdc_set_phase_tap(&desc->out, taps->out, taps->valid, reg);
|
||||
|
||||
writel(reg, sdc->regs + ASPEED_SDC_PHASE);
|
||||
spin_unlock(&sdc->lock);
|
||||
}
|
||||
|
||||
#define PICOSECONDS_PER_SECOND 1000000000000ULL
|
||||
#define ASPEED_SDHCI_NR_TAPS 15
|
||||
/* Measured value with *handwave* environmentals and static loading */
|
||||
#define ASPEED_SDHCI_MAX_TAP_DELAY_PS 1253
|
||||
static int aspeed_sdhci_phase_to_tap(struct device *dev, unsigned long rate_hz,
|
||||
int phase_deg)
|
||||
{
|
||||
u64 phase_period_ps;
|
||||
u64 prop_delay_ps;
|
||||
u64 clk_period_ps;
|
||||
unsigned int tap;
|
||||
u8 inverted;
|
||||
|
||||
phase_deg %= 360;
|
||||
|
||||
if (phase_deg >= 180) {
|
||||
inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK;
|
||||
phase_deg -= 180;
|
||||
dev_dbg(dev,
|
||||
"Inverting clock to reduce phase correction from %d to %d degrees\n",
|
||||
phase_deg + 180, phase_deg);
|
||||
} else {
|
||||
inverted = 0;
|
||||
}
|
||||
|
||||
prop_delay_ps = ASPEED_SDHCI_MAX_TAP_DELAY_PS / ASPEED_SDHCI_NR_TAPS;
|
||||
clk_period_ps = div_u64(PICOSECONDS_PER_SECOND, (u64)rate_hz);
|
||||
phase_period_ps = div_u64((u64)phase_deg * clk_period_ps, 360ULL);
|
||||
|
||||
tap = div_u64(phase_period_ps, prop_delay_ps);
|
||||
if (tap > ASPEED_SDHCI_NR_TAPS) {
|
||||
dev_warn(dev,
|
||||
"Requested out of range phase tap %d for %d degrees of phase compensation at %luHz, clamping to tap %d\n",
|
||||
tap, phase_deg, rate_hz, ASPEED_SDHCI_NR_TAPS);
|
||||
tap = ASPEED_SDHCI_NR_TAPS;
|
||||
}
|
||||
|
||||
return inverted | tap;
|
||||
}
|
||||
|
||||
static void
|
||||
aspeed_sdhci_phases_to_taps(struct device *dev, unsigned long rate,
|
||||
const struct mmc_clk_phase *phases,
|
||||
struct aspeed_sdhci_tap_param *taps)
|
||||
{
|
||||
taps->valid = phases->valid;
|
||||
|
||||
if (!phases->valid)
|
||||
return;
|
||||
|
||||
taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg);
|
||||
taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg);
|
||||
}
|
||||
|
||||
static void
|
||||
aspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate)
|
||||
{
|
||||
struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps;
|
||||
struct mmc_clk_phase *params;
|
||||
struct aspeed_sdhci *sdhci;
|
||||
struct device *dev;
|
||||
|
||||
dev = host->mmc->parent;
|
||||
sdhci = sdhci_pltfm_priv(sdhci_priv(host));
|
||||
|
||||
if (!sdhci->phase_desc)
|
||||
return;
|
||||
|
||||
params = &sdhci->phase_map.phase[host->timing];
|
||||
aspeed_sdhci_phases_to_taps(dev, rate, params, taps);
|
||||
aspeed_sdc_set_phase_taps(sdhci->parent, sdhci->phase_desc, taps);
|
||||
dev_dbg(dev,
|
||||
"Using taps [%d, %d] for [%d, %d] degrees of phase correction at %luHz (%d)\n",
|
||||
taps->in & ASPEED_SDHCI_NR_TAPS,
|
||||
taps->out & ASPEED_SDHCI_NR_TAPS,
|
||||
params->in_deg, params->out_deg, rate, host->timing);
|
||||
}
|
||||
|
||||
static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
unsigned long parent;
|
||||
unsigned long parent, bus;
|
||||
struct aspeed_sdhci *sdhci;
|
||||
int div;
|
||||
u16 clk;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
sdhci = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
parent = clk_get_rate(pltfm_host->clk);
|
||||
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
@ -68,14 +218,34 @@ static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
if (WARN_ON(clock > host->max_clk))
|
||||
clock = host->max_clk;
|
||||
|
||||
for (div = 2; div < 256; div *= 2) {
|
||||
if ((parent / div) <= clock)
|
||||
/*
|
||||
* Regarding the AST2600:
|
||||
*
|
||||
* If (EMMC12C[7:6], EMMC12C[15:8] == 0) then
|
||||
* period of SDCLK = period of SDMCLK.
|
||||
*
|
||||
* If (EMMC12C[7:6], EMMC12C[15:8] != 0) then
|
||||
* period of SDCLK = period of SDMCLK * 2 * (EMMC12C[7:6], EMMC[15:8])
|
||||
*
|
||||
* If you keep EMMC12C[7:6] = 0 and EMMC12C[15:8] as one-hot,
|
||||
* 0x1/0x2/0x4/etc, you will find it is compatible to AST2400 or AST2500
|
||||
*
|
||||
* Keep the one-hot behaviour for backwards compatibility except for
|
||||
* supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture
|
||||
* the 0-value capability in clk_div_start.
|
||||
*/
|
||||
for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) {
|
||||
bus = parent / div;
|
||||
if (bus <= clock)
|
||||
break;
|
||||
}
|
||||
|
||||
div >>= 1;
|
||||
|
||||
clk = div << SDHCI_DIVIDER_SHIFT;
|
||||
|
||||
aspeed_sdhci_configure_phase(host, bus);
|
||||
|
||||
sdhci_enable_clk(host, clk);
|
||||
}
|
||||
|
||||
@ -157,6 +327,7 @@ static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
|
||||
|
||||
static int aspeed_sdhci_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct aspeed_sdhci_pdata *aspeed_pdata;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct aspeed_sdhci *dev;
|
||||
struct sdhci_host *host;
|
||||
@ -164,12 +335,19 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
|
||||
int slot;
|
||||
int ret;
|
||||
|
||||
aspeed_pdata = of_device_get_match_data(&pdev->dev);
|
||||
if (!aspeed_pdata) {
|
||||
dev_err(&pdev->dev, "Missing platform configuration data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
dev = sdhci_pltfm_priv(pltfm_host);
|
||||
dev->pdata = aspeed_pdata;
|
||||
dev->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@ -180,8 +358,17 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
|
||||
else if (slot >= 2)
|
||||
return -EINVAL;
|
||||
|
||||
dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
|
||||
dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
|
||||
if (slot < dev->pdata->nr_phase_descs) {
|
||||
dev->phase_desc = &dev->pdata->phase_desc[slot];
|
||||
} else {
|
||||
dev_info(&pdev->dev,
|
||||
"Phase control not supported for slot %d\n", slot);
|
||||
dev->phase_desc = NULL;
|
||||
}
|
||||
|
||||
dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8;
|
||||
|
||||
dev_info(&pdev->dev, "Configured for slot %d\n", slot);
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
@ -199,6 +386,9 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
|
||||
if (dev->phase_desc)
|
||||
mmc_of_parse_clk_phase(host->mmc, &dev->phase_map);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
@ -230,10 +420,49 @@ static int aspeed_sdhci_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = {
|
||||
.clk_div_start = 2,
|
||||
};
|
||||
|
||||
static const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = {
|
||||
/* SDHCI/Slot 0 */
|
||||
[0] = {
|
||||
.in = {
|
||||
.tap_mask = ASPEED_SDC_S0_PHASE_IN,
|
||||
.enable_mask = ASPEED_SDC_S0_PHASE_IN_EN,
|
||||
.enable_value = 1,
|
||||
},
|
||||
.out = {
|
||||
.tap_mask = ASPEED_SDC_S0_PHASE_OUT,
|
||||
.enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN,
|
||||
.enable_value = 3,
|
||||
},
|
||||
},
|
||||
/* SDHCI/Slot 1 */
|
||||
[1] = {
|
||||
.in = {
|
||||
.tap_mask = ASPEED_SDC_S1_PHASE_IN,
|
||||
.enable_mask = ASPEED_SDC_S1_PHASE_IN_EN,
|
||||
.enable_value = 1,
|
||||
},
|
||||
.out = {
|
||||
.tap_mask = ASPEED_SDC_S1_PHASE_OUT,
|
||||
.enable_mask = ASPEED_SDC_S1_PHASE_OUT_EN,
|
||||
.enable_value = 3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = {
|
||||
.clk_div_start = 1,
|
||||
.phase_desc = ast2600_sdhci_phase,
|
||||
.nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase),
|
||||
};
|
||||
|
||||
static const struct of_device_id aspeed_sdhci_of_match[] = {
|
||||
{ .compatible = "aspeed,ast2400-sdhci", },
|
||||
{ .compatible = "aspeed,ast2500-sdhci", },
|
||||
{ .compatible = "aspeed,ast2600-sdhci", },
|
||||
{ .compatible = "aspeed,ast2400-sdhci", .data = &ast2400_sdhci_pdata, },
|
||||
{ .compatible = "aspeed,ast2500-sdhci", .data = &ast2400_sdhci_pdata, },
|
||||
{ .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -327,6 +556,29 @@ static struct platform_driver aspeed_sdc_driver = {
|
||||
.remove = aspeed_sdc_remove,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST)
|
||||
#include "sdhci-of-aspeed-test.c"
|
||||
|
||||
static inline int aspeed_sdc_tests_init(void)
|
||||
{
|
||||
return __kunit_test_suites_init(aspeed_sdc_test_suites);
|
||||
}
|
||||
|
||||
static inline void aspeed_sdc_tests_exit(void)
|
||||
{
|
||||
__kunit_test_suites_exit(aspeed_sdc_test_suites);
|
||||
}
|
||||
#else
|
||||
static inline int aspeed_sdc_tests_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void aspeed_sdc_tests_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init aspeed_sdc_init(void)
|
||||
{
|
||||
int rc;
|
||||
@ -337,7 +589,18 @@ static int __init aspeed_sdc_init(void)
|
||||
|
||||
rc = platform_driver_register(&aspeed_sdc_driver);
|
||||
if (rc < 0)
|
||||
platform_driver_unregister(&aspeed_sdhci_driver);
|
||||
goto cleanup_sdhci;
|
||||
|
||||
rc = aspeed_sdc_tests_init();
|
||||
if (rc < 0) {
|
||||
platform_driver_unregister(&aspeed_sdc_driver);
|
||||
goto cleanup_sdhci;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_sdhci:
|
||||
platform_driver_unregister(&aspeed_sdhci_driver);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -345,6 +608,8 @@ module_init(aspeed_sdc_init);
|
||||
|
||||
static void __exit aspeed_sdc_exit(void)
|
||||
{
|
||||
aspeed_sdc_tests_exit();
|
||||
|
||||
platform_driver_unregister(&aspeed_sdc_driver);
|
||||
platform_driver_unregister(&aspeed_sdhci_driver);
|
||||
}
|
||||
|
@ -112,6 +112,7 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
|
||||
.ops = &sdhci_dwcmshc_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static int dwcmshc_probe(struct platform_device *pdev)
|
||||
|
@ -84,12 +84,21 @@
|
||||
#define GLI_9763E_VHS_REV_W 0x2
|
||||
#define PCIE_GLI_9763E_MB 0x888
|
||||
#define GLI_9763E_MB_CMDQ_OFF BIT(19)
|
||||
#define GLI_9763E_MB_ERP_ON BIT(7)
|
||||
#define PCIE_GLI_9763E_SCR 0x8E0
|
||||
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
||||
|
||||
#define PCIE_GLI_9763E_CFG2 0x8A4
|
||||
#define GLI_9763E_CFG2_L1DLY GENMASK(28, 19)
|
||||
#define GLI_9763E_CFG2_L1DLY_MAX 0x3FF
|
||||
|
||||
#define PCIE_GLI_9763E_MMC_CTRL 0x960
|
||||
#define GLI_9763E_HS400_SLOW BIT(3)
|
||||
|
||||
#define PCIE_GLI_9763E_CLKRXDLY 0x934
|
||||
#define GLI_9763E_HS400_RXDLY GENMASK(31, 28)
|
||||
#define GLI_9763E_HS400_RXDLY_5 0x5
|
||||
|
||||
#define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200
|
||||
#define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
|
||||
SDHCI_TRNS_BLK_CNT_EN | \
|
||||
@ -791,6 +800,17 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
||||
value &= ~GLI_9763E_HS400_SLOW;
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_MMC_CTRL, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG2, &value);
|
||||
value &= ~GLI_9763E_CFG2_L1DLY;
|
||||
/* set ASPM L1 entry delay to 260us */
|
||||
value |= FIELD_PREP(GLI_9763E_CFG2_L1DLY, GLI_9763E_CFG2_L1DLY_MAX);
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG2, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, &value);
|
||||
value &= ~GLI_9763E_HS400_RXDLY;
|
||||
value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5);
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
||||
value &= ~GLI_9763E_VHS_REV;
|
||||
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
|
||||
@ -814,7 +834,8 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value);
|
||||
if (!(value & GLI_9763E_MB_CMDQ_OFF))
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
if (value & GLI_9763E_MB_ERP_ON)
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
|
||||
gli_pcie_enable_msi(slot);
|
||||
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||
|
@ -33,6 +33,8 @@
|
||||
#define O2_SD_ADMA2 0xE7
|
||||
#define O2_SD_INF_MOD 0xF1
|
||||
#define O2_SD_MISC_CTRL4 0xFC
|
||||
#define O2_SD_MISC_CTRL 0x1C0
|
||||
#define O2_SD_PWR_FORCE_L0 0x0002
|
||||
#define O2_SD_TUNING_CTRL 0x300
|
||||
#define O2_SD_PLL_SETTING 0x304
|
||||
#define O2_SD_MISC_SETTING 0x308
|
||||
@ -300,6 +302,8 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
int current_bus_width = 0;
|
||||
u32 scratch32 = 0;
|
||||
u16 scratch = 0;
|
||||
|
||||
/*
|
||||
* This handler only implements the eMMC tuning that is specific to
|
||||
@ -312,6 +316,17 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
|
||||
(opcode != MMC_SEND_TUNING_BLOCK)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Force power mode enter L0 */
|
||||
scratch = sdhci_readw(host, O2_SD_MISC_CTRL);
|
||||
scratch |= O2_SD_PWR_FORCE_L0;
|
||||
sdhci_writew(host, scratch, O2_SD_MISC_CTRL);
|
||||
|
||||
/* wait DLL lock, timeout value 5ms */
|
||||
if (readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host,
|
||||
scratch32, (scratch32 & O2_DLL_LOCK_STATUS), 1, 5000))
|
||||
pr_warn("%s: DLL can't lock in 5ms after force L0 during tuning.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
/*
|
||||
* Judge the tuning reason, whether caused by dll shift
|
||||
* If cause by dll shift, should call sdhci_o2_dll_recovery
|
||||
@ -344,6 +359,11 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
sdhci_set_bus_width(host, current_bus_width);
|
||||
}
|
||||
|
||||
/* Cancel force power mode enter L0 */
|
||||
scratch = sdhci_readw(host, O2_SD_MISC_CTRL);
|
||||
scratch &= ~(O2_SD_PWR_FORCE_L0);
|
||||
sdhci_writew(host, scratch, O2_SD_MISC_CTRL);
|
||||
|
||||
sdhci_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_reset(host, SDHCI_RESET_DATA);
|
||||
|
||||
|
@ -1,235 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* SDHCI support for SiRF primaII and marco SoCs
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHCI_CLK_DELAY_SETTING 0x4C
|
||||
#define SDHCI_SIRF_8BITBUS BIT(3)
|
||||
#define SIRF_TUNING_COUNT 16384
|
||||
|
||||
static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS);
|
||||
|
||||
/*
|
||||
* CSR atlas7 and prima2 SD host version is not 3.0
|
||||
* 8bit-width enable bit of CSR SD hosts is 3,
|
||||
* while stardard hosts use bit 5
|
||||
*/
|
||||
if (width == MMC_BUS_WIDTH_8)
|
||||
ctrl |= SDHCI_SIRF_8BITBUS;
|
||||
else if (width == MMC_BUS_WIDTH_4)
|
||||
ctrl |= SDHCI_CTRL_4BITBUS;
|
||||
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 val = readl(host->ioaddr + reg);
|
||||
|
||||
if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
|
||||
(host->mmc->caps & MMC_CAP_UHS_SDR50))) {
|
||||
/* fake CAP_1 register */
|
||||
val = SDHCI_SUPPORT_DDR50 |
|
||||
SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
|
||||
}
|
||||
|
||||
if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
|
||||
u32 prss = val;
|
||||
/* fake chips as V3.0 host conreoller */
|
||||
prss &= ~(0xFF << 16);
|
||||
val = prss | (SDHCI_SPEC_300 << 16);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u16 ret = 0;
|
||||
|
||||
ret = readw(host->ioaddr + reg);
|
||||
|
||||
if (unlikely(reg == SDHCI_HOST_VERSION)) {
|
||||
ret = readw(host->ioaddr + SDHCI_HOST_VERSION);
|
||||
ret |= SDHCI_SPEC_300;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
int tuning_seq_cnt = 3;
|
||||
int phase;
|
||||
u8 tuned_phase_cnt = 0;
|
||||
int rc = 0, longest_range = 0;
|
||||
int start = -1, end = 0, tuning_value = -1, range = 0;
|
||||
u16 clock_setting;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING);
|
||||
clock_setting &= ~0x3fff;
|
||||
|
||||
retry:
|
||||
phase = 0;
|
||||
tuned_phase_cnt = 0;
|
||||
do {
|
||||
sdhci_writel(host,
|
||||
clock_setting | phase,
|
||||
SDHCI_CLK_DELAY_SETTING);
|
||||
|
||||
if (!mmc_send_tuning(mmc, opcode, NULL)) {
|
||||
/* Tuning is successful at this tuning point */
|
||||
tuned_phase_cnt++;
|
||||
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
if (start == -1)
|
||||
start = phase;
|
||||
end = phase;
|
||||
range++;
|
||||
if (phase == (SIRF_TUNING_COUNT - 1)
|
||||
&& range > longest_range)
|
||||
tuning_value = (start + end) / 2;
|
||||
} else {
|
||||
dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
if (range > longest_range) {
|
||||
tuning_value = (start + end) / 2;
|
||||
longest_range = range;
|
||||
}
|
||||
start = -1;
|
||||
end = range = 0;
|
||||
}
|
||||
} while (++phase < SIRF_TUNING_COUNT);
|
||||
|
||||
if (tuned_phase_cnt && tuning_value > 0) {
|
||||
/*
|
||||
* Finally set the selected phase in delay
|
||||
* line hw block.
|
||||
*/
|
||||
phase = tuning_value;
|
||||
sdhci_writel(host,
|
||||
clock_setting | phase,
|
||||
SDHCI_CLK_DELAY_SETTING);
|
||||
|
||||
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
} else {
|
||||
if (--tuning_seq_cnt)
|
||||
goto retry;
|
||||
/* Tuning failed */
|
||||
dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
|
||||
mmc_hostname(mmc));
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_sirf_ops = {
|
||||
.read_l = sdhci_sirf_readl_le,
|
||||
.read_w = sdhci_sirf_readw_le,
|
||||
.platform_execute_tuning = sdhci_sirf_execute_tuning,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_bus_width = sdhci_sirf_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_sirf_pdata = {
|
||||
.ops = &sdhci_sirf_ops,
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static int sdhci_sirf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get clock");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret)
|
||||
goto err_clk_prepare;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
|
||||
/*
|
||||
* We must request the IRQ after sdhci_add_host(), as the tasklet only
|
||||
* gets setup in sdhci_add_host() and we oops.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err_request_cd;
|
||||
if (!ret)
|
||||
mmc_gpiod_request_cd_irq(host->mmc);
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_cd:
|
||||
sdhci_remove_host(host, 0);
|
||||
err_sdhci_add:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
err_clk_prepare:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_sirf_of_match[] = {
|
||||
{ .compatible = "sirf,prima2-sdhc" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
|
||||
|
||||
static struct platform_driver sdhci_sirf_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-sirf",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = sdhci_sirf_of_match,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_sirf_probe,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_sirf_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -708,14 +708,14 @@ static int sdhci_sprd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
sdhci_remove_host(host, 0);
|
||||
|
||||
clk_disable_unprepare(sprd_host->clk_sdio);
|
||||
clk_disable_unprepare(sprd_host->clk_enable);
|
||||
clk_disable_unprepare(sprd_host->clk_2x_enable);
|
||||
|
||||
mmc_free_host(mmc);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -689,6 +689,7 @@ static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
|
||||
|
||||
static const struct of_device_id sdhci_xenon_dt_ids[] = {
|
||||
{ .compatible = "marvell,armada-ap806-sdhci", .data = (void *)XENON_AP806},
|
||||
{ .compatible = "marvell,armada-ap807-sdhci", .data = (void *)XENON_AP807},
|
||||
{ .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110},
|
||||
{ .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700},
|
||||
{}
|
||||
|
@ -514,6 +514,26 @@ static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
|
||||
.flags = IOMUX_PRESENT,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_am64_8bit_pdata = {
|
||||
.ops = &sdhci_j721e_8bit_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct sdhci_am654_driver_data sdhci_am64_8bit_drvdata = {
|
||||
.pdata = &sdhci_am64_8bit_pdata,
|
||||
.flags = DLL_PRESENT | DLL_CALIB,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_am64_4bit_pdata = {
|
||||
.ops = &sdhci_j721e_4bit_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct sdhci_am654_driver_data sdhci_am64_4bit_drvdata = {
|
||||
.pdata = &sdhci_am64_4bit_pdata,
|
||||
.flags = IOMUX_PRESENT,
|
||||
};
|
||||
|
||||
static const struct soc_device_attribute sdhci_am654_devices[] = {
|
||||
{ .family = "AM65X",
|
||||
.revision = "SR1.0",
|
||||
@ -737,6 +757,14 @@ static const struct of_device_id sdhci_am654_of_match[] = {
|
||||
.compatible = "ti,j721e-sdhci-4bit",
|
||||
.data = &sdhci_j721e_4bit_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am64-sdhci-8bit",
|
||||
.data = &sdhci_am64_8bit_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am64-sdhci-4bit",
|
||||
.data = &sdhci_am64_4bit_drvdata,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_am654_of_match);
|
||||
|
@ -245,6 +245,7 @@ struct sunxi_idma_des {
|
||||
|
||||
struct sunxi_mmc_cfg {
|
||||
u32 idma_des_size_bits;
|
||||
u32 idma_des_shift;
|
||||
const struct sunxi_mmc_clk_delay *clk_delays;
|
||||
|
||||
/* does the IP block support autocalibration? */
|
||||
@ -344,7 +345,7 @@ static int sunxi_mmc_init_host(struct sunxi_mmc_host *host)
|
||||
/* Enable CEATA support */
|
||||
mmc_writel(host, REG_FUNS, SDXC_CEATA_ON);
|
||||
/* Set DMA descriptor list base address */
|
||||
mmc_writel(host, REG_DLBA, host->sg_dma);
|
||||
mmc_writel(host, REG_DLBA, host->sg_dma >> host->cfg->idma_des_shift);
|
||||
|
||||
rval = mmc_readl(host, REG_GCTRL);
|
||||
rval |= SDXC_INTERRUPT_ENABLE_BIT;
|
||||
@ -374,8 +375,10 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
|
||||
|
||||
next_desc += sizeof(struct sunxi_idma_des);
|
||||
pdes[i].buf_addr_ptr1 =
|
||||
cpu_to_le32(sg_dma_address(&data->sg[i]));
|
||||
pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc);
|
||||
cpu_to_le32(sg_dma_address(&data->sg[i]) >>
|
||||
host->cfg->idma_des_shift);
|
||||
pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc >>
|
||||
host->cfg->idma_des_shift);
|
||||
}
|
||||
|
||||
pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD);
|
||||
@ -1179,6 +1182,23 @@ static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
|
||||
.needs_new_timings = true,
|
||||
};
|
||||
|
||||
static const struct sunxi_mmc_cfg sun50i_a100_cfg = {
|
||||
.idma_des_size_bits = 16,
|
||||
.idma_des_shift = 2,
|
||||
.clk_delays = NULL,
|
||||
.can_calibrate = true,
|
||||
.mask_data0 = true,
|
||||
.needs_new_timings = true,
|
||||
};
|
||||
|
||||
static const struct sunxi_mmc_cfg sun50i_a100_emmc_cfg = {
|
||||
.idma_des_size_bits = 13,
|
||||
.idma_des_shift = 2,
|
||||
.clk_delays = NULL,
|
||||
.can_calibrate = true,
|
||||
.needs_new_timings = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_mmc_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg },
|
||||
{ .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg },
|
||||
@ -1187,6 +1207,8 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
|
||||
{ .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a100-mmc", .data = &sun50i_a100_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a100-emmc", .data = &sun50i_a100_emmc_cfg },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
|
||||
@ -1507,6 +1529,8 @@ static int sunxi_mmc_runtime_suspend(struct device *dev)
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sunxi_mmc_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
|
||||
sunxi_mmc_runtime_resume,
|
||||
NULL)
|
||||
|
@ -731,9 +731,9 @@ err_out:
|
||||
mmc_request_done(mmc, mrq);
|
||||
}
|
||||
|
||||
static void tifm_sd_end_cmd(unsigned long data)
|
||||
static void tifm_sd_end_cmd(struct tasklet_struct *t)
|
||||
{
|
||||
struct tifm_sd *host = (struct tifm_sd*)data;
|
||||
struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
|
||||
struct tifm_dev *sock = host->dev;
|
||||
struct mmc_host *mmc = tifm_get_drvdata(sock);
|
||||
struct mmc_request *mrq;
|
||||
@ -968,8 +968,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
|
||||
*/
|
||||
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
|
||||
|
||||
tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
|
||||
timer_setup(&host->timer, tifm_sd_abort, 0);
|
||||
|
||||
mmc->ops = &tifm_sd_ops;
|
||||
|
@ -181,7 +181,7 @@ struct tmio_mmc_host {
|
||||
unsigned int direction, int blk_size);
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
void (*reset)(struct tmio_mmc_host *host);
|
||||
bool (*check_retune)(struct tmio_mmc_host *host);
|
||||
bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||
unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host);
|
||||
|
||||
|
@ -477,8 +477,10 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host, unsigned int stat)
|
||||
if (!data)
|
||||
goto out;
|
||||
|
||||
if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
|
||||
stat & TMIO_STAT_TXUNDERRUN)
|
||||
if (stat & TMIO_STAT_DATATIMEOUT)
|
||||
data->error = -ETIMEDOUT;
|
||||
else if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
|
||||
stat & TMIO_STAT_TXUNDERRUN)
|
||||
data->error = -EILSEQ;
|
||||
if (host->dma_on && (data->flags & MMC_DATA_WRITE)) {
|
||||
u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
|
||||
@ -802,7 +804,7 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
|
||||
}
|
||||
|
||||
/* Error means retune, but executed command was still successful */
|
||||
if (host->check_retune && host->check_retune(host))
|
||||
if (host->check_retune && host->check_retune(host, mrq))
|
||||
mmc_retune_needed(host->mmc);
|
||||
|
||||
/* If SET_BLOCK_COUNT, continue with main command */
|
||||
|
@ -81,9 +81,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable)
|
||||
}
|
||||
|
||||
/* external DMA engine */
|
||||
static void uniphier_sd_external_dma_issue(unsigned long arg)
|
||||
static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
|
||||
{
|
||||
struct tmio_mmc_host *host = (void *)arg;
|
||||
struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
|
||||
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
|
||||
|
||||
uniphier_sd_dma_endisable(host, 1);
|
||||
@ -190,8 +190,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host,
|
||||
host->chan_rx = chan;
|
||||
host->chan_tx = chan;
|
||||
|
||||
tasklet_init(&host->dma_issue, uniphier_sd_external_dma_issue,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
|
||||
}
|
||||
|
||||
static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
|
||||
@ -228,9 +227,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = {
|
||||
.dataend = uniphier_sd_external_dma_dataend,
|
||||
};
|
||||
|
||||
static void uniphier_sd_internal_dma_issue(unsigned long arg)
|
||||
static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
|
||||
{
|
||||
struct tmio_mmc_host *host = (void *)arg;
|
||||
struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
@ -309,8 +308,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host,
|
||||
|
||||
host->chan_tx = (void *)0xdeadbeaf;
|
||||
|
||||
tasklet_init(&host->dma_issue, uniphier_sd_internal_dma_issue,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
|
||||
}
|
||||
|
||||
static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)
|
||||
|
@ -1858,10 +1858,12 @@ static int usdhi6_probe(struct platform_device *pdev)
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret < 0)
|
||||
goto e_clk_off;
|
||||
goto e_release_dma;
|
||||
|
||||
return 0;
|
||||
|
||||
e_release_dma:
|
||||
usdhi6_dma_release(host);
|
||||
e_clk_off:
|
||||
clk_disable_unprepare(host->clk);
|
||||
e_free_mmc:
|
||||
|
@ -959,14 +959,12 @@ static void via_sdc_timeout(struct timer_list *t)
|
||||
spin_unlock_irqrestore(&sdhost->lock, flags);
|
||||
}
|
||||
|
||||
static void via_sdc_tasklet_finish(unsigned long param)
|
||||
static void via_sdc_tasklet_finish(struct tasklet_struct *t)
|
||||
{
|
||||
struct via_crdr_mmc_host *host;
|
||||
struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
|
||||
unsigned long flags;
|
||||
struct mmc_request *mrq;
|
||||
|
||||
host = (struct via_crdr_mmc_host *)param;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
del_timer(&host->timer);
|
||||
@ -1050,8 +1048,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
|
||||
|
||||
INIT_WORK(&host->carddet_work, via_sdc_card_detect);
|
||||
|
||||
tasklet_init(&host->finish_tasklet, via_sdc_tasklet_finish,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
|
||||
|
||||
addrbase = host->sdhc_mmiobase;
|
||||
writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
|
||||
|
@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
|
||||
return host->mrq->cmd->data;
|
||||
}
|
||||
|
||||
static void wbsd_tasklet_card(unsigned long param)
|
||||
static void wbsd_tasklet_card(struct tasklet_struct *t)
|
||||
{
|
||||
struct wbsd_host *host = (struct wbsd_host *)param;
|
||||
struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
|
||||
u8 csr;
|
||||
int delay = -1;
|
||||
|
||||
@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(unsigned long param)
|
||||
mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
|
||||
}
|
||||
|
||||
static void wbsd_tasklet_fifo(unsigned long param)
|
||||
static void wbsd_tasklet_fifo(struct tasklet_struct *t)
|
||||
{
|
||||
struct wbsd_host *host = (struct wbsd_host *)param;
|
||||
struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
|
||||
struct mmc_data *data;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
@ -1067,9 +1067,9 @@ end:
|
||||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
static void wbsd_tasklet_crc(unsigned long param)
|
||||
static void wbsd_tasklet_crc(struct tasklet_struct *t)
|
||||
{
|
||||
struct wbsd_host *host = (struct wbsd_host *)param;
|
||||
struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
|
||||
struct mmc_data *data;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
@ -1091,9 +1091,9 @@ end:
|
||||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
static void wbsd_tasklet_timeout(unsigned long param)
|
||||
static void wbsd_tasklet_timeout(struct tasklet_struct *t)
|
||||
{
|
||||
struct wbsd_host *host = (struct wbsd_host *)param;
|
||||
struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
|
||||
struct mmc_data *data;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
@ -1115,9 +1115,9 @@ end:
|
||||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
static void wbsd_tasklet_finish(unsigned long param)
|
||||
static void wbsd_tasklet_finish(struct tasklet_struct *t)
|
||||
{
|
||||
struct wbsd_host *host = (struct wbsd_host *)param;
|
||||
struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
|
||||
struct mmc_data *data;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
@ -1449,16 +1449,11 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq)
|
||||
/*
|
||||
* Set up tasklets. Must be done before requesting interrupt.
|
||||
*/
|
||||
tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
|
||||
(unsigned long)host);
|
||||
tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
|
||||
(unsigned long)host);
|
||||
tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
|
||||
(unsigned long)host);
|
||||
tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
|
||||
(unsigned long)host);
|
||||
tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
|
||||
(unsigned long)host);
|
||||
tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
|
||||
tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
|
||||
tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
|
||||
tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
|
||||
tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
|
||||
|
||||
/*
|
||||
* Allocate interrupt.
|
||||
|
@ -179,8 +179,8 @@ int ufshcd_hba_init_crypto_capabilities(struct ufs_hba *hba)
|
||||
}
|
||||
|
||||
/* The actual number of configurations supported is (CFGC+1) */
|
||||
err = blk_ksm_init(&hba->ksm,
|
||||
hba->crypto_capabilities.config_count + 1);
|
||||
err = devm_blk_ksm_init(hba->dev, &hba->ksm,
|
||||
hba->crypto_capabilities.config_count + 1);
|
||||
if (err)
|
||||
goto out_free_caps;
|
||||
|
||||
@ -238,8 +238,3 @@ void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba,
|
||||
if (hba->caps & UFSHCD_CAP_CRYPTO)
|
||||
blk_ksm_register(&hba->ksm, q);
|
||||
}
|
||||
|
||||
void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba)
|
||||
{
|
||||
blk_ksm_destroy(&hba->ksm);
|
||||
}
|
||||
|
@ -43,8 +43,6 @@ void ufshcd_init_crypto(struct ufs_hba *hba);
|
||||
void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba,
|
||||
struct request_queue *q);
|
||||
|
||||
void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba);
|
||||
|
||||
#else /* CONFIG_SCSI_UFS_CRYPTO */
|
||||
|
||||
static inline void ufshcd_prepare_lrbp_crypto(struct request *rq,
|
||||
@ -69,9 +67,6 @@ static inline void ufshcd_init_crypto(struct ufs_hba *hba) { }
|
||||
static inline void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba,
|
||||
struct request_queue *q) { }
|
||||
|
||||
static inline void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba)
|
||||
{ }
|
||||
|
||||
#endif /* CONFIG_SCSI_UFS_CRYPTO */
|
||||
|
||||
#endif /* _UFSHCD_CRYPTO_H */
|
||||
|
@ -9152,7 +9152,6 @@ EXPORT_SYMBOL_GPL(ufshcd_remove);
|
||||
*/
|
||||
void ufshcd_dealloc_host(struct ufs_hba *hba)
|
||||
{
|
||||
ufshcd_crypto_destroy_keyslot_manager(hba);
|
||||
scsi_host_put(hba->host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
|
||||
|
@ -85,6 +85,9 @@ struct blk_keyslot_manager {
|
||||
|
||||
int blk_ksm_init(struct blk_keyslot_manager *ksm, unsigned int num_slots);
|
||||
|
||||
int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm,
|
||||
unsigned int num_slots);
|
||||
|
||||
blk_status_t blk_ksm_get_slot_for_key(struct blk_keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
struct blk_ksm_keyslot **slot_ptr);
|
||||
|
@ -311,7 +311,6 @@ struct mmc_card {
|
||||
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
||||
unsigned int nr_parts;
|
||||
|
||||
unsigned int bouncesz; /* Bounce buffer size */
|
||||
struct workqueue_struct *complete_wq; /* Private workqueue */
|
||||
};
|
||||
|
||||
|
@ -162,6 +162,12 @@ struct mmc_request {
|
||||
bool cap_cmd_during_tfr;
|
||||
|
||||
int tag;
|
||||
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
bool crypto_enabled;
|
||||
int crypto_key_slot;
|
||||
u32 data_unit_num;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mmc_card;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
|
||||
struct mmc_ios {
|
||||
unsigned int clock; /* clock rate */
|
||||
@ -79,6 +80,17 @@ struct mmc_ios {
|
||||
bool enhanced_strobe; /* hs400es selection */
|
||||
};
|
||||
|
||||
struct mmc_clk_phase {
|
||||
bool valid;
|
||||
u16 in_deg;
|
||||
u16 out_deg;
|
||||
};
|
||||
|
||||
#define MMC_NUM_CLK_PHASES (MMC_TIMING_MMC_HS400 + 1)
|
||||
struct mmc_clk_phase_map {
|
||||
struct mmc_clk_phase phase[MMC_NUM_CLK_PHASES];
|
||||
};
|
||||
|
||||
struct mmc_host;
|
||||
|
||||
struct mmc_host_ops {
|
||||
@ -384,6 +396,11 @@ struct mmc_host {
|
||||
#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 */
|
||||
#define MMC_CAP2_MERGE_CAPABLE (1 << 26) /* Host can merge a segment over the segment size */
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
#define MMC_CAP2_CRYPTO (1 << 27) /* Host supports inline encryption */
|
||||
#else
|
||||
#define MMC_CAP2_CRYPTO 0
|
||||
#endif
|
||||
|
||||
int fixed_drv_type; /* fixed driver type for non-removable media */
|
||||
|
||||
@ -412,7 +429,6 @@ struct mmc_host {
|
||||
unsigned int doing_retune:1; /* re-tuning in progress */
|
||||
unsigned int retune_now:1; /* do re-tuning at next req */
|
||||
unsigned int retune_paused:1; /* re-tuning is temporarily disabled */
|
||||
unsigned int use_blk_mq:1; /* use blk-mq */
|
||||
unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */
|
||||
unsigned int can_dma_map_merge:1; /* merging can be used */
|
||||
|
||||
@ -478,6 +494,11 @@ struct mmc_host {
|
||||
bool cqe_enabled;
|
||||
bool cqe_on;
|
||||
|
||||
/* Inline encryption support */
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
struct blk_keyslot_manager ksm;
|
||||
#endif
|
||||
|
||||
/* Host Software Queue support */
|
||||
bool hsq_enabled;
|
||||
|
||||
@ -490,6 +511,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *);
|
||||
int mmc_add_host(struct mmc_host *);
|
||||
void mmc_remove_host(struct mmc_host *);
|
||||
void mmc_free_host(struct mmc_host *);
|
||||
void mmc_of_parse_clk_phase(struct mmc_host *host,
|
||||
struct mmc_clk_phase_map *map);
|
||||
int mmc_of_parse(struct mmc_host *host);
|
||||
int mmc_of_parse_voltage(struct device_node *np, u32 *mask);
|
||||
|
||||
|
@ -108,8 +108,7 @@ struct omap_mmc_platform_data {
|
||||
const char *name;
|
||||
u32 ocr_mask;
|
||||
|
||||
/* Card detection IRQs */
|
||||
int card_detect_irq;
|
||||
/* Card detection */
|
||||
int (*card_detect)(struct device *dev, int slot);
|
||||
|
||||
unsigned int ban_openended:1;
|
||||
|
Loading…
Reference in New Issue
Block a user