linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
@ 2020-11-06  2:26 AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 01/27] mmc: add UHS-II related definitions in public headers AKASHI Takahiro
                   ` (27 more replies)
  0 siblings, 28 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:26 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 01/27] mmc: add UHS-II related definitions in public headers
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 02/27] mmc: core: UHS-II support, modify power-up sequence AKASHI Takahiro
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

From: Ben Chuang <ben.chuang@genesyslogic.com.tw>

Add UHS-II support in public headers

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 02/27] mmc: core: UHS-II support, modify power-up sequence
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 01/27] mmc: add UHS-II related definitions in public headers AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip set_chip_select() AKASHI Takahiro
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip set_chip_select()
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 01/27] mmc: add UHS-II related definitions in public headers AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 02/27] mmc: core: UHS-II support, modify power-up sequence AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-10  7:15   ` Bough Chen
  2020-11-06  2:27 ` [RFC PATCH v3.1 04/27] mmc: core: UHS-II support, try to select UHS-II interface AKASHI Takahiro
                   ` (24 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

mmc_set_chip_select() should be called only in UHS-II mode,
and not for UHS-II mode.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 04/27] mmc: core: UHS-II support, try to select UHS-II interface
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip set_chip_select() AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 05/27] mmc: core: UHS-II support, skip TMODE setup in some cases AKASHI Takahiro
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

From: Ben Chuang <ben.chuang@genesyslogic.com.tw>

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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 <ben.chuang@genesyslogic.com.tw>
+ *  Copyright (C) 2020 Linaro Limited
+ *  Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ */
+
+#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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 05/27] mmc: core: UHS-II support, skip TMODE setup in some cases
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 04/27] mmc: core: UHS-II support, try to select UHS-II interface AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 06/27] mmc: core: UHS-II support, generate UHS-II SD command packet AKASHI Takahiro
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 06/27] mmc: core: UHS-II support, generate UHS-II SD command packet
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 05/27] mmc: core: UHS-II support, skip TMODE setup in some cases AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 07/27] mmc: core: UHS-II support, set APP_CMD bit if necessary AKASHI Takahiro
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 07/27] mmc: core: UHS-II support, set APP_CMD bit if necessary
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 06/27] mmc: core: UHS-II support, generate UHS-II SD command packet AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support AKASHI Takahiro
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (6 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 07/27] mmc: core: UHS-II support, set APP_CMD bit if necessary AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:14   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers AKASHI Takahiro
                   ` (19 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (7 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:15   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 10/27] mmc: sdhci: add UHS-II module AKASHI Takahiro
                   ` (18 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

Add UHS-II related definitions in shdci.h and sdhci-uhs2.h.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 10/27] mmc: sdhci: add UHS-II module
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (8 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 11/27] mmc: sdhci-uhs2: dump UHS-II registers AKASHI Takahiro
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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 <ben.chuang@genesyslogic.com.tw>
+ *  Copyright (C) 2020 Linaro Limited
+ *  Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ */
+
+#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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 11/27] mmc: sdhci-uhs2: dump UHS-II registers
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (9 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 10/27] mmc: sdhci: add UHS-II module AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function AKASHI Takahiro
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

Dump UHS-II specific registers, if available, in sdhci_dumpregs()
for informative/debugging use.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (10 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 11/27] mmc: sdhci-uhs2: dump UHS-II registers AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:16   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2 AKASHI Takahiro
                   ` (15 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

Sdhci_uhs2_reset() does a UHS-II specific reset operation.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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 <takahiro.akashi@linaro.org>
  */
 
+#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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (11 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:16   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch() AKASHI Takahiro
                   ` (14 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch()
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (12 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2 AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:16   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 15/27] mmc: sdhci-uhs2: add set_timeout() AKASHI Takahiro
                   ` (13 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
so no voltage switch required.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 15/27] mmc: sdhci-uhs2: add set_timeout()
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (13 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch() AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios() AKASHI Takahiro
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

This is a UHS-II version of sdhci's set_timeout() operation.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios()
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (14 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 15/27] mmc: sdhci-uhs2: add set_timeout() AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:17   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface AKASHI Takahiro
                   ` (11 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

This is a sdhci version of mmc's set_ios operation.
It covers both UHS-I and UHS-II.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (15 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios() AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:17   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations AKASHI Takahiro
                   ` (10 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (16 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:17   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface AKASHI Takahiro
                   ` (9 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (17 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:18   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others AKASHI Takahiro
                   ` (8 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (18 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-26  8:18   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() " AKASHI Takahiro
                   ` (7 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

This is a sdhci version of mmc's request operation.
It covers both UHS-I and UHS-II.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() and others
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (19 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-12-01 16:46   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver AKASHI Takahiro
                   ` (6 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (20 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() " AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-12-03  9:42   ` Adrian Hunter
  2020-11-06  2:27 ` [RFC PATCH v3.1 23/27] mmc: sdhci-uhs2: add pre-detect_init hook AKASHI Takahiro
                   ` (5 subsequent siblings)
  27 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 23/27] mmc: sdhci-uhs2: add pre-detect_init hook
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (21 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 24/27] mmc: core: add post-mmc_attach_sd hook AKASHI Takahiro
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

From: Ben Chuang <ben.chuang@genesyslogic.com.tw>

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 24/27] mmc: core: add post-mmc_attach_sd hook
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (22 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 23/27] mmc: sdhci-uhs2: add pre-detect_init hook AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 25/27] mmc: sdhci-uhs2: " AKASHI Takahiro
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

This "post" hook for mmc_attach_sd() will be required to enable UHS-II
support, at least, on GL9755.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 25/27] mmc: sdhci-uhs2: add post-mmc_attach_sd hook
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (23 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 24/27] mmc: core: add post-mmc_attach_sd hook AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 26/27] mmc: sdhci-pci: add UHS-II support framework AKASHI Takahiro
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

From: Ben Chuang <ben.chuang@genesyslogic.com.tw>

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 26/27] mmc: sdhci-pci: add UHS-II support framework
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (24 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 25/27] mmc: sdhci-uhs2: " AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-06  2:27 ` [RFC PATCH v3.1 27/27] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755 AKASHI Takahiro
  2020-11-25  7:41 ` [RFC PATCH v3.1 00/27] Add support UHS-II " AKASHI Takahiro
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [RFC PATCH v3.1 27/27] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (25 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 26/27] mmc: sdhci-pci: add UHS-II support framework AKASHI Takahiro
@ 2020-11-06  2:27 ` AKASHI Takahiro
  2020-11-25  7:41 ` [RFC PATCH v3.1 00/27] Add support UHS-II " AKASHI Takahiro
  27 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-06  2:27 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu, AKASHI Takahiro

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 <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 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


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* RE: [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip set_chip_select()
  2020-11-06  2:27 ` [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip set_chip_select() AKASHI Takahiro
@ 2020-11-10  7:15   ` Bough Chen
  0 siblings, 0 replies; 67+ messages in thread
From: Bough Chen @ 2020-11-10  7:15 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, adrian.hunter
  Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

> -----Original Message-----
> From: AKASHI Takahiro [mailto:takahiro.akashi@linaro.org]
> Sent: 2020年11月6日 10:27
> To: ulf.hansson@linaro.org; adrian.hunter@intel.com
> Cc: linux-mmc@vger.kernel.org; linux-kernel@vger.kernel.org;
> ben.chuang@genesyslogic.com.tw; greg.tu@genesyslogic.com.tw; AKASHI
> Takahiro <takahiro.akashi@linaro.org>
> 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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
                   ` (26 preceding siblings ...)
  2020-11-06  2:27 ` [RFC PATCH v3.1 27/27] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755 AKASHI Takahiro
@ 2020-11-25  7:41 ` AKASHI Takahiro
  2020-11-25 10:43   ` Ulf Hansson
  2020-11-26  8:18   ` Adrian Hunter
  27 siblings, 2 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-25  7:41 UTC (permalink / raw)
  To: ulf.hansson, adrian.hunter; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-11-25  7:41 ` [RFC PATCH v3.1 00/27] Add support UHS-II " AKASHI Takahiro
@ 2020-11-25 10:43   ` Ulf Hansson
  2020-11-26  0:06     ` AKASHI Takahiro
  2020-11-26  8:18   ` Adrian Hunter
  1 sibling, 1 reply; 67+ messages in thread
From: Ulf Hansson @ 2020-11-25 10:43 UTC (permalink / raw)
  To: AKASHI Takahiro, Ulf Hansson, Adrian Hunter, linux-mmc,
	Linux Kernel Mailing List, Ben Chuang, greg.tu

On Wed, 25 Nov 2020 at 08:41, AKASHI Takahiro
<takahiro.akashi@linaro.org> 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

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-11-25 10:43   ` Ulf Hansson
@ 2020-11-26  0:06     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-26  0:06 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Adrian Hunter, linux-mmc, Linux Kernel Mailing List, Ben Chuang, greg.tu

On Wed, Nov 25, 2020 at 11:43:18AM +0100, Ulf Hansson wrote:
> On Wed, 25 Nov 2020 at 08:41, AKASHI Takahiro
> <takahiro.akashi@linaro.org> 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

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support
  2020-11-06  2:27 ` [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support AKASHI Takahiro
@ 2020-11-26  8:14   ` Adrian Hunter
  2020-11-30  5:17     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:14 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers
  2020-11-06  2:27 ` [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers AKASHI Takahiro
@ 2020-11-26  8:15   ` Adrian Hunter
  2020-11-30  5:21     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:15 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function
  2020-11-06  2:27 ` [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function AKASHI Takahiro
@ 2020-11-26  8:16   ` Adrian Hunter
  2020-11-30  6:20     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:16 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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 <takahiro.akashi@linaro.org>
>   */
>  
> +#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);
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2
  2020-11-06  2:27 ` [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2 AKASHI Takahiro
@ 2020-11-26  8:16   ` Adrian Hunter
  2020-11-30  7:15     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:16 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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);
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch()
  2020-11-06  2:27 ` [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch() AKASHI Takahiro
@ 2020-11-26  8:16   ` Adrian Hunter
  2020-11-30  7:38     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:16 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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;
>  }
>  
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios()
  2020-11-06  2:27 ` [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios() AKASHI Takahiro
@ 2020-11-26  8:17   ` Adrian Hunter
  2020-11-30  7:51     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:17 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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);
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface
  2020-11-06  2:27 ` [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface AKASHI Takahiro
@ 2020-11-26  8:17   ` Adrian Hunter
  2020-12-01  2:25     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:17 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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;
>  }
>  
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations
  2020-11-06  2:27 ` [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations AKASHI Takahiro
@ 2020-11-26  8:17   ` Adrian Hunter
  2020-12-01  2:27     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:17 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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;
>  }
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface
  2020-11-06  2:27 ` [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface AKASHI Takahiro
@ 2020-11-26  8:18   ` Adrian Hunter
  2020-12-01  2:28     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:18 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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;
>  }
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others
  2020-11-06  2:27 ` [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others AKASHI Takahiro
@ 2020-11-26  8:18   ` Adrian Hunter
  2020-12-01  2:40     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:18 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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);
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-11-25  7:41 ` [RFC PATCH v3.1 00/27] Add support UHS-II " AKASHI Takahiro
  2020-11-25 10:43   ` Ulf Hansson
@ 2020-11-26  8:18   ` Adrian Hunter
  2020-12-01  3:09     ` AKASHI Takahiro
  1 sibling, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-11-26  8:18 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support
  2020-11-26  8:14   ` Adrian Hunter
@ 2020-11-30  5:17     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-30  5:17 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers
  2020-11-26  8:15   ` Adrian Hunter
@ 2020-11-30  5:21     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-30  5:21 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function
  2020-11-26  8:16   ` Adrian Hunter
@ 2020-11-30  6:20     ` AKASHI Takahiro
  2020-11-30  7:37       ` Adrian Hunter
  0 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-30  6:20 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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 <takahiro.akashi@linaro.org>
> >   */
> >  
> > +#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);
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2
  2020-11-26  8:16   ` Adrian Hunter
@ 2020-11-30  7:15     ` AKASHI Takahiro
  2020-11-30  7:44       ` Adrian Hunter
  0 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-30  7:15 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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);
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function
  2020-11-30  6:20     ` AKASHI Takahiro
@ 2020-11-30  7:37       ` Adrian Hunter
  0 siblings, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-11-30  7:37 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  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 <takahiro.akashi@linaro.org>
>>>   */
>>>  
>>> +#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.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch()
  2020-11-26  8:16   ` Adrian Hunter
@ 2020-11-30  7:38     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-30  7:38 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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;
> >  }
> >  
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2
  2020-11-30  7:15     ` AKASHI Takahiro
@ 2020-11-30  7:44       ` Adrian Hunter
  0 siblings, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-11-30  7:44 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  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.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios()
  2020-11-26  8:17   ` Adrian Hunter
@ 2020-11-30  7:51     ` AKASHI Takahiro
  2020-12-03  9:51       ` Adrian Hunter
  0 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-11-30  7:51 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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);
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface
  2020-11-26  8:17   ` Adrian Hunter
@ 2020-12-01  2:25     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-01  2:25 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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;
> >  }
> >  
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations
  2020-11-26  8:17   ` Adrian Hunter
@ 2020-12-01  2:27     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-01  2:27 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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;
> >  }
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface
  2020-11-26  8:18   ` Adrian Hunter
@ 2020-12-01  2:28     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-01  2:28 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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;
> >  }
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others
  2020-11-26  8:18   ` Adrian Hunter
@ 2020-12-01  2:40     ` AKASHI Takahiro
  2020-12-01 11:24       ` Adrian Hunter
  0 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-01  2:40 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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);
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-11-26  8:18   ` Adrian Hunter
@ 2020-12-01  3:09     ` AKASHI Takahiro
  2020-12-03  9:55       ` Adrian Hunter
  2020-12-03 10:02       ` Adrian Hunter
  0 siblings, 2 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-01  3:09 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others
  2020-12-01  2:40     ` AKASHI Takahiro
@ 2020-12-01 11:24       ` Adrian Hunter
  0 siblings, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-12-01 11:24 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  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);
>>>
>>


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() and others
  2020-11-06  2:27 ` [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() " AKASHI Takahiro
@ 2020-12-01 16:46   ` Adrian Hunter
  2020-12-08  7:37     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-12-01 16:46 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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);
>  
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver
  2020-11-06  2:27 ` [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver AKASHI Takahiro
@ 2020-12-03  9:42   ` Adrian Hunter
  2020-12-08  7:42     ` AKASHI Takahiro
  0 siblings, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-12-03  9:42 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson; +Cc: linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  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);
>  
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios()
  2020-11-30  7:51     ` AKASHI Takahiro
@ 2020-12-03  9:51       ` Adrian Hunter
  0 siblings, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-12-03  9:51 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  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);
>>>
>>


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-12-01  3:09     ` AKASHI Takahiro
@ 2020-12-03  9:55       ` Adrian Hunter
  2020-12-08  7:58         ` AKASHI Takahiro
  2020-12-03 10:02       ` Adrian Hunter
  1 sibling, 1 reply; 67+ messages in thread
From: Adrian Hunter @ 2020-12-03  9:55 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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.


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-12-01  3:09     ` AKASHI Takahiro
  2020-12-03  9:55       ` Adrian Hunter
@ 2020-12-03 10:02       ` Adrian Hunter
  1 sibling, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-12-03 10:02 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() and others
  2020-12-01 16:46   ` Adrian Hunter
@ 2020-12-08  7:37     ` AKASHI Takahiro
  2020-12-08  8:37       ` Adrian Hunter
  0 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-08  7:37 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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);
> >  
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver
  2020-12-03  9:42   ` Adrian Hunter
@ 2020-12-08  7:42     ` AKASHI Takahiro
  0 siblings, 0 replies; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-08  7:42 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  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);
> >  
> > 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-12-03  9:55       ` Adrian Hunter
@ 2020-12-08  7:58         ` AKASHI Takahiro
  2020-12-08  8:48           ` Adrian Hunter
  0 siblings, 1 reply; 67+ messages in thread
From: AKASHI Takahiro @ 2020-12-08  7:58 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: ulf.hansson, linux-mmc, linux-kernel, ben.chuang, greg.tu

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

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() and others
  2020-12-08  7:37     ` AKASHI Takahiro
@ 2020-12-08  8:37       ` Adrian Hunter
  0 siblings, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-12-08  8:37 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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 <ben.chuang@genesyslogic.com.tw>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  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);
>>>  
>>>
>>


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755
  2020-12-08  7:58         ` AKASHI Takahiro
@ 2020-12-08  8:48           ` Adrian Hunter
  0 siblings, 0 replies; 67+ messages in thread
From: Adrian Hunter @ 2020-12-08  8:48 UTC (permalink / raw)
  To: AKASHI Takahiro, ulf.hansson, linux-mmc, linux-kernel,
	ben.chuang, greg.tu

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

^ permalink raw reply	[flat|nested] 67+ messages in thread

end of thread, other threads:[~2020-12-08  8:49 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-06  2:26 [RFC PATCH v3.1 00/27] Add support UHS-II for GL9755 AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 01/27] mmc: add UHS-II related definitions in public headers AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 02/27] mmc: core: UHS-II support, modify power-up sequence AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 03/27] mmc: core: UHS-II support, skip set_chip_select() AKASHI Takahiro
2020-11-10  7:15   ` Bough Chen
2020-11-06  2:27 ` [RFC PATCH v3.1 04/27] mmc: core: UHS-II support, try to select UHS-II interface AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 05/27] mmc: core: UHS-II support, skip TMODE setup in some cases AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 06/27] mmc: core: UHS-II support, generate UHS-II SD command packet AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 07/27] mmc: core: UHS-II support, set APP_CMD bit if necessary AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 08/27] mmc: sdhci: add a kernel configuration for enabling UHS-II support AKASHI Takahiro
2020-11-26  8:14   ` Adrian Hunter
2020-11-30  5:17     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 09/27] mmc: sdhci: add UHS-II related definitions in headers AKASHI Takahiro
2020-11-26  8:15   ` Adrian Hunter
2020-11-30  5:21     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 10/27] mmc: sdhci: add UHS-II module AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 11/27] mmc: sdhci-uhs2: dump UHS-II registers AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 12/27] mmc: sdhci-uhs2: add reset function AKASHI Takahiro
2020-11-26  8:16   ` Adrian Hunter
2020-11-30  6:20     ` AKASHI Takahiro
2020-11-30  7:37       ` Adrian Hunter
2020-11-06  2:27 ` [RFC PATCH v3.1 13/27] mmc: sdhci-uhs2: add set_power() to support vdd2 AKASHI Takahiro
2020-11-26  8:16   ` Adrian Hunter
2020-11-30  7:15     ` AKASHI Takahiro
2020-11-30  7:44       ` Adrian Hunter
2020-11-06  2:27 ` [RFC PATCH v3.1 14/27] mmc: sdhci-uhs2: skip signal_voltage_switch() AKASHI Takahiro
2020-11-26  8:16   ` Adrian Hunter
2020-11-30  7:38     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 15/27] mmc: sdhci-uhs2: add set_timeout() AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 16/27] mmc: sdhci-uhs2: add set_ios() AKASHI Takahiro
2020-11-26  8:17   ` Adrian Hunter
2020-11-30  7:51     ` AKASHI Takahiro
2020-12-03  9:51       ` Adrian Hunter
2020-11-06  2:27 ` [RFC PATCH v3.1 17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface AKASHI Takahiro
2020-11-26  8:17   ` Adrian Hunter
2020-12-01  2:25     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 18/27] mmc: sdhci-uhs2: add clock operations AKASHI Takahiro
2020-11-26  8:17   ` Adrian Hunter
2020-12-01  2:27     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 19/27] mmc: sdhci-uhs2: add set_reg() to initialise the interface AKASHI Takahiro
2020-11-26  8:18   ` Adrian Hunter
2020-12-01  2:28     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 20/27] mmc: sdhci-uhs2: add request() and others AKASHI Takahiro
2020-11-26  8:18   ` Adrian Hunter
2020-12-01  2:40     ` AKASHI Takahiro
2020-12-01 11:24       ` Adrian Hunter
2020-11-06  2:27 ` [RFC PATCH v3.1 21/27] mmc: sdhci-uhs2: add irq() " AKASHI Takahiro
2020-12-01 16:46   ` Adrian Hunter
2020-12-08  7:37     ` AKASHI Takahiro
2020-12-08  8:37       ` Adrian Hunter
2020-11-06  2:27 ` [RFC PATCH v3.1 22/27] mmc: sdhci-uhs2: add add_host() and others to set up the driver AKASHI Takahiro
2020-12-03  9:42   ` Adrian Hunter
2020-12-08  7:42     ` AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 23/27] mmc: sdhci-uhs2: add pre-detect_init hook AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 24/27] mmc: core: add post-mmc_attach_sd hook AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 25/27] mmc: sdhci-uhs2: " AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 26/27] mmc: sdhci-pci: add UHS-II support framework AKASHI Takahiro
2020-11-06  2:27 ` [RFC PATCH v3.1 27/27] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755 AKASHI Takahiro
2020-11-25  7:41 ` [RFC PATCH v3.1 00/27] Add support UHS-II " AKASHI Takahiro
2020-11-25 10:43   ` Ulf Hansson
2020-11-26  0:06     ` AKASHI Takahiro
2020-11-26  8:18   ` Adrian Hunter
2020-12-01  3:09     ` AKASHI Takahiro
2020-12-03  9:55       ` Adrian Hunter
2020-12-08  7:58         ` AKASHI Takahiro
2020-12-08  8:48           ` Adrian Hunter
2020-12-03 10:02       ` Adrian Hunter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).