mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-08 23:26:28 -04:00
Merge tag 'mmc-v6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Scan the eMMC boot areas for partition table - Clarify purpose of mmc_can* functions by renaming them to mmc_card_can* - Clarify helpers for host capabilities by renaming them to mmc_host_can* - Add support for graceful host removal for SD and eMMC - Further avoid re-storing power to the eMMC before a shutdown - Add quirk to disable DDR50 tuning and use it for some Swissbit SD-cards MMC host: - mtk-sd: Add support for Dimensity 1200 MT6893 - mtk-sd: Fix condition to enable single burst type - mtk-sd: Optimize several code-paths by aggregating register-writes - renesas_sdhi: Add support for the Renesas RZ/V2N variant - sdhci-msm: Add support for the SM7150 variant - sdhci-esdhc-imx: Re-factor the system PM logic - sdhci-esdhc-imx: Lots of improvements around the tuning support - sdhci-of-arasan: Add support for the Renesas RZ/N1D variant - sdhci-of-dwcmhsc: Add Sophgo SG2044 support - sdhci-of-esdhc: Add support for the LS1021a variant - sdhci-of-k1: Add new driver to support for SpacemiT K1 controller - sdhci-pic32: Convert microchip,sdhci-pic32 DT doc to json schema - wmt-sdmmc: Convert DT doc to json schema" * tag 'mmc-v6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (59 commits) dt-bindings: mmc: sdhci-of-dwcmhsc: Allow use of a power-domain mmc: sdhci-esdhc-imx: fix few build warnings mmc: bcm2835: Use str_read_write() helper mmc: host: sdhci-esdhc-imx: refactor the system PM logic mmc: sdhci: export APIs for sdhci irq wakeup mmc: sdhci-of-k1: add support for SpacemiT K1 SoC dt-bindings: mmc: spacemit,sdhci: add support for K1 SoC mmc: core: Scan the eMMC boot areas for partition table dt-binding: mmc: microchip,sdhci-pic32: convert text based binding to json schema mmc: rename mmc_boot_partition_access() to mmc_host_can_access_boot() mmc: rename mmc_host_uhs() to mmc_host_can_uhs() mmc: rename mmc_host_done_complete() to mmc_host_can_done_complete() mmc: rename mmc_host_cmd23() to mmc_host_can_cmd23() mmc: sdhci-esdhc-imx: fix defined but not used warnings dt-bindings: mmc: vt8500-sdmmc: Convert to YAML dt-bindings: mmc: sdhci-msm: Add the SM7150 compatible dt-bindings: mmc: fsl,esdhc: add compatible string fsl,ls1021a-esdhc mmc: cavium-thunderx: Use non-hybrid PCI devres API dt-bindings: mmc: mtk-sd: Add support for Dimensity 1200 MT6893 dt-bindings: mmc: sdhci-of-dwcmhsc: Add Sophgo SG2044 support ...
This commit is contained in:
@@ -38,6 +38,15 @@ allOf:
|
||||
- items:
|
||||
- const: clk_out_sd1
|
||||
- const: clk_in_sd1
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,rzn1-sdhci
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 2
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@@ -45,6 +54,10 @@ properties:
|
||||
- const: arasan,sdhci-8.9a # generic Arasan SDHCI 8.9a PHY
|
||||
- const: arasan,sdhci-4.9a # generic Arasan SDHCI 4.9a PHY
|
||||
- const: arasan,sdhci-5.1 # generic Arasan SDHCI 5.1 PHY
|
||||
- items:
|
||||
- const: renesas,r9a06g032-sdhci # Renesas RZ/N1D SoC
|
||||
- const: renesas,rzn1-sdhci # Renesas RZ/N1 family
|
||||
- const: arasan,sdhci-8.9a
|
||||
- items:
|
||||
- const: rockchip,rk3399-sdhci-5.1 # rk3399 eMMC PHY
|
||||
- const: arasan,sdhci-5.1
|
||||
@@ -109,7 +122,14 @@ properties:
|
||||
- const: gate
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: int
|
||||
- const: wakeup
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
|
||||
@@ -24,6 +24,7 @@ properties:
|
||||
- fsl,t1040-esdhc
|
||||
- fsl,t4240-esdhc
|
||||
- fsl,ls1012a-esdhc
|
||||
- fsl,ls1021a-esdhc
|
||||
- fsl,ls1028a-esdhc
|
||||
- fsl,ls1088a-esdhc
|
||||
- fsl,ls1043a-esdhc
|
||||
|
||||
@@ -52,9 +52,14 @@ properties:
|
||||
- const: core
|
||||
- const: axi
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
marvell,pad-type:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum:
|
||||
@@ -142,7 +147,7 @@ properties:
|
||||
This property provides the re-tuning counter.
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
- $ref: sdhci-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -164,26 +169,6 @@ allOf:
|
||||
|
||||
marvell,pad-type: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- marvell,armada-cp110-sdhci
|
||||
- marvell,armada-ap807-sdhci
|
||||
- marvell,armada-ap806-sdhci
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: axi
|
||||
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
* Microchip PIC32 SDHCI Controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-pic32 driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "microchip,pic32mzda-sdhci"
|
||||
- interrupts: Should contain interrupt
|
||||
- clock-names: Should be "base_clk", "sys_clk".
|
||||
See: Documentation/devicetree/bindings/resource-names.txt
|
||||
- clocks: Phandle to the clock.
|
||||
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- pinctrl-names: A pinctrl state names "default" must be defined.
|
||||
- pinctrl-0: Phandle referencing pin configuration of the SDHCI controller.
|
||||
See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||
|
||||
Example:
|
||||
|
||||
sdhci@1f8ec000 {
|
||||
compatible = "microchip,pic32mzda-sdhci";
|
||||
reg = <0x1f8ec000 0x100>;
|
||||
interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&rootclk REF4CLK>, <&rootclk PB5CLK>;
|
||||
clock-names = "base_clk", "sys_clk";
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_sdhc1>;
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/microchip,sdhci-pic32.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip PIC32 SDHI Controller
|
||||
|
||||
description:
|
||||
The Microchip PIC32 family of microcontrollers (MCUs) includes models with
|
||||
Secure Digital Host Controller Interface (SDHCI) controllers, allowing them
|
||||
to interface with Secure Digital (SD) cards. This interface is used for reading,
|
||||
writing, and managing data on SD cards, enabling storage and data transfer
|
||||
capabilities in embedded systems.
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,pic32mzda-sdhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: base_clk
|
||||
- const: sys_clk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- pinctrl-names
|
||||
- pinctrl-0
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clock/microchip,pic32-clock.h>
|
||||
mmc@1f8ec000 {
|
||||
compatible = "microchip,pic32mzda-sdhci";
|
||||
reg = <0x1f8ec000 0x100>;
|
||||
interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&rootclk REF4CLK>, <&rootclk PB5CLK>;
|
||||
clock-names = "base_clk", "sys_clk";
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_sdhc1>;
|
||||
};
|
||||
...
|
||||
@@ -32,6 +32,7 @@ properties:
|
||||
- const: mediatek,mt2701-mmc
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt6893-mmc
|
||||
- mediatek,mt8186-mmc
|
||||
- mediatek,mt8188-mmc
|
||||
- mediatek,mt8192-mmc
|
||||
@@ -299,6 +300,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt6893-mmc
|
||||
- mediatek,mt8186-mmc
|
||||
- mediatek,mt8188-mmc
|
||||
- mediatek,mt8195-mmc
|
||||
|
||||
@@ -69,7 +69,9 @@ properties:
|
||||
- renesas,sdhi-r9a09g011 # RZ/V2M
|
||||
- const: renesas,rzg2l-sdhi
|
||||
- items:
|
||||
- const: renesas,sdhi-r9a09g047 # RZ/G3E
|
||||
- enum:
|
||||
- renesas,sdhi-r9a09g047 # RZ/G3E
|
||||
- renesas,sdhi-r9a09g056 # RZ/V2N
|
||||
- const: renesas,sdhi-r9a09g057 # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
|
||||
@@ -60,6 +60,7 @@ properties:
|
||||
- qcom,sm6125-sdhci
|
||||
- qcom,sm6350-sdhci
|
||||
- qcom,sm6375-sdhci
|
||||
- qcom,sm7150-sdhci
|
||||
- qcom,sm8150-sdhci
|
||||
- qcom,sm8250-sdhci
|
||||
- qcom,sm8350-sdhci
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
The properties specific for SD host controllers. For properties shared by MMC
|
||||
host controllers refer to the mmc[1] bindings.
|
||||
|
||||
[1] Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
|
||||
Optional properties:
|
||||
- sdhci-caps-mask: The sdhci capabilities register is incorrect. This 64bit
|
||||
property corresponds to the bits in the sdhci capability register. If the bit
|
||||
is on in the mask then the bit is incorrect in the register and should be
|
||||
turned off, before applying sdhci-caps.
|
||||
- sdhci-caps: The sdhci capabilities register is incorrect. This 64bit
|
||||
property corresponds to the bits in the sdhci capability register. If the
|
||||
bit is on in the property then the bit should be turned on.
|
||||
@@ -19,6 +19,9 @@ properties:
|
||||
- rockchip,rk3562-dwcmshc
|
||||
- rockchip,rk3576-dwcmshc
|
||||
- const: rockchip,rk3588-dwcmshc
|
||||
- items:
|
||||
- const: sophgo,sg2044-dwcmshc
|
||||
- const: sophgo,sg2042-dwcmshc
|
||||
- enum:
|
||||
- rockchip,rk3568-dwcmshc
|
||||
- rockchip,rk3588-dwcmshc
|
||||
@@ -117,10 +120,6 @@ allOf:
|
||||
required:
|
||||
- power-domains
|
||||
|
||||
else:
|
||||
properties:
|
||||
power-domains: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
53
Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml
Normal file
53
Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/spacemit,sdhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SpacemiT SDHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Yixun Lan <dlan@gentoo.org>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: spacemit,k1-sdhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: core clock, used by internal controller
|
||||
- description: io clock, output for SD, SDIO, eMMC device
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: io
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
mmc@d4281000 {
|
||||
compatible = "spacemit,k1-sdhci";
|
||||
reg = <0xd4281000 0x200>;
|
||||
interrupts = <101>;
|
||||
interrupt-parent = <&plic>;
|
||||
clocks = <&clk_apmu 10>, <&clk_apmu 13>;
|
||||
clock-names = "core", "io";
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
* Wondermedia WM8505/WM8650 SD/MMC Host Controller
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the wmt-sdmmc driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "wm,wm8505-sdhc".
|
||||
- interrupts: Two interrupts are required - regular irq and dma irq.
|
||||
|
||||
Optional properties:
|
||||
- sdon-inverted: SD_ON bit is inverted on the controller
|
||||
|
||||
Examples:
|
||||
|
||||
sdhc@d800a000 {
|
||||
compatible = "wm,wm8505-sdhc";
|
||||
reg = <0xd800a000 0x1000>;
|
||||
interrupts = <20 21>;
|
||||
clocks = <&sdhc>;
|
||||
bus-width = <4>;
|
||||
sdon-inverted;
|
||||
};
|
||||
|
||||
66
Documentation/devicetree/bindings/mmc/wm,wm8505-sdhc.yaml
Normal file
66
Documentation/devicetree/bindings/mmc/wm,wm8505-sdhc.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/wm,wm8505-sdhc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: WonderMedia SoC SDHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Alexey Charkov <alchark@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: wm,wm8505-sdhc
|
||||
- items:
|
||||
- const: wm,wm8650-sdhc
|
||||
- const: wm,wm8505-sdhc
|
||||
- items:
|
||||
- const: wm,wm8750-sdhc
|
||||
- const: wm,wm8505-sdhc
|
||||
- items:
|
||||
- const: wm,wm8850-sdhc
|
||||
- const: wm,wm8505-sdhc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: SDMMC controller interrupt
|
||||
- description: SDMMC controller DMA interrupt
|
||||
|
||||
sdon-inverted:
|
||||
type: boolean
|
||||
description: All chips before (not including) WM8505 rev. A2 treated their
|
||||
"clock stop" bit (register offset 0x08 a.k.a. SDMMC_BUSMODE, bit 0x10)
|
||||
as "set 1 to disable SD clock", while all the later versions treated it
|
||||
as "set 0 to disable SD clock". Set this property for later versions of
|
||||
wm,wm8505-sdhc. On wm,wm8650-sdhc and later this property is implied and
|
||||
does not need to be set explicitly
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
mmc@d800a000 {
|
||||
compatible = "wm,wm8505-sdhc";
|
||||
reg = <0xd800a000 0x1000>;
|
||||
interrupts = <20>, <21>;
|
||||
clocks = <&sdhc>;
|
||||
bus-width = <4>;
|
||||
sdon-inverted;
|
||||
};
|
||||
@@ -1220,7 +1220,7 @@ static void mmc_blk_issue_erase_rq(struct mmc_queue *mq, struct request *req,
|
||||
int err = 0;
|
||||
blk_status_t status = BLK_STS_OK;
|
||||
|
||||
if (!mmc_can_erase(card)) {
|
||||
if (!mmc_card_can_erase(card)) {
|
||||
status = BLK_STS_NOTSUPP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1276,7 +1276,7 @@ static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||
int err = 0, type = MMC_BLK_SECDISCARD;
|
||||
blk_status_t status = BLK_STS_OK;
|
||||
|
||||
if (!(mmc_can_secure_erase_trim(card))) {
|
||||
if (!(mmc_card_can_secure_erase_trim(card))) {
|
||||
status = BLK_STS_NOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
@@ -1284,7 +1284,7 @@ static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||
from = blk_rq_pos(req);
|
||||
nr = blk_rq_sectors(req);
|
||||
|
||||
if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
|
||||
if (mmc_card_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
|
||||
arg = MMC_SECURE_TRIM1_ARG;
|
||||
else
|
||||
arg = MMC_SECURE_ERASE_ARG;
|
||||
@@ -2278,7 +2278,7 @@ void mmc_blk_mq_recovery(struct mmc_queue *mq)
|
||||
static void mmc_blk_mq_complete_prev_req(struct mmc_queue *mq,
|
||||
struct request **prev_req)
|
||||
{
|
||||
if (mmc_host_done_complete(mq->card->host))
|
||||
if (mmc_host_can_done_complete(mq->card->host))
|
||||
return;
|
||||
|
||||
mutex_lock(&mq->complete_lock);
|
||||
@@ -2317,7 +2317,7 @@ static void mmc_blk_mq_req_done(struct mmc_request *mrq)
|
||||
struct mmc_host *host = mq->card->host;
|
||||
unsigned long flags;
|
||||
|
||||
if (!mmc_host_done_complete(host)) {
|
||||
if (!mmc_host_can_done_complete(host)) {
|
||||
bool waiting;
|
||||
|
||||
/*
|
||||
@@ -2430,7 +2430,7 @@ static int mmc_blk_mq_issue_rw_rq(struct mmc_queue *mq,
|
||||
mq->rw_wait = false;
|
||||
|
||||
/* Release re-tuning here where there is no synchronization required */
|
||||
if (err || mmc_host_done_complete(host))
|
||||
if (err || mmc_host_can_done_complete(host))
|
||||
mmc_retune_release(host);
|
||||
|
||||
out_post_req:
|
||||
@@ -2618,7 +2618,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
*/
|
||||
md->read_only = mmc_blk_readonly(card);
|
||||
|
||||
if (mmc_host_cmd23(card->host)) {
|
||||
if (mmc_host_can_cmd23(card->host)) {
|
||||
if ((mmc_card_mmc(card) &&
|
||||
card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
|
||||
(mmc_card_sd(card) && !mmc_card_ult_capacity(card) &&
|
||||
@@ -2655,7 +2655,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
md->disk->private_data = md;
|
||||
md->parent = parent;
|
||||
set_disk_ro(md->disk, md->read_only || default_ro);
|
||||
if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
|
||||
if (area_type & MMC_BLK_DATA_AREA_RPMB)
|
||||
md->disk->flags |= GENHD_FL_NO_PART;
|
||||
|
||||
/*
|
||||
|
||||
@@ -89,6 +89,7 @@ struct mmc_fixup {
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
#define CID_MANFID_SAMSUNG 0x15
|
||||
#define CID_MANFID_APACER 0x27
|
||||
#define CID_MANFID_SWISSBIT 0x5D
|
||||
#define CID_MANFID_KINGSTON 0x70
|
||||
#define CID_MANFID_HYNIX 0x90
|
||||
#define CID_MANFID_KINGSTON_SD 0x9F
|
||||
@@ -294,4 +295,9 @@ static inline int mmc_card_broken_sd_poweroff_notify(const struct mmc_card *c)
|
||||
return c->quirks & MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY;
|
||||
}
|
||||
|
||||
static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1837,52 +1837,44 @@ int mmc_erase(struct mmc_card *card, sector_t from, unsigned int nr,
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_erase);
|
||||
|
||||
int mmc_can_erase(struct mmc_card *card)
|
||||
bool mmc_card_can_erase(struct mmc_card *card)
|
||||
{
|
||||
if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
|
||||
return 1;
|
||||
return 0;
|
||||
return (card->csd.cmdclass & CCC_ERASE && card->erase_size);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_erase);
|
||||
EXPORT_SYMBOL(mmc_card_can_erase);
|
||||
|
||||
int mmc_can_trim(struct mmc_card *card)
|
||||
bool mmc_card_can_trim(struct mmc_card *card)
|
||||
{
|
||||
if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) &&
|
||||
(!(card->quirks & MMC_QUIRK_TRIM_BROKEN)))
|
||||
return 1;
|
||||
return 0;
|
||||
return ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) &&
|
||||
(!(card->quirks & MMC_QUIRK_TRIM_BROKEN)));
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_trim);
|
||||
EXPORT_SYMBOL(mmc_card_can_trim);
|
||||
|
||||
int mmc_can_discard(struct mmc_card *card)
|
||||
bool mmc_card_can_discard(struct mmc_card *card)
|
||||
{
|
||||
/*
|
||||
* As there's no way to detect the discard support bit at v4.5
|
||||
* use the s/w feature support filed.
|
||||
*/
|
||||
if (card->ext_csd.feature_support & MMC_DISCARD_FEATURE)
|
||||
return 1;
|
||||
return 0;
|
||||
return (card->ext_csd.feature_support & MMC_DISCARD_FEATURE);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_discard);
|
||||
EXPORT_SYMBOL(mmc_card_can_discard);
|
||||
|
||||
int mmc_can_sanitize(struct mmc_card *card)
|
||||
bool mmc_card_can_sanitize(struct mmc_card *card)
|
||||
{
|
||||
if (!mmc_can_trim(card) && !mmc_can_erase(card))
|
||||
return 0;
|
||||
if (!mmc_card_can_trim(card) && !mmc_card_can_erase(card))
|
||||
return false;
|
||||
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE)
|
||||
return 1;
|
||||
return 0;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int mmc_can_secure_erase_trim(struct mmc_card *card)
|
||||
bool mmc_card_can_secure_erase_trim(struct mmc_card *card)
|
||||
{
|
||||
if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) &&
|
||||
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
|
||||
return 1;
|
||||
return 0;
|
||||
return ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) &&
|
||||
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN));
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_secure_erase_trim);
|
||||
EXPORT_SYMBOL(mmc_card_can_secure_erase_trim);
|
||||
|
||||
int mmc_erase_group_aligned(struct mmc_card *card, sector_t from,
|
||||
unsigned int nr)
|
||||
@@ -1987,7 +1979,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
|
||||
return card->pref_erase;
|
||||
|
||||
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
|
||||
if (mmc_can_trim(card)) {
|
||||
if (mmc_card_can_trim(card)) {
|
||||
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
|
||||
if (max_trim < max_discard || max_discard == 0)
|
||||
max_discard = max_trim;
|
||||
|
||||
@@ -118,11 +118,11 @@ bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq);
|
||||
|
||||
int mmc_erase(struct mmc_card *card, sector_t from, unsigned int nr, unsigned int arg);
|
||||
int mmc_can_erase(struct mmc_card *card);
|
||||
int mmc_can_trim(struct mmc_card *card);
|
||||
int mmc_can_discard(struct mmc_card *card);
|
||||
int mmc_can_sanitize(struct mmc_card *card);
|
||||
int mmc_can_secure_erase_trim(struct mmc_card *card);
|
||||
bool mmc_card_can_erase(struct mmc_card *card);
|
||||
bool mmc_card_can_trim(struct mmc_card *card);
|
||||
bool mmc_card_can_discard(struct mmc_card *card);
|
||||
bool mmc_card_can_sanitize(struct mmc_card *card);
|
||||
bool mmc_card_can_secure_erase_trim(struct mmc_card *card);
|
||||
int mmc_erase_group_aligned(struct mmc_card *card, sector_t from, unsigned int nr);
|
||||
unsigned int mmc_calc_max_discard(struct mmc_card *card);
|
||||
|
||||
|
||||
@@ -39,22 +39,22 @@ static inline void mmc_retune_recheck(struct mmc_host *host)
|
||||
host->retune_now = 1;
|
||||
}
|
||||
|
||||
static inline int mmc_host_cmd23(struct mmc_host *host)
|
||||
static inline int mmc_host_can_cmd23(struct mmc_host *host)
|
||||
{
|
||||
return host->caps & MMC_CAP_CMD23;
|
||||
}
|
||||
|
||||
static inline bool mmc_host_done_complete(struct mmc_host *host)
|
||||
static inline bool mmc_host_can_done_complete(struct mmc_host *host)
|
||||
{
|
||||
return host->caps & MMC_CAP_DONE_COMPLETE;
|
||||
}
|
||||
|
||||
static inline int mmc_boot_partition_access(struct mmc_host *host)
|
||||
static inline int mmc_host_can_access_boot(struct mmc_host *host)
|
||||
{
|
||||
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
|
||||
}
|
||||
|
||||
static inline int mmc_host_uhs(struct mmc_host *host)
|
||||
static inline int mmc_host_can_uhs(struct mmc_host *host)
|
||||
{
|
||||
return host->caps &
|
||||
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
#define MIN_CACHE_EN_TIMEOUT_MS 1600
|
||||
#define CACHE_FLUSH_TIMEOUT_MS 30000 /* 30s */
|
||||
|
||||
enum mmc_poweroff_type {
|
||||
MMC_POWEROFF_SUSPEND,
|
||||
MMC_POWEROFF_SHUTDOWN,
|
||||
MMC_POWEROFF_UNBIND,
|
||||
};
|
||||
|
||||
static const unsigned int tran_exp[] = {
|
||||
10000, 100000, 1000000, 10000000,
|
||||
0, 0, 0, 0
|
||||
@@ -453,7 +459,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
* There are two boot regions of equal size, defined in
|
||||
* multiples of 128K.
|
||||
*/
|
||||
if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) {
|
||||
if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_host_can_access_boot(card->host)) {
|
||||
for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
|
||||
part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
||||
mmc_part_add(card, part_size,
|
||||
@@ -572,7 +578,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
* RPMB regions are defined in multiples of 128K.
|
||||
*/
|
||||
card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT];
|
||||
if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) {
|
||||
if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_can_cmd23(card->host)) {
|
||||
mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17,
|
||||
EXT_CSD_PART_CONFIG_ACC_RPMB,
|
||||
"rpmb", 0, false,
|
||||
@@ -674,7 +680,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||
u8 *ext_csd;
|
||||
int err;
|
||||
|
||||
if (!mmc_can_ext_csd(card))
|
||||
if (!mmc_card_can_ext_csd(card))
|
||||
return 0;
|
||||
|
||||
err = mmc_get_ext_csd(card, &ext_csd);
|
||||
@@ -953,7 +959,7 @@ static int mmc_select_powerclass(struct mmc_card *card)
|
||||
int err, ddr;
|
||||
|
||||
/* Power class selection is supported for versions >= 4.0 */
|
||||
if (!mmc_can_ext_csd(card))
|
||||
if (!mmc_card_can_ext_csd(card))
|
||||
return 0;
|
||||
|
||||
bus_width = host->ios.bus_width;
|
||||
@@ -1016,7 +1022,7 @@ static int mmc_select_bus_width(struct mmc_card *card)
|
||||
unsigned idx, bus_width = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!mmc_can_ext_csd(card) ||
|
||||
if (!mmc_card_can_ext_csd(card) ||
|
||||
!(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
|
||||
return 0;
|
||||
|
||||
@@ -1537,7 +1543,7 @@ static int mmc_select_timing(struct mmc_card *card)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!mmc_can_ext_csd(card))
|
||||
if (!mmc_card_can_ext_csd(card))
|
||||
goto bus_speed;
|
||||
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
|
||||
@@ -1798,9 +1804,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
|
||||
/* set erase_arg */
|
||||
if (mmc_can_discard(card))
|
||||
if (mmc_card_can_discard(card))
|
||||
card->erase_arg = MMC_DISCARD_ARG;
|
||||
else if (mmc_can_trim(card))
|
||||
else if (mmc_card_can_trim(card))
|
||||
card->erase_arg = MMC_TRIM_ARG;
|
||||
else
|
||||
card->erase_arg = MMC_ERASE_ARG;
|
||||
@@ -1949,7 +1955,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_can_sleep(struct mmc_card *card)
|
||||
static bool mmc_card_can_sleep(struct mmc_card *card)
|
||||
{
|
||||
return card->ext_csd.rev >= 3;
|
||||
}
|
||||
@@ -2007,13 +2013,26 @@ static int mmc_sleep(struct mmc_host *host)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_can_poweroff_notify(const struct mmc_card *card)
|
||||
static bool mmc_card_can_poweroff_notify(const struct mmc_card *card)
|
||||
{
|
||||
return card &&
|
||||
mmc_card_mmc(card) &&
|
||||
(card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
|
||||
}
|
||||
|
||||
static bool mmc_host_can_poweroff_notify(const struct mmc_host *host,
|
||||
enum mmc_poweroff_type pm_type)
|
||||
{
|
||||
if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)
|
||||
return true;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND &&
|
||||
pm_type == MMC_POWEROFF_SUSPEND)
|
||||
return true;
|
||||
|
||||
return pm_type == MMC_POWEROFF_SHUTDOWN;
|
||||
}
|
||||
|
||||
static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
{
|
||||
unsigned int timeout = card->ext_csd.generic_cmd6_time;
|
||||
@@ -2036,15 +2055,6 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Host is being removed. Free up the current card.
|
||||
*/
|
||||
static void mmc_remove(struct mmc_host *host)
|
||||
{
|
||||
mmc_remove_card(host->card);
|
||||
host->card = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection - card is alive.
|
||||
*/
|
||||
@@ -2070,7 +2080,8 @@ static void mmc_detect(struct mmc_host *host)
|
||||
mmc_put_card(host->card, NULL);
|
||||
|
||||
if (err) {
|
||||
mmc_remove(host);
|
||||
mmc_remove_card(host->card);
|
||||
host->card = NULL;
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
@@ -2108,11 +2119,13 @@ static int _mmc_flush_cache(struct mmc_host *host)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
|
||||
{
|
||||
unsigned int notify_type = EXT_CSD_POWER_OFF_SHORT;
|
||||
int err = 0;
|
||||
unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
|
||||
EXT_CSD_POWER_OFF_LONG;
|
||||
|
||||
if (pm_type == MMC_POWEROFF_SHUTDOWN)
|
||||
notify_type = EXT_CSD_POWER_OFF_LONG;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
@@ -2123,11 +2136,10 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (mmc_can_poweroff_notify(host->card) &&
|
||||
((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend ||
|
||||
(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND)))
|
||||
if (mmc_card_can_poweroff_notify(host->card) &&
|
||||
mmc_host_can_poweroff_notify(host, pm_type))
|
||||
err = mmc_poweroff_notify(host->card, notify_type);
|
||||
else if (mmc_can_sleep(host->card))
|
||||
else if (mmc_card_can_sleep(host->card))
|
||||
err = mmc_sleep(host);
|
||||
else if (!mmc_host_is_spi(host))
|
||||
err = mmc_deselect_cards(host);
|
||||
@@ -2141,6 +2153,20 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Host is being removed. Free up the current card and do a graceful power-off.
|
||||
*/
|
||||
static void mmc_remove(struct mmc_host *host)
|
||||
{
|
||||
get_device(&host->card->dev);
|
||||
mmc_remove_card(host->card);
|
||||
|
||||
_mmc_suspend(host, MMC_POWEROFF_UNBIND);
|
||||
|
||||
put_device(&host->card->dev);
|
||||
host->card = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend callback
|
||||
*/
|
||||
@@ -2148,7 +2174,7 @@ static int mmc_suspend(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = _mmc_suspend(host, true);
|
||||
err = _mmc_suspend(host, MMC_POWEROFF_SUSPEND);
|
||||
if (!err) {
|
||||
pm_runtime_disable(&host->card->dev);
|
||||
pm_runtime_set_suspended(&host->card->dev);
|
||||
@@ -2187,15 +2213,16 @@ static int mmc_shutdown(struct mmc_host *host)
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* In a specific case for poweroff notify, we need to resume the card
|
||||
* before we can shutdown it properly.
|
||||
* If the card remains suspended at this point and it was done by using
|
||||
* the sleep-cmd (CMD5), we may need to re-initialize it first, to allow
|
||||
* us to send the preferred poweroff-notification cmd at shutdown.
|
||||
*/
|
||||
if (mmc_can_poweroff_notify(host->card) &&
|
||||
!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
|
||||
if (mmc_card_can_poweroff_notify(host->card) &&
|
||||
!mmc_host_can_poweroff_notify(host, MMC_POWEROFF_SUSPEND))
|
||||
err = _mmc_resume(host);
|
||||
|
||||
if (!err)
|
||||
err = _mmc_suspend(host, false);
|
||||
err = _mmc_suspend(host, MMC_POWEROFF_SHUTDOWN);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -2219,7 +2246,7 @@ static int mmc_runtime_suspend(struct mmc_host *host)
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
err = _mmc_suspend(host, true);
|
||||
err = _mmc_suspend(host, MMC_POWEROFF_SUSPEND);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggressive suspend\n",
|
||||
mmc_hostname(host), err);
|
||||
@@ -2242,14 +2269,12 @@ static int mmc_runtime_resume(struct mmc_host *host)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_can_reset(struct mmc_card *card)
|
||||
static bool mmc_card_can_reset(struct mmc_card *card)
|
||||
{
|
||||
u8 rst_n_function;
|
||||
|
||||
rst_n_function = card->ext_csd.rst_n_function;
|
||||
if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
|
||||
return 0;
|
||||
return 1;
|
||||
return ((rst_n_function & EXT_CSD_RST_N_EN_MASK) == EXT_CSD_RST_N_ENABLED);
|
||||
}
|
||||
|
||||
static int _mmc_hw_reset(struct mmc_host *host)
|
||||
@@ -2263,7 +2288,7 @@ static int _mmc_hw_reset(struct mmc_host *host)
|
||||
_mmc_flush_cache(host);
|
||||
|
||||
if ((host->caps & MMC_CAP_HW_RESET) && host->ops->card_hw_reset &&
|
||||
mmc_can_reset(card)) {
|
||||
mmc_card_can_reset(card)) {
|
||||
/* If the card accept RST_n signal, send it. */
|
||||
mmc_set_clock(host, host->f_init);
|
||||
host->ops->card_hw_reset(host);
|
||||
|
||||
@@ -383,7 +383,7 @@ int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
||||
if (!card || !new_ext_csd)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mmc_can_ext_csd(card))
|
||||
if (!mmc_card_can_ext_csd(card))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
@@ -944,7 +944,7 @@ static int mmc_interrupt_hpi(struct mmc_card *card)
|
||||
return err;
|
||||
}
|
||||
|
||||
int mmc_can_ext_csd(struct mmc_card *card)
|
||||
bool mmc_card_can_ext_csd(struct mmc_card *card)
|
||||
{
|
||||
return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
|
||||
}
|
||||
@@ -1046,7 +1046,7 @@ int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms)
|
||||
struct mmc_host *host = card->host;
|
||||
int err;
|
||||
|
||||
if (!mmc_can_sanitize(card)) {
|
||||
if (!mmc_card_can_sanitize(card)) {
|
||||
pr_warn("%s: Sanitize not supported\n", mmc_hostname(host));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||
int mmc_can_ext_csd(struct mmc_card *card);
|
||||
bool mmc_card_can_ext_csd(struct mmc_card *card);
|
||||
int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
|
||||
bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
|
||||
unsigned int timeout_ms);
|
||||
|
||||
@@ -191,7 +191,7 @@ static void mmc_test_prepare_sbc(struct mmc_test_card *test,
|
||||
{
|
||||
struct mmc_card *card = test->card;
|
||||
|
||||
if (!mrq->sbc || !mmc_host_cmd23(card->host) ||
|
||||
if (!mrq->sbc || !mmc_host_can_cmd23(card->host) ||
|
||||
!mmc_test_card_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
|
||||
(card->quirks & MMC_QUIRK_BLK_NO_CMD23)) {
|
||||
mrq->sbc = NULL;
|
||||
@@ -1510,7 +1510,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
|
||||
if (!mmc_can_erase(test->card))
|
||||
if (!mmc_card_can_erase(test->card))
|
||||
return 0;
|
||||
|
||||
return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9,
|
||||
@@ -1746,10 +1746,10 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
||||
struct timespec64 ts1, ts2;
|
||||
int ret;
|
||||
|
||||
if (!mmc_can_trim(test->card))
|
||||
if (!mmc_card_can_trim(test->card))
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
if (!mmc_can_erase(test->card))
|
||||
if (!mmc_card_can_erase(test->card))
|
||||
return RESULT_UNSUP_HOST;
|
||||
|
||||
for (sz = 512; sz < t->max_sz; sz <<= 1) {
|
||||
@@ -1863,10 +1863,10 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
|
||||
struct timespec64 ts1, ts2;
|
||||
int ret;
|
||||
|
||||
if (!mmc_can_trim(test->card))
|
||||
if (!mmc_card_can_trim(test->card))
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
if (!mmc_can_erase(test->card))
|
||||
if (!mmc_card_can_erase(test->card))
|
||||
return RESULT_UNSUP_HOST;
|
||||
|
||||
for (sz = 512; sz <= t->max_sz; sz <<= 1) {
|
||||
@@ -2114,7 +2114,7 @@ static int mmc_test_rw_multiple(struct mmc_test_card *test,
|
||||
return 0;
|
||||
|
||||
/* prepare test area */
|
||||
if (mmc_can_erase(test->card) &&
|
||||
if (mmc_card_can_erase(test->card) &&
|
||||
tdata->prepare & MMC_TEST_PREP_ERASE) {
|
||||
ret = mmc_erase(test->card, dev_addr,
|
||||
size / 512, test->card->erase_arg);
|
||||
@@ -2390,7 +2390,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
|
||||
512, write);
|
||||
|
||||
if (use_sbc && t->blocks > 1 && !mrq->sbc) {
|
||||
ret = mmc_host_cmd23(host) ?
|
||||
ret = mmc_host_can_cmd23(host) ?
|
||||
RESULT_UNSUP_CARD :
|
||||
RESULT_UNSUP_HOST;
|
||||
goto out_free;
|
||||
|
||||
@@ -184,9 +184,9 @@ static void mmc_queue_setup_discard(struct mmc_card *card,
|
||||
return;
|
||||
|
||||
lim->max_hw_discard_sectors = max_discard;
|
||||
if (mmc_can_secure_erase_trim(card))
|
||||
if (mmc_card_can_secure_erase_trim(card))
|
||||
lim->max_secure_erase_sectors = max_discard;
|
||||
if (mmc_can_trim(card) && card->erased_byte == 0)
|
||||
if (mmc_card_can_trim(card) && card->erased_byte == 0)
|
||||
lim->max_write_zeroes_sectors = max_discard;
|
||||
|
||||
/* granularity must not be greater than max. discard */
|
||||
@@ -352,7 +352,7 @@ static struct gendisk *mmc_alloc_disk(struct mmc_queue *mq,
|
||||
};
|
||||
struct gendisk *disk;
|
||||
|
||||
if (mmc_can_erase(card))
|
||||
if (mmc_card_can_erase(card))
|
||||
mmc_queue_setup_discard(card, &lim);
|
||||
|
||||
lim.max_hw_sectors = min(host->max_blk_count, host->max_req_size / 512);
|
||||
|
||||
@@ -34,6 +34,16 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = {
|
||||
MMC_QUIRK_BROKEN_SD_CACHE | MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY,
|
||||
EXT_CSD_REV_ANY),
|
||||
|
||||
/*
|
||||
* Swissbit series S46-u cards throw I/O errors during tuning requests
|
||||
* after the initial tuning request expectedly times out. This has
|
||||
* only been observed on cards manufactured on 01/2019 that are using
|
||||
* Bay Trail host controllers.
|
||||
*/
|
||||
_FIXUP_EXT("0016G", CID_MANFID_SWISSBIT, 0x5342, 2019, 1,
|
||||
0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
|
||||
MMC_QUIRK_NO_UHS_DDR50_TUNING, EXT_CSD_REV_ANY),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@ static void sd_update_bus_speed_mode(struct mmc_card *card)
|
||||
* If the host doesn't support any of the UHS-I modes, fallback on
|
||||
* default speed.
|
||||
*/
|
||||
if (!mmc_host_uhs(card->host)) {
|
||||
if (!mmc_host_can_uhs(card->host)) {
|
||||
card->sd_bus_speed = 0;
|
||||
return;
|
||||
}
|
||||
@@ -617,6 +617,29 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the card should tune or not.
|
||||
*/
|
||||
static bool mmc_sd_use_tuning(struct mmc_card *card)
|
||||
{
|
||||
/*
|
||||
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
|
||||
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
|
||||
*/
|
||||
if (mmc_host_is_spi(card->host))
|
||||
return false;
|
||||
|
||||
switch (card->host->ios.timing) {
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
return true;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
return !mmc_card_no_uhs_ddr50_tuning(card);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* UHS-I specific initialization procedure
|
||||
*/
|
||||
@@ -660,14 +683,7 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
|
||||
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
|
||||
*/
|
||||
if (!mmc_host_is_spi(card->host) &&
|
||||
(card->host->ios.timing == MMC_TIMING_UHS_SDR50 ||
|
||||
card->host->ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||
card->host->ios.timing == MMC_TIMING_UHS_SDR104)) {
|
||||
if (mmc_sd_use_tuning(card)) {
|
||||
err = mmc_execute_tuning(card);
|
||||
|
||||
/*
|
||||
@@ -851,7 +867,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
|
||||
* to switch to 1.8V signaling level. If the card has failed
|
||||
* repeatedly to switch however, skip this.
|
||||
*/
|
||||
if (retries && mmc_host_uhs(host))
|
||||
if (retries && mmc_host_can_uhs(host))
|
||||
ocr |= SD_OCR_S18R;
|
||||
|
||||
/*
|
||||
@@ -1493,7 +1509,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
* signaling. Detect that situation and try to initialize a UHS-I (1.8V)
|
||||
* transfer mode.
|
||||
*/
|
||||
if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) &&
|
||||
if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_can_uhs(host) &&
|
||||
mmc_sd_card_using_v18(card) &&
|
||||
host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
|
||||
if (mmc_host_set_uhs_voltage(host) ||
|
||||
@@ -1508,7 +1524,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
|
||||
/* Initialization sequence for UHS-I cards */
|
||||
if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
|
||||
if (rocr & SD_ROCR_S18A && mmc_host_can_uhs(host)) {
|
||||
err = mmc_sd_init_uhs_card(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
@@ -1596,15 +1612,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Host is being removed. Free up the current card.
|
||||
*/
|
||||
static void mmc_sd_remove(struct mmc_host *host)
|
||||
{
|
||||
mmc_remove_card(host->card);
|
||||
host->card = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection - card is alive.
|
||||
*/
|
||||
@@ -1630,7 +1637,8 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||
mmc_put_card(host->card, NULL);
|
||||
|
||||
if (err) {
|
||||
mmc_sd_remove(host);
|
||||
mmc_remove_card(host->card);
|
||||
host->card = NULL;
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
@@ -1730,6 +1738,19 @@ static int _mmc_sd_suspend(struct mmc_host *host)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Host is being removed. Free up the current card and do a graceful power-off.
|
||||
*/
|
||||
static void mmc_sd_remove(struct mmc_host *host)
|
||||
{
|
||||
get_device(&host->card->dev);
|
||||
mmc_remove_card(host->card);
|
||||
|
||||
_mmc_sd_suspend(host);
|
||||
|
||||
put_device(&host->card->dev);
|
||||
host->card = NULL;
|
||||
}
|
||||
/*
|
||||
* Callback for suspend
|
||||
*/
|
||||
|
||||
@@ -198,7 +198,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (mmc_host_uhs(card->host)) {
|
||||
if (mmc_host_can_uhs(card->host)) {
|
||||
if (data & SDIO_UHS_DDR50)
|
||||
card->sw_caps.sd3_bus_mode
|
||||
|= SD_MODE_UHS_DDR50 | SD_MODE_UHS_SDR50
|
||||
@@ -527,7 +527,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
|
||||
* If the host doesn't support any of the UHS-I modes, fallback on
|
||||
* default speed.
|
||||
*/
|
||||
if (!mmc_host_uhs(card->host))
|
||||
if (!mmc_host_can_uhs(card->host))
|
||||
return 0;
|
||||
|
||||
bus_speed = SDIO_SPEED_SDR12;
|
||||
@@ -669,7 +669,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
/* to query card if 1.8V signalling is supported */
|
||||
if (mmc_host_uhs(host))
|
||||
if (mmc_host_can_uhs(host))
|
||||
ocr |= R4_18V_PRESENT;
|
||||
|
||||
try_again:
|
||||
|
||||
@@ -228,13 +228,13 @@ int mmc_gpiod_set_cd_config(struct mmc_host *host, unsigned long config)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_set_cd_config);
|
||||
|
||||
bool mmc_can_gpio_cd(struct mmc_host *host)
|
||||
bool mmc_host_can_gpio_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
return ctx->cd_gpio ? true : false;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_gpio_cd);
|
||||
EXPORT_SYMBOL(mmc_host_can_gpio_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_request_ro - request a gpio descriptor for write protection
|
||||
@@ -275,10 +275,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_request_ro);
|
||||
|
||||
bool mmc_can_gpio_ro(struct mmc_host *host)
|
||||
bool mmc_host_can_gpio_ro(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
return ctx->ro_gpio ? true : false;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_gpio_ro);
|
||||
EXPORT_SYMBOL(mmc_host_can_gpio_ro);
|
||||
|
||||
@@ -250,6 +250,20 @@ config MMC_SDHCI_OF_DWCMSHC
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_K1
|
||||
tristate "SDHCI OF support for the SpacemiT K1 SoC"
|
||||
depends on ARCH_SPACEMIT || COMPILE_TEST
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
depends on COMMON_CLK
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
found in the SpacemiT K1 SoC.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_SPARX5
|
||||
tristate "SDHCI OF support for the MCHP Sparx5 SoC"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
|
||||
@@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_K1) += sdhci-of-k1.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
@@ -208,7 +209,7 @@ static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read)
|
||||
len = min(host->sg_miter.length, blksize);
|
||||
|
||||
dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n",
|
||||
read ? "read" : "write", blksize);
|
||||
str_read_write(read), blksize);
|
||||
|
||||
host->sg_miter.consumed = len;
|
||||
host->blocks--;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
@@ -391,8 +392,7 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
|
||||
|
||||
if (time_after(jiffies, wait_max)) {
|
||||
dev_err(dev, "PIO %s timeout - EDM %08x\n",
|
||||
is_read ? "read" : "write",
|
||||
edm);
|
||||
str_read_write(is_read), edm);
|
||||
hsts = SDHSTS_REW_TIME_OUT;
|
||||
break;
|
||||
}
|
||||
@@ -435,12 +435,12 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host)
|
||||
SDHSTS_CRC7_ERROR |
|
||||
SDHSTS_FIFO_ERROR)) {
|
||||
dev_err(dev, "%s transfer error - HSTS %08x\n",
|
||||
is_read ? "read" : "write", sdhsts);
|
||||
str_read_write(is_read), sdhsts);
|
||||
host->data->error = -EILSEQ;
|
||||
} else if ((sdhsts & (SDHSTS_CMD_TIME_OUT |
|
||||
SDHSTS_REW_TIME_OUT))) {
|
||||
dev_err(dev, "%s timeout error - HSTS %08x\n",
|
||||
is_read ? "read" : "write", sdhsts);
|
||||
str_read_write(is_read), sdhsts);
|
||||
host->data->error = -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_request_regions(pdev, KBUILD_MODNAME);
|
||||
ret = pcim_request_all_regions(pdev, KBUILD_MODNAME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -164,7 +164,6 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
||||
}
|
||||
}
|
||||
clk_disable_unprepare(host->clk);
|
||||
pci_release_regions(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -183,7 +182,6 @@ static void thunder_mmc_remove(struct pci_dev *pdev)
|
||||
writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host));
|
||||
|
||||
clk_disable_unprepare(host->clk);
|
||||
pci_release_regions(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id thunder_mmc_id_table[] = {
|
||||
|
||||
@@ -3622,7 +3622,7 @@ int dw_mci_runtime_suspend(struct device *dev)
|
||||
clk_disable_unprepare(host->ciu_clk);
|
||||
|
||||
if (host->slot &&
|
||||
(mmc_can_gpio_cd(host->slot->mmc) ||
|
||||
(mmc_host_can_gpio_cd(host->slot->mmc) ||
|
||||
!mmc_card_is_removable(host->slot->mmc)))
|
||||
clk_disable_unprepare(host->biu_clk);
|
||||
|
||||
@@ -3636,7 +3636,7 @@ int dw_mci_runtime_resume(struct device *dev)
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
if (host->slot &&
|
||||
(mmc_can_gpio_cd(host->slot->mmc) ||
|
||||
(mmc_host_can_gpio_cd(host->slot->mmc) ||
|
||||
!mmc_card_is_removable(host->slot->mmc))) {
|
||||
ret = clk_prepare_enable(host->biu_clk);
|
||||
if (ret)
|
||||
@@ -3690,7 +3690,7 @@ int dw_mci_runtime_resume(struct device *dev)
|
||||
|
||||
err:
|
||||
if (host->slot &&
|
||||
(mmc_can_gpio_cd(host->slot->mmc) ||
|
||||
(mmc_host_can_gpio_cd(host->slot->mmc) ||
|
||||
!mmc_card_is_removable(host->slot->mmc)))
|
||||
clk_disable_unprepare(host->biu_clk);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
@@ -83,6 +84,7 @@
|
||||
#define EMMC51_CFG0 0x204
|
||||
#define EMMC50_CFG0 0x208
|
||||
#define EMMC50_CFG1 0x20c
|
||||
#define EMMC50_CFG2 0x21c
|
||||
#define EMMC50_CFG3 0x220
|
||||
#define SDC_FIFO_CFG 0x228
|
||||
#define CQHCI_SETTING 0x7fc
|
||||
@@ -233,7 +235,9 @@
|
||||
|
||||
/* MSDC_PATCH_BIT mask */
|
||||
#define MSDC_PATCH_BIT_ODDSUPP BIT(1) /* RW */
|
||||
#define MSDC_PATCH_BIT_DIS_WRMON BIT(2) /* RW */
|
||||
#define MSDC_PATCH_BIT_RD_DAT_SEL BIT(3) /* RW */
|
||||
#define MSDC_PATCH_BIT_DESCUP_SEL BIT(6) /* RW */
|
||||
#define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7)
|
||||
#define MSDC_CKGEN_MSDC_DLY_SEL GENMASK(14, 10)
|
||||
#define MSDC_PATCH_BIT_IODSSEL BIT(16) /* RW */
|
||||
@@ -246,10 +250,22 @@
|
||||
#define MSDC_PATCH_BIT_SPCPUSH BIT(29) /* RW */
|
||||
#define MSDC_PATCH_BIT_DECRCTMO BIT(30) /* RW */
|
||||
|
||||
#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
|
||||
/* MSDC_PATCH_BIT1 mask */
|
||||
#define MSDC_PB1_WRDAT_CRC_TACNTR GENMASK(2, 0) /* RW */
|
||||
#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
|
||||
#define MSDC_PB1_BUSY_CHECK_SEL BIT(7) /* RW */
|
||||
#define MSDC_PATCH_BIT1_STOP_DLY GENMASK(11, 8) /* RW */
|
||||
#define MSDC_PB1_DDR_CMD_FIX_SEL BIT(14) /* RW */
|
||||
#define MSDC_PB1_SINGLE_BURST BIT(16) /* RW */
|
||||
#define MSDC_PB1_RSVD20 GENMASK(18, 17) /* RW */
|
||||
#define MSDC_PB1_AUTO_SYNCST_CLR BIT(19) /* RW */
|
||||
#define MSDC_PB1_MARK_POP_WATER BIT(20) /* RW */
|
||||
#define MSDC_PB1_LP_DCM_EN BIT(21) /* RW */
|
||||
#define MSDC_PB1_RSVD3 BIT(22) /* RW */
|
||||
#define MSDC_PB1_AHB_GDMA_HCLK BIT(23) /* RW */
|
||||
#define MSDC_PB1_MSDC_CLK_ENFEAT GENMASK(31, 24) /* RW */
|
||||
|
||||
/* MSDC_PATCH_BIT2 mask */
|
||||
#define MSDC_PATCH_BIT2_CFGRESP BIT(15) /* RW */
|
||||
#define MSDC_PATCH_BIT2_CFGCRCSTS BIT(28) /* RW */
|
||||
#define MSDC_PB2_SUPPORT_64G BIT(1) /* RW */
|
||||
@@ -291,7 +307,10 @@
|
||||
/* EMMC50_CFG1 mask */
|
||||
#define EMMC50_CFG1_DS_CFG BIT(28) /* RW */
|
||||
|
||||
#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
|
||||
/* EMMC50_CFG2 mask */
|
||||
#define EMMC50_CFG2_AXI_SET_LEN GENMASK(27, 24) /* RW */
|
||||
|
||||
#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
|
||||
|
||||
#define SDC_FIFO_CFG_WRVALIDSEL BIT(24) /* RW */
|
||||
#define SDC_FIFO_CFG_RDVALIDSEL BIT(25) /* RW */
|
||||
@@ -927,15 +946,15 @@ static int msdc_ungate_clock(struct msdc_host *host)
|
||||
|
||||
static void msdc_new_tx_setting(struct msdc_host *host)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!host->top_base)
|
||||
return;
|
||||
|
||||
sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
|
||||
TEST_LOOP_DSCLK_MUX_SEL);
|
||||
sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
|
||||
TEST_LOOP_LATCH_MUX_SEL);
|
||||
sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
|
||||
TEST_HS400_CMD_LOOP_MUX_SEL);
|
||||
val = readl(host->top_base + LOOP_TEST_CONTROL);
|
||||
val |= TEST_LOOP_DSCLK_MUX_SEL;
|
||||
val |= TEST_LOOP_LATCH_MUX_SEL;
|
||||
val &= ~TEST_HS400_CMD_LOOP_MUX_SEL;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_LEGACY:
|
||||
@@ -945,19 +964,18 @@ static void msdc_new_tx_setting(struct msdc_host *host)
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
|
||||
LOOP_EN_SEL_CLK);
|
||||
val &= ~LOOP_EN_SEL_CLK;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
|
||||
LOOP_EN_SEL_CLK);
|
||||
val |= LOOP_EN_SEL_CLK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
writel(val, host->top_base + LOOP_TEST_CONTROL);
|
||||
}
|
||||
|
||||
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
||||
@@ -1816,7 +1834,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
|
||||
|
||||
static void msdc_init_hw(struct msdc_host *host)
|
||||
{
|
||||
u32 val;
|
||||
u32 val, pb1_val, pb2_val;
|
||||
u32 tune_reg = host->dev_comp->pad_tune_reg;
|
||||
struct mmc_host *mmc = mmc_from_priv(host);
|
||||
|
||||
@@ -1869,71 +1887,115 @@ static void msdc_init_hw(struct msdc_host *host)
|
||||
}
|
||||
writel(0, host->base + MSDC_IOCON);
|
||||
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
|
||||
writel(0x403c0046, host->base + MSDC_PATCH_BIT);
|
||||
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
|
||||
writel(0xffff4089, host->base + MSDC_PATCH_BIT1);
|
||||
sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
|
||||
|
||||
/*
|
||||
* Patch bit 0 and 1 are completely rewritten, but for patch bit 2
|
||||
* defaults are retained and, if necessary, only some bits are fixed
|
||||
* up: read the PB2 register here for later usage in this function.
|
||||
*/
|
||||
pb2_val = readl(host->base + MSDC_PATCH_BIT2);
|
||||
|
||||
/* Enable odd number support for 8-bit data bus */
|
||||
val = MSDC_PATCH_BIT_ODDSUPP;
|
||||
|
||||
/* Disable SD command register write monitor */
|
||||
val |= MSDC_PATCH_BIT_DIS_WRMON;
|
||||
|
||||
/* Issue transfer done interrupt after GPD update */
|
||||
val |= MSDC_PATCH_BIT_DESCUP_SEL;
|
||||
|
||||
/* Extend R1B busy detection delay (in clock cycles) */
|
||||
val |= FIELD_PREP(MSDC_PATCH_BIT_BUSYDLY, 15);
|
||||
|
||||
/* Enable CRC phase timeout during data write operation */
|
||||
val |= MSDC_PATCH_BIT_DECRCTMO;
|
||||
|
||||
/* Set CKGEN delay to one stage */
|
||||
val |= FIELD_PREP(MSDC_CKGEN_MSDC_DLY_SEL, 1);
|
||||
|
||||
/* First MSDC_PATCH_BIT setup is done: pull the trigger! */
|
||||
writel(val, host->base + MSDC_PATCH_BIT);
|
||||
|
||||
/* Set wr data, crc status, cmd response turnaround period for UHS104 */
|
||||
pb1_val = FIELD_PREP(MSDC_PB1_WRDAT_CRC_TACNTR, 1);
|
||||
pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_CMDTA, 1);
|
||||
pb1_val |= MSDC_PB1_DDR_CMD_FIX_SEL;
|
||||
|
||||
/* Support 'single' burst type only when AXI_LEN is 0 */
|
||||
sdr_get_field(host->base + EMMC50_CFG2, EMMC50_CFG2_AXI_SET_LEN, &val);
|
||||
if (!val)
|
||||
pb1_val |= MSDC_PB1_SINGLE_BURST;
|
||||
|
||||
/* Set auto sync state clear, block gap stop clk */
|
||||
pb1_val |= MSDC_PB1_RSVD20 | MSDC_PB1_AUTO_SYNCST_CLR | MSDC_PB1_MARK_POP_WATER;
|
||||
|
||||
/* Set low power DCM, use HCLK for GDMA, use MSDC CLK for everything else */
|
||||
pb1_val |= MSDC_PB1_LP_DCM_EN | MSDC_PB1_RSVD3 |
|
||||
MSDC_PB1_AHB_GDMA_HCLK | MSDC_PB1_MSDC_CLK_ENFEAT;
|
||||
|
||||
/* If needed, enable R1b command busy check at controller init time */
|
||||
if (!host->dev_comp->busy_check)
|
||||
pb1_val |= MSDC_PB1_BUSY_CHECK_SEL;
|
||||
|
||||
if (host->dev_comp->stop_clk_fix) {
|
||||
if (host->dev_comp->stop_dly_sel)
|
||||
sdr_set_field(host->base + MSDC_PATCH_BIT1,
|
||||
MSDC_PATCH_BIT1_STOP_DLY,
|
||||
host->dev_comp->stop_dly_sel);
|
||||
pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_STOP_DLY,
|
||||
host->dev_comp->stop_dly_sel);
|
||||
|
||||
if (host->dev_comp->pop_en_cnt)
|
||||
sdr_set_field(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PB2_POP_EN_CNT,
|
||||
host->dev_comp->pop_en_cnt);
|
||||
if (host->dev_comp->pop_en_cnt) {
|
||||
pb2_val &= ~MSDC_PB2_POP_EN_CNT;
|
||||
pb2_val |= FIELD_PREP(MSDC_PB2_POP_EN_CNT,
|
||||
host->dev_comp->pop_en_cnt);
|
||||
}
|
||||
|
||||
sdr_clr_bits(host->base + SDC_FIFO_CFG,
|
||||
SDC_FIFO_CFG_WRVALIDSEL);
|
||||
sdr_clr_bits(host->base + SDC_FIFO_CFG,
|
||||
SDC_FIFO_CFG_RDVALIDSEL);
|
||||
sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL);
|
||||
sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL);
|
||||
}
|
||||
|
||||
if (host->dev_comp->busy_check)
|
||||
sdr_clr_bits(host->base + MSDC_PATCH_BIT1, BIT(7));
|
||||
|
||||
if (host->dev_comp->async_fifo) {
|
||||
sdr_set_field(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PB2_RESPWAIT, 3);
|
||||
if (host->dev_comp->enhance_rx) {
|
||||
if (host->top_base)
|
||||
sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
|
||||
SDC_RX_ENH_EN);
|
||||
else
|
||||
sdr_set_bits(host->base + SDC_ADV_CFG0,
|
||||
SDC_RX_ENHANCE_EN);
|
||||
/* Set CMD response timeout multiplier to 65 + (16 * 3) cycles */
|
||||
pb2_val &= ~MSDC_PB2_RESPWAIT;
|
||||
pb2_val |= FIELD_PREP(MSDC_PB2_RESPWAIT, 3);
|
||||
|
||||
/* eMMC4.5: Select async FIFO path for CMD resp and CRC status */
|
||||
pb2_val &= ~MSDC_PATCH_BIT2_CFGRESP;
|
||||
pb2_val |= MSDC_PATCH_BIT2_CFGCRCSTS;
|
||||
|
||||
if (!host->dev_comp->enhance_rx) {
|
||||
/* eMMC4.5: Delay 2T for CMD resp and CRC status EN signals */
|
||||
pb2_val &= ~(MSDC_PB2_RESPSTSENSEL | MSDC_PB2_CRCSTSENSEL);
|
||||
pb2_val |= FIELD_PREP(MSDC_PB2_RESPSTSENSEL, 2);
|
||||
pb2_val |= FIELD_PREP(MSDC_PB2_CRCSTSENSEL, 2);
|
||||
} else if (host->top_base) {
|
||||
sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, SDC_RX_ENH_EN);
|
||||
} else {
|
||||
sdr_set_field(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PB2_RESPSTSENSEL, 2);
|
||||
sdr_set_field(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PB2_CRCSTSENSEL, 2);
|
||||
sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_RX_ENHANCE_EN);
|
||||
}
|
||||
/* use async fifo, then no need tune internal delay */
|
||||
sdr_clr_bits(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PATCH_BIT2_CFGRESP);
|
||||
sdr_set_bits(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PATCH_BIT2_CFGCRCSTS);
|
||||
}
|
||||
|
||||
if (host->dev_comp->support_64g)
|
||||
sdr_set_bits(host->base + MSDC_PATCH_BIT2,
|
||||
MSDC_PB2_SUPPORT_64G);
|
||||
pb2_val |= MSDC_PB2_SUPPORT_64G;
|
||||
|
||||
/* Patch Bit 1/2 setup is done: pull the trigger! */
|
||||
writel(pb1_val, host->base + MSDC_PATCH_BIT1);
|
||||
writel(pb2_val, host->base + MSDC_PATCH_BIT2);
|
||||
sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
|
||||
|
||||
if (host->dev_comp->data_tune) {
|
||||
if (host->top_base) {
|
||||
sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
|
||||
PAD_DAT_RD_RXDLY_SEL);
|
||||
sdr_clr_bits(host->top_base + EMMC_TOP_CONTROL,
|
||||
DATA_K_VALUE_SEL);
|
||||
sdr_set_bits(host->top_base + EMMC_TOP_CMD,
|
||||
PAD_CMD_RD_RXDLY_SEL);
|
||||
u32 top_ctl_val = readl(host->top_base + EMMC_TOP_CONTROL);
|
||||
u32 top_cmd_val = readl(host->top_base + EMMC_TOP_CMD);
|
||||
|
||||
top_cmd_val |= PAD_CMD_RD_RXDLY_SEL;
|
||||
top_ctl_val |= PAD_DAT_RD_RXDLY_SEL;
|
||||
top_ctl_val &= ~DATA_K_VALUE_SEL;
|
||||
if (host->tuning_step > PAD_DELAY_HALF) {
|
||||
sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
|
||||
PAD_DAT_RD_RXDLY2_SEL);
|
||||
sdr_set_bits(host->top_base + EMMC_TOP_CMD,
|
||||
PAD_CMD_RD_RXDLY2_SEL);
|
||||
top_cmd_val |= PAD_CMD_RD_RXDLY2_SEL;
|
||||
top_ctl_val |= PAD_DAT_RD_RXDLY2_SEL;
|
||||
}
|
||||
|
||||
writel(top_ctl_val, host->top_base + EMMC_TOP_CONTROL);
|
||||
writel(top_cmd_val, host->top_base + EMMC_TOP_CMD);
|
||||
} else {
|
||||
sdr_set_bits(host->base + tune_reg,
|
||||
MSDC_PAD_TUNE_RD_SEL |
|
||||
@@ -2143,15 +2205,17 @@ static inline void msdc_set_cmd_delay(struct msdc_host *host, u32 value)
|
||||
u32 tune_reg = host->dev_comp->pad_tune_reg;
|
||||
|
||||
if (host->top_base) {
|
||||
u32 regval = readl(host->top_base + EMMC_TOP_CMD);
|
||||
|
||||
regval &= ~(PAD_CMD_RXDLY | PAD_CMD_RXDLY2);
|
||||
|
||||
if (value < PAD_DELAY_HALF) {
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY, value);
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2, 0);
|
||||
regval |= FIELD_PREP(PAD_CMD_RXDLY, value);
|
||||
} else {
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY,
|
||||
PAD_DELAY_HALF - 1);
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2,
|
||||
value - PAD_DELAY_HALF);
|
||||
regval |= FIELD_PREP(PAD_CMD_RXDLY, PAD_DELAY_HALF - 1);
|
||||
regval |= FIELD_PREP(PAD_CMD_RXDLY2, value - PAD_DELAY_HALF);
|
||||
}
|
||||
writel(regval, host->top_base + EMMC_TOP_CMD);
|
||||
} else {
|
||||
if (value < PAD_DELAY_HALF) {
|
||||
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY, value);
|
||||
@@ -2171,17 +2235,18 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value)
|
||||
u32 tune_reg = host->dev_comp->pad_tune_reg;
|
||||
|
||||
if (host->top_base) {
|
||||
u32 regval = readl(host->top_base + EMMC_TOP_CONTROL);
|
||||
|
||||
regval &= ~(PAD_DAT_RD_RXDLY | PAD_DAT_RD_RXDLY2);
|
||||
|
||||
if (value < PAD_DELAY_HALF) {
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
|
||||
PAD_DAT_RD_RXDLY, value);
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
|
||||
PAD_DAT_RD_RXDLY2, 0);
|
||||
regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, value);
|
||||
regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value);
|
||||
} else {
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
|
||||
PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
|
||||
sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
|
||||
PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
|
||||
regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
|
||||
regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
|
||||
}
|
||||
writel(regval, host->top_base + EMMC_TOP_CONTROL);
|
||||
} else {
|
||||
if (value < PAD_DELAY_HALF) {
|
||||
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_DATRRDLY, value);
|
||||
@@ -2977,7 +3042,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
|
||||
|
||||
if (!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
||||
!mmc_can_gpio_cd(mmc) &&
|
||||
!mmc_host_can_gpio_cd(mmc) &&
|
||||
host->dev_comp->use_internal_cd) {
|
||||
/*
|
||||
* Is removable but no GPIO declared, so
|
||||
|
||||
@@ -1112,7 +1112,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
|
||||
|
||||
/* For some SoC, we disable internal WP. GPIO may override this */
|
||||
if (mmc_can_gpio_ro(host->mmc))
|
||||
if (mmc_host_can_gpio_ro(host->mmc))
|
||||
mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
|
||||
|
||||
/* SDR speeds are only available on Gen2+ */
|
||||
@@ -1166,12 +1166,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
if (ret)
|
||||
goto efree;
|
||||
|
||||
rcfg.of_node = of_get_child_by_name(dev->of_node, "vqmmc-regulator");
|
||||
if (!of_device_is_available(rcfg.of_node)) {
|
||||
of_node_put(rcfg.of_node);
|
||||
rcfg.of_node = NULL;
|
||||
}
|
||||
|
||||
rcfg.of_node = of_get_available_child_by_name(dev->of_node, "vqmmc-regulator");
|
||||
if (rcfg.of_node) {
|
||||
rcfg.driver_data = priv->host;
|
||||
rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
|
||||
@@ -1240,15 +1235,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
|
||||
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all);
|
||||
|
||||
num_irqs = platform_irq_count(pdev);
|
||||
if (num_irqs < 0) {
|
||||
ret = num_irqs;
|
||||
goto edisclk;
|
||||
}
|
||||
|
||||
/* There must be at least one IRQ source */
|
||||
if (!num_irqs) {
|
||||
ret = -ENXIO;
|
||||
num_irqs = platform_irq_count(pdev);
|
||||
if (num_irqs <= 0) {
|
||||
ret = num_irqs ?: -ENOENT;
|
||||
goto edisclk;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
#include "cqhci.h"
|
||||
|
||||
#define ESDHC_SYS_CTRL_DTOCV_MASK GENMASK(19, 16)
|
||||
#define ESDHC_SYS_CTRL_RST_FIFO BIT(22)
|
||||
#define ESDHC_SYS_CTRL_IPP_RST_N BIT(23)
|
||||
#define ESDHC_SYS_CTRL_RESET_TUNING BIT(28)
|
||||
#define ESDHC_CTRL_D3CD 0x08
|
||||
#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
|
||||
/* VENDOR SPEC register */
|
||||
@@ -81,7 +83,11 @@
|
||||
#define ESDHC_TUNE_CTRL_STEP 1
|
||||
#define ESDHC_TUNE_CTRL_MIN 0
|
||||
#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
|
||||
|
||||
#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK GENMASK(30, 16)
|
||||
#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK GENMASK(30, 24)
|
||||
#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK GENMASK(14, 8)
|
||||
#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK GENMASK(7, 4)
|
||||
#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK GENMASK(3, 0)
|
||||
/* strobe dll register */
|
||||
#define ESDHC_STROBE_DLL_CTRL 0x70
|
||||
#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
|
||||
@@ -104,6 +110,7 @@
|
||||
|
||||
#define ESDHC_TUNING_CTRL 0xcc
|
||||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||
#define ESDHC_TUNING_WINDOW_MASK GENMASK(22, 20)
|
||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
|
||||
#define ESDHC_TUNING_START_TAP_MASK 0x7f
|
||||
@@ -205,6 +212,8 @@
|
||||
/* The IP does not have GPIO CD wake capabilities */
|
||||
#define ESDHC_FLAG_SKIP_CD_WAKE BIT(18)
|
||||
|
||||
#define ESDHC_AUTO_TUNING_WINDOW 3
|
||||
|
||||
enum wp_types {
|
||||
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
|
||||
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
|
||||
@@ -235,6 +244,8 @@ struct esdhc_platform_data {
|
||||
unsigned int tuning_step; /* The delay cell steps in tuning procedure */
|
||||
unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */
|
||||
unsigned int strobe_dll_delay_target; /* The delay cell for strobe pad (read clock) */
|
||||
unsigned int saved_tuning_delay_cell; /* save the value of tuning delay cell */
|
||||
unsigned int saved_auto_tuning_window; /* save the auto tuning window width */
|
||||
};
|
||||
|
||||
struct esdhc_soc_data {
|
||||
@@ -264,35 +275,35 @@ static const struct esdhc_soc_data usdhc_imx6q_data = {
|
||||
};
|
||||
|
||||
static const struct esdhc_soc_data usdhc_imx6sl_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
|
||||
| ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
|
||||
};
|
||||
|
||||
static const struct esdhc_soc_data usdhc_imx6sll_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
|
||||
};
|
||||
|
||||
static const struct esdhc_soc_data usdhc_imx6sx_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
|
||||
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
|
||||
};
|
||||
|
||||
static const struct esdhc_soc_data usdhc_imx6ull_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_ERR010450
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
|
||||
};
|
||||
|
||||
static const struct esdhc_soc_data usdhc_imx7d_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
|
||||
@@ -308,7 +319,7 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx7ulp_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
|
||||
@@ -321,7 +332,7 @@ static struct esdhc_soc_data usdhc_imxrt1050_data = {
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx8qxp_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
|
||||
@@ -330,7 +341,7 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = {
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx8mm_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
|
||||
@@ -870,6 +881,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
||||
|
||||
esdhc_clrset_le(host, mask, new_val, reg);
|
||||
return;
|
||||
case SDHCI_TIMEOUT_CONTROL:
|
||||
esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
|
||||
FIELD_PREP(ESDHC_SYS_CTRL_DTOCV_MASK, val),
|
||||
ESDHC_SYSTEM_CONTROL);
|
||||
return;
|
||||
case SDHCI_SOFTWARE_RESET:
|
||||
if (val & SDHCI_RESET_DATA)
|
||||
new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
|
||||
@@ -1057,7 +1073,7 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 ctrl;
|
||||
u32 ctrl, tuning_ctrl, sys_ctrl;
|
||||
int ret;
|
||||
|
||||
/* Reset the tuning circuit */
|
||||
@@ -1071,6 +1087,21 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
|
||||
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
/*
|
||||
* enable the std tuning just in case it cleared in
|
||||
* sdhc_esdhc_tuning_restore.
|
||||
*/
|
||||
tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
if (!(tuning_ctrl & ESDHC_STD_TUNING_EN)) {
|
||||
tuning_ctrl |= ESDHC_STD_TUNING_EN;
|
||||
writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
}
|
||||
|
||||
/* set the reset tuning bit */
|
||||
sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
|
||||
sys_ctrl |= ESDHC_SYS_CTRL_RESET_TUNING;
|
||||
writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
@@ -1130,7 +1161,7 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
|
||||
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
||||
{
|
||||
u32 reg;
|
||||
u32 reg, sys_ctrl;
|
||||
u8 sw_rst;
|
||||
int ret;
|
||||
|
||||
@@ -1149,10 +1180,21 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
||||
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
||||
ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, val),
|
||||
host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
|
||||
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
|
||||
|
||||
/* set RST_FIFO to reset the async FIFO, and wat it to self-clear */
|
||||
sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
|
||||
sys_ctrl |= ESDHC_SYS_CTRL_RST_FIFO;
|
||||
writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL);
|
||||
ret = readl_poll_timeout(host->ioaddr + ESDHC_SYSTEM_CONTROL, sys_ctrl,
|
||||
!(sys_ctrl & ESDHC_SYS_CTRL_RST_FIFO), 10, 100);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! RST_FIFO not clear in 100us\n");
|
||||
}
|
||||
|
||||
static void esdhc_post_tuning(struct sdhci_host *host)
|
||||
@@ -1172,9 +1214,10 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
int min, max, avg, ret;
|
||||
int win_length, target_min, target_max, target_win_length;
|
||||
u32 clk_tune_ctrl_status, temp;
|
||||
|
||||
min = ESDHC_TUNE_CTRL_MIN;
|
||||
max = ESDHC_TUNE_CTRL_MIN;
|
||||
min = target_min = ESDHC_TUNE_CTRL_MIN;
|
||||
max = target_max = ESDHC_TUNE_CTRL_MIN;
|
||||
target_win_length = 0;
|
||||
while (max < ESDHC_TUNE_CTRL_MAX) {
|
||||
/* find the mininum delay first which can pass tuning */
|
||||
@@ -1211,6 +1254,30 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
||||
/* use average delay to get the best timing */
|
||||
avg = (target_min + target_max) / 2;
|
||||
esdhc_prepare_tuning(host, avg);
|
||||
|
||||
/*
|
||||
* adjust the delay according to tuning window, make preparation
|
||||
* for the auto-tuning logic. According to hardware suggest, need
|
||||
* to config the auto tuning window width to 3, to make the auto
|
||||
* tuning logic have enough space to handle the sample point shift
|
||||
* caused by temperature change.
|
||||
*/
|
||||
clk_tune_ctrl_status = FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK,
|
||||
avg - ESDHC_AUTO_TUNING_WINDOW) |
|
||||
FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK,
|
||||
ESDHC_AUTO_TUNING_WINDOW) |
|
||||
FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK,
|
||||
ESDHC_AUTO_TUNING_WINDOW);
|
||||
|
||||
writel(clk_tune_ctrl_status, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
ret = readl_poll_timeout(host->ioaddr + ESDHC_TUNE_CTRL_STATUS, temp,
|
||||
clk_tune_ctrl_status ==
|
||||
FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK, temp),
|
||||
1, 10);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"clock tuning control status not set in 10us\n");
|
||||
|
||||
ret = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
esdhc_post_tuning(host);
|
||||
|
||||
@@ -1385,17 +1452,6 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
|
||||
return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
|
||||
}
|
||||
|
||||
static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
/* use maximum timeout counter */
|
||||
esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
|
||||
esdhc_is_usdhc(imx_data) ? 0xF0000 : 0xE0000,
|
||||
ESDHC_SYSTEM_CONTROL);
|
||||
}
|
||||
|
||||
static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
int cmd_error = 0;
|
||||
@@ -1432,7 +1488,6 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.get_min_clock = esdhc_pltfm_get_min_clock,
|
||||
.get_max_timeout_count = esdhc_get_max_timeout_count,
|
||||
.get_ro = esdhc_pltfm_get_ro,
|
||||
.set_timeout = esdhc_set_timeout,
|
||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.set_uhs_signaling = esdhc_set_uhs_signaling,
|
||||
.reset = esdhc_reset,
|
||||
@@ -1529,6 +1584,16 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
||||
<< ESDHC_TUNING_STEP_SHIFT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Config the tuning window to the hardware suggested value 3.
|
||||
* This tuning window is used for auto tuning logic. The default
|
||||
* tuning window is 2, here change to 3 make the window a bit
|
||||
* wider, give auto tuning enough space to handle the sample
|
||||
* point shift cause by temperature change.
|
||||
*/
|
||||
tmp &= ~ESDHC_TUNING_WINDOW_MASK;
|
||||
tmp |= FIELD_PREP(ESDHC_TUNING_WINDOW_MASK, ESDHC_AUTO_TUNING_WINDOW);
|
||||
|
||||
/* Disable the CMD CRC check for tuning, if not, need to
|
||||
* add some delay after every tuning command, because
|
||||
* hardware standard tuning logic will directly go to next
|
||||
@@ -1569,6 +1634,63 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void sdhc_esdhc_tuning_save(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* SD/eMMC do not need this tuning save because it will re-init
|
||||
* after system resume back.
|
||||
* Here save the tuning delay value for SDIO device since it may
|
||||
* keep power during system PM. And for usdhc, only SDR50 and
|
||||
* SDR104 mode for SDIO device need to do tuning, and need to
|
||||
* save/restore.
|
||||
*/
|
||||
if (host->timing == MMC_TIMING_UHS_SDR50 ||
|
||||
host->timing == MMC_TIMING_UHS_SDR104) {
|
||||
reg = readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
reg = FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK, reg);
|
||||
imx_data->boarddata.saved_tuning_delay_cell = reg;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhc_esdhc_tuning_restore(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 reg;
|
||||
|
||||
if (host->timing == MMC_TIMING_UHS_SDR50 ||
|
||||
host->timing == MMC_TIMING_UHS_SDR104) {
|
||||
/*
|
||||
* restore the tuning delay value actually is a
|
||||
* manual tuning method, so clear the standard
|
||||
* tuning enable bit here. Will set back this
|
||||
* ESDHC_STD_TUNING_EN in esdhc_reset_tuning()
|
||||
* when trigger re-tuning.
|
||||
*/
|
||||
reg = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
reg &= ~ESDHC_STD_TUNING_EN;
|
||||
writel(reg, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
|
||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
reg |= ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
|
||||
writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK,
|
||||
imx_data->boarddata.saved_tuning_delay_cell) |
|
||||
FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK,
|
||||
ESDHC_AUTO_TUNING_WINDOW) |
|
||||
FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK,
|
||||
ESDHC_AUTO_TUNING_WINDOW),
|
||||
host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void esdhc_cqe_enable(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
@@ -1777,6 +1899,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||
* to distinguish the card type.
|
||||
*/
|
||||
host->mmc_host_ops.init_card = usdhc_init_card;
|
||||
|
||||
host->max_timeout_count = 0xF;
|
||||
}
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||
@@ -1885,11 +2009,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
ret = cqhci_suspend(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Switch to runtime resume for two reasons:
|
||||
* 1, there is register access (e.g., wakeup control register), so
|
||||
* need to make sure gate on ipg clock.
|
||||
* 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really
|
||||
* invoke its ->runtime_resume callback (needs_force_resume = 1).
|
||||
*/
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
|
||||
(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
|
||||
@@ -1897,12 +2024,22 @@ static int sdhci_esdhc_suspend(struct device *dev)
|
||||
mmc_retune_needed(host->mmc);
|
||||
}
|
||||
|
||||
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
|
||||
mmc_retune_needed(host->mmc);
|
||||
/*
|
||||
* For the device need to keep power during system PM, need
|
||||
* to save the tuning delay value just in case the usdhc
|
||||
* lost power during system PM.
|
||||
*/
|
||||
if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
|
||||
esdhc_is_usdhc(imx_data))
|
||||
sdhc_esdhc_tuning_save(host);
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (device_may_wakeup(dev)) {
|
||||
/* The irqs of imx are not shared. It is safe to disable */
|
||||
disable_irq(host->irq);
|
||||
ret = sdhci_enable_irq_wakeups(host);
|
||||
if (!ret)
|
||||
dev_warn(dev, "Failed to enable irq wakeup\n");
|
||||
}
|
||||
|
||||
ret = pinctrl_pm_select_sleep_state(dev);
|
||||
if (ret)
|
||||
@@ -1910,30 +2047,46 @@ static int sdhci_esdhc_suspend(struct device *dev)
|
||||
|
||||
ret = mmc_gpio_set_cd_wake(host->mmc, true);
|
||||
|
||||
/*
|
||||
* Make sure invoke runtime_suspend to gate off clock.
|
||||
* uSDHC IP supports in-band SDIO wakeup even without clock.
|
||||
*/
|
||||
pm_runtime_force_suspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
pm_runtime_force_resume(dev);
|
||||
|
||||
ret = mmc_gpio_set_cd_wake(host->mmc, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* re-initialize hw state in case it's lost in low power mode */
|
||||
sdhci_esdhc_imx_hwinit(host);
|
||||
|
||||
ret = sdhci_resume_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (host->irq_wake_enabled) {
|
||||
sdhci_disable_irq_wakeups(host);
|
||||
enable_irq(host->irq);
|
||||
}
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||
ret = cqhci_resume(host->mmc);
|
||||
/*
|
||||
* restore the saved tuning delay value for the device which keep
|
||||
* power during system PM.
|
||||
*/
|
||||
if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
|
||||
esdhc_is_usdhc(imx_data))
|
||||
sdhc_esdhc_tuning_restore(host);
|
||||
|
||||
if (!ret)
|
||||
ret = mmc_gpio_set_cd_wake(host->mmc, false);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
304
drivers/mmc/host/sdhci-of-k1.c
Normal file
304
drivers/mmc/host/sdhci-of-k1.c
Normal file
@@ -0,0 +1,304 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
|
||||
* Copyright (c) 2025 Yixun Lan <dlan@gentoo.org>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHC_MMC_CTRL_REG 0x114
|
||||
#define MISC_INT_EN BIT(1)
|
||||
#define MISC_INT BIT(2)
|
||||
#define ENHANCE_STROBE_EN BIT(8)
|
||||
#define MMC_HS400 BIT(9)
|
||||
#define MMC_HS200 BIT(10)
|
||||
#define MMC_CARD_MODE BIT(12)
|
||||
|
||||
#define SDHC_TX_CFG_REG 0x11C
|
||||
#define TX_INT_CLK_SEL BIT(30)
|
||||
#define TX_MUX_SEL BIT(31)
|
||||
|
||||
#define SDHC_PHY_CTRL_REG 0x160
|
||||
#define PHY_FUNC_EN BIT(0)
|
||||
#define PHY_PLL_LOCK BIT(1)
|
||||
#define HOST_LEGACY_MODE BIT(31)
|
||||
|
||||
#define SDHC_PHY_FUNC_REG 0x164
|
||||
#define PHY_TEST_EN BIT(7)
|
||||
#define HS200_USE_RFIFO BIT(15)
|
||||
|
||||
#define SDHC_PHY_DLLCFG 0x168
|
||||
#define DLL_PREDLY_NUM GENMASK(3, 2)
|
||||
#define DLL_FULLDLY_RANGE GENMASK(5, 4)
|
||||
#define DLL_VREG_CTRL GENMASK(7, 6)
|
||||
#define DLL_ENABLE BIT(31)
|
||||
|
||||
#define SDHC_PHY_DLLCFG1 0x16C
|
||||
#define DLL_REG1_CTRL GENMASK(7, 0)
|
||||
#define DLL_REG2_CTRL GENMASK(15, 8)
|
||||
#define DLL_REG3_CTRL GENMASK(23, 16)
|
||||
#define DLL_REG4_CTRL GENMASK(31, 24)
|
||||
|
||||
#define SDHC_PHY_DLLSTS 0x170
|
||||
#define DLL_LOCK_STATE BIT(0)
|
||||
|
||||
#define SDHC_PHY_PADCFG_REG 0x178
|
||||
#define PHY_DRIVE_SEL GENMASK(2, 0)
|
||||
#define RX_BIAS_CTRL BIT(5)
|
||||
|
||||
struct spacemit_sdhci_host {
|
||||
struct clk *clk_core;
|
||||
struct clk *clk_io;
|
||||
};
|
||||
|
||||
/* All helper functions will update clr/set while preserve rest bits */
|
||||
static inline void spacemit_sdhci_setbits(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
sdhci_writel(host, sdhci_readl(host, reg) | val, reg);
|
||||
}
|
||||
|
||||
static inline void spacemit_sdhci_clrbits(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
sdhci_writel(host, sdhci_readl(host, reg) & ~val, reg);
|
||||
}
|
||||
|
||||
static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u32 set, int reg)
|
||||
{
|
||||
u32 val = sdhci_readl(host, reg);
|
||||
|
||||
val = (val & ~clr) | set;
|
||||
sdhci_writel(host, val, reg);
|
||||
}
|
||||
|
||||
static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
if (mask != SDHCI_RESET_ALL)
|
||||
return;
|
||||
|
||||
spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
|
||||
|
||||
spacemit_sdhci_clrsetbits(host, PHY_DRIVE_SEL,
|
||||
RX_BIAS_CTRL | FIELD_PREP(PHY_DRIVE_SEL, 4),
|
||||
SDHC_PHY_PADCFG_REG);
|
||||
|
||||
if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC))
|
||||
spacemit_sdhci_setbits(host, MMC_CARD_MODE, SDHC_MMC_CTRL_REG);
|
||||
}
|
||||
|
||||
static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
|
||||
{
|
||||
if (timing == MMC_TIMING_MMC_HS200)
|
||||
spacemit_sdhci_setbits(host, MMC_HS200, SDHC_MMC_CTRL_REG);
|
||||
|
||||
if (timing == MMC_TIMING_MMC_HS400)
|
||||
spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG);
|
||||
|
||||
sdhci_set_uhs_signaling(host, timing);
|
||||
|
||||
if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO))
|
||||
spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50)
|
||||
spacemit_sdhci_setbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG);
|
||||
else
|
||||
spacemit_sdhci_clrbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG);
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
};
|
||||
|
||||
static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host)
|
||||
{
|
||||
u32 state;
|
||||
int ret;
|
||||
|
||||
spacemit_sdhci_clrsetbits(host, DLL_PREDLY_NUM | DLL_FULLDLY_RANGE | DLL_VREG_CTRL,
|
||||
FIELD_PREP(DLL_PREDLY_NUM, 1) |
|
||||
FIELD_PREP(DLL_FULLDLY_RANGE, 1) |
|
||||
FIELD_PREP(DLL_VREG_CTRL, 1),
|
||||
SDHC_PHY_DLLCFG);
|
||||
|
||||
spacemit_sdhci_clrsetbits(host, DLL_REG1_CTRL,
|
||||
FIELD_PREP(DLL_REG1_CTRL, 0x92),
|
||||
SDHC_PHY_DLLCFG1);
|
||||
|
||||
spacemit_sdhci_setbits(host, DLL_ENABLE, SDHC_PHY_DLLCFG);
|
||||
|
||||
ret = readl_poll_timeout(host->ioaddr + SDHC_PHY_DLLSTS, state,
|
||||
state & DLL_LOCK_STATE, 2, 100);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n");
|
||||
}
|
||||
|
||||
static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (!ios->enhanced_strobe) {
|
||||
spacemit_sdhci_clrbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
|
||||
return;
|
||||
}
|
||||
|
||||
spacemit_sdhci_setbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
|
||||
spacemit_sdhci_phy_dll_init(host);
|
||||
}
|
||||
|
||||
static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_get_rate(pltfm_host->clk);
|
||||
}
|
||||
|
||||
static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG);
|
||||
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
spacemit_sdhci_phy_dll_init(host);
|
||||
host->mmc->caps &= ~MMC_CAP_WAIT_WHILE_BUSY;
|
||||
}
|
||||
|
||||
static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
spacemit_sdhci_clrbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
|
||||
spacemit_sdhci_clrbits(host, MMC_HS400 | MMC_HS200 | ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
|
||||
spacemit_sdhci_clrbits(host, HS200_USE_RFIFO, SDHC_PHY_FUNC_REG);
|
||||
|
||||
udelay(5);
|
||||
|
||||
spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
|
||||
}
|
||||
|
||||
static inline int spacemit_sdhci_get_clocks(struct device *dev,
|
||||
struct sdhci_pltfm_host *pltfm_host)
|
||||
{
|
||||
struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhst->clk_core = devm_clk_get_enabled(dev, "core");
|
||||
if (IS_ERR(sdhst->clk_core))
|
||||
return -EINVAL;
|
||||
|
||||
sdhst->clk_io = devm_clk_get_enabled(dev, "io");
|
||||
if (IS_ERR(sdhst->clk_io))
|
||||
return -EINVAL;
|
||||
|
||||
pltfm_host->clk = sdhst->clk_io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops spacemit_sdhci_ops = {
|
||||
.get_max_clock = spacemit_sdhci_clk_get_max_clock,
|
||||
.reset = spacemit_sdhci_reset,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_clock = spacemit_sdhci_set_clock,
|
||||
.set_uhs_signaling = spacemit_sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
|
||||
.ops = &spacemit_sdhci_ops,
|
||||
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_32BIT_ADMA_SIZE |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA |
|
||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct of_device_id spacemit_sdhci_of_match[] = {
|
||||
{ .compatible = "spacemit,k1-sdhci" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match);
|
||||
|
||||
static int spacemit_sdhci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spacemit_sdhci_host *sdhst;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_host *host;
|
||||
struct mmc_host_ops *mops;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_pltfm;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) {
|
||||
mops = &host->mmc_host_ops;
|
||||
mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400;
|
||||
mops->hs400_complete = spacemit_sdhci_post_select_hs400;
|
||||
mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200;
|
||||
mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe;
|
||||
}
|
||||
|
||||
host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
|
||||
|
||||
if (spacemit_sdhci_get_clocks(dev, pltfm_host))
|
||||
goto err_pltfm;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_pltfm;
|
||||
|
||||
return 0;
|
||||
|
||||
err_pltfm:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver spacemit_sdhci_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-spacemit",
|
||||
.of_match_table = spacemit_sdhci_of_match,
|
||||
},
|
||||
.probe = spacemit_sdhci_probe,
|
||||
.remove = sdhci_pltfm_remove,
|
||||
};
|
||||
module_platform_driver(spacemit_sdhci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SpacemiT SDHCI platform driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1270,7 +1270,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
||||
mmc->f_max = 48000000;
|
||||
}
|
||||
|
||||
if (!mmc_can_gpio_ro(mmc))
|
||||
if (!mmc_host_can_gpio_ro(mmc))
|
||||
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
|
||||
|
||||
pltfm_host->clk = devm_clk_get(dev, "fck");
|
||||
|
||||
@@ -158,7 +158,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
|
||||
u32 present;
|
||||
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
||||
!mmc_card_is_removable(host->mmc) || mmc_can_gpio_cd(host->mmc))
|
||||
!mmc_card_is_removable(host->mmc) || mmc_host_can_gpio_cd(host->mmc))
|
||||
return;
|
||||
|
||||
if (enable) {
|
||||
@@ -2571,7 +2571,7 @@ int sdhci_get_ro(struct mmc_host *mmc)
|
||||
is_readonly = 0;
|
||||
} else if (host->ops->get_ro) {
|
||||
is_readonly = host->ops->get_ro(host);
|
||||
} else if (mmc_can_gpio_ro(mmc)) {
|
||||
} else if (mmc_host_can_gpio_ro(mmc)) {
|
||||
is_readonly = mmc_gpio_get_ro(mmc);
|
||||
/* Do not invert twice */
|
||||
allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
|
||||
@@ -3744,7 +3744,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
|
||||
{
|
||||
return mmc_card_is_removable(host->mmc) &&
|
||||
!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
||||
!mmc_can_gpio_cd(host->mmc);
|
||||
!mmc_host_can_gpio_cd(host->mmc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3755,7 +3755,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
|
||||
* sdhci_disable_irq_wakeups() since it will be set by
|
||||
* sdhci_enable_card_detection() or sdhci_init().
|
||||
*/
|
||||
static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
|
||||
bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
|
||||
{
|
||||
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE |
|
||||
SDHCI_WAKE_ON_INT;
|
||||
@@ -3787,8 +3787,9 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
|
||||
|
||||
return host->irq_wake_enabled;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
|
||||
|
||||
static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
|
||||
void sdhci_disable_irq_wakeups(struct sdhci_host *host)
|
||||
{
|
||||
u8 val;
|
||||
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
|
||||
@@ -3802,6 +3803,7 @@ static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
|
||||
|
||||
host->irq_wake_enabled = false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
|
||||
|
||||
int sdhci_suspend_host(struct sdhci_host *host)
|
||||
{
|
||||
|
||||
@@ -875,6 +875,8 @@ void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
|
||||
dma_addr_t addr, int len, unsigned int cmd);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
bool sdhci_enable_irq_wakeups(struct sdhci_host *host);
|
||||
void sdhci_disable_irq_wakeups(struct sdhci_host *host);
|
||||
int sdhci_suspend_host(struct sdhci_host *host);
|
||||
int sdhci_resume_host(struct sdhci_host *host);
|
||||
int sdhci_runtime_suspend_host(struct sdhci_host *host);
|
||||
|
||||
@@ -791,7 +791,7 @@ static int spmmc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (mmc_can_gpio_cd(mmc))
|
||||
if (mmc_host_can_gpio_cd(mmc))
|
||||
ret = mmc_gpio_get_cd(mmc);
|
||||
|
||||
if (ret < 0)
|
||||
|
||||
@@ -1176,14 +1176,14 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
||||
dma_max_mapping_size(&pdev->dev));
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
if (mmc_can_gpio_ro(mmc))
|
||||
if (mmc_host_can_gpio_ro(mmc))
|
||||
_host->ops.get_ro = mmc_gpio_get_ro;
|
||||
|
||||
if (mmc_can_gpio_cd(mmc))
|
||||
if (mmc_host_can_gpio_cd(mmc))
|
||||
_host->ops.get_cd = mmc_gpio_get_cd;
|
||||
|
||||
/* must be set before tmio_mmc_reset() */
|
||||
_host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
|
||||
_host->native_hotplug = !(mmc_host_can_gpio_cd(mmc) ||
|
||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||
!mmc_card_is_removable(mmc));
|
||||
|
||||
|
||||
@@ -329,6 +329,7 @@ struct mmc_card {
|
||||
#define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */
|
||||
#define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */
|
||||
#define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */
|
||||
#define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */
|
||||
|
||||
bool written_flag; /* Indicates eMMC has been written since power on */
|
||||
bool reenable_cmdq; /* Re-enable Command Queue */
|
||||
|
||||
@@ -24,7 +24,7 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
int mmc_gpiod_set_cd_config(struct mmc_host *host, unsigned long config);
|
||||
int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on);
|
||||
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
|
||||
bool mmc_can_gpio_cd(struct mmc_host *host);
|
||||
bool mmc_can_gpio_ro(struct mmc_host *host);
|
||||
bool mmc_host_can_gpio_cd(struct mmc_host *host);
|
||||
bool mmc_host_can_gpio_ro(struct mmc_host *host);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user