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:
Linus Torvalds
2025-05-27 16:23:25 -07:00
44 changed files with 1093 additions and 378 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>;
};

View 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/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>;
};
...

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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.

View File

@@ -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:

View 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";
};

View File

@@ -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;
};

View 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;
};

View File

@@ -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;
/*

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 |

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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
};

View File

@@ -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
*/

View File

@@ -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:

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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--;

View File

@@ -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;
}
}

View File

@@ -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[] = {

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View 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");

View File

@@ -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");

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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)

View File

@@ -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));

View File

@@ -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 */

View File

@@ -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