Merge tag 'mmc-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Allow synchronous detection of (e)MMC/SD/SDIO cards
   - Fixup error check for ioctls for SPI hosts
   - Disable broken SD-Cache support for Kingston Canvas Go Plus from 2019
   - Disable broken eMMC-Trim support for Kingston EMMC04G-M627
   - Disable broken eMMC-Trim support for Micron MTFC4GACAJCN-1M

  MMC host:
   - bcm2835: Convert DT bindings to YAML
   - mmci:
      - Enable asynchronous probe
      - Transform the ux500 HW-busy detection into a proper state machine
      - Add support for SW busy-end timeouts for the ux500 variants
   - mmci_stm32:
      - Add support for sdm32 variant revision v3.0 used on STM32MP25
      - Improve the tuning sequence
   - mtk-sd: Tune polling-period to improve performance
   - sdhci: Fixup DMA configuration for 64-bit DMA mode
   - sdhci-bcm-kona: Convert DT bindings to YAML
   - sdhci-msm:
      - Switch to use the new ICE API
      - Add support for the SC8280XP/IPQ6018/QDU1000/QRU1000 variants
   - sdhci-pci-gli:
      - Add support SD Express cards for GL9767
      - Add support for the Genesys Logic GL9767 variant"

* tag 'mmc-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (42 commits)
  dt-bindings: mmc: fsl-imx-esdhc: Add imx6ul support
  mmc: mmci: Add support for SW busy-end timeouts
  mmc: Add MMC_QUIRK_BROKEN_SD_CACHE for Kingston Canvas Go Plus from 11/2019
  mmc: core: disable TRIM on Kingston EMMC04G-M627
  mmc: mmci: stm32: add delay block support for STM32MP25
  mmc: mmci: stm32: prepare other delay block support
  mmc: mmci: stm32: manage block gap hardware flow control
  mmc: mmci: Add support for sdmmc variant revision v3.0
  mmc: mmci: add stm32_idmabsize_align parameter
  dt-bindings: mmc: mmci: Add st,stm32mp25-sdmmc2 compatible
  mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M
  mmc: mmci: Break out a helper function
  mmc: mmci: Use a switch statement machine
  mmc: mmci: Use state machine state as exit condition
  mmc: mmci: Retry the busy start condition
  mmc: mmci: Make busy complete state machine explicit
  mmc: mmci: Break out error check in busy detect
  mmc: mmci: Stash status while waiting for busy
  mmc: mmci: Unwind big if() clause
  mmc: mmci: Clear busy_status when starting command
  ...
This commit is contained in:
Linus Torvalds
2023-06-28 14:06:39 -07:00
32 changed files with 1074 additions and 340 deletions

View File

@@ -53,10 +53,11 @@ properties:
items: items:
- const: arm,pl18x - const: arm,pl18x
- const: arm,primecell - const: arm,primecell
- description: Entry for STMicroelectronics variant of PL18x. - description: Entries for STMicroelectronics variant of PL18x.
This dedicated compatible is used by bootloaders.
items: items:
- const: st,stm32-sdmmc2 - enum:
- st,stm32-sdmmc2
- st,stm32mp25-sdmmc2
- const: arm,pl18x - const: arm,pl18x
- const: arm,primecell - const: arm,primecell

View File

@@ -1,23 +0,0 @@
Broadcom BCM2835 SDHOST controller
This file documents differences between the core properties described
by mmc.txt and the properties that represent the BCM2835 controller.
Required properties:
- compatible: Should be "brcm,bcm2835-sdhost".
- clocks: The clock feeding the SDHOST controller.
Optional properties:
- dmas: DMA channel for read and write.
See Documentation/devicetree/bindings/dma/dma.txt for details
Example:
sdhost: mmc@7e202000 {
compatible = "brcm,bcm2835-sdhost";
reg = <0x7e202000 0x100>;
interrupts = <2 24>;
clocks = <&clocks BCM2835_CLOCK_VPU>;
dmas = <&dma 13>;
dma-names = "rx-tx";
};

View File

@@ -0,0 +1,54 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/brcm,bcm2835-sdhost.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom BCM2835 SDHOST controller
maintainers:
- Stefan Wahren <stefan.wahren@i2se.com>
allOf:
- $ref: mmc-controller.yaml
properties:
compatible:
const: brcm,bcm2835-sdhost
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
dmas:
maxItems: 1
dma-names:
const: rx-tx
required:
- compatible
- reg
- interrupts
- clocks
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/bcm2835.h>
sdhost: mmc@7e202000 {
compatible = "brcm,bcm2835-sdhost";
reg = <0x7e202000 0x100>;
interrupts = <2 24>;
clocks = <&clocks BCM2835_CLOCK_VPU>;
dmas = <&dma 13>;
dma-names = "rx-tx";
bus-width = <4>;
};

View File

@@ -1,21 +0,0 @@
Broadcom BCM281xx SDHCI
This file documents differences between the core properties in mmc.txt
and the properties present in the bcm281xx SDHCI
Required properties:
- compatible : Should be "brcm,kona-sdhci"
- DEPRECATED: compatible : Should be "bcm,kona-sdhci"
- clocks: phandle + clock specifier pair of the external clock
Refer to clocks/clock-bindings.txt for generic clock consumer properties.
Example:
sdio2: sdio@3f1a0000 {
compatible = "brcm,kona-sdhci";
reg = <0x3f1a0000 0x10000>;
clocks = <&sdio3_clk>;
interrupts = <0x0 74 0x4>;
};

View File

@@ -0,0 +1,48 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/brcm,kona-sdhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Kona family SDHCI controller
maintainers:
- Florian Fainelli <f.fainelli@gmail.com>
allOf:
- $ref: sdhci-common.yaml#
properties:
compatible:
const: brcm,kona-sdhci
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- interrupts
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/bcm281xx.h>
mmc@3f1a0000 {
compatible = "brcm,kona-sdhci";
reg = <0x3f1a0000 0x10000>;
clocks = <&master_ccu BCM281XX_MASTER_CCU_SDIO3>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
};
...

View File

@@ -42,6 +42,7 @@ properties:
- enum: - enum:
- fsl,imx6sll-usdhc - fsl,imx6sll-usdhc
- fsl,imx6ull-usdhc - fsl,imx6ull-usdhc
- fsl,imx6ul-usdhc
- const: fsl,imx6sx-usdhc - const: fsl,imx6sx-usdhc
- items: - items:
- const: fsl,imx7d-usdhc - const: fsl,imx7d-usdhc

View File

@@ -36,11 +36,14 @@ properties:
- enum: - enum:
- qcom,ipq5018-sdhci - qcom,ipq5018-sdhci
- qcom,ipq5332-sdhci - qcom,ipq5332-sdhci
- qcom,ipq6018-sdhci
- qcom,ipq9574-sdhci - qcom,ipq9574-sdhci
- qcom,qcm2290-sdhci - qcom,qcm2290-sdhci
- qcom,qcs404-sdhci - qcom,qcs404-sdhci
- qcom,qdu1000-sdhci
- qcom,sc7180-sdhci - qcom,sc7180-sdhci
- qcom,sc7280-sdhci - qcom,sc7280-sdhci
- qcom,sc8280xp-sdhci
- qcom,sdm630-sdhci - qcom,sdm630-sdhci
- qcom,sdm670-sdhci - qcom,sdm670-sdhci
- qcom,sdm845-sdhci - qcom,sdm845-sdhci

View File

@@ -44,12 +44,10 @@ static const char *tpc_names[] = {
* memstick_debug_get_tpc_name - debug helper that returns string for * memstick_debug_get_tpc_name - debug helper that returns string for
* a TPC number * a TPC number
*/ */
const char *memstick_debug_get_tpc_name(int tpc) static __maybe_unused const char *memstick_debug_get_tpc_name(int tpc)
{ {
return tpc_names[tpc-1]; return tpc_names[tpc-1];
} }
EXPORT_SYMBOL(memstick_debug_get_tpc_name);
/* Read a register*/ /* Read a register*/
static inline u32 r592_read_reg(struct r592_device *dev, int address) static inline u32 r592_read_reg(struct r592_device *dev, int address)

View File

@@ -178,6 +178,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
int recovery_mode, int recovery_mode,
struct mmc_queue *mq); struct mmc_queue *mq);
static void mmc_blk_hsq_req_done(struct mmc_request *mrq); static void mmc_blk_hsq_req_done(struct mmc_request *mrq);
static int mmc_spi_err_check(struct mmc_card *card);
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
{ {
@@ -608,6 +609,11 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
return 0; return 0;
if (mmc_host_is_spi(card->host)) {
if (idata->ic.write_flag || r1b_resp || cmd.flags & MMC_RSP_SPI_BUSY)
return mmc_spi_err_check(card);
return err;
}
/* Ensure RPMB/R1B command has completed by polling with CMD13. */ /* Ensure RPMB/R1B command has completed by polling with CMD13. */
if (idata->rpmb || r1b_resp) if (idata->rpmb || r1b_resp)
err = mmc_poll_for_busy(card, busy_timeout_ms, false, err = mmc_poll_for_busy(card, busy_timeout_ms, false,
@@ -2505,9 +2511,9 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
string_get_size((u64)size, 512, STRING_UNITS_2, string_get_size((u64)size, 512, STRING_UNITS_2,
cap_str, sizeof(cap_str)); cap_str, sizeof(cap_str));
pr_info("%s: %s %s %s %s\n", pr_info("%s: %s %s %s%s\n",
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
cap_str, md->read_only ? "(ro)" : ""); cap_str, md->read_only ? " (ro)" : "");
/* used in ->open, must be set before add_disk: */ /* used in ->open, must be set before add_disk: */
if (area_type == MMC_BLK_DATA_AREA_MAIN) if (area_type == MMC_BLK_DATA_AREA_MAIN)
@@ -2899,12 +2905,12 @@ static const struct file_operations mmc_dbg_ext_csd_fops = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
static int mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md) static void mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
{ {
struct dentry *root; struct dentry *root;
if (!card->debugfs_root) if (!card->debugfs_root)
return 0; return;
root = card->debugfs_root; root = card->debugfs_root;
@@ -2913,19 +2919,13 @@ static int mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
debugfs_create_file_unsafe("status", 0400, root, debugfs_create_file_unsafe("status", 0400, root,
card, card,
&mmc_dbg_card_status_fops); &mmc_dbg_card_status_fops);
if (!md->status_dentry)
return -EIO;
} }
if (mmc_card_mmc(card)) { if (mmc_card_mmc(card)) {
md->ext_csd_dentry = md->ext_csd_dentry =
debugfs_create_file("ext_csd", S_IRUSR, root, card, debugfs_create_file("ext_csd", S_IRUSR, root, card,
&mmc_dbg_ext_csd_fops); &mmc_dbg_ext_csd_fops);
if (!md->ext_csd_dentry)
return -EIO;
} }
return 0;
} }
static void mmc_blk_remove_debugfs(struct mmc_card *card, static void mmc_blk_remove_debugfs(struct mmc_card *card,
@@ -2934,22 +2934,17 @@ static void mmc_blk_remove_debugfs(struct mmc_card *card,
if (!card->debugfs_root) if (!card->debugfs_root)
return; return;
if (!IS_ERR_OR_NULL(md->status_dentry)) { debugfs_remove(md->status_dentry);
debugfs_remove(md->status_dentry); md->status_dentry = NULL;
md->status_dentry = NULL;
}
if (!IS_ERR_OR_NULL(md->ext_csd_dentry)) { debugfs_remove(md->ext_csd_dentry);
debugfs_remove(md->ext_csd_dentry); md->ext_csd_dentry = NULL;
md->ext_csd_dentry = NULL;
}
} }
#else #else
static int mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md) static void mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
{ {
return 0;
} }
static void mmc_blk_remove_debugfs(struct mmc_card *card, static void mmc_blk_remove_debugfs(struct mmc_card *card,

View File

@@ -53,6 +53,10 @@ struct mmc_fixup {
unsigned int manfid; unsigned int manfid;
unsigned short oemid; unsigned short oemid;
/* Manufacturing date */
unsigned short year;
unsigned char month;
/* SDIO-specific fields. You can use SDIO_ANY_ID here of course */ /* SDIO-specific fields. You can use SDIO_ANY_ID here of course */
u16 cis_vendor, cis_device; u16 cis_vendor, cis_device;
@@ -68,6 +72,8 @@ struct mmc_fixup {
#define CID_MANFID_ANY (-1u) #define CID_MANFID_ANY (-1u)
#define CID_OEMID_ANY ((unsigned short) -1) #define CID_OEMID_ANY ((unsigned short) -1)
#define CID_YEAR_ANY ((unsigned short) -1)
#define CID_MONTH_ANY ((unsigned char) -1)
#define CID_NAME_ANY (NULL) #define CID_NAME_ANY (NULL)
#define EXT_CSD_REV_ANY (-1u) #define EXT_CSD_REV_ANY (-1u)
@@ -81,17 +87,21 @@ struct mmc_fixup {
#define CID_MANFID_APACER 0x27 #define CID_MANFID_APACER 0x27
#define CID_MANFID_KINGSTON 0x70 #define CID_MANFID_KINGSTON 0x70
#define CID_MANFID_HYNIX 0x90 #define CID_MANFID_HYNIX 0x90
#define CID_MANFID_KINGSTON_SD 0x9F
#define CID_MANFID_NUMONYX 0xFE #define CID_MANFID_NUMONYX 0xFE
#define END_FIXUP { NULL } #define END_FIXUP { NULL }
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ #define _FIXUP_EXT(_name, _manfid, _oemid, _year, _month, \
_cis_vendor, _cis_device, \ _rev_start, _rev_end, \
_fixup, _data, _ext_csd_rev) \ _cis_vendor, _cis_device, \
_fixup, _data, _ext_csd_rev) \
{ \ { \
.name = (_name), \ .name = (_name), \
.manfid = (_manfid), \ .manfid = (_manfid), \
.oemid = (_oemid), \ .oemid = (_oemid), \
.year = (_year), \
.month = (_month), \
.rev_start = (_rev_start), \ .rev_start = (_rev_start), \
.rev_end = (_rev_end), \ .rev_end = (_rev_end), \
.cis_vendor = (_cis_vendor), \ .cis_vendor = (_cis_vendor), \
@@ -103,8 +113,8 @@ struct mmc_fixup {
#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \ #define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
_fixup, _data, _ext_csd_rev) \ _fixup, _data, _ext_csd_rev) \
_FIXUP_EXT(_name, _manfid, \ _FIXUP_EXT(_name, _manfid, _oemid, CID_YEAR_ANY, CID_MONTH_ANY, \
_oemid, _rev_start, _rev_end, \ _rev_start, _rev_end, \
SDIO_ANY_ID, SDIO_ANY_ID, \ SDIO_ANY_ID, SDIO_ANY_ID, \
_fixup, _data, _ext_csd_rev) \ _fixup, _data, _ext_csd_rev) \
@@ -118,8 +128,9 @@ struct mmc_fixup {
_ext_csd_rev) _ext_csd_rev)
#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \ #define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, CID_OEMID_ANY, \
CID_OEMID_ANY, 0, -1ull, \ CID_YEAR_ANY, CID_MONTH_ANY, \
0, -1ull, \
_vendor, _device, \ _vendor, _device, \
_fixup, _data, EXT_CSD_REV_ANY) \ _fixup, _data, EXT_CSD_REV_ANY) \
@@ -264,4 +275,9 @@ static inline int mmc_card_broken_sd_discard(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_BROKEN_SD_DISCARD; return c->quirks & MMC_QUIRK_BROKEN_SD_DISCARD;
} }
static inline int mmc_card_broken_sd_cache(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_BROKEN_SD_CACHE;
}
#endif #endif

View File

@@ -2199,10 +2199,8 @@ int mmc_card_alternative_gpt_sector(struct mmc_card *card, sector_t *gpt_sector)
} }
EXPORT_SYMBOL(mmc_card_alternative_gpt_sector); EXPORT_SYMBOL(mmc_card_alternative_gpt_sector);
void mmc_rescan(struct work_struct *work) static void __mmc_rescan(struct mmc_host *host)
{ {
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i; int i;
if (host->rescan_disable) if (host->rescan_disable)
@@ -2274,6 +2272,14 @@ void mmc_rescan(struct work_struct *work)
mmc_schedule_delayed_work(&host->detect, HZ); mmc_schedule_delayed_work(&host->detect, HZ);
} }
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
__mmc_rescan(host);
}
void mmc_start_host(struct mmc_host *host) void mmc_start_host(struct mmc_host *host)
{ {
host->f_init = max(min(freqs[0], host->f_max), host->f_min); host->f_init = max(min(freqs[0], host->f_max), host->f_min);
@@ -2286,7 +2292,8 @@ void mmc_start_host(struct mmc_host *host)
} }
mmc_gpiod_request_cd_irq(host); mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false); host->detect_change = 1;
__mmc_rescan(host);
} }
void __mmc_stop_host(struct mmc_host *host) void __mmc_stop_host(struct mmc_host *host)

View File

@@ -53,6 +53,15 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = {
MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_BLK_NO_CMD23), MMC_QUIRK_BLK_NO_CMD23),
/*
* Kingston Canvas Go! Plus microSD cards never finish SD cache flush.
* This has so far only been observed on cards from 11/2019, while new
* cards from 2023/05 do not exhibit this behavior.
*/
_FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11,
0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
/* /*
* Some SD cards lockup while using CMD23 multiblock transfers. * Some SD cards lockup while using CMD23 multiblock transfers.
*/ */
@@ -100,6 +109,20 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = {
MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc, MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_TRIM_BROKEN), MMC_QUIRK_TRIM_BROKEN),
/*
* Micron MTFC4GACAJCN-1M advertises TRIM but it does not seems to
* support being used to offload WRITE_ZEROES.
*/
MMC_FIXUP("Q2J54A", CID_MANFID_MICRON, 0x014e, add_quirk_mmc,
MMC_QUIRK_TRIM_BROKEN),
/*
* Kingston EMMC04G-M627 advertises TRIM but it does not seems to
* support being used to offload WRITE_ZEROES.
*/
MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
MMC_QUIRK_TRIM_BROKEN),
/* /*
* Some SD cards reports discard support while they don't * Some SD cards reports discard support while they don't
*/ */
@@ -209,6 +232,10 @@ static inline void mmc_fixup_device(struct mmc_card *card,
if (f->of_compatible && if (f->of_compatible &&
!mmc_fixup_of_compatible_match(card, f->of_compatible)) !mmc_fixup_of_compatible_match(card, f->of_compatible))
continue; continue;
if (f->year != CID_YEAR_ANY && f->year != card->cid.year)
continue;
if (f->month != CID_MONTH_ANY && f->month != card->cid.month)
continue;
dev_dbg(&card->dev, "calling %ps\n", f->vendor_fixup); dev_dbg(&card->dev, "calling %ps\n", f->vendor_fixup);
f->vendor_fixup(card, f->data); f->vendor_fixup(card, f->data);

View File

@@ -1170,7 +1170,7 @@ static int sd_parse_ext_reg_perf(struct mmc_card *card, u8 fno, u8 page,
card->ext_perf.feature_support |= SD_EXT_PERF_HOST_MAINT; card->ext_perf.feature_support |= SD_EXT_PERF_HOST_MAINT;
/* Cache support at bit 0. */ /* Cache support at bit 0. */
if (reg_buf[4] & BIT(0)) if ((reg_buf[4] & BIT(0)) && !mmc_card_broken_sd_cache(card))
card->ext_perf.feature_support |= SD_EXT_PERF_CACHE; card->ext_perf.feature_support |= SD_EXT_PERF_CACHE;
/* Command queue support indicated via queue depth bits (0 to 4). */ /* Command queue support indicated via queue depth bits (0 to 4). */

View File

@@ -550,7 +550,7 @@ config MMC_SDHCI_MSM
depends on MMC_SDHCI_PLTFM depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS select MMC_SDHCI_IO_ACCESSORS
select MMC_CQHCI select MMC_CQHCI
select QCOM_SCM if MMC_CRYPTO select QCOM_INLINE_CRYPTO_ENGINE if MMC_CRYPTO
help help
This selects the Secure Digital Host Controller Interface (SDHCI) This selects the Secure Digital Host Controller Interface (SDHCI)
support present in Qualcomm SOCs. The controller supports support present in Qualcomm SOCs. The controller supports

View File

@@ -5,6 +5,7 @@
#define LINUX_MMC_CQHCI_H #define LINUX_MMC_CQHCI_H
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/bitfield.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/spinlock_types.h> #include <linux/spinlock_types.h>
#include <linux/types.h> #include <linux/types.h>
@@ -23,6 +24,8 @@
/* capabilities */ /* capabilities */
#define CQHCI_CAP 0x04 #define CQHCI_CAP 0x04
#define CQHCI_CAP_CS 0x10000000 /* Crypto Support */ #define CQHCI_CAP_CS 0x10000000 /* Crypto Support */
#define CQHCI_CAP_ITCFMUL GENMASK(15, 12)
#define CQHCI_ITCFMUL(x) FIELD_GET(CQHCI_CAP_ITCFMUL, (x))
/* configuration */ /* configuration */
#define CQHCI_CFG 0x08 #define CQHCI_CFG 0x08

View File

@@ -52,7 +52,7 @@ static int dw_mci_bluefield_probe(struct platform_device *pdev)
static struct platform_driver dw_mci_bluefield_pltfm_driver = { static struct platform_driver dw_mci_bluefield_pltfm_driver = {
.probe = dw_mci_bluefield_probe, .probe = dw_mci_bluefield_probe,
.remove = dw_mci_pltfm_remove, .remove_new = dw_mci_pltfm_remove,
.driver = { .driver = {
.name = "dwmmc_bluefield", .name = "dwmmc_bluefield",
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,

View File

@@ -470,7 +470,7 @@ static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = {
static struct platform_driver dw_mci_k3_pltfm_driver = { static struct platform_driver dw_mci_k3_pltfm_driver = {
.probe = dw_mci_k3_probe, .probe = dw_mci_k3_probe,
.remove = dw_mci_pltfm_remove, .remove_new = dw_mci_pltfm_remove,
.driver = { .driver = {
.name = "dwmmc_k3", .name = "dwmmc_k3",
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,

View File

@@ -121,18 +121,17 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, drv_data); return dw_mci_pltfm_register(pdev, drv_data);
} }
int dw_mci_pltfm_remove(struct platform_device *pdev) void dw_mci_pltfm_remove(struct platform_device *pdev)
{ {
struct dw_mci *host = platform_get_drvdata(pdev); struct dw_mci *host = platform_get_drvdata(pdev);
dw_mci_remove(host); dw_mci_remove(host);
return 0;
} }
EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove); EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
static struct platform_driver dw_mci_pltfm_driver = { static struct platform_driver dw_mci_pltfm_driver = {
.probe = dw_mci_pltfm_probe, .probe = dw_mci_pltfm_probe,
.remove = dw_mci_pltfm_remove, .remove_new = dw_mci_pltfm_remove,
.driver = { .driver = {
.name = "dw_mmc", .name = "dw_mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,

View File

@@ -10,7 +10,7 @@
extern int dw_mci_pltfm_register(struct platform_device *pdev, extern int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data); const struct dw_mci_drv_data *drv_data);
extern int dw_mci_pltfm_remove(struct platform_device *pdev); extern void dw_mci_pltfm_remove(struct platform_device *pdev);
extern const struct dev_pm_ops dw_mci_pltfm_pmops; extern const struct dev_pm_ops dw_mci_pltfm_pmops;
#endif /* _DW_MMC_PLTFM_H_ */ #endif /* _DW_MMC_PLTFM_H_ */

View File

@@ -172,7 +172,7 @@ static int dw_mci_starfive_probe(struct platform_device *pdev)
static struct platform_driver dw_mci_starfive_driver = { static struct platform_driver dw_mci_starfive_driver = {
.probe = dw_mci_starfive_probe, .probe = dw_mci_starfive_probe,
.remove = dw_mci_pltfm_remove, .remove_new = dw_mci_pltfm_remove,
.driver = { .driver = {
.name = "dwmmc_starfive", .name = "dwmmc_starfive",
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,

View File

@@ -776,6 +776,11 @@ static void meson_mx_sdhc_init_hw(struct mmc_host *mmc)
regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS); regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
} }
static void meason_mx_mmc_free_host(void *data)
{
mmc_free_host(data);
}
static int meson_mx_sdhc_probe(struct platform_device *pdev) static int meson_mx_sdhc_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@@ -788,8 +793,7 @@ static int meson_mx_sdhc_probe(struct platform_device *pdev)
if (!mmc) if (!mmc)
return -ENOMEM; return -ENOMEM;
ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host, ret = devm_add_action_or_reset(dev, meason_mx_mmc_free_host, mmc);
mmc);
if (ret) { if (ret) {
dev_err(dev, "Failed to register mmc_free_host action\n"); dev_err(dev, "Failed to register mmc_free_host action\n");
return ret; return ret;

View File

@@ -37,6 +37,7 @@
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/workqueue.h>
#include <asm/div64.h> #include <asm/div64.h>
#include <asm/io.h> #include <asm/io.h>
@@ -270,6 +271,7 @@ static struct variant_data variant_stm32_sdmmc = {
.datactrl_any_blocksz = true, .datactrl_any_blocksz = true,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.stm32_idmabsize_mask = GENMASK(12, 5), .stm32_idmabsize_mask = GENMASK(12, 5),
.stm32_idmabsize_align = BIT(5),
.busy_timeout = true, .busy_timeout = true,
.busy_detect = true, .busy_detect = true,
.busy_detect_flag = MCI_STM32_BUSYD0, .busy_detect_flag = MCI_STM32_BUSYD0,
@@ -296,6 +298,35 @@ static struct variant_data variant_stm32_sdmmcv2 = {
.datactrl_any_blocksz = true, .datactrl_any_blocksz = true,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.stm32_idmabsize_mask = GENMASK(16, 5), .stm32_idmabsize_mask = GENMASK(16, 5),
.stm32_idmabsize_align = BIT(5),
.dma_lli = true,
.busy_timeout = true,
.busy_detect = true,
.busy_detect_flag = MCI_STM32_BUSYD0,
.busy_detect_mask = MCI_STM32_BUSYD0ENDMASK,
.init = sdmmc_variant_init,
};
static struct variant_data variant_stm32_sdmmcv3 = {
.fifosize = 256 * 4,
.fifohalfsize = 128 * 4,
.f_max = 267000000,
.stm32_clkdiv = true,
.cmdreg_cpsm_enable = MCI_CPSM_STM32_ENABLE,
.cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC,
.cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC,
.cmdreg_srsp = MCI_CPSM_STM32_SRSP,
.cmdreg_stop = MCI_CPSM_STM32_CMDSTOP,
.data_cmd_enable = MCI_CPSM_STM32_CMDTRANS,
.irq_pio_mask = MCI_IRQ_PIO_STM32_MASK,
.datactrl_first = true,
.datacnt_useless = true,
.datalength_bits = 25,
.datactrl_blocksz = 14,
.datactrl_any_blocksz = true,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.stm32_idmabsize_mask = GENMASK(16, 6),
.stm32_idmabsize_align = BIT(6),
.dma_lli = true, .dma_lli = true,
.busy_timeout = true, .busy_timeout = true,
.busy_detect = true, .busy_detect = true,
@@ -654,10 +685,52 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
return MCI_DPSM_ENABLE | (host->data->blksz << 16); return MCI_DPSM_ENABLE | (host->data->blksz << 16);
} }
static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk) static void ux500_busy_clear_mask_done(struct mmci_host *host)
{ {
void __iomem *base = host->base; void __iomem *base = host->base;
writel(host->variant->busy_detect_mask, base + MMCICLEAR);
writel(readl(base + MMCIMASK0) &
~host->variant->busy_detect_mask, base + MMCIMASK0);
host->busy_state = MMCI_BUSY_DONE;
host->busy_status = 0;
}
/*
* ux500_busy_complete() - this will wait until the busy status
* goes off, saving any status that occur in the meantime into
* host->busy_status until we know the card is not busy any more.
* The function returns true when the busy detection is ended
* and we should continue processing the command.
*
* The Ux500 typically fires two IRQs over a busy cycle like this:
*
* DAT0 busy +-----------------+
* | |
* DAT0 not busy ----+ +--------
*
* ^ ^
* | |
* IRQ1 IRQ2
*/
static bool ux500_busy_complete(struct mmci_host *host, struct mmc_command *cmd,
u32 status, u32 err_msk)
{
void __iomem *base = host->base;
int retries = 10;
if (status & err_msk) {
/* Stop any ongoing busy detection if an error occurs */
ux500_busy_clear_mask_done(host);
goto out_ret_state;
}
/*
* The state transitions are encoded in a state machine crossing
* the edges in this switch statement.
*/
switch (host->busy_state) {
/* /*
* Before unmasking for the busy end IRQ, confirm that the * Before unmasking for the busy end IRQ, confirm that the
* command was sent successfully. To keep track of having a * command was sent successfully. To keep track of having a
@@ -667,19 +740,33 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
* Note that, the card may need a couple of clock cycles before * Note that, the card may need a couple of clock cycles before
* it starts signaling busy on DAT0, hence re-read the * it starts signaling busy on DAT0, hence re-read the
* MMCISTATUS register here, to allow the busy bit to be set. * MMCISTATUS register here, to allow the busy bit to be set.
* Potentially we may even need to poll the register for a
* while, to allow it to be set, but tests indicates that it
* isn't needed.
*/ */
if (!host->busy_status && !(status & err_msk) && case MMCI_BUSY_DONE:
(readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { /*
writel(readl(base + MMCIMASK0) | * Save the first status register read to be sure to catch
host->variant->busy_detect_mask, * all bits that may be lost will retrying. If the command
base + MMCIMASK0); * is still busy this will result in assigning 0 to
* host->busy_status, which is what it should be in IDLE.
*/
host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND); host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
return false; while (retries) {
} status = readl(base + MMCISTATUS);
/* Keep accumulating status bits */
host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
if (status & host->variant->busy_detect_flag) {
writel(readl(base + MMCIMASK0) |
host->variant->busy_detect_mask,
base + MMCIMASK0);
host->busy_state = MMCI_BUSY_WAITING_FOR_START_IRQ;
schedule_delayed_work(&host->ux500_busy_timeout_work,
msecs_to_jiffies(cmd->busy_timeout));
goto out_ret_state;
}
retries--;
}
dev_dbg(mmc_dev(host->mmc), "no busy signalling in time\n");
ux500_busy_clear_mask_done(host);
break;
/* /*
* If there is a command in-progress that has been successfully * If there is a command in-progress that has been successfully
@@ -692,27 +779,39 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
* both the start and the end interrupts needs to be cleared, * both the start and the end interrupts needs to be cleared,
* one after the other. So, clear the busy start IRQ here. * one after the other. So, clear the busy start IRQ here.
*/ */
if (host->busy_status && case MMCI_BUSY_WAITING_FOR_START_IRQ:
(status & host->variant->busy_detect_flag)) { if (status & host->variant->busy_detect_flag) {
writel(host->variant->busy_detect_mask, base + MMCICLEAR); host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
return false; writel(host->variant->busy_detect_mask, base + MMCICLEAR);
host->busy_state = MMCI_BUSY_WAITING_FOR_END_IRQ;
} else {
dev_dbg(mmc_dev(host->mmc),
"lost busy status when waiting for busy start IRQ\n");
cancel_delayed_work(&host->ux500_busy_timeout_work);
ux500_busy_clear_mask_done(host);
}
break;
case MMCI_BUSY_WAITING_FOR_END_IRQ:
if (!(status & host->variant->busy_detect_flag)) {
host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
writel(host->variant->busy_detect_mask, base + MMCICLEAR);
cancel_delayed_work(&host->ux500_busy_timeout_work);
ux500_busy_clear_mask_done(host);
} else {
dev_dbg(mmc_dev(host->mmc),
"busy status still asserted when handling busy end IRQ - will keep waiting\n");
}
break;
default:
dev_dbg(mmc_dev(host->mmc), "fell through on state %d\n",
host->busy_state);
break;
} }
/* out_ret_state:
* If there is a command in-progress that has been successfully return (host->busy_state == MMCI_BUSY_DONE);
* sent and the busy bit isn't set, it means we have received
* the busy end IRQ. Clear and mask the IRQ, then continue to
* process the command.
*/
if (host->busy_status) {
writel(host->variant->busy_detect_mask, base + MMCICLEAR);
writel(readl(base + MMCIMASK0) &
~host->variant->busy_detect_mask, base + MMCIMASK0);
host->busy_status = 0;
}
return true;
} }
/* /*
@@ -1214,6 +1313,7 @@ static void
mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
{ {
void __iomem *base = host->base; void __iomem *base = host->base;
bool busy_resp = cmd->flags & MMC_RSP_BUSY;
unsigned long long clks; unsigned long long clks;
dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
@@ -1238,10 +1338,14 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
c |= host->variant->cmdreg_srsp; c |= host->variant->cmdreg_srsp;
} }
if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) { host->busy_status = 0;
if (!cmd->busy_timeout) host->busy_state = MMCI_BUSY_DONE;
cmd->busy_timeout = 10 * MSEC_PER_SEC;
/* Assign a default timeout if the core does not provide one */
if (busy_resp && !cmd->busy_timeout)
cmd->busy_timeout = 10 * MSEC_PER_SEC;
if (busy_resp && host->variant->busy_timeout) {
if (cmd->busy_timeout > host->mmc->max_busy_timeout) if (cmd->busy_timeout > host->mmc->max_busy_timeout)
clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk; clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk;
else else
@@ -1382,7 +1486,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
/* Handle busy detection on DAT0 if the variant supports it. */ /* Handle busy detection on DAT0 if the variant supports it. */
if (busy_resp && host->variant->busy_detect) if (busy_resp && host->variant->busy_detect)
if (!host->ops->busy_complete(host, status, err_msk)) if (!host->ops->busy_complete(host, cmd, status, err_msk))
return; return;
host->cmd = NULL; host->cmd = NULL;
@@ -1429,6 +1533,34 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
} }
} }
/*
* This busy timeout worker is used to "kick" the command IRQ if a
* busy detect IRQ fails to appear in reasonable time. Only used on
* variants with busy detection IRQ delivery.
*/
static void ux500_busy_timeout_work(struct work_struct *work)
{
struct mmci_host *host = container_of(work, struct mmci_host,
ux500_busy_timeout_work.work);
unsigned long flags;
u32 status;
spin_lock_irqsave(&host->lock, flags);
if (host->cmd) {
dev_dbg(mmc_dev(host->mmc), "timeout waiting for busy IRQ\n");
/* If we are still busy let's tag on a cmd-timeout error. */
status = readl(host->base + MMCISTATUS);
if (status & host->variant->busy_detect_flag)
status |= MCI_CMDTIMEOUT;
mmci_cmd_irq(host, host->cmd, status);
}
spin_unlock_irqrestore(&host->lock, flags);
}
static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain) static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain)
{ {
return remain - (readl(host->base + MMCIFIFOCNT) << 2); return remain - (readl(host->base + MMCIFIFOCNT) << 2);
@@ -2243,6 +2375,10 @@ static int mmci_probe(struct amba_device *dev,
goto clk_disable; goto clk_disable;
} }
if (host->variant->busy_detect)
INIT_DELAYED_WORK(&host->ux500_busy_timeout_work,
ux500_busy_timeout_work);
writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0); writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0);
amba_set_drvdata(dev, mmc); amba_set_drvdata(dev, mmc);
@@ -2441,6 +2577,11 @@ static const struct amba_id mmci_ids[] = {
.mask = 0xf0ffffff, .mask = 0xf0ffffff,
.data = &variant_stm32_sdmmcv2, .data = &variant_stm32_sdmmcv2,
}, },
{
.id = 0x00353180,
.mask = 0xf0ffffff,
.data = &variant_stm32_sdmmcv3,
},
/* Qualcomm variants */ /* Qualcomm variants */
{ {
.id = 0x00051180, .id = 0x00051180,
@@ -2456,6 +2597,7 @@ static struct amba_driver mmci_driver = {
.drv = { .drv = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.pm = &mmci_dev_pm_ops, .pm = &mmci_dev_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
.probe = mmci_probe, .probe = mmci_probe,
.remove = mmci_remove, .remove = mmci_remove,

View File

@@ -218,6 +218,11 @@
#define MCI_STM32_BUSYD0ENDMASK BIT(21) #define MCI_STM32_BUSYD0ENDMASK BIT(21)
#define MMCIMASK1 0x040 #define MMCIMASK1 0x040
/* STM32 sdmmc data FIFO threshold register */
#define MMCI_STM32_FIFOTHRR 0x044
#define MMCI_STM32_THR_MASK GENMASK(3, 0)
#define MMCIFIFOCNT 0x048 #define MMCIFIFOCNT 0x048
#define MMCIFIFO 0x080 /* to 0x0bc */ #define MMCIFIFO 0x080 /* to 0x0bc */
@@ -227,8 +232,6 @@
#define MMCI_STM32_IDMALLIEN BIT(1) #define MMCI_STM32_IDMALLIEN BIT(1)
#define MMCI_STM32_IDMABSIZER 0x054 #define MMCI_STM32_IDMABSIZER 0x054
#define MMCI_STM32_IDMABNDT_SHIFT 5
#define MMCI_STM32_IDMABNDT_MASK GENMASK(12, 5)
#define MMCI_STM32_IDMABASE0R 0x058 #define MMCI_STM32_IDMABASE0R 0x058
@@ -261,6 +264,19 @@ struct clk;
struct dma_chan; struct dma_chan;
struct mmci_host; struct mmci_host;
/**
* enum mmci_busy_state - enumerate the busy detect wait states
*
* This is used for the state machine waiting for different busy detect
* interrupts on hardware that fire a single IRQ for start and end of
* the busy detect phase on DAT0.
*/
enum mmci_busy_state {
MMCI_BUSY_WAITING_FOR_START_IRQ,
MMCI_BUSY_WAITING_FOR_END_IRQ,
MMCI_BUSY_DONE,
};
/** /**
* struct variant_data - MMCI variant-specific quirks * struct variant_data - MMCI variant-specific quirks
* @clkreg: default value for MCICLOCK register * @clkreg: default value for MCICLOCK register
@@ -361,6 +377,7 @@ struct variant_data {
u32 opendrain; u32 opendrain;
u8 dma_lli:1; u8 dma_lli:1;
u32 stm32_idmabsize_mask; u32 stm32_idmabsize_mask;
u32 stm32_idmabsize_align;
void (*init)(struct mmci_host *host); void (*init)(struct mmci_host *host);
}; };
@@ -380,7 +397,7 @@ struct mmci_host_ops {
void (*dma_error)(struct mmci_host *host); void (*dma_error)(struct mmci_host *host);
void (*set_clkreg)(struct mmci_host *host, unsigned int desired); void (*set_clkreg)(struct mmci_host *host, unsigned int desired);
void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr); void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr);
bool (*busy_complete)(struct mmci_host *host, u32 status, u32 err_msk); bool (*busy_complete)(struct mmci_host *host, struct mmc_command *cmd, u32 status, u32 err_msk);
void (*pre_sig_volt_switch)(struct mmci_host *host); void (*pre_sig_volt_switch)(struct mmci_host *host);
int (*post_sig_volt_switch)(struct mmci_host *host, struct mmc_ios *ios); int (*post_sig_volt_switch)(struct mmci_host *host, struct mmc_ios *ios);
}; };
@@ -409,6 +426,7 @@ struct mmci_host {
u32 clk_reg; u32 clk_reg;
u32 clk_reg_add; u32 clk_reg_add;
u32 datactrl_reg; u32 datactrl_reg;
enum mmci_busy_state busy_state;
u32 busy_status; u32 busy_status;
u32 mask1_reg; u32 mask1_reg;
u8 vqmmc_enabled:1; u8 vqmmc_enabled:1;
@@ -437,6 +455,7 @@ struct mmci_host {
void *dma_priv; void *dma_priv;
s32 next_cookie; s32 next_cookie;
struct delayed_work ux500_busy_timeout_work;
}; };
#define dma_inprogress(host) ((host)->dma_in_progress) #define dma_inprogress(host) ((host)->dma_in_progress)

View File

@@ -15,7 +15,6 @@
#include "mmci.h" #include "mmci.h"
#define SDMMC_LLI_BUF_LEN PAGE_SIZE #define SDMMC_LLI_BUF_LEN PAGE_SIZE
#define SDMMC_IDMA_BURST BIT(MMCI_STM32_IDMABNDT_SHIFT)
#define DLYB_CR 0x0 #define DLYB_CR 0x0
#define DLYB_CR_DEN BIT(0) #define DLYB_CR_DEN BIT(0)
@@ -34,6 +33,20 @@
#define DLYB_LNG_TIMEOUT_US 1000 #define DLYB_LNG_TIMEOUT_US 1000
#define SDMMC_VSWEND_TIMEOUT_US 10000 #define SDMMC_VSWEND_TIMEOUT_US 10000
#define SYSCFG_DLYBSD_CR 0x0
#define DLYBSD_CR_EN BIT(0)
#define DLYBSD_CR_RXTAPSEL_MASK GENMASK(6, 1)
#define DLYBSD_TAPSEL_NB 32
#define DLYBSD_BYP_EN BIT(16)
#define DLYBSD_BYP_CMD GENMASK(21, 17)
#define DLYBSD_ANTIGLITCH_EN BIT(22)
#define SYSCFG_DLYBSD_SR 0x4
#define DLYBSD_SR_LOCK BIT(0)
#define DLYBSD_SR_RXTAPSEL_ACK BIT(1)
#define DLYBSD_TIMEOUT_1S_IN_US 1000000
struct sdmmc_lli_desc { struct sdmmc_lli_desc {
u32 idmalar; u32 idmalar;
u32 idmabase; u32 idmabase;
@@ -48,10 +61,21 @@ struct sdmmc_idma {
bool use_bounce_buffer; bool use_bounce_buffer;
}; };
struct sdmmc_dlyb;
struct sdmmc_tuning_ops {
int (*dlyb_enable)(struct sdmmc_dlyb *dlyb);
void (*set_input_ck)(struct sdmmc_dlyb *dlyb);
int (*tuning_prepare)(struct mmci_host *host);
int (*set_cfg)(struct sdmmc_dlyb *dlyb, int unit __maybe_unused,
int phase, bool sampler __maybe_unused);
};
struct sdmmc_dlyb { struct sdmmc_dlyb {
void __iomem *base; void __iomem *base;
u32 unit; u32 unit;
u32 max; u32 max;
struct sdmmc_tuning_ops *ops;
}; };
static int sdmmc_idma_validate_data(struct mmci_host *host, static int sdmmc_idma_validate_data(struct mmci_host *host,
@@ -69,7 +93,8 @@ static int sdmmc_idma_validate_data(struct mmci_host *host,
idma->use_bounce_buffer = false; idma->use_bounce_buffer = false;
for_each_sg(data->sg, sg, data->sg_len - 1, i) { for_each_sg(data->sg, sg, data->sg_len - 1, i) {
if (!IS_ALIGNED(sg->offset, sizeof(u32)) || if (!IS_ALIGNED(sg->offset, sizeof(u32)) ||
!IS_ALIGNED(sg->length, SDMMC_IDMA_BURST)) { !IS_ALIGNED(sg->length,
host->variant->stm32_idmabsize_align)) {
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),
"unaligned scatterlist: ofst:%x length:%d\n", "unaligned scatterlist: ofst:%x length:%d\n",
data->sg->offset, data->sg->length); data->sg->offset, data->sg->length);
@@ -293,23 +318,13 @@ static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired)
clk |= host->clk_reg_add; clk |= host->clk_reg_add;
clk |= ddr; clk |= ddr;
/* if (host->mmc->ios.timing >= MMC_TIMING_UHS_SDR50)
* SDMMC_FBCK is selected when an external Delay Block is needed
* with SDR104 or HS200.
*/
if (host->mmc->ios.timing >= MMC_TIMING_UHS_SDR50) {
clk |= MCI_STM32_CLK_BUSSPEED; clk |= MCI_STM32_CLK_BUSSPEED;
if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104 ||
host->mmc->ios.timing == MMC_TIMING_MMC_HS200) {
clk &= ~MCI_STM32_CLK_SEL_MSK;
clk |= MCI_STM32_CLK_SELFBCK;
}
}
mmci_write_clkreg(host, clk); mmci_write_clkreg(host, clk);
} }
static void sdmmc_dlyb_input_ck(struct sdmmc_dlyb *dlyb) static void sdmmc_dlyb_mp15_input_ck(struct sdmmc_dlyb *dlyb)
{ {
if (!dlyb || !dlyb->base) if (!dlyb || !dlyb->base)
return; return;
@@ -326,7 +341,8 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr)
/* adds OF options */ /* adds OF options */
pwr = host->pwr_reg_add; pwr = host->pwr_reg_add;
sdmmc_dlyb_input_ck(dlyb); if (dlyb && dlyb->ops->set_input_ck)
dlyb->ops->set_input_ck(dlyb);
if (ios.power_mode == MMC_POWER_OFF) { if (ios.power_mode == MMC_POWER_OFF) {
/* Only a reset could power-off sdmmc */ /* Only a reset could power-off sdmmc */
@@ -371,6 +387,19 @@ static u32 sdmmc_get_dctrl_cfg(struct mmci_host *host)
datactrl = mmci_dctrl_blksz(host); datactrl = mmci_dctrl_blksz(host);
if (host->hw_revision >= 3) {
u32 thr = 0;
if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104 ||
host->mmc->ios.timing == MMC_TIMING_MMC_HS200) {
thr = ffs(min_t(unsigned int, host->data->blksz,
host->variant->fifosize));
thr = min_t(u32, thr, MMCI_STM32_THR_MASK);
}
writel_relaxed(thr, host->base + MMCI_STM32_FIFOTHRR);
}
if (host->mmc->card && mmc_card_sdio(host->mmc->card) && if (host->mmc->card && mmc_card_sdio(host->mmc->card) &&
host->data->blocks == 1) host->data->blocks == 1)
datactrl |= MCI_DPSM_STM32_MODE_SDIO; datactrl |= MCI_DPSM_STM32_MODE_SDIO;
@@ -382,7 +411,8 @@ static u32 sdmmc_get_dctrl_cfg(struct mmci_host *host)
return datactrl; return datactrl;
} }
static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk) static bool sdmmc_busy_complete(struct mmci_host *host, struct mmc_command *cmd,
u32 status, u32 err_msk)
{ {
void __iomem *base = host->base; void __iomem *base = host->base;
u32 busy_d0, busy_d0end, mask, sdmmc_status; u32 busy_d0, busy_d0end, mask, sdmmc_status;
@@ -423,8 +453,15 @@ complete:
return true; return true;
} }
static void sdmmc_dlyb_set_cfgr(struct sdmmc_dlyb *dlyb, static int sdmmc_dlyb_mp15_enable(struct sdmmc_dlyb *dlyb)
int unit, int phase, bool sampler) {
writel_relaxed(DLYB_CR_DEN, dlyb->base + DLYB_CR);
return 0;
}
static int sdmmc_dlyb_mp15_set_cfg(struct sdmmc_dlyb *dlyb,
int unit, int phase, bool sampler)
{ {
u32 cfgr; u32 cfgr;
@@ -436,16 +473,18 @@ static void sdmmc_dlyb_set_cfgr(struct sdmmc_dlyb *dlyb,
if (!sampler) if (!sampler)
writel_relaxed(DLYB_CR_DEN, dlyb->base + DLYB_CR); writel_relaxed(DLYB_CR_DEN, dlyb->base + DLYB_CR);
return 0;
} }
static int sdmmc_dlyb_lng_tuning(struct mmci_host *host) static int sdmmc_dlyb_mp15_prepare(struct mmci_host *host)
{ {
struct sdmmc_dlyb *dlyb = host->variant_priv; struct sdmmc_dlyb *dlyb = host->variant_priv;
u32 cfgr; u32 cfgr;
int i, lng, ret; int i, lng, ret;
for (i = 0; i <= DLYB_CFGR_UNIT_MAX; i++) { for (i = 0; i <= DLYB_CFGR_UNIT_MAX; i++) {
sdmmc_dlyb_set_cfgr(dlyb, i, DLYB_CFGR_SEL_MAX, true); dlyb->ops->set_cfg(dlyb, i, DLYB_CFGR_SEL_MAX, true);
ret = readl_relaxed_poll_timeout(dlyb->base + DLYB_CFGR, cfgr, ret = readl_relaxed_poll_timeout(dlyb->base + DLYB_CFGR, cfgr,
(cfgr & DLYB_CFGR_LNGF), (cfgr & DLYB_CFGR_LNGF),
@@ -471,14 +510,58 @@ static int sdmmc_dlyb_lng_tuning(struct mmci_host *host)
return 0; return 0;
} }
static int sdmmc_dlyb_mp25_enable(struct sdmmc_dlyb *dlyb)
{
u32 cr, sr;
cr = readl_relaxed(dlyb->base + SYSCFG_DLYBSD_CR);
cr |= DLYBSD_CR_EN;
writel_relaxed(cr, dlyb->base + SYSCFG_DLYBSD_CR);
return readl_relaxed_poll_timeout(dlyb->base + SYSCFG_DLYBSD_SR,
sr, sr & DLYBSD_SR_LOCK, 1,
DLYBSD_TIMEOUT_1S_IN_US);
}
static int sdmmc_dlyb_mp25_set_cfg(struct sdmmc_dlyb *dlyb,
int unit __maybe_unused, int phase,
bool sampler __maybe_unused)
{
u32 cr, sr;
cr = readl_relaxed(dlyb->base + SYSCFG_DLYBSD_CR);
cr &= ~DLYBSD_CR_RXTAPSEL_MASK;
cr |= FIELD_PREP(DLYBSD_CR_RXTAPSEL_MASK, phase);
writel_relaxed(cr, dlyb->base + SYSCFG_DLYBSD_CR);
return readl_relaxed_poll_timeout(dlyb->base + SYSCFG_DLYBSD_SR,
sr, sr & DLYBSD_SR_RXTAPSEL_ACK, 1,
DLYBSD_TIMEOUT_1S_IN_US);
}
static int sdmmc_dlyb_mp25_prepare(struct mmci_host *host)
{
struct sdmmc_dlyb *dlyb = host->variant_priv;
dlyb->max = DLYBSD_TAPSEL_NB;
return 0;
}
static int sdmmc_dlyb_phase_tuning(struct mmci_host *host, u32 opcode) static int sdmmc_dlyb_phase_tuning(struct mmci_host *host, u32 opcode)
{ {
struct sdmmc_dlyb *dlyb = host->variant_priv; struct sdmmc_dlyb *dlyb = host->variant_priv;
int cur_len = 0, max_len = 0, end_of_len = 0; int cur_len = 0, max_len = 0, end_of_len = 0;
int phase; int phase, ret;
for (phase = 0; phase <= dlyb->max; phase++) { for (phase = 0; phase <= dlyb->max; phase++) {
sdmmc_dlyb_set_cfgr(dlyb, dlyb->unit, phase, false); ret = dlyb->ops->set_cfg(dlyb, dlyb->unit, phase, false);
if (ret) {
dev_err(mmc_dev(host->mmc), "tuning config failed\n");
return ret;
}
if (mmc_send_tuning(host->mmc, opcode, NULL)) { if (mmc_send_tuning(host->mmc, opcode, NULL)) {
cur_len = 0; cur_len = 0;
@@ -496,10 +579,15 @@ static int sdmmc_dlyb_phase_tuning(struct mmci_host *host, u32 opcode)
return -EINVAL; return -EINVAL;
} }
writel_relaxed(0, dlyb->base + DLYB_CR); if (dlyb->ops->set_input_ck)
dlyb->ops->set_input_ck(dlyb);
phase = end_of_len - max_len / 2; phase = end_of_len - max_len / 2;
sdmmc_dlyb_set_cfgr(dlyb, dlyb->unit, phase, false); ret = dlyb->ops->set_cfg(dlyb, dlyb->unit, phase, false);
if (ret) {
dev_err(mmc_dev(host->mmc), "tuning reconfig failed\n");
return ret;
}
dev_dbg(mmc_dev(host->mmc), "unit:%d max_dly:%d phase:%d\n", dev_dbg(mmc_dev(host->mmc), "unit:%d max_dly:%d phase:%d\n",
dlyb->unit, dlyb->max, phase); dlyb->unit, dlyb->max, phase);
@@ -511,12 +599,33 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{ {
struct mmci_host *host = mmc_priv(mmc); struct mmci_host *host = mmc_priv(mmc);
struct sdmmc_dlyb *dlyb = host->variant_priv; struct sdmmc_dlyb *dlyb = host->variant_priv;
u32 clk;
int ret;
if ((host->mmc->ios.timing != MMC_TIMING_UHS_SDR104 &&
host->mmc->ios.timing != MMC_TIMING_MMC_HS200) ||
host->mmc->actual_clock <= 50000000)
return 0;
if (!dlyb || !dlyb->base) if (!dlyb || !dlyb->base)
return -EINVAL; return -EINVAL;
if (sdmmc_dlyb_lng_tuning(host)) ret = dlyb->ops->dlyb_enable(dlyb);
return -EINVAL; if (ret)
return ret;
/*
* SDMMC_FBCK is selected when an external Delay Block is needed
* with SDR104 or HS200.
*/
clk = host->clk_reg;
clk &= ~MCI_STM32_CLK_SEL_MSK;
clk |= MCI_STM32_CLK_SELFBCK;
mmci_write_clkreg(host, clk);
ret = dlyb->ops->tuning_prepare(host);
if (ret)
return ret;
return sdmmc_dlyb_phase_tuning(host, opcode); return sdmmc_dlyb_phase_tuning(host, opcode);
} }
@@ -574,6 +683,19 @@ static struct mmci_host_ops sdmmc_variant_ops = {
.post_sig_volt_switch = sdmmc_post_sig_volt_switch, .post_sig_volt_switch = sdmmc_post_sig_volt_switch,
}; };
static struct sdmmc_tuning_ops dlyb_tuning_mp15_ops = {
.dlyb_enable = sdmmc_dlyb_mp15_enable,
.set_input_ck = sdmmc_dlyb_mp15_input_ck,
.tuning_prepare = sdmmc_dlyb_mp15_prepare,
.set_cfg = sdmmc_dlyb_mp15_set_cfg,
};
static struct sdmmc_tuning_ops dlyb_tuning_mp25_ops = {
.dlyb_enable = sdmmc_dlyb_mp25_enable,
.tuning_prepare = sdmmc_dlyb_mp25_prepare,
.set_cfg = sdmmc_dlyb_mp25_set_cfg,
};
void sdmmc_variant_init(struct mmci_host *host) void sdmmc_variant_init(struct mmci_host *host)
{ {
struct device_node *np = host->mmc->parent->of_node; struct device_node *np = host->mmc->parent->of_node;
@@ -592,6 +714,11 @@ void sdmmc_variant_init(struct mmci_host *host)
return; return;
dlyb->base = base_dlyb; dlyb->base = base_dlyb;
if (of_device_is_compatible(np, "st,stm32mp25-sdmmc2"))
dlyb->ops = &dlyb_tuning_mp25_ops;
else
dlyb->ops = &dlyb_tuning_mp15_ops;
host->variant_priv = dlyb; host->variant_priv = dlyb;
host->mmc_ops->execute_tuning = sdmmc_execute_tuning; host->mmc_ops->execute_tuning = sdmmc_execute_tuning;
} }

View File

@@ -473,6 +473,7 @@ struct msdc_host {
struct msdc_tune_para def_tune_para; /* default tune setting */ struct msdc_tune_para def_tune_para; /* default tune setting */
struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */ struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
struct cqhci_host *cq_host; struct cqhci_host *cq_host;
u32 cq_ssc1_time;
}; };
static const struct mtk_mmc_compatible mt2701_compat = { static const struct mtk_mmc_compatible mt2701_compat = {
@@ -2450,9 +2451,49 @@ static void msdc_hs400_enhanced_strobe(struct mmc_host *mmc,
} }
} }
static void msdc_cqe_cit_cal(struct msdc_host *host, u64 timer_ns)
{
struct mmc_host *mmc = mmc_from_priv(host);
struct cqhci_host *cq_host = mmc->cqe_private;
u8 itcfmul;
u64 hclk_freq, value;
/*
* On MediaTek SoCs the MSDC controller's CQE uses msdc_hclk as ITCFVAL
* so we multiply/divide the HCLK frequency by ITCFMUL to calculate the
* Send Status Command Idle Timer (CIT) value.
*/
hclk_freq = (u64)clk_get_rate(host->h_clk);
itcfmul = CQHCI_ITCFMUL(cqhci_readl(cq_host, CQHCI_CAP));
switch (itcfmul) {
case 0x0:
do_div(hclk_freq, 1000);
break;
case 0x1:
do_div(hclk_freq, 100);
break;
case 0x2:
do_div(hclk_freq, 10);
break;
case 0x3:
break;
case 0x4:
hclk_freq = hclk_freq * 10;
break;
default:
host->cq_ssc1_time = 0x40;
return;
}
value = hclk_freq * timer_ns;
do_div(value, 1000000000);
host->cq_ssc1_time = value;
}
static void msdc_cqe_enable(struct mmc_host *mmc) static void msdc_cqe_enable(struct mmc_host *mmc)
{ {
struct msdc_host *host = mmc_priv(mmc); struct msdc_host *host = mmc_priv(mmc);
struct cqhci_host *cq_host = mmc->cqe_private;
/* enable cmdq irq */ /* enable cmdq irq */
writel(MSDC_INT_CMDQ, host->base + MSDC_INTEN); writel(MSDC_INT_CMDQ, host->base + MSDC_INTEN);
@@ -2462,6 +2503,9 @@ static void msdc_cqe_enable(struct mmc_host *mmc)
msdc_set_busy_timeout(host, 20 * 1000000000ULL, 0); msdc_set_busy_timeout(host, 20 * 1000000000ULL, 0);
/* default read data timeout 1s */ /* default read data timeout 1s */
msdc_set_timeout(host, 1000000000ULL, 0); msdc_set_timeout(host, 1000000000ULL, 0);
/* Set the send status command idle timer */
cqhci_writel(cq_host, host->cq_ssc1_time, CQHCI_SSC1);
} }
static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery) static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
@@ -2707,7 +2751,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
/* Support for SDIO eint irq ? */ /* Support for SDIO eint irq ? */
if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) { if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) {
host->eint_irq = platform_get_irq_byname(pdev, "sdio_wakeup"); host->eint_irq = platform_get_irq_byname_optional(pdev, "sdio_wakeup");
if (host->eint_irq > 0) { if (host->eint_irq > 0) {
host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint"); host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint");
if (IS_ERR(host->pins_eint)) { if (IS_ERR(host->pins_eint)) {
@@ -2803,6 +2847,8 @@ static int msdc_drv_probe(struct platform_device *pdev)
/* cqhci 16bit length */ /* cqhci 16bit length */
/* 0 size, means 65536 so we don't have to -1 here */ /* 0 size, means 65536 so we don't have to -1 here */
mmc->max_seg_size = 64 * 1024; mmc->max_seg_size = 64 * 1024;
/* Reduce CIT to 0x40 that corresponds to 2.35us */
msdc_cqe_cit_cal(host, 2350);
} }
ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq, ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq,

View File

@@ -13,12 +13,13 @@
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <soc/qcom/ice.h>
#include "sdhci-cqhci.h" #include "sdhci-cqhci.h"
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "cqhci.h" #include "cqhci.h"
@@ -258,12 +259,14 @@ struct sdhci_msm_variant_info {
struct sdhci_msm_host { struct sdhci_msm_host {
struct platform_device *pdev; struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */ void __iomem *core_mem; /* MSM SDCC mapped address */
void __iomem *ice_mem; /* MSM ICE mapped address (if available) */
int pwr_irq; /* power irq */ int pwr_irq; /* power irq */
struct clk *bus_clk; /* SDHC bus voter clock */ struct clk *bus_clk; /* SDHC bus voter clock */
struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/ struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/
/* core, iface, cal, sleep, and ice clocks */ /* core, iface, cal and sleep clocks */
struct clk_bulk_data bulk_clks[5]; struct clk_bulk_data bulk_clks[4];
#ifdef CONFIG_MMC_CRYPTO
struct qcom_ice *ice;
#endif
unsigned long clk_rate; unsigned long clk_rate;
struct mmc_host *mmc; struct mmc_host *mmc;
bool use_14lpp_dll_reset; bool use_14lpp_dll_reset;
@@ -1804,164 +1807,51 @@ out:
#ifdef CONFIG_MMC_CRYPTO #ifdef CONFIG_MMC_CRYPTO
#define AES_256_XTS_KEY_SIZE 64
/* QCOM ICE registers */
#define QCOM_ICE_REG_VERSION 0x0008
#define QCOM_ICE_REG_FUSE_SETTING 0x0010
#define QCOM_ICE_FUSE_SETTING_MASK 0x1
#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2
#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4
#define QCOM_ICE_REG_BIST_STATUS 0x0070
#define QCOM_ICE_BIST_STATUS_MASK 0xF0000000
#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000
#define sdhci_msm_ice_writel(host, val, reg) \
writel((val), (host)->ice_mem + (reg))
#define sdhci_msm_ice_readl(host, reg) \
readl((host)->ice_mem + (reg))
static bool sdhci_msm_ice_supported(struct sdhci_msm_host *msm_host)
{
struct device *dev = mmc_dev(msm_host->mmc);
u32 regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_VERSION);
int major = regval >> 24;
int minor = (regval >> 16) & 0xFF;
int step = regval & 0xFFFF;
/* For now this driver only supports ICE version 3. */
if (major != 3) {
dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
major, minor, step);
return false;
}
dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n",
major, minor, step);
/* If fuses are blown, ICE might not work in the standard way. */
regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_FUSE_SETTING);
if (regval & (QCOM_ICE_FUSE_SETTING_MASK |
QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK |
QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
dev_warn(dev, "Fuses are blown; ICE is unusable!\n");
return false;
}
return true;
}
static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
{
return devm_clk_get(dev, "ice");
}
static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
struct cqhci_host *cq_host) struct cqhci_host *cq_host)
{ {
struct mmc_host *mmc = msm_host->mmc; struct mmc_host *mmc = msm_host->mmc;
struct device *dev = mmc_dev(mmc); struct device *dev = mmc_dev(mmc);
struct resource *res; struct qcom_ice *ice;
if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS)) if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
return 0; return 0;
res = platform_get_resource_byname(msm_host->pdev, IORESOURCE_MEM, ice = of_qcom_ice_get(dev);
"ice"); if (ice == ERR_PTR(-EOPNOTSUPP)) {
if (!res) { dev_warn(dev, "Disabling inline encryption support\n");
dev_warn(dev, "ICE registers not found\n"); ice = NULL;
goto disable;
} }
if (!qcom_scm_ice_available()) { if (IS_ERR_OR_NULL(ice))
dev_warn(dev, "ICE SCM interface not found\n"); return PTR_ERR_OR_ZERO(ice);
goto disable;
}
msm_host->ice_mem = devm_ioremap_resource(dev, res);
if (IS_ERR(msm_host->ice_mem))
return PTR_ERR(msm_host->ice_mem);
if (!sdhci_msm_ice_supported(msm_host))
goto disable;
msm_host->ice = ice;
mmc->caps2 |= MMC_CAP2_CRYPTO; mmc->caps2 |= MMC_CAP2_CRYPTO;
return 0; return 0;
disable:
dev_warn(dev, "Disabling inline encryption support\n");
return 0;
}
static void sdhci_msm_ice_low_power_mode_enable(struct sdhci_msm_host *msm_host)
{
u32 regval;
regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
/*
* Enable low power mode sequence
* [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0
*/
regval |= 0x7000;
sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
}
static void sdhci_msm_ice_optimization_enable(struct sdhci_msm_host *msm_host)
{
u32 regval;
/* ICE Optimizations Enable Sequence */
regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
regval |= 0xD807100;
/* ICE HPG requires delay before writing */
udelay(5);
sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
udelay(5);
}
/*
* Wait until the ICE BIST (built-in self-test) has completed.
*
* This may be necessary before ICE can be used.
*
* Note that we don't really care whether the BIST passed or failed; we really
* just want to make sure that it isn't still running. This is because (a) the
* BIST is a FIPS compliance thing that never fails in practice, (b) ICE is
* documented to reject crypto requests if the BIST fails, so we needn't do it
* in software too, and (c) properly testing storage encryption requires testing
* the full storage stack anyway, and not relying on hardware-level self-tests.
*/
static int sdhci_msm_ice_wait_bist_status(struct sdhci_msm_host *msm_host)
{
u32 regval;
int err;
err = readl_poll_timeout(msm_host->ice_mem + QCOM_ICE_REG_BIST_STATUS,
regval, !(regval & QCOM_ICE_BIST_STATUS_MASK),
50, 5000);
if (err)
dev_err(mmc_dev(msm_host->mmc),
"Timed out waiting for ICE self-test to complete\n");
return err;
} }
static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
{ {
if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)) if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return; qcom_ice_enable(msm_host->ice);
sdhci_msm_ice_low_power_mode_enable(msm_host);
sdhci_msm_ice_optimization_enable(msm_host);
sdhci_msm_ice_wait_bist_status(msm_host);
} }
static int __maybe_unused sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{ {
if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)) if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return 0; return qcom_ice_resume(msm_host->ice);
return sdhci_msm_ice_wait_bist_status(msm_host);
return 0;
}
static __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
{
if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return qcom_ice_suspend(msm_host->ice);
return 0;
} }
/* /*
@@ -1972,48 +1862,28 @@ static int sdhci_msm_program_key(struct cqhci_host *cq_host,
const union cqhci_crypto_cfg_entry *cfg, const union cqhci_crypto_cfg_entry *cfg,
int slot) int slot)
{ {
struct device *dev = mmc_dev(cq_host->mmc); struct sdhci_host *host = mmc_priv(cq_host->mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
union cqhci_crypto_cap_entry cap; union cqhci_crypto_cap_entry cap;
union {
u8 bytes[AES_256_XTS_KEY_SIZE];
u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
} key;
int i;
int err;
if (!(cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE))
return qcom_scm_ice_invalidate_key(slot);
/* Only AES-256-XTS has been tested so far. */ /* Only AES-256-XTS has been tested so far. */
cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx]; cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx];
if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS || if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS ||
cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256) { cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256)
dev_err_ratelimited(dev,
"Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
cap.algorithm_id, cap.key_size);
return -EINVAL; return -EINVAL;
}
memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE); if (cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE)
return qcom_ice_program_key(msm_host->ice,
/* QCOM_ICE_CRYPTO_ALG_AES_XTS,
* The SCM call byte-swaps the 32-bit words of the key. So we have to QCOM_ICE_CRYPTO_KEY_SIZE_256,
* do the same, in order for the final key be correct. cfg->crypto_key,
*/ cfg->data_unit_size, slot);
for (i = 0; i < ARRAY_SIZE(key.words); i++) else
__cpu_to_be32s(&key.words[i]); return qcom_ice_evict_key(msm_host->ice, slot);
err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
QCOM_SCM_ICE_CIPHER_AES_256_XTS,
cfg->data_unit_size);
memzero_explicit(&key, sizeof(key));
return err;
} }
#else /* CONFIG_MMC_CRYPTO */ #else /* CONFIG_MMC_CRYPTO */
static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
{
return NULL;
}
static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
struct cqhci_host *cq_host) struct cqhci_host *cq_host)
@@ -2025,11 +1895,17 @@ static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
{ {
} }
static inline int __maybe_unused static inline __maybe_unused int
sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{ {
return 0; return 0;
} }
static inline __maybe_unused int
sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
{
return 0;
}
#endif /* !CONFIG_MMC_CRYPTO */ #endif /* !CONFIG_MMC_CRYPTO */
/*****************************************************************************\ /*****************************************************************************\
@@ -2633,11 +2509,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
clk = NULL; clk = NULL;
msm_host->bulk_clks[3].clk = clk; msm_host->bulk_clks[3].clk = clk;
clk = sdhci_msm_ice_get_clk(&pdev->dev);
if (IS_ERR(clk))
clk = NULL;
msm_host->bulk_clks[4].clk = clk;
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks); msm_host->bulk_clks);
if (ret) if (ret)
@@ -2830,7 +2701,7 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks); msm_host->bulk_clks);
return 0; return sdhci_msm_ice_suspend(msm_host);
} }
static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)

View File

@@ -1903,6 +1903,7 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(GLI, 9750, gl9750), SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
SDHCI_PCI_DEVICE(GLI, 9755, gl9755), SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e), SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e),
SDHCI_PCI_DEVICE(GLI, 9767, gl9767),
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd), SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
/* Generic SD host controller */ /* Generic SD host controller */
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)}, {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},

View File

@@ -149,6 +149,72 @@
#define PCI_GLI_9755_PM_CTRL 0xFC #define PCI_GLI_9755_PM_CTRL 0xFC
#define PCI_GLI_9755_PM_STATE GENMASK(1, 0) #define PCI_GLI_9755_PM_STATE GENMASK(1, 0)
#define SDHCI_GLI_9767_GM_BURST_SIZE 0x510
#define SDHCI_GLI_9767_GM_BURST_SIZE_AXI_ALWAYS_SET BIT(8)
#define PCIE_GLI_9767_VHS 0x884
#define GLI_9767_VHS_REV GENMASK(19, 16)
#define GLI_9767_VHS_REV_R 0x0
#define GLI_9767_VHS_REV_M 0x1
#define GLI_9767_VHS_REV_W 0x2
#define PCIE_GLI_9767_COM_MAILBOX 0x888
#define PCIE_GLI_9767_COM_MAILBOX_SSC_EN BIT(1)
#define PCIE_GLI_9767_CFG 0x8A0
#define PCIE_GLI_9767_CFG_LOW_PWR_OFF BIT(12)
#define PCIE_GLI_9767_COMBO_MUX_CTL 0x8C8
#define PCIE_GLI_9767_COMBO_MUX_CTL_RST_EN BIT(6)
#define PCIE_GLI_9767_COMBO_MUX_CTL_WAIT_PERST_EN BIT(10)
#define PCIE_GLI_9767_PWR_MACRO_CTL 0x8D0
#define PCIE_GLI_9767_PWR_MACRO_CTL_LOW_VOLTAGE GENMASK(3, 0)
#define PCIE_GLI_9767_PWR_MACRO_CTL_LD0_LOW_OUTPUT_VOLTAGE GENMASK(15, 12)
#define PCIE_GLI_9767_PWR_MACRO_CTL_LD0_LOW_OUTPUT_VOLTAGE_VALUE 0x7
#define PCIE_GLI_9767_PWR_MACRO_CTL_RCLK_AMPLITUDE_CTL GENMASK(29, 28)
#define PCIE_GLI_9767_PWR_MACRO_CTL_RCLK_AMPLITUDE_CTL_VALUE 0x3
#define PCIE_GLI_9767_SCR 0x8E0
#define PCIE_GLI_9767_SCR_AUTO_AXI_W_BURST BIT(6)
#define PCIE_GLI_9767_SCR_AUTO_AXI_R_BURST BIT(7)
#define PCIE_GLI_9767_SCR_AXI_REQ BIT(9)
#define PCIE_GLI_9767_SCR_CARD_DET_PWR_SAVING_EN BIT(10)
#define PCIE_GLI_9767_SCR_SYSTEM_CLK_SELECT_MODE0 BIT(16)
#define PCIE_GLI_9767_SCR_SYSTEM_CLK_SELECT_MODE1 BIT(17)
#define PCIE_GLI_9767_SCR_CORE_PWR_D3_OFF BIT(21)
#define PCIE_GLI_9767_SCR_CFG_RST_DATA_LINK_DOWN BIT(30)
#define PCIE_GLI_9767_SDHC_CAP 0x91C
#define PCIE_GLI_9767_SDHC_CAP_SDEI_RESULT BIT(5)
#define PCIE_GLI_9767_SD_PLL_CTL 0x938
#define PCIE_GLI_9767_SD_PLL_CTL_PLL_LDIV GENMASK(9, 0)
#define PCIE_GLI_9767_SD_PLL_CTL_PLL_PDIV GENMASK(15, 12)
#define PCIE_GLI_9767_SD_PLL_CTL_PLL_DIR_EN BIT(16)
#define PCIE_GLI_9767_SD_PLL_CTL_SSC_EN BIT(19)
#define PCIE_GLI_9767_SD_PLL_CTL_SSC_STEP_SETTING GENMASK(28, 24)
#define PCIE_GLI_9767_SD_PLL_CTL2 0x93C
#define PCIE_GLI_9767_SD_PLL_CTL2_PLLSSC_PPM GENMASK(31, 16)
#define PCIE_GLI_9767_SD_EXPRESS_CTL 0x940
#define PCIE_GLI_9767_SD_EXPRESS_CTL_SDEI_EXE BIT(0)
#define PCIE_GLI_9767_SD_EXPRESS_CTL_SD_EXPRESS_MODE BIT(1)
#define PCIE_GLI_9767_SD_DATA_MULTI_CTL 0x944
#define PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME GENMASK(23, 16)
#define PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME_VALUE 0x64
#define PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2 0x950
#define PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2_SDEI_COMPLETE BIT(0)
#define PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_EN_REG2 0x954
#define PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_EN_REG2_SDEI_COMPLETE_STATUS_EN BIT(0)
#define PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2 0x958
#define PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2_SDEI_COMPLETE_SIGNAL_EN BIT(0)
#define GLI_MAX_TUNING_LOOP 40 #define GLI_MAX_TUNING_LOOP 40
/* Genesys Logic chipset */ /* Genesys Logic chipset */
@@ -693,6 +759,293 @@ static void gl9755_hw_setting(struct sdhci_pci_slot *slot)
gl9755_wt_off(pdev); gl9755_wt_off(pdev);
} }
static inline void gl9767_vhs_read(struct pci_dev *pdev)
{
u32 vhs_enable;
u32 vhs_value;
pci_read_config_dword(pdev, PCIE_GLI_9767_VHS, &vhs_value);
vhs_enable = FIELD_GET(GLI_9767_VHS_REV, vhs_value);
if (vhs_enable == GLI_9767_VHS_REV_R)
return;
vhs_value &= ~GLI_9767_VHS_REV;
vhs_value |= FIELD_PREP(GLI_9767_VHS_REV, GLI_9767_VHS_REV_R);
pci_write_config_dword(pdev, PCIE_GLI_9767_VHS, vhs_value);
}
static inline void gl9767_vhs_write(struct pci_dev *pdev)
{
u32 vhs_enable;
u32 vhs_value;
pci_read_config_dword(pdev, PCIE_GLI_9767_VHS, &vhs_value);
vhs_enable = FIELD_GET(GLI_9767_VHS_REV, vhs_value);
if (vhs_enable == GLI_9767_VHS_REV_W)
return;
vhs_value &= ~GLI_9767_VHS_REV;
vhs_value |= FIELD_PREP(GLI_9767_VHS_REV, GLI_9767_VHS_REV_W);
pci_write_config_dword(pdev, PCIE_GLI_9767_VHS, vhs_value);
}
static bool gl9767_ssc_enable(struct pci_dev *pdev)
{
u32 value;
u8 enable;
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_COM_MAILBOX, &value);
enable = FIELD_GET(PCIE_GLI_9767_COM_MAILBOX_SSC_EN, value);
gl9767_vhs_read(pdev);
return enable;
}
static void gl9767_set_ssc(struct pci_dev *pdev, u8 enable, u8 step, u16 ppm)
{
u32 pll;
u32 ssc;
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL, &pll);
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL2, &ssc);
pll &= ~(PCIE_GLI_9767_SD_PLL_CTL_SSC_STEP_SETTING |
PCIE_GLI_9767_SD_PLL_CTL_SSC_EN);
ssc &= ~PCIE_GLI_9767_SD_PLL_CTL2_PLLSSC_PPM;
pll |= FIELD_PREP(PCIE_GLI_9767_SD_PLL_CTL_SSC_STEP_SETTING, step) |
FIELD_PREP(PCIE_GLI_9767_SD_PLL_CTL_SSC_EN, enable);
ssc |= FIELD_PREP(PCIE_GLI_9767_SD_PLL_CTL2_PLLSSC_PPM, ppm);
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL2, ssc);
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL, pll);
gl9767_vhs_read(pdev);
}
static void gl9767_set_pll(struct pci_dev *pdev, u8 dir, u16 ldiv, u8 pdiv)
{
u32 pll;
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL, &pll);
pll &= ~(PCIE_GLI_9767_SD_PLL_CTL_PLL_LDIV |
PCIE_GLI_9767_SD_PLL_CTL_PLL_PDIV |
PCIE_GLI_9767_SD_PLL_CTL_PLL_DIR_EN);
pll |= FIELD_PREP(PCIE_GLI_9767_SD_PLL_CTL_PLL_LDIV, ldiv) |
FIELD_PREP(PCIE_GLI_9767_SD_PLL_CTL_PLL_PDIV, pdiv) |
FIELD_PREP(PCIE_GLI_9767_SD_PLL_CTL_PLL_DIR_EN, dir);
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL, pll);
gl9767_vhs_read(pdev);
/* wait for pll stable */
usleep_range(1000, 1100);
}
static void gl9767_set_ssc_pll_205mhz(struct pci_dev *pdev)
{
bool enable = gl9767_ssc_enable(pdev);
/* set pll to 205MHz and ssc */
gl9767_set_ssc(pdev, enable, 0x1F, 0xF5C3);
gl9767_set_pll(pdev, 0x1, 0x246, 0x0);
}
static void gl9767_disable_ssc_pll(struct pci_dev *pdev)
{
u32 pll;
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL, &pll);
pll &= ~(PCIE_GLI_9767_SD_PLL_CTL_PLL_DIR_EN | PCIE_GLI_9767_SD_PLL_CTL_SSC_EN);
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_PLL_CTL, pll);
gl9767_vhs_read(pdev);
}
static void sdhci_gl9767_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
struct mmc_ios *ios = &host->mmc->ios;
struct pci_dev *pdev;
u32 value;
u16 clk;
pdev = slot->chip->pdev;
host->mmc->actual_clock = 0;
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_CFG, &value);
value |= PCIE_GLI_9767_CFG_LOW_PWR_OFF;
pci_write_config_dword(pdev, PCIE_GLI_9767_CFG, value);
gl9767_disable_ssc_pll(pdev);
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (clock == 0)
return;
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) {
host->mmc->actual_clock = 205000000;
gl9767_set_ssc_pll_205mhz(pdev);
}
sdhci_enable_clk(host, clk);
pci_read_config_dword(pdev, PCIE_GLI_9767_CFG, &value);
value &= ~PCIE_GLI_9767_CFG_LOW_PWR_OFF;
pci_write_config_dword(pdev, PCIE_GLI_9767_CFG, value);
gl9767_vhs_read(pdev);
}
static void gli_set_9767(struct sdhci_host *host)
{
u32 value;
value = sdhci_readl(host, SDHCI_GLI_9767_GM_BURST_SIZE);
value &= ~SDHCI_GLI_9767_GM_BURST_SIZE_AXI_ALWAYS_SET;
sdhci_writel(host, value, SDHCI_GLI_9767_GM_BURST_SIZE);
}
static void gl9767_hw_setting(struct sdhci_pci_slot *slot)
{
struct pci_dev *pdev = slot->chip->pdev;
u32 value;
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_PWR_MACRO_CTL, &value);
value &= ~(PCIE_GLI_9767_PWR_MACRO_CTL_LOW_VOLTAGE |
PCIE_GLI_9767_PWR_MACRO_CTL_LD0_LOW_OUTPUT_VOLTAGE |
PCIE_GLI_9767_PWR_MACRO_CTL_RCLK_AMPLITUDE_CTL);
value |= PCIE_GLI_9767_PWR_MACRO_CTL_LOW_VOLTAGE |
FIELD_PREP(PCIE_GLI_9767_PWR_MACRO_CTL_LD0_LOW_OUTPUT_VOLTAGE,
PCIE_GLI_9767_PWR_MACRO_CTL_LD0_LOW_OUTPUT_VOLTAGE_VALUE) |
FIELD_PREP(PCIE_GLI_9767_PWR_MACRO_CTL_RCLK_AMPLITUDE_CTL,
PCIE_GLI_9767_PWR_MACRO_CTL_RCLK_AMPLITUDE_CTL_VALUE);
pci_write_config_dword(pdev, PCIE_GLI_9767_PWR_MACRO_CTL, value);
pci_read_config_dword(pdev, PCIE_GLI_9767_SCR, &value);
value &= ~(PCIE_GLI_9767_SCR_SYSTEM_CLK_SELECT_MODE0 |
PCIE_GLI_9767_SCR_SYSTEM_CLK_SELECT_MODE1 |
PCIE_GLI_9767_SCR_CFG_RST_DATA_LINK_DOWN);
value |= PCIE_GLI_9767_SCR_AUTO_AXI_W_BURST |
PCIE_GLI_9767_SCR_AUTO_AXI_R_BURST |
PCIE_GLI_9767_SCR_AXI_REQ |
PCIE_GLI_9767_SCR_CARD_DET_PWR_SAVING_EN |
PCIE_GLI_9767_SCR_CORE_PWR_D3_OFF;
pci_write_config_dword(pdev, PCIE_GLI_9767_SCR, value);
gl9767_vhs_read(pdev);
}
static void sdhci_gl9767_reset(struct sdhci_host *host, u8 mask)
{
sdhci_reset(host, mask);
gli_set_9767(host);
}
static int gl9767_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pci_slot *slot = sdhci_priv(host);
struct pci_dev *pdev;
u32 value;
int i;
pdev = slot->chip->pdev;
if (mmc->ops->get_ro(mmc)) {
mmc->ios.timing &= ~(MMC_TIMING_SD_EXP | MMC_TIMING_SD_EXP_1_2V);
return 0;
}
gl9767_vhs_write(pdev);
pci_read_config_dword(pdev, PCIE_GLI_9767_COMBO_MUX_CTL, &value);
value &= ~(PCIE_GLI_9767_COMBO_MUX_CTL_RST_EN | PCIE_GLI_9767_COMBO_MUX_CTL_WAIT_PERST_EN);
pci_write_config_dword(pdev, PCIE_GLI_9767_COMBO_MUX_CTL, value);
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_DATA_MULTI_CTL, &value);
value &= ~PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME;
value |= FIELD_PREP(PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME,
PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME_VALUE);
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_DATA_MULTI_CTL, value);
pci_read_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2, &value);
value |= PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2_SDEI_COMPLETE;
pci_write_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2, value);
pci_read_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_EN_REG2, &value);
value |= PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_EN_REG2_SDEI_COMPLETE_STATUS_EN;
pci_write_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_EN_REG2, value);
pci_read_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2, &value);
value |= PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2_SDEI_COMPLETE_SIGNAL_EN;
pci_write_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2, value);
pci_read_config_dword(pdev, PCIE_GLI_9767_CFG, &value);
value |= PCIE_GLI_9767_CFG_LOW_PWR_OFF;
pci_write_config_dword(pdev, PCIE_GLI_9767_CFG, value);
value = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
value &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_PLL_EN);
sdhci_writew(host, value, SDHCI_CLOCK_CONTROL);
value = sdhci_readb(host, SDHCI_POWER_CONTROL);
value |= (SDHCI_VDD2_POWER_180 | SDHCI_VDD2_POWER_ON);
sdhci_writeb(host, value, SDHCI_POWER_CONTROL);
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_EXPRESS_CTL, &value);
value |= PCIE_GLI_9767_SD_EXPRESS_CTL_SDEI_EXE;
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_EXPRESS_CTL, value);
for (i = 0; i < 2; i++) {
usleep_range(10000, 10100);
pci_read_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2, &value);
if (value & PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2_SDEI_COMPLETE) {
pci_write_config_dword(pdev, PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2,
value);
break;
}
}
pci_read_config_dword(pdev, PCIE_GLI_9767_SDHC_CAP, &value);
if (value & PCIE_GLI_9767_SDHC_CAP_SDEI_RESULT) {
pci_read_config_dword(pdev, PCIE_GLI_9767_SD_EXPRESS_CTL, &value);
value |= PCIE_GLI_9767_SD_EXPRESS_CTL_SD_EXPRESS_MODE;
pci_write_config_dword(pdev, PCIE_GLI_9767_SD_EXPRESS_CTL, value);
} else {
mmc->ios.timing &= ~(MMC_TIMING_SD_EXP | MMC_TIMING_SD_EXP_1_2V);
value = sdhci_readb(host, SDHCI_POWER_CONTROL);
value &= ~(SDHCI_VDD2_POWER_180 | SDHCI_VDD2_POWER_ON);
sdhci_writeb(host, value, SDHCI_POWER_CONTROL);
value = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
value |= (SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_PLL_EN);
sdhci_writew(host, value, SDHCI_CLOCK_CONTROL);
}
gl9767_vhs_read(pdev);
return 0;
}
static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot) static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
{ {
struct sdhci_host *host = slot->host; struct sdhci_host *host = slot->host;
@@ -717,6 +1070,21 @@ static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
return 0; return 0;
} }
static int gli_probe_slot_gl9767(struct sdhci_pci_slot *slot)
{
struct sdhci_host *host = slot->host;
gli_set_9767(host);
gl9767_hw_setting(slot);
gli_pcie_enable_msi(slot);
slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
host->mmc->caps2 |= MMC_CAP2_SD_EXP;
host->mmc_host_ops.init_sd_express = gl9767_init_sd_express;
sdhci_enable_v4_mode(host);
return 0;
}
static void sdhci_gli_voltage_switch(struct sdhci_host *host) static void sdhci_gli_voltage_switch(struct sdhci_host *host)
{ {
/* /*
@@ -740,6 +1108,25 @@ static void sdhci_gli_voltage_switch(struct sdhci_host *host)
usleep_range(100000, 110000); usleep_range(100000, 110000);
} }
static void sdhci_gl9767_voltage_switch(struct sdhci_host *host)
{
/*
* According to Section 3.6.1 signal voltage switch procedure in
* SD Host Controller Simplified Spec. 4.20, steps 6~8 are as
* follows:
* (6) Set 1.8V Signal Enable in the Host Control 2 register.
* (7) Wait 5ms. 1.8V voltage regulator shall be stable within this
* period.
* (8) If 1.8V Signal Enable is cleared by Host Controller, go to
* step (12).
*
* Wait 5ms after set 1.8V signal enable in Host Control 2 register
* to ensure 1.8V signal enable bit is set by GL9767.
*
*/
usleep_range(5000, 5500);
}
static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask) static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask)
{ {
sdhci_reset(host, mask); sdhci_reset(host, mask);
@@ -1150,3 +1537,22 @@ const struct sdhci_pci_fixes sdhci_gl9763e = {
#endif #endif
.add_host = gl9763e_add_host, .add_host = gl9763e_add_host,
}; };
static const struct sdhci_ops sdhci_gl9767_ops = {
.set_clock = sdhci_gl9767_set_clock,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_gl9767_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.voltage_switch = sdhci_gl9767_voltage_switch,
};
const struct sdhci_pci_fixes sdhci_gl9767 = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50,
.probe_slot = gli_probe_slot_gl9767,
.ops = &sdhci_gl9767_ops,
#ifdef CONFIG_PM_SLEEP
.resume = sdhci_pci_gli_resume,
#endif
};

View File

@@ -76,6 +76,7 @@
#define PCI_DEVICE_ID_GLI_9755 0x9755 #define PCI_DEVICE_ID_GLI_9755 0x9755
#define PCI_DEVICE_ID_GLI_9750 0x9750 #define PCI_DEVICE_ID_GLI_9750 0x9750
#define PCI_DEVICE_ID_GLI_9763E 0xe763 #define PCI_DEVICE_ID_GLI_9763E 0xe763
#define PCI_DEVICE_ID_GLI_9767 0x9767
/* /*
* PCI device class and mask * PCI device class and mask
@@ -195,5 +196,6 @@ extern const struct sdhci_pci_fixes sdhci_o2;
extern const struct sdhci_pci_fixes sdhci_gl9750; extern const struct sdhci_pci_fixes sdhci_gl9750;
extern const struct sdhci_pci_fixes sdhci_gl9755; extern const struct sdhci_pci_fixes sdhci_gl9755;
extern const struct sdhci_pci_fixes sdhci_gl9763e; extern const struct sdhci_pci_fixes sdhci_gl9763e;
extern const struct sdhci_pci_fixes sdhci_gl9767;
#endif /* __SDHCI_PCI_H */ #endif /* __SDHCI_PCI_H */

View File

@@ -1167,6 +1167,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
} }
} }
sdhci_config_dma(host);
if (host->flags & SDHCI_REQ_USE_DMA) { if (host->flags & SDHCI_REQ_USE_DMA) {
int sg_cnt = sdhci_pre_dma_transfer(host, data, COOKIE_MAPPED); int sg_cnt = sdhci_pre_dma_transfer(host, data, COOKIE_MAPPED);
@@ -1186,8 +1188,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
} }
} }
sdhci_config_dma(host);
if (!(host->flags & SDHCI_REQ_USE_DMA)) { if (!(host->flags & SDHCI_REQ_USE_DMA)) {
int flags; int flags;

View File

@@ -99,6 +99,13 @@
#define SDHCI_POWER_180 0x0A #define SDHCI_POWER_180 0x0A
#define SDHCI_POWER_300 0x0C #define SDHCI_POWER_300 0x0C
#define SDHCI_POWER_330 0x0E #define SDHCI_POWER_330 0x0E
/*
* VDD2 - UHS2 or PCIe/NVMe
* VDD2 power on/off and voltage select
*/
#define SDHCI_VDD2_POWER_ON 0x10
#define SDHCI_VDD2_POWER_120 0x80
#define SDHCI_VDD2_POWER_180 0xA0
#define SDHCI_BLOCK_GAP_CONTROL 0x2A #define SDHCI_BLOCK_GAP_CONTROL 0x2A

View File

@@ -294,6 +294,7 @@ struct mmc_card {
#define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */ #define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */
#define MMC_QUIRK_BROKEN_HPI (1<<13) /* Disable broken HPI support */ #define MMC_QUIRK_BROKEN_HPI (1<<13) /* Disable broken HPI support */
#define MMC_QUIRK_BROKEN_SD_DISCARD (1<<14) /* Disable broken SD discard support */ #define MMC_QUIRK_BROKEN_SD_DISCARD (1<<14) /* Disable broken SD discard support */
#define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */
bool reenable_cmdq; /* Re-enable Command Queue */ bool reenable_cmdq; /* Re-enable Command Queue */