This is an interim snapshot of our next version, v4, for enabling
UHS-II on MMC/SD.
It is focused on 'sdhci' side to address Adrian's comments regarding
"modularising" sdhci-uhs2.c.
The whole aim of this version is to get early feedback from Adrian (and
others) on this issue. Without any consensus about the code structure,
it would make little sense to go further ahead on sdhci side.
(Actually, Adrian has made no comments other than "modularising" so far.)
I heavily reworked/refactored sdhci-uhs2.c and re-organised the patch
set to meet what I believe Adrian expects; no UHS-II related code in
Legacy (UHS-I) code or sdhci.c.
Nevertheless, almost of all changes I made are trivial and straightforward
in this direction, and I believe that there is no logic changed since v3
except sdhci_uhs2_irq(), as ops->irq hook, where we must deal with UHS-II
command sequences in addition to UHS-II errors. So I added extra handlings.
I admit that there is plenty of room for improvements (for example,
handling host->flags), but again the focal point here is how sdhci-uhs2.c
should be built as a module.
Please review this series (particularly Patch#8-#26 and #27) from this
viewpoint in the first place.
(Ben is working on 'host' side but there is no change on 'host' side
in this submission except a minor tweak.)
Thanks,
-Takahiro Akashi
------ original cover letter from v3 ------
Summary
=======
These patches[1] support UHS-II and fix GL9755 UHS-II compatibility.
About UHS-II, roughly deal with the following three parts:
1) A UHS-II detection and initialization:
- Host setup to support UHS-II (Section 3.13.1 Host Controller Setup Sequence
[2]).
- Detect a UHS-II I/F (Section 3.13.2 Card Interface Detection Sequence[2]).
- In step(9) of Section 3.13.2 in [2], UHS-II initialization is include Section
3.13.3 UHS-II Card Initialization and Section 3.13.4 UHS-II Setting Register
Setup Sequence.
2) Send Legacy SD command through SD-TRAN
- Encapsulated SD packets are defined in SD-TRAN in order to ensure Legacy SD
compatibility and preserve Legacy SD infrastructures (Section 7.1.1 Packet
Types and Format Overview[3]).
- Host issue a UHS-II CCMD packet or a UHS-II DCMD (Section 3.13.5 UHS-II
CCMD Packet issuing and Section 3.13.6 UHS-II DCMD Packet issuing[2]).
3) UHS-II Interrupt
- Except for UHS-II error interrupts, most interrupts share the original
interrupt registers.
Patch structure
===============
patch#1-#7: for core
patch#8-#17: for sdhci
patch#18-#21: for GL9755
Tests
=====
Ran 'dd' command to evaluate the performance:
(SanDisk UHS-II card on GL9755 controller)
Read Write
UHS-II disabled (UHS-I): 88.3MB/s 60.7MB/s
UHS-II enabled : 206MB/s 80MB/s
TODO
====
- replace some define with BIT macro
Reference
=========
[1] https://gitlab.com/ben.chuang/linux-uhs2-gl9755.git
[2] SD Host Controller Simplified Specification 4.20
[3] UHS-II Simplified Addendum 1.02
Changes in v3 (Jul. 10, 2020)
* rebased to v5.8-rc4
* add copyright notice
* reorganize the patch set and split some commits into smaller ones
* separate uhs-2 headers from others
* correct wrong spellings
* fix most of checkpatch warnings/errors
* remove all k[cz]alloc() from the code
* guard sdhci-uhs2 specific code with
'if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2))'
* make sdhci-uhs2.c as a module
* trivial changes, including
- rename back sdhci-core.c to sdhci.c
- allow vendor code to disable uhs2 if v4_mode == 0
in __sdhci_add_host()
- merge uhs2_power_up() into mmc_power_up()
- remove flag_uhs2 from mmc_attach_sd()
- add function descriptions to EXPORT'ed functions
- other minor code optimization
Changes in v2 (Jan. 9, 2020)
* rebased to v5.5-rc5
AKASHI Takahiro (23):
mmc: core: UHS-II support, modify power-up sequence
mmc: core: UHS-II support, skip set_chip_select()
mmc: core: UHS-II support, skip TMODE setup in some cases
mmc: core: UHS-II support, generate UHS-II SD command packet
mmc: core: UHS-II support, set APP_CMD bit if necessary
mmc: sdhci: add a kernel configuration for enabling UHS-II support
mmc: sdhci: add UHS-II related definitions in headers
mmc: sdhci: add UHS-II module
mmc: sdhci-uhs2: dump UHS-II registers
mmc: sdhci-uhs2: add reset function
mmc: sdhci-uhs2: add set_power() to support vdd2
mmc: sdhci-uhs2: skip signal_voltage_switch()
mmc: sdhci-uhs2: add set_timeout()
mmc: sdhci-uhs2: add set_ios()
mmc: sdhci-uhs2: add detect_init() to detect the interface
mmc: sdhci-uhs2: add clock operations
mmc: sdhci-uhs2: add set_reg() to initialise the interface
mmc: sdhci-uhs2: add request() and others
mmc: sdhci-uhs2: add irq() and others
mmc: sdhci-uhs2: add add_host() and others to set up the driver
mmc: core: add post-mmc_attach_sd hook
mmc: sdhci-pci: add UHS-II support framework
mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
Ben Chuang (4):
mmc: add UHS-II related definitions in public headers
mmc: core: UHS-II support, try to select UHS-II interface
mmc: sdhci-uhs2: add pre-detect_init hook
mmc: sdhci-uhs2: add post-mmc_attach_sd hook
drivers/mmc/core/Makefile | 2 +-
drivers/mmc/core/block.c | 7 +-
drivers/mmc/core/bus.c | 5 +-
drivers/mmc/core/core.c | 118 +-
drivers/mmc/core/regulator.c | 14 +
drivers/mmc/core/sd.c | 32 +
drivers/mmc/core/sd_ops.c | 12 +
drivers/mmc/core/uhs2.c | 883 +++++++++++++++
drivers/mmc/core/uhs2.h | 21 +
drivers/mmc/host/Kconfig | 10 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-pci-core.c | 16 +-
drivers/mmc/host/sdhci-pci-gli.c | 318 +++++-
drivers/mmc/host/sdhci-pci.h | 3 +
drivers/mmc/host/sdhci-uhs2.c | 1697 +++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 224 ++++
drivers/mmc/host/sdhci.c | 333 +++---
drivers/mmc/host/sdhci.h | 117 +-
include/linux/mmc/card.h | 1 +
include/linux/mmc/core.h | 6 +
include/linux/mmc/host.h | 31 +
include/linux/mmc/uhs2.h | 268 +++++
22 files changed, 3961 insertions(+), 158 deletions(-)
create mode 100644 drivers/mmc/core/uhs2.c
create mode 100644 drivers/mmc/core/uhs2.h
create mode 100644 drivers/mmc/host/sdhci-uhs2.c
create mode 100644 drivers/mmc/host/sdhci-uhs2.h
create mode 100644 include/linux/mmc/uhs2.h
--
2.28.0
From: Ben Chuang <[email protected]>
Add UHS-II support in public headers
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
include/linux/mmc/card.h | 1 +
include/linux/mmc/core.h | 6 +
include/linux/mmc/host.h | 30 +++++
include/linux/mmc/uhs2.h | 268 +++++++++++++++++++++++++++++++++++++++
4 files changed, 305 insertions(+)
create mode 100644 include/linux/mmc/uhs2.h
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 42df06c6b19c..c26eada7f62e 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -181,6 +181,7 @@ struct sd_switch_caps {
#define SD_SET_CURRENT_LIMIT_400 1
#define SD_SET_CURRENT_LIMIT_600 2
#define SD_SET_CURRENT_LIMIT_800 3
+#define SD_SET_CURRENT_LIMIT_1000 4
#define SD_SET_CURRENT_NO_CHANGE (-1)
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 29aa50711626..52cb628d03fd 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -7,6 +7,7 @@
#include <linux/completion.h>
#include <linux/types.h>
+#include <linux/mmc/uhs2.h>
struct mmc_data;
struct mmc_request;
@@ -109,6 +110,11 @@ struct mmc_command {
unsigned int busy_timeout; /* busy detect timeout in ms */
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
+
+ struct uhs2_command *uhs2_cmd; /* UHS2 command */
+ u8 *uhs2_resp; /* UHS2 native cmd resp */
+ u8 uhs2_resp_len; /* UHS2 native cmd resp len */
+ u8 uhs2_tmode0_flag; /* UHS2 transfer mode flag */
};
struct mmc_data {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index c079b932330f..19a265190ad3 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -15,10 +15,12 @@
#include <linux/mmc/card.h>
#include <linux/mmc/pm.h>
#include <linux/dma-direction.h>
+#include <linux/mmc/uhs2.h>
struct mmc_ios {
unsigned int clock; /* clock rate */
unsigned short vdd;
+ unsigned short vdd2; /* UHS2 VDD2 power supply */
unsigned int power_delay_ms; /* waiting for stable power */
/* vdd stores the bit number of the selected voltage range from below. */
@@ -60,6 +62,7 @@ struct mmc_ios {
#define MMC_TIMING_MMC_DDR52 8
#define MMC_TIMING_MMC_HS200 9
#define MMC_TIMING_MMC_HS400 10
+#define MMC_TIMING_UHS2 11
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
@@ -173,6 +176,11 @@ struct mmc_host_ops {
*/
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
+ /* UHS2 interfaces */
+ int (*uhs2_detect_init)(struct mmc_host *host);
+ int (*uhs2_set_reg)(struct mmc_host *host, enum uhs2_act act);
+ void (*uhs2_disable_clk)(struct mmc_host *host);
+ void (*uhs2_enable_clk)(struct mmc_host *host);
};
struct mmc_cqe_ops {
@@ -265,6 +273,7 @@ struct mmc_pwrseq;
struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
+ struct regulator *vmmc2; /* UHS2 VDD2 power supply */
struct regulator *vqmmc; /* Optional Vccq supply */
};
@@ -285,6 +294,7 @@ struct mmc_host {
u32 ocr_avail_sdio; /* SDIO-specific OCR */
u32 ocr_avail_sd; /* SD-specific OCR */
u32 ocr_avail_mmc; /* MMC-specific OCR */
+ u32 ocr_avail_uhs2; /* UHS2-specific OCR */
#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
#endif
@@ -292,6 +302,7 @@ struct mmc_host {
u32 max_current_330;
u32 max_current_300;
u32 max_current_180;
+ u32 max_current_180_vdd2; /* UHS2 vdd2 max curt. */
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
@@ -310,6 +321,7 @@ struct mmc_host {
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
+#define MMC_VDD2_165_195 0x00000080 /* UHS2 VDD2 1.65 ~ 1.95 */
u32 caps; /* Host capabilities */
@@ -343,6 +355,7 @@ struct mmc_host {
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
+#define MMC_CAP_UHS2 (1 << 26) /* Host supports UHS2 mode */
#define MMC_CAP_DONE_COMPLETE (1 << 27) /* RW reqs can be completed within mmc_request_done() */
#define MMC_CAP_CD_WAKE (1 << 28) /* Enable card detect wake */
#define MMC_CAP_CMD_DURING_TFR (1 << 29) /* Commands during data transfer */
@@ -382,6 +395,17 @@ struct mmc_host {
mmc_pm_flag_t pm_caps; /* supported pm features */
+ struct uhs2_host_caps uhs2_caps; /* UHS2 host capabilities */
+ struct uhs2_card_prop uhs2_dev_prop; /* UHS2 device properties */
+ u32 group_desc; /* UHS2 property */
+ int flags;
+#define MMC_UHS2_SUPPORT (1 << 0)
+#define MMC_UHS2_INITIALIZED (1 << 1)
+#define MMC_UHS2_2L_HD (1 << 2)
+#define MMC_UHS2_APP_CMD (1 << 3)
+#define MMC_UHS2_SPEED_B (1 << 4)
+#define MMC_SUPPORT_ADMA3 (1 << 5)
+
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_segs; /* see blk_queue_max_segments */
@@ -578,6 +602,12 @@ static inline int mmc_card_uhs(struct mmc_card *card)
card->host->ios.timing <= MMC_TIMING_UHS_DDR50;
}
+static inline bool mmc_card_uhs2(struct mmc_card *card)
+{
+ return (card->host->flags & MMC_UHS2_SUPPORT) &&
+ (card->host->flags & MMC_UHS2_INITIALIZED);
+}
+
void mmc_retune_timer_stop(struct mmc_host *host);
static inline void mmc_retune_needed(struct mmc_host *host)
diff --git a/include/linux/mmc/uhs2.h b/include/linux/mmc/uhs2.h
new file mode 100644
index 000000000000..298ac7cd8904
--- /dev/null
+++ b/include/linux/mmc/uhs2.h
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/drivers/mmc/host/uhs2.h - UHS-II driver
+ *
+ * Header file for UHS-II packets, Host Controller registers and I/O
+ * accessors.
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ */
+#ifndef LINUX_MMC_UHS2_H
+#define LINUX_MMC_UHS2_H
+
+struct mmc_request;
+
+/* LINK Layer definition */
+/* UHS2 Header */
+#define UHS2_NATIVE_PACKET_POS 7
+#define UHS2_NATIVE_PACKET (1 << UHS2_NATIVE_PACKET_POS)
+
+#define UHS2_PACKET_TYPE_POS 4
+#define UHS2_PACKET_TYPE_CCMD (0 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_DCMD (1 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_RES (2 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_DATA (3 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_MSG (7 << UHS2_PACKET_TYPE_POS)
+
+#define UHS2_DEST_ID_MASK 0x0F
+#define UHS2_DEST_ID 0x1
+
+#define UHS2_SRC_ID_POS 12
+#define UHS2_SRC_ID_MASK 0xF000
+
+#define UHS2_TRANS_ID_POS 8
+#define UHS2_TRANS_ID_MASK 0x0700
+
+/* UHS2 MSG */
+#define UHS2_MSG_CTG_POS 5
+#define UHS2_MSG_CTG_LMSG 0x00
+#define UHS2_MSG_CTG_INT 0x60
+#define UHS2_MSG_CTG_AMSG 0x80
+
+#define UHS2_MSG_CTG_FCREQ 0x00
+#define UHS2_MSG_CTG_FCRDY 0x01
+#define UHS2_MSG_CTG_STAT 0x02
+
+#define UHS2_MSG_CODE_POS 8
+#define UHS2_MSG_CODE_FC_UNRECOVER_ERR 0x8
+#define UHS2_MSG_CODE_STAT_UNRECOVER_ERR 0x8
+#define UHS2_MSG_CODE_STAT_RECOVER_ERR 0x1
+
+/* TRANS Layer definition */
+
+/* Native packets*/
+#define UHS2_NATIVE_CMD_RW_POS 7
+#define UHS2_NATIVE_CMD_WRITE (1 << UHS2_NATIVE_CMD_RW_POS)
+#define UHS2_NATIVE_CMD_READ (0 << UHS2_NATIVE_CMD_RW_POS)
+
+#define UHS2_NATIVE_CMD_PLEN_POS 4
+#define UHS2_NATIVE_CMD_PLEN_4B (1 << UHS2_NATIVE_CMD_PLEN_POS)
+#define UHS2_NATIVE_CMD_PLEN_8B (2 << UHS2_NATIVE_CMD_PLEN_POS)
+#define UHS2_NATIVE_CMD_PLEN_16B (3 << UHS2_NATIVE_CMD_PLEN_POS)
+
+#define UHS2_NATIVE_CCMD_GET_MIOADR_MASK 0xF00
+#define UHS2_NATIVE_CCMD_MIOADR_MASK 0x0F
+
+#define UHS2_NATIVE_CCMD_LIOADR_POS 8
+#define UHS2_NATIVE_CCMD_GET_LIOADR_MASK 0x0FF
+
+#define UHS2_DCMD_DM_POS 6
+#define UHS2_DCMD_2L_HD_MODE (1 << UHS2_DCMD_DM_POS)
+#define UHS2_DCMD_LM_POS 5
+#define UHS2_DCMD_LM_TLEN_EXIST (1 << UHS2_DCMD_LM_POS)
+#define UHS2_DCMD_TLUM_POS 4
+#define UHS2_DCMD_TLUM_BYTE_MODE (1 << UHS2_DCMD_TLUM_POS)
+#define UHS2_NATIVE_DCMD_DAM_POS 3
+#define UHS2_NATIVE_DCMD_DAM_IO (1 << UHS2_NATIVE_DCMD_DAM_POS)
+/*
+ * Per UHS2 spec, DCMD payload should be MSB first. There may be
+ * two types of data be assembled to MSB:
+ * 1. TLEN: Input block size for single read/write and number of blocks
+ * for multiple read/write to calculate TLEN as MSB first per spec.
+ * 2. SD command argument.
+ */
+static inline u32 uhs2_dcmd_convert_msb(u32 input)
+{
+ u32 ret = 0;
+
+ ret = ((input & 0xFF) << 24) |
+ (((input >> 8) & 0xFF) << 16) |
+ (((input >> 16) & 0xFF) << 8) |
+ ((input >> 24) & 0xFF);
+ return ret;
+}
+
+#define UHS2_RES_NACK_POS 7
+#define UHS2_RES_NACK_MASK (0x1 << UHS2_RES_NACK_POS)
+
+#define UHS2_RES_ECODE_POS 4
+#define UHS2_RES_ECODE_MASK 0x7
+#define UHS2_RES_ECODE_COND 1
+#define UHS2_RES_ECODE_ARG 2
+#define UHS2_RES_ECODE_GEN 3
+
+/* IOADR of device registers */
+#define UHS2_IOADR_GENERIC_CAPS 0x00
+#define UHS2_IOADR_PHY_CAPS 0x02
+#define UHS2_IOADR_LINK_CAPS 0x04
+#define UHS2_IOADR_RSV_CAPS 0x06
+#define UHS2_IOADR_GENERIC_SETTINGS 0x08
+#define UHS2_IOADR_PHY_SETTINGS 0x0A
+#define UHS2_IOADR_LINK_SETTINGS 0x0C
+#define UHS2_IOADR_PRESET 0x40
+
+/* SD application packets */
+#define UHS2_SD_CMD_INDEX_POS 8
+
+#define UHS2_SD_CMD_APP_POS 14
+#define UHS2_SD_CMD_APP (1 << UHS2_SD_CMD_APP_POS)
+
+struct uhs2_command {
+ u16 header;
+ u16 arg;
+ u32 *payload;
+ u32 payload_len;
+ u32 packet_len;
+};
+
+struct uhs2_host_caps {
+ u32 dap;
+ u32 gap;
+ u32 maxblk_len;
+ u32 n_fcu;
+ u8 n_lanes;
+ u8 addr64;
+ u8 card_type;
+ u8 phy_rev;
+ u8 speed_range;
+ u8 can_hibernate;
+ u8 n_lss_sync;
+ u8 n_lss_dir;
+ u8 link_rev;
+ u8 host_type;
+ u8 n_data_gap;
+
+ u32 maxblk_len_set;
+ u32 n_fcu_set;
+ u8 n_lanes_set;
+ u8 n_lss_sync_set;
+ u8 n_lss_dir_set;
+ u8 n_data_gap_set;
+ u8 max_retry_set;
+};
+
+struct uhs2_card_prop {
+ u32 node_id;
+ u32 dap;
+ u32 gap;
+ u32 n_fcu;
+ u32 maxblk_len;
+ u8 n_lanes;
+ u8 dadr_len;
+ u8 app_type;
+ u8 phy_minor_rev;
+ u8 phy_major_rev;
+ u8 can_hibernate;
+ u8 n_lss_sync;
+ u8 n_lss_dir;
+ u8 link_minor_rev;
+ u8 link_major_rev;
+ u8 dev_type;
+ u8 n_data_gap;
+
+ u32 n_fcu_set;
+ u32 maxblk_len_set;
+ u8 n_lanes_set;
+ u8 speed_range_set;
+ u8 n_lss_sync_set;
+ u8 n_lss_dir_set;
+ u8 n_data_gap_set;
+ u8 pwrctrl_mode_set;
+ u8 max_retry_set;
+
+ u8 cfg_complete;
+};
+
+enum uhs2_act {
+ SET_CONFIG,
+ ENABLE_INT,
+ DISABLE_INT,
+ SET_SPEED_B,
+ CHECK_DORMANT,
+};
+
+/* UHS-II Device Registers */
+#define UHS2_DEV_CONFIG_REG 0x000
+
+/* General Caps and Settings registers */
+#define UHS2_DEV_CONFIG_GEN_CAPS (UHS2_DEV_CONFIG_REG + 0x000)
+#define UHS2_DEV_CONFIG_N_LANES_POS 8
+#define UHS2_DEV_CONFIG_N_LANES_MASK 0x3F
+#define UHS2_DEV_CONFIG_2L_HD_FD 0x1
+#define UHS2_DEV_CONFIG_2D1U_FD 0x2
+#define UHS2_DEV_CONFIG_1D2U_FD 0x4
+#define UHS2_DEV_CONFIG_2D2U_FD 0x8
+#define UHS2_DEV_CONFIG_DADR_POS 14
+#define UHS2_DEV_CONFIG_DADR_MASK 0x1
+#define UHS2_DEV_CONFIG_APP_POS 16
+#define UHS2_DEV_CONFIG_APP_MASK 0xFF
+#define UHS2_DEV_CONFIG_APP_SD_MEM 0x1
+
+#define UHS2_DEV_CONFIG_GEN_SET (UHS2_DEV_CONFIG_REG + 0x008)
+#define UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD 0x0
+#define UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE (0x1 << 31)
+
+/* PHY Caps and Settings registers */
+#define UHS2_DEV_CONFIG_PHY_CAPS (UHS2_DEV_CONFIG_REG + 0x002)
+#define UHS2_DEV_CONFIG_PHY_MINOR_MASK 0xF
+#define UHS2_DEV_CONFIG_PHY_MAJOR_POS 4
+#define UHS2_DEV_CONFIG_PHY_MAJOR_MASK 0x3
+#define UHS2_DEV_CONFIG_CAN_HIBER_POS 15
+#define UHS2_DEV_CONFIG_CAN_HIBER_MASK 0x1
+#define UHS2_DEV_CONFIG_PHY_CAPS1 (UHS2_DEV_CONFIG_REG + 0x003)
+#define UHS2_DEV_CONFIG_N_LSS_SYN_MASK 0xF
+#define UHS2_DEV_CONFIG_N_LSS_DIR_POS 4
+#define UHS2_DEV_CONFIG_N_LSS_DIR_MASK 0xF
+
+#define UHS2_DEV_CONFIG_PHY_SET (UHS2_DEV_CONFIG_REG + 0x00A)
+#define UHS2_DEV_CONFIG_PHY_SET_SPEED_POS 6
+#define UHS2_DEV_CONFIG_PHY_SET_SPEED_A 0x0
+#define UHS2_DEV_CONFIG_PHY_SET_SPEED_B 0x1
+
+/* LINK-TRAN Caps and Settings registers */
+#define UHS2_DEV_CONFIG_LINK_TRAN_CAPS (UHS2_DEV_CONFIG_REG + 0x004)
+#define UHS2_DEV_CONFIG_LT_MINOR_MASK 0xF
+#define UHS2_DEV_CONFIG_LT_MAJOR_POS 4
+#define UHS2_DEV_CONFIG_LT_MAJOR_MASK 0x3
+#define UHS2_DEV_CONFIG_N_FCU_POS 8
+#define UHS2_DEV_CONFIG_N_FCU_MASK 0xFF
+#define UHS2_DEV_CONFIG_DEV_TYPE_POS 16
+#define UHS2_DEV_CONFIG_DEV_TYPE_MASK 0x7
+#define UHS2_DEV_CONFIG_MAX_BLK_LEN_POS 20
+#define UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK 0xFFF
+#define UHS2_DEV_CONFIG_LINK_TRAN_CAPS1 (UHS2_DEV_CONFIG_REG + 0x005)
+#define UHS2_DEV_CONFIG_N_DATA_GAP_MASK 0xFF
+
+#define UHS2_DEV_CONFIG_LINK_TRAN_SET (UHS2_DEV_CONFIG_REG + 0x00C)
+#define UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN 0x200
+#define UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS 16
+
+/* Preset register */
+#define UHS2_DEV_CONFIG_PRESET (UHS2_DEV_CONFIG_REG + 0x040)
+
+#define UHS2_DEV_INT_REG 0x100
+
+#define UHS2_DEV_STATUS_REG 0x180
+
+#define UHS2_DEV_CMD_REG 0x200
+#define UHS2_DEV_CMD_FULL_RESET (UHS2_DEV_CMD_REG + 0x000)
+#define UHS2_DEV_CMD_GO_DORMANT_STATE (UHS2_DEV_CMD_REG + 0x001)
+#define UHS2_DEV_CMD_DORMANT_HIBER (0x1 << 7)
+#define UHS2_DEV_CMD_DEVICE_INIT (UHS2_DEV_CMD_REG + 0x002)
+#define UHS2_DEV_CMD_ENUMERATE (UHS2_DEV_CMD_REG + 0x003)
+#define UHS2_DEV_CMD_TRANS_ABORT (UHS2_DEV_CMD_REG + 0x004)
+
+#define UHS2_RCLK_MAX 52000000
+#define UHS2_RCLK_MIN 26000000
+
+#endif /* LINUX_MMC_UHS2_H */
--
2.28.0
According to Fig. 3-35 in "SD Host Controller Simplified Spec. Ver4.20":
- Prepare vdd1, vdd2 and ios.timing for using after/in step (2)
- chip_select is not used in UHS-II, used to return to the legacy flow
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/core.c | 61 +++++++++++++++++++++++++-----------
drivers/mmc/core/regulator.c | 14 +++++++++
2 files changed, 56 insertions(+), 19 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d42037f0f10d..5541ed956c4d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1315,33 +1315,52 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
if (host->ios.power_mode == MMC_POWER_ON)
return;
- mmc_pwrseq_pre_power_on(host);
+ if (host->flags & MMC_UHS2_SUPPORT) {
+ /* TODO: handle 'ocr' parameter */
+ host->ios.vdd = fls(host->ocr_avail) - 1;
+ host->ios.vdd2 = fls(host->ocr_avail_uhs2) - 1;
+ if (mmc_host_is_spi(host))
+ host->ios.chip_select = MMC_CS_HIGH;
+ else
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.timing = MMC_TIMING_UHS2;
+ } else {
+ mmc_pwrseq_pre_power_on(host);
- host->ios.vdd = fls(ocr) - 1;
- host->ios.power_mode = MMC_POWER_UP;
- /* Set initial state and call mmc_set_ios */
- mmc_set_initial_state(host);
+ host->ios.vdd = fls(ocr) - 1;
+ host->ios.power_mode = MMC_POWER_UP;
+ /* Set initial state and call mmc_set_ios */
+ mmc_set_initial_state(host);
- mmc_set_initial_signal_voltage(host);
+ mmc_set_initial_signal_voltage(host);
- /*
- * This delay should be sufficient to allow the power supply
- * to reach the minimum voltage.
- */
- mmc_delay(host->ios.power_delay_ms);
+ /*
+ * This delay should be sufficient to allow the power supply
+ * to reach the minimum voltage.
+ */
+ mmc_delay(host->ios.power_delay_ms);
- mmc_pwrseq_post_power_on(host);
+ mmc_pwrseq_post_power_on(host);
+ }
host->ios.clock = host->f_init;
-
host->ios.power_mode = MMC_POWER_ON;
+
mmc_set_ios(host);
- /*
- * This delay must be at least 74 clock sizes, or 1 ms, or the
- * time required to reach a stable voltage.
- */
- mmc_delay(host->ios.power_delay_ms);
+ if (host->flags & MMC_UHS2_SUPPORT)
+ /*
+ * This delay should be sufficient to allow the power supply
+ * to reach the minimum voltage.
+ */
+ /* TODO: avoid an immediate value */
+ mmc_delay(10);
+ else
+ /*
+ * This delay must be at least 74 clock sizes, or 1 ms, or the
+ * time required to reach a stable voltage.
+ */
+ mmc_delay(host->ios.power_delay_ms);
}
void mmc_power_off(struct mmc_host *host)
@@ -2316,7 +2335,11 @@ void mmc_start_host(struct mmc_host *host)
if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
mmc_claim_host(host);
- mmc_power_up(host, host->ocr_avail);
+
+ /* Power up here will make UHS2 init ugly. */
+ if (!(host->caps & MMC_CAP_UHS2))
+ mmc_power_up(host, host->ocr_avail);
+
mmc_release_host(host);
}
diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 609201a467ef..629e25bc8cb7 100644
--- a/drivers/mmc/core/regulator.c
+++ b/drivers/mmc/core/regulator.c
@@ -249,6 +249,7 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
+ mmc->supply.vmmc2 = devm_regulator_get_optional(dev, "vmmc2");
if (IS_ERR(mmc->supply.vmmc)) {
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
@@ -268,6 +269,19 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
dev_dbg(dev, "No vqmmc regulator found\n");
}
+ if (IS_ERR(mmc->supply.vmmc2)) {
+ if (PTR_ERR(mmc->supply.vmmc2) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_dbg(dev, "No vmmc2 regulator found\n");
+ } else {
+ ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc2);
+ if (ret > 0)
+ mmc->ocr_avail_uhs2 = ret;
+ else
+ dev_warn(dev, "Failed getting UHS2 OCR mask: %d\n",
+ ret);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
--
2.28.0
mmc_set_chip_select() should be called only in UHS-II mode,
and not for UHS-II mode.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/core.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 5541ed956c4d..4e12bd98fc08 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -898,8 +898,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
*/
void mmc_set_chip_select(struct mmc_host *host, int mode)
{
- host->ios.chip_select = mode;
- mmc_set_ios(host);
+ if (!(host->flags & MMC_UHS2_INITIALIZED)) {
+ host->ios.chip_select = mode;
+ mmc_set_ios(host);
+ }
}
/*
--
2.28.0
UHS-II's data command packet has TMODE fields in which parameters for
data transaction, like Duplex Mode(DM) and Length Mode(LM), are specified.
In some cases, we don't need to initialize them and so set uhs2_tmode0_flag
to 1 in order to skip them in generating a packet.
(The code will be added in the next commit.)
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/block.c | 7 ++++++-
drivers/mmc/core/sd_ops.c | 3 +++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 8d3df0be0355..f09794811042 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -56,6 +56,7 @@
#include "mmc_ops.h"
#include "quirks.h"
#include "sd_ops.h"
+#include "uhs2.h"
MODULE_ALIAS("mmc:block");
#ifdef MODULE_PARAM_PREFIX
@@ -1523,6 +1524,9 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
struct request *req = mmc_queue_req_to_req(mqrq);
struct mmc_blk_data *md = mq->blkdata;
bool do_rel_wr, do_data_tag;
+ bool do_multi;
+
+ do_multi = (card->host->flags & MMC_UHS2_INITIALIZED) ? true : false;
mmc_blk_data_prep(mq, mqrq, disable_multi, &do_rel_wr, &do_data_tag);
@@ -1533,7 +1537,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
brq->cmd.arg <<= 9;
brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
- if (brq->data.blocks > 1 || do_rel_wr) {
+ if (brq->data.blocks > 1 || do_rel_wr || do_multi) {
/* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
*/
@@ -1546,6 +1550,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
brq->mrq.stop = NULL;
readcmd = MMC_READ_SINGLE_BLOCK;
writecmd = MMC_WRITE_BLOCK;
+ brq->cmd.uhs2_tmode0_flag = 1;
}
brq->cmd.opcode = rq_data_dir(req) == READ ? readcmd : writecmd;
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 22bf528294b9..f58bb50872f6 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -235,6 +235,7 @@ int mmc_app_send_scr(struct mmc_card *card)
cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.uhs2_tmode0_flag = 1;
data.blksz = 8;
data.blocks = 1;
@@ -282,6 +283,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
cmd.arg &= ~(0xF << (group * 4));
cmd.arg |= value << (group * 4);
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.uhs2_tmode0_flag = 1;
data.blksz = 64;
data.blocks = 1;
@@ -323,6 +325,7 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr)
cmd.opcode = SD_APP_SD_STATUS;
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.uhs2_tmode0_flag = 1;
data.blksz = 64;
data.blocks = 1;
--
2.28.0
In SD-TRAN protocol, legacy SD commands should be "encapsulated" in SD
packets as described in SD specification.
Please see section 7.1 and 7.2.1 in "UHS-II Simplified Addendum."
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/core.c | 18 +++++++++++
drivers/mmc/core/uhs2.c | 70 +++++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/uhs2.h | 1 +
3 files changed, 89 insertions(+)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index a08cb1c0f6d2..ac43e93033e6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -337,6 +337,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
+ struct uhs2_command uhs2_cmd;
+ u32 payload[4]; /* for maximum size */
int err;
init_completion(&mrq->cmd_completion);
@@ -354,6 +356,13 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
if (err)
return err;
+ if (host->flags & MMC_UHS2_SUPPORT &&
+ host->flags & MMC_UHS2_INITIALIZED) {
+ uhs2_cmd.payload = payload;
+ mrq->cmd->uhs2_cmd = &uhs2_cmd;
+ uhs2_prepare_sd_cmd(host, mrq);
+ }
+
led_trigger_event(host->led, LED_FULL);
__mmc_start_request(host, mrq);
@@ -433,6 +442,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
*/
int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
+ struct uhs2_command uhs2_cmd;
+ u32 payload[4]; /* for maximum size */
int err;
/*
@@ -453,6 +464,13 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
if (err)
goto out_err;
+ if (host->flags & MMC_UHS2_SUPPORT &&
+ host->flags & MMC_UHS2_INITIALIZED) {
+ uhs2_cmd.payload = payload;
+ mrq->cmd->uhs2_cmd = &uhs2_cmd;
+ uhs2_prepare_sd_cmd(host, mrq);
+ }
+
err = host->cqe_ops->cqe_request(host, mrq);
if (err)
goto out_err;
diff --git a/drivers/mmc/core/uhs2.c b/drivers/mmc/core/uhs2.c
index acb46b5b57ef..1f6d0e0cf355 100644
--- a/drivers/mmc/core/uhs2.c
+++ b/drivers/mmc/core/uhs2.c
@@ -807,3 +807,73 @@ int mmc_uhs2_rescan_try_freq(struct mmc_host *host, unsigned int freq)
return err;
}
EXPORT_SYMBOL_GPL(mmc_uhs2_rescan_try_freq);
+
+/**
+ * uhs2_prepare_sd_cmd - prepare for SD command packet
+ * @host: MMC host
+ * @mrq: MMC request
+ *
+ * Initialize and fill in a header and a payload of SD command packet.
+ * The caller should allocate uhs2_command in host->cmd->uhs2_cmd in
+ * advance.
+ *
+ * Return: 0 on success, non-zero error on failure
+ */
+int uhs2_prepare_sd_cmd(struct mmc_host *host, struct mmc_request *mrq)
+{
+ struct mmc_command *cmd;
+ struct uhs2_command *uhs2_cmd;
+ u16 header = 0, arg = 0;
+ u32 *payload;
+ u8 plen = 0;
+
+ cmd = mrq->cmd;
+ header = host->uhs2_dev_prop.node_id;
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
+ header |= UHS2_PACKET_TYPE_DCMD;
+ else
+ header |= UHS2_PACKET_TYPE_CCMD;
+
+ arg = cmd->opcode << UHS2_SD_CMD_INDEX_POS;
+
+ uhs2_cmd = cmd->uhs2_cmd;
+ payload = uhs2_cmd->payload;
+ plen = 2; /* at the maximum */
+
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC &&
+ !cmd->uhs2_tmode0_flag) {
+ if (host->flags & MMC_UHS2_2L_HD)
+ arg |= UHS2_DCMD_2L_HD_MODE;
+
+ arg |= UHS2_DCMD_LM_TLEN_EXIST;
+
+ if (cmd->data->blocks == 1 &&
+ cmd->data->blksz != 512 &&
+ cmd->opcode != MMC_READ_SINGLE_BLOCK &&
+ cmd->opcode != MMC_WRITE_BLOCK) {
+ arg |= UHS2_DCMD_TLUM_BYTE_MODE;
+ payload[1] = uhs2_dcmd_convert_msb(cmd->data->blksz);
+ } else {
+ payload[1] = uhs2_dcmd_convert_msb(cmd->data->blocks);
+ }
+
+ if (cmd->opcode == SD_IO_RW_EXTENDED) {
+ arg &= ~(UHS2_DCMD_LM_TLEN_EXIST |
+ UHS2_DCMD_TLUM_BYTE_MODE |
+ UHS2_NATIVE_DCMD_DAM_IO);
+ payload[1] = 0;
+ plen = 1;
+ }
+ } else {
+ plen = 1;
+ }
+
+ payload[0] = uhs2_dcmd_convert_msb(cmd->arg);
+ pr_debug("%s: %s: sd_cmd->arg = 0x%x, payload[0]= 0x%x.\n",
+ mmc_hostname(host), __func__, cmd->arg, payload[0]);
+
+ uhs2_cmd_assemble(cmd, uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uhs2_prepare_sd_cmd);
diff --git a/drivers/mmc/core/uhs2.h b/drivers/mmc/core/uhs2.h
index e3389d4dda3b..48486ba21062 100644
--- a/drivers/mmc/core/uhs2.h
+++ b/drivers/mmc/core/uhs2.h
@@ -16,5 +16,6 @@
#define UHS2_PHY_INIT_ERR 1
int mmc_uhs2_rescan_try_freq(struct mmc_host *host, unsigned int freq);
+int uhs2_prepare_sd_cmd(struct mmc_host *host, struct mmc_request *mrq);
#endif /* MMC_UHS2_H */
--
2.28.0
This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
in the following commits to indicate UHS-II specific code in sdhci
controllers.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/Kconfig | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 31481c9fcc2e..5ca9ac03db40 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -89,6 +89,15 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
This is the case for the Nintendo Wii SDHCI.
+config MMC_SDHCI_UHS2
+ tristate "UHS2 support on SDHCI controller"
+ depends on MMC_SDHCI
+ help
+ This option is selected by SDHCI controller drivers that want to
+ support UHS2-capable devices.
+
+ If you have a controller with this feature, say Y or M here.
+
config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
--
2.28.0
Add UHS-II related definitions in shdci.h and sdhci-uhs2.h.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.h | 210 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.h | 73 +++++++++++-
2 files changed, 282 insertions(+), 1 deletion(-)
create mode 100644 drivers/mmc/host/sdhci-uhs2.h
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
new file mode 100644
index 000000000000..3b157df9c89c
--- /dev/null
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/drivers/mmc/host/sdhci-uhs2.h - Secure Digital Host Controller
+ * Interface driver
+ *
+ * Header file for Host Controller UHS2 related registers and I/O accessors.
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ */
+#ifndef __SDHCI_UHS2_H
+#define __SDHCI_UHS2_H
+
+#include <linux/bits.h>
+
+/*
+ * UHS-II Controller registers
+ * 0x74 preset in sdhci.h
+ * 0x80
+ * 0x84-0xB4
+ * 0xB8-0xCF
+ * 0xE0-0xE7
+ */
+/* UHS2 */
+#define SDHCI_UHS2_BLOCK_SIZE 0x80
+#define SDHCI_UHS2_MAKE_BLKSZ(dma, blksz) \
+ ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF))
+
+#define SDHCI_UHS2_BLOCK_COUNT 0x84
+
+#define SDHCI_UHS2_CMD_PACKET 0x88
+#define SDHCI_UHS2_CMD_PACK_MAX_LEN 20
+
+#define SDHCI_UHS2_TRANS_MODE 0x9C
+#define SDHCI_UHS2_TRNS_DMA BIT(0)
+#define SDHCI_UHS2_TRNS_BLK_CNT_EN BIT(1)
+#define SDHCI_UHS2_TRNS_DATA_TRNS_WRT BIT(4)
+#define SDHCI_UHS2_TRNS_BLK_BYTE_MODE BIT(5)
+#define SDHCI_UHS2_TRNS_RES_R5 BIT(6)
+#define SDHCI_UHS2_TRNS_RES_ERR_CHECK_EN BIT(7)
+#define SDHCI_UHS2_TRNS_RES_INT_DIS BIT(8)
+#define SDHCI_UHS2_TRNS_WAIT_EBSY BIT(14)
+#define SDHCI_UHS2_TRNS_2L_HD BIT(15)
+
+#define SDHCI_UHS2_COMMAND 0x9E
+#define SDHCI_UHS2_COMMAND_SUB_CMD 0x0004
+#define SDHCI_UHS2_COMMAND_DATA 0x0020
+#define SDHCI_UHS2_COMMAND_TRNS_ABORT 0x0040
+#define SDHCI_UHS2_COMMAND_CMD12 0x0080
+#define SDHCI_UHS2_COMMAND_DORMANT 0x00C0
+#define SDHCI_UHS2_COMMAND_PACK_LEN_MASK GENMASK(12,8)
+#define SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT 8
+
+#define SDHCI_UHS2_RESPONSE 0xA0
+#define SDHCI_UHS2_RESPONSE_MAX_LEN 20
+
+#define SDHCI_UHS2_MSG_SELECT 0xB4
+#define SDHCI_UHS2_MSG_SELECT_CURR 0x0
+#define SDHCI_UHS2_MSG_SELECT_ONE 0x1
+#define SDHCI_UHS2_MSG_SELECT_TWO 0x2
+#define SDHCI_UHS2_MSG_SELECT_THREE 0x3
+
+#define SDHCI_UHS2_MSG 0xB8
+
+#define SDHCI_UHS2_DEV_INT_STATUS 0xBC
+
+#define SDHCI_UHS2_DEV_SELECT 0xBE
+#define SDHCI_UHS2_DEV_SELECT_DEV_SEL_MASK GENMASK(3,0)
+#define SDHCI_UHS2_DEV_SELECT_INT_MSG_EN BIT(7)
+
+#define SDHCI_UHS2_DEV_INT_CODE 0xBF
+
+#define SDHCI_UHS2_SW_RESET 0xC0
+#define SDHCI_UHS2_SW_RESET_FULL 0x0001
+#define SDHCI_UHS2_SW_RESET_SD 0x0002
+
+#define SDHCI_UHS2_TIMER_CTRL 0xC2
+#define SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT 4
+
+#define SDHCI_UHS2_ERR_INT_STATUS 0xC4
+#define SDHCI_UHS2_ERR_INT_STATUS_EN 0xC8
+#define SDHCI_UHS2_ERR_INT_SIG_EN 0xCC
+#define SDHCI_UHS2_ERR_INT_STATUS_HEADER BIT(0)
+#define SDHCI_UHS2_ERR_INT_STATUS_RES BIT(1)
+#define SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP BIT(2)
+#define SDHCI_UHS2_ERR_INT_STATUS_CRC BIT(3)
+#define SDHCI_UHS2_ERR_INT_STATUS_FRAME BIT(4)
+#define SDHCI_UHS2_ERR_INT_STATUS_TID BIT(5)
+#define SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER BIT(7)
+#define SDHCI_UHS2_ERR_INT_STATUS_EBUSY BIT(8)
+#define SDHCI_UHS2_ERR_INT_STATUS_ADMA BIT(15)
+#define SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT BIT(16)
+#define SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT BIT(17)
+#define SDHCI_UHS2_ERR_INT_STATUS_VENDOR BIT(27)
+#define SDHCI_UHS2_ERR_INT_STATUS_MASK \
+ (SDHCI_UHS2_ERR_INT_STATUS_HEADER | \
+ SDHCI_UHS2_ERR_INT_STATUS_RES | \
+ SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP | \
+ SDHCI_UHS2_ERR_INT_STATUS_CRC | \
+ SDHCI_UHS2_ERR_INT_STATUS_FRAME | \
+ SDHCI_UHS2_ERR_INT_STATUS_TID | \
+ SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER | \
+ SDHCI_UHS2_ERR_INT_STATUS_EBUSY | \
+ SDHCI_UHS2_ERR_INT_STATUS_ADMA | \
+ SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT | \
+ SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT)
+#define SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK \
+ (SDHCI_UHS2_ERR_INT_STATUS_HEADER | \
+ SDHCI_UHS2_ERR_INT_STATUS_RES | \
+ SDHCI_UHS2_ERR_INT_STATUS_FRAME | \
+ SDHCI_UHS2_ERR_INT_STATUS_TID | \
+ SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
+/* CRC Error occurs during a packet receiving */
+#define SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK \
+ (SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP | \
+ SDHCI_UHS2_ERR_INT_STATUS_CRC | \
+ SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER | \
+ SDHCI_UHS2_ERR_INT_STATUS_EBUSY | \
+ SDHCI_UHS2_ERR_INT_STATUS_ADMA | \
+ SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT)
+
+#define SDHCI_UHS2_SET_PTR 0xE0
+#define SDHCI_UHS2_GEN_SET_POWER_LOW 0x0001
+#define SDHCI_UHS2_GEN_SET_N_LANES_POS 8
+#define SDHCI_UHS2_GEN_SET_2L_FD_HD 0x0
+#define SDHCI_UHS2_GEN_SET_2D1U_FD 0x2
+#define SDHCI_UHS2_GEN_SET_1D2U_FD 0x3
+#define SDHCI_UHS2_GEN_SET_2D2U_FD 0x4
+
+#define SDHCI_UHS2_PHY_SET_SPEED_POS 6
+#define SDHCI_UHS2_PHY_SET_HIBER_EN BIT(12)
+#define SDHCI_UHS2_PHY_SET_N_LSS_SYN_MASK GENMASK(19,16)
+#define SDHCI_UHS2_PHY_SET_N_LSS_SYN_POS 16
+#define SDHCI_UHS2_PHY_SET_N_LSS_DIR_MASK GENMASK(23,20)
+#define SDHCI_UHS2_PHY_SET_N_LSS_DIR_POS 20
+
+#define SDHCI_UHS2_TRAN_SET_N_FCU_MASK GENMASK(15,8)
+#define SDHCI_UHS2_TRAN_SET_N_FCU_POS 8
+#define SDHCI_UHS2_TRAN_SET_RETRY_CNT_MASK GENMASK(17,16)
+#define SDHCI_UHS2_TRAN_SET_RETRY_CNT_POS 16
+
+#define SDHCI_UHS2_TRAN_SET_1_N_DAT_GAP_MASK GENMASK(7,0)
+
+#define SDHCI_UHS2_HOST_CAPS_PTR 0xE2
+#define SDHCI_UHS2_HOST_CAPS_GEN_OFFSET 0
+#define SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK GENMASK(3,0)
+#define SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK GENMASK(7,4)
+#define SDHCI_UHS2_HOST_CAPS_GEN_GAP(gap) ((gap) * 360)
+#define SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT 4
+#define SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK GENMASK(13,8)
+#define SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT 8
+#define SDHCI_UHS2_HOST_CAPS_GEN_2L_HD_FD 1
+#define SDHCI_UHS2_HOST_CAPS_GEN_2D1U_FD 2
+#define SDHCI_UHS2_HOST_CAPS_GEN_1D2U_FD 4
+#define SDHCI_UHS2_HOST_CAPS_GEN_2D2U_FD 8
+#define SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64 BIT(14)
+#define SDHCI_UHS2_HOST_CAPS_GEN_BOOT BIT(15)
+#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK GENMASK(17,16)
+#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT 16
+#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_RMV 0
+#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_EMB 1
+#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_EMB_RMV 2
+#define SDHCI_UHS2_HOST_CAPS_GEN_NUM_DEV_MASK GENMASK(21,18)
+#define SDHCI_UHS2_HOST_CAPS_GEN_NUM_DEV_SHIFT 18
+#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_MASK GENMASK(23,22)
+#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_SHIFT 22
+#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_P2P 0
+#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_RING 1
+#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_HUB 2
+#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_HUB_RING 3
+
+#define SDHCI_UHS2_HOST_CAPS_PHY_OFFSET 4
+#define SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK GENMASK(5,0)
+#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK GENMASK(7,6)
+#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT 6
+#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_A 0
+#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_B 1
+#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK GENMASK(19,16)
+#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT 16
+#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK GENMASK(23,20)
+#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT 20
+#define SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET 8
+#define SDHCI_UHS2_HOST_CAPS_TRAN_LINK_REV_MASK GENMASK(5,0)
+#define SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK GENMASK(15,8)
+#define SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT 8
+#define SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK GENMASK(18,16)
+#define SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT 16
+#define SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK GENMASK(31,20)
+#define SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT 20
+
+#define SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET 12
+#define SDHCI_UHS2_HOST_CAPS_TRAN_1_N_DATA_GAP_MASK GENMASK(7,0)
+
+#define SDHCI_UHS2_TEST_PTR 0xE4
+#define SDHCI_UHS2_TEST_ERR_HEADER BIT(0)
+#define SDHCI_UHS2_TEST_ERR_RES BIT(1)
+#define SDHCI_UHS2_TEST_ERR_RETRY_EXP BIT(2)
+#define SDHCI_UHS2_TEST_ERR_CRC BIT(3)
+#define SDHCI_UHS2_TEST_ERR_FRAME BIT(4)
+#define SDHCI_UHS2_TEST_ERR_TID BIT(5)
+#define SDHCI_UHS2_TEST_ERR_UNRECOVER BIT(7)
+#define SDHCI_UHS2_TEST_ERR_EBUSY BIT(8)
+#define SDHCI_UHS2_TEST_ERR_ADMA BIT(15)
+#define SDHCI_UHS2_TEST_ERR_RES_TIMEOUT BIT(16)
+#define SDHCI_UHS2_TEST_ERR_DEADLOCK_TIMEOUT BIT(17)
+#define SDHCI_UHS2_TEST_ERR_VENDOR BIT(27)
+
+#define SDHCI_UHS2_EMBED_CTRL 0xE6
+#define SDHCI_UHS2_VENDOR 0xE8
+
+#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 0770c036e2ff..d9d7a76cedc1 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -43,8 +43,27 @@
#define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20
+/*
+ * Defined in Host Version 4.10.
+ * 1 - R5 (SDIO)
+ * 0 - R1 (Memory)
+ */
+#define SDHCI_TRNS_RES_TYPE 0x40
+#define SDHCI_TRNS_RES_ERR_CHECK 0x80
+#define SDHCI_TRNS_RES_INT_DIS 0x0100
+
#define SDHCI_COMMAND 0x0E
#define SDHCI_CMD_RESP_MASK 0x03
+
+/*
+ * Host Version 4.10 adds this bit to distinguish a main command or
+ * sub command.
+ * CMD53(SDIO) - main command
+ * CMD52(SDIO) - sub command which doesn't have data block or doesn't
+ * indicate busy.
+ */
+#define SDHCI_CMD_SUB_CMD 0x04
+
#define SDHCI_CMD_CRC 0x08
#define SDHCI_CMD_INDEX 0x10
#define SDHCI_CMD_DATA 0x20
@@ -60,11 +79,19 @@
#define SDHCI_RESPONSE 0x10
+#define SDHCI_RESPONSE_CM_TRAN_ABORT_OFFSET 0x10
+#define SDHCI_RESPONSE_CM_TRAN_ABORT_SIZE 4
+#define SDHCI_RESPONSE_SD_TRAN_ABORT_OFFSET 0x18
+#define SDHCI_RESPONSE_SD_TRAN_ABORT_SIZE 8
+
#define SDHCI_BUFFER 0x20
#define SDHCI_PRESENT_STATE 0x24
#define SDHCI_CMD_INHIBIT 0x00000001
#define SDHCI_DATA_INHIBIT 0x00000002
+
+#define SDHCI_DATA_HIGH_LVL_MASK 0x000000F0
+
#define SDHCI_DOING_WRITE 0x00000100
#define SDHCI_DOING_READ 0x00000200
#define SDHCI_SPACE_AVAILABLE 0x00000400
@@ -80,6 +107,13 @@
#define SDHCI_DATA_0_LVL_MASK 0x00100000
#define SDHCI_CMD_LVL 0x01000000
+#define SDHCI_HOST_REGULATOR_STABLE 0x02000000
+#define SDHCI_CMD_NOT_ISSUE_ERR 0x08000000
+#define SDHCI_SUB_CMD_STATUS 0x10000000
+#define SDHCI_UHS2_IN_DORMANT_STATE 0x20000000
+#define SDHCI_UHS2_LANE_SYNC 0x40000000
+#define SDHCI_UHS2_IF_DETECT 0x80000000
+
#define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02
@@ -100,6 +134,11 @@
#define SDHCI_POWER_300 0x0C
#define SDHCI_POWER_330 0x0E
+/* VDD2 - UHS2 */
+#define SDHCI_VDD2_POWER_ON 0x10
+#define SDHCI_VDD2_POWER_180 0xA0
+#define SDHCI_VDD2_POWER_120 0x80
+
#define SDHCI_BLOCK_GAP_CONTROL 0x2A
#define SDHCI_WAKE_UP_CONTROL 0x2B
@@ -110,7 +149,7 @@
#define SDHCI_CLOCK_CONTROL 0x2C
#define SDHCI_DIVIDER_SHIFT 8
#define SDHCI_DIVIDER_HI_SHIFT 6
-#define SDHCI_DIV_MASK 0xFF
+#define SDHCI_DIV_MASK 0xFF
#define SDHCI_DIV_MASK_LEN 8
#define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
@@ -139,6 +178,10 @@
#define SDHCI_INT_CARD_REMOVE 0x00000080
#define SDHCI_INT_CARD_INT 0x00000100
#define SDHCI_INT_RETUNE 0x00001000
+
+/* Host Version 4.10 */
+#define SDHCI_INT_FX_EVENT 0x00002000
+
#define SDHCI_INT_CQE 0x00004000
#define SDHCI_INT_ERROR 0x00008000
#define SDHCI_INT_TIMEOUT 0x00010000
@@ -152,6 +195,9 @@
#define SDHCI_INT_AUTO_CMD_ERR 0x01000000
#define SDHCI_INT_ADMA_ERROR 0x02000000
+/* Host Version 4.0 */
+#define SDHCI_INT_RESPONSE_ERROR 0x08000000
+
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
@@ -178,6 +224,9 @@
#define SDHCI_AUTO_CMD_END_BIT 0x00000008
#define SDHCI_AUTO_CMD_INDEX 0x00000010
+/* Host Version 4.10 */
+#define SDHCI_ACMD_RESPONSE_ERROR 0x0020
+
#define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_UHS_MASK 0x0007
#define SDHCI_CTRL_UHS_SDR12 0x0000
@@ -186,6 +235,7 @@
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
+#define SDHCI_CTRL_UHS_2 0x0007 /* UHS-2 */
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
@@ -194,9 +244,12 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_UHS2_INTERFACE_EN 0x0100 /* UHS-2 */
+#define SDHCI_CTRL_ADMA2_LEN_MODE 0x0400
#define SDHCI_CMD23_ENABLE 0x0800
#define SDHCI_CTRL_V4_MODE 0x1000
#define SDHCI_CTRL_64BIT_ADDR 0x2000
+#define SDHCI_CTRL_ASYNC_INT_EN 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -217,11 +270,13 @@
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT_V4 0x08000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_CAN_ASYNC_INT 0x20000000
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
+#define SDHCI_SUPPORT_UHS2 0x00000008 /* UHS-2 support */
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
@@ -230,19 +285,28 @@
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
#define SDHCI_CAN_DO_ADMA3 0x08000000
+#define SDHCI_SUPPORT_VDD2_180 0x10000000 /* UHS-2 1.8V VDD2 */
+#define SDHCI_RSVD_FOR_VDD2 0x20000000 /* Rsvd for future VDD2 */
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_MAX_CURRENT 0x48
+#define SDHCI_MAX_CURRENT_1 0x4C
#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
+#define SDHCI_MAX_CURRENT_VDD2_180_MASK GENMASK(7, 0) /* UHS2 */
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */
#define SDHCI_SET_ACMD12_ERROR 0x50
+/* Host Version 4.10 */
+#define SDHCI_SET_ACMD_RESPONSE_ERROR 0x20
#define SDHCI_SET_INT_ERROR 0x52
+/* Host Version 4.10 */
+#define SDHCI_SET_INT_TUNING_ERROR 0x0400
+#define SDHCI_SET_INT_RESPONSE_ERROR 0x0800
#define SDHCI_ADMA_ERROR 0x54
@@ -259,10 +323,16 @@
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
+
+/* TODO: 0x74 is used for UHS2 in 4.10. How about HS400? */
+/* UHS2 */
+#define SDHCI_PRESET_FOR_UHS2 0x74
#define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
#define SDHCI_PRESET_CLKGEN_SEL BIT(10)
#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
+#define SDHCI_ADMA3_ADDRESS 0x78
+
#define SDHCI_SLOT_INT_STATUS 0xFC
#define SDHCI_HOST_VERSION 0xFE
@@ -652,6 +722,7 @@ struct sdhci_ops {
void (*request_done)(struct sdhci_host *host,
struct mmc_request *mrq);
void (*dump_vendor_regs)(struct sdhci_host *host);
+ void (*dump_uhs2_regs)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
2.28.0
This patch adds sdhci-uhs2.c as a module for UHS-II support.
This is a skelton for further development in this patch series.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-uhs2.c | 46 +++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
create mode 100644 drivers/mmc/host/sdhci-uhs2.c
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 451c25fc2c69..f6e9b05c3b30 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
+obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
new file mode 100644
index 000000000000..f29d3a4ed43c
--- /dev/null
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/drivers/mmc/host/sdhci_uhs2.c - Secure Digital Host Controller
+ * Interface driver
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ * Copyright (C) 2020 Genesys Logic, Inc.
+ * Authors: Ben Chuang <[email protected]>
+ * Copyright (C) 2020 Linaro Limited
+ * Author: AKASHI Takahiro <[email protected]>
+ */
+
+#include <linux/module.h>
+
+#include "sdhci.h"
+#include "sdhci-uhs2.h"
+
+#define DRIVER_NAME "sdhci_uhs2"
+#define DBG(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
+{
+ return 0;
+}
+
+static int __init sdhci_uhs2_mod_init(void)
+{
+ return 0;
+}
+module_init(sdhci_uhs2_mod_init);
+
+static void __exit sdhci_uhs2_exit(void)
+{
+}
+module_exit(sdhci_uhs2_exit);
+
+MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
+MODULE_DESCRIPTION("MMC UHS-II Support");
+MODULE_LICENSE("GPL v2");
--
2.28.0
Dump UHS-II specific registers, if available, in sdhci_dumpregs()
for informative/debugging use.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 30 ++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 4 ++++
drivers/mmc/host/sdhci.c | 3 +++
3 files changed, 37 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index f29d3a4ed43c..08905ed081fb 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -18,6 +18,36 @@
#define DRIVER_NAME "sdhci_uhs2"
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+#define SDHCI_UHS2_DUMP(f, x...) \
+ pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+
+void sdhci_uhs2_dump_regs(struct sdhci_host *host)
+{
+ if (!host->mmc || !(host->mmc->flags & MMC_UHS2_SUPPORT))
+ return;
+
+ SDHCI_UHS2_DUMP("==================== UHS2 ==================\n");
+ SDHCI_UHS2_DUMP("Blk Size: 0x%08x | Blk Cnt: 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_BLOCK_SIZE),
+ sdhci_readl(host, SDHCI_UHS2_BLOCK_COUNT));
+ SDHCI_UHS2_DUMP("Cmd: 0x%08x | Trn mode: 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_COMMAND),
+ sdhci_readw(host, SDHCI_UHS2_TRANS_MODE));
+ SDHCI_UHS2_DUMP("Int Stat: 0x%08x | Dev Sel : 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_DEV_INT_STATUS),
+ sdhci_readb(host, SDHCI_UHS2_DEV_SELECT));
+ SDHCI_UHS2_DUMP("Dev Int Code: 0x%08x\n",
+ sdhci_readb(host, SDHCI_UHS2_DEV_INT_CODE));
+ SDHCI_UHS2_DUMP("Reset: 0x%08x | Timer: 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_SW_RESET),
+ sdhci_readw(host, SDHCI_UHS2_TIMER_CTRL));
+ SDHCI_UHS2_DUMP("ErrInt: 0x%08x | ErrIntEn: 0x%08x\n",
+ sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS),
+ sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS_EN));
+ SDHCI_UHS2_DUMP("ErrSigEn: 0x%08x\n",
+ sdhci_readl(host, SDHCI_UHS2_ERR_INT_SIG_EN));
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
/*****************************************************************************\
* *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 3b157df9c89c..b9529d32b58d 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -207,4 +207,8 @@
#define SDHCI_UHS2_EMBED_CTRL 0xE6
#define SDHCI_UHS2_VENDOR 0xE8
+struct sdhci_host;
+
+void sdhci_uhs2_dump_regs(struct sdhci_host *host);
+
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 592a55a34b58..d4a57e8c9bb8 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -111,6 +111,9 @@ void sdhci_dumpregs(struct sdhci_host *host)
}
}
+ if (host->ops->dump_uhs2_regs)
+ host->ops->dump_uhs2_regs(host);
+
if (host->ops->dump_vendor_regs)
host->ops->dump_vendor_regs(host);
--
2.28.0
For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
so no voltage switch required.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 2bf78cc4e9ed..1eca89359351 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -178,6 +178,29 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
+/*****************************************************************************\
+ * *
+ * MMC callbacks *
+ * *
+\*****************************************************************************/
+
+static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ /*
+ * For UHS2, the signal voltage is supplied by vdd2 which is
+ * already 1.8v so no voltage switch required.
+ */
+ if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+ host->version >= SDHCI_SPEC_400 &&
+ host->mmc->flags & MMC_UHS2_SUPPORT)
+ return 0;
+
+ return sdhci_start_signal_voltage_switch(mmc, ios);
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -186,6 +209,9 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
{
+ host->mmc_host_ops.start_signal_voltage_switch =
+ sdhci_uhs2_start_signal_voltage_switch;
+
return 0;
}
--
2.28.0
This is a sdhci version of mmc's set_ios operation.
It covers both UHS-I and UHS-II.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 100 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 1 +
drivers/mmc/host/sdhci.c | 40 +++++++++-----
drivers/mmc/host/sdhci.h | 2 +
4 files changed, 128 insertions(+), 15 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index d9e98c097bfe..637464748cc4 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -263,6 +263,74 @@ void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
+/**
+ * sdhci_uhs2_clear_set_irqs - set Error Interrupt Status Enable register
+ * @host: SDHCI host
+ * @clear: bit-wise clear mask
+ * @set: bit-wise set mask
+ *
+ * Set/unset bits in UHS-II Error Interrupt Status Enable register
+ */
+void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+{
+ u32 ier;
+
+ ier = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS_EN);
+ ier &= ~clear;
+ ier |= set;
+ sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_STATUS_EN);
+ sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_SIG_EN);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_clear_set_irqs);
+
+static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u8 cmd_res, dead_lock;
+ u16 ctrl_2;
+ unsigned long flags;
+
+ /* FIXME: why lock? */
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* UHS2 Timeout Control */
+ sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
+
+ /* change to use calculate value */
+ cmd_res |= dead_lock << SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT;
+
+ sdhci_uhs2_clear_set_irqs(host,
+ SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
+ SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT,
+ 0);
+ sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
+ sdhci_uhs2_clear_set_irqs(host, 0,
+ SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
+ SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT);
+
+ /* UHS2 timing */
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (ios->timing == MMC_TIMING_UHS2)
+ ctrl_2 |= SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN;
+ else
+ ctrl_2 &= ~(SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN);
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+ if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
+ sdhci_enable_preset_value(host, true);
+
+ if (host->ops->set_power)
+ host->ops->set_power(host, ios->power_mode, ios->vdd);
+ else
+ sdhci_set_power(host, ios->power_mode, ios->vdd);
+ udelay(100);
+
+ host->timing = ios->timing;
+ sdhci_set_clock(host, host->clock);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
/*****************************************************************************\
* *
* MMC callbacks *
@@ -286,6 +354,37 @@ static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
return sdhci_start_signal_voltage_switch(mmc, ios);
}
+void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (!(host->version >= SDHCI_SPEC_400) ||
+ !(host->mmc->flags & MMC_UHS2_SUPPORT &&
+ host->mmc->caps & MMC_CAP_UHS2)) {
+ sdhci_set_ios(mmc, ios);
+ return;
+ }
+
+ if (ios->power_mode == MMC_POWER_UNDEFINED)
+ return;
+
+ if (host->flags & SDHCI_DEVICE_DEAD) {
+ if (!IS_ERR(mmc->supply.vmmc) &&
+ ios->power_mode == MMC_POWER_OFF)
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ if (!IS_ERR_OR_NULL(mmc->supply.vmmc2) &&
+ ios->power_mode == MMC_POWER_OFF)
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
+ return;
+ }
+
+ /* FIXME: host->timing = ios->timing */
+
+ sdhci_set_ios_common(mmc, ios);
+
+ __sdhci_uhs2_set_ios(mmc, ios);
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -296,6 +395,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
{
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_uhs2_start_signal_voltage_switch;
+ host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
return 0;
}
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index efe70577bc74..c1ff4ac1ab7a 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -214,5 +214,6 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
+void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0b741eb546cb..becb228330af 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -48,8 +48,6 @@
static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
-static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
-
static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
void sdhci_dumpregs(struct sdhci_host *host)
@@ -1836,6 +1834,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
case MMC_TIMING_MMC_HS400:
preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
break;
+ case MMC_TIMING_UHS2:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_UHS2);
+ break;
default:
pr_warn("%s: Invalid UHS-I mode selected\n",
mmc_hostname(host->mmc));
@@ -2249,20 +2250,9 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
}
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
-void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
- u8 ctrl;
-
- if (ios->power_mode == MMC_POWER_UNDEFINED)
- return;
-
- if (host->flags & SDHCI_DEVICE_DEAD) {
- if (!IS_ERR(mmc->supply.vmmc) &&
- ios->power_mode == MMC_POWER_OFF)
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
- return;
- }
/*
* Reset the chip on each power off.
@@ -2299,6 +2289,25 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->ops->set_power(host, ios->power_mode, ios->vdd);
else
sdhci_set_power(host, ios->power_mode, ios->vdd);
+}
+EXPORT_SYMBOL_GPL(sdhci_set_ios_common);
+
+void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u8 ctrl;
+
+ if (ios->power_mode == MMC_POWER_UNDEFINED)
+ return;
+
+ if (host->flags & SDHCI_DEVICE_DEAD) {
+ if (!IS_ERR(mmc->supply.vmmc) &&
+ ios->power_mode == MMC_POWER_OFF)
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ return;
+ }
+
+ sdhci_set_ios_common(mmc, ios);
if (host->ops->platform_send_init_74_clocks)
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
@@ -2869,7 +2878,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
}
EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
-static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
+void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
{
/* Host Controller v3.00 defines preset value registers */
if (host->version < SDHCI_SPEC_300)
@@ -2897,6 +2906,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
host->preset_enabled = enable;
}
}
+EXPORT_SYMBOL_GPL(sdhci_enable_preset_value);
static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
int err)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 2b5b8295cf92..e84ebddb20d8 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -851,6 +851,8 @@ void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
+void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
+void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios);
void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios);
--
2.28.0
Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init
operation. After detected, the host's UHS-II capabilities will be set up
here and interrupts will also be enabled.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 160 ++++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 637464748cc4..994dff967e85 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -391,12 +391,172 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* *
\*****************************************************************************/
+static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
+{
+ int timeout = 100;
+
+ udelay(200); /* wait for 200us before check */
+
+ while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_UHS2_IF_DETECT)) {
+ if (timeout == 0) {
+ pr_warn("%s: not detect UHS2 interface in 200us.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ /* Enable UHS2 error interrupts */
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+ SDHCI_UHS2_ERR_INT_STATUS_MASK);
+
+ timeout = 150;
+ while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_UHS2_LANE_SYNC)) {
+ if (timeout == 0) {
+ pr_warn("%s: UHS2 Lane sync fail in 150ms.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ DBG("%s: UHS2 Lane synchronized in UHS2 mode, PHY is initialized.\n",
+ mmc_hostname(host->mmc));
+ return 0;
+}
+
+static int sdhci_uhs2_init(struct sdhci_host *host)
+{
+ u16 caps_ptr = 0;
+ u32 caps_gen = 0;
+ u32 caps_phy = 0;
+ u32 caps_tran[2] = {0, 0};
+ struct mmc_host *mmc = host->mmc;
+
+ /*
+ * TODO: may add corresponding members in sdhci_host to
+ * keep these caps.
+ */
+ caps_ptr = sdhci_readw(host, SDHCI_UHS2_HOST_CAPS_PTR);
+ if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
+ pr_err("%s: SDHCI_UHS2_HOST_CAPS_PTR(%d) is wrong.\n",
+ mmc_hostname(mmc), caps_ptr);
+ return -ENODEV;
+ }
+ caps_gen = sdhci_readl(host,
+ caps_ptr + SDHCI_UHS2_HOST_CAPS_GEN_OFFSET);
+ caps_phy = sdhci_readl(host,
+ caps_ptr + SDHCI_UHS2_HOST_CAPS_PHY_OFFSET);
+ caps_tran[0] = sdhci_readl(host,
+ caps_ptr + SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET);
+ caps_tran[1] = sdhci_readl(host,
+ caps_ptr
+ + SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET);
+
+ /* General Caps */
+ mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK;
+ mmc->uhs2_caps.gap = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK) >>
+ SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT;
+ mmc->uhs2_caps.n_lanes = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT;
+ mmc->uhs2_caps.addr64 =
+ (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64) ? 1 : 0;
+ mmc->uhs2_caps.card_type =
+ (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK) >>
+ SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT;
+
+ /* PHY Caps */
+ mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK;
+ mmc->uhs2_caps.speed_range =
+ (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT;
+ mmc->uhs2_caps.n_lss_sync =
+ (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT;
+ mmc->uhs2_caps.n_lss_dir =
+ (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT;
+ if (mmc->uhs2_caps.n_lss_sync == 0)
+ mmc->uhs2_caps.n_lss_sync = 16 << 2;
+ else
+ mmc->uhs2_caps.n_lss_sync <<= 2;
+ if (mmc->uhs2_caps.n_lss_dir == 0)
+ mmc->uhs2_caps.n_lss_dir = 16 << 3;
+ else
+ mmc->uhs2_caps.n_lss_dir <<= 3;
+
+ /* LINK/TRAN Caps */
+ mmc->uhs2_caps.link_rev =
+ caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_LINK_REV_MASK;
+ mmc->uhs2_caps.n_fcu =
+ (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT;
+ if (mmc->uhs2_caps.n_fcu == 0)
+ mmc->uhs2_caps.n_fcu = 256;
+ mmc->uhs2_caps.host_type =
+ (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT;
+ mmc->uhs2_caps.maxblk_len =
+ (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK)
+ >> SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT;
+ mmc->uhs2_caps.n_data_gap =
+ caps_tran[1] & SDHCI_UHS2_HOST_CAPS_TRAN_1_N_DATA_GAP_MASK;
+
+ return 0;
+}
+
+static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ int ret = -EIO;
+
+ DBG("%s: begin UHS2 init.\n", __func__);
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (sdhci_uhs2_interface_detect(host)) {
+ pr_warn("%s: cannot detect UHS2 interface.\n",
+ mmc_hostname(host->mmc));
+ goto out;
+ }
+
+ if (sdhci_uhs2_init(host)) {
+ pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
+ goto out;
+ }
+
+ /* Init complete, do soft reset and enable UHS2 error irqs. */
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+ SDHCI_UHS2_ERR_INT_STATUS_MASK);
+ /*
+ * !!! SDHCI_INT_ENABLE and SDHCI_SIGNAL_ENABLE was cleared
+ * by SDHCI_UHS2_SW_RESET_SD
+ */
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ ret = 0;
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
{
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_uhs2_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
+ if (!host->mmc_host_ops.uhs2_detect_init)
+ host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
+
return 0;
}
--
2.28.0
This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 41 +++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 994dff967e85..55362ace1857 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -11,6 +11,7 @@
*/
#include <linux/delay.h>
+#include <linux/ktime.h>
#include <linux/module.h>
#include "sdhci.h"
@@ -385,6 +386,42 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
__sdhci_uhs2_set_ios(mmc, ios);
}
+static void sdhci_uhs2_disable_clk(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+static void sdhci_uhs2_enable_clk(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ ktime_t timeout;
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 20 ms */
+ timeout = ktime_add_ms(ktime_get(), 20);
+ while (1) {
+ bool timedout = ktime_after(ktime_get(), timeout);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ if (clk & SDHCI_CLOCK_INT_STABLE)
+ break;
+ if (timedout) {
+ pr_err("%s: Internal clock never stabilised.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return;
+ }
+ udelay(10);
+ }
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -556,6 +593,10 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
if (!host->mmc_host_ops.uhs2_detect_init)
host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
+ if (!host->mmc_host_ops.uhs2_disable_clk)
+ host->mmc_host_ops.uhs2_disable_clk = sdhci_uhs2_disable_clk;
+ if (!host->mmc_host_ops.uhs2_enable_clk)
+ host->mmc_host_ops.uhs2_enable_clk = sdhci_uhs2_enable_clk;
return 0;
}
--
2.28.0
This is a sdhci version of mmc's uhs2_set_reg operation.
UHS-II interface (related registers) will be initialised here.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 103 ++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 55362ace1857..d8afb99a9918 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -332,6 +332,68 @@ static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags);
}
+/* TODO: move this function to sdhci.c */
+static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+{
+ u32 ier;
+
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+ ier &= ~clear;
+ ier |= set;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_uhs2_set_config(struct sdhci_host *host)
+{
+ u32 value;
+ u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SET_PTR);
+ u16 sdhci_uhs2_gen_set_reg = (sdhci_uhs2_set_ptr + 0);
+ u16 sdhci_uhs2_phy_set_reg = (sdhci_uhs2_set_ptr + 4);
+ u16 sdhci_uhs2_tran_set_reg = (sdhci_uhs2_set_ptr + 8);
+ u16 sdhci_uhs2_tran_set_1_reg = (sdhci_uhs2_set_ptr + 12);
+
+ /* Set Gen Settings */
+ sdhci_writel(host, host->mmc->uhs2_caps.n_lanes_set <<
+ SDHCI_UHS2_GEN_SET_N_LANES_POS, sdhci_uhs2_gen_set_reg);
+
+ /* Set PHY Settings */
+ value = (host->mmc->uhs2_caps.n_lss_dir_set <<
+ SDHCI_UHS2_PHY_SET_N_LSS_DIR_POS) |
+ (host->mmc->uhs2_caps.n_lss_sync_set <<
+ SDHCI_UHS2_PHY_SET_N_LSS_SYN_POS);
+ if (host->mmc->flags & MMC_UHS2_SPEED_B)
+ value |= 1 << SDHCI_UHS2_PHY_SET_SPEED_POS;
+ sdhci_writel(host, value, sdhci_uhs2_phy_set_reg);
+
+ /* Set LINK-TRAN Settings */
+ value = (host->mmc->uhs2_caps.max_retry_set <<
+ SDHCI_UHS2_TRAN_SET_RETRY_CNT_POS) |
+ (host->mmc->uhs2_caps.n_fcu_set <<
+ SDHCI_UHS2_TRAN_SET_N_FCU_POS);
+ sdhci_writel(host, value, sdhci_uhs2_tran_set_reg);
+ sdhci_writel(host, host->mmc->uhs2_caps.n_data_gap_set,
+ sdhci_uhs2_tran_set_1_reg);
+}
+
+static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
+{
+ int timeout = 100;
+
+ while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_UHS2_IN_DORMANT_STATE)) {
+ if (timeout == 0) {
+ pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+ timeout--;
+ mdelay(1);
+ }
+ return 0;
+}
+
/*****************************************************************************\
* *
* MMC callbacks *
@@ -422,6 +484,45 @@ static void sdhci_uhs2_enable_clk(struct mmc_host *mmc)
}
}
+static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ int err = 0;
+ u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SET_PTR);
+ u16 sdhci_uhs2_phy_set_reg = (sdhci_uhs2_set_ptr + 4);
+
+ DBG("Begin sdhci_uhs2_set_reg, act %d.\n", act);
+ spin_lock_irqsave(&host->lock, flags);
+
+ switch (act) {
+ case SET_CONFIG:
+ sdhci_uhs2_set_config(host);
+ break;
+ case ENABLE_INT:
+ sdhci_clear_set_irqs(host, 0, SDHCI_INT_CARD_INT);
+ break;
+ case DISABLE_INT:
+ sdhci_clear_set_irqs(host, SDHCI_INT_CARD_INT, 0);
+ break;
+ case SET_SPEED_B:
+ sdhci_writeb(host, 1 << SDHCI_UHS2_PHY_SET_SPEED_POS,
+ sdhci_uhs2_phy_set_reg);
+ break;
+ case CHECK_DORMANT:
+ err = sdhci_uhs2_check_dormant(host);
+ break;
+ default:
+ pr_err("%s: input action %d is wrong!\n",
+ mmc_hostname(host->mmc), act);
+ err = -EIO;
+ break;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return err;
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -597,6 +698,8 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
host->mmc_host_ops.uhs2_disable_clk = sdhci_uhs2_disable_clk;
if (!host->mmc_host_ops.uhs2_enable_clk)
host->mmc_host_ops.uhs2_enable_clk = sdhci_uhs2_enable_clk;
+ if (!host->mmc_host_ops.uhs2_set_reg)
+ host->mmc_host_ops.uhs2_set_reg = sdhci_uhs2_set_reg;
return 0;
}
--
2.28.0
This is a sdhci version of mmc's request operation.
It covers both UHS-I and UHS-II.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 529 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 93 +++---
drivers/mmc/host/sdhci.h | 21 ++
3 files changed, 610 insertions(+), 33 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index d8afb99a9918..36e52553977a 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/module.h>
+#include <linux/mmc/mmc.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -394,6 +395,27 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
return 0;
}
+/* TODO: move them to a header */
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+static inline void sdhci_led_activate(struct sdhci_host *host)
+{
+}
+
+static inline void sdhci_led_deactivate(struct sdhci_host *host)
+{
+}
+#else
+static inline void sdhci_led_activate(struct sdhci_host *host)
+{
+ __sdhci_led_activate(host);
+}
+
+static inline void sdhci_led_deactivate(struct sdhci_host *host)
+{
+ __sdhci_led_deactivate(host);
+}
+#endif
+
/*****************************************************************************\
* *
* MMC callbacks *
@@ -523,6 +545,512 @@ static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
return err;
}
+static bool sdhci_uhs2_send_command(struct sdhci_host *host,
+ struct mmc_command *cmd);
+static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
+ struct mmc_command *cmd,
+ unsigned long flags);
+
+void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ unsigned long flags;
+ bool present;
+
+ /* FIXME: check more flags? */
+ if (!host->mmc->flags & MMC_UHS2_SUPPORT) {
+ sdhci_request(mmc, mrq);
+ return;
+ }
+
+ /* Firstly check card presence */
+ present = mmc->ops->get_cd(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ sdhci_led_activate(host);
+
+ if (sdhci_present_error(host, mrq->cmd, present))
+ goto out_finish;
+
+ cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
+
+ if (!sdhci_uhs2_send_command_retry(host, cmd, flags))
+ goto out_finish;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return;
+
+out_finish:
+ sdhci_finish_mrq(host, mrq);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_request);
+
+int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!host->mmc->flags & MMC_UHS2_SUPPORT)
+ return sdhci_request_atomic(mmc, mrq);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (sdhci_present_error(host, mrq->cmd, true)) {
+ sdhci_finish_mrq(host, mrq);
+ goto out_finish;
+ }
+
+ cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
+
+ /*
+ * The HSQ may send a command in interrupt context without polling
+ * the busy signaling, which means we should return BUSY if controller
+ * has not released inhibit bits to allow HSQ trying to send request
+ * again in non-atomic context. So we should not finish this request
+ * here.
+ */
+ if (!sdhci_uhs2_send_command(host, cmd))
+ ret = -EBUSY;
+ else
+ sdhci_led_activate(host);
+
+out_finish:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
+
+/*****************************************************************************\
+ * *
+ * Core functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_uhs2_prepare_data(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ struct mmc_data *data = cmd->data;
+
+ sdhci_initialize_data(host, data);
+
+ sdhci_prepare_dma(host, data);
+
+ sdhci_writew(host, data->blksz, SDHCI_UHS2_BLOCK_SIZE);
+ sdhci_writew(host, data->blocks, SDHCI_UHS2_BLOCK_COUNT);
+}
+
+#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
+static void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ if (!sdhci_external_dma_setup(host, cmd)) {
+ __sdhci_external_dma_prepare_data(host, cmd);
+ } else {
+ sdhci_external_dma_release(host);
+ pr_err("%s: Cannot use external DMA, switch to the DMA/PIO which standard SDHCI provides.\n",
+ mmc_hostname(host->mmc));
+ sdhci_uhs2_prepare_data(host, cmd);
+ }
+}
+#else
+static inline void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ /* This should never happen */
+ WARN_ON_ONCE(1);
+}
+
+static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+}
+#endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
+
+static void sdhci_uhs2_finish_data(struct sdhci_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ __sdhci_finish_data_common(host);
+
+ /*
+ * FIXME: Is this condition needed?
+ if (host->mmc->flags & MMC_UHS2_INITIALIZED)
+ */
+ __sdhci_finish_mrq(host, data->mrq);
+}
+
+static void sdhci_uhs2_set_transfer_mode(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ u16 mode;
+ struct mmc_data *data = cmd->data;
+ u16 arg;
+
+ if (!data) {
+ /* clear Auto CMD settings for no data CMDs */
+ arg = cmd->uhs2_cmd->arg;
+ if ((((arg & 0xF) << 8) | ((arg >> 8) & 0xFF)) ==
+ UHS2_DEV_CMD_TRANS_ABORT) {
+ mode = 0;
+ } else {
+ mode = sdhci_readw(host, SDHCI_UHS2_TRANS_MODE);
+ if (cmd->opcode == MMC_STOP_TRANSMISSION ||
+ cmd->opcode == MMC_ERASE)
+ mode |= SDHCI_UHS2_TRNS_WAIT_EBSY;
+ else
+ /* send status mode */
+ if (cmd->opcode == MMC_SEND_STATUS)
+ mode = 0;
+ }
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG))
+ DBG("UHS2 no data trans mode is 0x%x.\n", mode);
+
+ sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
+ return;
+ }
+
+ WARN_ON(!host->data);
+
+ mode = SDHCI_UHS2_TRNS_BLK_CNT_EN | SDHCI_UHS2_TRNS_WAIT_EBSY;
+ if (data->flags & MMC_DATA_WRITE)
+ mode |= SDHCI_UHS2_TRNS_DATA_TRNS_WRT;
+
+ if (data->blocks == 1 &&
+ data->blksz != 512 &&
+ cmd->opcode != MMC_READ_SINGLE_BLOCK &&
+ cmd->opcode != MMC_WRITE_BLOCK) {
+ mode &= ~SDHCI_UHS2_TRNS_BLK_CNT_EN;
+ mode |= SDHCI_UHS2_TRNS_BLK_BYTE_MODE;
+ }
+
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ mode |= SDHCI_UHS2_TRNS_DMA;
+
+ if ((host->mmc->flags & MMC_UHS2_2L_HD) && !cmd->uhs2_tmode0_flag)
+ mode |= SDHCI_UHS2_TRNS_2L_HD;
+
+ sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG))
+ DBG("UHS2 trans mode is 0x%x.\n", mode);
+}
+
+static void __sdhci_uhs2_send_command(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ int i, j;
+ int cmd_reg;
+
+ if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
+ if (!cmd->uhs2_cmd) {
+ pr_err("%s: fatal error, no uhs2_cmd!\n",
+ mmc_hostname(host->mmc));
+ return;
+ }
+ }
+
+ i = 0;
+ sdhci_writel(host,
+ ((u32)cmd->uhs2_cmd->arg << 16) |
+ (u32)cmd->uhs2_cmd->header,
+ SDHCI_UHS2_CMD_PACKET + i);
+ i += 4;
+
+ /*
+ * Per spec, playload (config) should be MSB before sending out.
+ * But we don't need convert here because had set payload as
+ * MSB when preparing config read/write commands.
+ */
+ for (j = 0; j < cmd->uhs2_cmd->payload_len / sizeof(u32); j++) {
+ sdhci_writel(host, *(cmd->uhs2_cmd->payload + j),
+ SDHCI_UHS2_CMD_PACKET + i);
+ i += 4;
+ }
+
+ for ( ; i < SDHCI_UHS2_CMD_PACK_MAX_LEN; i += 4)
+ sdhci_writel(host, 0, SDHCI_UHS2_CMD_PACKET + i);
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
+ DBG("UHS2 CMD packet_len = %d.\n", cmd->uhs2_cmd->packet_len);
+ for (i = 0; i < cmd->uhs2_cmd->packet_len; i++)
+ DBG("UHS2 CMD_PACKET[%d] = 0x%x.\n", i,
+ sdhci_readb(host, SDHCI_UHS2_CMD_PACKET + i));
+ }
+
+ cmd_reg = cmd->uhs2_cmd->packet_len <<
+ SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT;
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
+ cmd_reg |= SDHCI_UHS2_COMMAND_DATA;
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd_reg |= SDHCI_UHS2_COMMAND_CMD12;
+
+ /* UHS2 Native ABORT */
+ if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
+ ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
+ ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) == UHS2_DEV_CMD_TRANS_ABORT))
+ cmd_reg |= SDHCI_UHS2_COMMAND_TRNS_ABORT;
+
+ /* UHS2 Native DORMANT */
+ if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
+ ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
+ ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) ==
+ UHS2_DEV_CMD_GO_DORMANT_STATE))
+ cmd_reg |= SDHCI_UHS2_COMMAND_DORMANT;
+
+ DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
+
+ sdhci_writew(host, cmd_reg, SDHCI_UHS2_COMMAND);
+}
+
+static bool sdhci_uhs2_send_command(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ int flags;
+ u32 mask;
+ unsigned long timeout;
+
+ /* FIXME: Is this check necessary? */
+ if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
+ return sdhci_send_command(host, cmd);
+
+ WARN_ON(host->cmd);
+
+ /* Initially, a command has no error */
+ cmd->error = 0;
+
+ if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
+ cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd->flags |= MMC_RSP_BUSY;
+
+ mask = SDHCI_CMD_INHIBIT;
+ if (sdhci_data_line_cmd(cmd))
+ mask |= SDHCI_DATA_INHIBIT;
+
+ /* We shouldn't wait for data inihibit for stop commands, even
+ though they might use busy signaling */
+ if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
+ mask &= ~SDHCI_DATA_INHIBIT;
+
+ if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
+ return false;
+
+ host->cmd = cmd;
+ host->data_timeout = 0;
+ if (sdhci_data_line_cmd(cmd)) {
+ WARN_ON(host->data_cmd);
+ host->data_cmd = cmd;
+ __sdhci_uhs2_set_timeout(host);
+ }
+
+ if (cmd->data) {
+ if (host->use_external_dma)
+ sdhci_uhs2_external_dma_prepare_data(host, cmd);
+ else
+ sdhci_uhs2_prepare_data(host, cmd);
+ }
+
+ sdhci_uhs2_set_transfer_mode(host, cmd);
+
+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+ WARN_ONCE(1, "Unsupported response type!\n");
+ /*
+ * This does not happen in practice because 136-bit response
+ * commands never have busy waiting, so rather than complicate
+ * the error path, just remove busy waiting and continue.
+ */
+ cmd->flags &= ~MMC_RSP_BUSY;
+ }
+
+ if (!(cmd->flags & MMC_RSP_PRESENT))
+ flags = SDHCI_CMD_RESP_NONE;
+ else if (cmd->flags & MMC_RSP_136)
+ flags = SDHCI_CMD_RESP_LONG;
+ else if (cmd->flags & MMC_RSP_BUSY)
+ flags = SDHCI_CMD_RESP_SHORT_BUSY;
+ else
+ flags = SDHCI_CMD_RESP_SHORT;
+
+ if (cmd->flags & MMC_RSP_CRC)
+ flags |= SDHCI_CMD_CRC;
+ if (cmd->flags & MMC_RSP_OPCODE)
+ flags |= SDHCI_CMD_INDEX;
+
+ /* CMD19 is special in that the Data Present Select should be set */
+ if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
+ flags |= SDHCI_CMD_DATA;
+
+ timeout = jiffies;
+ if (host->data_timeout)
+ timeout += nsecs_to_jiffies(host->data_timeout);
+ else if (!cmd->data && cmd->busy_timeout > 9000)
+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
+ else
+ timeout += 10 * HZ;
+ sdhci_mod_timer(host, cmd->mrq, timeout);
+
+ if (host->use_external_dma)
+ sdhci_external_dma_pre_transfer(host, cmd);
+
+ __sdhci_uhs2_send_command(host, cmd);
+
+ return true;
+}
+
+static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
+ struct mmc_command *cmd,
+ unsigned long flags)
+ __releases(host->lock)
+ __acquires(host->lock)
+{
+ struct mmc_command *deferred_cmd = host->deferred_cmd;
+ int timeout = 10; /* Approx. 10 ms */
+ bool present;
+
+ while (!sdhci_uhs2_send_command(host, cmd)) {
+ if (!timeout--) {
+ pr_err("%s: Controller never released inhibit bit(s).\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ cmd->error = -EIO;
+ return false;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ usleep_range(1000, 1250);
+
+ present = host->mmc->ops->get_cd(host->mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* A deferred command might disappear, handle that */
+ if (cmd == deferred_cmd && cmd != host->deferred_cmd)
+ return true;
+
+ if (sdhci_present_error(host, cmd, present))
+ return false;
+ }
+
+ if (cmd == host->deferred_cmd)
+ host->deferred_cmd = NULL;
+
+ return true;
+}
+
+static void __sdhci_uhs2_finish_command(struct sdhci_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ u8 resp;
+ u8 ecode;
+ bool bReadA0 = 0;
+ int i;
+
+ if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
+ resp = sdhci_readb(host, SDHCI_UHS2_RESPONSE + 2);
+ if (resp & UHS2_RES_NACK_MASK) {
+ ecode = (resp >> UHS2_RES_ECODE_POS) &
+ UHS2_RES_ECODE_MASK;
+ pr_err("%s: NACK is got, ECODE=0x%x.\n",
+ mmc_hostname(host->mmc), ecode);
+ }
+ bReadA0 = 1;
+ }
+
+ if (cmd->uhs2_resp &&
+ cmd->uhs2_resp_len && cmd->uhs2_resp_len <= 20) {
+ /* Get whole response of some native CCMD, like
+ * DEVICE_INIT, ENUMERATE.
+ */
+ for (i = 0; i < cmd->uhs2_resp_len; i++)
+ cmd->uhs2_resp[i] =
+ sdhci_readb(host, SDHCI_UHS2_RESPONSE + i);
+ } else {
+ /* Get SD CMD response and Payload for some read
+ * CCMD, like INQUIRY_CFG.
+ */
+ /* Per spec (p136), payload field is divided into
+ * a unit of DWORD and transmission order within
+ * a DWORD is big endian.
+ */
+ if (!bReadA0)
+ sdhci_readl(host, SDHCI_UHS2_RESPONSE);
+ for (i = 4; i < 20; i += 4) {
+ cmd->resp[i / 4 - 1] =
+ (sdhci_readb(host,
+ SDHCI_UHS2_RESPONSE + i) << 24) |
+ (sdhci_readb(host,
+ SDHCI_UHS2_RESPONSE + i + 1)
+ << 16) |
+ (sdhci_readb(host,
+ SDHCI_UHS2_RESPONSE + i + 2)
+ << 8) |
+ sdhci_readb(host, SDHCI_UHS2_RESPONSE + i + 3);
+ }
+ }
+}
+
+static void sdhci_uhs2_finish_command(struct sdhci_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+
+ /* FIXME: Is this check necessary? */
+ if (!(host->mmc->flags & MMC_UHS2_SUPPORT)) {
+ sdhci_finish_command(host);
+ return;
+ }
+
+ __sdhci_uhs2_finish_command(host);
+
+ host->cmd = NULL;
+
+ if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd)
+ mmc_command_done(host->mmc, cmd->mrq);
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * The busy signal uses DAT0 so this is similar to waiting
+ * for data to complete.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (cmd->flags & MMC_RSP_BUSY) {
+ if (cmd->data) {
+ DBG("Cannot wait for busy signal when also doing a data transfer");
+ } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
+ cmd == host->data_cmd) {
+ /* Command complete before busy is ended */
+ return;
+ }
+ }
+
+ /* Finished CMD23, now send actual command. */
+ if (cmd == cmd->mrq->sbc) {
+ if (!sdhci_uhs2_send_command(host, cmd->mrq->cmd)) {
+ WARN_ON(host->deferred_cmd);
+ host->deferred_cmd = cmd->mrq->cmd;
+ }
+ } else {
+
+ /* Processed actual command. */
+ if (host->data && host->data_early)
+ sdhci_uhs2_finish_data(host);
+
+ if (!cmd->data)
+ __sdhci_finish_mrq(host, cmd->mrq);
+ }
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -691,6 +1219,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_uhs2_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
+ host->mmc_host_ops.request = sdhci_uhs2_request;
if (!host->mmc_host_ops.uhs2_detect_init)
host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index becb228330af..a9f5449bddcc 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -48,8 +48,6 @@
static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
-static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
-
void sdhci_dumpregs(struct sdhci_host *host)
{
SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
@@ -148,10 +146,13 @@ void sdhci_enable_v4_mode(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_enable_v4_mode);
-static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
+bool sdhci_data_line_cmd(struct mmc_command *cmd)
{
return cmd->data || cmd->flags & MMC_RSP_BUSY;
}
+EXPORT_SYMBOL_GPL(sdhci_data_line_cmd);
+
+/* TODO: move this as an inline function to a header */
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
@@ -363,7 +364,7 @@ static void sdhci_reinit(struct sdhci_host *host)
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
}
-static void __sdhci_led_activate(struct sdhci_host *host)
+void __sdhci_led_activate(struct sdhci_host *host)
{
u8 ctrl;
@@ -374,8 +375,9 @@ static void __sdhci_led_activate(struct sdhci_host *host)
ctrl |= SDHCI_CTRL_LED;
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
+EXPORT_SYMBOL_GPL(__sdhci_led_activate);
-static void __sdhci_led_deactivate(struct sdhci_host *host)
+void __sdhci_led_deactivate(struct sdhci_host *host)
{
u8 ctrl;
@@ -386,6 +388,7 @@ static void __sdhci_led_deactivate(struct sdhci_host *host)
ctrl &= ~SDHCI_CTRL_LED;
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
+EXPORT_SYMBOL_GPL(__sdhci_led_deactivate);
#if IS_REACHABLE(CONFIG_LEDS_CLASS)
static void sdhci_led_control(struct led_classdev *led,
@@ -464,14 +467,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
#endif
-static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
- unsigned long timeout)
+void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
+ unsigned long timeout)
{
if (sdhci_data_line_cmd(mrq->cmd))
mod_timer(&host->data_timer, timeout);
else
mod_timer(&host->timer, timeout);
}
+EXPORT_SYMBOL_GPL(sdhci_mod_timer);
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
@@ -1043,8 +1047,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
__sdhci_set_timeout(host, cmd);
}
-static void sdhci_initialize_data(struct sdhci_host *host,
- struct mmc_data *data)
+void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data)
{
WARN_ON(host->data);
@@ -1057,6 +1060,7 @@ static void sdhci_initialize_data(struct sdhci_host *host,
host->data_early = 0;
host->data->bytes_xfered = 0;
}
+EXPORT_SYMBOL_GPL(sdhci_initialize_data);
static inline void sdhci_set_block_info(struct sdhci_host *host,
struct mmc_data *data)
@@ -1079,12 +1083,8 @@ static inline void sdhci_set_block_info(struct sdhci_host *host,
}
}
-static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data)
{
- struct mmc_data *data = cmd->data;
-
- sdhci_initialize_data(host, data);
-
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
struct scatterlist *sg;
unsigned int length_mask, offset_mask;
@@ -1169,6 +1169,16 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
}
sdhci_set_transfer_irqs(host);
+}
+EXPORT_SYMBOL_GPL(sdhci_prepare_dma);
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ struct mmc_data *data = cmd->data;
+
+ sdhci_initialize_data(host, data);
+
+ sdhci_prepare_dma(host, data);
sdhci_set_block_info(host, data);
}
@@ -1211,8 +1221,7 @@ static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
}
-static int sdhci_external_dma_setup(struct sdhci_host *host,
- struct mmc_command *cmd)
+int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
{
int ret, i;
enum dma_transfer_direction dir;
@@ -1264,8 +1273,9 @@ static int sdhci_external_dma_setup(struct sdhci_host *host,
return ret;
}
+EXPORT_SYMBOL_GPL(sdhci_external_dma_setup);
-static void sdhci_external_dma_release(struct sdhci_host *host)
+void sdhci_external_dma_release(struct sdhci_host *host)
{
if (host->tx_chan) {
dma_release_channel(host->tx_chan);
@@ -1279,9 +1289,10 @@ static void sdhci_external_dma_release(struct sdhci_host *host)
sdhci_switch_external_dma(host, false);
}
+EXPORT_SYMBOL_GPL(sdhci_external_dma_release);
-static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
- struct mmc_command *cmd)
+void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
+ struct mmc_command *cmd)
{
struct mmc_data *data = cmd->data;
@@ -1292,6 +1303,7 @@ static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
sdhci_set_block_info(host, data);
}
+EXPORT_SYMBOL(__sdhci_external_dma_prepare_data);
static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
struct mmc_command *cmd)
@@ -1306,8 +1318,8 @@ static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
}
}
-static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
- struct mmc_command *cmd)
+void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
+ struct mmc_command *cmd)
{
struct dma_chan *chan;
@@ -1318,6 +1330,7 @@ static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
if (chan)
dma_async_issue_pending(chan);
}
+EXPORT_SYMBOL_GPL(sdhci_external_dma_pre_transfer);
#else
@@ -1369,11 +1382,11 @@ static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
}
-static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
- struct mmc_request *mrq)
+bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq)
{
return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
}
+EXPORT_SYMBOL_GPL(sdhci_manual_cmd23);
static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
struct mmc_command *cmd,
@@ -1483,7 +1496,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
WARN_ON(i >= SDHCI_MAX_MRQS);
}
-static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
+void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
if (host->cmd && host->cmd->mrq == mrq)
host->cmd = NULL;
@@ -1507,15 +1520,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
if (!sdhci_has_requests(host))
sdhci_led_deactivate(host);
}
+EXPORT_SYMBOL_GPL(__sdhci_finish_mrq);
-static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
+void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
__sdhci_finish_mrq(host, mrq);
queue_work(host->complete_wq, &host->complete_work);
}
+EXPORT_SYMBOL_GPL(sdhci_finish_mrq);
-static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
+void __sdhci_finish_data_common(struct sdhci_host *host)
{
struct mmc_command *data_cmd = host->data_cmd;
struct mmc_data *data = host->data;
@@ -1548,6 +1563,14 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
data->bytes_xfered = 0;
else
data->bytes_xfered = data->blksz * data->blocks;
+}
+EXPORT_SYMBOL_GPL(__sdhci_finish_data_common);
+
+static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
+{
+ struct mmc_data *data = host->data;
+
+ __sdhci_finish_data_common(host);
/*
* Need to send CMD12 if -
@@ -1586,12 +1609,13 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
}
}
-static void sdhci_finish_data(struct sdhci_host *host)
+void sdhci_finish_data(struct sdhci_host *host)
{
__sdhci_finish_data(host, false);
}
+EXPORT_SYMBOL_GPL(sdhci_finish_data);
-static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
int flags;
u32 mask;
@@ -1633,8 +1657,6 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_prepare_data(host, cmd);
}
- sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
-
sdhci_set_transfer_mode(host, cmd);
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
@@ -1678,13 +1700,16 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (host->use_external_dma)
sdhci_external_dma_pre_transfer(host, cmd);
+ sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
+
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
return true;
}
+EXPORT_SYMBOL_GPL(sdhci_send_command);
-static bool sdhci_present_error(struct sdhci_host *host,
- struct mmc_command *cmd, bool present)
+bool sdhci_present_error(struct sdhci_host *host,
+ struct mmc_command *cmd, bool present)
{
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
cmd->error = -ENOMEDIUM;
@@ -1693,6 +1718,7 @@ static bool sdhci_present_error(struct sdhci_host *host,
return false;
}
+EXPORT_SYMBOL_GPL(sdhci_present_error);
static bool sdhci_send_command_retry(struct sdhci_host *host,
struct mmc_command *cmd,
@@ -1755,7 +1781,7 @@ static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
}
}
-static void sdhci_finish_command(struct sdhci_host *host)
+void sdhci_finish_command(struct sdhci_host *host)
{
struct mmc_command *cmd = host->cmd;
@@ -1808,6 +1834,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
__sdhci_finish_mrq(host, cmd->mrq);
}
}
+EXPORT_SYMBOL_GPL(sdhci_finish_command);
static u16 sdhci_get_preset_value(struct sdhci_host *host)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index e84ebddb20d8..6eeb74741da3 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -831,8 +831,29 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
__sdhci_read_caps(host, NULL, NULL, NULL);
}
+bool sdhci_data_line_cmd(struct mmc_command *cmd);
void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
+void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
+ unsigned long timeout);
+void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
+void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data);
+#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
+int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd);
+void sdhci_external_dma_release(struct sdhci_host *host);
+void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
+ struct mmc_command *cmd);
+void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
+ struct mmc_command *cmd);
+#endif
+bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
+void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
+void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
+void __sdhci_finish_data_common(struct sdhci_host *host);
+bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
+void sdhci_finish_command(struct sdhci_host *host);
+bool sdhci_present_error(struct sdhci_host *host,
+ struct mmc_command *cmd, bool present);
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock);
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
--
2.28.0
Sdhci_uhs2_reset() does a UHS-II specific reset operation.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 49 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 1 +
drivers/mmc/host/sdhci.c | 3 ++-
drivers/mmc/host/sdhci.h | 1 +
4 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 08905ed081fb..e2b9743fe17d 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -10,6 +10,7 @@
* Author: AKASHI Takahiro <[email protected]>
*/
+#include <linux/delay.h>
#include <linux/module.h>
#include "sdhci.h"
@@ -49,6 +50,54 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
+/*****************************************************************************\
+ * *
+ * Low level functions *
+ * *
+\*****************************************************************************/
+
+/**
+ * sdhci_uhs2_reset - invoke SW reset
+ * @host: SDHCI host
+ * @mask: Control mask
+ *
+ * Invoke SW reset, depending on a bit in @mask and wait for completion.
+ */
+void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
+{
+ unsigned long timeout;
+
+ if (!(host->mmc->caps & MMC_CAP_UHS2))
+ return;
+
+ sdhci_writew(host, mask, SDHCI_UHS2_SW_RESET);
+
+ if (mask & SDHCI_UHS2_SW_RESET_FULL) {
+ host->clock = 0;
+ /* Reset-all turns off SD Bus Power */
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_off(host);
+ }
+
+ /* Wait max 100 ms */
+ timeout = 10000;
+
+ /* hw clears the bit when it's done */
+ while (sdhci_readw(host, SDHCI_UHS2_SW_RESET) & mask) {
+ if (timeout == 0) {
+ pr_err("%s: %s: Reset 0x%x never completed.\n",
+ __func__, mmc_hostname(host->mmc), (int)mask);
+ pr_err("%s: clean reset bit\n",
+ mmc_hostname(host->mmc));
+ sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
+ return;
+ }
+ timeout--;
+ udelay(10);
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index b9529d32b58d..7bb7a0d67109 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -210,5 +210,6 @@
struct sdhci_host;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
+void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index d4a57e8c9bb8..af336bdb4305 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -195,13 +195,14 @@ static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
pm_runtime_get_noresume(host->mmc->parent);
}
-static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
+void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
{
if (!host->bus_on)
return;
host->bus_on = false;
pm_runtime_put_noidle(host->mmc->parent);
}
+EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_off);
void sdhci_reset(struct sdhci_host *host, u8 mask)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index d9d7a76cedc1..b9932423db08 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -831,6 +831,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
__sdhci_read_caps(host, NULL, NULL, NULL);
}
+void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock);
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
--
2.28.0
This is a UHS-II version of sdhci's add_host/remove_host operation.
Any sdhci drivers which are capable of handling UHS-II cards must
call those functions instead of the corresponding sdhci's.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 198 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
drivers/mmc/host/sdhci.c | 24 +++--
drivers/mmc/host/sdhci.h | 10 ++
4 files changed, 226 insertions(+), 8 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index d50134e912f3..5d3362ea138f 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -15,6 +15,7 @@
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/mmc/mmc.h>
+#include <linux/regulator/consumer.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -406,6 +407,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
{
}
#else
+static inline int sdhci_led_register(struct sdhci_host *host)
+{
+ return 0;
+}
+
+static inline void sdhci_led_unregister(struct sdhci_host *host)
+{
+}
+
static inline void sdhci_led_activate(struct sdhci_host *host)
{
__sdhci_led_activate(host);
@@ -1298,6 +1308,194 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*****************************************************************************\
+ *
+ * Device allocation/registration *
+ * *
+\*****************************************************************************/
+
+static int __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
+{
+ struct mmc_host *mmc;
+ u32 max_current_caps2;
+
+ if (host->version < SDHCI_SPEC_400)
+ return 0;
+
+ mmc = host->mmc;
+
+ /* Support UHS2 */
+ if (caps1 & SDHCI_SUPPORT_UHS2)
+ mmc->caps |= MMC_CAP_UHS2;
+
+ max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
+
+ if ((caps1 & SDHCI_SUPPORT_VDD2_180) &&
+ !max_current_caps2 &&
+ !IS_ERR(mmc->supply.vmmc2)) {
+ /* UHS2 - VDD2 */
+ int curr = regulator_get_current_limit(mmc->supply.vmmc2);
+
+ if (curr > 0) {
+ /* convert to SDHCI_MAX_CURRENT format */
+ curr = curr / 1000; /* convert to mA */
+ curr = curr / SDHCI_MAX_CURRENT_MULTIPLIER;
+ curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
+ max_current_caps2 = curr;
+ }
+ }
+
+ if (caps1 & SDHCI_SUPPORT_VDD2_180) {
+ mmc->ocr_avail_uhs2 |= MMC_VDD2_165_195;
+ /*
+ * UHS2 doesn't require this. Only UHS-I bus needs to set
+ * max current.
+ */
+ mmc->max_current_180_vdd2 = (max_current_caps2 &
+ SDHCI_MAX_CURRENT_VDD2_180_MASK) *
+ SDHCI_MAX_CURRENT_MULTIPLIER;
+ } else {
+ mmc->caps &= ~MMC_CAP_UHS2;
+ }
+
+ return 0;
+}
+
+static int sdhci_uhs2_host_ops_init(struct sdhci_host *host);
+
+static int __sdhci_uhs2_add_host(struct sdhci_host *host)
+{
+ unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
+ struct mmc_host *mmc = host->mmc;
+ int ret;
+
+ if ((mmc->caps2 & MMC_CAP2_CQE) &&
+ (host->quirks & SDHCI_QUIRK_BROKEN_CQE)) {
+ mmc->caps2 &= ~MMC_CAP2_CQE;
+ mmc->cqe_ops = NULL;
+ }
+
+ /* overwrite ops */
+ if (mmc->caps & MMC_CAP_UHS2)
+ sdhci_uhs2_host_ops_init(host);
+
+ host->complete_wq = alloc_workqueue("sdhci", flags, 0);
+ if (!host->complete_wq)
+ return -ENOMEM;
+
+ INIT_WORK(&host->complete_work, sdhci_uhs2_complete_work);
+
+ timer_setup(&host->timer, sdhci_timeout_timer, 0);
+ timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
+
+ init_waitqueue_head(&host->buf_ready_int);
+
+ sdhci_init(host, 0);
+
+ ret = request_threaded_irq(host->irq, sdhci_irq,
+ sdhci_uhs2_thread_irq,
+ IRQF_SHARED, mmc_hostname(mmc), host);
+ if (ret) {
+ pr_err("%s: Failed to request IRQ %d: %d\n",
+ mmc_hostname(mmc), host->irq, ret);
+ goto unwq;
+ }
+
+ ret = sdhci_led_register(host);
+ if (ret) {
+ pr_err("%s: Failed to register LED device: %d\n",
+ mmc_hostname(mmc), ret);
+ goto unirq;
+ }
+
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto unled;
+
+ pr_info("%s: SDHCI controller on %s [%s] using %s\n",
+ mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
+ host->use_external_dma ? "External DMA" :
+ (host->flags & SDHCI_USE_ADMA) ?
+ (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
+ (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
+
+ sdhci_enable_card_detection(host);
+
+ return 0;
+
+unled:
+ sdhci_led_unregister(host);
+unirq:
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
+ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+ free_irq(host->irq, host);
+unwq:
+ destroy_workqueue(host->complete_wq);
+
+ return ret;
+}
+
+static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+ if (!(host->mmc) || !(host->mmc->flags & MMC_UHS2_SUPPORT))
+ return;
+
+ if (!dead)
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
+
+ sdhci_writel(host, 0, SDHCI_UHS2_ERR_INT_STATUS_EN);
+ sdhci_writel(host, 0, SDHCI_UHS2_ERR_INT_SIG_EN);
+ host->mmc->flags &= ~MMC_UHS2_INITIALIZED;
+}
+
+int sdhci_uhs2_add_host(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ int ret;
+
+ ret = sdhci_setup_host(host);
+ if (ret)
+ return ret;
+
+ if (host->version >= SDHCI_SPEC_400) {
+ ret = __sdhci_uhs2_add_host_v4(host, host->caps1);
+ if (ret)
+ goto cleanup;
+ }
+
+ if ((mmc->caps & MMC_CAP_UHS2) && !host->v4_mode)
+ /* host doesn't want to enable UHS2 support */
+ /* FIXME: Do we have to do some cleanup here? */
+ mmc->caps &= ~MMC_CAP_UHS2;
+
+ ret = __sdhci_uhs2_add_host(host);
+ if (ret)
+ goto cleanup2;
+
+ return 0;
+
+cleanup2:
+ /*
+ * TODO: Is this a right cleanup?
+ */
+ if (host->version >= SDHCI_SPEC_400)
+ __sdhci_uhs2_remove_host(host, 0);
+cleanup:
+ sdhci_cleanup_host(host);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_add_host);
+
+void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+ __sdhci_uhs2_remove_host(host, dead);
+
+ sdhci_uhs2_remove_host(host, dead);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_remove_host);
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index b74af641d00e..34e140f21284 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -218,5 +218,7 @@ void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
+int sdhci_uhs2_add_host(struct sdhci_host *host);
+void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index e2dfc7767bcf..d37c3edb1ed0 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -176,10 +176,11 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
}
-static void sdhci_enable_card_detection(struct sdhci_host *host)
+void sdhci_enable_card_detection(struct sdhci_host *host)
{
sdhci_set_card_detection(host, true);
}
+EXPORT_SYMBOL_GPL(sdhci_enable_card_detection);
static void sdhci_disable_card_detection(struct sdhci_host *host)
{
@@ -237,7 +238,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
}
EXPORT_SYMBOL_GPL(sdhci_reset);
-static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
+void sdhci_do_reset(struct sdhci_host *host, u8 mask)
{
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
struct mmc_host *mmc = host->mmc;
@@ -258,6 +259,7 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
host->preset_enabled = false;
}
}
+EXPORT_SYMBOL_GPL(sdhci_do_reset);
static void sdhci_set_default_irqs(struct sdhci_host *host)
{
@@ -321,7 +323,7 @@ static void sdhci_config_dma(struct sdhci_host *host)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
-static void sdhci_init(struct sdhci_host *host, int soft)
+void sdhci_init(struct sdhci_host *host, int soft)
{
struct mmc_host *mmc = host->mmc;
unsigned long flags;
@@ -346,6 +348,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
mmc->ops->set_ios(mmc, &mmc->ios);
}
}
+EXPORT_SYMBOL_GPL(sdhci_init);
static void sdhci_reinit(struct sdhci_host *host)
{
@@ -410,7 +413,7 @@ static void sdhci_led_control(struct led_classdev *led,
spin_unlock_irqrestore(&host->lock, flags);
}
-static int sdhci_led_register(struct sdhci_host *host)
+int sdhci_led_register(struct sdhci_host *host)
{
struct mmc_host *mmc = host->mmc;
@@ -427,14 +430,16 @@ static int sdhci_led_register(struct sdhci_host *host)
return led_classdev_register(mmc_dev(mmc), &host->led);
}
+EXPORT_SYMBOL_GPL(sdhci_led_register);
-static void sdhci_led_unregister(struct sdhci_host *host)
+void sdhci_led_unregister(struct sdhci_host *host)
{
if (host->quirks & SDHCI_QUIRK_NO_LED)
return;
led_classdev_unregister(&host->led);
}
+EXPORT_SYMBOL_GPL(sdhci_led_unregister);
static inline void sdhci_led_activate(struct sdhci_host *host)
{
@@ -3175,7 +3180,7 @@ static void sdhci_complete_work(struct work_struct *work)
;
}
-static void sdhci_timeout_timer(struct timer_list *t)
+void sdhci_timeout_timer(struct timer_list *t)
{
struct sdhci_host *host;
unsigned long flags;
@@ -3195,8 +3200,9 @@ static void sdhci_timeout_timer(struct timer_list *t)
spin_unlock_irqrestore(&host->lock, flags);
}
+EXPORT_SYMBOL_GPL(sdhci_timeout_timer);
-static void sdhci_timeout_data_timer(struct timer_list *t)
+void sdhci_timeout_data_timer(struct timer_list *t)
{
struct sdhci_host *host;
unsigned long flags;
@@ -3226,6 +3232,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
spin_unlock_irqrestore(&host->lock, flags);
}
+EXPORT_SYMBOL_GPL(sdhci_timeout_data_timer);
/*****************************************************************************\
* *
@@ -3469,7 +3476,7 @@ static inline bool sdhci_defer_done(struct sdhci_host *host,
data->host_cookie == COOKIE_MAPPED);
}
-static irqreturn_t sdhci_irq(int irq, void *dev_id)
+irqreturn_t sdhci_irq(int irq, void *dev_id)
{
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS] = {0};
irqreturn_t result = IRQ_NONE;
@@ -3609,6 +3616,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
return result;
}
+EXPORT_SYMBOL_GPL(sdhci_irq);
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 74572b54ec47..b1d856664b58 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -832,8 +832,15 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
}
bool sdhci_data_line_cmd(struct mmc_command *cmd);
+void sdhci_enable_card_detection(struct sdhci_host *host);
void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
+void sdhci_do_reset(struct sdhci_host *host, u8 mask);
+void sdhci_init(struct sdhci_host *host, int soft);
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+int sdhci_led_register(struct sdhci_host *host);
+void sdhci_led_unregister(struct sdhci_host *host);
+#endif
void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
unsigned long timeout);
void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
@@ -884,6 +891,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
bool sdhci_request_done(struct sdhci_host *host);
+void sdhci_timeout_timer(struct timer_list *t);
+void sdhci_timeout_data_timer(struct timer_list *t);
+irqreturn_t sdhci_irq(int irq, void *dev_id);
void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
--
2.28.0
This "post" hook for mmc_attach_sd() will be required to enable UHS-II
support, at least, on GL9755.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/sd.c | 6 ++++++
include/linux/mmc/host.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 54e155ff44ff..9e0a5abe9be1 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1411,6 +1411,12 @@ int mmc_attach_sd(struct mmc_host *host)
goto remove_card;
mmc_claim_host(host);
+
+ /* TODO: Is this the right place? */
+ if ((host->flags & MMC_UHS2_INITIALIZED) &&
+ host->ops->uhs2_post_attach_sd)
+ host->ops->uhs2_post_attach_sd(host);
+
return 0;
remove_card:
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 19a265190ad3..03131e4504ad 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -181,6 +181,7 @@ struct mmc_host_ops {
int (*uhs2_set_reg)(struct mmc_host *host, enum uhs2_act act);
void (*uhs2_disable_clk)(struct mmc_host *host);
void (*uhs2_enable_clk)(struct mmc_host *host);
+ void (*uhs2_post_attach_sd)(struct mmc_host *host);
};
struct mmc_cqe_ops {
--
2.28.0
Changes are:
* Disable GL9755 overcurrent interrupt when power on/off on UHS-II.
* Enable the internal clock when do reset on UHS-II mode.
* Set ZC to 0x0 for Sandisk cards and set ZC to 0xB for others.
* Increase timeout value before detecting UHS-II interface.
* Add vendor settings fro UHS-II mode.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/Kconfig | 1 +
drivers/mmc/host/sdhci-pci-gli.c | 318 ++++++++++++++++++++++++++++++-
2 files changed, 318 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5ca9ac03db40..cef033dd02ce 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -102,6 +102,7 @@ config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
select MMC_CQHCI
+ select MMC_SDHCI_UHS2
select IOSF_MBI if X86
select MMC_SDHCI_IO_ACCESSORS
help
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 9887485a4134..dad465800446 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -14,6 +14,7 @@
#include <linux/delay.h>
#include "sdhci.h"
#include "sdhci-pci.h"
+#include "sdhci-uhs2.h"
#include "cqhci.h"
/* Genesys Logic extra registers */
@@ -72,6 +73,42 @@
#define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0)
#define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1
+#define PCI_GLI_9755_WT 0x800
+#define PCI_GLI_9755_WT_EN BIT(0)
+#define GLI_9755_WT_EN_ON 0x1
+#define GLI_9755_WT_EN_OFF 0x0
+
+#define PCI_GLI_9755_PLLSSC 0x68
+#define PCI_GLI_9755_PLLSSC_RTL BIT(24)
+#define GLI_9755_PLLSSC_RTL_VALUE 0x1
+#define PCI_GLI_9755_PLLSSC_TRANS_PASS BIT(27)
+#define GLI_9755_PLLSSC_TRANS_PASS_VALUE 0x1
+#define PCI_GLI_9755_PLLSSC_RECV GENMASK(29, 28)
+#define GLI_9755_PLLSSC_RECV_VALUE 0x3
+#define PCI_GLI_9755_PLLSSC_TRAN GENMASK(31, 30)
+#define GLI_9755_PLLSSC_TRAN_VALUE 0x3
+
+#define PCI_GLI_9755_UHS2_PLL 0x6C
+#define PCI_GLI_9755_UHS2_PLL_SSC GENMASK(9, 8)
+#define GLI_9755_UHS2_PLL_SSC_VALUE 0x0
+#define PCI_GLI_9755_UHS2_PLL_DELAY BIT(18)
+#define GLI_9755_UHS2_PLL_DELAY_VALUE 0x1
+#define PCI_GLI_9755_UHS2_PLL_PDRST BIT(27)
+#define GLI_9755_UHS2_PLL_PDRST_VALUE 0x1
+
+#define PCI_GLI_9755_UHS2_SERDES 0x70
+#define PCI_GLI_9755_UHS2_SERDES_INTR GENMASK(2, 0)
+#define GLI_9755_UHS2_SERDES_INTR_VALUE 0x3
+#define PCI_GLI_9755_UHS2_SERDES_ZC1 BIT(3)
+#define GLI_9755_UHS2_SERDES_ZC1_VALUE 0x0
+#define PCI_GLI_9755_UHS2_SERDES_ZC2 GENMASK(7, 4)
+#define GLI_9755_UHS2_SERDES_ZC2_DEFAULT 0xB
+#define GLI_9755_UHS2_SERDES_ZC2_SANDISK 0x0
+#define PCI_GLI_9755_UHS2_SERDES_TRAN GENMASK(27, 24)
+#define GLI_9755_UHS2_SERDES_TRAN_VALUE 0xC
+#define PCI_GLI_9755_UHS2_SERDES_RECV GENMASK(31, 28)
+#define GLI_9755_UHS2_SERDES_RECV_VALUE 0xF
+
#define SDHCI_GLI_9763E_CTRL_HS400 0x7
#define SDHCI_GLI_9763E_HS400_ES_REG 0x52C
@@ -519,6 +556,276 @@ static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_enable_clk(host, clk);
}
+static void gl9755_vendor_init(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u32 serdes;
+ u32 pllssc;
+ u32 uhs2_pll;
+
+ gl9755_wt_on(pdev);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_UHS2_SERDES, &serdes);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_TRAN;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_TRAN,
+ GLI_9755_UHS2_SERDES_TRAN_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_RECV;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_RECV,
+ GLI_9755_UHS2_SERDES_RECV_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_INTR;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_INTR,
+ GLI_9755_UHS2_SERDES_INTR_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_ZC1;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC1,
+ GLI_9755_UHS2_SERDES_ZC1_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_ZC2;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC2,
+ GLI_9755_UHS2_SERDES_ZC2_DEFAULT);
+ pci_write_config_dword(pdev, PCI_GLI_9755_UHS2_SERDES, serdes);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_UHS2_PLL, &uhs2_pll);
+ uhs2_pll &= ~PCI_GLI_9755_UHS2_PLL_SSC;
+ uhs2_pll |= FIELD_PREP(PCI_GLI_9755_UHS2_PLL_SSC,
+ GLI_9755_UHS2_PLL_SSC_VALUE);
+ uhs2_pll &= ~PCI_GLI_9755_UHS2_PLL_DELAY;
+ uhs2_pll |= FIELD_PREP(PCI_GLI_9755_UHS2_PLL_DELAY,
+ GLI_9755_UHS2_PLL_DELAY_VALUE);
+ uhs2_pll &= ~PCI_GLI_9755_UHS2_PLL_PDRST;
+ uhs2_pll |= FIELD_PREP(PCI_GLI_9755_UHS2_PLL_PDRST,
+ GLI_9755_UHS2_PLL_PDRST_VALUE);
+ pci_write_config_dword(pdev, PCI_GLI_9755_UHS2_PLL, uhs2_pll);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_PLLSSC, &pllssc);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_RTL;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_RTL,
+ GLI_9755_PLLSSC_RTL_VALUE);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_TRANS_PASS;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_TRANS_PASS,
+ GLI_9755_PLLSSC_TRANS_PASS_VALUE);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_RECV;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_RECV,
+ GLI_9755_PLLSSC_RECV_VALUE);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_TRAN;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_TRAN,
+ GLI_9755_PLLSSC_TRAN_VALUE);
+ pci_write_config_dword(pdev, PCI_GLI_9755_PLLSSC, pllssc);
+
+ gl9755_wt_off(pdev);
+}
+
+static void gl9755_pre_detect_init(struct sdhci_host *host)
+{
+ /* GL9755 need more time on UHS2 detect flow */
+ sdhci_writeb(host, 0xA7, SDHCI_UHS2_TIMER_CTRL);
+}
+
+static void gl9755_post_attach_sd(struct sdhci_host *host)
+{
+ struct pci_dev *pdev;
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ u32 serdes;
+
+ slot = sdhci_priv(host);
+ chip = slot->chip;
+ pdev = chip->pdev;
+
+ gl9755_wt_on(pdev);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_UHS2_SERDES, &serdes);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_ZC1;
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_ZC2;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC1,
+ GLI_9755_UHS2_SERDES_ZC1_VALUE);
+
+ /* the manfid of sandisk card is 0x3 */
+ if (host->mmc->card->cid.manfid == 0x3)
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC2,
+ GLI_9755_UHS2_SERDES_ZC2_SANDISK);
+ else
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC2,
+ GLI_9755_UHS2_SERDES_ZC2_DEFAULT);
+
+ pci_write_config_dword(pdev, PCI_GLI_9755_UHS2_SERDES, serdes);
+
+ gl9755_wt_off(pdev);
+}
+
+static void gl9755_overcurrent_event_enable(struct sdhci_host *host,
+ bool enable)
+{
+ u32 mask;
+
+ mask = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
+ if (enable)
+ mask |= SDHCI_INT_BUS_POWER;
+ else
+ mask &= ~SDHCI_INT_BUS_POWER;
+
+ sdhci_writel(host, mask, SDHCI_SIGNAL_ENABLE);
+
+ mask = sdhci_readl(host, SDHCI_INT_ENABLE);
+ if (enable)
+ mask |= SDHCI_INT_BUS_POWER;
+ else
+ mask &= ~SDHCI_INT_BUS_POWER;
+
+ sdhci_writel(host, mask, SDHCI_INT_ENABLE);
+}
+
+static void gl9755_set_power(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd)
+{
+ u8 pwr = 0;
+
+ if (mode != MMC_POWER_OFF) {
+ switch (1 << vdd) {
+ case MMC_VDD_165_195:
+ /*
+ * Without a regulator, SDHCI does not support 2.0v
+ * so we only get here if the driver deliberately
+ * added the 2.0v range to ocr_avail. Map it to 1.8v
+ * for the purpose of turning on the power.
+ */
+ case MMC_VDD_20_21:
+ pwr = SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr = SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr = SDHCI_POWER_330;
+ break;
+ default:
+ WARN(1, "%s: Invalid vdd %#x\n",
+ mmc_hostname(host->mmc), vdd);
+ break;
+ }
+
+ pwr |= SDHCI_VDD2_POWER_180;
+ }
+
+ if (host->pwr == pwr)
+ return;
+
+ host->pwr = pwr;
+
+ if (pwr == 0) {
+ gl9755_overcurrent_event_enable(host, false);
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ } else {
+ gl9755_overcurrent_event_enable(host, false);
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ pwr |= (SDHCI_POWER_ON | SDHCI_VDD2_POWER_ON);
+
+ sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
+ /* wait stable */
+ mdelay(5);
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ /* wait stable */
+ mdelay(5);
+ gl9755_overcurrent_event_enable(host, true);
+ }
+}
+
+static bool sdhci_wait_clock_stable(struct sdhci_host *host)
+{
+ u16 clk = 0;
+ ktime_t timeout;
+
+ /* Wait max 20 ms */
+ timeout = ktime_add_ms(ktime_get(), 20);
+ while (1) {
+ bool timedout = ktime_after(ktime_get(), timeout);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ if (clk & SDHCI_CLOCK_INT_STABLE)
+ break;
+ if (timedout) {
+ pr_err("%s: Internal clock never stabilised.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return false;
+ }
+ udelay(10);
+ }
+ return true;
+}
+
+static void gl9755_uhs2_reset_sd_tran(struct sdhci_host *host)
+{
+ /* do this on UHS2 mode */
+ if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ sdhci_uhs2_clear_set_irqs(host,
+ SDHCI_INT_ALL_MASK,
+ SDHCI_UHS2_ERR_INT_STATUS_MASK);
+ }
+}
+
+static void sdhci_gl9755_reset(struct sdhci_host *host, u8 mask)
+{
+ ktime_t timeout;
+ u16 ctrl2;
+ u16 clk_ctrl;
+
+ /* need internal clock */
+ if (mask & SDHCI_RESET_ALL) {
+ ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ clk_ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+ if ((ctrl2 & SDHCI_CTRL_V4_MODE) &&
+ (ctrl2 & SDHCI_CTRL_UHS2_INTERFACE_EN)) {
+ sdhci_writew(host,
+ SDHCI_CLOCK_INT_EN,
+ SDHCI_CLOCK_CONTROL);
+ } else {
+ sdhci_writew(host,
+ SDHCI_CLOCK_INT_EN,
+ SDHCI_CLOCK_CONTROL);
+ sdhci_wait_clock_stable(host);
+ sdhci_writew(host,
+ SDHCI_CTRL_V4_MODE,
+ SDHCI_HOST_CONTROL2);
+ }
+ }
+
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+ /* reset sd-tran on UHS2 mode if need to reset cmd/data */
+ if ((mask & SDHCI_RESET_CMD) | (mask & SDHCI_RESET_DATA))
+ gl9755_uhs2_reset_sd_tran(host);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = ktime_add_ms(ktime_get(), 100);
+
+ /* hw clears the bit when it's done */
+ while (1) {
+ bool timedout = ktime_after(ktime_get(), timeout);
+
+ if (!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask))
+ break;
+ if (timedout) {
+ pr_err("%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ /* manual clear */
+ sdhci_writeb(host, 0, SDHCI_SOFTWARE_RESET);
+ return;
+ }
+ udelay(10);
+ }
+}
+
static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
{
struct sdhci_host *host = slot->host;
@@ -537,6 +844,7 @@ static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
gli_pcie_enable_msi(slot);
slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
sdhci_enable_v4_mode(host);
+ gl9755_vendor_init(host);
return 0;
}
@@ -800,17 +1108,25 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
static const struct sdhci_ops sdhci_gl9755_ops = {
.set_clock = sdhci_gl9755_set_clock,
+ .set_power = gl9755_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_reset,
+ .reset = sdhci_gl9755_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.voltage_switch = sdhci_gli_voltage_switch,
+ .dump_uhs2_regs = sdhci_uhs2_dump_regs,
+ .set_timeout = sdhci_uhs2_set_timeout,
+ .irq = sdhci_uhs2_irq,
+ .uhs2_pre_detect_init = gl9755_pre_detect_init,
+ .uhs2_post_attach_sd = gl9755_post_attach_sd,
};
const struct sdhci_pci_fixes sdhci_gl9755 = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50,
.probe_slot = gli_probe_slot_gl9755,
+ .add_host = sdhci_pci_uhs2_add_host,
+ .remove_host = sdhci_pci_uhs2_remove_host,
.ops = &sdhci_gl9755_ops,
#ifdef CONFIG_PM_SLEEP
.resume = sdhci_pci_gli_resume,
--
2.28.0
This is a UHS-II version of sdhci's set_timeout() operation.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 85 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 1 +
2 files changed, 86 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 1eca89359351..d9e98c097bfe 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -178,6 +178,91 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
+static u8 sdhci_calc_timeout_uhs2(struct sdhci_host *host, u8 *cmd_res,
+ u8 *dead_lock)
+{
+ u8 count;
+ unsigned int cmd_res_timeout, dead_lock_timeout, current_timeout;
+
+ /*
+ * If the host controller provides us with an incorrect timeout
+ * value, just skip the check and use 0xE. The hardware may take
+ * longer to time out, but that's much better than having a too-short
+ * timeout value.
+ */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) {
+ *cmd_res = 0xE;
+ *dead_lock = 0xE;
+ return 0xE;
+ }
+
+ /* timeout in us */
+ cmd_res_timeout = 5 * 1000;
+ dead_lock_timeout = 1 * 1000 * 1000;
+
+ /*
+ * Figure out needed cycles.
+ * We do this in steps in order to fit inside a 32 bit int.
+ * The first step is the minimum timeout, which will have a
+ * minimum resolution of 6 bits:
+ * (1) 2^13*1000 > 2^22,
+ * (2) host->timeout_clk < 2^16
+ * =>
+ * (1) / (2) > 2^6
+ */
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < cmd_res_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ if (count >= 0xF) {
+ DBG("%s: Too large timeout 0x%x requested for CMD_RES!\n",
+ mmc_hostname(host->mmc), count);
+ count = 0xE;
+ }
+ *cmd_res = count;
+
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < dead_lock_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ if (count >= 0xF) {
+ DBG("%s: Too large timeout 0x%x requested for DEADLOCK!\n",
+ mmc_hostname(host->mmc), count);
+ count = 0xE;
+ }
+ *dead_lock = count;
+
+ return count;
+}
+
+static void __sdhci_uhs2_set_timeout(struct sdhci_host *host)
+{
+ u8 cmd_res, dead_lock;
+
+ sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
+ cmd_res |= dead_lock << SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT;
+ sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
+}
+
+void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ __sdhci_set_timeout(host, cmd);
+
+ if (host->mmc->flags & MMC_UHS2_SUPPORT)
+ __sdhci_uhs2_set_timeout(host);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
+
/*****************************************************************************\
* *
* MMC callbacks *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 3c19d8e44c36..efe70577bc74 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -213,5 +213,6 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
+void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
#endif /* __SDHCI_UHS2_H */
--
2.28.0
In UHS-II mode, MMC_APP_CMD command need not to be sent.
Instead, APP_CMD bit in a packet should be set.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/sd_ops.c | 9 +++++++++
drivers/mmc/core/uhs2.c | 4 ++++
2 files changed, 13 insertions(+)
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index f58bb50872f6..9dc296356928 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -26,6 +26,15 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
if (WARN_ON(card && card->host != host))
return -EINVAL;
+ /* UHS2 packet has APP bit so only set APP_CMD flag here.
+ * Will set the APP bit when assembling UHS2 packet.
+ */
+ if (host->flags & MMC_UHS2_SUPPORT &&
+ host->flags & MMC_UHS2_INITIALIZED) {
+ host->flags |= MMC_UHS2_APP_CMD;
+ return 0;
+ }
+
cmd.opcode = MMC_APP_CMD;
if (card) {
diff --git a/drivers/mmc/core/uhs2.c b/drivers/mmc/core/uhs2.c
index 1f6d0e0cf355..c0d61e7d9e4b 100644
--- a/drivers/mmc/core/uhs2.c
+++ b/drivers/mmc/core/uhs2.c
@@ -835,6 +835,10 @@ int uhs2_prepare_sd_cmd(struct mmc_host *host, struct mmc_request *mrq)
header |= UHS2_PACKET_TYPE_CCMD;
arg = cmd->opcode << UHS2_SD_CMD_INDEX_POS;
+ if (host->flags & MMC_UHS2_APP_CMD) {
+ arg |= UHS2_SD_CMD_APP;
+ host->flags &= ~MMC_UHS2_APP_CMD;
+ }
uhs2_cmd = cmd->uhs2_cmd;
payload = uhs2_cmd->payload;
--
2.28.0
From: Ben Chuang <[email protected]>
The flow of "interface selection and initialization" was a bit modified
for UHS-II card. This commit follows the sequence defined in SD
specification (Part 1).
See section 7.2.3 in "UHS-II Simplified Addendum."
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/core/Makefile | 2 +-
drivers/mmc/core/bus.c | 5 +-
drivers/mmc/core/core.c | 33 +-
drivers/mmc/core/sd.c | 26 ++
drivers/mmc/core/uhs2.c | 809 ++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/uhs2.h | 20 +
6 files changed, 891 insertions(+), 4 deletions(-)
create mode 100644 drivers/mmc/core/uhs2.c
create mode 100644 drivers/mmc/core/uhs2.h
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 95ffe008ebdf..e2a90dc98afc 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -8,7 +8,7 @@ mmc_core-y := core.o bus.o host.o \
mmc.o mmc_ops.o sd.o sd_ops.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o \
- slot-gpio.o regulator.o
+ slot-gpio.o regulator.o uhs2.o
mmc_core-$(CONFIG_OF) += pwrseq.o
obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index c2e70b757dd1..02dcdce54547 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -362,8 +362,9 @@ int mmc_add_card(struct mmc_card *card)
} else {
pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
- mmc_card_uhs(card) ? "ultra high speed " :
- (mmc_card_hs(card) ? "high speed " : ""),
+ mmc_card_uhs2(card) ? "ultra high speed 2 " :
+ (mmc_card_uhs(card) ? "ultra high speed 1 " :
+ (mmc_card_hs(card) ? "high speed " : "")),
mmc_card_hs400(card) ? "HS400 " :
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_hs400es(card) ? "Enhanced strobe " : "",
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 4e12bd98fc08..a08cb1c0f6d2 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -31,6 +31,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/slot-gpio.h>
+#include <linux/mmc/uhs2.h>
#define CREATE_TRACE_POINTS
#include <trace/events/mmc.h>
@@ -41,6 +42,7 @@
#include "host.h"
#include "sdio_bus.h"
#include "pwrseq.h"
+#include "uhs2.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -51,6 +53,7 @@
#define SD_DISCARD_TIMEOUT_MS (250)
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
+static const unsigned int uhs2_freqs[] = { 52000000, 26000000 };
/*
* Enabling software CRCs on the data blocks can be a significant (30%)
@@ -2176,9 +2179,10 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
if (!mmc_attach_sdio(host))
return 0;
- if (!(host->caps2 & MMC_CAP2_NO_SD))
+ if (!(host->caps2 & MMC_CAP2_NO_SD)) {
if (!mmc_attach_sd(host))
return 0;
+ }
if (!(host->caps2 & MMC_CAP2_NO_MMC))
if (!mmc_attach_mmc(host))
@@ -2311,6 +2315,33 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+ if (host->caps & MMC_CAP_UHS2) {
+ /*
+ * Start to try UHS-II initialization from 52MHz to 26MHz
+ * (RCLK range) per spec.
+ */
+ for (i = 0; i < ARRAY_SIZE(uhs2_freqs); i++) {
+ unsigned int freq = uhs2_freqs[i];
+ int err;
+
+ err = mmc_uhs2_rescan_try_freq(host,
+ max(freq, host->f_min));
+ if (!err) {
+ mmc_release_host(host);
+ goto out;
+ }
+
+ if (err == UHS2_PHY_INIT_ERR)
+ /* UHS2 IF detect or Lane Sync error.
+ * Try legacy interface.
+ */
+ break;
+
+ if (freq <= host->f_min)
+ break;
+ }
+ }
+
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
unsigned int freq = freqs[i];
if (freq > host->f_max) {
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 6f054c449d46..54e155ff44ff 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -931,6 +931,20 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
err = mmc_read_switch(card);
if (err)
return err;
+ if (host->flags & MMC_UHS2_INITIALIZED) {
+ u8 status[64];
+ /*
+ * send CMD6 to set Maximum Power Consumption
+ * to get better performance
+ */
+ err = mmc_sd_switch(card, 0, 3,
+ SD_SET_CURRENT_LIMIT_1000, status);
+ if (!err) {
+ err = mmc_sd_switch(card, 1, 3,
+ SD_SET_CURRENT_LIMIT_1000,
+ status);
+ }
+ }
}
/*
@@ -1073,6 +1087,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto free_card;
+ /* For UHS2, skip the UHS-I initialization. */
+ if ((host->flags & MMC_UHS2_SUPPORT) &&
+ (host->flags & MMC_UHS2_INITIALIZED))
+ goto done;
+
/*
* If the card has not been power cycled, it may still be using 1.8V
* signaling. Detect that situation and try to initialize a UHS-I (1.8V)
@@ -1364,6 +1383,13 @@ int mmc_attach_sd(struct mmc_host *host)
rocr = mmc_select_voltage(host, ocr);
+ /*
+ * Some cards have zero value of rocr in UHS-II mode. Assign host's ocr
+ * value to rocr.
+ */
+ if ((host->flags & MMC_UHS2_INITIALIZED) && !rocr)
+ rocr = host->ocr_avail;
+
/*
* Can we support the voltage(s) of the card(s)?
*/
diff --git a/drivers/mmc/core/uhs2.c b/drivers/mmc/core/uhs2.c
new file mode 100644
index 000000000000..acb46b5b57ef
--- /dev/null
+++ b/drivers/mmc/core/uhs2.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/drivers/mmc/core/uhs2.c - UHS-II driver
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ * Copyright (C) 2020 Genesys Logic, Inc.
+ * Authors: Ben Chuang <[email protected]>
+ * Copyright (C) 2020 Linaro Limited
+ * Author: AKASHI Takahiro <[email protected]>
+ */
+
+#include <asm/byteorder.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/mmc/uhs2.h>
+
+#include "uhs2.h"
+#include "mmc_ops.h"
+#include "sd_ops.h"
+#include "core.h"
+
+#define DBG(f, x...) \
+ pr_warn("[%s()]: " f, __func__, ## x)
+
+/**
+ * uhs2_cmd_assemble - assemble and build up uhs2 command
+ * @cmd: MMC command
+ * @uhs2_cmd: UHS2 command
+ * @header: Value of packet header
+ * @arg: Argument of packet
+ * @payload: Payload of packet
+ * @plen: Payload length
+ * @resp: Buffer for response
+ * @resp_len: Response buffer length
+ *
+ * resp is inputted outside which should be a variable created by caller
+ * so caller should handle it. For SD command, there is no uhs2_resp and
+ * response should be stored in resp of mmc_command.
+ */
+static void uhs2_cmd_assemble(struct mmc_command *cmd,
+ struct uhs2_command *uhs2_cmd,
+ u16 header, u16 arg,
+ u32 *payload, u8 plen, u8 *resp, u8 resp_len)
+{
+ uhs2_cmd->header = header;
+ uhs2_cmd->arg = arg;
+ uhs2_cmd->payload = payload;
+ uhs2_cmd->payload_len = plen * sizeof(u32);
+ uhs2_cmd->packet_len = uhs2_cmd->payload_len + 4;
+
+ cmd->uhs2_cmd = uhs2_cmd;
+ cmd->uhs2_resp = resp;
+ cmd->uhs2_resp_len = resp_len;
+
+ pr_debug("%s: uhs2_cmd: header=0x%x arg=0x%x\n",
+ __func__, uhs2_cmd->header, uhs2_cmd->arg);
+ pr_debug("%s: payload_len=%d packet_len=%d resp_len=%d\n",
+ __func__, uhs2_cmd->payload_len, uhs2_cmd->packet_len,
+ cmd->uhs2_resp_len);
+}
+
+static int uhs2_dev_init(struct mmc_host *host)
+{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u32 cnt;
+ u32 dap, gap, gap1;
+ u16 header = 0, arg = 0;
+ u32 payload[1];
+ u8 plen = 1;
+ u8 gd = 0, cf = 1;
+ u8 resp[6] = {0};
+ u8 resp_len = 6;
+ int err;
+
+ dap = host->uhs2_caps.dap;
+ gap = host->uhs2_caps.gap;
+
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
+ arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_4B |
+ (UHS2_DEV_CMD_DEVICE_INIT >> 8);
+
+ /* need this for some cards */
+ cmd.busy_timeout = 1000;
+
+ for (cnt = 0; cnt < 30; cnt++) {
+ payload[0] = ((dap & 0xF) << 12) |
+ (cf << 11) |
+ ((gd & 0xF) << 4) |
+ (gap & 0xF);
+
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,
+ payload, plen, resp, resp_len);
+
+ DBG("Begin DEVICE_INIT, header=0x%x, arg=0x%x, payload=0x%x.\n",
+ header, arg, payload[0]);
+
+ DBG("Sending DEVICE_INIT. Count = %d\n", cnt);
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
+ int i;
+
+ pr_warn("%s: DEVICE_INIT response is: ",
+ mmc_hostname(host));
+ for (i = 0; i < resp_len; i++)
+ pr_warn("0x%x ", resp[i]);
+ pr_warn("\n");
+ }
+
+ if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
+ pr_err("%s: DEVICE_INIT response is wrong!\n",
+ mmc_hostname(host));
+ return -EIO;
+ }
+
+ if (resp[5] & 0x8) {
+ DBG("CF is set, device is initialized!\n");
+ host->group_desc = gd;
+ break;
+ }
+ gap1 = resp[4] & 0x0F;
+ if (gap == gap1)
+ gd++;
+ }
+ if (cnt == 30) {
+ pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
+ mmc_hostname(host));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int uhs2_enum(struct mmc_host *host)
+{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header = 0, arg = 0;
+ u32 payload[1];
+ u8 plen = 1;
+ u8 id_f = 0xF, id_l = 0x0;
+ u8 resp[8] = {0};
+ u8 resp_len = 8;
+ int err;
+
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
+ arg = ((UHS2_DEV_CMD_ENUMERATE & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_4B |
+ (UHS2_DEV_CMD_ENUMERATE >> 8);
+
+ payload[0] = (id_f << 4) | id_l;
+
+ DBG("Begin ENUMERATE, header=0x%x, arg=0x%x, payload=0x%x.\n",
+ header, arg, payload[0]);
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen,
+ resp, resp_len);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
+ int i;
+
+ pr_warn("%s: ENUMERATE response is: ", mmc_hostname(host));
+ for (i = 0; i < resp_len; i++)
+ pr_warn("0x%x ", resp[i]);
+ pr_warn("\n");
+ }
+
+ if (resp[3] != (UHS2_DEV_CMD_ENUMERATE & 0xFF)) {
+ pr_err("%s: ENUMERATE response is wrong!\n",
+ mmc_hostname(host));
+ return -EIO;
+ }
+
+ id_f = (resp[4] >> 4) & 0xF;
+ id_l = resp[4] & 0xF;
+ DBG("id_f = %d, id_l = %d.\n", id_f, id_l);
+ DBG("Enumerate Cmd Completed. No. of Devices connected = %d\n",
+ id_l - id_f + 1);
+ host->uhs2_dev_prop.node_id = id_f;
+
+ return 0;
+}
+
+static int uhs2_config_read(struct mmc_host *host)
+{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header = 0, arg = 0;
+ u32 cap;
+ int err;
+
+ DBG("INQUIRY_CFG: read Generic Caps.\n");
+ header = UHS2_NATIVE_PACKET |
+ UHS2_PACKET_TYPE_CCMD |
+ host->uhs2_dev_prop.node_id;
+ arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_READ |
+ UHS2_NATIVE_CMD_PLEN_4B |
+ (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
+
+ DBG("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n",
+ header, arg);
+ /* There is no payload because per spec, there should be
+ * no payload field for read CCMD.
+ * Plen is set in arg. Per spec, plen for read CCMD
+ * represents the len of read data which is assigned in payload
+ * of following RES (p136).
+ */
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
+ int i;
+
+ pr_warn("%s: INQUIRY_CFG generic response is: ",
+ mmc_hostname(host));
+ for (i = 0; i < 2; i++)
+ pr_warn("0x%x ", cmd.resp[i]);
+ pr_warn("\n");
+ }
+
+ cap = cmd.resp[0];
+ DBG("Device Generic Caps (0-31) is: 0x%x.\n", cap);
+ host->uhs2_dev_prop.n_lanes = (cap >> UHS2_DEV_CONFIG_N_LANES_POS) &
+ UHS2_DEV_CONFIG_N_LANES_MASK;
+ host->uhs2_dev_prop.dadr_len = (cap >> UHS2_DEV_CONFIG_DADR_POS) &
+ UHS2_DEV_CONFIG_DADR_MASK;
+ host->uhs2_dev_prop.app_type = (cap >> UHS2_DEV_CONFIG_APP_POS) &
+ UHS2_DEV_CONFIG_APP_MASK;
+
+ DBG("INQUIRY_CFG: read PHY Caps.\n");
+ arg = ((UHS2_DEV_CONFIG_PHY_CAPS & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_READ |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_PHY_CAPS >> 8);
+
+ DBG("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n",
+ header, arg);
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
+ int i;
+
+ pr_warn("%s: INQUIRY_CFG PHY response is: ",
+ mmc_hostname(host));
+ for (i = 0; i < 2; i++)
+ pr_warn("0x%x ", cmd.resp[i]);
+ pr_warn("\n");
+ }
+
+ cap = cmd.resp[0];
+ DBG("Device PHY Caps (0-31) is: 0x%x.\n", cap);
+ host->uhs2_dev_prop.phy_minor_rev = cap &
+ UHS2_DEV_CONFIG_PHY_MINOR_MASK;
+ host->uhs2_dev_prop.phy_major_rev = (cap >>
+ UHS2_DEV_CONFIG_PHY_MAJOR_POS) &
+ UHS2_DEV_CONFIG_PHY_MAJOR_MASK;
+ host->uhs2_dev_prop.can_hibernate = (cap >>
+ UHS2_DEV_CONFIG_CAN_HIBER_POS) &
+ UHS2_DEV_CONFIG_CAN_HIBER_MASK;
+
+ cap = cmd.resp[1];
+ DBG("Device PHY Caps (32-63) is: 0x%x.\n", cap);
+ host->uhs2_dev_prop.n_lss_sync = cap & UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
+ host->uhs2_dev_prop.n_lss_dir = (cap >>
+ UHS2_DEV_CONFIG_N_LSS_DIR_POS) &
+ UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+ if (host->uhs2_dev_prop.n_lss_sync == 0)
+ host->uhs2_dev_prop.n_lss_sync = 16 << 2;
+ else
+ host->uhs2_dev_prop.n_lss_sync <<= 2;
+
+ if (host->uhs2_dev_prop.n_lss_dir == 0)
+ host->uhs2_dev_prop.n_lss_dir = 16 << 3;
+ else
+ host->uhs2_dev_prop.n_lss_dir <<= 3;
+
+ DBG("INQUIRY_CFG: read LINK-TRAN Caps.\n");
+ arg = ((UHS2_DEV_CONFIG_LINK_TRAN_CAPS & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_READ |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_LINK_TRAN_CAPS >> 8);
+
+ DBG("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n",
+ header, arg);
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
+ int i;
+
+ pr_warn("%s: INQUIRY_CFG Link-Tran response is: ",
+ mmc_hostname(host));
+ for (i = 0; i < 2; i++)
+ pr_warn("0x%x ", cmd.resp[i]);
+ pr_warn("\n");
+ }
+
+ cap = cmd.resp[0];
+ DBG("Device LINK-TRAN Caps (0-31) is: 0x%x.\n", cap);
+ host->uhs2_dev_prop.link_minor_rev = cap &
+ UHS2_DEV_CONFIG_LT_MINOR_MASK;
+ host->uhs2_dev_prop.link_major_rev = (cap >>
+ UHS2_DEV_CONFIG_LT_MAJOR_POS) &
+ UHS2_DEV_CONFIG_LT_MAJOR_MASK;
+ host->uhs2_dev_prop.n_fcu = (cap >> UHS2_DEV_CONFIG_N_FCU_POS) &
+ UHS2_DEV_CONFIG_N_FCU_MASK;
+ host->uhs2_dev_prop.dev_type = (cap >> UHS2_DEV_CONFIG_DEV_TYPE_POS) &
+ UHS2_DEV_CONFIG_DEV_TYPE_MASK;
+ host->uhs2_dev_prop.maxblk_len = (cap >>
+ UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) &
+ UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK;
+
+ cap = cmd.resp[1];
+ DBG("Device LINK-TRAN Caps (32-63) is: 0x%x.\n", cap);
+ host->uhs2_dev_prop.n_data_gap = cap & UHS2_DEV_CONFIG_N_DATA_GAP_MASK;
+ if (host->uhs2_dev_prop.n_fcu == 0)
+ host->uhs2_dev_prop.n_fcu = 256;
+
+ return 0;
+}
+
+static int uhs2_config_write(struct mmc_host *host)
+{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header = 0, arg = 0;
+ u32 nTry;
+ u32 payload[2];
+ u8 nMinDataGap;
+ u8 plen;
+ int err;
+ u8 resp[5] = {0};
+ u8 resp_len = 5;
+ /*
+ * must long enough for RECV_SW_DIR == 2;
+ * need lss_dir at least 4 for GL9755 device
+ * max compitable gLssDir = 0
+ */
+ u32 gLssDir = 0;
+
+ DBG("SET_COMMON_CFG: write Generic Settings.\n");
+ header = UHS2_NATIVE_PACKET |
+ UHS2_PACKET_TYPE_CCMD | host->uhs2_dev_prop.node_id;
+ arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+ if (host->uhs2_dev_prop.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
+ host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
+ /* Support HD */
+ DBG("Both Host and device support 2L-HD.\n");
+ host->flags |= MMC_UHS2_2L_HD;
+ host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+ host->uhs2_dev_prop.n_lanes_set =
+ UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+ nMinDataGap = 1;
+ } else {
+ /* Only support 2L-FD so far */
+ host->flags &= ~MMC_UHS2_2L_HD;
+ host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+ host->uhs2_dev_prop.n_lanes_set =
+ UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+ nMinDataGap = 3;
+ }
+
+ plen = 2;
+ payload[0] = host->uhs2_dev_prop.n_lanes_set <<
+ UHS2_DEV_CONFIG_N_LANES_POS;
+ payload[1] = 0;
+ payload[0] = cpu_to_be32(payload[0]);
+ payload[1] = cpu_to_be32(payload[1]);
+
+ DBG("Begin SET_COMMON_CFG, header=0x%x, arg=0x%x\n", header, arg);
+ DBG("UHS2 write Generic Settings %08x %08x\n",
+ payload[0], payload[1]);
+ DBG("flags=%08x dev_prop.n_lanes_set=%x host_caps.n_lanes_set=%x\n",
+ host->flags,
+ host->uhs2_dev_prop.n_lanes_set,
+ host->uhs2_caps.n_lanes_set);
+
+ /*
+ * There is no payload because per spec, there should be
+ * no payload field for read CCMD.
+ * Plen is set in arg. Per spec, plen for read CCMD
+ * represents the len of read data which is assigned in payload
+ * of following RES (p136).
+ */
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ DBG("SET_COMMON_CFG: PHY Settings.\n");
+ arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_PHY_SET >> 8);
+
+ for (nTry = 0; nTry < 2; nTry++) {
+ plen = 2;
+
+ if (host->uhs2_caps.speed_range ==
+ UHS2_DEV_CONFIG_PHY_SET_SPEED_B) {
+ host->flags |= MMC_UHS2_SPEED_B;
+ host->uhs2_dev_prop.speed_range_set =
+ UHS2_DEV_CONFIG_PHY_SET_SPEED_B;
+ DBG("set dev_prop.speed_range_set to SPEED_B\n");
+ } else {
+ host->uhs2_dev_prop.speed_range_set =
+ UHS2_DEV_CONFIG_PHY_SET_SPEED_A;
+ host->flags &= ~MMC_UHS2_SPEED_B;
+ DBG("set dev_prop.speed_range_set to SPEED_A\n");
+ }
+
+ payload[0] = host->uhs2_dev_prop.speed_range_set <<
+ UHS2_DEV_CONFIG_PHY_SET_SPEED_POS;
+
+ host->uhs2_dev_prop.n_lss_sync_set =
+ (min(host->uhs2_dev_prop.n_lss_sync,
+ host->uhs2_caps.n_lss_sync) >> 2) &
+ UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
+ host->uhs2_caps.n_lss_sync_set =
+ host->uhs2_dev_prop.n_lss_sync_set;
+
+ if (nTry) {
+ host->uhs2_dev_prop.n_lss_dir_set =
+ (max(host->uhs2_dev_prop.n_lss_dir,
+ host->uhs2_caps.n_lss_dir) >> 3) &
+ UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+ host->uhs2_caps.n_lss_dir_set =
+ host->uhs2_dev_prop.n_lss_dir_set;
+ payload[1] = (host->uhs2_dev_prop.n_lss_dir_set <<
+ UHS2_DEV_CONFIG_N_LSS_DIR_POS) |
+ host->uhs2_dev_prop.n_lss_sync_set;
+ } else {
+ host->uhs2_caps.n_lss_dir_set =
+ (host->uhs2_dev_prop.n_lss_dir >> 3) &
+ UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+ host->uhs2_dev_prop.n_lss_dir_set =
+ ((host->uhs2_caps.n_lss_dir >> 3) + 1) &
+ UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+ }
+
+ if (!gLssDir) {
+ host->uhs2_dev_prop.n_lss_dir_set = 0;
+ } else {
+ host->uhs2_dev_prop.n_lss_dir_set =
+ max((u8)gLssDir,
+ host->uhs2_dev_prop.n_lss_dir_set);
+ }
+
+ payload[1] = (host->uhs2_dev_prop.n_lss_dir_set <<
+ UHS2_DEV_CONFIG_N_LSS_DIR_POS) |
+ host->uhs2_dev_prop.n_lss_sync_set;
+ payload[0] = cpu_to_be32(payload[0]);
+ payload[1] = cpu_to_be32(payload[1]);
+
+ DBG("UHS2 SET PHY Settings %08x %08x\n",
+ payload[0], payload[1]);
+ DBG("host->flags=%08x dev_prop.speed_range_set=%x\n",
+ host->flags,
+ host->uhs2_dev_prop.speed_range_set);
+ DBG("dev_prop.n_lss_sync_set=%x host_caps.n_lss_sync_set=%x\n",
+ host->uhs2_dev_prop.n_lss_sync_set,
+ host->uhs2_caps.n_lss_sync_set);
+ DBG("dev_prop.n_lss_dir_set=%x host_caps.n_lss_dir_set=%x\n",
+ host->uhs2_dev_prop.n_lss_dir_set,
+ host->uhs2_caps.n_lss_dir_set);
+
+ DBG("Begin SET_COMMON_CFG header=0x%x arg=0x%x\n",
+ header, arg);
+ DBG("\t\tpayload[0]=0x%x payload[1]=0x%x\n",
+ payload[0], payload[1]);
+
+ resp_len = 4;
+ memset(resp, 0, sizeof(resp));
+
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,
+ payload, plen, resp, resp_len);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ if (!(resp[2] & 0x80))
+ break;
+
+ DBG("%s: %s: UHS2 SET PHY Settings fail, res= 0x%x!\n",
+ mmc_hostname(host), __func__, resp[2]);
+ }
+
+ DBG("SET_COMMON_CFG: LINK-TRAN Settings.\n");
+ arg = ((UHS2_DEV_CONFIG_LINK_TRAN_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_LINK_TRAN_SET >> 8);
+
+ plen = 2;
+
+ if (host->uhs2_dev_prop.app_type == UHS2_DEV_CONFIG_APP_SD_MEM)
+ host->uhs2_dev_prop.maxblk_len_set =
+ UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN;
+ else
+ host->uhs2_dev_prop.maxblk_len_set =
+ min(host->uhs2_dev_prop.maxblk_len,
+ host->uhs2_caps.maxblk_len);
+ host->uhs2_caps.maxblk_len_set = host->uhs2_dev_prop.maxblk_len_set;
+
+ host->uhs2_dev_prop.n_fcu_set =
+ min(host->uhs2_dev_prop.n_fcu,
+ host->uhs2_caps.n_fcu);
+ host->uhs2_caps.n_fcu_set = host->uhs2_dev_prop.n_fcu_set;
+
+ host->uhs2_dev_prop.n_data_gap_set =
+ max(nMinDataGap, host->uhs2_dev_prop.n_data_gap);
+
+ host->uhs2_caps.n_data_gap_set = host->uhs2_dev_prop.n_data_gap_set;
+
+ host->uhs2_caps.max_retry_set = 3;
+ host->uhs2_dev_prop.max_retry_set = host->uhs2_caps.max_retry_set;
+
+ payload[0] = (host->uhs2_dev_prop.maxblk_len_set <<
+ UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) |
+ (host->uhs2_dev_prop.max_retry_set <<
+ UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) |
+ (host->uhs2_dev_prop.n_fcu_set <<
+ UHS2_DEV_CONFIG_N_FCU_POS);
+ payload[1] = host->uhs2_dev_prop.n_data_gap_set;
+ payload[0] = cpu_to_be32(payload[0]);
+ payload[1] = cpu_to_be32(payload[1]);
+
+ DBG("Begin SET_COMMON_CFG header=0x%x arg=0x%x\n", header, arg);
+ DBG("\t\tpayload[0]=0x%x payload[1]=0x%x\n", payload[0], payload[1]);
+
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ DBG("SET_COMMON_CFG: Set Config Completion.\n");
+ arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+ plen = 2;
+ payload[0] = 0;
+ payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE;
+ payload[0] = cpu_to_be32(payload[0]);
+ payload[1] = cpu_to_be32(payload[1]);
+
+ DBG("Begin SET_COMMON_CFG, header=0x%x, arg=0x%x, payload[0] = 0x%x.\n",
+ header, arg, payload[0]);
+ resp_len = 5;
+ memset(resp, 0, sizeof(resp));
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen,
+ resp, resp_len);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ /* Set host Config Setting registers */
+ if (!host->ops->uhs2_set_reg ||
+ host->ops->uhs2_set_reg(host, SET_CONFIG)) {
+ pr_err("%s: %s: UHS2 SET_CONFIG fail!\n",
+ mmc_hostname(host), __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int uhs2_go_dormant(struct mmc_host *host, bool hibernate)
+{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header = 0, arg = 0;
+ u32 payload[1];
+ u8 plen = 1;
+ int err;
+
+ /* Disable Normal INT */
+ if (!host->ops->uhs2_set_reg ||
+ host->ops->uhs2_set_reg(host, DISABLE_INT)) {
+ pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
+ mmc_hostname(host), __func__);
+ return -EIO;
+ }
+
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD |
+ host->uhs2_dev_prop.node_id;
+
+ arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_4B |
+ (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
+
+ if (hibernate)
+ payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
+
+ DBG("Begin GO_DORMANT_STATE, header=0x%x, arg=0x%x, payload=0x%x.\n",
+ header, arg, payload[0]);
+ uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err) {
+ pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ /* Check Dormant State in Present */
+ if (!host->ops->uhs2_set_reg ||
+ host->ops->uhs2_set_reg(host, CHECK_DORMANT)) {
+ pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
+ mmc_hostname(host), __func__);
+ return -EIO;
+ }
+
+ if (host->ops->uhs2_disable_clk)
+ host->ops->uhs2_disable_clk(host);
+
+ return 0;
+}
+
+static int uhs2_change_speed(struct mmc_host *host)
+{
+ int err;
+
+ /* Change Speed Range */
+ if (!host->ops->uhs2_set_reg ||
+ host->ops->uhs2_set_reg(host, SET_SPEED_B)) {
+ pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
+ mmc_hostname(host), __func__);
+ return -EIO;
+ }
+
+ err = uhs2_go_dormant(host, false);
+ if (err) {
+ pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return -EIO;
+ }
+
+ /* restore sd clock */
+ mdelay(5);
+ if (host->ops->uhs2_enable_clk)
+ host->ops->uhs2_enable_clk(host);
+
+ /* Enable Normal INT */
+ if (!host->ops->uhs2_set_reg ||
+ host->ops->uhs2_set_reg(host, ENABLE_INT)) {
+ pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
+ mmc_hostname(host), __func__);
+ return -EIO;
+ }
+
+ if (!host->ops->uhs2_detect_init ||
+ host->ops->uhs2_detect_init(host)) {
+ pr_err("%s: %s: uhs2_detect_init() fail!\n",
+ mmc_hostname(host), __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * mmc_uhs2_rescan_try_freq - select UHS2 interface
+ * @host: MMC host
+ * @freq: Bus speed
+ *
+ * Try to select UHS2 interface and initialize the bus for a given
+ * frequency, @freq.
+ *
+ * Return: 0 on success, non-zero error on failure
+ */
+int mmc_uhs2_rescan_try_freq(struct mmc_host *host, unsigned int freq)
+{
+ int err = -EIO;
+
+ host->flags |= MMC_UHS2_SUPPORT;
+ host->f_init = freq;
+
+ pr_debug("%s: %s: trying to init card at %u Hz\n",
+ mmc_hostname(host), __func__, host->f_init);
+
+ mmc_power_up(host, 0);
+ if (!host->ops->uhs2_detect_init ||
+ host->ops->uhs2_detect_init(host)) {
+ pr_err("%s: fail to detect UHS2!\n", mmc_hostname(host));
+ err = UHS2_PHY_INIT_ERR;
+ goto init_fail;
+ }
+
+ if (uhs2_dev_init(host)) {
+ pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
+ goto init_fail;
+ }
+
+ if (uhs2_enum(host)) {
+ pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
+ goto init_fail;
+ }
+
+ if (uhs2_config_read(host)) {
+ pr_err("%s: UHS2 INQUIRY_CONFIG fail!\n", mmc_hostname(host));
+ goto init_fail;
+ }
+
+ if (uhs2_config_write(host)) {
+ pr_err("%s: UHS2 SET_COMMON_CONFIG fail!\n",
+ mmc_hostname(host));
+ goto init_fail;
+ }
+
+ mmc_delay(10);
+
+ /* Change to Speed Range B if it is supported */
+ if (host->flags & MMC_UHS2_SPEED_B)
+ if (uhs2_change_speed(host)) {
+ pr_err("%s: UHS2 uhs2_change_speed() fail!\n",
+ mmc_hostname(host));
+ goto init_fail;
+ }
+
+ host->flags |= MMC_UHS2_INITIALIZED;
+
+ mmc_send_if_cond(host, host->ocr_avail);
+
+ /* On market, only can some SD cards support UHS-II so only call SD
+ * attach process here.
+ */
+ if (!(host->caps2 & MMC_CAP2_NO_SD)) {
+ err = mmc_attach_sd(host);
+ if (!err)
+ return 0;
+ } else {
+ err = -EIO;
+ }
+
+init_fail:
+ mmc_power_off(host);
+ if (host->flags & MMC_UHS2_INITIALIZED)
+ host->flags &= ~MMC_UHS2_INITIALIZED;
+ host->flags &= ~MMC_UHS2_SUPPORT;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mmc_uhs2_rescan_try_freq);
diff --git a/drivers/mmc/core/uhs2.h b/drivers/mmc/core/uhs2.h
new file mode 100644
index 000000000000..e3389d4dda3b
--- /dev/null
+++ b/drivers/mmc/core/uhs2.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * driver/mmc/core/uhs2.h - UHS-II driver
+ *
+ * Header file for UHS-II packets, Host Controller registers and I/O
+ * accessors.
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ */
+#ifndef MMC_UHS2_H
+#define MMC_UHS2_H
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+
+#define UHS2_PHY_INIT_ERR 1
+
+int mmc_uhs2_rescan_try_freq(struct mmc_host *host, unsigned int freq);
+
+#endif /* MMC_UHS2_H */
--
2.28.0
From: Ben Chuang <[email protected]>
This "post" hook for mmc_attach_sd(), uhs2_post_attach_sd, will be required
to enable UHS-II support, at least, on GL9755.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 4e2cb73a63bd..b85d9d077c61 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -724,6 +724,7 @@ struct sdhci_ops {
void (*dump_vendor_regs)(struct sdhci_host *host);
void (*dump_uhs2_regs)(struct sdhci_host *host);
void (*uhs2_pre_detect_init)(struct sdhci_host *host);
+ void (*uhs2_post_attach_sd)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
2.28.0
From: Ben Chuang <[email protected]>
This "pre" hook for detect_init(), uhs2_pre_detect_init, will be required
to enable UHS-II support, at least, on GL9755.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 3 +++
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 5d3362ea138f..3dd81c89d8f1 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -1631,6 +1631,9 @@ static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
DBG("%s: begin UHS2 init.\n", __func__);
spin_lock_irqsave(&host->lock, flags);
+ if (host->ops && host->ops->uhs2_pre_detect_init)
+ host->ops->uhs2_pre_detect_init(host);
+
if (sdhci_uhs2_interface_detect(host)) {
pr_warn("%s: cannot detect UHS2 interface.\n",
mmc_hostname(host->mmc));
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index b1d856664b58..4e2cb73a63bd 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -723,6 +723,7 @@ struct sdhci_ops {
struct mmc_request *mrq);
void (*dump_vendor_regs)(struct sdhci_host *host);
void (*dump_uhs2_regs)(struct sdhci_host *host);
+ void (*uhs2_pre_detect_init)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
2.28.0
This patch prepares for adding UHS-II support at a specific UHS-II
capable sdhci-pci controller, GL9755 for now.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-pci-core.c | 16 +++++++++++++++-
drivers/mmc/host/sdhci-pci.h | 3 +++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 23da7f7fe093..a896c7b325d0 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -39,6 +39,7 @@
#include "sdhci.h"
#include "sdhci-pci.h"
+#include "sdhci-uhs2.h"
static void sdhci_pci_hw_reset(struct sdhci_host *host);
@@ -2215,7 +2216,10 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
if (scratch == (u32)-1)
dead = 1;
- sdhci_remove_host(slot->host, dead);
+ if (slot->chip->fixes && slot->chip->fixes->remove_host)
+ slot->chip->fixes->remove_host(slot, dead);
+ else
+ sdhci_remove_host(slot->host, dead);
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
slot->chip->fixes->remove_slot(slot, dead);
@@ -2226,6 +2230,16 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_free_host(slot->host);
}
+int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot)
+{
+ return sdhci_uhs2_add_host(slot->host);
+}
+
+void sdhci_pci_uhs2_remove_host(struct sdhci_pci_slot *slot, int dead)
+{
+ sdhci_uhs2_remove_host(slot->host, dead);
+}
+
static void sdhci_pci_runtime_pm_allow(struct device *dev)
{
pm_suspend_ignore_children(dev, 1);
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index d0ed232af0eb..c961ec3cec26 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -137,6 +137,7 @@ struct sdhci_pci_fixes {
int (*probe_slot) (struct sdhci_pci_slot *);
int (*add_host) (struct sdhci_pci_slot *);
void (*remove_slot) (struct sdhci_pci_slot *, int);
+ void (*remove_host) (struct sdhci_pci_slot *, int);
#ifdef CONFIG_PM_SLEEP
int (*suspend) (struct sdhci_pci_chip *);
@@ -186,6 +187,8 @@ static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
return (void *)slot->private;
}
+int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot);
+void sdhci_pci_uhs2_remove_host(struct sdhci_pci_slot *slot, int dead);
#ifdef CONFIG_PM_SLEEP
int sdhci_pci_resume_host(struct sdhci_pci_chip *chip);
#endif
--
2.28.0
This is a UHS-II version of sdhci's request() operation.
It handles UHS-II related command interrupts and errors.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 247 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 3 +
drivers/mmc/host/sdhci.c | 112 ++++++++-------
drivers/mmc/host/sdhci.h | 6 +
4 files changed, 319 insertions(+), 49 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 36e52553977a..d50134e912f3 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -11,6 +11,7 @@
*/
#include <linux/delay.h>
+#include <linux/dmaengine.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/mmc/mmc.h>
@@ -670,6 +671,12 @@ static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
struct mmc_command *cmd)
{
}
+
+static inline struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ return NULL;
+}
#endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
static void sdhci_uhs2_finish_data(struct sdhci_host *host)
@@ -1051,6 +1058,246 @@ static void sdhci_uhs2_finish_command(struct sdhci_host *host)
}
}
+/*****************************************************************************\
+ * *
+ * Request done *
+ * *
+\*****************************************************************************/
+
+static bool sdhci_uhs2_request_done(struct sdhci_host *host)
+{
+ unsigned long flags;
+ struct mmc_request *mrq;
+ int i;
+
+ /* FIXME: UHS2_INITIALIZED, instead? */
+ if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
+ return sdhci_request_done(host);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+ mrq = host->mrqs_done[i];
+ if (mrq)
+ break;
+ }
+
+ if (!mrq) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return true;
+ }
+
+ /*
+ * Always unmap the data buffers if they were mapped by
+ * sdhci_prepare_data() whenever we finish with a request.
+ * This avoids leaking DMA mappings on error.
+ */
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ struct mmc_data *data = mrq->data;
+
+ if (host->use_external_dma && data &&
+ (mrq->cmd->error || data->error)) {
+ struct dma_chan *chan = sdhci_external_dma_channel(host, data);
+
+ host->mrqs_done[i] = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+ dmaengine_terminate_sync(chan);
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_set_mrq_done(host, mrq);
+ }
+
+ sdhci_request_done_dma(host, mrq);
+ }
+
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (sdhci_needs_reset(host, mrq)) {
+ /*
+ * Do not finish until command and data lines are available for
+ * reset. Note there can only be one other mrq, so it cannot
+ * also be in mrqs_done, otherwise host->cmd and host->data_cmd
+ * would both be null.
+ */
+ if (host->cmd || host->data_cmd) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return true;
+ }
+
+ /* Some controllers need this kick or reset won't work here */
+ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
+ /* This is to force an update */
+ host->ops->set_clock(host, host->clock);
+
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+ host->pending_reset = false;
+ }
+
+ host->mrqs_done[i] = NULL;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (host->ops->request_done)
+ host->ops->request_done(host, mrq);
+ else
+ mmc_request_done(host->mmc, mrq);
+
+ return false;
+}
+
+static void sdhci_uhs2_complete_work(struct work_struct *work)
+{
+ struct sdhci_host *host = container_of(work, struct sdhci_host,
+ complete_work);
+
+ while (!sdhci_uhs2_request_done(host))
+ ;
+}
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
+{
+ struct mmc_command *cmd = host->cmd;
+
+ DBG("*** %s got UHS2 error interrupt: 0x%08x\n",
+ mmc_hostname(host->mmc), uhs2mask);
+
+ if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK) {
+ if (!host->cmd) {
+ pr_err("%s: Got cmd interrupt 0x%08x but no cmd.\n",
+ mmc_hostname(host->mmc),
+ (unsigned int)uhs2mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ host->cmd->error = -EILSEQ;
+ if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
+ host->cmd->error = -ETIMEDOUT;
+ }
+
+ if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK) {
+ if (!host->data) {
+ pr_err("%s: Got data interrupt 0x%08x but no data.\n",
+ mmc_hostname(host->mmc),
+ (unsigned int)uhs2mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+
+ if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT) {
+ pr_err("%s: Got deadlock timeout interrupt 0x%08x\n",
+ mmc_hostname(host->mmc),
+ (unsigned int)uhs2mask);
+ host->data->error = -ETIMEDOUT;
+ } else if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_ADMA) {
+ pr_err("%s: ADMA error = 0x %x\n",
+ mmc_hostname(host->mmc),
+ sdhci_readb(host, SDHCI_ADMA_ERROR));
+ host->data->error = -EIO;
+ } else {
+ host->data->error = -EILSEQ;
+ }
+ }
+
+ if (host->data && host->data->error)
+ sdhci_uhs2_finish_data(host);
+ else
+ sdhci_finish_mrq(host, cmd->mrq);
+}
+
+u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
+{
+ u32 mask = intmask, uhs2mask;
+
+ if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
+ goto out;
+
+ /*
+ * TODO: We should mask Normal Error Interrupt Status Register
+ * in UHS-2 mode so that we don't have to care SD mode errors.
+ */
+ if (intmask & SDHCI_INT_ERROR) {
+ uhs2mask = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS);
+ if (!(uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK))
+ goto cmd_irq;
+
+ /* Clear error interrupts */
+ sdhci_writel(host, uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK,
+ SDHCI_UHS2_ERR_INT_STATUS);
+
+ /* Handle error interrupts */
+ __sdhci_uhs2_irq(host, uhs2mask);
+
+ /* Caller, shdci_irq(), doesn't have to care UHS-2 errors */
+ intmask &= ~SDHCI_INT_ERROR;
+ mask &= SDHCI_INT_ERROR;
+ }
+
+cmd_irq:
+ /*
+ * TODO: Cleanup
+ * INT_RESPONSE is enough instead of INT_CMD_MASK, assuming that
+ * INT_ERROR and INT_CMD_MASK won't happen at the same time.
+ */
+ if (intmask & SDHCI_INT_CMD_MASK) {
+ /* Clear command interrupt */
+ sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
+ SDHCI_INT_STATUS);
+
+ /* Handle command interrupt */
+ if (intmask & SDHCI_INT_RESPONSE)
+ sdhci_uhs2_finish_command(host);
+
+ /* Caller, shdci_irq(), doesn't have to care UHS-2 command */
+ intmask &= ~SDHCI_INT_CMD_MASK;
+ mask &= SDHCI_INT_CMD_MASK;
+ }
+
+ /* Clear already-handled interrupts. */
+ sdhci_writel(host, mask, SDHCI_INT_STATUS);
+
+out:
+ return intmask;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_irq);
+
+static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
+{
+ struct sdhci_host *host = dev_id;
+ struct mmc_command *cmd;
+ unsigned long flags;
+ u32 isr;
+
+ while (!sdhci_uhs2_request_done(host))
+ ;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ isr = host->thread_isr;
+ host->thread_isr = 0;
+
+ cmd = host->deferred_cmd;
+ if (cmd && !sdhci_uhs2_send_command_retry(host, cmd, flags))
+ sdhci_finish_mrq(host, cmd->mrq);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+ struct mmc_host *mmc = host->mmc;
+
+ mmc->ops->card_event(mmc);
+ mmc_detect_change(mmc, msecs_to_jiffies(200));
+ }
+
+ return IRQ_HANDLED;
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index c1ff4ac1ab7a..b74af641d00e 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -215,5 +215,8 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
+void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
+int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
+u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index a9f5449bddcc..e2dfc7767bcf 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1215,11 +1215,12 @@ static int sdhci_external_dma_init(struct sdhci_host *host)
return ret;
}
-static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
- struct mmc_data *data)
+struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
+ struct mmc_data *data)
{
return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
}
+EXPORT_SYMBOL_GPL(sdhci_external_dma_channel);
int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
{
@@ -1466,7 +1467,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
}
-static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
+bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
{
return (!(host->flags & SDHCI_DEVICE_DEAD) &&
((mrq->cmd && mrq->cmd->error) ||
@@ -1474,8 +1475,9 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
(mrq->data && mrq->data->stop && mrq->data->stop->error) ||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
}
+EXPORT_SYMBOL_GPL(sdhci_needs_reset);
-static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
+void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
{
int i;
@@ -1495,6 +1497,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
WARN_ON(i >= SDHCI_MAX_MRQS);
}
+EXPORT_SYMBOL_GPL(sdhci_set_mrq_done);
void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
@@ -3029,7 +3032,56 @@ static const struct mmc_host_ops sdhci_ops = {
* *
\*****************************************************************************/
-static bool sdhci_request_done(struct sdhci_host *host)
+void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ struct mmc_data *data = mrq->data;
+
+ if (data && data->host_cookie == COOKIE_MAPPED) {
+ if (host->bounce_buffer) {
+ /*
+ * On reads, copy the bounced data into the
+ * sglist
+ */
+ if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
+ unsigned int length = data->bytes_xfered;
+
+ if (length > host->bounce_buffer_size) {
+ pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
+ mmc_hostname(host->mmc),
+ host->bounce_buffer_size,
+ data->bytes_xfered);
+ /* Cap it down and continue */
+ length = host->bounce_buffer_size;
+ }
+ dma_sync_single_for_cpu(
+ host->mmc->parent,
+ host->bounce_addr,
+ host->bounce_buffer_size,
+ DMA_FROM_DEVICE);
+ sg_copy_from_buffer(data->sg,
+ data->sg_len,
+ host->bounce_buffer,
+ length);
+ } else {
+ /* No copying, just switch ownership */
+ dma_sync_single_for_cpu(
+ host->mmc->parent,
+ host->bounce_addr,
+ host->bounce_buffer_size,
+ mmc_get_dma_dir(data));
+ }
+ } else {
+ /* Unmap the raw data */
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len,
+ mmc_get_dma_dir(data));
+ }
+ data->host_cookie = COOKIE_UNMAPPED;
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_request_done_dma);
+
+bool sdhci_request_done(struct sdhci_host *host)
{
unsigned long flags;
struct mmc_request *mrq;
@@ -3067,48 +3119,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
sdhci_set_mrq_done(host, mrq);
}
- if (data && data->host_cookie == COOKIE_MAPPED) {
- if (host->bounce_buffer) {
- /*
- * On reads, copy the bounced data into the
- * sglist
- */
- if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
- unsigned int length = data->bytes_xfered;
-
- if (length > host->bounce_buffer_size) {
- pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
- mmc_hostname(host->mmc),
- host->bounce_buffer_size,
- data->bytes_xfered);
- /* Cap it down and continue */
- length = host->bounce_buffer_size;
- }
- dma_sync_single_for_cpu(
- host->mmc->parent,
- host->bounce_addr,
- host->bounce_buffer_size,
- DMA_FROM_DEVICE);
- sg_copy_from_buffer(data->sg,
- data->sg_len,
- host->bounce_buffer,
- length);
- } else {
- /* No copying, just switch ownership */
- dma_sync_single_for_cpu(
- host->mmc->parent,
- host->bounce_addr,
- host->bounce_buffer_size,
- mmc_get_dma_dir(data));
- }
- } else {
- /* Unmap the raw data */
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
- mmc_get_dma_dir(data));
- }
- data->host_cookie = COOKIE_UNMAPPED;
- }
+ sdhci_request_done_dma(host, mrq);
}
/*
@@ -3132,8 +3143,10 @@ static bool sdhci_request_done(struct sdhci_host *host)
/* This is to force an update */
host->ops->set_clock(host, host->clock);
- /* Spec says we should do both at the same time, but Ricoh
- controllers do not like that. */
+ /*
+ * Spec says we should do both at the same time, but
+ * Ricoh controllers do not like that.
+ */
sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);
@@ -3151,6 +3164,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
return false;
}
+EXPORT_SYMBOL_GPL(sdhci_request_done);
static void sdhci_complete_work(struct work_struct *work)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 6eeb74741da3..74572b54ec47 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -845,8 +845,12 @@ void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
struct mmc_command *cmd);
void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
struct mmc_command *cmd);
+struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
+ struct mmc_data *data);
#endif
bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
+bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq);
+void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq);
void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
void __sdhci_finish_data_common(struct sdhci_host *host);
@@ -878,6 +882,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios);
void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
+void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
+bool sdhci_request_done(struct sdhci_host *host);
void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
--
2.28.0
This is a UHS-II version of sdhci's set_power operation.
VDD2, as well as VDD, is handled here.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
---
drivers/mmc/host/sdhci-uhs2.c | 80 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
drivers/mmc/host/sdhci.c | 58 +++++++++++++++----------
drivers/mmc/host/sdhci.h | 2 +
4 files changed, 119 insertions(+), 23 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index e2b9743fe17d..2bf78cc4e9ed 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -98,6 +98,86 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd)
+{
+ struct mmc_host *mmc = host->mmc;
+ u8 pwr;
+
+ /* FIXME: check if flags & MMC_UHS2_SUPPORT? */
+ if (!(host->mmc->caps & MMC_CAP_UHS2)) {
+ sdhci_set_power(host, mode, vdd);
+ return;
+ }
+
+ if (mode != MMC_POWER_OFF) {
+ pwr = sdhci_get_vdd_value(vdd);
+ if (!pwr)
+ WARN(1, "%s: Invalid vdd %#x\n",
+ mmc_hostname(host->mmc), vdd);
+
+ pwr |= SDHCI_VDD2_POWER_180;
+ }
+
+ if (host->pwr == pwr)
+ return;
+ host ->pwr = pwr;
+
+ if (pwr == 0) {
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ if (!IS_ERR(host->mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc2))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
+
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_off(host);
+ } else {
+ if (!IS_ERR(host->mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc2))
+ /* support 1.8v only for now */
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2,
+ fls(MMC_VDD2_165_195) - 1);
+
+ /*
+ * Spec says that we should clear the power reg before setting
+ * a new value. Some controllers don't seem to like this though.
+ */
+ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ /*
+ * At least the Marvell CaFe chip gets confused if we set the
+ * voltage and set turn on power at the same time, so set the
+ * voltage first.
+ */
+ if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+ /* vdd first */
+ pwr |= SDHCI_POWER_ON;
+ sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
+ mdelay(5);
+
+ pwr |= SDHCI_VDD2_POWER_ON;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ mdelay(5);
+
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_on(host);
+
+ /*
+ * Some controllers need an extra 10ms delay of 10ms before
+ * they can apply clock after applying power
+ */
+ if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
+ mdelay(10);
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 7bb7a0d67109..3c19d8e44c36 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -211,5 +211,7 @@ struct sdhci_host;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
+void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index af336bdb4305..0b741eb546cb 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -187,13 +187,14 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)
sdhci_set_card_detection(host, false);
}
-static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
+void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
{
if (host->bus_on)
return;
host->bus_on = true;
pm_runtime_get_noresume(host->mmc->parent);
}
+EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_on);
void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
{
@@ -2017,36 +2018,47 @@ static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode,
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
}
+unsigned short sdhci_get_vdd_value(unsigned short vdd)
+{
+ u8 pwr;
+
+ switch (1 << vdd) {
+ case MMC_VDD_165_195:
+ /*
+ * Without a regulator, SDHCI does not support 2.0v
+ * so we only get here if the driver deliberately
+ * added the 2.0v range to ocr_avail. Map it to 1.8v
+ * for the purpose of turning on the power.
+ */
+ case MMC_VDD_20_21:
+ pwr = SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr = SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr = SDHCI_POWER_330;
+ break;
+ default:
+ pwr = 0;
+ }
+
+ return pwr;
+}
+EXPORT_SYMBOL_GPL(sdhci_get_vdd_value);
+
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd)
{
u8 pwr = 0;
if (mode != MMC_POWER_OFF) {
- switch (1 << vdd) {
- case MMC_VDD_165_195:
- /*
- * Without a regulator, SDHCI does not support 2.0v
- * so we only get here if the driver deliberately
- * added the 2.0v range to ocr_avail. Map it to 1.8v
- * for the purpose of turning on the power.
- */
- case MMC_VDD_20_21:
- pwr = SDHCI_POWER_180;
- break;
- case MMC_VDD_29_30:
- case MMC_VDD_30_31:
- pwr = SDHCI_POWER_300;
- break;
- case MMC_VDD_32_33:
- case MMC_VDD_33_34:
- pwr = SDHCI_POWER_330;
- break;
- default:
+ pwr = sdhci_get_vdd_value(vdd);
+ if (!pwr)
WARN(1, "%s: Invalid vdd %#x\n",
mmc_hostname(host->mmc), vdd);
- break;
- }
}
if (host->pwr == pwr)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index b9932423db08..2b5b8295cf92 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -831,6 +831,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
__sdhci_read_caps(host, NULL, NULL, NULL);
}
+void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock);
@@ -841,6 +842,7 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
unsigned char mode,
unsigned short vdd);
+unsigned short sdhci_get_vdd_value(unsigned short vdd);
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
--
2.28.0
> -----Original Message-----
> From: AKASHI Takahiro [mailto:[email protected]]
> Sent: 2020??11??6?? 10:27
> To: [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; AKASHI
> Takahiro <[email protected]>
> Subject: [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip
> set_chip_select()
>
> mmc_set_chip_select() should be called only in UHS-II mode, and not for UHS-II
> mode.
Typo, only in UHS-I mode.
Regards
Haibo Chen
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/core/core.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index
> 5541ed956c4d..4e12bd98fc08 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -898,8 +898,10 @@ static inline void mmc_set_ios(struct mmc_host
> *host)
> */
> void mmc_set_chip_select(struct mmc_host *host, int mode) {
> - host->ios.chip_select = mode;
> - mmc_set_ios(host);
> + if (!(host->flags & MMC_UHS2_INITIALIZED)) {
> + host->ios.chip_select = mode;
> + mmc_set_ios(host);
> + }
> }
>
> /*
> --
> 2.28.0
Gentle ping;
On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
> This is an interim snapshot of our next version, v4, for enabling
> UHS-II on MMC/SD.
>
> It is focused on 'sdhci' side to address Adrian's comments regarding
> "modularising" sdhci-uhs2.c.
> The whole aim of this version is to get early feedback from Adrian (and
> others) on this issue. Without any consensus about the code structure,
Any comments so far?
-Takahiro Akashi
> it would make little sense to go further ahead on sdhci side.
> (Actually, Adrian has made no comments other than "modularising" so far.)
>
> I heavily reworked/refactored sdhci-uhs2.c and re-organised the patch
> set to meet what I believe Adrian expects; no UHS-II related code in
> Legacy (UHS-I) code or sdhci.c.
>
> Nevertheless, almost of all changes I made are trivial and straightforward
> in this direction, and I believe that there is no logic changed since v3
> except sdhci_uhs2_irq(), as ops->irq hook, where we must deal with UHS-II
> command sequences in addition to UHS-II errors. So I added extra handlings.
>
> I admit that there is plenty of room for improvements (for example,
> handling host->flags), but again the focal point here is how sdhci-uhs2.c
> should be built as a module.
>
> Please review this series (particularly Patch#8-#26 and #27) from this
> viewpoint in the first place.
> (Ben is working on 'host' side but there is no change on 'host' side
> in this submission except a minor tweak.)
>
> Thanks,
> -Takahiro Akashi
>
> ------ original cover letter from v3 ------
> Summary
> =======
> These patches[1] support UHS-II and fix GL9755 UHS-II compatibility.
>
> About UHS-II, roughly deal with the following three parts:
> 1) A UHS-II detection and initialization:
> - Host setup to support UHS-II (Section 3.13.1 Host Controller Setup Sequence
> [2]).
> - Detect a UHS-II I/F (Section 3.13.2 Card Interface Detection Sequence[2]).
> - In step(9) of Section 3.13.2 in [2], UHS-II initialization is include Section
> 3.13.3 UHS-II Card Initialization and Section 3.13.4 UHS-II Setting Register
> Setup Sequence.
>
> 2) Send Legacy SD command through SD-TRAN
> - Encapsulated SD packets are defined in SD-TRAN in order to ensure Legacy SD
> compatibility and preserve Legacy SD infrastructures (Section 7.1.1 Packet
> Types and Format Overview[3]).
> - Host issue a UHS-II CCMD packet or a UHS-II DCMD (Section 3.13.5 UHS-II
> CCMD Packet issuing and Section 3.13.6 UHS-II DCMD Packet issuing[2]).
>
> 3) UHS-II Interrupt
> - Except for UHS-II error interrupts, most interrupts share the original
> interrupt registers.
>
> Patch structure
> ===============
> patch#1-#7: for core
> patch#8-#17: for sdhci
> patch#18-#21: for GL9755
>
> Tests
> =====
> Ran 'dd' command to evaluate the performance:
> (SanDisk UHS-II card on GL9755 controller)
> Read Write
> UHS-II disabled (UHS-I): 88.3MB/s 60.7MB/s
> UHS-II enabled : 206MB/s 80MB/s
>
> TODO
> ====
> - replace some define with BIT macro
>
> Reference
> =========
> [1] https://gitlab.com/ben.chuang/linux-uhs2-gl9755.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
>
> Changes in v3 (Jul. 10, 2020)
> * rebased to v5.8-rc4
> * add copyright notice
> * reorganize the patch set and split some commits into smaller ones
> * separate uhs-2 headers from others
> * correct wrong spellings
> * fix most of checkpatch warnings/errors
> * remove all k[cz]alloc() from the code
> * guard sdhci-uhs2 specific code with
> 'if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2))'
> * make sdhci-uhs2.c as a module
> * trivial changes, including
> - rename back sdhci-core.c to sdhci.c
> - allow vendor code to disable uhs2 if v4_mode == 0
> in __sdhci_add_host()
> - merge uhs2_power_up() into mmc_power_up()
> - remove flag_uhs2 from mmc_attach_sd()
> - add function descriptions to EXPORT'ed functions
> - other minor code optimization
>
> Changes in v2 (Jan. 9, 2020)
> * rebased to v5.5-rc5
>
> AKASHI Takahiro (23):
> mmc: core: UHS-II support, modify power-up sequence
> mmc: core: UHS-II support, skip set_chip_select()
> mmc: core: UHS-II support, skip TMODE setup in some cases
> mmc: core: UHS-II support, generate UHS-II SD command packet
> mmc: core: UHS-II support, set APP_CMD bit if necessary
> mmc: sdhci: add a kernel configuration for enabling UHS-II support
> mmc: sdhci: add UHS-II related definitions in headers
> mmc: sdhci: add UHS-II module
> mmc: sdhci-uhs2: dump UHS-II registers
> mmc: sdhci-uhs2: add reset function
> mmc: sdhci-uhs2: add set_power() to support vdd2
> mmc: sdhci-uhs2: skip signal_voltage_switch()
> mmc: sdhci-uhs2: add set_timeout()
> mmc: sdhci-uhs2: add set_ios()
> mmc: sdhci-uhs2: add detect_init() to detect the interface
> mmc: sdhci-uhs2: add clock operations
> mmc: sdhci-uhs2: add set_reg() to initialise the interface
> mmc: sdhci-uhs2: add request() and others
> mmc: sdhci-uhs2: add irq() and others
> mmc: sdhci-uhs2: add add_host() and others to set up the driver
> mmc: core: add post-mmc_attach_sd hook
> mmc: sdhci-pci: add UHS-II support framework
> mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
>
> Ben Chuang (4):
> mmc: add UHS-II related definitions in public headers
> mmc: core: UHS-II support, try to select UHS-II interface
> mmc: sdhci-uhs2: add pre-detect_init hook
> mmc: sdhci-uhs2: add post-mmc_attach_sd hook
>
> drivers/mmc/core/Makefile | 2 +-
> drivers/mmc/core/block.c | 7 +-
> drivers/mmc/core/bus.c | 5 +-
> drivers/mmc/core/core.c | 118 +-
> drivers/mmc/core/regulator.c | 14 +
> drivers/mmc/core/sd.c | 32 +
> drivers/mmc/core/sd_ops.c | 12 +
> drivers/mmc/core/uhs2.c | 883 +++++++++++++++
> drivers/mmc/core/uhs2.h | 21 +
> drivers/mmc/host/Kconfig | 10 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-pci-core.c | 16 +-
> drivers/mmc/host/sdhci-pci-gli.c | 318 +++++-
> drivers/mmc/host/sdhci-pci.h | 3 +
> drivers/mmc/host/sdhci-uhs2.c | 1697 +++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 224 ++++
> drivers/mmc/host/sdhci.c | 333 +++---
> drivers/mmc/host/sdhci.h | 117 +-
> include/linux/mmc/card.h | 1 +
> include/linux/mmc/core.h | 6 +
> include/linux/mmc/host.h | 31 +
> include/linux/mmc/uhs2.h | 268 +++++
> 22 files changed, 3961 insertions(+), 158 deletions(-)
> create mode 100644 drivers/mmc/core/uhs2.c
> create mode 100644 drivers/mmc/core/uhs2.h
> create mode 100644 drivers/mmc/host/sdhci-uhs2.c
> create mode 100644 drivers/mmc/host/sdhci-uhs2.h
> create mode 100644 include/linux/mmc/uhs2.h
>
> --
> 2.28.0
>
On Wed, 25 Nov 2020 at 08:41, AKASHI Takahiro
<[email protected]> wrote:
>
> Gentle ping;
>
> On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
> > This is an interim snapshot of our next version, v4, for enabling
> > UHS-II on MMC/SD.
> >
> > It is focused on 'sdhci' side to address Adrian's comments regarding
> > "modularising" sdhci-uhs2.c.
> > The whole aim of this version is to get early feedback from Adrian (and
> > others) on this issue. Without any consensus about the code structure,
>
> Any comments so far?
I haven't been able to look at sdhci parts (I will try to), but as you
know, I am relying on Adrian's help with this.
When it comes to the core part, I am planning to help to put some of
the foundation in place for the mmc core changes. Although,
unfortunately I haven't been able to post patches, yet. I will keep
you in the loop, when I get to it.
[...]
Kind regards
Uffe
On Wed, Nov 25, 2020 at 11:43:18AM +0100, Ulf Hansson wrote:
> On Wed, 25 Nov 2020 at 08:41, AKASHI Takahiro
> <[email protected]> wrote:
> >
> > Gentle ping;
> >
> > On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
> > > This is an interim snapshot of our next version, v4, for enabling
> > > UHS-II on MMC/SD.
> > >
> > > It is focused on 'sdhci' side to address Adrian's comments regarding
> > > "modularising" sdhci-uhs2.c.
> > > The whole aim of this version is to get early feedback from Adrian (and
> > > others) on this issue. Without any consensus about the code structure,
> >
> > Any comments so far?
>
> I haven't been able to look at sdhci parts (I will try to), but as you
> know, I am relying on Adrian's help with this.
Yeah, I understand.
> When it comes to the core part, I am planning to help to put some of
> the foundation in place for the mmc core changes. Although,
> unfortunately I haven't been able to post patches, yet. I will keep
> you in the loop, when I get to it.
I think Ben has some idea on the topic.
-Takahiro Akashi
> [...]
>
> Kind regards
> Uffe
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init
> operation. After detected, the host's UHS-II capabilities will be set up
> here and interrupts will also be enabled.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 160 ++++++++++++++++++++++++++++++++++
> 1 file changed, 160 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 637464748cc4..994dff967e85 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -391,12 +391,172 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> * *
> \*****************************************************************************/
>
> +static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
> +{
> + int timeout = 100;
Please comment on where timeouts / delays come from. e.g. as per spec
> +
> + udelay(200); /* wait for 200us before check */
> +
> + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
> + SDHCI_UHS2_IF_DETECT)) {
Can be read_poll_timeout(sdhci_readl,...,host, SDHCI_PRESENT_STATE)
> + if (timeout == 0) {
> + pr_warn("%s: not detect UHS2 interface in 200us.\n",
> + mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return -EIO;
> + }
> + timeout--;
> + mdelay(1);
> + }
> +
> + /* Enable UHS2 error interrupts */
> + sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
> + SDHCI_UHS2_ERR_INT_STATUS_MASK);
> +
> + timeout = 150;
> + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
> + SDHCI_UHS2_LANE_SYNC)) {
Ditto
> + if (timeout == 0) {
> + pr_warn("%s: UHS2 Lane sync fail in 150ms.\n",
> + mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return -EIO;
> + }
> + timeout--;
> + mdelay(1);
> + }
> +
> + DBG("%s: UHS2 Lane synchronized in UHS2 mode, PHY is initialized.\n",
> + mmc_hostname(host->mmc));
> + return 0;
> +}
> +
> +static int sdhci_uhs2_init(struct sdhci_host *host)
> +{
> + u16 caps_ptr = 0;
> + u32 caps_gen = 0;
> + u32 caps_phy = 0;
> + u32 caps_tran[2] = {0, 0};
> + struct mmc_host *mmc = host->mmc;
> +
> + /*
> + * TODO: may add corresponding members in sdhci_host to
> + * keep these caps.
> + */
> + caps_ptr = sdhci_readw(host, SDHCI_UHS2_HOST_CAPS_PTR);
> + if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
> + pr_err("%s: SDHCI_UHS2_HOST_CAPS_PTR(%d) is wrong.\n",
> + mmc_hostname(mmc), caps_ptr);
> + return -ENODEV;
> + }
> + caps_gen = sdhci_readl(host,
> + caps_ptr + SDHCI_UHS2_HOST_CAPS_GEN_OFFSET);
> + caps_phy = sdhci_readl(host,
> + caps_ptr + SDHCI_UHS2_HOST_CAPS_PHY_OFFSET);
> + caps_tran[0] = sdhci_readl(host,
> + caps_ptr + SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET);
> + caps_tran[1] = sdhci_readl(host,
> + caps_ptr
> + + SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET);
> +
> + /* General Caps */
> + mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK;
> + mmc->uhs2_caps.gap = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK) >>
> + SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT;
> + mmc->uhs2_caps.n_lanes = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT;
> + mmc->uhs2_caps.addr64 =
> + (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64) ? 1 : 0;
> + mmc->uhs2_caps.card_type =
> + (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK) >>
> + SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT;
> +
> + /* PHY Caps */
> + mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK;
> + mmc->uhs2_caps.speed_range =
> + (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT;
> + mmc->uhs2_caps.n_lss_sync =
> + (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT;
> + mmc->uhs2_caps.n_lss_dir =
> + (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT;
> + if (mmc->uhs2_caps.n_lss_sync == 0)
> + mmc->uhs2_caps.n_lss_sync = 16 << 2;
> + else
> + mmc->uhs2_caps.n_lss_sync <<= 2;
> + if (mmc->uhs2_caps.n_lss_dir == 0)
> + mmc->uhs2_caps.n_lss_dir = 16 << 3;
> + else
> + mmc->uhs2_caps.n_lss_dir <<= 3;
> +
> + /* LINK/TRAN Caps */
> + mmc->uhs2_caps.link_rev =
> + caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_LINK_REV_MASK;
> + mmc->uhs2_caps.n_fcu =
> + (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT;
> + if (mmc->uhs2_caps.n_fcu == 0)
> + mmc->uhs2_caps.n_fcu = 256;
> + mmc->uhs2_caps.host_type =
> + (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT;
> + mmc->uhs2_caps.maxblk_len =
> + (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK)
> + >> SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT;
> + mmc->uhs2_caps.n_data_gap =
> + caps_tran[1] & SDHCI_UHS2_HOST_CAPS_TRAN_1_N_DATA_GAP_MASK;
> +
> + return 0;
> +}
> +
> +static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + unsigned long flags;
> + int ret = -EIO;
> +
> + DBG("%s: begin UHS2 init.\n", __func__);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (sdhci_uhs2_interface_detect(host)) {
> + pr_warn("%s: cannot detect UHS2 interface.\n",
> + mmc_hostname(host->mmc));
> + goto out;
> + }
> +
> + if (sdhci_uhs2_init(host)) {
> + pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
> + goto out;
> + }
> +
> + /* Init complete, do soft reset and enable UHS2 error irqs. */
> + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
> + sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
> + SDHCI_UHS2_ERR_INT_STATUS_MASK);
> + /*
> + * !!! SDHCI_INT_ENABLE and SDHCI_SIGNAL_ENABLE was cleared
> + * by SDHCI_UHS2_SW_RESET_SD
> + */
> + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> +
> + ret = 0;
> +out:
> + spin_unlock_irqrestore(&host->lock, flags);
> + return ret;
> +}
> +
> static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> {
> host->mmc_host_ops.start_signal_voltage_switch =
> sdhci_uhs2_start_signal_voltage_switch;
> host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
>
> + if (!host->mmc_host_ops.uhs2_detect_init)
> + host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
> +
> return 0;
> }
>
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
> so no voltage switch required.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 2bf78cc4e9ed..1eca89359351 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -178,6 +178,29 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> }
> EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
>
> +/*****************************************************************************\
> + * *
> + * MMC callbacks *
> + * *
> +\*****************************************************************************/
> +
> +static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
> + struct mmc_ios *ios)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> +
> + /*
> + * For UHS2, the signal voltage is supplied by vdd2 which is
> + * already 1.8v so no voltage switch required.
> + */
> + if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
> + host->version >= SDHCI_SPEC_400 &&
> + host->mmc->flags & MMC_UHS2_SUPPORT)
Could this be the same helper function suggested elsewhere i.e.
if (!sdhci_uhs2_mode(host))
> + return 0;
> +
> + return sdhci_start_signal_voltage_switch(mmc, ios);
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -186,6 +209,9 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
>
> static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> {
> + host->mmc_host_ops.start_signal_voltage_switch =
> + sdhci_uhs2_start_signal_voltage_switch;
> +
> return 0;
> }
>
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a sdhci version of mmc's uhs2_set_reg operation.
> UHS-II interface (related registers) will be initialised here.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 103 ++++++++++++++++++++++++++++++++++
> 1 file changed, 103 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 55362ace1857..d8afb99a9918 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -332,6 +332,68 @@ static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +/* TODO: move this function to sdhci.c */
> +static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
> +{
> + u32 ier;
> +
> + ier = sdhci_readl(host, SDHCI_INT_ENABLE);
> + ier &= ~clear;
> + ier |= set;
> + sdhci_writel(host, ier, SDHCI_INT_ENABLE);
> + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
> +}
> +
> +static void sdhci_uhs2_set_config(struct sdhci_host *host)
> +{
> + u32 value;
> + u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SET_PTR);
> + u16 sdhci_uhs2_gen_set_reg = (sdhci_uhs2_set_ptr + 0);
> + u16 sdhci_uhs2_phy_set_reg = (sdhci_uhs2_set_ptr + 4);
> + u16 sdhci_uhs2_tran_set_reg = (sdhci_uhs2_set_ptr + 8);
> + u16 sdhci_uhs2_tran_set_1_reg = (sdhci_uhs2_set_ptr + 12);
> +
> + /* Set Gen Settings */
> + sdhci_writel(host, host->mmc->uhs2_caps.n_lanes_set <<
> + SDHCI_UHS2_GEN_SET_N_LANES_POS, sdhci_uhs2_gen_set_reg);
> +
> + /* Set PHY Settings */
> + value = (host->mmc->uhs2_caps.n_lss_dir_set <<
> + SDHCI_UHS2_PHY_SET_N_LSS_DIR_POS) |
> + (host->mmc->uhs2_caps.n_lss_sync_set <<
> + SDHCI_UHS2_PHY_SET_N_LSS_SYN_POS);
> + if (host->mmc->flags & MMC_UHS2_SPEED_B)
> + value |= 1 << SDHCI_UHS2_PHY_SET_SPEED_POS;
> + sdhci_writel(host, value, sdhci_uhs2_phy_set_reg);
> +
> + /* Set LINK-TRAN Settings */
> + value = (host->mmc->uhs2_caps.max_retry_set <<
> + SDHCI_UHS2_TRAN_SET_RETRY_CNT_POS) |
> + (host->mmc->uhs2_caps.n_fcu_set <<
> + SDHCI_UHS2_TRAN_SET_N_FCU_POS);
> + sdhci_writel(host, value, sdhci_uhs2_tran_set_reg);
> + sdhci_writel(host, host->mmc->uhs2_caps.n_data_gap_set,
> + sdhci_uhs2_tran_set_1_reg);
> +}
> +
> +static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
> +{
> + int timeout = 100;
> +
> + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
> + SDHCI_UHS2_IN_DORMANT_STATE)) {
Can use read_poll_timeout(sdhci_readl,..., host, SDHCI_PRESENT_STATE)
> + if (timeout == 0) {
> + pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n",
> + mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return -EIO;
> + }
> + timeout--;
> + mdelay(1);
> + }
> + return 0;
> +}
> +
> /*****************************************************************************\
> * *
> * MMC callbacks *
> @@ -422,6 +484,45 @@ static void sdhci_uhs2_enable_clk(struct mmc_host *mmc)
> }
> }
>
> +static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + unsigned long flags;
> + int err = 0;
> + u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SET_PTR);
> + u16 sdhci_uhs2_phy_set_reg = (sdhci_uhs2_set_ptr + 4);
> +
> + DBG("Begin sdhci_uhs2_set_reg, act %d.\n", act);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + switch (act) {
> + case SET_CONFIG:
> + sdhci_uhs2_set_config(host);
> + break;
> + case ENABLE_INT:
> + sdhci_clear_set_irqs(host, 0, SDHCI_INT_CARD_INT);
> + break;
> + case DISABLE_INT:
> + sdhci_clear_set_irqs(host, SDHCI_INT_CARD_INT, 0);
> + break;
> + case SET_SPEED_B:
> + sdhci_writeb(host, 1 << SDHCI_UHS2_PHY_SET_SPEED_POS,
> + sdhci_uhs2_phy_set_reg);
> + break;
> + case CHECK_DORMANT:
> + err = sdhci_uhs2_check_dormant(host);
> + break;
> + default:
> + pr_err("%s: input action %d is wrong!\n",
> + mmc_hostname(host->mmc), act);
> + err = -EIO;
> + break;
> + }
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> + return err;
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -597,6 +698,8 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> host->mmc_host_ops.uhs2_disable_clk = sdhci_uhs2_disable_clk;
> if (!host->mmc_host_ops.uhs2_enable_clk)
> host->mmc_host_ops.uhs2_enable_clk = sdhci_uhs2_enable_clk;
> + if (!host->mmc_host_ops.uhs2_set_reg)
> + host->mmc_host_ops.uhs2_set_reg = sdhci_uhs2_set_reg;
>
> return 0;
> }
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a sdhci version of mmc's set_ios operation.
> It covers both UHS-I and UHS-II.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 100 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 1 +
> drivers/mmc/host/sdhci.c | 40 +++++++++-----
> drivers/mmc/host/sdhci.h | 2 +
> 4 files changed, 128 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index d9e98c097bfe..637464748cc4 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -263,6 +263,74 @@ void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
> }
> EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
>
> +/**
> + * sdhci_uhs2_clear_set_irqs - set Error Interrupt Status Enable register
> + * @host: SDHCI host
> + * @clear: bit-wise clear mask
> + * @set: bit-wise set mask
> + *
> + * Set/unset bits in UHS-II Error Interrupt Status Enable register
> + */
> +void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
> +{
> + u32 ier;
> +
> + ier = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS_EN);
> + ier &= ~clear;
> + ier |= set;
> + sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_STATUS_EN);
> + sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_SIG_EN);
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_clear_set_irqs);
> +
> +static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + u8 cmd_res, dead_lock;
> + u16 ctrl_2;
> + unsigned long flags;
> +
> + /* FIXME: why lock? */
> + spin_lock_irqsave(&host->lock, flags);
> +
> + /* UHS2 Timeout Control */
> + sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
> +
> + /* change to use calculate value */
> + cmd_res |= dead_lock << SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT;
> +
> + sdhci_uhs2_clear_set_irqs(host,
> + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
> + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT,
> + 0);
> + sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
> + sdhci_uhs2_clear_set_irqs(host, 0,
> + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
> + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT);
> +
> + /* UHS2 timing */
> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + if (ios->timing == MMC_TIMING_UHS2)
> + ctrl_2 |= SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN;
> + else
> + ctrl_2 &= ~(SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN);
> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +
> + if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
> + sdhci_enable_preset_value(host, true);
> +
> + if (host->ops->set_power)
> + host->ops->set_power(host, ios->power_mode, ios->vdd);
> + else
> + sdhci_set_power(host, ios->power_mode, ios->vdd);
> + udelay(100);
> +
> + host->timing = ios->timing;
> + sdhci_set_clock(host, host->clock);
sdhci_set_ios_common() already called ->set_clock() and ->set_power(), so I
am not really following what is going on here. Can you explain some more?
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> /*****************************************************************************\
> * *
> * MMC callbacks *
> @@ -286,6 +354,37 @@ static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
> return sdhci_start_signal_voltage_switch(mmc, ios);
> }
>
> +void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> +
> + if (!(host->version >= SDHCI_SPEC_400) ||
> + !(host->mmc->flags & MMC_UHS2_SUPPORT &&
> + host->mmc->caps & MMC_CAP_UHS2)) {
> + sdhci_set_ios(mmc, ios);
> + return;
> + }
> +
> + if (ios->power_mode == MMC_POWER_UNDEFINED)
> + return;
> +
> + if (host->flags & SDHCI_DEVICE_DEAD) {
> + if (!IS_ERR(mmc->supply.vmmc) &&
> + ios->power_mode == MMC_POWER_OFF)
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> + if (!IS_ERR_OR_NULL(mmc->supply.vmmc2) &&
> + ios->power_mode == MMC_POWER_OFF)
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
> + return;
> + }
> +
> + /* FIXME: host->timing = ios->timing */
> +
> + sdhci_set_ios_common(mmc, ios);
> +
> + __sdhci_uhs2_set_ios(mmc, ios);
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -296,6 +395,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> {
> host->mmc_host_ops.start_signal_voltage_switch =
> sdhci_uhs2_start_signal_voltage_switch;
> + host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
>
> return 0;
> }
> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> index efe70577bc74..c1ff4ac1ab7a 100644
> --- a/drivers/mmc/host/sdhci-uhs2.h
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -214,5 +214,6 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> unsigned short vdd);
> void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
> +void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
>
> #endif /* __SDHCI_UHS2_H */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 0b741eb546cb..becb228330af 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -48,8 +48,6 @@
> static unsigned int debug_quirks = 0;
> static unsigned int debug_quirks2;
>
> -static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
> -
> static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
>
> void sdhci_dumpregs(struct sdhci_host *host)
> @@ -1836,6 +1834,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
> case MMC_TIMING_MMC_HS400:
> preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
> break;
> + case MMC_TIMING_UHS2:
> + preset = sdhci_readw(host, SDHCI_PRESET_FOR_UHS2);
> + break;
> default:
> pr_warn("%s: Invalid UHS-I mode selected\n",
> mmc_hostname(host->mmc));
> @@ -2249,20 +2250,9 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
> }
> EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
>
> -void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios)
> {
> struct sdhci_host *host = mmc_priv(mmc);
> - u8 ctrl;
> -
> - if (ios->power_mode == MMC_POWER_UNDEFINED)
> - return;
> -
> - if (host->flags & SDHCI_DEVICE_DEAD) {
> - if (!IS_ERR(mmc->supply.vmmc) &&
> - ios->power_mode == MMC_POWER_OFF)
> - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> - return;
> - }
>
> /*
> * Reset the chip on each power off.
> @@ -2299,6 +2289,25 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> host->ops->set_power(host, ios->power_mode, ios->vdd);
> else
> sdhci_set_power(host, ios->power_mode, ios->vdd);
> +}
> +EXPORT_SYMBOL_GPL(sdhci_set_ios_common);
> +
> +void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + u8 ctrl;
> +
> + if (ios->power_mode == MMC_POWER_UNDEFINED)
> + return;
> +
> + if (host->flags & SDHCI_DEVICE_DEAD) {
> + if (!IS_ERR(mmc->supply.vmmc) &&
> + ios->power_mode == MMC_POWER_OFF)
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> + return;
> + }
> +
> + sdhci_set_ios_common(mmc, ios);
>
> if (host->ops->platform_send_init_74_clocks)
> host->ops->platform_send_init_74_clocks(host, ios->power_mode);
> @@ -2869,7 +2878,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
> }
> EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
>
> -static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
> +void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
> {
> /* Host Controller v3.00 defines preset value registers */
> if (host->version < SDHCI_SPEC_300)
> @@ -2897,6 +2906,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
> host->preset_enabled = enable;
> }
> }
> +EXPORT_SYMBOL_GPL(sdhci_enable_preset_value);
>
> static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
> int err)
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 2b5b8295cf92..e84ebddb20d8 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -851,6 +851,8 @@ void sdhci_set_bus_width(struct sdhci_host *host, int width);
> void sdhci_reset(struct sdhci_host *host, u8 mask);
> void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
> int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
> +void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
> +void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios);
> void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
> int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> struct mmc_ios *ios);
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 41 +++++++++++++++++++++++++++++++++++
> 1 file changed, 41 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 994dff967e85..55362ace1857 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -11,6 +11,7 @@
> */
>
> #include <linux/delay.h>
> +#include <linux/ktime.h>
> #include <linux/module.h>
>
> #include "sdhci.h"
> @@ -385,6 +386,42 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> __sdhci_uhs2_set_ios(mmc, ios);
> }
>
> +static void sdhci_uhs2_disable_clk(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +
> + clk &= ~SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +}
> +
> +static void sdhci_uhs2_enable_clk(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + ktime_t timeout;
> +
> + clk |= SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> + /* Wait max 20 ms */
> + timeout = ktime_add_ms(ktime_get(), 20);
> + while (1) {
> + bool timedout = ktime_after(ktime_get(), timeout);
> +
> + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
Can use read_poll_timeout(sdhci_readw,..., host, SDHCI_CLOCK_CONTROL)
> + if (clk & SDHCI_CLOCK_INT_STABLE)
> + break;
> + if (timedout) {
> + pr_err("%s: Internal clock never stabilised.\n",
> + mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return;
> + }
> + udelay(10);
> + }
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -556,6 +593,10 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
>
> if (!host->mmc_host_ops.uhs2_detect_init)
> host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
> + if (!host->mmc_host_ops.uhs2_disable_clk)
> + host->mmc_host_ops.uhs2_disable_clk = sdhci_uhs2_disable_clk;
> + if (!host->mmc_host_ops.uhs2_enable_clk)
> + host->mmc_host_ops.uhs2_enable_clk = sdhci_uhs2_enable_clk;
>
> return 0;
> }
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
> in the following commits to indicate UHS-II specific code in sdhci
> controllers.
This patch needs to be combined with a patch that actually uses the config.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/Kconfig | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 31481c9fcc2e..5ca9ac03db40 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -89,6 +89,15 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
>
> This is the case for the Nintendo Wii SDHCI.
>
> +config MMC_SDHCI_UHS2
> + tristate "UHS2 support on SDHCI controller"
> + depends on MMC_SDHCI
> + help
> + This option is selected by SDHCI controller drivers that want to
> + support UHS2-capable devices.
> +
> + If you have a controller with this feature, say Y or M here.
> +
> config MMC_SDHCI_PCI
> tristate "SDHCI support on PCI bus"
> depends on MMC_SDHCI && PCI
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> Add UHS-II related definitions in shdci.h and sdhci-uhs2.h.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.h | 210 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.h | 73 +++++++++++-
> 2 files changed, 282 insertions(+), 1 deletion(-)
> create mode 100644 drivers/mmc/host/sdhci-uhs2.h
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> new file mode 100644
> index 000000000000..3b157df9c89c
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -0,0 +1,210 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * linux/drivers/mmc/host/sdhci-uhs2.h - Secure Digital Host Controller
> + * Interface driver
> + *
> + * Header file for Host Controller UHS2 related registers and I/O accessors.
> + *
> + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> + */
> +#ifndef __SDHCI_UHS2_H
> +#define __SDHCI_UHS2_H
> +
> +#include <linux/bits.h>
> +
> +/*
> + * UHS-II Controller registers
> + * 0x74 preset in sdhci.h
> + * 0x80
> + * 0x84-0xB4
> + * 0xB8-0xCF
> + * 0xE0-0xE7
> + */
> +/* UHS2 */
> +#define SDHCI_UHS2_BLOCK_SIZE 0x80
> +#define SDHCI_UHS2_MAKE_BLKSZ(dma, blksz) \
> + ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF))
> +
> +#define SDHCI_UHS2_BLOCK_COUNT 0x84
> +
> +#define SDHCI_UHS2_CMD_PACKET 0x88
> +#define SDHCI_UHS2_CMD_PACK_MAX_LEN 20
> +
> +#define SDHCI_UHS2_TRANS_MODE 0x9C
> +#define SDHCI_UHS2_TRNS_DMA BIT(0)
> +#define SDHCI_UHS2_TRNS_BLK_CNT_EN BIT(1)
> +#define SDHCI_UHS2_TRNS_DATA_TRNS_WRT BIT(4)
> +#define SDHCI_UHS2_TRNS_BLK_BYTE_MODE BIT(5)
> +#define SDHCI_UHS2_TRNS_RES_R5 BIT(6)
> +#define SDHCI_UHS2_TRNS_RES_ERR_CHECK_EN BIT(7)
> +#define SDHCI_UHS2_TRNS_RES_INT_DIS BIT(8)
> +#define SDHCI_UHS2_TRNS_WAIT_EBSY BIT(14)
> +#define SDHCI_UHS2_TRNS_2L_HD BIT(15)
> +
> +#define SDHCI_UHS2_COMMAND 0x9E
> +#define SDHCI_UHS2_COMMAND_SUB_CMD 0x0004
> +#define SDHCI_UHS2_COMMAND_DATA 0x0020
> +#define SDHCI_UHS2_COMMAND_TRNS_ABORT 0x0040
> +#define SDHCI_UHS2_COMMAND_CMD12 0x0080
> +#define SDHCI_UHS2_COMMAND_DORMANT 0x00C0
> +#define SDHCI_UHS2_COMMAND_PACK_LEN_MASK GENMASK(12,8)
> +#define SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT 8
> +
> +#define SDHCI_UHS2_RESPONSE 0xA0
> +#define SDHCI_UHS2_RESPONSE_MAX_LEN 20
> +
> +#define SDHCI_UHS2_MSG_SELECT 0xB4
> +#define SDHCI_UHS2_MSG_SELECT_CURR 0x0
> +#define SDHCI_UHS2_MSG_SELECT_ONE 0x1
> +#define SDHCI_UHS2_MSG_SELECT_TWO 0x2
> +#define SDHCI_UHS2_MSG_SELECT_THREE 0x3
> +
> +#define SDHCI_UHS2_MSG 0xB8
> +
> +#define SDHCI_UHS2_DEV_INT_STATUS 0xBC
> +
> +#define SDHCI_UHS2_DEV_SELECT 0xBE
> +#define SDHCI_UHS2_DEV_SELECT_DEV_SEL_MASK GENMASK(3,0)
> +#define SDHCI_UHS2_DEV_SELECT_INT_MSG_EN BIT(7)
> +
> +#define SDHCI_UHS2_DEV_INT_CODE 0xBF
> +
> +#define SDHCI_UHS2_SW_RESET 0xC0
> +#define SDHCI_UHS2_SW_RESET_FULL 0x0001
> +#define SDHCI_UHS2_SW_RESET_SD 0x0002
> +
> +#define SDHCI_UHS2_TIMER_CTRL 0xC2
> +#define SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT 4
> +
> +#define SDHCI_UHS2_ERR_INT_STATUS 0xC4
> +#define SDHCI_UHS2_ERR_INT_STATUS_EN 0xC8
> +#define SDHCI_UHS2_ERR_INT_SIG_EN 0xCC
> +#define SDHCI_UHS2_ERR_INT_STATUS_HEADER BIT(0)
> +#define SDHCI_UHS2_ERR_INT_STATUS_RES BIT(1)
> +#define SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP BIT(2)
> +#define SDHCI_UHS2_ERR_INT_STATUS_CRC BIT(3)
> +#define SDHCI_UHS2_ERR_INT_STATUS_FRAME BIT(4)
> +#define SDHCI_UHS2_ERR_INT_STATUS_TID BIT(5)
> +#define SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER BIT(7)
> +#define SDHCI_UHS2_ERR_INT_STATUS_EBUSY BIT(8)
> +#define SDHCI_UHS2_ERR_INT_STATUS_ADMA BIT(15)
> +#define SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT BIT(16)
> +#define SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT BIT(17)
> +#define SDHCI_UHS2_ERR_INT_STATUS_VENDOR BIT(27)
> +#define SDHCI_UHS2_ERR_INT_STATUS_MASK \
> + (SDHCI_UHS2_ERR_INT_STATUS_HEADER | \
> + SDHCI_UHS2_ERR_INT_STATUS_RES | \
> + SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP | \
> + SDHCI_UHS2_ERR_INT_STATUS_CRC | \
> + SDHCI_UHS2_ERR_INT_STATUS_FRAME | \
> + SDHCI_UHS2_ERR_INT_STATUS_TID | \
> + SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER | \
> + SDHCI_UHS2_ERR_INT_STATUS_EBUSY | \
> + SDHCI_UHS2_ERR_INT_STATUS_ADMA | \
> + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT | \
> + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT)
> +#define SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK \
> + (SDHCI_UHS2_ERR_INT_STATUS_HEADER | \
> + SDHCI_UHS2_ERR_INT_STATUS_RES | \
> + SDHCI_UHS2_ERR_INT_STATUS_FRAME | \
> + SDHCI_UHS2_ERR_INT_STATUS_TID | \
> + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
> +/* CRC Error occurs during a packet receiving */
> +#define SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK \
> + (SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP | \
> + SDHCI_UHS2_ERR_INT_STATUS_CRC | \
> + SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER | \
> + SDHCI_UHS2_ERR_INT_STATUS_EBUSY | \
> + SDHCI_UHS2_ERR_INT_STATUS_ADMA | \
> + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT)
> +
> +#define SDHCI_UHS2_SET_PTR 0xE0
> +#define SDHCI_UHS2_GEN_SET_POWER_LOW 0x0001
> +#define SDHCI_UHS2_GEN_SET_N_LANES_POS 8
> +#define SDHCI_UHS2_GEN_SET_2L_FD_HD 0x0
> +#define SDHCI_UHS2_GEN_SET_2D1U_FD 0x2
> +#define SDHCI_UHS2_GEN_SET_1D2U_FD 0x3
> +#define SDHCI_UHS2_GEN_SET_2D2U_FD 0x4
> +
> +#define SDHCI_UHS2_PHY_SET_SPEED_POS 6
> +#define SDHCI_UHS2_PHY_SET_HIBER_EN BIT(12)
> +#define SDHCI_UHS2_PHY_SET_N_LSS_SYN_MASK GENMASK(19,16)
> +#define SDHCI_UHS2_PHY_SET_N_LSS_SYN_POS 16
> +#define SDHCI_UHS2_PHY_SET_N_LSS_DIR_MASK GENMASK(23,20)
> +#define SDHCI_UHS2_PHY_SET_N_LSS_DIR_POS 20
> +
> +#define SDHCI_UHS2_TRAN_SET_N_FCU_MASK GENMASK(15,8)
> +#define SDHCI_UHS2_TRAN_SET_N_FCU_POS 8
> +#define SDHCI_UHS2_TRAN_SET_RETRY_CNT_MASK GENMASK(17,16)
> +#define SDHCI_UHS2_TRAN_SET_RETRY_CNT_POS 16
> +
> +#define SDHCI_UHS2_TRAN_SET_1_N_DAT_GAP_MASK GENMASK(7,0)
> +
> +#define SDHCI_UHS2_HOST_CAPS_PTR 0xE2
> +#define SDHCI_UHS2_HOST_CAPS_GEN_OFFSET 0
> +#define SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK GENMASK(3,0)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK GENMASK(7,4)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_GAP(gap) ((gap) * 360)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT 4
> +#define SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK GENMASK(13,8)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT 8
> +#define SDHCI_UHS2_HOST_CAPS_GEN_2L_HD_FD 1
> +#define SDHCI_UHS2_HOST_CAPS_GEN_2D1U_FD 2
> +#define SDHCI_UHS2_HOST_CAPS_GEN_1D2U_FD 4
> +#define SDHCI_UHS2_HOST_CAPS_GEN_2D2U_FD 8
> +#define SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64 BIT(14)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BOOT BIT(15)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK GENMASK(17,16)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT 16
> +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_RMV 0
> +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_EMB 1
> +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_EMB_RMV 2
> +#define SDHCI_UHS2_HOST_CAPS_GEN_NUM_DEV_MASK GENMASK(21,18)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_NUM_DEV_SHIFT 18
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_MASK GENMASK(23,22)
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_SHIFT 22
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_P2P 0
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_RING 1
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_HUB 2
> +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_HUB_RING 3
> +
> +#define SDHCI_UHS2_HOST_CAPS_PHY_OFFSET 4
> +#define SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK GENMASK(5,0)
> +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK GENMASK(7,6)
> +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT 6
> +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_A 0
> +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_B 1
> +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK GENMASK(19,16)
> +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT 16
> +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK GENMASK(23,20)
> +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT 20
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET 8
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_LINK_REV_MASK GENMASK(5,0)
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK GENMASK(15,8)
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT 8
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK GENMASK(18,16)
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT 16
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK GENMASK(31,20)
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT 20
> +
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET 12
> +#define SDHCI_UHS2_HOST_CAPS_TRAN_1_N_DATA_GAP_MASK GENMASK(7,0)
> +
> +#define SDHCI_UHS2_TEST_PTR 0xE4
> +#define SDHCI_UHS2_TEST_ERR_HEADER BIT(0)
> +#define SDHCI_UHS2_TEST_ERR_RES BIT(1)
> +#define SDHCI_UHS2_TEST_ERR_RETRY_EXP BIT(2)
> +#define SDHCI_UHS2_TEST_ERR_CRC BIT(3)
> +#define SDHCI_UHS2_TEST_ERR_FRAME BIT(4)
> +#define SDHCI_UHS2_TEST_ERR_TID BIT(5)
> +#define SDHCI_UHS2_TEST_ERR_UNRECOVER BIT(7)
> +#define SDHCI_UHS2_TEST_ERR_EBUSY BIT(8)
> +#define SDHCI_UHS2_TEST_ERR_ADMA BIT(15)
> +#define SDHCI_UHS2_TEST_ERR_RES_TIMEOUT BIT(16)
> +#define SDHCI_UHS2_TEST_ERR_DEADLOCK_TIMEOUT BIT(17)
> +#define SDHCI_UHS2_TEST_ERR_VENDOR BIT(27)
> +
> +#define SDHCI_UHS2_EMBED_CTRL 0xE6
> +#define SDHCI_UHS2_VENDOR 0xE8
> +
> +#endif /* __SDHCI_UHS2_H */
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 0770c036e2ff..d9d7a76cedc1 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -43,8 +43,27 @@
> #define SDHCI_TRNS_READ 0x10
> #define SDHCI_TRNS_MULTI 0x20
>
> +/*
> + * Defined in Host Version 4.10.
> + * 1 - R5 (SDIO)
> + * 0 - R1 (Memory)
> + */
> +#define SDHCI_TRNS_RES_TYPE 0x40
> +#define SDHCI_TRNS_RES_ERR_CHECK 0x80
> +#define SDHCI_TRNS_RES_INT_DIS 0x0100
> +
> #define SDHCI_COMMAND 0x0E
> #define SDHCI_CMD_RESP_MASK 0x03
> +
> +/*
> + * Host Version 4.10 adds this bit to distinguish a main command or
> + * sub command.
> + * CMD53(SDIO) - main command
> + * CMD52(SDIO) - sub command which doesn't have data block or doesn't
> + * indicate busy.
> + */
> +#define SDHCI_CMD_SUB_CMD 0x04
> +
> #define SDHCI_CMD_CRC 0x08
> #define SDHCI_CMD_INDEX 0x10
> #define SDHCI_CMD_DATA 0x20
> @@ -60,11 +79,19 @@
>
> #define SDHCI_RESPONSE 0x10
>
> +#define SDHCI_RESPONSE_CM_TRAN_ABORT_OFFSET 0x10
> +#define SDHCI_RESPONSE_CM_TRAN_ABORT_SIZE 4
> +#define SDHCI_RESPONSE_SD_TRAN_ABORT_OFFSET 0x18
> +#define SDHCI_RESPONSE_SD_TRAN_ABORT_SIZE 8
I would prefer to have all the UHS2 definitions in sdhci-uhs2. If they
belong to an existing register, just put a comment saying what register.
> +
> #define SDHCI_BUFFER 0x20
>
> #define SDHCI_PRESENT_STATE 0x24
> #define SDHCI_CMD_INHIBIT 0x00000001
> #define SDHCI_DATA_INHIBIT 0x00000002
> +
> +#define SDHCI_DATA_HIGH_LVL_MASK 0x000000F0
> +
> #define SDHCI_DOING_WRITE 0x00000100
> #define SDHCI_DOING_READ 0x00000200
> #define SDHCI_SPACE_AVAILABLE 0x00000400
> @@ -80,6 +107,13 @@
> #define SDHCI_DATA_0_LVL_MASK 0x00100000
> #define SDHCI_CMD_LVL 0x01000000
>
> +#define SDHCI_HOST_REGULATOR_STABLE 0x02000000
> +#define SDHCI_CMD_NOT_ISSUE_ERR 0x08000000
> +#define SDHCI_SUB_CMD_STATUS 0x10000000
> +#define SDHCI_UHS2_IN_DORMANT_STATE 0x20000000
> +#define SDHCI_UHS2_LANE_SYNC 0x40000000
> +#define SDHCI_UHS2_IF_DETECT 0x80000000
> +
> #define SDHCI_HOST_CONTROL 0x28
> #define SDHCI_CTRL_LED 0x01
> #define SDHCI_CTRL_4BITBUS 0x02
> @@ -100,6 +134,11 @@
> #define SDHCI_POWER_300 0x0C
> #define SDHCI_POWER_330 0x0E
>
> +/* VDD2 - UHS2 */
> +#define SDHCI_VDD2_POWER_ON 0x10
> +#define SDHCI_VDD2_POWER_180 0xA0
> +#define SDHCI_VDD2_POWER_120 0x80
> +
> #define SDHCI_BLOCK_GAP_CONTROL 0x2A
>
> #define SDHCI_WAKE_UP_CONTROL 0x2B
> @@ -110,7 +149,7 @@
> #define SDHCI_CLOCK_CONTROL 0x2C
> #define SDHCI_DIVIDER_SHIFT 8
> #define SDHCI_DIVIDER_HI_SHIFT 6
> -#define SDHCI_DIV_MASK 0xFF
> +#define SDHCI_DIV_MASK 0xFF
> #define SDHCI_DIV_MASK_LEN 8
> #define SDHCI_DIV_HI_MASK 0x300
> #define SDHCI_PROG_CLOCK_MODE 0x0020
> @@ -139,6 +178,10 @@
> #define SDHCI_INT_CARD_REMOVE 0x00000080
> #define SDHCI_INT_CARD_INT 0x00000100
> #define SDHCI_INT_RETUNE 0x00001000
> +
> +/* Host Version 4.10 */
> +#define SDHCI_INT_FX_EVENT 0x00002000
> +
> #define SDHCI_INT_CQE 0x00004000
> #define SDHCI_INT_ERROR 0x00008000
> #define SDHCI_INT_TIMEOUT 0x00010000
> @@ -152,6 +195,9 @@
> #define SDHCI_INT_AUTO_CMD_ERR 0x01000000
> #define SDHCI_INT_ADMA_ERROR 0x02000000
>
> +/* Host Version 4.0 */
> +#define SDHCI_INT_RESPONSE_ERROR 0x08000000
> +
> #define SDHCI_INT_NORMAL_MASK 0x00007FFF
> #define SDHCI_INT_ERROR_MASK 0xFFFF8000
>
> @@ -178,6 +224,9 @@
> #define SDHCI_AUTO_CMD_END_BIT 0x00000008
> #define SDHCI_AUTO_CMD_INDEX 0x00000010
>
> +/* Host Version 4.10 */
> +#define SDHCI_ACMD_RESPONSE_ERROR 0x0020
> +
> #define SDHCI_HOST_CONTROL2 0x3E
> #define SDHCI_CTRL_UHS_MASK 0x0007
> #define SDHCI_CTRL_UHS_SDR12 0x0000
> @@ -186,6 +235,7 @@
> #define SDHCI_CTRL_UHS_SDR104 0x0003
> #define SDHCI_CTRL_UHS_DDR50 0x0004
> #define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
> +#define SDHCI_CTRL_UHS_2 0x0007 /* UHS-2 */
> #define SDHCI_CTRL_VDD_180 0x0008
> #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
> #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> @@ -194,9 +244,12 @@
> #define SDHCI_CTRL_DRV_TYPE_D 0x0030
> #define SDHCI_CTRL_EXEC_TUNING 0x0040
> #define SDHCI_CTRL_TUNED_CLK 0x0080
> +#define SDHCI_CTRL_UHS2_INTERFACE_EN 0x0100 /* UHS-2 */
> +#define SDHCI_CTRL_ADMA2_LEN_MODE 0x0400
> #define SDHCI_CMD23_ENABLE 0x0800
> #define SDHCI_CTRL_V4_MODE 0x1000
> #define SDHCI_CTRL_64BIT_ADDR 0x2000
> +#define SDHCI_CTRL_ASYNC_INT_EN 0x4000
> #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
>
> #define SDHCI_CAPABILITIES 0x40
> @@ -217,11 +270,13 @@
> #define SDHCI_CAN_VDD_180 0x04000000
> #define SDHCI_CAN_64BIT_V4 0x08000000
> #define SDHCI_CAN_64BIT 0x10000000
> +#define SDHCI_CAN_ASYNC_INT 0x20000000
>
> #define SDHCI_CAPABILITIES_1 0x44
> #define SDHCI_SUPPORT_SDR50 0x00000001
> #define SDHCI_SUPPORT_SDR104 0x00000002
> #define SDHCI_SUPPORT_DDR50 0x00000004
> +#define SDHCI_SUPPORT_UHS2 0x00000008 /* UHS-2 support */
> #define SDHCI_DRIVER_TYPE_A 0x00000010
> #define SDHCI_DRIVER_TYPE_C 0x00000020
> #define SDHCI_DRIVER_TYPE_D 0x00000040
> @@ -230,19 +285,28 @@
> #define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
> #define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
> #define SDHCI_CAN_DO_ADMA3 0x08000000
> +#define SDHCI_SUPPORT_VDD2_180 0x10000000 /* UHS-2 1.8V VDD2 */
> +#define SDHCI_RSVD_FOR_VDD2 0x20000000 /* Rsvd for future VDD2 */
> #define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
>
> #define SDHCI_MAX_CURRENT 0x48
> +#define SDHCI_MAX_CURRENT_1 0x4C
> #define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
> #define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
> #define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
> #define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
> +#define SDHCI_MAX_CURRENT_VDD2_180_MASK GENMASK(7, 0) /* UHS2 */
> #define SDHCI_MAX_CURRENT_MULTIPLIER 4
>
> /* 4C-4F reserved for more max current */
>
> #define SDHCI_SET_ACMD12_ERROR 0x50
> +/* Host Version 4.10 */
> +#define SDHCI_SET_ACMD_RESPONSE_ERROR 0x20
> #define SDHCI_SET_INT_ERROR 0x52
> +/* Host Version 4.10 */
> +#define SDHCI_SET_INT_TUNING_ERROR 0x0400
> +#define SDHCI_SET_INT_RESPONSE_ERROR 0x0800
>
> #define SDHCI_ADMA_ERROR 0x54
>
> @@ -259,10 +323,16 @@
> #define SDHCI_PRESET_FOR_SDR104 0x6C
> #define SDHCI_PRESET_FOR_DDR50 0x6E
> #define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
> +
> +/* TODO: 0x74 is used for UHS2 in 4.10. How about HS400? */
> +/* UHS2 */
> +#define SDHCI_PRESET_FOR_UHS2 0x74
> #define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
> #define SDHCI_PRESET_CLKGEN_SEL BIT(10)
> #define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
>
> +#define SDHCI_ADMA3_ADDRESS 0x78
> +
> #define SDHCI_SLOT_INT_STATUS 0xFC
>
> #define SDHCI_HOST_VERSION 0xFE
> @@ -652,6 +722,7 @@ struct sdhci_ops {
> void (*request_done)(struct sdhci_host *host,
> struct mmc_request *mrq);
> void (*dump_vendor_regs)(struct sdhci_host *host);
> + void (*dump_uhs2_regs)(struct sdhci_host *host);
> };
>
> #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> Sdhci_uhs2_reset() does a UHS-II specific reset operation.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 49 +++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 1 +
> drivers/mmc/host/sdhci.c | 3 ++-
> drivers/mmc/host/sdhci.h | 1 +
> 4 files changed, 53 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 08905ed081fb..e2b9743fe17d 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -10,6 +10,7 @@
> * Author: AKASHI Takahiro <[email protected]>
> */
>
> +#include <linux/delay.h>
> #include <linux/module.h>
>
> #include "sdhci.h"
> @@ -49,6 +50,54 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host)
> }
> EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
>
> +/*****************************************************************************\
> + * *
> + * Low level functions *
> + * *
> +\*****************************************************************************/
> +
> +/**
> + * sdhci_uhs2_reset - invoke SW reset
> + * @host: SDHCI host
> + * @mask: Control mask
> + *
> + * Invoke SW reset, depending on a bit in @mask and wait for completion.
> + */
> +void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
> +{
> + unsigned long timeout;
> +
> + if (!(host->mmc->caps & MMC_CAP_UHS2))
Please make a helper so this can be like:
if (!sdhci_uhs2_mode(host))
The capability will be always present for hosts that support UHS2, but not
all cards support UHS2 mode. I suggest just adding a bool to struct
sdhci_host to keep track of when the host is in UHS2 mode.
> + return;
> +
> + sdhci_writew(host, mask, SDHCI_UHS2_SW_RESET);
> +
> + if (mask & SDHCI_UHS2_SW_RESET_FULL) {
> + host->clock = 0;
> + /* Reset-all turns off SD Bus Power */
> + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
I would prefer not to support any legacy quirks that we do not need right
now. Just be sure to add a comment somewhere listing which quirks are not
supported for UHS2 host controllers.
> + sdhci_runtime_pm_bus_off(host);
> + }
> +
> + /* Wait max 100 ms */
> + timeout = 10000;
> +
> + /* hw clears the bit when it's done */
> + while (sdhci_readw(host, SDHCI_UHS2_SW_RESET) & mask) {
This kind of loop can now be done with read_poll_timeout_atomic(sdhci_readw,
..., host, SDHCI_UHS2_SW_RESET)
> + if (timeout == 0) {
> + pr_err("%s: %s: Reset 0x%x never completed.\n",
> + __func__, mmc_hostname(host->mmc), (int)mask);
> + pr_err("%s: clean reset bit\n",
> + mmc_hostname(host->mmc));
> + sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
> + return;
> + }
> + timeout--;
> + udelay(10);
> + }
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> index b9529d32b58d..7bb7a0d67109 100644
> --- a/drivers/mmc/host/sdhci-uhs2.h
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -210,5 +210,6 @@
> struct sdhci_host;
>
> void sdhci_uhs2_dump_regs(struct sdhci_host *host);
> +void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
>
> #endif /* __SDHCI_UHS2_H */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index d4a57e8c9bb8..af336bdb4305 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -195,13 +195,14 @@ static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
> pm_runtime_get_noresume(host->mmc->parent);
> }
>
> -static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
> +void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
> {
> if (!host->bus_on)
> return;
> host->bus_on = false;
> pm_runtime_put_noidle(host->mmc->parent);
> }
> +EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_off);
>
> void sdhci_reset(struct sdhci_host *host, u8 mask)
> {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index d9d7a76cedc1..b9932423db08 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -831,6 +831,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> __sdhci_read_caps(host, NULL, NULL, NULL);
> }
>
> +void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
> unsigned int *actual_clock);
> void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a UHS-II version of sdhci's set_power operation.
> VDD2, as well as VDD, is handled here.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 80 +++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 2 +
> drivers/mmc/host/sdhci.c | 58 +++++++++++++++----------
> drivers/mmc/host/sdhci.h | 2 +
> 4 files changed, 119 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index e2b9743fe17d..2bf78cc4e9ed 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -98,6 +98,86 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
> }
> EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
>
> +void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> + unsigned short vdd)
This function isn't used, so let's rename it sdhci_uhs2_set_power_noreg and
drop regulator support.
> +{
> + struct mmc_host *mmc = host->mmc;
> + u8 pwr;
> +
> + /* FIXME: check if flags & MMC_UHS2_SUPPORT? */
> + if (!(host->mmc->caps & MMC_CAP_UHS2)) {
As commented in another patch, please use a helper fn
> + sdhci_set_power(host, mode, vdd);
> + return;
> + }
> +
> + if (mode != MMC_POWER_OFF) {
> + pwr = sdhci_get_vdd_value(vdd);
Simpler to open code this esp. as there are only 2 valid values:
switch (1 << vdd) {
case MMC_VDD_29_30..MMC_VDD_30_31:
pwr = SDHCI_POWER_300;
break;
case MMC_VDD_32_33..MMC_VDD_33_34:
pwr = SDHCI_POWER_330;
break;
default:
WARN(1, "%s: Invalid vdd %#x\n",
mmc_hostname(host->mmc), vdd);
break;
}
> + if (!pwr)
> + WARN(1, "%s: Invalid vdd %#x\n",
> + mmc_hostname(host->mmc), vdd);
> +
> + pwr |= SDHCI_VDD2_POWER_180;
> + }
> +
> + if (host->pwr == pwr)
> + return;
> + host ->pwr = pwr;
> +
> + if (pwr == 0) {
> + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
> +
> + if (!IS_ERR(host->mmc->supply.vmmc))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> + if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc2))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
> +
> + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
Please drop support for legacy quirk SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON
> + sdhci_runtime_pm_bus_off(host);
> + } else {
> + if (!IS_ERR(host->mmc->supply.vmmc))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
> + if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc2))
> + /* support 1.8v only for now */
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2,
> + fls(MMC_VDD2_165_195) - 1);
> +
> + /*
> + * Spec says that we should clear the power reg before setting
> + * a new value. Some controllers don't seem to like this though.
> + */
> + if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
Please drop support for legacy quirk here and several cases below. As I
mentioned in another patch, just put a comment somewhere listing what is
not supported for UHS2 host controllers.
> + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
> +
> + /*
> + * At least the Marvell CaFe chip gets confused if we set the
> + * voltage and set turn on power at the same time, so set the
> + * voltage first.
> + */
> + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
> + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> +
> + /* vdd first */
> + pwr |= SDHCI_POWER_ON;
> + sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
> + mdelay(5);
> +
> + pwr |= SDHCI_VDD2_POWER_ON;
> + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> + mdelay(5);
> +
> + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
> + sdhci_runtime_pm_bus_on(host);
> +
> + /*
> + * Some controllers need an extra 10ms delay of 10ms before
> + * they can apply clock after applying power
> + */
> + if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
> + mdelay(10);
> + }
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> index 7bb7a0d67109..3c19d8e44c36 100644
> --- a/drivers/mmc/host/sdhci-uhs2.h
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -211,5 +211,7 @@ struct sdhci_host;
>
> void sdhci_uhs2_dump_regs(struct sdhci_host *host);
> void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> +void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> + unsigned short vdd);
>
> #endif /* __SDHCI_UHS2_H */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index af336bdb4305..0b741eb546cb 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -187,13 +187,14 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)
> sdhci_set_card_detection(host, false);
> }
>
> -static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
> +void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
> {
> if (host->bus_on)
> return;
> host->bus_on = true;
> pm_runtime_get_noresume(host->mmc->parent);
> }
> +EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_on);
>
> void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
> {
> @@ -2017,36 +2018,47 @@ static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode,
> sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
> }
>
> +unsigned short sdhci_get_vdd_value(unsigned short vdd)
> +{
> + u8 pwr;
> +
> + switch (1 << vdd) {
> + case MMC_VDD_165_195:
> + /*
> + * Without a regulator, SDHCI does not support 2.0v
> + * so we only get here if the driver deliberately
> + * added the 2.0v range to ocr_avail. Map it to 1.8v
> + * for the purpose of turning on the power.
> + */
> + case MMC_VDD_20_21:
> + pwr = SDHCI_POWER_180;
> + break;
> + case MMC_VDD_29_30:
> + case MMC_VDD_30_31:
> + pwr = SDHCI_POWER_300;
> + break;
> + case MMC_VDD_32_33:
> + case MMC_VDD_33_34:
> + pwr = SDHCI_POWER_330;
> + break;
> + default:
> + pwr = 0;
> + }
> +
> + return pwr;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_get_vdd_value);
> +
> void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
> unsigned short vdd)
> {
> u8 pwr = 0;
>
> if (mode != MMC_POWER_OFF) {
> - switch (1 << vdd) {
> - case MMC_VDD_165_195:
> - /*
> - * Without a regulator, SDHCI does not support 2.0v
> - * so we only get here if the driver deliberately
> - * added the 2.0v range to ocr_avail. Map it to 1.8v
> - * for the purpose of turning on the power.
> - */
> - case MMC_VDD_20_21:
> - pwr = SDHCI_POWER_180;
> - break;
> - case MMC_VDD_29_30:
> - case MMC_VDD_30_31:
> - pwr = SDHCI_POWER_300;
> - break;
> - case MMC_VDD_32_33:
> - case MMC_VDD_33_34:
> - pwr = SDHCI_POWER_330;
> - break;
> - default:
> + pwr = sdhci_get_vdd_value(vdd);
> + if (!pwr)
> WARN(1, "%s: Invalid vdd %#x\n",
> mmc_hostname(host->mmc), vdd);
> - break;
> - }
> }
>
> if (host->pwr == pwr)
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index b9932423db08..2b5b8295cf92 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -831,6 +831,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> __sdhci_read_caps(host, NULL, NULL, NULL);
> }
>
> +void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
> void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
> unsigned int *actual_clock);
> @@ -841,6 +842,7 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
> void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
> unsigned char mode,
> unsigned short vdd);
> +unsigned short sdhci_get_vdd_value(unsigned short vdd);
> void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
> unsigned short vdd);
> void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
>
On 25/11/20 9:41 am, AKASHI Takahiro wrote:
> Gentle ping;
>
> On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
>> This is an interim snapshot of our next version, v4, for enabling
>> UHS-II on MMC/SD.
>>
>> It is focused on 'sdhci' side to address Adrian's comments regarding
>> "modularising" sdhci-uhs2.c.
>> The whole aim of this version is to get early feedback from Adrian (and
>> others) on this issue. Without any consensus about the code structure,
>
> Any comments so far?
>
Overall, I like this approach of separating UHS2 from legacy sdhci as much
as possible. The only major change, is to drop support for legacy quirks
and features that you do not need. The reason for that, is that there may
be few drivers that end up with UHS-II support (opting instead for SD
Express), so there is no point going to a lot of trouble to support things
that never get used.
From what I have seen that looks like it includes:
- any quirks
- SDHCI LED support
- external DMA support
In this regard, the important thing is to have a comment somewhere that
lists what is not supported.
I have only looked at SDHCI patches so far, and only up to about patch 20,
but maybe that gives you enough to go on for a while.
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a sdhci version of mmc's request operation.
> It covers both UHS-I and UHS-II.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 529 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 93 +++---
> drivers/mmc/host/sdhci.h | 21 ++
> 3 files changed, 610 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index d8afb99a9918..36e52553977a 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -13,6 +13,7 @@
> #include <linux/delay.h>
> #include <linux/ktime.h>
> #include <linux/module.h>
> +#include <linux/mmc/mmc.h>
>
> #include "sdhci.h"
> #include "sdhci-uhs2.h"
> @@ -394,6 +395,27 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
> return 0;
> }
>
> +/* TODO: move them to a header */
> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> +static inline void sdhci_led_activate(struct sdhci_host *host)
> +{
> +}
> +
> +static inline void sdhci_led_deactivate(struct sdhci_host *host)
> +{
> +}
> +#else
> +static inline void sdhci_led_activate(struct sdhci_host *host)
> +{
> + __sdhci_led_activate(host);
> +}
> +
> +static inline void sdhci_led_deactivate(struct sdhci_host *host)
> +{
> + __sdhci_led_deactivate(host);
> +}
> +#endif
Unless you need LED support, let's drop it
> +
> /*****************************************************************************\
> * *
> * MMC callbacks *
> @@ -523,6 +545,512 @@ static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
> return err;
> }
>
> +static bool sdhci_uhs2_send_command(struct sdhci_host *host,
> + struct mmc_command *cmd);
> +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
> + struct mmc_command *cmd,
> + unsigned long flags);
Always order functions so that early function declarations are not needed
> +
> +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + struct mmc_command *cmd;
> + unsigned long flags;
> + bool present;
> +
> + /* FIXME: check more flags? */
> + if (!host->mmc->flags & MMC_UHS2_SUPPORT) {
if (sdhci_uhs2_mode(host))
> + sdhci_request(mmc, mrq);
> + return;
> + }
> +
> + /* Firstly check card presence */
> + present = mmc->ops->get_cd(mmc);
Checking CD here is another legacy hangover. Should be able to check the
bus power instead. i.e. if the card has been removed, the bus power (bit-0
Power Control reg) should get set to 0. That can be checked under spin lock.
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + sdhci_led_activate(host);
Drop support for sdhci LEDs unless yoy are using them
> +
> + if (sdhci_present_error(host, mrq->cmd, present))
> + goto out_finish;
> +
> + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
UHS2 doesn't use CMD23 does it?
> +
> + if (!sdhci_uhs2_send_command_retry(host, cmd, flags))
Don't use sdhci_uhs2_send_command_retry. sdhci_send_command_retry is again
dealing with legacy hangover. The inhibit bits should not be set. If they
are it can be an error immediately. So just use sdhci_uhs2_send_commend()
> + goto out_finish;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + return;
> +
> +out_finish:
> + sdhci_finish_mrq(host, mrq);
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request);
> +
> +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
Should end up being able simply to call sdhci_uhs2_request (or vice versa)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + struct mmc_command *cmd;
> + unsigned long flags;
> + int ret = 0;
> +
> + if (!host->mmc->flags & MMC_UHS2_SUPPORT)
> + return sdhci_request_atomic(mmc, mrq);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (sdhci_present_error(host, mrq->cmd, true)) {
> + sdhci_finish_mrq(host, mrq);
> + goto out_finish;
> + }
> +
> + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
> +
> + /*
> + * The HSQ may send a command in interrupt context without polling
> + * the busy signaling, which means we should return BUSY if controller
> + * has not released inhibit bits to allow HSQ trying to send request
> + * again in non-atomic context. So we should not finish this request
> + * here.
> + */
> + if (!sdhci_uhs2_send_command(host, cmd))
> + ret = -EBUSY;
> + else
> + sdhci_led_activate(host);
> +
> +out_finish:
> + spin_unlock_irqrestore(&host->lock, flags);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
> +
> +/*****************************************************************************\
> + * *
> + * Core functions *
> + * *
> +\*****************************************************************************/
> +
> +static void sdhci_uhs2_prepare_data(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> + struct mmc_data *data = cmd->data;
> +
> + sdhci_initialize_data(host, data);
> +
> + sdhci_prepare_dma(host, data);
> +
> + sdhci_writew(host, data->blksz, SDHCI_UHS2_BLOCK_SIZE);
> + sdhci_writew(host, data->blocks, SDHCI_UHS2_BLOCK_COUNT);
> +}
> +
> +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
> +static void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> + if (!sdhci_external_dma_setup(host, cmd)) {
> + __sdhci_external_dma_prepare_data(host, cmd);
> + } else {
> + sdhci_external_dma_release(host);
> + pr_err("%s: Cannot use external DMA, switch to the DMA/PIO which standard SDHCI provides.\n",
> + mmc_hostname(host->mmc));
> + sdhci_uhs2_prepare_data(host, cmd);
> + }
> +}
> +#else
> +static inline void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> + /* This should never happen */
> + WARN_ON_ONCE(1);
> +}
> +
> +static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> +}
> +#endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
Let's start without support for external DMA - unless you need it
> +
> +static void sdhci_uhs2_finish_data(struct sdhci_host *host)
> +{
> + struct mmc_data *data = host->data;
> +
> + __sdhci_finish_data_common(host);
> +
> + /*
> + * FIXME: Is this condition needed?
> + if (host->mmc->flags & MMC_UHS2_INITIALIZED)
> + */
> + __sdhci_finish_mrq(host, data->mrq);
> +}
> +
> +static void sdhci_uhs2_set_transfer_mode(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> + u16 mode;
> + struct mmc_data *data = cmd->data;
> + u16 arg;
> +
> + if (!data) {
> + /* clear Auto CMD settings for no data CMDs */
> + arg = cmd->uhs2_cmd->arg;
> + if ((((arg & 0xF) << 8) | ((arg >> 8) & 0xFF)) ==
> + UHS2_DEV_CMD_TRANS_ABORT) {
> + mode = 0;
> + } else {
> + mode = sdhci_readw(host, SDHCI_UHS2_TRANS_MODE);
> + if (cmd->opcode == MMC_STOP_TRANSMISSION ||
> + cmd->opcode == MMC_ERASE)
> + mode |= SDHCI_UHS2_TRNS_WAIT_EBSY;
> + else
> + /* send status mode */
> + if (cmd->opcode == MMC_SEND_STATUS)
> + mode = 0;
> + }
> +
> + if (IS_ENABLED(CONFIG_MMC_DEBUG))
> + DBG("UHS2 no data trans mode is 0x%x.\n", mode);
> +
> + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
> + return;
> + }
> +
> + WARN_ON(!host->data);
> +
> + mode = SDHCI_UHS2_TRNS_BLK_CNT_EN | SDHCI_UHS2_TRNS_WAIT_EBSY;
> + if (data->flags & MMC_DATA_WRITE)
> + mode |= SDHCI_UHS2_TRNS_DATA_TRNS_WRT;
> +
> + if (data->blocks == 1 &&
> + data->blksz != 512 &&
> + cmd->opcode != MMC_READ_SINGLE_BLOCK &&
> + cmd->opcode != MMC_WRITE_BLOCK) {
> + mode &= ~SDHCI_UHS2_TRNS_BLK_CNT_EN;
> + mode |= SDHCI_UHS2_TRNS_BLK_BYTE_MODE;
> + }
> +
> + if (host->flags & SDHCI_REQ_USE_DMA)
> + mode |= SDHCI_UHS2_TRNS_DMA;
> +
> + if ((host->mmc->flags & MMC_UHS2_2L_HD) && !cmd->uhs2_tmode0_flag)
> + mode |= SDHCI_UHS2_TRNS_2L_HD;
> +
> + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
> +
> + if (IS_ENABLED(CONFIG_MMC_DEBUG))
> + DBG("UHS2 trans mode is 0x%x.\n", mode);
> +}
> +
> +static void __sdhci_uhs2_send_command(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> + int i, j;
> + int cmd_reg;
> +
> + if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
> + if (!cmd->uhs2_cmd) {
> + pr_err("%s: fatal error, no uhs2_cmd!\n",
> + mmc_hostname(host->mmc));
> + return;
> + }
> + }
> +
> + i = 0;
> + sdhci_writel(host,
> + ((u32)cmd->uhs2_cmd->arg << 16) |
> + (u32)cmd->uhs2_cmd->header,
> + SDHCI_UHS2_CMD_PACKET + i);
> + i += 4;
> +
> + /*
> + * Per spec, playload (config) should be MSB before sending out.
> + * But we don't need convert here because had set payload as
> + * MSB when preparing config read/write commands.
> + */
> + for (j = 0; j < cmd->uhs2_cmd->payload_len / sizeof(u32); j++) {
> + sdhci_writel(host, *(cmd->uhs2_cmd->payload + j),
> + SDHCI_UHS2_CMD_PACKET + i);
> + i += 4;
> + }
> +
> + for ( ; i < SDHCI_UHS2_CMD_PACK_MAX_LEN; i += 4)
> + sdhci_writel(host, 0, SDHCI_UHS2_CMD_PACKET + i);
> +
> + if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
> + DBG("UHS2 CMD packet_len = %d.\n", cmd->uhs2_cmd->packet_len);
> + for (i = 0; i < cmd->uhs2_cmd->packet_len; i++)
> + DBG("UHS2 CMD_PACKET[%d] = 0x%x.\n", i,
> + sdhci_readb(host, SDHCI_UHS2_CMD_PACKET + i));
> + }
> +
> + cmd_reg = cmd->uhs2_cmd->packet_len <<
> + SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT;
> + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
> + cmd_reg |= SDHCI_UHS2_COMMAND_DATA;
> + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> + cmd_reg |= SDHCI_UHS2_COMMAND_CMD12;
> +
> + /* UHS2 Native ABORT */
> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> + ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
> + ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) == UHS2_DEV_CMD_TRANS_ABORT))
> + cmd_reg |= SDHCI_UHS2_COMMAND_TRNS_ABORT;
> +
> + /* UHS2 Native DORMANT */
> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> + ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
> + ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) ==
> + UHS2_DEV_CMD_GO_DORMANT_STATE))
> + cmd_reg |= SDHCI_UHS2_COMMAND_DORMANT;
> +
> + DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
> +
> + sdhci_writew(host, cmd_reg, SDHCI_UHS2_COMMAND);
> +}
> +
> +static bool sdhci_uhs2_send_command(struct sdhci_host *host,
> + struct mmc_command *cmd)
> +{
> + int flags;
> + u32 mask;
> + unsigned long timeout;
> +
> + /* FIXME: Is this check necessary? */
No
> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
> + return sdhci_send_command(host, cmd);
> +
> + WARN_ON(host->cmd);
> +
> + /* Initially, a command has no error */
> + cmd->error = 0;
> +
> + if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
Quirk
> + cmd->opcode == MMC_STOP_TRANSMISSION)
> + cmd->flags |= MMC_RSP_BUSY;
> +
> + mask = SDHCI_CMD_INHIBIT;
> + if (sdhci_data_line_cmd(cmd))
> + mask |= SDHCI_DATA_INHIBIT;
Command inhibit for data is not supported by UHS2
> +
> + /* We shouldn't wait for data inihibit for stop commands, even
> + though they might use busy signaling */
> + if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
> + mask &= ~SDHCI_DATA_INHIBIT;
Ditto
> +
> + if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
> + return false;
> +
> + host->cmd = cmd;
> + host->data_timeout = 0;
> + if (sdhci_data_line_cmd(cmd)) {
> + WARN_ON(host->data_cmd);
> + host->data_cmd = cmd;
> + __sdhci_uhs2_set_timeout(host);
> + }
> +
> + if (cmd->data) {
> + if (host->use_external_dma)
> + sdhci_uhs2_external_dma_prepare_data(host, cmd);
To start with, let's drop support for external DMA with UHS2. i.e. disallow
UHS2 mode if external DMA is enabled
> + else
> + sdhci_uhs2_prepare_data(host, cmd);
> + }
> +
> + sdhci_uhs2_set_transfer_mode(host, cmd);
> +
> + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
> + WARN_ONCE(1, "Unsupported response type!\n");
> + /*
> + * This does not happen in practice because 136-bit response
> + * commands never have busy waiting, so rather than complicate
> + * the error path, just remove busy waiting and continue.
> + */
> + cmd->flags &= ~MMC_RSP_BUSY;
> + }
> +
> + if (!(cmd->flags & MMC_RSP_PRESENT))
> + flags = SDHCI_CMD_RESP_NONE;
> + else if (cmd->flags & MMC_RSP_136)
> + flags = SDHCI_CMD_RESP_LONG;
> + else if (cmd->flags & MMC_RSP_BUSY)
> + flags = SDHCI_CMD_RESP_SHORT_BUSY;
> + else
> + flags = SDHCI_CMD_RESP_SHORT;
> +
> + if (cmd->flags & MMC_RSP_CRC)
> + flags |= SDHCI_CMD_CRC;
> + if (cmd->flags & MMC_RSP_OPCODE)
> + flags |= SDHCI_CMD_INDEX;
> +
> + /* CMD19 is special in that the Data Present Select should be set */
> + if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
> + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
No tuning commands fo UHS2
> + flags |= SDHCI_CMD_DATA;
flags are never used?
> +
> + timeout = jiffies;
> + if (host->data_timeout)
> + timeout += nsecs_to_jiffies(host->data_timeout);
> + else if (!cmd->data && cmd->busy_timeout > 9000)
> + timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
> + else
> + timeout += 10 * HZ;
> + sdhci_mod_timer(host, cmd->mrq, timeout);
> +
> + if (host->use_external_dma)
> + sdhci_external_dma_pre_transfer(host, cmd);
> +
> + __sdhci_uhs2_send_command(host, cmd);
> +
> + return true;
> +}
> +
> +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
> + struct mmc_command *cmd,
> + unsigned long flags)
Can probably get rid of sdhci_uhs2_send_command_retry
> + __releases(host->lock)
> + __acquires(host->lock)
> +{
> + struct mmc_command *deferred_cmd = host->deferred_cmd;
> + int timeout = 10; /* Approx. 10 ms */
> + bool present;
> +
> + while (!sdhci_uhs2_send_command(host, cmd)) {
> + if (!timeout--) {
> + pr_err("%s: Controller never released inhibit bit(s).\n",
> + mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + cmd->error = -EIO;
> + return false;
> + }
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + usleep_range(1000, 1250);
> +
> + present = host->mmc->ops->get_cd(host->mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + /* A deferred command might disappear, handle that */
> + if (cmd == deferred_cmd && cmd != host->deferred_cmd)
> + return true;
> +
> + if (sdhci_present_error(host, cmd, present))
> + return false;
> + }
> +
> + if (cmd == host->deferred_cmd)
> + host->deferred_cmd = NULL;
> +
> + return true;
> +}
> +
> +static void __sdhci_uhs2_finish_command(struct sdhci_host *host)
> +{
> + struct mmc_command *cmd = host->cmd;
> + u8 resp;
> + u8 ecode;
> + bool bReadA0 = 0;
> + int i;
> +
> + if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
> + resp = sdhci_readb(host, SDHCI_UHS2_RESPONSE + 2);
> + if (resp & UHS2_RES_NACK_MASK) {
> + ecode = (resp >> UHS2_RES_ECODE_POS) &
> + UHS2_RES_ECODE_MASK;
> + pr_err("%s: NACK is got, ECODE=0x%x.\n",
> + mmc_hostname(host->mmc), ecode);
> + }
> + bReadA0 = 1;
> + }
> +
> + if (cmd->uhs2_resp &&
> + cmd->uhs2_resp_len && cmd->uhs2_resp_len <= 20) {
> + /* Get whole response of some native CCMD, like
> + * DEVICE_INIT, ENUMERATE.
> + */
> + for (i = 0; i < cmd->uhs2_resp_len; i++)
> + cmd->uhs2_resp[i] =
> + sdhci_readb(host, SDHCI_UHS2_RESPONSE + i);
> + } else {
> + /* Get SD CMD response and Payload for some read
> + * CCMD, like INQUIRY_CFG.
> + */
> + /* Per spec (p136), payload field is divided into
> + * a unit of DWORD and transmission order within
> + * a DWORD is big endian.
> + */
> + if (!bReadA0)
> + sdhci_readl(host, SDHCI_UHS2_RESPONSE);
> + for (i = 4; i < 20; i += 4) {
> + cmd->resp[i / 4 - 1] =
> + (sdhci_readb(host,
> + SDHCI_UHS2_RESPONSE + i) << 24) |
> + (sdhci_readb(host,
> + SDHCI_UHS2_RESPONSE + i + 1)
> + << 16) |
> + (sdhci_readb(host,
> + SDHCI_UHS2_RESPONSE + i + 2)
> + << 8) |
> + sdhci_readb(host, SDHCI_UHS2_RESPONSE + i + 3);
> + }
> + }
> +}
> +
> +static void sdhci_uhs2_finish_command(struct sdhci_host *host)
> +{
> + struct mmc_command *cmd = host->cmd;
> +
> + /* FIXME: Is this check necessary? */
> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT)) {
> + sdhci_finish_command(host);
> + return;
> + }
> +
> + __sdhci_uhs2_finish_command(host);
> +
> + host->cmd = NULL;
> +
> + if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd)
> + mmc_command_done(host->mmc, cmd->mrq);
> +
> + /*
> + * The host can send and interrupt when the busy state has
> + * ended, allowing us to wait without wasting CPU cycles.
> + * The busy signal uses DAT0 so this is similar to waiting
> + * for data to complete.
> + *
> + * Note: The 1.0 specification is a bit ambiguous about this
> + * feature so there might be some problems with older
> + * controllers.
> + */
> + if (cmd->flags & MMC_RSP_BUSY) {
> + if (cmd->data) {
> + DBG("Cannot wait for busy signal when also doing a data transfer");
> + } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
> + cmd == host->data_cmd) {
> + /* Command complete before busy is ended */
> + return;
> + }
> + }
> +
> + /* Finished CMD23, now send actual command. */
UHS2 doesn't use CMD23 does it?
> + if (cmd == cmd->mrq->sbc) {
> + if (!sdhci_uhs2_send_command(host, cmd->mrq->cmd)) {
> + WARN_ON(host->deferred_cmd);
> + host->deferred_cmd = cmd->mrq->cmd;
> + }
> + } else {
> +
> + /* Processed actual command. */
> + if (host->data && host->data_early)
> + sdhci_uhs2_finish_data(host);
> +
> + if (!cmd->data)
> + __sdhci_finish_mrq(host, cmd->mrq);
> + }
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -691,6 +1219,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> host->mmc_host_ops.start_signal_voltage_switch =
> sdhci_uhs2_start_signal_voltage_switch;
> host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
> + host->mmc_host_ops.request = sdhci_uhs2_request;
>
> if (!host->mmc_host_ops.uhs2_detect_init)
> host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index becb228330af..a9f5449bddcc 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -48,8 +48,6 @@
> static unsigned int debug_quirks = 0;
> static unsigned int debug_quirks2;
>
> -static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
> -
> void sdhci_dumpregs(struct sdhci_host *host)
> {
> SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
> @@ -148,10 +146,13 @@ void sdhci_enable_v4_mode(struct sdhci_host *host)
> }
> EXPORT_SYMBOL_GPL(sdhci_enable_v4_mode);
>
> -static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
> +bool sdhci_data_line_cmd(struct mmc_command *cmd)
> {
> return cmd->data || cmd->flags & MMC_RSP_BUSY;
> }
> +EXPORT_SYMBOL_GPL(sdhci_data_line_cmd);
> +
> +/* TODO: move this as an inline function to a header */
>
> static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
> {
> @@ -363,7 +364,7 @@ static void sdhci_reinit(struct sdhci_host *host)
> mmc_detect_change(host->mmc, msecs_to_jiffies(200));
> }
>
> -static void __sdhci_led_activate(struct sdhci_host *host)
> +void __sdhci_led_activate(struct sdhci_host *host)
> {
> u8 ctrl;
>
> @@ -374,8 +375,9 @@ static void __sdhci_led_activate(struct sdhci_host *host)
> ctrl |= SDHCI_CTRL_LED;
> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> }
> +EXPORT_SYMBOL_GPL(__sdhci_led_activate);
>
> -static void __sdhci_led_deactivate(struct sdhci_host *host)
> +void __sdhci_led_deactivate(struct sdhci_host *host)
> {
> u8 ctrl;
>
> @@ -386,6 +388,7 @@ static void __sdhci_led_deactivate(struct sdhci_host *host)
> ctrl &= ~SDHCI_CTRL_LED;
> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> }
> +EXPORT_SYMBOL_GPL(__sdhci_led_deactivate);
>
> #if IS_REACHABLE(CONFIG_LEDS_CLASS)
> static void sdhci_led_control(struct led_classdev *led,
> @@ -464,14 +467,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
>
> #endif
>
> -static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> - unsigned long timeout)
> +void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> + unsigned long timeout)
> {
> if (sdhci_data_line_cmd(mrq->cmd))
> mod_timer(&host->data_timer, timeout);
> else
> mod_timer(&host->timer, timeout);
> }
> +EXPORT_SYMBOL_GPL(sdhci_mod_timer);
>
> static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
> {
> @@ -1043,8 +1047,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
> __sdhci_set_timeout(host, cmd);
> }
>
> -static void sdhci_initialize_data(struct sdhci_host *host,
> - struct mmc_data *data)
> +void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data)
> {
> WARN_ON(host->data);
>
> @@ -1057,6 +1060,7 @@ static void sdhci_initialize_data(struct sdhci_host *host,
> host->data_early = 0;
> host->data->bytes_xfered = 0;
> }
> +EXPORT_SYMBOL_GPL(sdhci_initialize_data);
>
> static inline void sdhci_set_block_info(struct sdhci_host *host,
> struct mmc_data *data)
> @@ -1079,12 +1083,8 @@ static inline void sdhci_set_block_info(struct sdhci_host *host,
> }
> }
>
> -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
> +void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data)
> {
> - struct mmc_data *data = cmd->data;
> -
> - sdhci_initialize_data(host, data);
> -
> if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> struct scatterlist *sg;
> unsigned int length_mask, offset_mask;
> @@ -1169,6 +1169,16 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
> }
>
> sdhci_set_transfer_irqs(host);
> +}
> +EXPORT_SYMBOL_GPL(sdhci_prepare_dma);
> +
> +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
> +{
> + struct mmc_data *data = cmd->data;
> +
> + sdhci_initialize_data(host, data);
> +
> + sdhci_prepare_dma(host, data);
>
> sdhci_set_block_info(host, data);
> }
> @@ -1211,8 +1221,7 @@ static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
> }
>
> -static int sdhci_external_dma_setup(struct sdhci_host *host,
> - struct mmc_command *cmd)
> +int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
> {
> int ret, i;
> enum dma_transfer_direction dir;
> @@ -1264,8 +1273,9 @@ static int sdhci_external_dma_setup(struct sdhci_host *host,
>
> return ret;
> }
> +EXPORT_SYMBOL_GPL(sdhci_external_dma_setup);
>
> -static void sdhci_external_dma_release(struct sdhci_host *host)
> +void sdhci_external_dma_release(struct sdhci_host *host)
> {
> if (host->tx_chan) {
> dma_release_channel(host->tx_chan);
> @@ -1279,9 +1289,10 @@ static void sdhci_external_dma_release(struct sdhci_host *host)
>
> sdhci_switch_external_dma(host, false);
> }
> +EXPORT_SYMBOL_GPL(sdhci_external_dma_release);
>
> -static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> - struct mmc_command *cmd)
> +void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> + struct mmc_command *cmd)
> {
> struct mmc_data *data = cmd->data;
>
> @@ -1292,6 +1303,7 @@ static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
>
> sdhci_set_block_info(host, data);
> }
> +EXPORT_SYMBOL(__sdhci_external_dma_prepare_data);
>
> static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
> struct mmc_command *cmd)
> @@ -1306,8 +1318,8 @@ static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
> }
> }
>
> -static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> - struct mmc_command *cmd)
> +void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> + struct mmc_command *cmd)
> {
> struct dma_chan *chan;
>
> @@ -1318,6 +1330,7 @@ static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> if (chan)
> dma_async_issue_pending(chan);
> }
> +EXPORT_SYMBOL_GPL(sdhci_external_dma_pre_transfer);
>
> #else
>
> @@ -1369,11 +1382,11 @@ static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
> return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
> }
>
> -static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
> - struct mmc_request *mrq)
> +bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq)
> {
> return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
> }
> +EXPORT_SYMBOL_GPL(sdhci_manual_cmd23);
>
> static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
> struct mmc_command *cmd,
> @@ -1483,7 +1496,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> WARN_ON(i >= SDHCI_MAX_MRQS);
> }
>
> -static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> +void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> {
> if (host->cmd && host->cmd->mrq == mrq)
> host->cmd = NULL;
> @@ -1507,15 +1520,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> if (!sdhci_has_requests(host))
> sdhci_led_deactivate(host);
> }
> +EXPORT_SYMBOL_GPL(__sdhci_finish_mrq);
>
> -static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> +void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> {
> __sdhci_finish_mrq(host, mrq);
>
> queue_work(host->complete_wq, &host->complete_work);
> }
> +EXPORT_SYMBOL_GPL(sdhci_finish_mrq);
>
> -static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> +void __sdhci_finish_data_common(struct sdhci_host *host)
> {
> struct mmc_command *data_cmd = host->data_cmd;
> struct mmc_data *data = host->data;
> @@ -1548,6 +1563,14 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> data->bytes_xfered = 0;
> else
> data->bytes_xfered = data->blksz * data->blocks;
> +}
> +EXPORT_SYMBOL_GPL(__sdhci_finish_data_common);
> +
> +static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> +{
> + struct mmc_data *data = host->data;
> +
> + __sdhci_finish_data_common(host);
>
> /*
> * Need to send CMD12 if -
> @@ -1586,12 +1609,13 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> }
> }
>
> -static void sdhci_finish_data(struct sdhci_host *host)
> +void sdhci_finish_data(struct sdhci_host *host)
> {
> __sdhci_finish_data(host, false);
> }
> +EXPORT_SYMBOL_GPL(sdhci_finish_data);
>
> -static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> +bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> {
> int flags;
> u32 mask;
> @@ -1633,8 +1657,6 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> sdhci_prepare_data(host, cmd);
> }
>
> - sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
> -
> sdhci_set_transfer_mode(host, cmd);
>
> if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
> @@ -1678,13 +1700,16 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> if (host->use_external_dma)
> sdhci_external_dma_pre_transfer(host, cmd);
>
> + sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
> +
> sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
>
> return true;
> }
> +EXPORT_SYMBOL_GPL(sdhci_send_command);
>
> -static bool sdhci_present_error(struct sdhci_host *host,
> - struct mmc_command *cmd, bool present)
> +bool sdhci_present_error(struct sdhci_host *host,
> + struct mmc_command *cmd, bool present)
> {
> if (!present || host->flags & SDHCI_DEVICE_DEAD) {
> cmd->error = -ENOMEDIUM;
> @@ -1693,6 +1718,7 @@ static bool sdhci_present_error(struct sdhci_host *host,
>
> return false;
> }
> +EXPORT_SYMBOL_GPL(sdhci_present_error);
>
> static bool sdhci_send_command_retry(struct sdhci_host *host,
> struct mmc_command *cmd,
> @@ -1755,7 +1781,7 @@ static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
> }
> }
>
> -static void sdhci_finish_command(struct sdhci_host *host)
> +void sdhci_finish_command(struct sdhci_host *host)
> {
> struct mmc_command *cmd = host->cmd;
>
> @@ -1808,6 +1834,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
> __sdhci_finish_mrq(host, cmd->mrq);
> }
> }
> +EXPORT_SYMBOL_GPL(sdhci_finish_command);
>
> static u16 sdhci_get_preset_value(struct sdhci_host *host)
> {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index e84ebddb20d8..6eeb74741da3 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -831,8 +831,29 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> __sdhci_read_caps(host, NULL, NULL, NULL);
> }
>
> +bool sdhci_data_line_cmd(struct mmc_command *cmd);
> void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
> void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> +void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> + unsigned long timeout);
> +void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
> +void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data);
> +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
> +int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd);
> +void sdhci_external_dma_release(struct sdhci_host *host);
> +void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> + struct mmc_command *cmd);
> +void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> + struct mmc_command *cmd);
> +#endif
> +bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
> +void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> +void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> +void __sdhci_finish_data_common(struct sdhci_host *host);
> +bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
> +void sdhci_finish_command(struct sdhci_host *host);
> +bool sdhci_present_error(struct sdhci_host *host,
> + struct mmc_command *cmd, bool present);
> u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
> unsigned int *actual_clock);
> void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
>
On Thu, Nov 26, 2020 at 10:14:36AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
> > in the following commits to indicate UHS-II specific code in sdhci
> > controllers.
>
> This patch needs to be combined with a patch that actually uses the config.
Earlier is better. So the patch #10/27 would be the best.
-Takahiro Akashi
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/Kconfig | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> > index 31481c9fcc2e..5ca9ac03db40 100644
> > --- a/drivers/mmc/host/Kconfig
> > +++ b/drivers/mmc/host/Kconfig
> > @@ -89,6 +89,15 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
> >
> > This is the case for the Nintendo Wii SDHCI.
> >
> > +config MMC_SDHCI_UHS2
> > + tristate "UHS2 support on SDHCI controller"
> > + depends on MMC_SDHCI
> > + help
> > + This option is selected by SDHCI controller drivers that want to
> > + support UHS2-capable devices.
> > +
> > + If you have a controller with this feature, say Y or M here.
> > +
> > config MMC_SDHCI_PCI
> > tristate "SDHCI support on PCI bus"
> > depends on MMC_SDHCI && PCI
> >
>
On Thu, Nov 26, 2020 at 10:15:29AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > Add UHS-II related definitions in shdci.h and sdhci-uhs2.h.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.h | 210 ++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.h | 73 +++++++++++-
> > 2 files changed, 282 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/mmc/host/sdhci-uhs2.h
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> > new file mode 100644
> > index 000000000000..3b157df9c89c
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -0,0 +1,210 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * linux/drivers/mmc/host/sdhci-uhs2.h - Secure Digital Host Controller
> > + * Interface driver
> > + *
> > + * Header file for Host Controller UHS2 related registers and I/O accessors.
> > + *
> > + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> > + */
> > +#ifndef __SDHCI_UHS2_H
> > +#define __SDHCI_UHS2_H
> > +
> > +#include <linux/bits.h>
> > +
> > +/*
> > + * UHS-II Controller registers
> > + * 0x74 preset in sdhci.h
> > + * 0x80
> > + * 0x84-0xB4
> > + * 0xB8-0xCF
> > + * 0xE0-0xE7
> > + */
> > +/* UHS2 */
> > +#define SDHCI_UHS2_BLOCK_SIZE 0x80
> > +#define SDHCI_UHS2_MAKE_BLKSZ(dma, blksz) \
> > + ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF))
> > +
> > +#define SDHCI_UHS2_BLOCK_COUNT 0x84
> > +
> > +#define SDHCI_UHS2_CMD_PACKET 0x88
> > +#define SDHCI_UHS2_CMD_PACK_MAX_LEN 20
> > +
> > +#define SDHCI_UHS2_TRANS_MODE 0x9C
> > +#define SDHCI_UHS2_TRNS_DMA BIT(0)
> > +#define SDHCI_UHS2_TRNS_BLK_CNT_EN BIT(1)
> > +#define SDHCI_UHS2_TRNS_DATA_TRNS_WRT BIT(4)
> > +#define SDHCI_UHS2_TRNS_BLK_BYTE_MODE BIT(5)
> > +#define SDHCI_UHS2_TRNS_RES_R5 BIT(6)
> > +#define SDHCI_UHS2_TRNS_RES_ERR_CHECK_EN BIT(7)
> > +#define SDHCI_UHS2_TRNS_RES_INT_DIS BIT(8)
> > +#define SDHCI_UHS2_TRNS_WAIT_EBSY BIT(14)
> > +#define SDHCI_UHS2_TRNS_2L_HD BIT(15)
> > +
> > +#define SDHCI_UHS2_COMMAND 0x9E
> > +#define SDHCI_UHS2_COMMAND_SUB_CMD 0x0004
> > +#define SDHCI_UHS2_COMMAND_DATA 0x0020
> > +#define SDHCI_UHS2_COMMAND_TRNS_ABORT 0x0040
> > +#define SDHCI_UHS2_COMMAND_CMD12 0x0080
> > +#define SDHCI_UHS2_COMMAND_DORMANT 0x00C0
> > +#define SDHCI_UHS2_COMMAND_PACK_LEN_MASK GENMASK(12,8)
> > +#define SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT 8
> > +
> > +#define SDHCI_UHS2_RESPONSE 0xA0
> > +#define SDHCI_UHS2_RESPONSE_MAX_LEN 20
> > +
> > +#define SDHCI_UHS2_MSG_SELECT 0xB4
> > +#define SDHCI_UHS2_MSG_SELECT_CURR 0x0
> > +#define SDHCI_UHS2_MSG_SELECT_ONE 0x1
> > +#define SDHCI_UHS2_MSG_SELECT_TWO 0x2
> > +#define SDHCI_UHS2_MSG_SELECT_THREE 0x3
> > +
> > +#define SDHCI_UHS2_MSG 0xB8
> > +
> > +#define SDHCI_UHS2_DEV_INT_STATUS 0xBC
> > +
> > +#define SDHCI_UHS2_DEV_SELECT 0xBE
> > +#define SDHCI_UHS2_DEV_SELECT_DEV_SEL_MASK GENMASK(3,0)
> > +#define SDHCI_UHS2_DEV_SELECT_INT_MSG_EN BIT(7)
> > +
> > +#define SDHCI_UHS2_DEV_INT_CODE 0xBF
> > +
> > +#define SDHCI_UHS2_SW_RESET 0xC0
> > +#define SDHCI_UHS2_SW_RESET_FULL 0x0001
> > +#define SDHCI_UHS2_SW_RESET_SD 0x0002
> > +
> > +#define SDHCI_UHS2_TIMER_CTRL 0xC2
> > +#define SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT 4
> > +
> > +#define SDHCI_UHS2_ERR_INT_STATUS 0xC4
> > +#define SDHCI_UHS2_ERR_INT_STATUS_EN 0xC8
> > +#define SDHCI_UHS2_ERR_INT_SIG_EN 0xCC
> > +#define SDHCI_UHS2_ERR_INT_STATUS_HEADER BIT(0)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_RES BIT(1)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP BIT(2)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_CRC BIT(3)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_FRAME BIT(4)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_TID BIT(5)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER BIT(7)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_EBUSY BIT(8)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_ADMA BIT(15)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT BIT(16)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT BIT(17)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_VENDOR BIT(27)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_MASK \
> > + (SDHCI_UHS2_ERR_INT_STATUS_HEADER | \
> > + SDHCI_UHS2_ERR_INT_STATUS_RES | \
> > + SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP | \
> > + SDHCI_UHS2_ERR_INT_STATUS_CRC | \
> > + SDHCI_UHS2_ERR_INT_STATUS_FRAME | \
> > + SDHCI_UHS2_ERR_INT_STATUS_TID | \
> > + SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER | \
> > + SDHCI_UHS2_ERR_INT_STATUS_EBUSY | \
> > + SDHCI_UHS2_ERR_INT_STATUS_ADMA | \
> > + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT | \
> > + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT)
> > +#define SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK \
> > + (SDHCI_UHS2_ERR_INT_STATUS_HEADER | \
> > + SDHCI_UHS2_ERR_INT_STATUS_RES | \
> > + SDHCI_UHS2_ERR_INT_STATUS_FRAME | \
> > + SDHCI_UHS2_ERR_INT_STATUS_TID | \
> > + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
> > +/* CRC Error occurs during a packet receiving */
> > +#define SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK \
> > + (SDHCI_UHS2_ERR_INT_STATUS_RETRY_EXP | \
> > + SDHCI_UHS2_ERR_INT_STATUS_CRC | \
> > + SDHCI_UHS2_ERR_INT_STATUS_UNRECOVER | \
> > + SDHCI_UHS2_ERR_INT_STATUS_EBUSY | \
> > + SDHCI_UHS2_ERR_INT_STATUS_ADMA | \
> > + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT)
> > +
> > +#define SDHCI_UHS2_SET_PTR 0xE0
> > +#define SDHCI_UHS2_GEN_SET_POWER_LOW 0x0001
> > +#define SDHCI_UHS2_GEN_SET_N_LANES_POS 8
> > +#define SDHCI_UHS2_GEN_SET_2L_FD_HD 0x0
> > +#define SDHCI_UHS2_GEN_SET_2D1U_FD 0x2
> > +#define SDHCI_UHS2_GEN_SET_1D2U_FD 0x3
> > +#define SDHCI_UHS2_GEN_SET_2D2U_FD 0x4
> > +
> > +#define SDHCI_UHS2_PHY_SET_SPEED_POS 6
> > +#define SDHCI_UHS2_PHY_SET_HIBER_EN BIT(12)
> > +#define SDHCI_UHS2_PHY_SET_N_LSS_SYN_MASK GENMASK(19,16)
> > +#define SDHCI_UHS2_PHY_SET_N_LSS_SYN_POS 16
> > +#define SDHCI_UHS2_PHY_SET_N_LSS_DIR_MASK GENMASK(23,20)
> > +#define SDHCI_UHS2_PHY_SET_N_LSS_DIR_POS 20
> > +
> > +#define SDHCI_UHS2_TRAN_SET_N_FCU_MASK GENMASK(15,8)
> > +#define SDHCI_UHS2_TRAN_SET_N_FCU_POS 8
> > +#define SDHCI_UHS2_TRAN_SET_RETRY_CNT_MASK GENMASK(17,16)
> > +#define SDHCI_UHS2_TRAN_SET_RETRY_CNT_POS 16
> > +
> > +#define SDHCI_UHS2_TRAN_SET_1_N_DAT_GAP_MASK GENMASK(7,0)
> > +
> > +#define SDHCI_UHS2_HOST_CAPS_PTR 0xE2
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_OFFSET 0
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK GENMASK(3,0)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK GENMASK(7,4)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_GAP(gap) ((gap) * 360)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT 4
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK GENMASK(13,8)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT 8
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_2L_HD_FD 1
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_2D1U_FD 2
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_1D2U_FD 4
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_2D2U_FD 8
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64 BIT(14)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BOOT BIT(15)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK GENMASK(17,16)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT 16
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_RMV 0
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_EMB 1
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_EMB_RMV 2
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_NUM_DEV_MASK GENMASK(21,18)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_NUM_DEV_SHIFT 18
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_MASK GENMASK(23,22)
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_SHIFT 22
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_P2P 0
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_RING 1
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_HUB 2
> > +#define SDHCI_UHS2_HOST_CAPS_GEN_BUS_TOPO_HUB_RING 3
> > +
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_OFFSET 4
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK GENMASK(5,0)
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK GENMASK(7,6)
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT 6
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_A 0
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_RANGE_B 1
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK GENMASK(19,16)
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT 16
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK GENMASK(23,20)
> > +#define SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT 20
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET 8
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_LINK_REV_MASK GENMASK(5,0)
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK GENMASK(15,8)
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT 8
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK GENMASK(18,16)
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT 16
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK GENMASK(31,20)
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT 20
> > +
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET 12
> > +#define SDHCI_UHS2_HOST_CAPS_TRAN_1_N_DATA_GAP_MASK GENMASK(7,0)
> > +
> > +#define SDHCI_UHS2_TEST_PTR 0xE4
> > +#define SDHCI_UHS2_TEST_ERR_HEADER BIT(0)
> > +#define SDHCI_UHS2_TEST_ERR_RES BIT(1)
> > +#define SDHCI_UHS2_TEST_ERR_RETRY_EXP BIT(2)
> > +#define SDHCI_UHS2_TEST_ERR_CRC BIT(3)
> > +#define SDHCI_UHS2_TEST_ERR_FRAME BIT(4)
> > +#define SDHCI_UHS2_TEST_ERR_TID BIT(5)
> > +#define SDHCI_UHS2_TEST_ERR_UNRECOVER BIT(7)
> > +#define SDHCI_UHS2_TEST_ERR_EBUSY BIT(8)
> > +#define SDHCI_UHS2_TEST_ERR_ADMA BIT(15)
> > +#define SDHCI_UHS2_TEST_ERR_RES_TIMEOUT BIT(16)
> > +#define SDHCI_UHS2_TEST_ERR_DEADLOCK_TIMEOUT BIT(17)
> > +#define SDHCI_UHS2_TEST_ERR_VENDOR BIT(27)
> > +
> > +#define SDHCI_UHS2_EMBED_CTRL 0xE6
> > +#define SDHCI_UHS2_VENDOR 0xE8
> > +
> > +#endif /* __SDHCI_UHS2_H */
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 0770c036e2ff..d9d7a76cedc1 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -43,8 +43,27 @@
> > #define SDHCI_TRNS_READ 0x10
> > #define SDHCI_TRNS_MULTI 0x20
> >
> > +/*
> > + * Defined in Host Version 4.10.
> > + * 1 - R5 (SDIO)
> > + * 0 - R1 (Memory)
> > + */
> > +#define SDHCI_TRNS_RES_TYPE 0x40
> > +#define SDHCI_TRNS_RES_ERR_CHECK 0x80
> > +#define SDHCI_TRNS_RES_INT_DIS 0x0100
> > +
> > #define SDHCI_COMMAND 0x0E
> > #define SDHCI_CMD_RESP_MASK 0x03
> > +
> > +/*
> > + * Host Version 4.10 adds this bit to distinguish a main command or
> > + * sub command.
> > + * CMD53(SDIO) - main command
> > + * CMD52(SDIO) - sub command which doesn't have data block or doesn't
> > + * indicate busy.
> > + */
> > +#define SDHCI_CMD_SUB_CMD 0x04
> > +
> > #define SDHCI_CMD_CRC 0x08
> > #define SDHCI_CMD_INDEX 0x10
> > #define SDHCI_CMD_DATA 0x20
> > @@ -60,11 +79,19 @@
> >
> > #define SDHCI_RESPONSE 0x10
> >
> > +#define SDHCI_RESPONSE_CM_TRAN_ABORT_OFFSET 0x10
> > +#define SDHCI_RESPONSE_CM_TRAN_ABORT_SIZE 4
> > +#define SDHCI_RESPONSE_SD_TRAN_ABORT_OFFSET 0x18
> > +#define SDHCI_RESPONSE_SD_TRAN_ABORT_SIZE 8
>
> I would prefer to have all the UHS2 definitions in sdhci-uhs2. If they
> belong to an existing register, just put a comment saying what register.
I would disagree, but if you really want, I'd defer to you.
-Takahiro Akashi
>
> > +
> > #define SDHCI_BUFFER 0x20
> >
> > #define SDHCI_PRESENT_STATE 0x24
> > #define SDHCI_CMD_INHIBIT 0x00000001
> > #define SDHCI_DATA_INHIBIT 0x00000002
> > +
> > +#define SDHCI_DATA_HIGH_LVL_MASK 0x000000F0
> > +
> > #define SDHCI_DOING_WRITE 0x00000100
> > #define SDHCI_DOING_READ 0x00000200
> > #define SDHCI_SPACE_AVAILABLE 0x00000400
> > @@ -80,6 +107,13 @@
> > #define SDHCI_DATA_0_LVL_MASK 0x00100000
> > #define SDHCI_CMD_LVL 0x01000000
> >
> > +#define SDHCI_HOST_REGULATOR_STABLE 0x02000000
> > +#define SDHCI_CMD_NOT_ISSUE_ERR 0x08000000
> > +#define SDHCI_SUB_CMD_STATUS 0x10000000
> > +#define SDHCI_UHS2_IN_DORMANT_STATE 0x20000000
> > +#define SDHCI_UHS2_LANE_SYNC 0x40000000
> > +#define SDHCI_UHS2_IF_DETECT 0x80000000
> > +
> > #define SDHCI_HOST_CONTROL 0x28
> > #define SDHCI_CTRL_LED 0x01
> > #define SDHCI_CTRL_4BITBUS 0x02
> > @@ -100,6 +134,11 @@
> > #define SDHCI_POWER_300 0x0C
> > #define SDHCI_POWER_330 0x0E
> >
> > +/* VDD2 - UHS2 */
> > +#define SDHCI_VDD2_POWER_ON 0x10
> > +#define SDHCI_VDD2_POWER_180 0xA0
> > +#define SDHCI_VDD2_POWER_120 0x80
> > +
> > #define SDHCI_BLOCK_GAP_CONTROL 0x2A
> >
> > #define SDHCI_WAKE_UP_CONTROL 0x2B
> > @@ -110,7 +149,7 @@
> > #define SDHCI_CLOCK_CONTROL 0x2C
> > #define SDHCI_DIVIDER_SHIFT 8
> > #define SDHCI_DIVIDER_HI_SHIFT 6
> > -#define SDHCI_DIV_MASK 0xFF
> > +#define SDHCI_DIV_MASK 0xFF
> > #define SDHCI_DIV_MASK_LEN 8
> > #define SDHCI_DIV_HI_MASK 0x300
> > #define SDHCI_PROG_CLOCK_MODE 0x0020
> > @@ -139,6 +178,10 @@
> > #define SDHCI_INT_CARD_REMOVE 0x00000080
> > #define SDHCI_INT_CARD_INT 0x00000100
> > #define SDHCI_INT_RETUNE 0x00001000
> > +
> > +/* Host Version 4.10 */
> > +#define SDHCI_INT_FX_EVENT 0x00002000
> > +
> > #define SDHCI_INT_CQE 0x00004000
> > #define SDHCI_INT_ERROR 0x00008000
> > #define SDHCI_INT_TIMEOUT 0x00010000
> > @@ -152,6 +195,9 @@
> > #define SDHCI_INT_AUTO_CMD_ERR 0x01000000
> > #define SDHCI_INT_ADMA_ERROR 0x02000000
> >
> > +/* Host Version 4.0 */
> > +#define SDHCI_INT_RESPONSE_ERROR 0x08000000
> > +
> > #define SDHCI_INT_NORMAL_MASK 0x00007FFF
> > #define SDHCI_INT_ERROR_MASK 0xFFFF8000
> >
> > @@ -178,6 +224,9 @@
> > #define SDHCI_AUTO_CMD_END_BIT 0x00000008
> > #define SDHCI_AUTO_CMD_INDEX 0x00000010
> >
> > +/* Host Version 4.10 */
> > +#define SDHCI_ACMD_RESPONSE_ERROR 0x0020
> > +
> > #define SDHCI_HOST_CONTROL2 0x3E
> > #define SDHCI_CTRL_UHS_MASK 0x0007
> > #define SDHCI_CTRL_UHS_SDR12 0x0000
> > @@ -186,6 +235,7 @@
> > #define SDHCI_CTRL_UHS_SDR104 0x0003
> > #define SDHCI_CTRL_UHS_DDR50 0x0004
> > #define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
> > +#define SDHCI_CTRL_UHS_2 0x0007 /* UHS-2 */
> > #define SDHCI_CTRL_VDD_180 0x0008
> > #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
> > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > @@ -194,9 +244,12 @@
> > #define SDHCI_CTRL_DRV_TYPE_D 0x0030
> > #define SDHCI_CTRL_EXEC_TUNING 0x0040
> > #define SDHCI_CTRL_TUNED_CLK 0x0080
> > +#define SDHCI_CTRL_UHS2_INTERFACE_EN 0x0100 /* UHS-2 */
> > +#define SDHCI_CTRL_ADMA2_LEN_MODE 0x0400
> > #define SDHCI_CMD23_ENABLE 0x0800
> > #define SDHCI_CTRL_V4_MODE 0x1000
> > #define SDHCI_CTRL_64BIT_ADDR 0x2000
> > +#define SDHCI_CTRL_ASYNC_INT_EN 0x4000
> > #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
> >
> > #define SDHCI_CAPABILITIES 0x40
> > @@ -217,11 +270,13 @@
> > #define SDHCI_CAN_VDD_180 0x04000000
> > #define SDHCI_CAN_64BIT_V4 0x08000000
> > #define SDHCI_CAN_64BIT 0x10000000
> > +#define SDHCI_CAN_ASYNC_INT 0x20000000
> >
> > #define SDHCI_CAPABILITIES_1 0x44
> > #define SDHCI_SUPPORT_SDR50 0x00000001
> > #define SDHCI_SUPPORT_SDR104 0x00000002
> > #define SDHCI_SUPPORT_DDR50 0x00000004
> > +#define SDHCI_SUPPORT_UHS2 0x00000008 /* UHS-2 support */
> > #define SDHCI_DRIVER_TYPE_A 0x00000010
> > #define SDHCI_DRIVER_TYPE_C 0x00000020
> > #define SDHCI_DRIVER_TYPE_D 0x00000040
> > @@ -230,19 +285,28 @@
> > #define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
> > #define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
> > #define SDHCI_CAN_DO_ADMA3 0x08000000
> > +#define SDHCI_SUPPORT_VDD2_180 0x10000000 /* UHS-2 1.8V VDD2 */
> > +#define SDHCI_RSVD_FOR_VDD2 0x20000000 /* Rsvd for future VDD2 */
> > #define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
> >
> > #define SDHCI_MAX_CURRENT 0x48
> > +#define SDHCI_MAX_CURRENT_1 0x4C
> > #define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
> > #define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
> > #define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
> > #define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
> > +#define SDHCI_MAX_CURRENT_VDD2_180_MASK GENMASK(7, 0) /* UHS2 */
> > #define SDHCI_MAX_CURRENT_MULTIPLIER 4
> >
> > /* 4C-4F reserved for more max current */
> >
> > #define SDHCI_SET_ACMD12_ERROR 0x50
> > +/* Host Version 4.10 */
> > +#define SDHCI_SET_ACMD_RESPONSE_ERROR 0x20
> > #define SDHCI_SET_INT_ERROR 0x52
> > +/* Host Version 4.10 */
> > +#define SDHCI_SET_INT_TUNING_ERROR 0x0400
> > +#define SDHCI_SET_INT_RESPONSE_ERROR 0x0800
> >
> > #define SDHCI_ADMA_ERROR 0x54
> >
> > @@ -259,10 +323,16 @@
> > #define SDHCI_PRESET_FOR_SDR104 0x6C
> > #define SDHCI_PRESET_FOR_DDR50 0x6E
> > #define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
> > +
> > +/* TODO: 0x74 is used for UHS2 in 4.10. How about HS400? */
> > +/* UHS2 */
> > +#define SDHCI_PRESET_FOR_UHS2 0x74
> > #define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
> > #define SDHCI_PRESET_CLKGEN_SEL BIT(10)
> > #define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
> >
> > +#define SDHCI_ADMA3_ADDRESS 0x78
> > +
> > #define SDHCI_SLOT_INT_STATUS 0xFC
> >
> > #define SDHCI_HOST_VERSION 0xFE
> > @@ -652,6 +722,7 @@ struct sdhci_ops {
> > void (*request_done)(struct sdhci_host *host,
> > struct mmc_request *mrq);
> > void (*dump_vendor_regs)(struct sdhci_host *host);
> > + void (*dump_uhs2_regs)(struct sdhci_host *host);
> > };
> >
> > #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
> >
>
On Thu, Nov 26, 2020 at 10:16:11AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > Sdhci_uhs2_reset() does a UHS-II specific reset operation.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 49 +++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 1 +
> > drivers/mmc/host/sdhci.c | 3 ++-
> > drivers/mmc/host/sdhci.h | 1 +
> > 4 files changed, 53 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 08905ed081fb..e2b9743fe17d 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -10,6 +10,7 @@
> > * Author: AKASHI Takahiro <[email protected]>
> > */
> >
> > +#include <linux/delay.h>
> > #include <linux/module.h>
> >
> > #include "sdhci.h"
> > @@ -49,6 +50,54 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
> >
> > +/*****************************************************************************\
> > + * *
> > + * Low level functions *
> > + * *
> > +\*****************************************************************************/
> > +
> > +/**
> > + * sdhci_uhs2_reset - invoke SW reset
> > + * @host: SDHCI host
> > + * @mask: Control mask
> > + *
> > + * Invoke SW reset, depending on a bit in @mask and wait for completion.
> > + */
> > +void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
> > +{
> > + unsigned long timeout;
> > +
> > + if (!(host->mmc->caps & MMC_CAP_UHS2))
>
> Please make a helper so this can be like:
>
> if (!sdhci_uhs2_mode(host))
>
> The capability will be always present for hosts that support UHS2, but not
> all cards support UHS2 mode. I suggest just adding a bool to struct
> sdhci_host to keep track of when the host is in UHS2 mode.
Given the fact that UHS-2 host may (potentially) support a topology like
a ring, this kind of status should be attributed to a card (structure)
rather than a host.
I'm asking Ben to modify the way how the current mode be managed
in both 'core' side and 'host' side.
That is why I marked "TODO" in many places to check the mode.
So I'd defer the change until his rework be done.
> > + return;
> > +
> > + sdhci_writew(host, mask, SDHCI_UHS2_SW_RESET);
> > +
> > + if (mask & SDHCI_UHS2_SW_RESET_FULL) {
> > + host->clock = 0;
> > + /* Reset-all turns off SD Bus Power */
> > + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
>
> I would prefer not to support any legacy quirks that we do not need right
> now. Just be sure to add a comment somewhere listing which quirks are not
> supported for UHS2 host controllers.
No strong opinion. I'd defer to you.
> > + sdhci_runtime_pm_bus_off(host);
> > + }
> > +
> > + /* Wait max 100 ms */
> > + timeout = 10000;
> > +
> > + /* hw clears the bit when it's done */
> > + while (sdhci_readw(host, SDHCI_UHS2_SW_RESET) & mask) {
>
> This kind of loop can now be done with read_poll_timeout_atomic(sdhci_readw,
> ..., host, SDHCI_UHS2_SW_RESET)
Okay.
-Takahiro Akashi
> > + if (timeout == 0) {
> > + pr_err("%s: %s: Reset 0x%x never completed.\n",
> > + __func__, mmc_hostname(host->mmc), (int)mask);
> > + pr_err("%s: clean reset bit\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
> > + return;
> > + }
> > + timeout--;
> > + udelay(10);
> > + }
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> > index b9529d32b58d..7bb7a0d67109 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.h
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -210,5 +210,6 @@
> > struct sdhci_host;
> >
> > void sdhci_uhs2_dump_regs(struct sdhci_host *host);
> > +void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> >
> > #endif /* __SDHCI_UHS2_H */
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index d4a57e8c9bb8..af336bdb4305 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -195,13 +195,14 @@ static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
> > pm_runtime_get_noresume(host->mmc->parent);
> > }
> >
> > -static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
> > +void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
> > {
> > if (!host->bus_on)
> > return;
> > host->bus_on = false;
> > pm_runtime_put_noidle(host->mmc->parent);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_off);
> >
> > void sdhci_reset(struct sdhci_host *host, u8 mask)
> > {
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index d9d7a76cedc1..b9932423db08 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -831,6 +831,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> > __sdhci_read_caps(host, NULL, NULL, NULL);
> > }
> >
> > +void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> > u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
> > unsigned int *actual_clock);
> > void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
> >
>
On Thu, Nov 26, 2020 at 10:16:27AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a UHS-II version of sdhci's set_power operation.
> > VDD2, as well as VDD, is handled here.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 80 +++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 2 +
> > drivers/mmc/host/sdhci.c | 58 +++++++++++++++----------
> > drivers/mmc/host/sdhci.h | 2 +
> > 4 files changed, 119 insertions(+), 23 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index e2b9743fe17d..2bf78cc4e9ed 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -98,6 +98,86 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
> >
> > +void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> > + unsigned short vdd)
>
> This function isn't used, so let's rename it sdhci_uhs2_set_power_noreg and
> drop regulator support.
I have no strong opinion, but here Ben might want to further rework
the new sdhci_uhs2_set_power_noreg() now that it is almost the same as
GLI's gl9755_set_power()(, adding a new quirk?).
> > +{
> > + struct mmc_host *mmc = host->mmc;
> > + u8 pwr;
> > +
> > + /* FIXME: check if flags & MMC_UHS2_SUPPORT? */
> > + if (!(host->mmc->caps & MMC_CAP_UHS2)) {
>
> As commented in another patch, please use a helper fn
As said, I would defer this.
> > + sdhci_set_power(host, mode, vdd);
> > + return;
> > + }
> > +
> > + if (mode != MMC_POWER_OFF) {
> > + pwr = sdhci_get_vdd_value(vdd);
>
> Simpler to open code this esp. as there are only 2 valid values:
>
> switch (1 << vdd) {
Can you ignore MMC_VDD_165_195 and MMC_VDD_20_21 here?
> case MMC_VDD_29_30..MMC_VDD_30_31:
> pwr = SDHCI_POWER_300;
> break;
> case MMC_VDD_32_33..MMC_VDD_33_34:
> pwr = SDHCI_POWER_330;
> break;
> default:
> WARN(1, "%s: Invalid vdd %#x\n",
> mmc_hostname(host->mmc), vdd);
> break;
> }
>
>
> > + if (!pwr)
> > + WARN(1, "%s: Invalid vdd %#x\n",
> > + mmc_hostname(host->mmc), vdd);
> > +
> > + pwr |= SDHCI_VDD2_POWER_180;
> > + }
> > +
> > + if (host->pwr == pwr)
> > + return;
> > + host ->pwr = pwr;
> > +
> > + if (pwr == 0) {
> > + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
> > +
> > + if (!IS_ERR(host->mmc->supply.vmmc))
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> > + if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc2))
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
> > +
> > + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
>
> Please drop support for legacy quirk SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON
Okay.
>
> > + sdhci_runtime_pm_bus_off(host);
> > + } else {
> > + if (!IS_ERR(host->mmc->supply.vmmc))
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
> > + if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc2))
> > + /* support 1.8v only for now */
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2,
> > + fls(MMC_VDD2_165_195) - 1);
> > +
> > + /*
> > + * Spec says that we should clear the power reg before setting
> > + * a new value. Some controllers don't seem to like this though.
> > + */
> > + if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
>
> Please drop support for legacy quirk here and several cases below. As I
> mentioned in another patch, just put a comment somewhere listing what is
> not supported for UHS2 host controllers.
Okay.
-Takahiro Akashi
>
> > + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
> > +
> > + /*
> > + * At least the Marvell CaFe chip gets confused if we set the
> > + * voltage and set turn on power at the same time, so set the
> > + * voltage first.
> > + */
> > + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
> > + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> > +
> > + /* vdd first */
> > + pwr |= SDHCI_POWER_ON;
> > + sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
> > + mdelay(5);
> > +
> > + pwr |= SDHCI_VDD2_POWER_ON;
> > + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> > + mdelay(5);
> > +
> > + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
> > + sdhci_runtime_pm_bus_on(host);
> > +
> > + /*
> > + * Some controllers need an extra 10ms delay of 10ms before
> > + * they can apply clock after applying power
> > + */
> > + if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
> > + mdelay(10);
> > + }
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> > index 7bb7a0d67109..3c19d8e44c36 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.h
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -211,5 +211,7 @@ struct sdhci_host;
> >
> > void sdhci_uhs2_dump_regs(struct sdhci_host *host);
> > void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> > +void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> > + unsigned short vdd);
> >
> > #endif /* __SDHCI_UHS2_H */
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index af336bdb4305..0b741eb546cb 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -187,13 +187,14 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)
> > sdhci_set_card_detection(host, false);
> > }
> >
> > -static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
> > +void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
> > {
> > if (host->bus_on)
> > return;
> > host->bus_on = true;
> > pm_runtime_get_noresume(host->mmc->parent);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_on);
> >
> > void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
> > {
> > @@ -2017,36 +2018,47 @@ static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode,
> > sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
> > }
> >
> > +unsigned short sdhci_get_vdd_value(unsigned short vdd)
> > +{
> > + u8 pwr;
> > +
> > + switch (1 << vdd) {
> > + case MMC_VDD_165_195:
> > + /*
> > + * Without a regulator, SDHCI does not support 2.0v
> > + * so we only get here if the driver deliberately
> > + * added the 2.0v range to ocr_avail. Map it to 1.8v
> > + * for the purpose of turning on the power.
> > + */
> > + case MMC_VDD_20_21:
> > + pwr = SDHCI_POWER_180;
> > + break;
> > + case MMC_VDD_29_30:
> > + case MMC_VDD_30_31:
> > + pwr = SDHCI_POWER_300;
> > + break;
> > + case MMC_VDD_32_33:
> > + case MMC_VDD_33_34:
> > + pwr = SDHCI_POWER_330;
> > + break;
> > + default:
> > + pwr = 0;
> > + }
> > +
> > + return pwr;
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_get_vdd_value);
> > +
> > void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
> > unsigned short vdd)
> > {
> > u8 pwr = 0;
> >
> > if (mode != MMC_POWER_OFF) {
> > - switch (1 << vdd) {
> > - case MMC_VDD_165_195:
> > - /*
> > - * Without a regulator, SDHCI does not support 2.0v
> > - * so we only get here if the driver deliberately
> > - * added the 2.0v range to ocr_avail. Map it to 1.8v
> > - * for the purpose of turning on the power.
> > - */
> > - case MMC_VDD_20_21:
> > - pwr = SDHCI_POWER_180;
> > - break;
> > - case MMC_VDD_29_30:
> > - case MMC_VDD_30_31:
> > - pwr = SDHCI_POWER_300;
> > - break;
> > - case MMC_VDD_32_33:
> > - case MMC_VDD_33_34:
> > - pwr = SDHCI_POWER_330;
> > - break;
> > - default:
> > + pwr = sdhci_get_vdd_value(vdd);
> > + if (!pwr)
> > WARN(1, "%s: Invalid vdd %#x\n",
> > mmc_hostname(host->mmc), vdd);
> > - break;
> > - }
> > }
> >
> > if (host->pwr == pwr)
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index b9932423db08..2b5b8295cf92 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -831,6 +831,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> > __sdhci_read_caps(host, NULL, NULL, NULL);
> > }
> >
> > +void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
> > void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> > u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
> > unsigned int *actual_clock);
> > @@ -841,6 +842,7 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
> > void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
> > unsigned char mode,
> > unsigned short vdd);
> > +unsigned short sdhci_get_vdd_value(unsigned short vdd);
> > void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
> > unsigned short vdd);
> > void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
> >
>
On 30/11/20 8:20 am, AKASHI Takahiro wrote:
> On Thu, Nov 26, 2020 at 10:16:11AM +0200, Adrian Hunter wrote:
>> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
>>> Sdhci_uhs2_reset() does a UHS-II specific reset operation.
>>>
>>> Signed-off-by: Ben Chuang <[email protected]>
>>> Signed-off-by: AKASHI Takahiro <[email protected]>
>>> ---
>>> drivers/mmc/host/sdhci-uhs2.c | 49 +++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci-uhs2.h | 1 +
>>> drivers/mmc/host/sdhci.c | 3 ++-
>>> drivers/mmc/host/sdhci.h | 1 +
>>> 4 files changed, 53 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
>>> index 08905ed081fb..e2b9743fe17d 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.c
>>> +++ b/drivers/mmc/host/sdhci-uhs2.c
>>> @@ -10,6 +10,7 @@
>>> * Author: AKASHI Takahiro <[email protected]>
>>> */
>>>
>>> +#include <linux/delay.h>
>>> #include <linux/module.h>
>>>
>>> #include "sdhci.h"
>>> @@ -49,6 +50,54 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host)
>>> }
>>> EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
>>>
>>> +/*****************************************************************************\
>>> + * *
>>> + * Low level functions *
>>> + * *
>>> +\*****************************************************************************/
>>> +
>>> +/**
>>> + * sdhci_uhs2_reset - invoke SW reset
>>> + * @host: SDHCI host
>>> + * @mask: Control mask
>>> + *
>>> + * Invoke SW reset, depending on a bit in @mask and wait for completion.
>>> + */
>>> +void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
>>> +{
>>> + unsigned long timeout;
>>> +
>>> + if (!(host->mmc->caps & MMC_CAP_UHS2))
>>
>> Please make a helper so this can be like:
>>
>> if (!sdhci_uhs2_mode(host))
>>
>> The capability will be always present for hosts that support UHS2, but not
>> all cards support UHS2 mode. I suggest just adding a bool to struct
>> sdhci_host to keep track of when the host is in UHS2 mode.
>
> Given the fact that UHS-2 host may (potentially) support a topology like
> a ring, this kind of status should be attributed to a card (structure)
> rather than a host.
It is very unlikely we would ever need to support that, so don't let it make
things more complicated.
On Thu, Nov 26, 2020 at 10:16:44AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
> > so no voltage switch required.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 26 ++++++++++++++++++++++++++
> > 1 file changed, 26 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 2bf78cc4e9ed..1eca89359351 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -178,6 +178,29 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> > }
> > EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
> >
> > +/*****************************************************************************\
> > + * *
> > + * MMC callbacks *
> > + * *
> > +\*****************************************************************************/
> > +
> > +static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
> > + struct mmc_ios *ios)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > +
> > + /*
> > + * For UHS2, the signal voltage is supplied by vdd2 which is
> > + * already 1.8v so no voltage switch required.
> > + */
> > + if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
> > + host->version >= SDHCI_SPEC_400 &&
> > + host->mmc->flags & MMC_UHS2_SUPPORT)
>
> Could this be the same helper function suggested elsewhere i.e.
>
> if (!sdhci_uhs2_mode(host))
ditto. I'd defer the change until some time later.
-Takahiro Akashi
>
> > + return 0;
> > +
> > + return sdhci_start_signal_voltage_switch(mmc, ios);
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > @@ -186,6 +209,9 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
> >
> > static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> > {
> > + host->mmc_host_ops.start_signal_voltage_switch =
> > + sdhci_uhs2_start_signal_voltage_switch;
> > +
> > return 0;
> > }
> >
> >
>
On 30/11/20 9:15 am, AKASHI Takahiro wrote:
> On Thu, Nov 26, 2020 at 10:16:27AM +0200, Adrian Hunter wrote:
>> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
>>> This is a UHS-II version of sdhci's set_power operation.
>>> VDD2, as well as VDD, is handled here.
>>>
>>> Signed-off-by: Ben Chuang <[email protected]>
>>> Signed-off-by: AKASHI Takahiro <[email protected]>
>>> ---
>>> drivers/mmc/host/sdhci-uhs2.c | 80 +++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci-uhs2.h | 2 +
>>> drivers/mmc/host/sdhci.c | 58 +++++++++++++++----------
>>> drivers/mmc/host/sdhci.h | 2 +
>>> 4 files changed, 119 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
>>> index e2b9743fe17d..2bf78cc4e9ed 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.c
>>> +++ b/drivers/mmc/host/sdhci-uhs2.c
>>> @@ -98,6 +98,86 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
>>> }
>>> EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
>>>
>>> +void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
>>> + unsigned short vdd)
>>
>> This function isn't used, so let's rename it sdhci_uhs2_set_power_noreg and
>> drop regulator support.
>
> I have no strong opinion, but here Ben might want to further rework
> the new sdhci_uhs2_set_power_noreg() now that it is almost the same as
> GLI's gl9755_set_power()(, adding a new quirk?).
>
>>> +{
>>> + struct mmc_host *mmc = host->mmc;
>>> + u8 pwr;
>>> +
>>> + /* FIXME: check if flags & MMC_UHS2_SUPPORT? */
>>> + if (!(host->mmc->caps & MMC_CAP_UHS2)) {
>>
>> As commented in another patch, please use a helper fn
>
> As said, I would defer this.
>
>>> + sdhci_set_power(host, mode, vdd);
>>> + return;
>>> + }
>>> +
>>> + if (mode != MMC_POWER_OFF) {
>>> + pwr = sdhci_get_vdd_value(vdd);
>>
>> Simpler to open code this esp. as there are only 2 valid values:
>>
>> switch (1 << vdd) {
>
> Can you ignore MMC_VDD_165_195 and MMC_VDD_20_21 here?
They are outside UHS-II spec, but you decide.
On Thu, Nov 26, 2020 at 10:17:11AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a sdhci version of mmc's set_ios operation.
> > It covers both UHS-I and UHS-II.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 100 ++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 1 +
> > drivers/mmc/host/sdhci.c | 40 +++++++++-----
> > drivers/mmc/host/sdhci.h | 2 +
> > 4 files changed, 128 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index d9e98c097bfe..637464748cc4 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -263,6 +263,74 @@ void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
> >
> > +/**
> > + * sdhci_uhs2_clear_set_irqs - set Error Interrupt Status Enable register
> > + * @host: SDHCI host
> > + * @clear: bit-wise clear mask
> > + * @set: bit-wise set mask
> > + *
> > + * Set/unset bits in UHS-II Error Interrupt Status Enable register
> > + */
> > +void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
> > +{
> > + u32 ier;
> > +
> > + ier = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS_EN);
> > + ier &= ~clear;
> > + ier |= set;
> > + sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_STATUS_EN);
> > + sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_SIG_EN);
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_clear_set_irqs);
> > +
> > +static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + u8 cmd_res, dead_lock;
> > + u16 ctrl_2;
> > + unsigned long flags;
> > +
> > + /* FIXME: why lock? */
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + /* UHS2 Timeout Control */
> > + sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
> > +
> > + /* change to use calculate value */
> > + cmd_res |= dead_lock << SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT;
> > +
> > + sdhci_uhs2_clear_set_irqs(host,
> > + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
> > + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT,
> > + 0);
> > + sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
> > + sdhci_uhs2_clear_set_irqs(host, 0,
> > + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
> > + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT);
> > +
> > + /* UHS2 timing */
> > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > + if (ios->timing == MMC_TIMING_UHS2)
> > + ctrl_2 |= SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN;
> > + else
> > + ctrl_2 &= ~(SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN);
> > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > +
> > + if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
> > + sdhci_enable_preset_value(host, true);
> > +
> > + if (host->ops->set_power)
> > + host->ops->set_power(host, ios->power_mode, ios->vdd);
> > + else
> > + sdhci_set_power(host, ios->power_mode, ios->vdd);
> > + udelay(100);
> > +
> > + host->timing = ios->timing;
> > + sdhci_set_clock(host, host->clock);
>
> sdhci_set_ios_common() already called ->set_clock() and ->set_power(), so I
> am not really following what is going on here. Can you explain some more?
To be frank, I don't know. The logic in Intel's (and/or Ben's?)
original code does so.
What I changed is to remove the code of setting (ios->vdd and) ios->vdd2,
which is executed before calling set_power(), in __sdhci_uhs2_set_ios().
So yes, effectively it may be of no use to call set_power() here.
-Takahiro Akashi
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * MMC callbacks *
> > @@ -286,6 +354,37 @@ static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
> > return sdhci_start_signal_voltage_switch(mmc, ios);
> > }
> >
> > +void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > +
> > + if (!(host->version >= SDHCI_SPEC_400) ||
> > + !(host->mmc->flags & MMC_UHS2_SUPPORT &&
> > + host->mmc->caps & MMC_CAP_UHS2)) {
> > + sdhci_set_ios(mmc, ios);
> > + return;
> > + }
> > +
> > + if (ios->power_mode == MMC_POWER_UNDEFINED)
> > + return;
> > +
> > + if (host->flags & SDHCI_DEVICE_DEAD) {
> > + if (!IS_ERR(mmc->supply.vmmc) &&
> > + ios->power_mode == MMC_POWER_OFF)
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> > + if (!IS_ERR_OR_NULL(mmc->supply.vmmc2) &&
> > + ios->power_mode == MMC_POWER_OFF)
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
> > + return;
> > + }
> > +
> > + /* FIXME: host->timing = ios->timing */
> > +
> > + sdhci_set_ios_common(mmc, ios);
> > +
> > + __sdhci_uhs2_set_ios(mmc, ios);
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > @@ -296,6 +395,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> > {
> > host->mmc_host_ops.start_signal_voltage_switch =
> > sdhci_uhs2_start_signal_voltage_switch;
> > + host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
> >
> > return 0;
> > }
> > diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> > index efe70577bc74..c1ff4ac1ab7a 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.h
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -214,5 +214,6 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> > void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> > unsigned short vdd);
> > void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
> > +void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
> >
> > #endif /* __SDHCI_UHS2_H */
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 0b741eb546cb..becb228330af 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -48,8 +48,6 @@
> > static unsigned int debug_quirks = 0;
> > static unsigned int debug_quirks2;
> >
> > -static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
> > -
> > static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
> >
> > void sdhci_dumpregs(struct sdhci_host *host)
> > @@ -1836,6 +1834,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
> > case MMC_TIMING_MMC_HS400:
> > preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
> > break;
> > + case MMC_TIMING_UHS2:
> > + preset = sdhci_readw(host, SDHCI_PRESET_FOR_UHS2);
> > + break;
> > default:
> > pr_warn("%s: Invalid UHS-I mode selected\n",
> > mmc_hostname(host->mmc));
> > @@ -2249,20 +2250,9 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
> >
> > -void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > +void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios)
> > {
> > struct sdhci_host *host = mmc_priv(mmc);
> > - u8 ctrl;
> > -
> > - if (ios->power_mode == MMC_POWER_UNDEFINED)
> > - return;
> > -
> > - if (host->flags & SDHCI_DEVICE_DEAD) {
> > - if (!IS_ERR(mmc->supply.vmmc) &&
> > - ios->power_mode == MMC_POWER_OFF)
> > - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> > - return;
> > - }
> >
> > /*
> > * Reset the chip on each power off.
> > @@ -2299,6 +2289,25 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > host->ops->set_power(host, ios->power_mode, ios->vdd);
> > else
> > sdhci_set_power(host, ios->power_mode, ios->vdd);
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_set_ios_common);
> > +
> > +void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + u8 ctrl;
> > +
> > + if (ios->power_mode == MMC_POWER_UNDEFINED)
> > + return;
> > +
> > + if (host->flags & SDHCI_DEVICE_DEAD) {
> > + if (!IS_ERR(mmc->supply.vmmc) &&
> > + ios->power_mode == MMC_POWER_OFF)
> > + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> > + return;
> > + }
> > +
> > + sdhci_set_ios_common(mmc, ios);
> >
> > if (host->ops->platform_send_init_74_clocks)
> > host->ops->platform_send_init_74_clocks(host, ios->power_mode);
> > @@ -2869,7 +2878,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
> >
> > -static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
> > +void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
> > {
> > /* Host Controller v3.00 defines preset value registers */
> > if (host->version < SDHCI_SPEC_300)
> > @@ -2897,6 +2906,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
> > host->preset_enabled = enable;
> > }
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_enable_preset_value);
> >
> > static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
> > int err)
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 2b5b8295cf92..e84ebddb20d8 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -851,6 +851,8 @@ void sdhci_set_bus_width(struct sdhci_host *host, int width);
> > void sdhci_reset(struct sdhci_host *host, u8 mask);
> > void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
> > int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
> > +void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
> > +void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios);
> > void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
> > int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> > struct mmc_ios *ios);
> >
>
On Thu, Nov 26, 2020 at 10:17:59AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 41 +++++++++++++++++++++++++++++++++++
> > 1 file changed, 41 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 994dff967e85..55362ace1857 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -11,6 +11,7 @@
> > */
> >
> > #include <linux/delay.h>
> > +#include <linux/ktime.h>
> > #include <linux/module.h>
> >
> > #include "sdhci.h"
> > @@ -385,6 +386,42 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > __sdhci_uhs2_set_ios(mmc, ios);
> > }
> >
> > +static void sdhci_uhs2_disable_clk(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > +
> > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +}
> > +
> > +static void sdhci_uhs2_enable_clk(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > + ktime_t timeout;
> > +
> > + clk |= SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > + /* Wait max 20 ms */
> > + timeout = ktime_add_ms(ktime_get(), 20);
> > + while (1) {
> > + bool timedout = ktime_after(ktime_get(), timeout);
> > +
> > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
>
> Can use read_poll_timeout(sdhci_readw,..., host, SDHCI_CLOCK_CONTROL)
Okay.
-Takahiro Akashi
> > + if (clk & SDHCI_CLOCK_INT_STABLE)
> > + break;
> > + if (timedout) {
> > + pr_err("%s: Internal clock never stabilised.\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return;
> > + }
> > + udelay(10);
> > + }
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > @@ -556,6 +593,10 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> >
> > if (!host->mmc_host_ops.uhs2_detect_init)
> > host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
> > + if (!host->mmc_host_ops.uhs2_disable_clk)
> > + host->mmc_host_ops.uhs2_disable_clk = sdhci_uhs2_disable_clk;
> > + if (!host->mmc_host_ops.uhs2_enable_clk)
> > + host->mmc_host_ops.uhs2_enable_clk = sdhci_uhs2_enable_clk;
> >
> > return 0;
> > }
> >
>
On Thu, Nov 26, 2020 at 10:18:44AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a sdhci version of mmc's request operation.
> > It covers both UHS-I and UHS-II.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 529 ++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 93 +++---
> > drivers/mmc/host/sdhci.h | 21 ++
> > 3 files changed, 610 insertions(+), 33 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index d8afb99a9918..36e52553977a 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -13,6 +13,7 @@
> > #include <linux/delay.h>
> > #include <linux/ktime.h>
> > #include <linux/module.h>
> > +#include <linux/mmc/mmc.h>
> >
> > #include "sdhci.h"
> > #include "sdhci-uhs2.h"
> > @@ -394,6 +395,27 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
> > return 0;
> > }
> >
> > +/* TODO: move them to a header */
> > +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> > +static inline void sdhci_led_activate(struct sdhci_host *host)
> > +{
> > +}
> > +
> > +static inline void sdhci_led_deactivate(struct sdhci_host *host)
> > +{
> > +}
> > +#else
> > +static inline void sdhci_led_activate(struct sdhci_host *host)
> > +{
> > + __sdhci_led_activate(host);
> > +}
> > +
> > +static inline void sdhci_led_deactivate(struct sdhci_host *host)
> > +{
> > + __sdhci_led_deactivate(host);
> > +}
> > +#endif
>
> Unless you need LED support, let's drop it
Okay
> > +
> > /*****************************************************************************\
> > * *
> > * MMC callbacks *
> > @@ -523,6 +545,512 @@ static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
> > return err;
> > }
> >
> > +static bool sdhci_uhs2_send_command(struct sdhci_host *host,
> > + struct mmc_command *cmd);
> > +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
> > + struct mmc_command *cmd,
> > + unsigned long flags);
>
> Always order functions so that early function declarations are not needed
Will try.
> > +
> > +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + struct mmc_command *cmd;
> > + unsigned long flags;
> > + bool present;
> > +
> > + /* FIXME: check more flags? */
> > + if (!host->mmc->flags & MMC_UHS2_SUPPORT) {
>
>
> if (sdhci_uhs2_mode(host))
>
> > + sdhci_request(mmc, mrq);
> > + return;
> > + }
> > +
> > + /* Firstly check card presence */
> > + present = mmc->ops->get_cd(mmc);
>
> Checking CD here is another legacy hangover. Should be able to check the
> bus power instead. i.e. if the card has been removed, the bus power (bit-0
> Power Control reg) should get set to 0. That can be checked under spin lock.
Replace it with check against SDHCI_POWER_ON.
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + sdhci_led_activate(host);
>
> Drop support for sdhci LEDs unless yoy are using them
Sure
> > +
> > + if (sdhci_present_error(host, mrq->cmd, present))
> > + goto out_finish;
> > +
> > + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
>
> UHS2 doesn't use CMD23 does it?
I don't know. Will replace it with
cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
> > +
> > + if (!sdhci_uhs2_send_command_retry(host, cmd, flags))
>
> Don't use sdhci_uhs2_send_command_retry. sdhci_send_command_retry is again
> dealing with legacy hangover. The inhibit bits should not be set. If they
> are it can be an error immediately. So just use sdhci_uhs2_send_commend()
Will follow your suggestion.
> > + goto out_finish;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + return;
> > +
> > +out_finish:
> > + sdhci_finish_mrq(host, mrq);
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_request);
> > +
> > +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
>
> Should end up being able simply to call sdhci_uhs2_request (or vice versa)
Well, there is one difference,i.e. whether we should check "card presence"
(or POWER_ON), isn't it?
I think we should preserve this difference.
Or we may simply remove _atomic version since there is no user for now.
Which do you prefer?
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + struct mmc_command *cmd;
> > + unsigned long flags;
> > + int ret = 0;
> > +
> > + if (!host->mmc->flags & MMC_UHS2_SUPPORT)
> > + return sdhci_request_atomic(mmc, mrq);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (sdhci_present_error(host, mrq->cmd, true)) {
> > + sdhci_finish_mrq(host, mrq);
> > + goto out_finish;
> > + }
> > +
> > + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
> > +
> > + /*
> > + * The HSQ may send a command in interrupt context without polling
> > + * the busy signaling, which means we should return BUSY if controller
> > + * has not released inhibit bits to allow HSQ trying to send request
> > + * again in non-atomic context. So we should not finish this request
> > + * here.
> > + */
> > + if (!sdhci_uhs2_send_command(host, cmd))
> > + ret = -EBUSY;
> > + else
> > + sdhci_led_activate(host);
> > +
> > +out_finish:
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
> > +
> > +/*****************************************************************************\
> > + * *
> > + * Core functions *
> > + * *
> > +\*****************************************************************************/
> > +
> > +static void sdhci_uhs2_prepare_data(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > + struct mmc_data *data = cmd->data;
> > +
> > + sdhci_initialize_data(host, data);
> > +
> > + sdhci_prepare_dma(host, data);
> > +
> > + sdhci_writew(host, data->blksz, SDHCI_UHS2_BLOCK_SIZE);
> > + sdhci_writew(host, data->blocks, SDHCI_UHS2_BLOCK_COUNT);
> > +}
> > +
> > +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
> > +static void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > + if (!sdhci_external_dma_setup(host, cmd)) {
> > + __sdhci_external_dma_prepare_data(host, cmd);
> > + } else {
> > + sdhci_external_dma_release(host);
> > + pr_err("%s: Cannot use external DMA, switch to the DMA/PIO which standard SDHCI provides.\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_uhs2_prepare_data(host, cmd);
> > + }
> > +}
> > +#else
> > +static inline void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > + /* This should never happen */
> > + WARN_ON_ONCE(1);
> > +}
> > +
> > +static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > +}
> > +#endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
>
> Let's start without support for external DMA - unless you need it
Okay
> > +
> > +static void sdhci_uhs2_finish_data(struct sdhci_host *host)
> > +{
> > + struct mmc_data *data = host->data;
> > +
> > + __sdhci_finish_data_common(host);
> > +
> > + /*
> > + * FIXME: Is this condition needed?
> > + if (host->mmc->flags & MMC_UHS2_INITIALIZED)
> > + */
> > + __sdhci_finish_mrq(host, data->mrq);
> > +}
> > +
> > +static void sdhci_uhs2_set_transfer_mode(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > + u16 mode;
> > + struct mmc_data *data = cmd->data;
> > + u16 arg;
> > +
> > + if (!data) {
> > + /* clear Auto CMD settings for no data CMDs */
> > + arg = cmd->uhs2_cmd->arg;
> > + if ((((arg & 0xF) << 8) | ((arg >> 8) & 0xFF)) ==
> > + UHS2_DEV_CMD_TRANS_ABORT) {
> > + mode = 0;
> > + } else {
> > + mode = sdhci_readw(host, SDHCI_UHS2_TRANS_MODE);
> > + if (cmd->opcode == MMC_STOP_TRANSMISSION ||
> > + cmd->opcode == MMC_ERASE)
> > + mode |= SDHCI_UHS2_TRNS_WAIT_EBSY;
> > + else
> > + /* send status mode */
> > + if (cmd->opcode == MMC_SEND_STATUS)
> > + mode = 0;
> > + }
> > +
> > + if (IS_ENABLED(CONFIG_MMC_DEBUG))
> > + DBG("UHS2 no data trans mode is 0x%x.\n", mode);
> > +
> > + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
> > + return;
> > + }
> > +
> > + WARN_ON(!host->data);
> > +
> > + mode = SDHCI_UHS2_TRNS_BLK_CNT_EN | SDHCI_UHS2_TRNS_WAIT_EBSY;
> > + if (data->flags & MMC_DATA_WRITE)
> > + mode |= SDHCI_UHS2_TRNS_DATA_TRNS_WRT;
> > +
> > + if (data->blocks == 1 &&
> > + data->blksz != 512 &&
> > + cmd->opcode != MMC_READ_SINGLE_BLOCK &&
> > + cmd->opcode != MMC_WRITE_BLOCK) {
> > + mode &= ~SDHCI_UHS2_TRNS_BLK_CNT_EN;
> > + mode |= SDHCI_UHS2_TRNS_BLK_BYTE_MODE;
> > + }
> > +
> > + if (host->flags & SDHCI_REQ_USE_DMA)
> > + mode |= SDHCI_UHS2_TRNS_DMA;
> > +
> > + if ((host->mmc->flags & MMC_UHS2_2L_HD) && !cmd->uhs2_tmode0_flag)
> > + mode |= SDHCI_UHS2_TRNS_2L_HD;
> > +
> > + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
> > +
> > + if (IS_ENABLED(CONFIG_MMC_DEBUG))
> > + DBG("UHS2 trans mode is 0x%x.\n", mode);
> > +}
> > +
> > +static void __sdhci_uhs2_send_command(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > + int i, j;
> > + int cmd_reg;
> > +
> > + if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
> > + if (!cmd->uhs2_cmd) {
> > + pr_err("%s: fatal error, no uhs2_cmd!\n",
> > + mmc_hostname(host->mmc));
> > + return;
> > + }
> > + }
> > +
> > + i = 0;
> > + sdhci_writel(host,
> > + ((u32)cmd->uhs2_cmd->arg << 16) |
> > + (u32)cmd->uhs2_cmd->header,
> > + SDHCI_UHS2_CMD_PACKET + i);
> > + i += 4;
> > +
> > + /*
> > + * Per spec, playload (config) should be MSB before sending out.
> > + * But we don't need convert here because had set payload as
> > + * MSB when preparing config read/write commands.
> > + */
> > + for (j = 0; j < cmd->uhs2_cmd->payload_len / sizeof(u32); j++) {
> > + sdhci_writel(host, *(cmd->uhs2_cmd->payload + j),
> > + SDHCI_UHS2_CMD_PACKET + i);
> > + i += 4;
> > + }
> > +
> > + for ( ; i < SDHCI_UHS2_CMD_PACK_MAX_LEN; i += 4)
> > + sdhci_writel(host, 0, SDHCI_UHS2_CMD_PACKET + i);
> > +
> > + if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
> > + DBG("UHS2 CMD packet_len = %d.\n", cmd->uhs2_cmd->packet_len);
> > + for (i = 0; i < cmd->uhs2_cmd->packet_len; i++)
> > + DBG("UHS2 CMD_PACKET[%d] = 0x%x.\n", i,
> > + sdhci_readb(host, SDHCI_UHS2_CMD_PACKET + i));
> > + }
> > +
> > + cmd_reg = cmd->uhs2_cmd->packet_len <<
> > + SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT;
> > + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
> > + cmd_reg |= SDHCI_UHS2_COMMAND_DATA;
> > + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> > + cmd_reg |= SDHCI_UHS2_COMMAND_CMD12;
> > +
> > + /* UHS2 Native ABORT */
> > + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> > + ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
> > + ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) == UHS2_DEV_CMD_TRANS_ABORT))
> > + cmd_reg |= SDHCI_UHS2_COMMAND_TRNS_ABORT;
> > +
> > + /* UHS2 Native DORMANT */
> > + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> > + ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
> > + ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) ==
> > + UHS2_DEV_CMD_GO_DORMANT_STATE))
> > + cmd_reg |= SDHCI_UHS2_COMMAND_DORMANT;
> > +
> > + DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
> > +
> > + sdhci_writew(host, cmd_reg, SDHCI_UHS2_COMMAND);
> > +}
> > +
> > +static bool sdhci_uhs2_send_command(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > +{
> > + int flags;
> > + u32 mask;
> > + unsigned long timeout;
> > +
> > + /* FIXME: Is this check necessary? */
>
> No
Will drop it.
>
> > + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
> > + return sdhci_send_command(host, cmd);
> > +
> > + WARN_ON(host->cmd);
> > +
> > + /* Initially, a command has no error */
> > + cmd->error = 0;
> > +
> > + if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
>
> Quirk
Will drop it.
>
> > + cmd->opcode == MMC_STOP_TRANSMISSION)
> > + cmd->flags |= MMC_RSP_BUSY;
> > +
> > + mask = SDHCI_CMD_INHIBIT;
> > + if (sdhci_data_line_cmd(cmd))
> > + mask |= SDHCI_DATA_INHIBIT;
>
> Command inhibit for data is not supported by UHS2
Will drop it.
> > +
> > + /* We shouldn't wait for data inihibit for stop commands, even
> > + though they might use busy signaling */
> > + if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
> > + mask &= ~SDHCI_DATA_INHIBIT;
>
> Ditto
Will drop it.
>
> > +
> > + if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
> > + return false;
> > +
> > + host->cmd = cmd;
> > + host->data_timeout = 0;
> > + if (sdhci_data_line_cmd(cmd)) {
> > + WARN_ON(host->data_cmd);
> > + host->data_cmd = cmd;
> > + __sdhci_uhs2_set_timeout(host);
> > + }
> > +
> > + if (cmd->data) {
> > + if (host->use_external_dma)
> > + sdhci_uhs2_external_dma_prepare_data(host, cmd);
>
> To start with, let's drop support for external DMA with UHS2. i.e. disallow
> UHS2 mode if external DMA is enabled
Okay.
I will add a check against host->use_external in uhs2_do_detect_init().
>
> > + else
> > + sdhci_uhs2_prepare_data(host, cmd);
> > + }
> > +
> > + sdhci_uhs2_set_transfer_mode(host, cmd);
> > +
> > + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
> > + WARN_ONCE(1, "Unsupported response type!\n");
> > + /*
> > + * This does not happen in practice because 136-bit response
> > + * commands never have busy waiting, so rather than complicate
> > + * the error path, just remove busy waiting and continue.
> > + */
> > + cmd->flags &= ~MMC_RSP_BUSY;
> > + }
> > +
> > + if (!(cmd->flags & MMC_RSP_PRESENT))
> > + flags = SDHCI_CMD_RESP_NONE;
> > + else if (cmd->flags & MMC_RSP_136)
> > + flags = SDHCI_CMD_RESP_LONG;
> > + else if (cmd->flags & MMC_RSP_BUSY)
> > + flags = SDHCI_CMD_RESP_SHORT_BUSY;
> > + else
> > + flags = SDHCI_CMD_RESP_SHORT;
> > +
> > + if (cmd->flags & MMC_RSP_CRC)
> > + flags |= SDHCI_CMD_CRC;
> > + if (cmd->flags & MMC_RSP_OPCODE)
> > + flags |= SDHCI_CMD_INDEX;
> > +
> > + /* CMD19 is special in that the Data Present Select should be set */
> > + if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
> > + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
>
> No tuning commands fo UHS2
>
> > + flags |= SDHCI_CMD_DATA;
>
> flags are never used?
Good catch. Will drop it.
> > +
> > + timeout = jiffies;
> > + if (host->data_timeout)
> > + timeout += nsecs_to_jiffies(host->data_timeout);
> > + else if (!cmd->data && cmd->busy_timeout > 9000)
> > + timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
> > + else
> > + timeout += 10 * HZ;
> > + sdhci_mod_timer(host, cmd->mrq, timeout);
> > +
> > + if (host->use_external_dma)
> > + sdhci_external_dma_pre_transfer(host, cmd);
> > +
> > + __sdhci_uhs2_send_command(host, cmd);
> > +
> > + return true;
> > +}
> > +
> > +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
> > + struct mmc_command *cmd,
> > + unsigned long flags)
>
> Can probably get rid of sdhci_uhs2_send_command_retry
Sure
> > + __releases(host->lock)
> > + __acquires(host->lock)
> > +{
> > + struct mmc_command *deferred_cmd = host->deferred_cmd;
> > + int timeout = 10; /* Approx. 10 ms */
> > + bool present;
> > +
> > + while (!sdhci_uhs2_send_command(host, cmd)) {
> > + if (!timeout--) {
> > + pr_err("%s: Controller never released inhibit bit(s).\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + cmd->error = -EIO;
> > + return false;
> > + }
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + usleep_range(1000, 1250);
> > +
> > + present = host->mmc->ops->get_cd(host->mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + /* A deferred command might disappear, handle that */
> > + if (cmd == deferred_cmd && cmd != host->deferred_cmd)
> > + return true;
> > +
> > + if (sdhci_present_error(host, cmd, present))
> > + return false;
> > + }
> > +
> > + if (cmd == host->deferred_cmd)
> > + host->deferred_cmd = NULL;
> > +
> > + return true;
> > +}
> > +
> > +static void __sdhci_uhs2_finish_command(struct sdhci_host *host)
> > +{
> > + struct mmc_command *cmd = host->cmd;
> > + u8 resp;
> > + u8 ecode;
> > + bool bReadA0 = 0;
> > + int i;
> > +
> > + if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
> > + resp = sdhci_readb(host, SDHCI_UHS2_RESPONSE + 2);
> > + if (resp & UHS2_RES_NACK_MASK) {
> > + ecode = (resp >> UHS2_RES_ECODE_POS) &
> > + UHS2_RES_ECODE_MASK;
> > + pr_err("%s: NACK is got, ECODE=0x%x.\n",
> > + mmc_hostname(host->mmc), ecode);
> > + }
> > + bReadA0 = 1;
> > + }
> > +
> > + if (cmd->uhs2_resp &&
> > + cmd->uhs2_resp_len && cmd->uhs2_resp_len <= 20) {
> > + /* Get whole response of some native CCMD, like
> > + * DEVICE_INIT, ENUMERATE.
> > + */
> > + for (i = 0; i < cmd->uhs2_resp_len; i++)
> > + cmd->uhs2_resp[i] =
> > + sdhci_readb(host, SDHCI_UHS2_RESPONSE + i);
> > + } else {
> > + /* Get SD CMD response and Payload for some read
> > + * CCMD, like INQUIRY_CFG.
> > + */
> > + /* Per spec (p136), payload field is divided into
> > + * a unit of DWORD and transmission order within
> > + * a DWORD is big endian.
> > + */
> > + if (!bReadA0)
> > + sdhci_readl(host, SDHCI_UHS2_RESPONSE);
> > + for (i = 4; i < 20; i += 4) {
> > + cmd->resp[i / 4 - 1] =
> > + (sdhci_readb(host,
> > + SDHCI_UHS2_RESPONSE + i) << 24) |
> > + (sdhci_readb(host,
> > + SDHCI_UHS2_RESPONSE + i + 1)
> > + << 16) |
> > + (sdhci_readb(host,
> > + SDHCI_UHS2_RESPONSE + i + 2)
> > + << 8) |
> > + sdhci_readb(host, SDHCI_UHS2_RESPONSE + i + 3);
> > + }
> > + }
> > +}
> > +
> > +static void sdhci_uhs2_finish_command(struct sdhci_host *host)
> > +{
> > + struct mmc_command *cmd = host->cmd;
> > +
> > + /* FIXME: Is this check necessary? */
> > + if (!(host->mmc->flags & MMC_UHS2_SUPPORT)) {
> > + sdhci_finish_command(host);
> > + return;
> > + }
> > +
> > + __sdhci_uhs2_finish_command(host);
> > +
> > + host->cmd = NULL;
> > +
> > + if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd)
> > + mmc_command_done(host->mmc, cmd->mrq);
> > +
> > + /*
> > + * The host can send and interrupt when the busy state has
> > + * ended, allowing us to wait without wasting CPU cycles.
> > + * The busy signal uses DAT0 so this is similar to waiting
> > + * for data to complete.
> > + *
> > + * Note: The 1.0 specification is a bit ambiguous about this
> > + * feature so there might be some problems with older
> > + * controllers.
> > + */
> > + if (cmd->flags & MMC_RSP_BUSY) {
> > + if (cmd->data) {
> > + DBG("Cannot wait for busy signal when also doing a data transfer");
> > + } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
> > + cmd == host->data_cmd) {
> > + /* Command complete before busy is ended */
> > + return;
> > + }
> > + }
> > +
> > + /* Finished CMD23, now send actual command. */
>
> UHS2 doesn't use CMD23 does it?
Should we remove any code in this function, other than this comment?
-Takahiro Akashi
> > + if (cmd == cmd->mrq->sbc) {
> > + if (!sdhci_uhs2_send_command(host, cmd->mrq->cmd)) {
> > + WARN_ON(host->deferred_cmd);
> > + host->deferred_cmd = cmd->mrq->cmd;
> > + }
> > + } else {
> > +
> > + /* Processed actual command. */
> > + if (host->data && host->data_early)
> > + sdhci_uhs2_finish_data(host);
> > +
> > + if (!cmd->data)
> > + __sdhci_finish_mrq(host, cmd->mrq);
> > + }
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > @@ -691,6 +1219,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> > host->mmc_host_ops.start_signal_voltage_switch =
> > sdhci_uhs2_start_signal_voltage_switch;
> > host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
> > + host->mmc_host_ops.request = sdhci_uhs2_request;
> >
> > if (!host->mmc_host_ops.uhs2_detect_init)
> > host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index becb228330af..a9f5449bddcc 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -48,8 +48,6 @@
> > static unsigned int debug_quirks = 0;
> > static unsigned int debug_quirks2;
> >
> > -static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
> > -
> > void sdhci_dumpregs(struct sdhci_host *host)
> > {
> > SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
> > @@ -148,10 +146,13 @@ void sdhci_enable_v4_mode(struct sdhci_host *host)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_enable_v4_mode);
> >
> > -static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
> > +bool sdhci_data_line_cmd(struct mmc_command *cmd)
> > {
> > return cmd->data || cmd->flags & MMC_RSP_BUSY;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_data_line_cmd);
> > +
> > +/* TODO: move this as an inline function to a header */
> >
> > static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
> > {
> > @@ -363,7 +364,7 @@ static void sdhci_reinit(struct sdhci_host *host)
> > mmc_detect_change(host->mmc, msecs_to_jiffies(200));
> > }
> >
> > -static void __sdhci_led_activate(struct sdhci_host *host)
> > +void __sdhci_led_activate(struct sdhci_host *host)
> > {
> > u8 ctrl;
> >
> > @@ -374,8 +375,9 @@ static void __sdhci_led_activate(struct sdhci_host *host)
> > ctrl |= SDHCI_CTRL_LED;
> > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> > }
> > +EXPORT_SYMBOL_GPL(__sdhci_led_activate);
> >
> > -static void __sdhci_led_deactivate(struct sdhci_host *host)
> > +void __sdhci_led_deactivate(struct sdhci_host *host)
> > {
> > u8 ctrl;
> >
> > @@ -386,6 +388,7 @@ static void __sdhci_led_deactivate(struct sdhci_host *host)
> > ctrl &= ~SDHCI_CTRL_LED;
> > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> > }
> > +EXPORT_SYMBOL_GPL(__sdhci_led_deactivate);
> >
> > #if IS_REACHABLE(CONFIG_LEDS_CLASS)
> > static void sdhci_led_control(struct led_classdev *led,
> > @@ -464,14 +467,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
> >
> > #endif
> >
> > -static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> > - unsigned long timeout)
> > +void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> > + unsigned long timeout)
> > {
> > if (sdhci_data_line_cmd(mrq->cmd))
> > mod_timer(&host->data_timer, timeout);
> > else
> > mod_timer(&host->timer, timeout);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_mod_timer);
> >
> > static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > @@ -1043,8 +1047,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
> > __sdhci_set_timeout(host, cmd);
> > }
> >
> > -static void sdhci_initialize_data(struct sdhci_host *host,
> > - struct mmc_data *data)
> > +void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data)
> > {
> > WARN_ON(host->data);
> >
> > @@ -1057,6 +1060,7 @@ static void sdhci_initialize_data(struct sdhci_host *host,
> > host->data_early = 0;
> > host->data->bytes_xfered = 0;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_initialize_data);
> >
> > static inline void sdhci_set_block_info(struct sdhci_host *host,
> > struct mmc_data *data)
> > @@ -1079,12 +1083,8 @@ static inline void sdhci_set_block_info(struct sdhci_host *host,
> > }
> > }
> >
> > -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
> > +void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data)
> > {
> > - struct mmc_data *data = cmd->data;
> > -
> > - sdhci_initialize_data(host, data);
> > -
> > if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> > struct scatterlist *sg;
> > unsigned int length_mask, offset_mask;
> > @@ -1169,6 +1169,16 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
> > }
> >
> > sdhci_set_transfer_irqs(host);
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_prepare_dma);
> > +
> > +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
> > +{
> > + struct mmc_data *data = cmd->data;
> > +
> > + sdhci_initialize_data(host, data);
> > +
> > + sdhci_prepare_dma(host, data);
> >
> > sdhci_set_block_info(host, data);
> > }
> > @@ -1211,8 +1221,7 @@ static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> > return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
> > }
> >
> > -static int sdhci_external_dma_setup(struct sdhci_host *host,
> > - struct mmc_command *cmd)
> > +int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
> > {
> > int ret, i;
> > enum dma_transfer_direction dir;
> > @@ -1264,8 +1273,9 @@ static int sdhci_external_dma_setup(struct sdhci_host *host,
> >
> > return ret;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_external_dma_setup);
> >
> > -static void sdhci_external_dma_release(struct sdhci_host *host)
> > +void sdhci_external_dma_release(struct sdhci_host *host)
> > {
> > if (host->tx_chan) {
> > dma_release_channel(host->tx_chan);
> > @@ -1279,9 +1289,10 @@ static void sdhci_external_dma_release(struct sdhci_host *host)
> >
> > sdhci_switch_external_dma(host, false);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_external_dma_release);
> >
> > -static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> > - struct mmc_command *cmd)
> > +void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > {
> > struct mmc_data *data = cmd->data;
> >
> > @@ -1292,6 +1303,7 @@ static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> >
> > sdhci_set_block_info(host, data);
> > }
> > +EXPORT_SYMBOL(__sdhci_external_dma_prepare_data);
> >
> > static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
> > struct mmc_command *cmd)
> > @@ -1306,8 +1318,8 @@ static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
> > }
> > }
> >
> > -static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > - struct mmc_command *cmd)
> > +void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > + struct mmc_command *cmd)
> > {
> > struct dma_chan *chan;
> >
> > @@ -1318,6 +1330,7 @@ static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > if (chan)
> > dma_async_issue_pending(chan);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_external_dma_pre_transfer);
> >
> > #else
> >
> > @@ -1369,11 +1382,11 @@ static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
> > return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
> > }
> >
> > -static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
> > - struct mmc_request *mrq)
> > +bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_manual_cmd23);
> >
> > static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
> > struct mmc_command *cmd,
> > @@ -1483,7 +1496,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> > WARN_ON(i >= SDHCI_MAX_MRQS);
> > }
> >
> > -static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> > +void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > if (host->cmd && host->cmd->mrq == mrq)
> > host->cmd = NULL;
> > @@ -1507,15 +1520,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> > if (!sdhci_has_requests(host))
> > sdhci_led_deactivate(host);
> > }
> > +EXPORT_SYMBOL_GPL(__sdhci_finish_mrq);
> >
> > -static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> > +void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > __sdhci_finish_mrq(host, mrq);
> >
> > queue_work(host->complete_wq, &host->complete_work);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_finish_mrq);
> >
> > -static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> > +void __sdhci_finish_data_common(struct sdhci_host *host)
> > {
> > struct mmc_command *data_cmd = host->data_cmd;
> > struct mmc_data *data = host->data;
> > @@ -1548,6 +1563,14 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> > data->bytes_xfered = 0;
> > else
> > data->bytes_xfered = data->blksz * data->blocks;
> > +}
> > +EXPORT_SYMBOL_GPL(__sdhci_finish_data_common);
> > +
> > +static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> > +{
> > + struct mmc_data *data = host->data;
> > +
> > + __sdhci_finish_data_common(host);
> >
> > /*
> > * Need to send CMD12 if -
> > @@ -1586,12 +1609,13 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
> > }
> > }
> >
> > -static void sdhci_finish_data(struct sdhci_host *host)
> > +void sdhci_finish_data(struct sdhci_host *host)
> > {
> > __sdhci_finish_data(host, false);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_finish_data);
> >
> > -static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> > +bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> > {
> > int flags;
> > u32 mask;
> > @@ -1633,8 +1657,6 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> > sdhci_prepare_data(host, cmd);
> > }
> >
> > - sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
> > -
> > sdhci_set_transfer_mode(host, cmd);
> >
> > if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
> > @@ -1678,13 +1700,16 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> > if (host->use_external_dma)
> > sdhci_external_dma_pre_transfer(host, cmd);
> >
> > + sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
> > +
> > sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
> >
> > return true;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_send_command);
> >
> > -static bool sdhci_present_error(struct sdhci_host *host,
> > - struct mmc_command *cmd, bool present)
> > +bool sdhci_present_error(struct sdhci_host *host,
> > + struct mmc_command *cmd, bool present)
> > {
> > if (!present || host->flags & SDHCI_DEVICE_DEAD) {
> > cmd->error = -ENOMEDIUM;
> > @@ -1693,6 +1718,7 @@ static bool sdhci_present_error(struct sdhci_host *host,
> >
> > return false;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_present_error);
> >
> > static bool sdhci_send_command_retry(struct sdhci_host *host,
> > struct mmc_command *cmd,
> > @@ -1755,7 +1781,7 @@ static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
> > }
> > }
> >
> > -static void sdhci_finish_command(struct sdhci_host *host)
> > +void sdhci_finish_command(struct sdhci_host *host)
> > {
> > struct mmc_command *cmd = host->cmd;
> >
> > @@ -1808,6 +1834,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
> > __sdhci_finish_mrq(host, cmd->mrq);
> > }
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_finish_command);
> >
> > static u16 sdhci_get_preset_value(struct sdhci_host *host)
> > {
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index e84ebddb20d8..6eeb74741da3 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -831,8 +831,29 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> > __sdhci_read_caps(host, NULL, NULL, NULL);
> > }
> >
> > +bool sdhci_data_line_cmd(struct mmc_command *cmd);
> > void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
> > void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> > +void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> > + unsigned long timeout);
> > +void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
> > +void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data);
> > +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
> > +int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd);
> > +void sdhci_external_dma_release(struct sdhci_host *host);
> > +void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> > + struct mmc_command *cmd);
> > +void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > + struct mmc_command *cmd);
> > +#endif
> > +bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
> > +void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> > +void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> > +void __sdhci_finish_data_common(struct sdhci_host *host);
> > +bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
> > +void sdhci_finish_command(struct sdhci_host *host);
> > +bool sdhci_present_error(struct sdhci_host *host,
> > + struct mmc_command *cmd, bool present);
> > u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
> > unsigned int *actual_clock);
> > void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
> >
>
Adrian,
Thank you for your review comments.
On Thu, Nov 26, 2020 at 10:18:55AM +0200, Adrian Hunter wrote:
> On 25/11/20 9:41 am, AKASHI Takahiro wrote:
> > Gentle ping;
> >
> > On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
> >> This is an interim snapshot of our next version, v4, for enabling
> >> UHS-II on MMC/SD.
> >>
> >> It is focused on 'sdhci' side to address Adrian's comments regarding
> >> "modularising" sdhci-uhs2.c.
> >> The whole aim of this version is to get early feedback from Adrian (and
> >> others) on this issue. Without any consensus about the code structure,
> >
> > Any comments so far?
> >
>
> Overall, I like this approach of separating UHS2 from legacy sdhci as much
> as possible. The only major change, is to drop support for legacy quirks
> and features that you do not need. The reason for that, is that there may
> be few drivers that end up with UHS-II support (opting instead for SD
> Express), so there is no point going to a lot of trouble to support things
> that never get used.
>
> From what I have seen that looks like it includes:
> - any quirks
GLI driver (gl9755) needs
* SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
* SDHCI_QUIRK2_BROKEN_DDR50
but they are managed in sdhci code.
> - SDHCI LED support
> - external DMA support
Should we add 'depends on !SDHCI_UHS2' to MMC_SDHCI_EXTERNAL_DMA?
> In this regard, the important thing is to have a comment somewhere that
> lists what is not supported.
>
> I have only looked at SDHCI patches so far, and only up to about patch 20,
> but maybe that gives you enough to go on for a while.
Well, I have almost done.
Can I expect your comments on the patches #21-#27 as well soon?
-Takahiro Akashi
On Thu, Nov 26, 2020 at 10:17:38AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init
> > operation. After detected, the host's UHS-II capabilities will be set up
> > here and interrupts will also be enabled.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 160 ++++++++++++++++++++++++++++++++++
> > 1 file changed, 160 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 637464748cc4..994dff967e85 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -391,12 +391,172 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > * *
> > \*****************************************************************************/
> >
> > +static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
> > +{
> > + int timeout = 100;
>
> Please comment on where timeouts / delays come from. e.g. as per spec
It does exist in the original code, but I will try to find the source
of information.
> > +
> > + udelay(200); /* wait for 200us before check */
> > +
> > + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
> > + SDHCI_UHS2_IF_DETECT)) {
>
>
> Can be read_poll_timeout(sdhci_readl,...,host, SDHCI_PRESENT_STATE)
Okay.
> > + if (timeout == 0) {
> > + pr_warn("%s: not detect UHS2 interface in 200us.\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return -EIO;
> > + }
> > + timeout--;
> > + mdelay(1);
> > + }
> > +
> > + /* Enable UHS2 error interrupts */
> > + sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
> > + SDHCI_UHS2_ERR_INT_STATUS_MASK);
> > +
> > + timeout = 150;
> > + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
> > + SDHCI_UHS2_LANE_SYNC)) {
>
> Ditto
Okay.
-Takahiro Akashi
>
> > + if (timeout == 0) {
> > + pr_warn("%s: UHS2 Lane sync fail in 150ms.\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return -EIO;
> > + }
> > + timeout--;
> > + mdelay(1);
> > + }
> > +
> > + DBG("%s: UHS2 Lane synchronized in UHS2 mode, PHY is initialized.\n",
> > + mmc_hostname(host->mmc));
> > + return 0;
> > +}
> > +
> > +static int sdhci_uhs2_init(struct sdhci_host *host)
> > +{
> > + u16 caps_ptr = 0;
> > + u32 caps_gen = 0;
> > + u32 caps_phy = 0;
> > + u32 caps_tran[2] = {0, 0};
> > + struct mmc_host *mmc = host->mmc;
> > +
> > + /*
> > + * TODO: may add corresponding members in sdhci_host to
> > + * keep these caps.
> > + */
> > + caps_ptr = sdhci_readw(host, SDHCI_UHS2_HOST_CAPS_PTR);
> > + if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
> > + pr_err("%s: SDHCI_UHS2_HOST_CAPS_PTR(%d) is wrong.\n",
> > + mmc_hostname(mmc), caps_ptr);
> > + return -ENODEV;
> > + }
> > + caps_gen = sdhci_readl(host,
> > + caps_ptr + SDHCI_UHS2_HOST_CAPS_GEN_OFFSET);
> > + caps_phy = sdhci_readl(host,
> > + caps_ptr + SDHCI_UHS2_HOST_CAPS_PHY_OFFSET);
> > + caps_tran[0] = sdhci_readl(host,
> > + caps_ptr + SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET);
> > + caps_tran[1] = sdhci_readl(host,
> > + caps_ptr
> > + + SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET);
> > +
> > + /* General Caps */
> > + mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK;
> > + mmc->uhs2_caps.gap = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK) >>
> > + SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT;
> > + mmc->uhs2_caps.n_lanes = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT;
> > + mmc->uhs2_caps.addr64 =
> > + (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64) ? 1 : 0;
> > + mmc->uhs2_caps.card_type =
> > + (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK) >>
> > + SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT;
> > +
> > + /* PHY Caps */
> > + mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK;
> > + mmc->uhs2_caps.speed_range =
> > + (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT;
> > + mmc->uhs2_caps.n_lss_sync =
> > + (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT;
> > + mmc->uhs2_caps.n_lss_dir =
> > + (caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT;
> > + if (mmc->uhs2_caps.n_lss_sync == 0)
> > + mmc->uhs2_caps.n_lss_sync = 16 << 2;
> > + else
> > + mmc->uhs2_caps.n_lss_sync <<= 2;
> > + if (mmc->uhs2_caps.n_lss_dir == 0)
> > + mmc->uhs2_caps.n_lss_dir = 16 << 3;
> > + else
> > + mmc->uhs2_caps.n_lss_dir <<= 3;
> > +
> > + /* LINK/TRAN Caps */
> > + mmc->uhs2_caps.link_rev =
> > + caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_LINK_REV_MASK;
> > + mmc->uhs2_caps.n_fcu =
> > + (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT;
> > + if (mmc->uhs2_caps.n_fcu == 0)
> > + mmc->uhs2_caps.n_fcu = 256;
> > + mmc->uhs2_caps.host_type =
> > + (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT;
> > + mmc->uhs2_caps.maxblk_len =
> > + (caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK)
> > + >> SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT;
> > + mmc->uhs2_caps.n_data_gap =
> > + caps_tran[1] & SDHCI_UHS2_HOST_CAPS_TRAN_1_N_DATA_GAP_MASK;
> > +
> > + return 0;
> > +}
> > +
> > +static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + unsigned long flags;
> > + int ret = -EIO;
> > +
> > + DBG("%s: begin UHS2 init.\n", __func__);
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (sdhci_uhs2_interface_detect(host)) {
> > + pr_warn("%s: cannot detect UHS2 interface.\n",
> > + mmc_hostname(host->mmc));
> > + goto out;
> > + }
> > +
> > + if (sdhci_uhs2_init(host)) {
> > + pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
> > + goto out;
> > + }
> > +
> > + /* Init complete, do soft reset and enable UHS2 error irqs. */
> > + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
> > + sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
> > + SDHCI_UHS2_ERR_INT_STATUS_MASK);
> > + /*
> > + * !!! SDHCI_INT_ENABLE and SDHCI_SIGNAL_ENABLE was cleared
> > + * by SDHCI_UHS2_SW_RESET_SD
> > + */
> > + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> > + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> > +
> > + ret = 0;
> > +out:
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return ret;
> > +}
> > +
> > static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> > {
> > host->mmc_host_ops.start_signal_voltage_switch =
> > sdhci_uhs2_start_signal_voltage_switch;
> > host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
> >
> > + if (!host->mmc_host_ops.uhs2_detect_init)
> > + host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
> > +
> > return 0;
> > }
> >
> >
>
On Thu, Nov 26, 2020 at 10:18:26AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a sdhci version of mmc's uhs2_set_reg operation.
> > UHS-II interface (related registers) will be initialised here.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 103 ++++++++++++++++++++++++++++++++++
> > 1 file changed, 103 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 55362ace1857..d8afb99a9918 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -332,6 +332,68 @@ static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +/* TODO: move this function to sdhci.c */
> > +static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
> > +{
> > + u32 ier;
> > +
> > + ier = sdhci_readl(host, SDHCI_INT_ENABLE);
> > + ier &= ~clear;
> > + ier |= set;
> > + sdhci_writel(host, ier, SDHCI_INT_ENABLE);
> > + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
> > +}
> > +
> > +static void sdhci_uhs2_set_config(struct sdhci_host *host)
> > +{
> > + u32 value;
> > + u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SET_PTR);
> > + u16 sdhci_uhs2_gen_set_reg = (sdhci_uhs2_set_ptr + 0);
> > + u16 sdhci_uhs2_phy_set_reg = (sdhci_uhs2_set_ptr + 4);
> > + u16 sdhci_uhs2_tran_set_reg = (sdhci_uhs2_set_ptr + 8);
> > + u16 sdhci_uhs2_tran_set_1_reg = (sdhci_uhs2_set_ptr + 12);
> > +
> > + /* Set Gen Settings */
> > + sdhci_writel(host, host->mmc->uhs2_caps.n_lanes_set <<
> > + SDHCI_UHS2_GEN_SET_N_LANES_POS, sdhci_uhs2_gen_set_reg);
> > +
> > + /* Set PHY Settings */
> > + value = (host->mmc->uhs2_caps.n_lss_dir_set <<
> > + SDHCI_UHS2_PHY_SET_N_LSS_DIR_POS) |
> > + (host->mmc->uhs2_caps.n_lss_sync_set <<
> > + SDHCI_UHS2_PHY_SET_N_LSS_SYN_POS);
> > + if (host->mmc->flags & MMC_UHS2_SPEED_B)
> > + value |= 1 << SDHCI_UHS2_PHY_SET_SPEED_POS;
> > + sdhci_writel(host, value, sdhci_uhs2_phy_set_reg);
> > +
> > + /* Set LINK-TRAN Settings */
> > + value = (host->mmc->uhs2_caps.max_retry_set <<
> > + SDHCI_UHS2_TRAN_SET_RETRY_CNT_POS) |
> > + (host->mmc->uhs2_caps.n_fcu_set <<
> > + SDHCI_UHS2_TRAN_SET_N_FCU_POS);
> > + sdhci_writel(host, value, sdhci_uhs2_tran_set_reg);
> > + sdhci_writel(host, host->mmc->uhs2_caps.n_data_gap_set,
> > + sdhci_uhs2_tran_set_1_reg);
> > +}
> > +
> > +static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
> > +{
> > + int timeout = 100;
> > +
> > + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
> > + SDHCI_UHS2_IN_DORMANT_STATE)) {
>
> Can use read_poll_timeout(sdhci_readl,..., host, SDHCI_PRESENT_STATE)
Okay
-Takahiro Akashi
> > + if (timeout == 0) {
> > + pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n",
> > + mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return -EIO;
> > + }
> > + timeout--;
> > + mdelay(1);
> > + }
> > + return 0;
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * MMC callbacks *
> > @@ -422,6 +484,45 @@ static void sdhci_uhs2_enable_clk(struct mmc_host *mmc)
> > }
> > }
> >
> > +static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + unsigned long flags;
> > + int err = 0;
> > + u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SET_PTR);
> > + u16 sdhci_uhs2_phy_set_reg = (sdhci_uhs2_set_ptr + 4);
> > +
> > + DBG("Begin sdhci_uhs2_set_reg, act %d.\n", act);
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + switch (act) {
> > + case SET_CONFIG:
> > + sdhci_uhs2_set_config(host);
> > + break;
> > + case ENABLE_INT:
> > + sdhci_clear_set_irqs(host, 0, SDHCI_INT_CARD_INT);
> > + break;
> > + case DISABLE_INT:
> > + sdhci_clear_set_irqs(host, SDHCI_INT_CARD_INT, 0);
> > + break;
> > + case SET_SPEED_B:
> > + sdhci_writeb(host, 1 << SDHCI_UHS2_PHY_SET_SPEED_POS,
> > + sdhci_uhs2_phy_set_reg);
> > + break;
> > + case CHECK_DORMANT:
> > + err = sdhci_uhs2_check_dormant(host);
> > + break;
> > + default:
> > + pr_err("%s: input action %d is wrong!\n",
> > + mmc_hostname(host->mmc), act);
> > + err = -EIO;
> > + break;
> > + }
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return err;
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > @@ -597,6 +698,8 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> > host->mmc_host_ops.uhs2_disable_clk = sdhci_uhs2_disable_clk;
> > if (!host->mmc_host_ops.uhs2_enable_clk)
> > host->mmc_host_ops.uhs2_enable_clk = sdhci_uhs2_enable_clk;
> > + if (!host->mmc_host_ops.uhs2_set_reg)
> > + host->mmc_host_ops.uhs2_set_reg = sdhci_uhs2_set_reg;
> >
> > return 0;
> > }
> >
>
On 1/12/20 4:40 am, AKASHI Takahiro wrote:
> On Thu, Nov 26, 2020 at 10:18:44AM +0200, Adrian Hunter wrote:
>> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
>>> This is a sdhci version of mmc's request operation.
>>> It covers both UHS-I and UHS-II.
>>>
>>> Signed-off-by: Ben Chuang <[email protected]>
>>> Signed-off-by: AKASHI Takahiro <[email protected]>
>>> ---
>>> drivers/mmc/host/sdhci-uhs2.c | 529 ++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci.c | 93 +++---
>>> drivers/mmc/host/sdhci.h | 21 ++
>>> 3 files changed, 610 insertions(+), 33 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
>>> index d8afb99a9918..36e52553977a 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.c
>>> +++ b/drivers/mmc/host/sdhci-uhs2.c
>>> @@ -13,6 +13,7 @@
>>> #include <linux/delay.h>
>>> #include <linux/ktime.h>
>>> #include <linux/module.h>
>>> +#include <linux/mmc/mmc.h>
>>>
>>> #include "sdhci.h"
>>> #include "sdhci-uhs2.h"
>>> @@ -394,6 +395,27 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
>>> return 0;
>>> }
>>>
>>> +/* TODO: move them to a header */
>>> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
>>> +static inline void sdhci_led_activate(struct sdhci_host *host)
>>> +{
>>> +}
>>> +
>>> +static inline void sdhci_led_deactivate(struct sdhci_host *host)
>>> +{
>>> +}
>>> +#else
>>> +static inline void sdhci_led_activate(struct sdhci_host *host)
>>> +{
>>> + __sdhci_led_activate(host);
>>> +}
>>> +
>>> +static inline void sdhci_led_deactivate(struct sdhci_host *host)
>>> +{
>>> + __sdhci_led_deactivate(host);
>>> +}
>>> +#endif
>>
>> Unless you need LED support, let's drop it
>
> Okay
>
>>> +
>>> /*****************************************************************************\
>>> * *
>>> * MMC callbacks *
>>> @@ -523,6 +545,512 @@ static int sdhci_uhs2_set_reg(struct mmc_host *mmc, enum uhs2_act act)
>>> return err;
>>> }
>>>
>>> +static bool sdhci_uhs2_send_command(struct sdhci_host *host,
>>> + struct mmc_command *cmd);
>>> +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
>>> + struct mmc_command *cmd,
>>> + unsigned long flags);
>>
>> Always order functions so that early function declarations are not needed
>
> Will try.
>
>>> +
>>> +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
>>> +{
>>> + struct sdhci_host *host = mmc_priv(mmc);
>>> + struct mmc_command *cmd;
>>> + unsigned long flags;
>>> + bool present;
>>> +
>>> + /* FIXME: check more flags? */
>>> + if (!host->mmc->flags & MMC_UHS2_SUPPORT) {
>>
>>
>> if (sdhci_uhs2_mode(host))
>>
>>> + sdhci_request(mmc, mrq);
>>> + return;
>>> + }
>>> +
>>> + /* Firstly check card presence */
>>> + present = mmc->ops->get_cd(mmc);
>>
>> Checking CD here is another legacy hangover. Should be able to check the
>> bus power instead. i.e. if the card has been removed, the bus power (bit-0
>> Power Control reg) should get set to 0. That can be checked under spin lock.
>
> Replace it with check against SDHCI_POWER_ON.
>
>>> +
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + sdhci_led_activate(host);
>>
>> Drop support for sdhci LEDs unless yoy are using them
>
> Sure
>
>>> +
>>> + if (sdhci_present_error(host, mrq->cmd, present))
>>> + goto out_finish;
>>> +
>>> + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
>>
>> UHS2 doesn't use CMD23 does it?
>
> I don't know. Will replace it with
> cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
UHS-II spec says to set LM=1 and set TLEN instead of using CMD23.
Just ignore mrq->sbc (set block count) i.e. cmd = mrq->cmd
>
>>> +
>>> + if (!sdhci_uhs2_send_command_retry(host, cmd, flags))
>>
>> Don't use sdhci_uhs2_send_command_retry. sdhci_send_command_retry is again
>> dealing with legacy hangover. The inhibit bits should not be set. If they
>> are it can be an error immediately. So just use sdhci_uhs2_send_commend()
>
> Will follow your suggestion.
>
>>> + goto out_finish;
>>> +
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +
>>> + return;
>>> +
>>> +out_finish:
>>> + sdhci_finish_mrq(host, mrq);
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request);
>>> +
>>> +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
>>
>> Should end up being able simply to call sdhci_uhs2_request (or vice versa)
>
> Well, there is one difference,i.e. whether we should check "card presence"
> (or POWER_ON), isn't it?
> I think we should preserve this difference.
>
> Or we may simply remove _atomic version since there is no user for now.
> Which do you prefer?
Checking card-detect (GPIO) is not guaranteed to be atomic, but reading the
bus power register to check presence is. So if you do that, then the
request function can be atomic. I guess the _request_atomic() one is the
one to implement, and then the _request() fn can call _request_atomic()
>
>>> +{
>>> + struct sdhci_host *host = mmc_priv(mmc);
>>> + struct mmc_command *cmd;
>>> + unsigned long flags;
>>> + int ret = 0;
>>> +
>>> + if (!host->mmc->flags & MMC_UHS2_SUPPORT)
>>> + return sdhci_request_atomic(mmc, mrq);
>>> +
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + if (sdhci_present_error(host, mrq->cmd, true)) {
>>> + sdhci_finish_mrq(host, mrq);
>>> + goto out_finish;
>>> + }
>>> +
>>> + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
>>> +
>>> + /*
>>> + * The HSQ may send a command in interrupt context without polling
>>> + * the busy signaling, which means we should return BUSY if controller
>>> + * has not released inhibit bits to allow HSQ trying to send request
>>> + * again in non-atomic context. So we should not finish this request
>>> + * here.
>>> + */
>>> + if (!sdhci_uhs2_send_command(host, cmd))
>>> + ret = -EBUSY;
>>> + else
>>> + sdhci_led_activate(host);
>>> +
>>> +out_finish:
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
>>> +
>>> +/*****************************************************************************\
>>> + * *
>>> + * Core functions *
>>> + * *
>>> +\*****************************************************************************/
>>> +
>>> +static void sdhci_uhs2_prepare_data(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> + struct mmc_data *data = cmd->data;
>>> +
>>> + sdhci_initialize_data(host, data);
>>> +
>>> + sdhci_prepare_dma(host, data);
>>> +
>>> + sdhci_writew(host, data->blksz, SDHCI_UHS2_BLOCK_SIZE);
>>> + sdhci_writew(host, data->blocks, SDHCI_UHS2_BLOCK_COUNT);
>>> +}
>>> +
>>> +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
>>> +static void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> + if (!sdhci_external_dma_setup(host, cmd)) {
>>> + __sdhci_external_dma_prepare_data(host, cmd);
>>> + } else {
>>> + sdhci_external_dma_release(host);
>>> + pr_err("%s: Cannot use external DMA, switch to the DMA/PIO which standard SDHCI provides.\n",
>>> + mmc_hostname(host->mmc));
>>> + sdhci_uhs2_prepare_data(host, cmd);
>>> + }
>>> +}
>>> +#else
>>> +static inline void sdhci_uhs2_external_dma_prepare_data(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> + /* This should never happen */
>>> + WARN_ON_ONCE(1);
>>> +}
>>> +
>>> +static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> +}
>>> +#endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
>>
>> Let's start without support for external DMA - unless you need it
>
> Okay
>
>>> +
>>> +static void sdhci_uhs2_finish_data(struct sdhci_host *host)
>>> +{
>>> + struct mmc_data *data = host->data;
>>> +
>>> + __sdhci_finish_data_common(host);
>>> +
>>> + /*
>>> + * FIXME: Is this condition needed?
>>> + if (host->mmc->flags & MMC_UHS2_INITIALIZED)
>>> + */
>>> + __sdhci_finish_mrq(host, data->mrq);
>>> +}
>>> +
>>> +static void sdhci_uhs2_set_transfer_mode(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> + u16 mode;
>>> + struct mmc_data *data = cmd->data;
>>> + u16 arg;
>>> +
>>> + if (!data) {
>>> + /* clear Auto CMD settings for no data CMDs */
>>> + arg = cmd->uhs2_cmd->arg;
>>> + if ((((arg & 0xF) << 8) | ((arg >> 8) & 0xFF)) ==
>>> + UHS2_DEV_CMD_TRANS_ABORT) {
>>> + mode = 0;
>>> + } else {
>>> + mode = sdhci_readw(host, SDHCI_UHS2_TRANS_MODE);
>>> + if (cmd->opcode == MMC_STOP_TRANSMISSION ||
>>> + cmd->opcode == MMC_ERASE)
>>> + mode |= SDHCI_UHS2_TRNS_WAIT_EBSY;
>>> + else
>>> + /* send status mode */
>>> + if (cmd->opcode == MMC_SEND_STATUS)
>>> + mode = 0;
>>> + }
>>> +
>>> + if (IS_ENABLED(CONFIG_MMC_DEBUG))
>>> + DBG("UHS2 no data trans mode is 0x%x.\n", mode);
>>> +
>>> + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
>>> + return;
>>> + }
>>> +
>>> + WARN_ON(!host->data);
>>> +
>>> + mode = SDHCI_UHS2_TRNS_BLK_CNT_EN | SDHCI_UHS2_TRNS_WAIT_EBSY;
>>> + if (data->flags & MMC_DATA_WRITE)
>>> + mode |= SDHCI_UHS2_TRNS_DATA_TRNS_WRT;
>>> +
>>> + if (data->blocks == 1 &&
>>> + data->blksz != 512 &&
>>> + cmd->opcode != MMC_READ_SINGLE_BLOCK &&
>>> + cmd->opcode != MMC_WRITE_BLOCK) {
>>> + mode &= ~SDHCI_UHS2_TRNS_BLK_CNT_EN;
>>> + mode |= SDHCI_UHS2_TRNS_BLK_BYTE_MODE;
>>> + }
>>> +
>>> + if (host->flags & SDHCI_REQ_USE_DMA)
>>> + mode |= SDHCI_UHS2_TRNS_DMA;
>>> +
>>> + if ((host->mmc->flags & MMC_UHS2_2L_HD) && !cmd->uhs2_tmode0_flag)
>>> + mode |= SDHCI_UHS2_TRNS_2L_HD;
>>> +
>>> + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
>>> +
>>> + if (IS_ENABLED(CONFIG_MMC_DEBUG))
>>> + DBG("UHS2 trans mode is 0x%x.\n", mode);
>>> +}
>>> +
>>> +static void __sdhci_uhs2_send_command(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> + int i, j;
>>> + int cmd_reg;
>>> +
>>> + if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
>>> + if (!cmd->uhs2_cmd) {
>>> + pr_err("%s: fatal error, no uhs2_cmd!\n",
>>> + mmc_hostname(host->mmc));
>>> + return;
>>> + }
>>> + }
>>> +
>>> + i = 0;
>>> + sdhci_writel(host,
>>> + ((u32)cmd->uhs2_cmd->arg << 16) |
>>> + (u32)cmd->uhs2_cmd->header,
>>> + SDHCI_UHS2_CMD_PACKET + i);
>>> + i += 4;
>>> +
>>> + /*
>>> + * Per spec, playload (config) should be MSB before sending out.
>>> + * But we don't need convert here because had set payload as
>>> + * MSB when preparing config read/write commands.
>>> + */
>>> + for (j = 0; j < cmd->uhs2_cmd->payload_len / sizeof(u32); j++) {
>>> + sdhci_writel(host, *(cmd->uhs2_cmd->payload + j),
>>> + SDHCI_UHS2_CMD_PACKET + i);
>>> + i += 4;
>>> + }
>>> +
>>> + for ( ; i < SDHCI_UHS2_CMD_PACK_MAX_LEN; i += 4)
>>> + sdhci_writel(host, 0, SDHCI_UHS2_CMD_PACKET + i);
>>> +
>>> + if (IS_ENABLED(CONFIG_MMC_DEBUG)) {
>>> + DBG("UHS2 CMD packet_len = %d.\n", cmd->uhs2_cmd->packet_len);
>>> + for (i = 0; i < cmd->uhs2_cmd->packet_len; i++)
>>> + DBG("UHS2 CMD_PACKET[%d] = 0x%x.\n", i,
>>> + sdhci_readb(host, SDHCI_UHS2_CMD_PACKET + i));
>>> + }
>>> +
>>> + cmd_reg = cmd->uhs2_cmd->packet_len <<
>>> + SDHCI_UHS2_COMMAND_PACK_LEN_SHIFT;
>>> + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
>>> + cmd_reg |= SDHCI_UHS2_COMMAND_DATA;
>>> + if (cmd->opcode == MMC_STOP_TRANSMISSION)
>>> + cmd_reg |= SDHCI_UHS2_COMMAND_CMD12;
>>> +
>>> + /* UHS2 Native ABORT */
>>> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
>>> + ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
>>> + ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) == UHS2_DEV_CMD_TRANS_ABORT))
>>> + cmd_reg |= SDHCI_UHS2_COMMAND_TRNS_ABORT;
>>> +
>>> + /* UHS2 Native DORMANT */
>>> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
>>> + ((((cmd->uhs2_cmd->arg & 0xF) << 8) |
>>> + ((cmd->uhs2_cmd->arg >> 8) & 0xFF)) ==
>>> + UHS2_DEV_CMD_GO_DORMANT_STATE))
>>> + cmd_reg |= SDHCI_UHS2_COMMAND_DORMANT;
>>> +
>>> + DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
>>> +
>>> + sdhci_writew(host, cmd_reg, SDHCI_UHS2_COMMAND);
>>> +}
>>> +
>>> +static bool sdhci_uhs2_send_command(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> +{
>>> + int flags;
>>> + u32 mask;
>>> + unsigned long timeout;
>>> +
>>> + /* FIXME: Is this check necessary? */
>>
>> No
>
> Will drop it.
>
>>
>>> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
>>> + return sdhci_send_command(host, cmd);
>>> +
>>> + WARN_ON(host->cmd);
>>> +
>>> + /* Initially, a command has no error */
>>> + cmd->error = 0;
>>> +
>>> + if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
>>
>> Quirk
>
> Will drop it.
>
>>
>>> + cmd->opcode == MMC_STOP_TRANSMISSION)
>>> + cmd->flags |= MMC_RSP_BUSY;
>>> +
>>> + mask = SDHCI_CMD_INHIBIT;
>>> + if (sdhci_data_line_cmd(cmd))
>>> + mask |= SDHCI_DATA_INHIBIT;
>>
>> Command inhibit for data is not supported by UHS2
>
> Will drop it.
>
>>> +
>>> + /* We shouldn't wait for data inihibit for stop commands, even
>>> + though they might use busy signaling */
>>> + if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
>>> + mask &= ~SDHCI_DATA_INHIBIT;
>>
>> Ditto
>
> Will drop it.
>
>>
>>> +
>>> + if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
>>> + return false;
>>> +
>>> + host->cmd = cmd;
>>> + host->data_timeout = 0;
>>> + if (sdhci_data_line_cmd(cmd)) {
>>> + WARN_ON(host->data_cmd);
>>> + host->data_cmd = cmd;
>>> + __sdhci_uhs2_set_timeout(host);
>>> + }
>>> +
>>> + if (cmd->data) {
>>> + if (host->use_external_dma)
>>> + sdhci_uhs2_external_dma_prepare_data(host, cmd);
>>
>> To start with, let's drop support for external DMA with UHS2. i.e. disallow
>> UHS2 mode if external DMA is enabled
>
> Okay.
> I will add a check against host->use_external in uhs2_do_detect_init().
>
>>
>>> + else
>>> + sdhci_uhs2_prepare_data(host, cmd);
>>> + }
>>> +
>>> + sdhci_uhs2_set_transfer_mode(host, cmd);
>>> +
>>> + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
>>> + WARN_ONCE(1, "Unsupported response type!\n");
>>> + /*
>>> + * This does not happen in practice because 136-bit response
>>> + * commands never have busy waiting, so rather than complicate
>>> + * the error path, just remove busy waiting and continue.
>>> + */
>>> + cmd->flags &= ~MMC_RSP_BUSY;
>>> + }
>>> +
>>> + if (!(cmd->flags & MMC_RSP_PRESENT))
>>> + flags = SDHCI_CMD_RESP_NONE;
>>> + else if (cmd->flags & MMC_RSP_136)
>>> + flags = SDHCI_CMD_RESP_LONG;
>>> + else if (cmd->flags & MMC_RSP_BUSY)
>>> + flags = SDHCI_CMD_RESP_SHORT_BUSY;
>>> + else
>>> + flags = SDHCI_CMD_RESP_SHORT;
>>> +
>>> + if (cmd->flags & MMC_RSP_CRC)
>>> + flags |= SDHCI_CMD_CRC;
>>> + if (cmd->flags & MMC_RSP_OPCODE)
>>> + flags |= SDHCI_CMD_INDEX;
>>> +
>>> + /* CMD19 is special in that the Data Present Select should be set */
>>> + if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
>>> + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
>>
>> No tuning commands fo UHS2
>>
>>> + flags |= SDHCI_CMD_DATA;
>>
>> flags are never used?
>
> Good catch. Will drop it.
>
>>> +
>>> + timeout = jiffies;
>>> + if (host->data_timeout)
>>> + timeout += nsecs_to_jiffies(host->data_timeout);
>>> + else if (!cmd->data && cmd->busy_timeout > 9000)
>>> + timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
>>> + else
>>> + timeout += 10 * HZ;
>>> + sdhci_mod_timer(host, cmd->mrq, timeout);
>>> +
>>> + if (host->use_external_dma)
>>> + sdhci_external_dma_pre_transfer(host, cmd);
>>> +
>>> + __sdhci_uhs2_send_command(host, cmd);
>>> +
>>> + return true;
>>> +}
>>> +
>>> +static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
>>> + struct mmc_command *cmd,
>>> + unsigned long flags)
>>
>> Can probably get rid of sdhci_uhs2_send_command_retry
>
> Sure
>
>>> + __releases(host->lock)
>>> + __acquires(host->lock)
>>> +{
>>> + struct mmc_command *deferred_cmd = host->deferred_cmd;
>>> + int timeout = 10; /* Approx. 10 ms */
>>> + bool present;
>>> +
>>> + while (!sdhci_uhs2_send_command(host, cmd)) {
>>> + if (!timeout--) {
>>> + pr_err("%s: Controller never released inhibit bit(s).\n",
>>> + mmc_hostname(host->mmc));
>>> + sdhci_dumpregs(host);
>>> + cmd->error = -EIO;
>>> + return false;
>>> + }
>>> +
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +
>>> + usleep_range(1000, 1250);
>>> +
>>> + present = host->mmc->ops->get_cd(host->mmc);
>>> +
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + /* A deferred command might disappear, handle that */
>>> + if (cmd == deferred_cmd && cmd != host->deferred_cmd)
>>> + return true;
>>> +
>>> + if (sdhci_present_error(host, cmd, present))
>>> + return false;
>>> + }
>>> +
>>> + if (cmd == host->deferred_cmd)
>>> + host->deferred_cmd = NULL;
>>> +
>>> + return true;
>>> +}
>>> +
>>> +static void __sdhci_uhs2_finish_command(struct sdhci_host *host)
>>> +{
>>> + struct mmc_command *cmd = host->cmd;
>>> + u8 resp;
>>> + u8 ecode;
>>> + bool bReadA0 = 0;
>>> + int i;
>>> +
>>> + if (host->mmc->flags & MMC_UHS2_INITIALIZED) {
>>> + resp = sdhci_readb(host, SDHCI_UHS2_RESPONSE + 2);
>>> + if (resp & UHS2_RES_NACK_MASK) {
>>> + ecode = (resp >> UHS2_RES_ECODE_POS) &
>>> + UHS2_RES_ECODE_MASK;
>>> + pr_err("%s: NACK is got, ECODE=0x%x.\n",
>>> + mmc_hostname(host->mmc), ecode);
>>> + }
>>> + bReadA0 = 1;
>>> + }
>>> +
>>> + if (cmd->uhs2_resp &&
>>> + cmd->uhs2_resp_len && cmd->uhs2_resp_len <= 20) {
>>> + /* Get whole response of some native CCMD, like
>>> + * DEVICE_INIT, ENUMERATE.
>>> + */
>>> + for (i = 0; i < cmd->uhs2_resp_len; i++)
>>> + cmd->uhs2_resp[i] =
>>> + sdhci_readb(host, SDHCI_UHS2_RESPONSE + i);
>>> + } else {
>>> + /* Get SD CMD response and Payload for some read
>>> + * CCMD, like INQUIRY_CFG.
>>> + */
>>> + /* Per spec (p136), payload field is divided into
>>> + * a unit of DWORD and transmission order within
>>> + * a DWORD is big endian.
>>> + */
>>> + if (!bReadA0)
>>> + sdhci_readl(host, SDHCI_UHS2_RESPONSE);
>>> + for (i = 4; i < 20; i += 4) {
>>> + cmd->resp[i / 4 - 1] =
>>> + (sdhci_readb(host,
>>> + SDHCI_UHS2_RESPONSE + i) << 24) |
>>> + (sdhci_readb(host,
>>> + SDHCI_UHS2_RESPONSE + i + 1)
>>> + << 16) |
>>> + (sdhci_readb(host,
>>> + SDHCI_UHS2_RESPONSE + i + 2)
>>> + << 8) |
>>> + sdhci_readb(host, SDHCI_UHS2_RESPONSE + i + 3);
>>> + }
>>> + }
>>> +}
>>> +
>>> +static void sdhci_uhs2_finish_command(struct sdhci_host *host)
>>> +{
>>> + struct mmc_command *cmd = host->cmd;
>>> +
>>> + /* FIXME: Is this check necessary? */
>>> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT)) {
>>> + sdhci_finish_command(host);
>>> + return;
>>> + }
>>> +
>>> + __sdhci_uhs2_finish_command(host);
>>> +
>>> + host->cmd = NULL;
>>> +
>>> + if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd)
>>> + mmc_command_done(host->mmc, cmd->mrq);
>>> +
>>> + /*
>>> + * The host can send and interrupt when the busy state has
>>> + * ended, allowing us to wait without wasting CPU cycles.
>>> + * The busy signal uses DAT0 so this is similar to waiting
>>> + * for data to complete.
>>> + *
>>> + * Note: The 1.0 specification is a bit ambiguous about this
>>> + * feature so there might be some problems with older
>>> + * controllers.
>>> + */
>>> + if (cmd->flags & MMC_RSP_BUSY) {
>>> + if (cmd->data) {
>>> + DBG("Cannot wait for busy signal when also doing a data transfer");
>>> + } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
>>> + cmd == host->data_cmd) {
>>> + /* Command complete before busy is ended */
>>> + return;
>>> + }
>>> + }
>>> +
>>> + /* Finished CMD23, now send actual command. */
>>
>> UHS2 doesn't use CMD23 does it?
>
> Should we remove any code in this function, other than this comment?
We should be able to assume (cmd != cmd->mrq->sbc) is always true because
sbc is never used.
>
> -Takahiro Akashi
>
>>> + if (cmd == cmd->mrq->sbc) {
>>> + if (!sdhci_uhs2_send_command(host, cmd->mrq->cmd)) {
>>> + WARN_ON(host->deferred_cmd);
>>> + host->deferred_cmd = cmd->mrq->cmd;
>>> + }
>>> + } else {
>>> +
>>> + /* Processed actual command. */
>>> + if (host->data && host->data_early)
>>> + sdhci_uhs2_finish_data(host);
>>> +
>>> + if (!cmd->data)
>>> + __sdhci_finish_mrq(host, cmd->mrq);
>>> + }
>>> +}
>>> +
>>> /*****************************************************************************\
>>> * *
>>> * Driver init/exit *
>>> @@ -691,6 +1219,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
>>> host->mmc_host_ops.start_signal_voltage_switch =
>>> sdhci_uhs2_start_signal_voltage_switch;
>>> host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
>>> + host->mmc_host_ops.request = sdhci_uhs2_request;
>>>
>>> if (!host->mmc_host_ops.uhs2_detect_init)
>>> host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
>>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>>> index becb228330af..a9f5449bddcc 100644
>>> --- a/drivers/mmc/host/sdhci.c
>>> +++ b/drivers/mmc/host/sdhci.c
>>> @@ -48,8 +48,6 @@
>>> static unsigned int debug_quirks = 0;
>>> static unsigned int debug_quirks2;
>>>
>>> -static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
>>> -
>>> void sdhci_dumpregs(struct sdhci_host *host)
>>> {
>>> SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
>>> @@ -148,10 +146,13 @@ void sdhci_enable_v4_mode(struct sdhci_host *host)
>>> }
>>> EXPORT_SYMBOL_GPL(sdhci_enable_v4_mode);
>>>
>>> -static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
>>> +bool sdhci_data_line_cmd(struct mmc_command *cmd)
>>> {
>>> return cmd->data || cmd->flags & MMC_RSP_BUSY;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_data_line_cmd);
>>> +
>>> +/* TODO: move this as an inline function to a header */
>>>
>>> static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
>>> {
>>> @@ -363,7 +364,7 @@ static void sdhci_reinit(struct sdhci_host *host)
>>> mmc_detect_change(host->mmc, msecs_to_jiffies(200));
>>> }
>>>
>>> -static void __sdhci_led_activate(struct sdhci_host *host)
>>> +void __sdhci_led_activate(struct sdhci_host *host)
>>> {
>>> u8 ctrl;
>>>
>>> @@ -374,8 +375,9 @@ static void __sdhci_led_activate(struct sdhci_host *host)
>>> ctrl |= SDHCI_CTRL_LED;
>>> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
>>> }
>>> +EXPORT_SYMBOL_GPL(__sdhci_led_activate);
>>>
>>> -static void __sdhci_led_deactivate(struct sdhci_host *host)
>>> +void __sdhci_led_deactivate(struct sdhci_host *host)
>>> {
>>> u8 ctrl;
>>>
>>> @@ -386,6 +388,7 @@ static void __sdhci_led_deactivate(struct sdhci_host *host)
>>> ctrl &= ~SDHCI_CTRL_LED;
>>> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
>>> }
>>> +EXPORT_SYMBOL_GPL(__sdhci_led_deactivate);
>>>
>>> #if IS_REACHABLE(CONFIG_LEDS_CLASS)
>>> static void sdhci_led_control(struct led_classdev *led,
>>> @@ -464,14 +467,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
>>>
>>> #endif
>>>
>>> -static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
>>> - unsigned long timeout)
>>> +void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
>>> + unsigned long timeout)
>>> {
>>> if (sdhci_data_line_cmd(mrq->cmd))
>>> mod_timer(&host->data_timer, timeout);
>>> else
>>> mod_timer(&host->timer, timeout);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_mod_timer);
>>>
>>> static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> @@ -1043,8 +1047,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
>>> __sdhci_set_timeout(host, cmd);
>>> }
>>>
>>> -static void sdhci_initialize_data(struct sdhci_host *host,
>>> - struct mmc_data *data)
>>> +void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data)
>>> {
>>> WARN_ON(host->data);
>>>
>>> @@ -1057,6 +1060,7 @@ static void sdhci_initialize_data(struct sdhci_host *host,
>>> host->data_early = 0;
>>> host->data->bytes_xfered = 0;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_initialize_data);
>>>
>>> static inline void sdhci_set_block_info(struct sdhci_host *host,
>>> struct mmc_data *data)
>>> @@ -1079,12 +1083,8 @@ static inline void sdhci_set_block_info(struct sdhci_host *host,
>>> }
>>> }
>>>
>>> -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>>> +void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data)
>>> {
>>> - struct mmc_data *data = cmd->data;
>>> -
>>> - sdhci_initialize_data(host, data);
>>> -
>>> if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
>>> struct scatterlist *sg;
>>> unsigned int length_mask, offset_mask;
>>> @@ -1169,6 +1169,16 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>>> }
>>>
>>> sdhci_set_transfer_irqs(host);
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_prepare_dma);
>>> +
>>> +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>>> +{
>>> + struct mmc_data *data = cmd->data;
>>> +
>>> + sdhci_initialize_data(host, data);
>>> +
>>> + sdhci_prepare_dma(host, data);
>>>
>>> sdhci_set_block_info(host, data);
>>> }
>>> @@ -1211,8 +1221,7 @@ static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
>>> return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
>>> }
>>>
>>> -static int sdhci_external_dma_setup(struct sdhci_host *host,
>>> - struct mmc_command *cmd)
>>> +int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
>>> {
>>> int ret, i;
>>> enum dma_transfer_direction dir;
>>> @@ -1264,8 +1273,9 @@ static int sdhci_external_dma_setup(struct sdhci_host *host,
>>>
>>> return ret;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_external_dma_setup);
>>>
>>> -static void sdhci_external_dma_release(struct sdhci_host *host)
>>> +void sdhci_external_dma_release(struct sdhci_host *host)
>>> {
>>> if (host->tx_chan) {
>>> dma_release_channel(host->tx_chan);
>>> @@ -1279,9 +1289,10 @@ static void sdhci_external_dma_release(struct sdhci_host *host)
>>>
>>> sdhci_switch_external_dma(host, false);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_external_dma_release);
>>>
>>> -static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>> - struct mmc_command *cmd)
>>> +void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> {
>>> struct mmc_data *data = cmd->data;
>>>
>>> @@ -1292,6 +1303,7 @@ static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>>
>>> sdhci_set_block_info(host, data);
>>> }
>>> +EXPORT_SYMBOL(__sdhci_external_dma_prepare_data);
>>>
>>> static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>> struct mmc_command *cmd)
>>> @@ -1306,8 +1318,8 @@ static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>> }
>>> }
>>>
>>> -static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> - struct mmc_command *cmd)
>>> +void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> + struct mmc_command *cmd)
>>> {
>>> struct dma_chan *chan;
>>>
>>> @@ -1318,6 +1330,7 @@ static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> if (chan)
>>> dma_async_issue_pending(chan);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_external_dma_pre_transfer);
>>>
>>> #else
>>>
>>> @@ -1369,11 +1382,11 @@ static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
>>> return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
>>> }
>>>
>>> -static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
>>> - struct mmc_request *mrq)
>>> +bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_manual_cmd23);
>>>
>>> static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
>>> struct mmc_command *cmd,
>>> @@ -1483,7 +1496,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
>>> WARN_ON(i >= SDHCI_MAX_MRQS);
>>> }
>>>
>>> -static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
>>> +void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> if (host->cmd && host->cmd->mrq == mrq)
>>> host->cmd = NULL;
>>> @@ -1507,15 +1520,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
>>> if (!sdhci_has_requests(host))
>>> sdhci_led_deactivate(host);
>>> }
>>> +EXPORT_SYMBOL_GPL(__sdhci_finish_mrq);
>>>
>>> -static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
>>> +void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> __sdhci_finish_mrq(host, mrq);
>>>
>>> queue_work(host->complete_wq, &host->complete_work);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_finish_mrq);
>>>
>>> -static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
>>> +void __sdhci_finish_data_common(struct sdhci_host *host)
>>> {
>>> struct mmc_command *data_cmd = host->data_cmd;
>>> struct mmc_data *data = host->data;
>>> @@ -1548,6 +1563,14 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
>>> data->bytes_xfered = 0;
>>> else
>>> data->bytes_xfered = data->blksz * data->blocks;
>>> +}
>>> +EXPORT_SYMBOL_GPL(__sdhci_finish_data_common);
>>> +
>>> +static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
>>> +{
>>> + struct mmc_data *data = host->data;
>>> +
>>> + __sdhci_finish_data_common(host);
>>>
>>> /*
>>> * Need to send CMD12 if -
>>> @@ -1586,12 +1609,13 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
>>> }
>>> }
>>>
>>> -static void sdhci_finish_data(struct sdhci_host *host)
>>> +void sdhci_finish_data(struct sdhci_host *host)
>>> {
>>> __sdhci_finish_data(host, false);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_finish_data);
>>>
>>> -static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>>> +bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>>> {
>>> int flags;
>>> u32 mask;
>>> @@ -1633,8 +1657,6 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>>> sdhci_prepare_data(host, cmd);
>>> }
>>>
>>> - sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
>>> -
>>> sdhci_set_transfer_mode(host, cmd);
>>>
>>> if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
>>> @@ -1678,13 +1700,16 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>>> if (host->use_external_dma)
>>> sdhci_external_dma_pre_transfer(host, cmd);
>>>
>>> + sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
>>> +
>>> sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
>>>
>>> return true;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_send_command);
>>>
>>> -static bool sdhci_present_error(struct sdhci_host *host,
>>> - struct mmc_command *cmd, bool present)
>>> +bool sdhci_present_error(struct sdhci_host *host,
>>> + struct mmc_command *cmd, bool present)
>>> {
>>> if (!present || host->flags & SDHCI_DEVICE_DEAD) {
>>> cmd->error = -ENOMEDIUM;
>>> @@ -1693,6 +1718,7 @@ static bool sdhci_present_error(struct sdhci_host *host,
>>>
>>> return false;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_present_error);
>>>
>>> static bool sdhci_send_command_retry(struct sdhci_host *host,
>>> struct mmc_command *cmd,
>>> @@ -1755,7 +1781,7 @@ static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
>>> }
>>> }
>>>
>>> -static void sdhci_finish_command(struct sdhci_host *host)
>>> +void sdhci_finish_command(struct sdhci_host *host)
>>> {
>>> struct mmc_command *cmd = host->cmd;
>>>
>>> @@ -1808,6 +1834,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
>>> __sdhci_finish_mrq(host, cmd->mrq);
>>> }
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_finish_command);
>>>
>>> static u16 sdhci_get_preset_value(struct sdhci_host *host)
>>> {
>>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>>> index e84ebddb20d8..6eeb74741da3 100644
>>> --- a/drivers/mmc/host/sdhci.h
>>> +++ b/drivers/mmc/host/sdhci.h
>>> @@ -831,8 +831,29 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
>>> __sdhci_read_caps(host, NULL, NULL, NULL);
>>> }
>>>
>>> +bool sdhci_data_line_cmd(struct mmc_command *cmd);
>>> void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
>>> void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
>>> +void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
>>> + unsigned long timeout);
>>> +void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
>>> +void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data);
>>> +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
>>> +int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd);
>>> +void sdhci_external_dma_release(struct sdhci_host *host);
>>> +void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>> + struct mmc_command *cmd);
>>> +void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> + struct mmc_command *cmd);
>>> +#endif
>>> +bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
>>> +void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
>>> +void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
>>> +void __sdhci_finish_data_common(struct sdhci_host *host);
>>> +bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
>>> +void sdhci_finish_command(struct sdhci_host *host);
>>> +bool sdhci_present_error(struct sdhci_host *host,
>>> + struct mmc_command *cmd, bool present);
>>> u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
>>> unsigned int *actual_clock);
>>> void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
>>>
>>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a UHS-II version of sdhci's request() operation.
> It handles UHS-II related command interrupts and errors.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 247 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 3 +
> drivers/mmc/host/sdhci.c | 112 ++++++++-------
> drivers/mmc/host/sdhci.h | 6 +
> 4 files changed, 319 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 36e52553977a..d50134e912f3 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -11,6 +11,7 @@
> */
>
> #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> #include <linux/ktime.h>
> #include <linux/module.h>
> #include <linux/mmc/mmc.h>
> @@ -670,6 +671,12 @@ static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> struct mmc_command *cmd)
> {
> }
> +
> +static inline struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> + struct mmc_data *data)
> +{
> + return NULL;
> +}
> #endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
>
> static void sdhci_uhs2_finish_data(struct sdhci_host *host)
> @@ -1051,6 +1058,246 @@ static void sdhci_uhs2_finish_command(struct sdhci_host *host)
> }
> }
>
> +/*****************************************************************************\
> + * *
> + * Request done *
> + * *
> +\*****************************************************************************/
> +
> +static bool sdhci_uhs2_request_done(struct sdhci_host *host)
> +{
> + unsigned long flags;
> + struct mmc_request *mrq;
> + int i;
> +
> + /* FIXME: UHS2_INITIALIZED, instead? */
> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
> + return sdhci_request_done(host);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + for (i = 0; i < SDHCI_MAX_MRQS; i++) {
> + mrq = host->mrqs_done[i];
> + if (mrq)
> + break;
> + }
> +
> + if (!mrq) {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return true;
> + }
> +
> + /*
> + * Always unmap the data buffers if they were mapped by
> + * sdhci_prepare_data() whenever we finish with a request.
> + * This avoids leaking DMA mappings on error.
> + */
> + if (host->flags & SDHCI_REQ_USE_DMA) {
> + struct mmc_data *data = mrq->data;
> +
> + if (host->use_external_dma && data &&
> + (mrq->cmd->error || data->error)) {
> + struct dma_chan *chan = sdhci_external_dma_channel(host, data);
> +
> + host->mrqs_done[i] = NULL;
> + spin_unlock_irqrestore(&host->lock, flags);
> + dmaengine_terminate_sync(chan);
> + spin_lock_irqsave(&host->lock, flags);
> + sdhci_set_mrq_done(host, mrq);
> + }
> +
> + sdhci_request_done_dma(host, mrq);
> + }
> +
> + /*
> + * The controller needs a reset of internal state machines
> + * upon error conditions.
> + */
> + if (sdhci_needs_reset(host, mrq)) {
> + /*
> + * Do not finish until command and data lines are available for
> + * reset. Note there can only be one other mrq, so it cannot
> + * also be in mrqs_done, otherwise host->cmd and host->data_cmd
> + * would both be null.
> + */
> + if (host->cmd || host->data_cmd) {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return true;
> + }
> +
> + /* Some controllers need this kick or reset won't work here */
> + if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
> + /* This is to force an update */
> + host->ops->set_clock(host, host->clock);
> +
> + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
It should be possible to use the ->reset() callback for UHS-II also, then
this function wouldn't be needed.
> + host->pending_reset = false;
> + }
> +
> + host->mrqs_done[i] = NULL;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + if (host->ops->request_done)
> + host->ops->request_done(host, mrq);
> + else
> + mmc_request_done(host->mmc, mrq);
> +
> + return false;
> +}
> +
> +static void sdhci_uhs2_complete_work(struct work_struct *work)
> +{
> + struct sdhci_host *host = container_of(work, struct sdhci_host,
> + complete_work);
> +
> + while (!sdhci_uhs2_request_done(host))
> + ;
> +}
> +
> +/*****************************************************************************\
> + * *
> + * Interrupt handling *
> + * *
> +\*****************************************************************************/
> +
> +static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
> +{
> + struct mmc_command *cmd = host->cmd;
> +
> + DBG("*** %s got UHS2 error interrupt: 0x%08x\n",
> + mmc_hostname(host->mmc), uhs2mask);
> +
> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK) {
> + if (!host->cmd) {
> + pr_err("%s: Got cmd interrupt 0x%08x but no cmd.\n",
> + mmc_hostname(host->mmc),
> + (unsigned int)uhs2mask);
> + sdhci_dumpregs(host);
> + return;
> + }
> + host->cmd->error = -EILSEQ;
> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
> + host->cmd->error = -ETIMEDOUT;
> + }
> +
> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK) {
> + if (!host->data) {
> + pr_err("%s: Got data interrupt 0x%08x but no data.\n",
> + mmc_hostname(host->mmc),
> + (unsigned int)uhs2mask);
> + sdhci_dumpregs(host);
> + return;
> + }
> +
> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT) {
> + pr_err("%s: Got deadlock timeout interrupt 0x%08x\n",
> + mmc_hostname(host->mmc),
> + (unsigned int)uhs2mask);
> + host->data->error = -ETIMEDOUT;
> + } else if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_ADMA) {
> + pr_err("%s: ADMA error = 0x %x\n",
> + mmc_hostname(host->mmc),
> + sdhci_readb(host, SDHCI_ADMA_ERROR));
> + host->data->error = -EIO;
> + } else {
> + host->data->error = -EILSEQ;
> + }
> + }
> +
> + if (host->data && host->data->error)
> + sdhci_uhs2_finish_data(host);
> + else
> + sdhci_finish_mrq(host, cmd->mrq);
> +}
> +
> +u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
> +{
> + u32 mask = intmask, uhs2mask;
> +
> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
> + goto out;
> +
> + /*
> + * TODO: We should mask Normal Error Interrupt Status Register
> + * in UHS-2 mode so that we don't have to care SD mode errors.
> + */
> + if (intmask & SDHCI_INT_ERROR) {
> + uhs2mask = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS);
> + if (!(uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK))
> + goto cmd_irq;
> +
> + /* Clear error interrupts */
> + sdhci_writel(host, uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK,
> + SDHCI_UHS2_ERR_INT_STATUS);
> +
> + /* Handle error interrupts */
> + __sdhci_uhs2_irq(host, uhs2mask);
> +
> + /* Caller, shdci_irq(), doesn't have to care UHS-2 errors */
> + intmask &= ~SDHCI_INT_ERROR;
> + mask &= SDHCI_INT_ERROR;
> + }
> +
> +cmd_irq:
> + /*
> + * TODO: Cleanup
> + * INT_RESPONSE is enough instead of INT_CMD_MASK, assuming that
> + * INT_ERROR and INT_CMD_MASK won't happen at the same time.
> + */
> + if (intmask & SDHCI_INT_CMD_MASK) {
> + /* Clear command interrupt */
> + sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
> + SDHCI_INT_STATUS);
> +
> + /* Handle command interrupt */
> + if (intmask & SDHCI_INT_RESPONSE)
> + sdhci_uhs2_finish_command(host);
> +
> + /* Caller, shdci_irq(), doesn't have to care UHS-2 command */
> + intmask &= ~SDHCI_INT_CMD_MASK;
> + mask &= SDHCI_INT_CMD_MASK;
> + }
> +
> + /* Clear already-handled interrupts. */
> + sdhci_writel(host, mask, SDHCI_INT_STATUS);
> +
> +out:
> + return intmask;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_irq);
> +
> +static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
> +{
> + struct sdhci_host *host = dev_id;
> + struct mmc_command *cmd;
> + unsigned long flags;
> + u32 isr;
> +
> + while (!sdhci_uhs2_request_done(host))
> + ;
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + isr = host->thread_isr;
> + host->thread_isr = 0;
> +
> + cmd = host->deferred_cmd;
We don't need to use deferred_cmd, so then this function should not be
needed. For UHS-II we should require that the upper layer sends the abort
command, so all the driver needs to do to handle errors, is to reset - which
is already handled by sdhci_request_done() if you make use of the ->reset()
callback.
> + if (cmd && !sdhci_uhs2_send_command_retry(host, cmd, flags))
> + sdhci_finish_mrq(host, cmd->mrq);
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
> + struct mmc_host *mmc = host->mmc;
> +
> + mmc->ops->card_event(mmc);
> + mmc_detect_change(mmc, msecs_to_jiffies(200));
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> index c1ff4ac1ab7a..b74af641d00e 100644
> --- a/drivers/mmc/host/sdhci-uhs2.h
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -215,5 +215,8 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> unsigned short vdd);
> void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
> void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
> +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
> +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
> +u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
>
> #endif /* __SDHCI_UHS2_H */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index a9f5449bddcc..e2dfc7767bcf 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1215,11 +1215,12 @@ static int sdhci_external_dma_init(struct sdhci_host *host)
> return ret;
> }
>
> -static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> - struct mmc_data *data)
> +struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> + struct mmc_data *data)
> {
> return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
> }
> +EXPORT_SYMBOL_GPL(sdhci_external_dma_channel);
>
> int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
> {
> @@ -1466,7 +1467,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
> sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> }
>
> -static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
> +bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
> {
> return (!(host->flags & SDHCI_DEVICE_DEAD) &&
> ((mrq->cmd && mrq->cmd->error) ||
> @@ -1474,8 +1475,9 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
> (mrq->data && mrq->data->stop && mrq->data->stop->error) ||
> (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
> }
> +EXPORT_SYMBOL_GPL(sdhci_needs_reset);
>
> -static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> +void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> {
> int i;
>
> @@ -1495,6 +1497,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
>
> WARN_ON(i >= SDHCI_MAX_MRQS);
> }
> +EXPORT_SYMBOL_GPL(sdhci_set_mrq_done);
>
> void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> {
> @@ -3029,7 +3032,56 @@ static const struct mmc_host_ops sdhci_ops = {
> * *
> \*****************************************************************************/
>
> -static bool sdhci_request_done(struct sdhci_host *host)
> +void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq)
> +{
> + struct mmc_data *data = mrq->data;
> +
> + if (data && data->host_cookie == COOKIE_MAPPED) {
> + if (host->bounce_buffer) {
> + /*
> + * On reads, copy the bounced data into the
> + * sglist
> + */
> + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
> + unsigned int length = data->bytes_xfered;
> +
> + if (length > host->bounce_buffer_size) {
> + pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
> + mmc_hostname(host->mmc),
> + host->bounce_buffer_size,
> + data->bytes_xfered);
> + /* Cap it down and continue */
> + length = host->bounce_buffer_size;
> + }
> + dma_sync_single_for_cpu(
> + host->mmc->parent,
> + host->bounce_addr,
> + host->bounce_buffer_size,
> + DMA_FROM_DEVICE);
> + sg_copy_from_buffer(data->sg,
> + data->sg_len,
> + host->bounce_buffer,
> + length);
> + } else {
> + /* No copying, just switch ownership */
> + dma_sync_single_for_cpu(
> + host->mmc->parent,
> + host->bounce_addr,
> + host->bounce_buffer_size,
> + mmc_get_dma_dir(data));
> + }
> + } else {
> + /* Unmap the raw data */
> + dma_unmap_sg(mmc_dev(host->mmc), data->sg,
> + data->sg_len,
> + mmc_get_dma_dir(data));
> + }
> + data->host_cookie = COOKIE_UNMAPPED;
> + }
> +}
> +EXPORT_SYMBOL_GPL(sdhci_request_done_dma);
> +
> +bool sdhci_request_done(struct sdhci_host *host)
> {
> unsigned long flags;
> struct mmc_request *mrq;
> @@ -3067,48 +3119,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
> sdhci_set_mrq_done(host, mrq);
> }
>
> - if (data && data->host_cookie == COOKIE_MAPPED) {
> - if (host->bounce_buffer) {
> - /*
> - * On reads, copy the bounced data into the
> - * sglist
> - */
> - if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
> - unsigned int length = data->bytes_xfered;
> -
> - if (length > host->bounce_buffer_size) {
> - pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
> - mmc_hostname(host->mmc),
> - host->bounce_buffer_size,
> - data->bytes_xfered);
> - /* Cap it down and continue */
> - length = host->bounce_buffer_size;
> - }
> - dma_sync_single_for_cpu(
> - host->mmc->parent,
> - host->bounce_addr,
> - host->bounce_buffer_size,
> - DMA_FROM_DEVICE);
> - sg_copy_from_buffer(data->sg,
> - data->sg_len,
> - host->bounce_buffer,
> - length);
> - } else {
> - /* No copying, just switch ownership */
> - dma_sync_single_for_cpu(
> - host->mmc->parent,
> - host->bounce_addr,
> - host->bounce_buffer_size,
> - mmc_get_dma_dir(data));
> - }
> - } else {
> - /* Unmap the raw data */
> - dma_unmap_sg(mmc_dev(host->mmc), data->sg,
> - data->sg_len,
> - mmc_get_dma_dir(data));
> - }
> - data->host_cookie = COOKIE_UNMAPPED;
> - }
> + sdhci_request_done_dma(host, mrq);
> }
>
> /*
> @@ -3132,8 +3143,10 @@ static bool sdhci_request_done(struct sdhci_host *host)
> /* This is to force an update */
> host->ops->set_clock(host, host->clock);
>
> - /* Spec says we should do both at the same time, but Ricoh
> - controllers do not like that. */
> + /*
> + * Spec says we should do both at the same time, but
> + * Ricoh controllers do not like that.
> + */
> sdhci_do_reset(host, SDHCI_RESET_CMD);
> sdhci_do_reset(host, SDHCI_RESET_DATA);
>
> @@ -3151,6 +3164,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
>
> return false;
> }
> +EXPORT_SYMBOL_GPL(sdhci_request_done);
>
> static void sdhci_complete_work(struct work_struct *work)
> {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 6eeb74741da3..74572b54ec47 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -845,8 +845,12 @@ void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> struct mmc_command *cmd);
> void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> struct mmc_command *cmd);
> +struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> + struct mmc_data *data);
> #endif
> bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
> +bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq);
> +void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq);
> void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> void __sdhci_finish_data_common(struct sdhci_host *host);
> @@ -878,6 +882,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
> int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> struct mmc_ios *ios);
> void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
> +void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
> +bool sdhci_request_done(struct sdhci_host *host);
> void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
> dma_addr_t addr, int len, unsigned int cmd);
>
>
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> This is a UHS-II version of sdhci's add_host/remove_host operation.
> Any sdhci drivers which are capable of handling UHS-II cards must
> call those functions instead of the corresponding sdhci's.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> ---
> drivers/mmc/host/sdhci-uhs2.c | 198 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 2 +
> drivers/mmc/host/sdhci.c | 24 +++--
> drivers/mmc/host/sdhci.h | 10 ++
> 4 files changed, 226 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index d50134e912f3..5d3362ea138f 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -15,6 +15,7 @@
> #include <linux/ktime.h>
> #include <linux/module.h>
> #include <linux/mmc/mmc.h>
> +#include <linux/regulator/consumer.h>
>
> #include "sdhci.h"
> #include "sdhci-uhs2.h"
> @@ -406,6 +407,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
> {
> }
> #else
> +static inline int sdhci_led_register(struct sdhci_host *host)
> +{
> + return 0;
> +}
> +
> +static inline void sdhci_led_unregister(struct sdhci_host *host)
> +{
> +}
> +
> static inline void sdhci_led_activate(struct sdhci_host *host)
> {
> __sdhci_led_activate(host);
> @@ -1298,6 +1308,194 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> +/*****************************************************************************\
> + *
> + * Device allocation/registration *
> + * *
> +\*****************************************************************************/
> +
> +static int __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
> +{
> + struct mmc_host *mmc;
> + u32 max_current_caps2;
> +
> + if (host->version < SDHCI_SPEC_400)
> + return 0;
> +
> + mmc = host->mmc;
> +
> + /* Support UHS2 */
> + if (caps1 & SDHCI_SUPPORT_UHS2)
> + mmc->caps |= MMC_CAP_UHS2;
> +
> + max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
> +
> + if ((caps1 & SDHCI_SUPPORT_VDD2_180) &&
> + !max_current_caps2 &&
> + !IS_ERR(mmc->supply.vmmc2)) {
> + /* UHS2 - VDD2 */
> + int curr = regulator_get_current_limit(mmc->supply.vmmc2);
> +
> + if (curr > 0) {
> + /* convert to SDHCI_MAX_CURRENT format */
> + curr = curr / 1000; /* convert to mA */
> + curr = curr / SDHCI_MAX_CURRENT_MULTIPLIER;
> + curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
> + max_current_caps2 = curr;
> + }
> + }
> +
> + if (caps1 & SDHCI_SUPPORT_VDD2_180) {
> + mmc->ocr_avail_uhs2 |= MMC_VDD2_165_195;
> + /*
> + * UHS2 doesn't require this. Only UHS-I bus needs to set
> + * max current.
> + */
> + mmc->max_current_180_vdd2 = (max_current_caps2 &
> + SDHCI_MAX_CURRENT_VDD2_180_MASK) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> + } else {
> + mmc->caps &= ~MMC_CAP_UHS2;
> + }
> +
> + return 0;
> +}
> +
> +static int sdhci_uhs2_host_ops_init(struct sdhci_host *host);
> +
> +static int __sdhci_uhs2_add_host(struct sdhci_host *host)
Can you leverage __sdhci_add_host() here?
> +{
> + unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
> + struct mmc_host *mmc = host->mmc;
> + int ret;
> +
> + if ((mmc->caps2 & MMC_CAP2_CQE) &&
> + (host->quirks & SDHCI_QUIRK_BROKEN_CQE)) {
> + mmc->caps2 &= ~MMC_CAP2_CQE;
> + mmc->cqe_ops = NULL;
> + }
> +
> + /* overwrite ops */
> + if (mmc->caps & MMC_CAP_UHS2)
> + sdhci_uhs2_host_ops_init(host);
> +
> + host->complete_wq = alloc_workqueue("sdhci", flags, 0);
> + if (!host->complete_wq)
> + return -ENOMEM;
> +
> + INIT_WORK(&host->complete_work, sdhci_uhs2_complete_work);
> +
> + timer_setup(&host->timer, sdhci_timeout_timer, 0);
> + timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
> +
> + init_waitqueue_head(&host->buf_ready_int);
> +
> + sdhci_init(host, 0);
> +
> + ret = request_threaded_irq(host->irq, sdhci_irq,
> + sdhci_uhs2_thread_irq,
> + IRQF_SHARED, mmc_hostname(mmc), host);
> + if (ret) {
> + pr_err("%s: Failed to request IRQ %d: %d\n",
> + mmc_hostname(mmc), host->irq, ret);
> + goto unwq;
> + }
> +
> + ret = sdhci_led_register(host);
> + if (ret) {
> + pr_err("%s: Failed to register LED device: %d\n",
> + mmc_hostname(mmc), ret);
> + goto unirq;
> + }
> +
> + ret = mmc_add_host(mmc);
> + if (ret)
> + goto unled;
> +
> + pr_info("%s: SDHCI controller on %s [%s] using %s\n",
> + mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
> + host->use_external_dma ? "External DMA" :
> + (host->flags & SDHCI_USE_ADMA) ?
> + (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
> + (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
> +
> + sdhci_enable_card_detection(host);
> +
> + return 0;
> +
> +unled:
> + sdhci_led_unregister(host);
> +unirq:
> + sdhci_do_reset(host, SDHCI_RESET_ALL);
> + sdhci_writel(host, 0, SDHCI_INT_ENABLE);
> + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
> + free_irq(host->irq, host);
> +unwq:
> + destroy_workqueue(host->complete_wq);
> +
> + return ret;
> +}
> +
> +static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
> +{
> + if (!(host->mmc) || !(host->mmc->flags & MMC_UHS2_SUPPORT))
> + return;
> +
> + if (!dead)
> + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
> +
> + sdhci_writel(host, 0, SDHCI_UHS2_ERR_INT_STATUS_EN);
> + sdhci_writel(host, 0, SDHCI_UHS2_ERR_INT_SIG_EN);
> + host->mmc->flags &= ~MMC_UHS2_INITIALIZED;
> +}
> +
> +int sdhci_uhs2_add_host(struct sdhci_host *host)
> +{
> + struct mmc_host *mmc = host->mmc;
> + int ret;
> +
> + ret = sdhci_setup_host(host);
> + if (ret)
> + return ret;
> +
> + if (host->version >= SDHCI_SPEC_400) {
> + ret = __sdhci_uhs2_add_host_v4(host, host->caps1);
> + if (ret)
> + goto cleanup;
> + }
> +
> + if ((mmc->caps & MMC_CAP_UHS2) && !host->v4_mode)
> + /* host doesn't want to enable UHS2 support */
> + /* FIXME: Do we have to do some cleanup here? */
> + mmc->caps &= ~MMC_CAP_UHS2;
> +
> + ret = __sdhci_uhs2_add_host(host);
> + if (ret)
> + goto cleanup2;
> +
> + return 0;
> +
> +cleanup2:
> + /*
> + * TODO: Is this a right cleanup?
> + */
> + if (host->version >= SDHCI_SPEC_400)
> + __sdhci_uhs2_remove_host(host, 0);
> +cleanup:
> + sdhci_cleanup_host(host);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_add_host);
> +
> +void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
> +{
> + __sdhci_uhs2_remove_host(host, dead);
> +
> + sdhci_uhs2_remove_host(host, dead);
Meant to be sdhci_remove_host() presumably?
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_remove_host);
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> index b74af641d00e..34e140f21284 100644
> --- a/drivers/mmc/host/sdhci-uhs2.h
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -218,5 +218,7 @@ void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
> void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
> int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
> u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
> +int sdhci_uhs2_add_host(struct sdhci_host *host);
> +void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
>
> #endif /* __SDHCI_UHS2_H */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index e2dfc7767bcf..d37c3edb1ed0 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -176,10 +176,11 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
> sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> }
>
> -static void sdhci_enable_card_detection(struct sdhci_host *host)
> +void sdhci_enable_card_detection(struct sdhci_host *host)
> {
> sdhci_set_card_detection(host, true);
> }
> +EXPORT_SYMBOL_GPL(sdhci_enable_card_detection);
>
> static void sdhci_disable_card_detection(struct sdhci_host *host)
> {
> @@ -237,7 +238,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
> }
> EXPORT_SYMBOL_GPL(sdhci_reset);
>
> -static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
> +void sdhci_do_reset(struct sdhci_host *host, u8 mask)
> {
> if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
> struct mmc_host *mmc = host->mmc;
> @@ -258,6 +259,7 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
> host->preset_enabled = false;
> }
> }
> +EXPORT_SYMBOL_GPL(sdhci_do_reset);
>
> static void sdhci_set_default_irqs(struct sdhci_host *host)
> {
> @@ -321,7 +323,7 @@ static void sdhci_config_dma(struct sdhci_host *host)
> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> }
>
> -static void sdhci_init(struct sdhci_host *host, int soft)
> +void sdhci_init(struct sdhci_host *host, int soft)
> {
> struct mmc_host *mmc = host->mmc;
> unsigned long flags;
> @@ -346,6 +348,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
> mmc->ops->set_ios(mmc, &mmc->ios);
> }
> }
> +EXPORT_SYMBOL_GPL(sdhci_init);
>
> static void sdhci_reinit(struct sdhci_host *host)
> {
> @@ -410,7 +413,7 @@ static void sdhci_led_control(struct led_classdev *led,
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> -static int sdhci_led_register(struct sdhci_host *host)
> +int sdhci_led_register(struct sdhci_host *host)
> {
> struct mmc_host *mmc = host->mmc;
>
> @@ -427,14 +430,16 @@ static int sdhci_led_register(struct sdhci_host *host)
>
> return led_classdev_register(mmc_dev(mmc), &host->led);
> }
> +EXPORT_SYMBOL_GPL(sdhci_led_register);
>
> -static void sdhci_led_unregister(struct sdhci_host *host)
> +void sdhci_led_unregister(struct sdhci_host *host)
> {
> if (host->quirks & SDHCI_QUIRK_NO_LED)
> return;
>
> led_classdev_unregister(&host->led);
> }
> +EXPORT_SYMBOL_GPL(sdhci_led_unregister);
>
> static inline void sdhci_led_activate(struct sdhci_host *host)
> {
> @@ -3175,7 +3180,7 @@ static void sdhci_complete_work(struct work_struct *work)
> ;
> }
>
> -static void sdhci_timeout_timer(struct timer_list *t)
> +void sdhci_timeout_timer(struct timer_list *t)
> {
> struct sdhci_host *host;
> unsigned long flags;
> @@ -3195,8 +3200,9 @@ static void sdhci_timeout_timer(struct timer_list *t)
>
> spin_unlock_irqrestore(&host->lock, flags);
> }
> +EXPORT_SYMBOL_GPL(sdhci_timeout_timer);
>
> -static void sdhci_timeout_data_timer(struct timer_list *t)
> +void sdhci_timeout_data_timer(struct timer_list *t)
> {
> struct sdhci_host *host;
> unsigned long flags;
> @@ -3226,6 +3232,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
>
> spin_unlock_irqrestore(&host->lock, flags);
> }
> +EXPORT_SYMBOL_GPL(sdhci_timeout_data_timer);
>
> /*****************************************************************************\
> * *
> @@ -3469,7 +3476,7 @@ static inline bool sdhci_defer_done(struct sdhci_host *host,
> data->host_cookie == COOKIE_MAPPED);
> }
>
> -static irqreturn_t sdhci_irq(int irq, void *dev_id)
> +irqreturn_t sdhci_irq(int irq, void *dev_id)
> {
> struct mmc_request *mrqs_done[SDHCI_MAX_MRQS] = {0};
> irqreturn_t result = IRQ_NONE;
> @@ -3609,6 +3616,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
>
> return result;
> }
> +EXPORT_SYMBOL_GPL(sdhci_irq);
>
> static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
> {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 74572b54ec47..b1d856664b58 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -832,8 +832,15 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> }
>
> bool sdhci_data_line_cmd(struct mmc_command *cmd);
> +void sdhci_enable_card_detection(struct sdhci_host *host);
> void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
> void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> +void sdhci_do_reset(struct sdhci_host *host, u8 mask);
> +void sdhci_init(struct sdhci_host *host, int soft);
> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> +int sdhci_led_register(struct sdhci_host *host);
> +void sdhci_led_unregister(struct sdhci_host *host);
> +#endif
> void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> unsigned long timeout);
> void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
> @@ -884,6 +891,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
> void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
> bool sdhci_request_done(struct sdhci_host *host);
> +void sdhci_timeout_timer(struct timer_list *t);
> +void sdhci_timeout_data_timer(struct timer_list *t);
> +irqreturn_t sdhci_irq(int irq, void *dev_id);
> void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
> dma_addr_t addr, int len, unsigned int cmd);
>
>
On 30/11/20 9:51 am, AKASHI Takahiro wrote:
> On Thu, Nov 26, 2020 at 10:17:11AM +0200, Adrian Hunter wrote:
>> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
>>> This is a sdhci version of mmc's set_ios operation.
>>> It covers both UHS-I and UHS-II.
>>>
>>> Signed-off-by: Ben Chuang <[email protected]>
>>> Signed-off-by: AKASHI Takahiro <[email protected]>
>>> ---
>>> drivers/mmc/host/sdhci-uhs2.c | 100 ++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci-uhs2.h | 1 +
>>> drivers/mmc/host/sdhci.c | 40 +++++++++-----
>>> drivers/mmc/host/sdhci.h | 2 +
>>> 4 files changed, 128 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
>>> index d9e98c097bfe..637464748cc4 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.c
>>> +++ b/drivers/mmc/host/sdhci-uhs2.c
>>> @@ -263,6 +263,74 @@ void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
>>> }
>>> EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
>>>
>>> +/**
>>> + * sdhci_uhs2_clear_set_irqs - set Error Interrupt Status Enable register
>>> + * @host: SDHCI host
>>> + * @clear: bit-wise clear mask
>>> + * @set: bit-wise set mask
>>> + *
>>> + * Set/unset bits in UHS-II Error Interrupt Status Enable register
>>> + */
>>> +void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
>>> +{
>>> + u32 ier;
>>> +
>>> + ier = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS_EN);
>>> + ier &= ~clear;
>>> + ier |= set;
>>> + sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_STATUS_EN);
>>> + sdhci_writel(host, ier, SDHCI_UHS2_ERR_INT_SIG_EN);
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_uhs2_clear_set_irqs);
>>> +
>>> +static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>> +{
>>> + struct sdhci_host *host = mmc_priv(mmc);
>>> + u8 cmd_res, dead_lock;
>>> + u16 ctrl_2;
>>> + unsigned long flags;
>>> +
>>> + /* FIXME: why lock? */
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + /* UHS2 Timeout Control */
>>> + sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
>>> +
>>> + /* change to use calculate value */
>>> + cmd_res |= dead_lock << SDHCI_UHS2_TIMER_CTRL_DEADLOCK_SHIFT;
>>> +
>>> + sdhci_uhs2_clear_set_irqs(host,
>>> + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
>>> + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT,
>>> + 0);
>>> + sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
>>> + sdhci_uhs2_clear_set_irqs(host, 0,
>>> + SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT |
>>> + SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT);
>>> +
>>> + /* UHS2 timing */
>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>> + if (ios->timing == MMC_TIMING_UHS2)
>>> + ctrl_2 |= SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN;
>>> + else
>>> + ctrl_2 &= ~(SDHCI_CTRL_UHS_2 | SDHCI_CTRL_UHS2_INTERFACE_EN);
>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>> +
>>> + if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
>>> + sdhci_enable_preset_value(host, true);
>>> +
>>> + if (host->ops->set_power)
>>> + host->ops->set_power(host, ios->power_mode, ios->vdd);
>>> + else
>>> + sdhci_set_power(host, ios->power_mode, ios->vdd);
>>> + udelay(100);
>>> +
>>> + host->timing = ios->timing;
>>> + sdhci_set_clock(host, host->clock);
>>
>> sdhci_set_ios_common() already called ->set_clock() and ->set_power(), so I
>> am not really following what is going on here. Can you explain some more?
>
> To be frank, I don't know. The logic in Intel's (and/or Ben's?)
> original code does so.
> What I changed is to remove the code of setting (ios->vdd and) ios->vdd2,
> which is executed before calling set_power(), in __sdhci_uhs2_set_ios().
>
> So yes, effectively it may be of no use to call set_power() here.
Please try to rationalize it. Also set_ios() should not need the spin lock,
and that allows clock and power callbacks to sleep if needed.
>
> -Takahiro Akashi
>
>>> +
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +}
>>> +
>>> /*****************************************************************************\
>>> * *
>>> * MMC callbacks *
>>> @@ -286,6 +354,37 @@ static int sdhci_uhs2_start_signal_voltage_switch(struct mmc_host *mmc,
>>> return sdhci_start_signal_voltage_switch(mmc, ios);
>>> }
>>>
>>> +void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>> +{
>>> + struct sdhci_host *host = mmc_priv(mmc);
>>> +
>>> + if (!(host->version >= SDHCI_SPEC_400) ||
>>> + !(host->mmc->flags & MMC_UHS2_SUPPORT &&
>>> + host->mmc->caps & MMC_CAP_UHS2)) {
>>> + sdhci_set_ios(mmc, ios);
>>> + return;
>>> + }
>>> +
>>> + if (ios->power_mode == MMC_POWER_UNDEFINED)
>>> + return;
>>> +
>>> + if (host->flags & SDHCI_DEVICE_DEAD) {
>>> + if (!IS_ERR(mmc->supply.vmmc) &&
>>> + ios->power_mode == MMC_POWER_OFF)
>>> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
>>> + if (!IS_ERR_OR_NULL(mmc->supply.vmmc2) &&
>>> + ios->power_mode == MMC_POWER_OFF)
>>> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
>>> + return;
>>> + }
>>> +
>>> + /* FIXME: host->timing = ios->timing */
>>> +
>>> + sdhci_set_ios_common(mmc, ios);
>>> +
>>> + __sdhci_uhs2_set_ios(mmc, ios);
>>> +}
>>> +
>>> /*****************************************************************************\
>>> * *
>>> * Driver init/exit *
>>> @@ -296,6 +395,7 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
>>> {
>>> host->mmc_host_ops.start_signal_voltage_switch =
>>> sdhci_uhs2_start_signal_voltage_switch;
>>> + host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
>>>
>>> return 0;
>>> }
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
>>> index efe70577bc74..c1ff4ac1ab7a 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.h
>>> +++ b/drivers/mmc/host/sdhci-uhs2.h
>>> @@ -214,5 +214,6 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
>>> void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
>>> unsigned short vdd);
>>> void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
>>> +void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
>>>
>>> #endif /* __SDHCI_UHS2_H */
>>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>>> index 0b741eb546cb..becb228330af 100644
>>> --- a/drivers/mmc/host/sdhci.c
>>> +++ b/drivers/mmc/host/sdhci.c
>>> @@ -48,8 +48,6 @@
>>> static unsigned int debug_quirks = 0;
>>> static unsigned int debug_quirks2;
>>>
>>> -static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
>>> -
>>> static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
>>>
>>> void sdhci_dumpregs(struct sdhci_host *host)
>>> @@ -1836,6 +1834,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
>>> case MMC_TIMING_MMC_HS400:
>>> preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
>>> break;
>>> + case MMC_TIMING_UHS2:
>>> + preset = sdhci_readw(host, SDHCI_PRESET_FOR_UHS2);
>>> + break;
>>> default:
>>> pr_warn("%s: Invalid UHS-I mode selected\n",
>>> mmc_hostname(host->mmc));
>>> @@ -2249,20 +2250,9 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
>>> }
>>> EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
>>>
>>> -void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>> +void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios)
>>> {
>>> struct sdhci_host *host = mmc_priv(mmc);
>>> - u8 ctrl;
>>> -
>>> - if (ios->power_mode == MMC_POWER_UNDEFINED)
>>> - return;
>>> -
>>> - if (host->flags & SDHCI_DEVICE_DEAD) {
>>> - if (!IS_ERR(mmc->supply.vmmc) &&
>>> - ios->power_mode == MMC_POWER_OFF)
>>> - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
>>> - return;
>>> - }
>>>
>>> /*
>>> * Reset the chip on each power off.
>>> @@ -2299,6 +2289,25 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>> host->ops->set_power(host, ios->power_mode, ios->vdd);
>>> else
>>> sdhci_set_power(host, ios->power_mode, ios->vdd);
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_set_ios_common);
>>> +
>>> +void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>> +{
>>> + struct sdhci_host *host = mmc_priv(mmc);
>>> + u8 ctrl;
>>> +
>>> + if (ios->power_mode == MMC_POWER_UNDEFINED)
>>> + return;
>>> +
>>> + if (host->flags & SDHCI_DEVICE_DEAD) {
>>> + if (!IS_ERR(mmc->supply.vmmc) &&
>>> + ios->power_mode == MMC_POWER_OFF)
>>> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
>>> + return;
>>> + }
>>> +
>>> + sdhci_set_ios_common(mmc, ios);
>>>
>>> if (host->ops->platform_send_init_74_clocks)
>>> host->ops->platform_send_init_74_clocks(host, ios->power_mode);
>>> @@ -2869,7 +2878,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
>>> }
>>> EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
>>>
>>> -static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
>>> +void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
>>> {
>>> /* Host Controller v3.00 defines preset value registers */
>>> if (host->version < SDHCI_SPEC_300)
>>> @@ -2897,6 +2906,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
>>> host->preset_enabled = enable;
>>> }
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_enable_preset_value);
>>>
>>> static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
>>> int err)
>>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>>> index 2b5b8295cf92..e84ebddb20d8 100644
>>> --- a/drivers/mmc/host/sdhci.h
>>> +++ b/drivers/mmc/host/sdhci.h
>>> @@ -851,6 +851,8 @@ void sdhci_set_bus_width(struct sdhci_host *host, int width);
>>> void sdhci_reset(struct sdhci_host *host, u8 mask);
>>> void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
>>> int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
>>> +void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
>>> +void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios);
>>> void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
>>> int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
>>> struct mmc_ios *ios);
>>>
>>
On 1/12/20 5:09 am, AKASHI Takahiro wrote:
> Adrian,
>
> Thank you for your review comments.
>
> On Thu, Nov 26, 2020 at 10:18:55AM +0200, Adrian Hunter wrote:
>> On 25/11/20 9:41 am, AKASHI Takahiro wrote:
>>> Gentle ping;
>>>
>>> On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
>>>> This is an interim snapshot of our next version, v4, for enabling
>>>> UHS-II on MMC/SD.
>>>>
>>>> It is focused on 'sdhci' side to address Adrian's comments regarding
>>>> "modularising" sdhci-uhs2.c.
>>>> The whole aim of this version is to get early feedback from Adrian (and
>>>> others) on this issue. Without any consensus about the code structure,
>>>
>>> Any comments so far?
>>>
>>
>> Overall, I like this approach of separating UHS2 from legacy sdhci as much
>> as possible. The only major change, is to drop support for legacy quirks
>> and features that you do not need. The reason for that, is that there may
>> be few drivers that end up with UHS-II support (opting instead for SD
>> Express), so there is no point going to a lot of trouble to support things
>> that never get used.
>>
>> From what I have seen that looks like it includes:
>> - any quirks
>
> GLI driver (gl9755) needs
> * SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
> * SDHCI_QUIRK2_BROKEN_DDR50
> but they are managed in sdhci code.
>
>> - SDHCI LED support
>> - external DMA support
>
> Should we add 'depends on !SDHCI_UHS2' to MMC_SDHCI_EXTERNAL_DMA?
>
>> In this regard, the important thing is to have a comment somewhere that
>> lists what is not supported.
>>
>> I have only looked at SDHCI patches so far, and only up to about patch 20,
>> but maybe that gives you enough to go on for a while.
>
> Well, I have almost done.
> Can I expect your comments on the patches #21-#27 as well soon?
I have made some more comments and that is all for now, except for anything
more you wish to discuss.
On 1/12/20 5:09 am, AKASHI Takahiro wrote:
> Adrian,
>
> Thank you for your review comments.
>
> On Thu, Nov 26, 2020 at 10:18:55AM +0200, Adrian Hunter wrote:
>> On 25/11/20 9:41 am, AKASHI Takahiro wrote:
>>> Gentle ping;
>>>
>>> On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
>>>> This is an interim snapshot of our next version, v4, for enabling
>>>> UHS-II on MMC/SD.
>>>>
>>>> It is focused on 'sdhci' side to address Adrian's comments regarding
>>>> "modularising" sdhci-uhs2.c.
>>>> The whole aim of this version is to get early feedback from Adrian (and
>>>> others) on this issue. Without any consensus about the code structure,
>>>
>>> Any comments so far?
>>>
>>
>> Overall, I like this approach of separating UHS2 from legacy sdhci as much
>> as possible. The only major change, is to drop support for legacy quirks
>> and features that you do not need. The reason for that, is that there may
>> be few drivers that end up with UHS-II support (opting instead for SD
>> Express), so there is no point going to a lot of trouble to support things
>> that never get used.
>>
>> From what I have seen that looks like it includes:
>> - any quirks
>
> GLI driver (gl9755) needs
> * SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
> * SDHCI_QUIRK2_BROKEN_DDR50
> but they are managed in sdhci code.
>
>> - SDHCI LED support
>> - external DMA support
>
> Should we add 'depends on !SDHCI_UHS2' to MMC_SDHCI_EXTERNAL_DMA?
No because SDHCI_UHS2 does not necessary mean UHS-II is actually being used
i.e. same kernel can support both even if no controller uses both
On Tue, Dec 01, 2020 at 06:46:39PM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a UHS-II version of sdhci's request() operation.
> > It handles UHS-II related command interrupts and errors.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 247 ++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 3 +
> > drivers/mmc/host/sdhci.c | 112 ++++++++-------
> > drivers/mmc/host/sdhci.h | 6 +
> > 4 files changed, 319 insertions(+), 49 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 36e52553977a..d50134e912f3 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -11,6 +11,7 @@
> > */
> >
> > #include <linux/delay.h>
> > +#include <linux/dmaengine.h>
> > #include <linux/ktime.h>
> > #include <linux/module.h>
> > #include <linux/mmc/mmc.h>
> > @@ -670,6 +671,12 @@ static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > struct mmc_command *cmd)
> > {
> > }
> > +
> > +static inline struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> > + struct mmc_data *data)
> > +{
> > + return NULL;
> > +}
> > #endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
> >
> > static void sdhci_uhs2_finish_data(struct sdhci_host *host)
> > @@ -1051,6 +1058,246 @@ static void sdhci_uhs2_finish_command(struct sdhci_host *host)
> > }
> > }
> >
> > +/*****************************************************************************\
> > + * *
> > + * Request done *
> > + * *
> > +\*****************************************************************************/
> > +
> > +static bool sdhci_uhs2_request_done(struct sdhci_host *host)
> > +{
> > + unsigned long flags;
> > + struct mmc_request *mrq;
> > + int i;
> > +
> > + /* FIXME: UHS2_INITIALIZED, instead? */
> > + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
> > + return sdhci_request_done(host);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + for (i = 0; i < SDHCI_MAX_MRQS; i++) {
> > + mrq = host->mrqs_done[i];
> > + if (mrq)
> > + break;
> > + }
> > +
> > + if (!mrq) {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return true;
> > + }
> > +
> > + /*
> > + * Always unmap the data buffers if they were mapped by
> > + * sdhci_prepare_data() whenever we finish with a request.
> > + * This avoids leaking DMA mappings on error.
> > + */
> > + if (host->flags & SDHCI_REQ_USE_DMA) {
> > + struct mmc_data *data = mrq->data;
> > +
> > + if (host->use_external_dma && data &&
> > + (mrq->cmd->error || data->error)) {
> > + struct dma_chan *chan = sdhci_external_dma_channel(host, data);
> > +
> > + host->mrqs_done[i] = NULL;
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + dmaengine_terminate_sync(chan);
> > + spin_lock_irqsave(&host->lock, flags);
> > + sdhci_set_mrq_done(host, mrq);
> > + }
> > +
> > + sdhci_request_done_dma(host, mrq);
> > + }
> > +
> > + /*
> > + * The controller needs a reset of internal state machines
> > + * upon error conditions.
> > + */
> > + if (sdhci_needs_reset(host, mrq)) {
> > + /*
> > + * Do not finish until command and data lines are available for
> > + * reset. Note there can only be one other mrq, so it cannot
> > + * also be in mrqs_done, otherwise host->cmd and host->data_cmd
> > + * would both be null.
> > + */
> > + if (host->cmd || host->data_cmd) {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return true;
> > + }
> > +
> > + /* Some controllers need this kick or reset won't work here */
> > + if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
> > + /* This is to force an update */
> > + host->ops->set_clock(host, host->clock);
> > +
> > + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
>
> It should be possible to use the ->reset() callback for UHS-II also, then
> this function wouldn't be needed.
The reason that I didn't so is that the second argument is 'u8' for
legacy, and u16 for uhs-2, more importantly that the register to be
accessed here is different and hence that the same bit may have a different
meaning/semantics. I thought that this kind of dependency can be confusing
and should be avoided.
That said, if you really want to use reset hook, I'd defer to you.
>
> > + host->pending_reset = false;
> > + }
> > +
> > + host->mrqs_done[i] = NULL;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + if (host->ops->request_done)
> > + host->ops->request_done(host, mrq);
> > + else
> > + mmc_request_done(host->mmc, mrq);
> > +
> > + return false;
> > +}
> > +
> > +static void sdhci_uhs2_complete_work(struct work_struct *work)
> > +{
> > + struct sdhci_host *host = container_of(work, struct sdhci_host,
> > + complete_work);
> > +
> > + while (!sdhci_uhs2_request_done(host))
> > + ;
> > +}
> > +
> > +/*****************************************************************************\
> > + * *
> > + * Interrupt handling *
> > + * *
> > +\*****************************************************************************/
> > +
> > +static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
> > +{
> > + struct mmc_command *cmd = host->cmd;
> > +
> > + DBG("*** %s got UHS2 error interrupt: 0x%08x\n",
> > + mmc_hostname(host->mmc), uhs2mask);
> > +
> > + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK) {
> > + if (!host->cmd) {
> > + pr_err("%s: Got cmd interrupt 0x%08x but no cmd.\n",
> > + mmc_hostname(host->mmc),
> > + (unsigned int)uhs2mask);
> > + sdhci_dumpregs(host);
> > + return;
> > + }
> > + host->cmd->error = -EILSEQ;
> > + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
> > + host->cmd->error = -ETIMEDOUT;
> > + }
> > +
> > + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK) {
> > + if (!host->data) {
> > + pr_err("%s: Got data interrupt 0x%08x but no data.\n",
> > + mmc_hostname(host->mmc),
> > + (unsigned int)uhs2mask);
> > + sdhci_dumpregs(host);
> > + return;
> > + }
> > +
> > + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT) {
> > + pr_err("%s: Got deadlock timeout interrupt 0x%08x\n",
> > + mmc_hostname(host->mmc),
> > + (unsigned int)uhs2mask);
> > + host->data->error = -ETIMEDOUT;
> > + } else if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_ADMA) {
> > + pr_err("%s: ADMA error = 0x %x\n",
> > + mmc_hostname(host->mmc),
> > + sdhci_readb(host, SDHCI_ADMA_ERROR));
> > + host->data->error = -EIO;
> > + } else {
> > + host->data->error = -EILSEQ;
> > + }
> > + }
> > +
> > + if (host->data && host->data->error)
> > + sdhci_uhs2_finish_data(host);
> > + else
> > + sdhci_finish_mrq(host, cmd->mrq);
> > +}
> > +
> > +u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
> > +{
> > + u32 mask = intmask, uhs2mask;
> > +
> > + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
> > + goto out;
> > +
> > + /*
> > + * TODO: We should mask Normal Error Interrupt Status Register
> > + * in UHS-2 mode so that we don't have to care SD mode errors.
> > + */
> > + if (intmask & SDHCI_INT_ERROR) {
> > + uhs2mask = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS);
> > + if (!(uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK))
> > + goto cmd_irq;
> > +
> > + /* Clear error interrupts */
> > + sdhci_writel(host, uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK,
> > + SDHCI_UHS2_ERR_INT_STATUS);
> > +
> > + /* Handle error interrupts */
> > + __sdhci_uhs2_irq(host, uhs2mask);
> > +
> > + /* Caller, shdci_irq(), doesn't have to care UHS-2 errors */
> > + intmask &= ~SDHCI_INT_ERROR;
> > + mask &= SDHCI_INT_ERROR;
> > + }
> > +
> > +cmd_irq:
> > + /*
> > + * TODO: Cleanup
> > + * INT_RESPONSE is enough instead of INT_CMD_MASK, assuming that
> > + * INT_ERROR and INT_CMD_MASK won't happen at the same time.
> > + */
> > + if (intmask & SDHCI_INT_CMD_MASK) {
> > + /* Clear command interrupt */
> > + sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
> > + SDHCI_INT_STATUS);
> > +
> > + /* Handle command interrupt */
> > + if (intmask & SDHCI_INT_RESPONSE)
> > + sdhci_uhs2_finish_command(host);
> > +
> > + /* Caller, shdci_irq(), doesn't have to care UHS-2 command */
> > + intmask &= ~SDHCI_INT_CMD_MASK;
> > + mask &= SDHCI_INT_CMD_MASK;
> > + }
> > +
> > + /* Clear already-handled interrupts. */
> > + sdhci_writel(host, mask, SDHCI_INT_STATUS);
> > +
> > +out:
> > + return intmask;
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_irq);
> > +
> > +static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
> > +{
> > + struct sdhci_host *host = dev_id;
> > + struct mmc_command *cmd;
> > + unsigned long flags;
> > + u32 isr;
> > +
> > + while (!sdhci_uhs2_request_done(host))
> > + ;
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + isr = host->thread_isr;
> > + host->thread_isr = 0;
> > +
> > + cmd = host->deferred_cmd;
>
> We don't need to use deferred_cmd, so then this function should not be
> needed.
I think that you meant that we should stick to sdhci_thread_irq() here.
Right?
-Takahiro Akashi
> For UHS-II we should require that the upper layer sends the abort
> command, so all the driver needs to do to handle errors, is to reset - which
> is already handled by sdhci_request_done() if you make use of the ->reset()
> callback.
>
> > + if (cmd && !sdhci_uhs2_send_command_retry(host, cmd, flags))
> > + sdhci_finish_mrq(host, cmd->mrq);
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
> > + struct mmc_host *mmc = host->mmc;
> > +
> > + mmc->ops->card_event(mmc);
> > + mmc_detect_change(mmc, msecs_to_jiffies(200));
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> > index c1ff4ac1ab7a..b74af641d00e 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.h
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -215,5 +215,8 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
> > unsigned short vdd);
> > void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
> > void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
> > +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
> > +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
> > +u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
> >
> > #endif /* __SDHCI_UHS2_H */
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index a9f5449bddcc..e2dfc7767bcf 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1215,11 +1215,12 @@ static int sdhci_external_dma_init(struct sdhci_host *host)
> > return ret;
> > }
> >
> > -static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> > - struct mmc_data *data)
> > +struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> > + struct mmc_data *data)
> > {
> > return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_external_dma_channel);
> >
> > int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
> > {
> > @@ -1466,7 +1467,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
> > sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> > }
> >
> > -static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
> > +bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > return (!(host->flags & SDHCI_DEVICE_DEAD) &&
> > ((mrq->cmd && mrq->cmd->error) ||
> > @@ -1474,8 +1475,9 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
> > (mrq->data && mrq->data->stop && mrq->data->stop->error) ||
> > (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_needs_reset);
> >
> > -static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> > +void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > int i;
> >
> > @@ -1495,6 +1497,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
> >
> > WARN_ON(i >= SDHCI_MAX_MRQS);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_set_mrq_done);
> >
> > void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
> > {
> > @@ -3029,7 +3032,56 @@ static const struct mmc_host_ops sdhci_ops = {
> > * *
> > \*****************************************************************************/
> >
> > -static bool sdhci_request_done(struct sdhci_host *host)
> > +void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq)
> > +{
> > + struct mmc_data *data = mrq->data;
> > +
> > + if (data && data->host_cookie == COOKIE_MAPPED) {
> > + if (host->bounce_buffer) {
> > + /*
> > + * On reads, copy the bounced data into the
> > + * sglist
> > + */
> > + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
> > + unsigned int length = data->bytes_xfered;
> > +
> > + if (length > host->bounce_buffer_size) {
> > + pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
> > + mmc_hostname(host->mmc),
> > + host->bounce_buffer_size,
> > + data->bytes_xfered);
> > + /* Cap it down and continue */
> > + length = host->bounce_buffer_size;
> > + }
> > + dma_sync_single_for_cpu(
> > + host->mmc->parent,
> > + host->bounce_addr,
> > + host->bounce_buffer_size,
> > + DMA_FROM_DEVICE);
> > + sg_copy_from_buffer(data->sg,
> > + data->sg_len,
> > + host->bounce_buffer,
> > + length);
> > + } else {
> > + /* No copying, just switch ownership */
> > + dma_sync_single_for_cpu(
> > + host->mmc->parent,
> > + host->bounce_addr,
> > + host->bounce_buffer_size,
> > + mmc_get_dma_dir(data));
> > + }
> > + } else {
> > + /* Unmap the raw data */
> > + dma_unmap_sg(mmc_dev(host->mmc), data->sg,
> > + data->sg_len,
> > + mmc_get_dma_dir(data));
> > + }
> > + data->host_cookie = COOKIE_UNMAPPED;
> > + }
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_request_done_dma);
> > +
> > +bool sdhci_request_done(struct sdhci_host *host)
> > {
> > unsigned long flags;
> > struct mmc_request *mrq;
> > @@ -3067,48 +3119,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
> > sdhci_set_mrq_done(host, mrq);
> > }
> >
> > - if (data && data->host_cookie == COOKIE_MAPPED) {
> > - if (host->bounce_buffer) {
> > - /*
> > - * On reads, copy the bounced data into the
> > - * sglist
> > - */
> > - if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
> > - unsigned int length = data->bytes_xfered;
> > -
> > - if (length > host->bounce_buffer_size) {
> > - pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
> > - mmc_hostname(host->mmc),
> > - host->bounce_buffer_size,
> > - data->bytes_xfered);
> > - /* Cap it down and continue */
> > - length = host->bounce_buffer_size;
> > - }
> > - dma_sync_single_for_cpu(
> > - host->mmc->parent,
> > - host->bounce_addr,
> > - host->bounce_buffer_size,
> > - DMA_FROM_DEVICE);
> > - sg_copy_from_buffer(data->sg,
> > - data->sg_len,
> > - host->bounce_buffer,
> > - length);
> > - } else {
> > - /* No copying, just switch ownership */
> > - dma_sync_single_for_cpu(
> > - host->mmc->parent,
> > - host->bounce_addr,
> > - host->bounce_buffer_size,
> > - mmc_get_dma_dir(data));
> > - }
> > - } else {
> > - /* Unmap the raw data */
> > - dma_unmap_sg(mmc_dev(host->mmc), data->sg,
> > - data->sg_len,
> > - mmc_get_dma_dir(data));
> > - }
> > - data->host_cookie = COOKIE_UNMAPPED;
> > - }
> > + sdhci_request_done_dma(host, mrq);
> > }
> >
> > /*
> > @@ -3132,8 +3143,10 @@ static bool sdhci_request_done(struct sdhci_host *host)
> > /* This is to force an update */
> > host->ops->set_clock(host, host->clock);
> >
> > - /* Spec says we should do both at the same time, but Ricoh
> > - controllers do not like that. */
> > + /*
> > + * Spec says we should do both at the same time, but
> > + * Ricoh controllers do not like that.
> > + */
> > sdhci_do_reset(host, SDHCI_RESET_CMD);
> > sdhci_do_reset(host, SDHCI_RESET_DATA);
> >
> > @@ -3151,6 +3164,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
> >
> > return false;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_request_done);
> >
> > static void sdhci_complete_work(struct work_struct *work)
> > {
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 6eeb74741da3..74572b54ec47 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -845,8 +845,12 @@ void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
> > struct mmc_command *cmd);
> > void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
> > struct mmc_command *cmd);
> > +struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
> > + struct mmc_data *data);
> > #endif
> > bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
> > +bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq);
> > +void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq);
> > void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> > void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
> > void __sdhci_finish_data_common(struct sdhci_host *host);
> > @@ -878,6 +882,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
> > int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> > struct mmc_ios *ios);
> > void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
> > +void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
> > +bool sdhci_request_done(struct sdhci_host *host);
> > void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
> > dma_addr_t addr, int len, unsigned int cmd);
> >
> >
>
On Thu, Dec 03, 2020 at 11:42:34AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> > This is a UHS-II version of sdhci's add_host/remove_host operation.
> > Any sdhci drivers which are capable of handling UHS-II cards must
> > call those functions instead of the corresponding sdhci's.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-uhs2.c | 198 ++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 2 +
> > drivers/mmc/host/sdhci.c | 24 +++--
> > drivers/mmc/host/sdhci.h | 10 ++
> > 4 files changed, 226 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index d50134e912f3..5d3362ea138f 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -15,6 +15,7 @@
> > #include <linux/ktime.h>
> > #include <linux/module.h>
> > #include <linux/mmc/mmc.h>
> > +#include <linux/regulator/consumer.h>
> >
> > #include "sdhci.h"
> > #include "sdhci-uhs2.h"
> > @@ -406,6 +407,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
> > {
> > }
> > #else
> > +static inline int sdhci_led_register(struct sdhci_host *host)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void sdhci_led_unregister(struct sdhci_host *host)
> > +{
> > +}
> > +
> > static inline void sdhci_led_activate(struct sdhci_host *host)
> > {
> > __sdhci_led_activate(host);
> > @@ -1298,6 +1308,194 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
> > return IRQ_HANDLED;
> > }
> >
> > +/*****************************************************************************\
> > + *
> > + * Device allocation/registration *
> > + * *
> > +\*****************************************************************************/
> > +
> > +static int __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
> > +{
> > + struct mmc_host *mmc;
> > + u32 max_current_caps2;
> > +
> > + if (host->version < SDHCI_SPEC_400)
> > + return 0;
> > +
> > + mmc = host->mmc;
> > +
> > + /* Support UHS2 */
> > + if (caps1 & SDHCI_SUPPORT_UHS2)
> > + mmc->caps |= MMC_CAP_UHS2;
> > +
> > + max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
> > +
> > + if ((caps1 & SDHCI_SUPPORT_VDD2_180) &&
> > + !max_current_caps2 &&
> > + !IS_ERR(mmc->supply.vmmc2)) {
> > + /* UHS2 - VDD2 */
> > + int curr = regulator_get_current_limit(mmc->supply.vmmc2);
> > +
> > + if (curr > 0) {
> > + /* convert to SDHCI_MAX_CURRENT format */
> > + curr = curr / 1000; /* convert to mA */
> > + curr = curr / SDHCI_MAX_CURRENT_MULTIPLIER;
> > + curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
> > + max_current_caps2 = curr;
> > + }
> > + }
> > +
> > + if (caps1 & SDHCI_SUPPORT_VDD2_180) {
> > + mmc->ocr_avail_uhs2 |= MMC_VDD2_165_195;
> > + /*
> > + * UHS2 doesn't require this. Only UHS-I bus needs to set
> > + * max current.
> > + */
> > + mmc->max_current_180_vdd2 = (max_current_caps2 &
> > + SDHCI_MAX_CURRENT_VDD2_180_MASK) *
> > + SDHCI_MAX_CURRENT_MULTIPLIER;
> > + } else {
> > + mmc->caps &= ~MMC_CAP_UHS2;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int sdhci_uhs2_host_ops_init(struct sdhci_host *host);
> > +
> > +static int __sdhci_uhs2_add_host(struct sdhci_host *host)
>
> Can you leverage __sdhci_add_host() here?
Yes if we can always use sdhci_request_done() instead of
sdhci_uhs2_request_done() and we don't need sdhci_uhs2_thread_irq().
> > +{
> > + unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
> > + struct mmc_host *mmc = host->mmc;
> > + int ret;
> > +
> > + if ((mmc->caps2 & MMC_CAP2_CQE) &&
> > + (host->quirks & SDHCI_QUIRK_BROKEN_CQE)) {
> > + mmc->caps2 &= ~MMC_CAP2_CQE;
> > + mmc->cqe_ops = NULL;
> > + }
> > +
> > + /* overwrite ops */
> > + if (mmc->caps & MMC_CAP_UHS2)
> > + sdhci_uhs2_host_ops_init(host);
> > +
> > + host->complete_wq = alloc_workqueue("sdhci", flags, 0);
> > + if (!host->complete_wq)
> > + return -ENOMEM;
> > +
> > + INIT_WORK(&host->complete_work, sdhci_uhs2_complete_work);
> > +
> > + timer_setup(&host->timer, sdhci_timeout_timer, 0);
> > + timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
> > +
> > + init_waitqueue_head(&host->buf_ready_int);
> > +
> > + sdhci_init(host, 0);
> > +
> > + ret = request_threaded_irq(host->irq, sdhci_irq,
> > + sdhci_uhs2_thread_irq,
> > + IRQF_SHARED, mmc_hostname(mmc), host);
> > + if (ret) {
> > + pr_err("%s: Failed to request IRQ %d: %d\n",
> > + mmc_hostname(mmc), host->irq, ret);
> > + goto unwq;
> > + }
> > +
> > + ret = sdhci_led_register(host);
> > + if (ret) {
> > + pr_err("%s: Failed to register LED device: %d\n",
> > + mmc_hostname(mmc), ret);
> > + goto unirq;
> > + }
> > +
> > + ret = mmc_add_host(mmc);
> > + if (ret)
> > + goto unled;
> > +
> > + pr_info("%s: SDHCI controller on %s [%s] using %s\n",
> > + mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
> > + host->use_external_dma ? "External DMA" :
> > + (host->flags & SDHCI_USE_ADMA) ?
> > + (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
> > + (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
> > +
> > + sdhci_enable_card_detection(host);
> > +
> > + return 0;
> > +
> > +unled:
> > + sdhci_led_unregister(host);
> > +unirq:
> > + sdhci_do_reset(host, SDHCI_RESET_ALL);
> > + sdhci_writel(host, 0, SDHCI_INT_ENABLE);
> > + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
> > + free_irq(host->irq, host);
> > +unwq:
> > + destroy_workqueue(host->complete_wq);
> > +
> > + return ret;
> > +}
> > +
> > +static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
> > +{
> > + if (!(host->mmc) || !(host->mmc->flags & MMC_UHS2_SUPPORT))
> > + return;
> > +
> > + if (!dead)
> > + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
> > +
> > + sdhci_writel(host, 0, SDHCI_UHS2_ERR_INT_STATUS_EN);
> > + sdhci_writel(host, 0, SDHCI_UHS2_ERR_INT_SIG_EN);
> > + host->mmc->flags &= ~MMC_UHS2_INITIALIZED;
> > +}
> > +
> > +int sdhci_uhs2_add_host(struct sdhci_host *host)
> > +{
> > + struct mmc_host *mmc = host->mmc;
> > + int ret;
> > +
> > + ret = sdhci_setup_host(host);
> > + if (ret)
> > + return ret;
> > +
> > + if (host->version >= SDHCI_SPEC_400) {
> > + ret = __sdhci_uhs2_add_host_v4(host, host->caps1);
> > + if (ret)
> > + goto cleanup;
> > + }
> > +
> > + if ((mmc->caps & MMC_CAP_UHS2) && !host->v4_mode)
> > + /* host doesn't want to enable UHS2 support */
> > + /* FIXME: Do we have to do some cleanup here? */
> > + mmc->caps &= ~MMC_CAP_UHS2;
> > +
> > + ret = __sdhci_uhs2_add_host(host);
> > + if (ret)
> > + goto cleanup2;
> > +
> > + return 0;
> > +
> > +cleanup2:
> > + /*
> > + * TODO: Is this a right cleanup?
> > + */
> > + if (host->version >= SDHCI_SPEC_400)
> > + __sdhci_uhs2_remove_host(host, 0);
> > +cleanup:
> > + sdhci_cleanup_host(host);
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_add_host);
> > +
> > +void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
> > +{
> > + __sdhci_uhs2_remove_host(host, dead);
> > +
> > + sdhci_uhs2_remove_host(host, dead);
>
> Meant to be sdhci_remove_host() presumably?
Correct. I wonder why I have noticed this bug in testing.
-Takahiro Akashi
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_remove_host);
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
> > index b74af641d00e..34e140f21284 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.h
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -218,5 +218,7 @@ void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
> > void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
> > int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
> > u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
> > +int sdhci_uhs2_add_host(struct sdhci_host *host);
> > +void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
> >
> > #endif /* __SDHCI_UHS2_H */
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index e2dfc7767bcf..d37c3edb1ed0 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -176,10 +176,11 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
> > sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> > }
> >
> > -static void sdhci_enable_card_detection(struct sdhci_host *host)
> > +void sdhci_enable_card_detection(struct sdhci_host *host)
> > {
> > sdhci_set_card_detection(host, true);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_enable_card_detection);
> >
> > static void sdhci_disable_card_detection(struct sdhci_host *host)
> > {
> > @@ -237,7 +238,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_reset);
> >
> > -static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
> > +void sdhci_do_reset(struct sdhci_host *host, u8 mask)
> > {
> > if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
> > struct mmc_host *mmc = host->mmc;
> > @@ -258,6 +259,7 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
> > host->preset_enabled = false;
> > }
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_do_reset);
> >
> > static void sdhci_set_default_irqs(struct sdhci_host *host)
> > {
> > @@ -321,7 +323,7 @@ static void sdhci_config_dma(struct sdhci_host *host)
> > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> > }
> >
> > -static void sdhci_init(struct sdhci_host *host, int soft)
> > +void sdhci_init(struct sdhci_host *host, int soft)
> > {
> > struct mmc_host *mmc = host->mmc;
> > unsigned long flags;
> > @@ -346,6 +348,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
> > mmc->ops->set_ios(mmc, &mmc->ios);
> > }
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_init);
> >
> > static void sdhci_reinit(struct sdhci_host *host)
> > {
> > @@ -410,7 +413,7 @@ static void sdhci_led_control(struct led_classdev *led,
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > -static int sdhci_led_register(struct sdhci_host *host)
> > +int sdhci_led_register(struct sdhci_host *host)
> > {
> > struct mmc_host *mmc = host->mmc;
> >
> > @@ -427,14 +430,16 @@ static int sdhci_led_register(struct sdhci_host *host)
> >
> > return led_classdev_register(mmc_dev(mmc), &host->led);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_led_register);
> >
> > -static void sdhci_led_unregister(struct sdhci_host *host)
> > +void sdhci_led_unregister(struct sdhci_host *host)
> > {
> > if (host->quirks & SDHCI_QUIRK_NO_LED)
> > return;
> >
> > led_classdev_unregister(&host->led);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_led_unregister);
> >
> > static inline void sdhci_led_activate(struct sdhci_host *host)
> > {
> > @@ -3175,7 +3180,7 @@ static void sdhci_complete_work(struct work_struct *work)
> > ;
> > }
> >
> > -static void sdhci_timeout_timer(struct timer_list *t)
> > +void sdhci_timeout_timer(struct timer_list *t)
> > {
> > struct sdhci_host *host;
> > unsigned long flags;
> > @@ -3195,8 +3200,9 @@ static void sdhci_timeout_timer(struct timer_list *t)
> >
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_timeout_timer);
> >
> > -static void sdhci_timeout_data_timer(struct timer_list *t)
> > +void sdhci_timeout_data_timer(struct timer_list *t)
> > {
> > struct sdhci_host *host;
> > unsigned long flags;
> > @@ -3226,6 +3232,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
> >
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_timeout_data_timer);
> >
> > /*****************************************************************************\
> > * *
> > @@ -3469,7 +3476,7 @@ static inline bool sdhci_defer_done(struct sdhci_host *host,
> > data->host_cookie == COOKIE_MAPPED);
> > }
> >
> > -static irqreturn_t sdhci_irq(int irq, void *dev_id)
> > +irqreturn_t sdhci_irq(int irq, void *dev_id)
> > {
> > struct mmc_request *mrqs_done[SDHCI_MAX_MRQS] = {0};
> > irqreturn_t result = IRQ_NONE;
> > @@ -3609,6 +3616,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
> >
> > return result;
> > }
> > +EXPORT_SYMBOL_GPL(sdhci_irq);
> >
> > static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
> > {
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 74572b54ec47..b1d856664b58 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -832,8 +832,15 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
> > }
> >
> > bool sdhci_data_line_cmd(struct mmc_command *cmd);
> > +void sdhci_enable_card_detection(struct sdhci_host *host);
> > void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
> > void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
> > +void sdhci_do_reset(struct sdhci_host *host, u8 mask);
> > +void sdhci_init(struct sdhci_host *host, int soft);
> > +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> > +int sdhci_led_register(struct sdhci_host *host);
> > +void sdhci_led_unregister(struct sdhci_host *host);
> > +#endif
> > void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
> > unsigned long timeout);
> > void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
> > @@ -884,6 +891,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> > void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
> > void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
> > bool sdhci_request_done(struct sdhci_host *host);
> > +void sdhci_timeout_timer(struct timer_list *t);
> > +void sdhci_timeout_data_timer(struct timer_list *t);
> > +irqreturn_t sdhci_irq(int irq, void *dev_id);
> > void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
> > dma_addr_t addr, int len, unsigned int cmd);
> >
> >
>
Adrian,
On Thu, Dec 03, 2020 at 11:55:23AM +0200, Adrian Hunter wrote:
> On 1/12/20 5:09 am, AKASHI Takahiro wrote:
> > Adrian,
> >
> > Thank you for your review comments.
> >
> > On Thu, Nov 26, 2020 at 10:18:55AM +0200, Adrian Hunter wrote:
> >> On 25/11/20 9:41 am, AKASHI Takahiro wrote:
> >>> Gentle ping;
> >>>
> >>> On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
> >>>> This is an interim snapshot of our next version, v4, for enabling
> >>>> UHS-II on MMC/SD.
> >>>>
> >>>> It is focused on 'sdhci' side to address Adrian's comments regarding
> >>>> "modularising" sdhci-uhs2.c.
> >>>> The whole aim of this version is to get early feedback from Adrian (and
> >>>> others) on this issue. Without any consensus about the code structure,
> >>>
> >>> Any comments so far?
> >>>
> >>
> >> Overall, I like this approach of separating UHS2 from legacy sdhci as much
> >> as possible. The only major change, is to drop support for legacy quirks
> >> and features that you do not need. The reason for that, is that there may
> >> be few drivers that end up with UHS-II support (opting instead for SD
> >> Express), so there is no point going to a lot of trouble to support things
> >> that never get used.
> >>
> >> From what I have seen that looks like it includes:
> >> - any quirks
> >
> > GLI driver (gl9755) needs
> > * SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
> > * SDHCI_QUIRK2_BROKEN_DDR50
> > but they are managed in sdhci code.
> >
> >> - SDHCI LED support
> >> - external DMA support
> >
> > Should we add 'depends on !SDHCI_UHS2' to MMC_SDHCI_EXTERNAL_DMA?
> >
> >> In this regard, the important thing is to have a comment somewhere that
> >> lists what is not supported.
> >>
> >> I have only looked at SDHCI patches so far, and only up to about patch 20,
> >> but maybe that gives you enough to go on for a while.
> >
> > Well, I have almost done.
> > Can I expect your comments on the patches #21-#27 as well soon?
>
> I have made some more comments and that is all for now, except for anything
> more you wish to discuss.
Thank you.
I assume that you don't have any objection against adding extra hooks
to sdhci_ops in patch#23 and #25, do you?
If so, since we don't have any critical issues to discuss,
I hope that my changes will be contained in the new version
where a major rework will be done on the core side by Ben.
-Takahiro Akashi
On 8/12/20 9:37 am, AKASHI Takahiro wrote:
> On Tue, Dec 01, 2020 at 06:46:39PM +0200, Adrian Hunter wrote:
>> On 6/11/20 4:27 am, AKASHI Takahiro wrote:
>>> This is a UHS-II version of sdhci's request() operation.
>>> It handles UHS-II related command interrupts and errors.
>>>
>>> Signed-off-by: Ben Chuang <[email protected]>
>>> Signed-off-by: AKASHI Takahiro <[email protected]>
>>> ---
>>> drivers/mmc/host/sdhci-uhs2.c | 247 ++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci-uhs2.h | 3 +
>>> drivers/mmc/host/sdhci.c | 112 ++++++++-------
>>> drivers/mmc/host/sdhci.h | 6 +
>>> 4 files changed, 319 insertions(+), 49 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
>>> index 36e52553977a..d50134e912f3 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.c
>>> +++ b/drivers/mmc/host/sdhci-uhs2.c
>>> @@ -11,6 +11,7 @@
>>> */
>>>
>>> #include <linux/delay.h>
>>> +#include <linux/dmaengine.h>
>>> #include <linux/ktime.h>
>>> #include <linux/module.h>
>>> #include <linux/mmc/mmc.h>
>>> @@ -670,6 +671,12 @@ static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> struct mmc_command *cmd)
>>> {
>>> }
>>> +
>>> +static inline struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
>>> + struct mmc_data *data)
>>> +{
>>> + return NULL;
>>> +}
>>> #endif /* CONFIG_MMC_SDHCI_EXTERNAL_DMA */
>>>
>>> static void sdhci_uhs2_finish_data(struct sdhci_host *host)
>>> @@ -1051,6 +1058,246 @@ static void sdhci_uhs2_finish_command(struct sdhci_host *host)
>>> }
>>> }
>>>
>>> +/*****************************************************************************\
>>> + * *
>>> + * Request done *
>>> + * *
>>> +\*****************************************************************************/
>>> +
>>> +static bool sdhci_uhs2_request_done(struct sdhci_host *host)
>>> +{
>>> + unsigned long flags;
>>> + struct mmc_request *mrq;
>>> + int i;
>>> +
>>> + /* FIXME: UHS2_INITIALIZED, instead? */
>>> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
>>> + return sdhci_request_done(host);
>>> +
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + for (i = 0; i < SDHCI_MAX_MRQS; i++) {
>>> + mrq = host->mrqs_done[i];
>>> + if (mrq)
>>> + break;
>>> + }
>>> +
>>> + if (!mrq) {
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> + return true;
>>> + }
>>> +
>>> + /*
>>> + * Always unmap the data buffers if they were mapped by
>>> + * sdhci_prepare_data() whenever we finish with a request.
>>> + * This avoids leaking DMA mappings on error.
>>> + */
>>> + if (host->flags & SDHCI_REQ_USE_DMA) {
>>> + struct mmc_data *data = mrq->data;
>>> +
>>> + if (host->use_external_dma && data &&
>>> + (mrq->cmd->error || data->error)) {
>>> + struct dma_chan *chan = sdhci_external_dma_channel(host, data);
>>> +
>>> + host->mrqs_done[i] = NULL;
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> + dmaengine_terminate_sync(chan);
>>> + spin_lock_irqsave(&host->lock, flags);
>>> + sdhci_set_mrq_done(host, mrq);
>>> + }
>>> +
>>> + sdhci_request_done_dma(host, mrq);
>>> + }
>>> +
>>> + /*
>>> + * The controller needs a reset of internal state machines
>>> + * upon error conditions.
>>> + */
>>> + if (sdhci_needs_reset(host, mrq)) {
>>> + /*
>>> + * Do not finish until command and data lines are available for
>>> + * reset. Note there can only be one other mrq, so it cannot
>>> + * also be in mrqs_done, otherwise host->cmd and host->data_cmd
>>> + * would both be null.
>>> + */
>>> + if (host->cmd || host->data_cmd) {
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> + return true;
>>> + }
>>> +
>>> + /* Some controllers need this kick or reset won't work here */
>>> + if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
>>> + /* This is to force an update */
>>> + host->ops->set_clock(host, host->clock);
>>> +
>>> + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
>>
>> It should be possible to use the ->reset() callback for UHS-II also, then
>> this function wouldn't be needed.
>
> The reason that I didn't so is that the second argument is 'u8' for
> legacy, and u16 for uhs-2, more importantly that the register to be
> accessed here is different and hence that the same bit may have a different
> meaning/semantics. I thought that this kind of dependency can be confusing
> and should be avoided.
We can always add comments to explain it.
Currently, there are 4 possibilities for reset:
1. full reset i.e. SDHCI_RESET_ALL : all standard registers are reset to
default values
2. reset command circuit i.e. SDHCI_RESET_CMD : error recovery for commands
3. reset data circuit i.e. SDHCI_RESET_DATA : error recovery for data
4. 2 + 3
My brief reading of the spec (might be wrong) suggests mapping:
SD mode UHS-II mode
SDHCI_RESET_ALL SDHCI_UHS2_SW_RESET_FULL
SDHCI_RESET_CMD SDHCI_RESET_CMD
SDHCI_RESET_DATA SDHCI_UHS2_SW_RESET_SD
>
> That said, if you really want to use reset hook, I'd defer to you.
>
>>
>>> + host->pending_reset = false;
>>> + }
>>> +
>>> + host->mrqs_done[i] = NULL;
>>> +
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +
>>> + if (host->ops->request_done)
>>> + host->ops->request_done(host, mrq);
>>> + else
>>> + mmc_request_done(host->mmc, mrq);
>>> +
>>> + return false;
>>> +}
>>> +
>>> +static void sdhci_uhs2_complete_work(struct work_struct *work)
>>> +{
>>> + struct sdhci_host *host = container_of(work, struct sdhci_host,
>>> + complete_work);
>>> +
>>> + while (!sdhci_uhs2_request_done(host))
>>> + ;
>>> +}
>>> +
>>> +/*****************************************************************************\
>>> + * *
>>> + * Interrupt handling *
>>> + * *
>>> +\*****************************************************************************/
>>> +
>>> +static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
>>> +{
>>> + struct mmc_command *cmd = host->cmd;
>>> +
>>> + DBG("*** %s got UHS2 error interrupt: 0x%08x\n",
>>> + mmc_hostname(host->mmc), uhs2mask);
>>> +
>>> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_CMD_MASK) {
>>> + if (!host->cmd) {
>>> + pr_err("%s: Got cmd interrupt 0x%08x but no cmd.\n",
>>> + mmc_hostname(host->mmc),
>>> + (unsigned int)uhs2mask);
>>> + sdhci_dumpregs(host);
>>> + return;
>>> + }
>>> + host->cmd->error = -EILSEQ;
>>> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_RES_TIMEOUT)
>>> + host->cmd->error = -ETIMEDOUT;
>>> + }
>>> +
>>> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DATA_MASK) {
>>> + if (!host->data) {
>>> + pr_err("%s: Got data interrupt 0x%08x but no data.\n",
>>> + mmc_hostname(host->mmc),
>>> + (unsigned int)uhs2mask);
>>> + sdhci_dumpregs(host);
>>> + return;
>>> + }
>>> +
>>> + if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_DEADLOCK_TIMEOUT) {
>>> + pr_err("%s: Got deadlock timeout interrupt 0x%08x\n",
>>> + mmc_hostname(host->mmc),
>>> + (unsigned int)uhs2mask);
>>> + host->data->error = -ETIMEDOUT;
>>> + } else if (uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_ADMA) {
>>> + pr_err("%s: ADMA error = 0x %x\n",
>>> + mmc_hostname(host->mmc),
>>> + sdhci_readb(host, SDHCI_ADMA_ERROR));
>>> + host->data->error = -EIO;
>>> + } else {
>>> + host->data->error = -EILSEQ;
>>> + }
>>> + }
>>> +
>>> + if (host->data && host->data->error)
>>> + sdhci_uhs2_finish_data(host);
>>> + else
>>> + sdhci_finish_mrq(host, cmd->mrq);
>>> +}
>>> +
>>> +u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
>>> +{
>>> + u32 mask = intmask, uhs2mask;
>>> +
>>> + if (!(host->mmc->flags & MMC_UHS2_SUPPORT))
>>> + goto out;
>>> +
>>> + /*
>>> + * TODO: We should mask Normal Error Interrupt Status Register
>>> + * in UHS-2 mode so that we don't have to care SD mode errors.
>>> + */
>>> + if (intmask & SDHCI_INT_ERROR) {
>>> + uhs2mask = sdhci_readl(host, SDHCI_UHS2_ERR_INT_STATUS);
>>> + if (!(uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK))
>>> + goto cmd_irq;
>>> +
>>> + /* Clear error interrupts */
>>> + sdhci_writel(host, uhs2mask & SDHCI_UHS2_ERR_INT_STATUS_MASK,
>>> + SDHCI_UHS2_ERR_INT_STATUS);
>>> +
>>> + /* Handle error interrupts */
>>> + __sdhci_uhs2_irq(host, uhs2mask);
>>> +
>>> + /* Caller, shdci_irq(), doesn't have to care UHS-2 errors */
>>> + intmask &= ~SDHCI_INT_ERROR;
>>> + mask &= SDHCI_INT_ERROR;
>>> + }
>>> +
>>> +cmd_irq:
>>> + /*
>>> + * TODO: Cleanup
>>> + * INT_RESPONSE is enough instead of INT_CMD_MASK, assuming that
>>> + * INT_ERROR and INT_CMD_MASK won't happen at the same time.
>>> + */
>>> + if (intmask & SDHCI_INT_CMD_MASK) {
>>> + /* Clear command interrupt */
>>> + sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
>>> + SDHCI_INT_STATUS);
>>> +
>>> + /* Handle command interrupt */
>>> + if (intmask & SDHCI_INT_RESPONSE)
>>> + sdhci_uhs2_finish_command(host);
>>> +
>>> + /* Caller, shdci_irq(), doesn't have to care UHS-2 command */
>>> + intmask &= ~SDHCI_INT_CMD_MASK;
>>> + mask &= SDHCI_INT_CMD_MASK;
>>> + }
>>> +
>>> + /* Clear already-handled interrupts. */
>>> + sdhci_writel(host, mask, SDHCI_INT_STATUS);
>>> +
>>> +out:
>>> + return intmask;
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_uhs2_irq);
>>> +
>>> +static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
>>> +{
>>> + struct sdhci_host *host = dev_id;
>>> + struct mmc_command *cmd;
>>> + unsigned long flags;
>>> + u32 isr;
>>> +
>>> + while (!sdhci_uhs2_request_done(host))
>>> + ;
>>> +
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + isr = host->thread_isr;
>>> + host->thread_isr = 0;
>>> +
>>> + cmd = host->deferred_cmd;
>>
>> We don't need to use deferred_cmd, so then this function should not be
>> needed.
>
> I think that you meant that we should stick to sdhci_thread_irq() here.
> Right?
>
> -Takahiro Akashi
>
>> For UHS-II we should require that the upper layer sends the abort
>> command, so all the driver needs to do to handle errors, is to reset - which
>> is already handled by sdhci_request_done() if you make use of the ->reset()
>> callback.
>>
>>> + if (cmd && !sdhci_uhs2_send_command_retry(host, cmd, flags))
>>> + sdhci_finish_mrq(host, cmd->mrq);
>>> +
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +
>>> + if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
>>> + struct mmc_host *mmc = host->mmc;
>>> +
>>> + mmc->ops->card_event(mmc);
>>> + mmc_detect_change(mmc, msecs_to_jiffies(200));
>>> + }
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> /*****************************************************************************\
>>> * *
>>> * Driver init/exit *
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
>>> index c1ff4ac1ab7a..b74af641d00e 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.h
>>> +++ b/drivers/mmc/host/sdhci-uhs2.h
>>> @@ -215,5 +215,8 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode,
>>> unsigned short vdd);
>>> void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
>>> void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
>>> +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq);
>>> +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
>>> +u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
>>>
>>> #endif /* __SDHCI_UHS2_H */
>>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>>> index a9f5449bddcc..e2dfc7767bcf 100644
>>> --- a/drivers/mmc/host/sdhci.c
>>> +++ b/drivers/mmc/host/sdhci.c
>>> @@ -1215,11 +1215,12 @@ static int sdhci_external_dma_init(struct sdhci_host *host)
>>> return ret;
>>> }
>>>
>>> -static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
>>> - struct mmc_data *data)
>>> +struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
>>> + struct mmc_data *data)
>>> {
>>> return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_external_dma_channel);
>>>
>>> int sdhci_external_dma_setup(struct sdhci_host *host, struct mmc_command *cmd)
>>> {
>>> @@ -1466,7 +1467,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
>>> sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
>>> }
>>>
>>> -static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
>>> +bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> return (!(host->flags & SDHCI_DEVICE_DEAD) &&
>>> ((mrq->cmd && mrq->cmd->error) ||
>>> @@ -1474,8 +1475,9 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
>>> (mrq->data && mrq->data->stop && mrq->data->stop->error) ||
>>> (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_needs_reset);
>>>
>>> -static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
>>> +void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> int i;
>>>
>>> @@ -1495,6 +1497,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
>>>
>>> WARN_ON(i >= SDHCI_MAX_MRQS);
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_set_mrq_done);
>>>
>>> void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
>>> {
>>> @@ -3029,7 +3032,56 @@ static const struct mmc_host_ops sdhci_ops = {
>>> * *
>>> \*****************************************************************************/
>>>
>>> -static bool sdhci_request_done(struct sdhci_host *host)
>>> +void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq)
>>> +{
>>> + struct mmc_data *data = mrq->data;
>>> +
>>> + if (data && data->host_cookie == COOKIE_MAPPED) {
>>> + if (host->bounce_buffer) {
>>> + /*
>>> + * On reads, copy the bounced data into the
>>> + * sglist
>>> + */
>>> + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
>>> + unsigned int length = data->bytes_xfered;
>>> +
>>> + if (length > host->bounce_buffer_size) {
>>> + pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
>>> + mmc_hostname(host->mmc),
>>> + host->bounce_buffer_size,
>>> + data->bytes_xfered);
>>> + /* Cap it down and continue */
>>> + length = host->bounce_buffer_size;
>>> + }
>>> + dma_sync_single_for_cpu(
>>> + host->mmc->parent,
>>> + host->bounce_addr,
>>> + host->bounce_buffer_size,
>>> + DMA_FROM_DEVICE);
>>> + sg_copy_from_buffer(data->sg,
>>> + data->sg_len,
>>> + host->bounce_buffer,
>>> + length);
>>> + } else {
>>> + /* No copying, just switch ownership */
>>> + dma_sync_single_for_cpu(
>>> + host->mmc->parent,
>>> + host->bounce_addr,
>>> + host->bounce_buffer_size,
>>> + mmc_get_dma_dir(data));
>>> + }
>>> + } else {
>>> + /* Unmap the raw data */
>>> + dma_unmap_sg(mmc_dev(host->mmc), data->sg,
>>> + data->sg_len,
>>> + mmc_get_dma_dir(data));
>>> + }
>>> + data->host_cookie = COOKIE_UNMAPPED;
>>> + }
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_request_done_dma);
>>> +
>>> +bool sdhci_request_done(struct sdhci_host *host)
>>> {
>>> unsigned long flags;
>>> struct mmc_request *mrq;
>>> @@ -3067,48 +3119,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
>>> sdhci_set_mrq_done(host, mrq);
>>> }
>>>
>>> - if (data && data->host_cookie == COOKIE_MAPPED) {
>>> - if (host->bounce_buffer) {
>>> - /*
>>> - * On reads, copy the bounced data into the
>>> - * sglist
>>> - */
>>> - if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
>>> - unsigned int length = data->bytes_xfered;
>>> -
>>> - if (length > host->bounce_buffer_size) {
>>> - pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
>>> - mmc_hostname(host->mmc),
>>> - host->bounce_buffer_size,
>>> - data->bytes_xfered);
>>> - /* Cap it down and continue */
>>> - length = host->bounce_buffer_size;
>>> - }
>>> - dma_sync_single_for_cpu(
>>> - host->mmc->parent,
>>> - host->bounce_addr,
>>> - host->bounce_buffer_size,
>>> - DMA_FROM_DEVICE);
>>> - sg_copy_from_buffer(data->sg,
>>> - data->sg_len,
>>> - host->bounce_buffer,
>>> - length);
>>> - } else {
>>> - /* No copying, just switch ownership */
>>> - dma_sync_single_for_cpu(
>>> - host->mmc->parent,
>>> - host->bounce_addr,
>>> - host->bounce_buffer_size,
>>> - mmc_get_dma_dir(data));
>>> - }
>>> - } else {
>>> - /* Unmap the raw data */
>>> - dma_unmap_sg(mmc_dev(host->mmc), data->sg,
>>> - data->sg_len,
>>> - mmc_get_dma_dir(data));
>>> - }
>>> - data->host_cookie = COOKIE_UNMAPPED;
>>> - }
>>> + sdhci_request_done_dma(host, mrq);
>>> }
>>>
>>> /*
>>> @@ -3132,8 +3143,10 @@ static bool sdhci_request_done(struct sdhci_host *host)
>>> /* This is to force an update */
>>> host->ops->set_clock(host, host->clock);
>>>
>>> - /* Spec says we should do both at the same time, but Ricoh
>>> - controllers do not like that. */
>>> + /*
>>> + * Spec says we should do both at the same time, but
>>> + * Ricoh controllers do not like that.
>>> + */
>>> sdhci_do_reset(host, SDHCI_RESET_CMD);
>>> sdhci_do_reset(host, SDHCI_RESET_DATA);
>>>
>>> @@ -3151,6 +3164,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
>>>
>>> return false;
>>> }
>>> +EXPORT_SYMBOL_GPL(sdhci_request_done);
>>>
>>> static void sdhci_complete_work(struct work_struct *work)
>>> {
>>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>>> index 6eeb74741da3..74572b54ec47 100644
>>> --- a/drivers/mmc/host/sdhci.h
>>> +++ b/drivers/mmc/host/sdhci.h
>>> @@ -845,8 +845,12 @@ void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
>>> struct mmc_command *cmd);
>>> void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
>>> struct mmc_command *cmd);
>>> +struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
>>> + struct mmc_data *data);
>>> #endif
>>> bool sdhci_manual_cmd23(struct sdhci_host *host, struct mmc_request *mrq);
>>> +bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq);
>>> +void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq);
>>> void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
>>> void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
>>> void __sdhci_finish_data_common(struct sdhci_host *host);
>>> @@ -878,6 +882,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
>>> int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
>>> struct mmc_ios *ios);
>>> void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
>>> +void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
>>> +bool sdhci_request_done(struct sdhci_host *host);
>>> void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
>>> dma_addr_t addr, int len, unsigned int cmd);
>>>
>>>
>>
On 8/12/20 9:58 am, AKASHI Takahiro wrote:
> Adrian,
>
> On Thu, Dec 03, 2020 at 11:55:23AM +0200, Adrian Hunter wrote:
>> On 1/12/20 5:09 am, AKASHI Takahiro wrote:
>>> Adrian,
>>>
>>> Thank you for your review comments.
>>>
>>> On Thu, Nov 26, 2020 at 10:18:55AM +0200, Adrian Hunter wrote:
>>>> On 25/11/20 9:41 am, AKASHI Takahiro wrote:
>>>>> Gentle ping;
>>>>>
>>>>> On Fri, Nov 06, 2020 at 11:26:59AM +0900, AKASHI Takahiro wrote:
>>>>>> This is an interim snapshot of our next version, v4, for enabling
>>>>>> UHS-II on MMC/SD.
>>>>>>
>>>>>> It is focused on 'sdhci' side to address Adrian's comments regarding
>>>>>> "modularising" sdhci-uhs2.c.
>>>>>> The whole aim of this version is to get early feedback from Adrian (and
>>>>>> others) on this issue. Without any consensus about the code structure,
>>>>>
>>>>> Any comments so far?
>>>>>
>>>>
>>>> Overall, I like this approach of separating UHS2 from legacy sdhci as much
>>>> as possible. The only major change, is to drop support for legacy quirks
>>>> and features that you do not need. The reason for that, is that there may
>>>> be few drivers that end up with UHS-II support (opting instead for SD
>>>> Express), so there is no point going to a lot of trouble to support things
>>>> that never get used.
>>>>
>>>> From what I have seen that looks like it includes:
>>>> - any quirks
>>>
>>> GLI driver (gl9755) needs
>>> * SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
>>> * SDHCI_QUIRK2_BROKEN_DDR50
>>> but they are managed in sdhci code.
>>>
>>>> - SDHCI LED support
>>>> - external DMA support
>>>
>>> Should we add 'depends on !SDHCI_UHS2' to MMC_SDHCI_EXTERNAL_DMA?
>>>
>>>> In this regard, the important thing is to have a comment somewhere that
>>>> lists what is not supported.
>>>>
>>>> I have only looked at SDHCI patches so far, and only up to about patch 20,
>>>> but maybe that gives you enough to go on for a while.
>>>
>>> Well, I have almost done.
>>> Can I expect your comments on the patches #21-#27 as well soon?
>>
>> I have made some more comments and that is all for now, except for anything
>> more you wish to discuss.
>
> Thank you.
> I assume that you don't have any objection against adding extra hooks
> to sdhci_ops in patch#23 and #25, do you?
No objections at the moment.
>
> If so, since we don't have any critical issues to discuss,
> I hope that my changes will be contained in the new version
> where a major rework will be done on the core side by Ben.
Ok