From: Victor Shih <[email protected]>
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-#6: for core
patch#7-#22: for sdhci
patch#23: for GL9755
Changes in v12 (September. 15, 2023)
* rebased to the linux-kernel-v6.6.0-rc1 in Ulf Hansson next branch.
* according to the comments provided by Adrian Hunter to modify the
patches base on the [V11 00/23] Add support UHS-II for GL9755.
* according to the comments provided by Ulf Hansson to modify the
patches base on the [V11 00/23] Add support UHS-II for GL9755.
* Patch#5: Remove unused max_current_180_vdd2.
* Patch#6: Use mmc_op_multi() to check DCMD which supports multi
read/write in mmc_uhs2_prepare_cmd().
Reference
=========
[1] https://gitlab.com/VictorShih/linux-uhs2.git
[2] SD Host Controller Simplified Specification 4.20
[3] UHS-II Simplified Addendum 1.02
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v11 -----------------
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-#6: for core
patch#7-#22: for sdhci
patch#23: for GL9755
Changes in v11 (September. 08, 2023)
* rebased to the linux-kernel-v6.5.0-rc5 in Ulf Hansson next branch.
* according to the comments provided by Adrian Hunter to modify the
patches base on the [V10 00/23] Add support UHS-II for GL9755.
* Patch#18: Drop the check mmc_card_uhs2_hd_mode(host->mmc)
in sdhci_uhs2_set_transfer_mode().
* Patch#20: Remove unused ocr_avail_uhs2.
Reference
=========
[1] https://gitlab.com/VictorShih/linux-uhs2.git
[2] SD Host Controller Simplified Specification 4.20
[3] UHS-II Simplified Addendum 1.02
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v10 -----------------
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-#6: for core
patch#7-#22: for sdhci
patch#23: for GL9755
Changes in v10 (August. 18, 2023)
* rebased to the linux-kernel-v6.5.0-rc5 in Ulf Hansson next branch.
* according to the comments provided by Ulf Hansson to modify the
patches base on the [V9 00/23] Add support UHS-II for GL9755.
* Patch#2: Drop unnecessary definitions and code.
* Patch#3: Modify the commit message.
* Patch#4: Modify the commit message.
* Patch#5: Drop unnecessary definitions.
* Patch#6: Move some definitions of PatchV9[02/23] to PatchV10[06/23].
Move some definitions of PatchV9[05/23] to PatchV10[06/23].
Drop do_multi in the mmc_blk_rw_rq_prep().
Use tmode_half_duplex to instead of uhs2_tmode0_flag.
Move entire control of the tmode into mmc_uhs2_prepare_cmd().
* Patch#11: Move some definitions of PatchV9[05/23] to PatchV10[11/23].
* Patch#18: Use tmode_half_duplex to instead of uhs2_tmode0_flag
in sdhci_uhs2_set_transfer_mode().
* Patch#20: Move some definitions of PatchV9[05/23] to PatchV10[20/23].
Reference
=========
[1] https://gitlab.com/VictorShih/linux-uhs2.git
[2] SD Host Controller Simplified Specification 4.20
[3] UHS-II Simplified Addendum 1.02
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v9 -----------------
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-#6: for core
patch#7-#22: for sdhci
patch#23: for GL9755
Changes in v9 (July. 21, 2023)
* rebased to the linux-kernel-v6.5.0-rc1 in Ulf Hansson next branch.
* according to the comments provided by Adrian Hunter to modify the
patches base on the [V8 00/23] Add support UHS-II for GL9755.
* Patch#2: move sd_uhs2_operation definition of PatchV8[05/23]
to PatchV9[02/23] for avoid compilation errors.
move uhs2_control definition of PatchV8[05/23]
to PatchV9[02/23] for avoid compilation errors.
move mmc_host flags definition of PatchV8[05/23]
to PatchV9[02/23] for avoid compilation errors.
move mmc_host flags MMC_UHS2_SUPPORT definition of
PatchV8[05/23] to PatchV9[02/23] for avoid compilation errors.
move mmc_host flags MMC_UHS2_SD_TRAN definition of
PatchV8[05/23] to PatchV9[02/23] for avoid compilation errors.
* Patch#7: Modify the commit message.
* Patch#8: Modify the commit message.
* Patch#11: Modify annotations in sdhci_get_vdd_value().
* Patch#14: Simplity the turning_on_clk in sdhci_set_ios().
* Patch#18: Modify the annotations in __sdhci_uhs2_send_command().
* Patch#19: Cancel export state of sdhci_set_mrq_done() function.
* Patch#23: Rename gl9755_pre_detect_init() to sdhci_gli_pre_detect_init().
Rename gl9755_uhs2_reset_sd_tran() to
sdhci_gli_uhs2_reset_sd_tran().
Reference
=========
[1] https://gitlab.com/VictorShih/linux-uhs2.git
[2] SD Host Controller Simplified Specification 4.20
[3] UHS-II Simplified Addendum 1.02
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v8 -----------------
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-#6: for core
patch#7-#22: for sdhci
patch#23: for GL9755
Changes in v8 (June. 21, 2023)
* rebased to the linux-kernel-v6.4.0-rc6 in Ulf Hansson next branch.
* fix most of checkpatch warnings/errors.
* according to the comments provided by Adrian Hunter to modify the
patches base on the [V7 00/23] Add support UHS-II for GL9755.
* Patch#6: Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
Modify return value in sd_uhs2_attach().
* Patch#7: Use tabs instead of spaces.
* Patch#8: Modify MODULE_LICENSE from "GPL v2" to "GPL".
* Patch#10: Adjust the position of matching brackets.
* Patch#11: Adjust the position of matching brackets.
Add the initial value of the pwr in sdhci_uhs2_set_power().
* Patch#13: Initialization be combined with declaration and realigned
in sdhci_calc_timeout_uhs2().
Forward declare struct mmc_command in sdhci_uhs2.h.
* Patch#14: Add the judgment formula for MMC_TIMING_SPEED_A_HD,
MMC_TIMING_SPEED_B and MMC_TIMING_SPEED_B_HD in
__sdhci_uhs2_set_ios().
Add the switch case for MMC_TIMING_SPEED_A_HD,
MMC_TIMING_SPEED_B and MMC_TIMING_SPEED_B_HD in
sdhci_get_preset_value().
mmc_opt_regulator_set_ocr() to instead of
mmc_regulator_set_ocr() in sdhci_uhs2_set_ios().
* Patch#15: usleep_range() to instead of udelay() in
sdhci_uhs2_interface_detect().
read_poll_timeout() to instead of read_poll_timeout_atomic()
in sdhci_uhs2_interface_detect().
Modify return value in sdhci_uhs2_do_detect_init().
* Patch#16: Remove unnecessary include file.
read_poll_timeout() to instead of read_poll_timeout_atomic()
in sdhci_uhs2_enable_clk().
Put the comment on the end and put the lines in descending
line length in sdhci_uhs2_enable_clk().
Modify return value in sdhci_uhs2_enable_clk().
* Patch#17: Reorder the definitions and lose the parentheses in
sdhci_uhs2_set_config().
read_poll_timeout() to instead of read_poll_timeout_atomic()
in sdhci_uhs2_check_dormant().
* Patch#18: Adjust the position of matching brackets in
sdhci_uhs2_send_command_retry().
Modify CameCase definition in __sdhci_uhs2_finish_command().
Modify error message in __sdhci_uhs2_finish_command().
sdhci_uhs2_send_command_retry() to instead of
sdhci_uhs2_send_command() in sdhci_uhs2_request().
Use sdhci_uhs2_mode() to simplify code in
sdhci_uhs2_request_atomic().
Add forward declaration for sdhci_send_command().
* Patch#19: Forward declare struct mmc_request in sdhci_uhs2.h.
Remove forward declaration of sdhci_send_command().
Use mmc_dev() to simplify code in sdhci_request_done_dma().
* Patch#20: Change return type to void for __sdhci_uhs2_add_host_v4().
Remove unused variables in __sdhci_uhs2_add_host_v4().
* Patch#22: Add config select MMC_SDHCI_UHS2 in Kconfig.
* Patch#23: Use sdhci_get_vdd_value() to simplify code in
gl9755_set_power().
Use read_poll_timeout_atomic() to simplify code in
sdhci_wait_clock_stable().
Use read_poll_timeout_atomic() to simplify code in
sdhci_gl9755_reset().
Reference
=========
[1] https://gitlab.com/VictorShih/linux-uhs2.git
[2] SD Host Controller Simplified Specification 4.20
[3] UHS-II Simplified Addendum 1.02
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v7 -----------------
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-#6: for core
patch#7-#22: for sdhci
patch#23: for GL9755
Changes in v7 (Mar. 31, 2023)
* rebased to the linux-kernel-v6.3.0-rc3 in Ulf Hansson next branch.
* according to the guidance and overall architecture provided
by Ulf Hansson, Ben Chuang and Jason Lai to implement the
UHS-2 Core function based on the patches of the [V4,0/6]
Preparations to support SD UHS-II cards[5].
* according to the guidance and comments provided by
Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
the UHS-2 Host function based on the patches of the
[RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
* implement the necessary function to let the UHS-2 Core/Host
work properly.
* fix most of checkpatch warnings/errors.
* according to the guidance and comments provided by
Adrian Hunter, Ben Chuang to implement the UHS-2
Host function based on the patches of the
[V5,00/26] Add support UHS-II for GL9755[6].
* according to the guidance and comments provided by
Ulf Hanssion, Adrian Hunter, Ben Chuang to implement the UHS-2
Host function based on the patches of the
[V6,00/24] Add support UHS-II for GL9755[7].
* The uhs2_post_attach_sd() function is no longer needed so drop
the V6 version of the Patch#22.
* Modifies the usage of the flags used by the sdhci host for
MMC_UHS2_INITIALIZED.
* Patch#1: Drop unnecessary bracket.
* Patch#2: Drop sd_uhs2_set_ios function.
Used ->uhs2_control() callback for uhs2_set_ios
in sd_uhs2_power_up().
Used ->uhs2_control() callback for uhs2_set_ios
in sd_uhs2_power_off().
Drop MMC_TIMING_SD_UHS2 in favor of MMC_TIMING_UHS2_SPEED_A.
Modify sd_uhs2_legacy_init to avoid the
sd_uhs2_reinit cycle issue.
* Patch#5: Drop unnecessary definitions.
* Patch#6: Drop unnecessary function.
Drop uhs2_state in favor of ios->timing.
* Patch#7: Reorder values and positions of definitions.
* Patch#9: Used sdhci_uhs2_mode function to simplify.
* Patch#11: Drop pwr variable in sdhci_uhs2_set_power function.
* Patch#14: Modify some descriptions.
Drop unnecessary function.
* Patch#15: Drop using uhs2_reset ops and use sdhci_uhs2_reset function
in the sdhci_do_detect_init function.
* Patch#17: Drop unnecessary function.
* Patch#18: Drop unnecessary whitespace changes.
Cancel the export state of some functions.
* Patch#19: Drop unnecessary function.
Used sdhci_uhs2_mode function to simplify.
Modify some descriptions.
Cancel the export state of some functions.
* Patch#20: Drop using __sdhci_uhs2_host function and use
__sdhci_add_host function in sdhci_uhs2_add_host function.
Cancel the export state of some functions.
* Patch#23: Drop using uhs2_post_attach_sd function.
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
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
[5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
[6] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
[7] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v6 -----------------
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-#6: for core
patch#7-#23: for sdhci
patch#24: for GL9755
Changes in v6 (Dec. 12, 2022)
* rebased to the linux-kernel-v6.1.0-rc8 in Ulf Hansson next branch.
* according to the guidance and overall architecture provided
by Ulf Hansson, Ben Chuang and Jason Lai to implement the
UHS-2 Core function based on the patches of the [V4,0/6]
Preparations to support SD UHS-II cards[5].
* according to the guidance and comments provided by
Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
the UHS-2 Host function based on the patches of the
[RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
* implement the necessary function to let the UHS-2 Core/Host
work properly.
* fix most of checkpatch warnings/errors.
* according to the guidance and comments provided by
Adrian Hunter, Ben Chuang to implement the UHS-2
Host function based on the patches of the
[V5,00/26] Add support UHS-II for GL9755[6].
* The uhs2_post_attach_sd() has implemented in Patch#6 and
Patch#17 so drop the V5 version of the Patch#23.
* Modifies the usage of the flags used by the sdhci host for
MMC_UHS2_INITIALIZED.
* Patch#5: Drop unused definitions and functions.
* Patch#7: Rename definitions.
Use BIT() GENMASK() in some cases.
* Patch#8: Merge V5 version of Patch[7] and Patch[9] into
V6 version of Patch[8].
* Patch#9: Drop unnecessary function.
Rename used definitions.
* Patch#10: Drop unnecessary function and simplify some code.
* Patch#11: Drop unnecessary function.
Add new mmc_opt_regulator_set_ocr function.
* Patch#13: Drop unnecessary function.
Use GENMASK() and FIELD_PREP() in some cases.
* Patch#14: Drop unnecessary function.
Modify return value in some function.
Use GENMASK() and FIELD_PREP() in some cases.
* Patch#15: Drop unnecessary function.
Rename used definitions.
Use GENMASK() and FIELD_GET() in some cases.
Wrap at 100 columns in some functions.
* Patch#16: Drop unnecessary function.
* Patch#17: Drop unnecessary function.
Drop the unnecessary parameter when call the DBG()
function.
Rename used definitions.
Cancel the export state of some functions.
Use GENMASK() and FIELD_PREP() in some cases.
* Patch#18: Drop unnecessary function.
Add uhs2_dev_cmd function to simplify some functions.
Rename used definitions.
Cancel the export state of some functions.
Use GENMASK() and FIELD_PREP() in some cases.
* Patch#19: Drop unnecessary function.
Add sdhci_uhs2_mode() in some functions.
Rename used definitions.
Cancel the export state of some functions.
* Patch#20: Add new complete_work_fn/thread_irq_fn variables in
struct sdhci_host.
Use complete_work_fn/thread_irq_fn variables in
sdhci_alloc_host()/sdhci_uhs2_add_host().
Rename used definitions.
* Patch[24]: Rename used definitions.
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
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
[5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
[6] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v5 -----------------
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-#6: for core
patch#7-#25: for sdhci
patch#26: for GL9755
Changes in v5 (Oct. 19, 2022)
* rebased to the linux-kernel-v6.1-rc1 in Ulf Hansson next branch.
* according to the guidance and overall architecture provided
by Ulf Hansson, Ben Chuang and Jason Lai to implement the
UHS-2 Core function based on the patches of the [V4,0/6]
Preparations to support SD UHS-II cards[5].
* according to the guidance and comments provided by
Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
the UHS-2 Host function based on the patches of the
[RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
* implement the necessary function to let the UHS-2 Core/Host
work properly.
* fix most of checkpatch warnings/errors
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
[4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
[5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
----------------- original cover letter from v3.1 -----------------
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
Ben Chuang (1):
mmc: sdhci-uhs2: add pre-detect_init hook
Ulf Hansson (4):
mmc: core: Cleanup printing of speed mode at card insertion
mmc: core: Prepare to support SD UHS-II cards
mmc: core: Announce successful insertion of an SD UHS-II card
mmc: core: Extend support for mmc regulators with a vqmmc2
Victor Shih (18):
mmc: core: Add definitions for SD UHS-II cards
mmc: core: Support UHS-II card control and access
mmc: sdhci: add UHS-II related definitions in headers
mmc: sdhci: add UHS-II module and add a kernel configuration
mmc: sdhci-uhs2: dump UHS-II registers
mmc: sdhci-uhs2: add reset function and uhs2_mode 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 uhs2_control() 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: sdhci-pci: add UHS-II support framework
mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
drivers/mmc/core/Makefile | 2 +-
drivers/mmc/core/bus.c | 38 +-
drivers/mmc/core/core.c | 25 +-
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/host.h | 7 +
drivers/mmc/core/mmc_ops.c | 24 +-
drivers/mmc/core/mmc_ops.h | 1 +
drivers/mmc/core/regulator.c | 34 +
drivers/mmc/core/sd.c | 13 +-
drivers/mmc/core/sd.h | 4 +
drivers/mmc/core/sd_ops.c | 9 +
drivers/mmc/core/sd_ops.h | 18 +
drivers/mmc/core/sd_uhs2.c | 1380 +++++++++++++++++++++++++++++
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 | 233 ++++-
drivers/mmc/host/sdhci-pci.h | 3 +
drivers/mmc/host/sdhci-uhs2.c | 1319 +++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 192 ++++
drivers/mmc/host/sdhci.c | 274 +++---
drivers/mmc/host/sdhci.h | 74 +-
include/linux/mmc/card.h | 36 +
include/linux/mmc/core.h | 13 +
include/linux/mmc/host.h | 76 ++
include/linux/mmc/sd_uhs2.h | 240 +++++
26 files changed, 3894 insertions(+), 149 deletions(-)
create mode 100644 drivers/mmc/core/sd_uhs2.c
create mode 100644 drivers/mmc/host/sdhci-uhs2.c
create mode 100644 drivers/mmc/host/sdhci-uhs2.h
create mode 100644 include/linux/mmc/sd_uhs2.h
--
2.25.1
From: Victor Shih <[email protected]>
Add UHS-II specific data structures for commands and defines for
registers, as described in Part 1 UHS-II Addendum Version 1.01.
UHS-II related definitions are listed below:
1. UHS-II card capability: sd_uhs2_caps{}
2. UHS-II configuration: sd_uhs2_config{}
3. UHS-II register I/O address and register field definitions: sd_uhs2.h
Signed-off-by: Ulf Hansson <[email protected]>
Signed-off-by: Jason Lai <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
---
Updates in V12:
- Remove unused max_current_180_vdd2.
Updates in V10:
- Drop unnecessary definitions.
Updates in V7:
- Remove unnecessary definitions.
Updates in V6:
- Remove unnecessary definitions and functions.
---
include/linux/mmc/card.h | 31 ++++-
include/linux/mmc/host.h | 25 +++-
include/linux/mmc/sd_uhs2.h | 240 ++++++++++++++++++++++++++++++++++++
3 files changed, 294 insertions(+), 2 deletions(-)
create mode 100644 include/linux/mmc/sd_uhs2.h
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 469fd68f854f..5cfc94b778f4 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -190,6 +190,12 @@ struct sd_switch_caps {
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600)
#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800)
+
+#define SD4_SET_POWER_LIMIT_0_72W 0
+#define SD4_SET_POWER_LIMIT_1_44W 1
+#define SD4_SET_POWER_LIMIT_2_16W 2
+#define SD4_SET_POWER_LIMIT_2_88W 3
+#define SD4_SET_POWER_LIMIT_1_80W 4
};
struct sd_ext_reg {
@@ -213,7 +219,30 @@ struct sd_ext_reg {
struct sd_uhs2_config {
u32 node_id;
- /* TODO: Extend with more register configs. */
+
+ 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 max_retry_set;
};
struct sdio_cccr {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 5dbc4f23797a..0d1a3e9ad93c 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -16,6 +16,7 @@
#include <linux/mmc/pm.h>
#include <linux/dma-direction.h>
#include <linux/blk-crypto-profile.h>
+#include <linux/mmc/sd_uhs2.h>
struct mmc_ios {
unsigned int clock; /* clock rate */
@@ -99,7 +100,29 @@ struct mmc_clk_phase_map {
};
struct sd_uhs2_caps {
- /* TODO: Add UHS-II capabilities for the host. */
+ u32 dap;
+ u32 gap;
+ u32 group_desc;
+ u32 maxblk_len;
+ u32 n_fcu;
+ u8 n_lanes;
+ u8 addr64;
+ u8 card_type;
+ u8 phy_rev;
+ u8 speed_range;
+ 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;
};
enum sd_uhs2_operation {
diff --git a/include/linux/mmc/sd_uhs2.h b/include/linux/mmc/sd_uhs2.h
new file mode 100644
index 000000000000..7abe9bd870c7
--- /dev/null
+++ b/include/linux/mmc/sd_uhs2.h
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * 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
+
+/* LINK Layer definition */
+/*
+ * UHS2 Header:
+ * Refer to UHS-II Addendum Version 1.02 Figure 5-2, the format of CCMD Header is described below:
+ * bit [3:0] : DID(Destination ID = Node ID of UHS2 card)
+ * bit [6:4] : TYP(Packet Type)
+ * 000b: CCMD(Control command packet)
+ * 001b: DCMD(Data command packet)
+ * 010b: RES(Response packet)
+ * 011b: DATA(Data payload packet)
+ * 111b: MSG(Message packet)
+ * Others: Reserved
+ * bit [7] : NP(Native Packet)
+ * bit [10:8] : TID(Transaction ID)
+ * bit [11] : Reserved
+ * bit [15:12]: SID(Source ID 0: Node ID of Host)
+ *
+ * Broadcast CCMD issued by Host is represented as DID=SID=0.
+ */
+/*
+ * UHS2 Argument:
+ * Refer to UHS-II Addendum Version 1.02 Figure 6-5, the format of CCMD Argument is described below:
+ * bit [3:0] : MSB of IOADR
+ * bit [5:4] : PLEN(Payload Length)
+ * 00b: 0 byte
+ * 01b: 4 bytes
+ * 10b: 8 bytes
+ * 11b: 16 bytes
+ * bit [6] : Reserved
+ * bit [7] : R/W(Read/Write)
+ * 0: Control read command
+ * 1: Control write command
+ * bit [15:8] : LSB of IOADR
+ *
+ * I/O Address specifies the address of register in UHS-II I/O space accessed by CCMD.
+ * The unit of I/O Address is 4 Bytes. It is transmitted in MSB first, LSB last.
+ */
+#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_CCMD_DEV_INIT_COMPLETE_FLAG BIT(11)
+#define UHS2_DEV_INIT_PAYLOAD_LEN 1
+#define UHS2_DEV_INIT_RESP_LEN 6
+#define UHS2_DEV_ENUM_PAYLOAD_LEN 1
+#define UHS2_DEV_ENUM_RESP_LEN 8
+#define UHS2_CFG_WRITE_PAYLOAD_LEN 2
+#define UHS2_CFG_WRITE_PHY_SET_RESP_LEN 4
+#define UHS2_CFG_WRITE_GENERIC_SET_RESP_LEN 5
+#define UHS2_GO_DORMANT_PAYLOAD_LEN 1
+
+/*
+ * UHS2 Argument:
+ * Refer to UHS-II Addendum Version 1.02 Figure 6-8, the format of DCMD Argument is described below:
+ * bit [3:0] : Reserved
+ * bit [6:3] : TMODE(Transfer Mode)
+ * bit 3: DAM(Data Access Mode)
+ * bit 4: TLUM(TLEN Unit Mode)
+ * bit 5: LM(Length Mode)
+ * bit 6: DM(Duplex Mode)
+ * bit [7] : R/W(Read/Write)
+ * 0: Control read command
+ * 1: Control write command
+ * bit [15:8] : Reserved
+ *
+ * I/O Address specifies the address of register in UHS-II I/O space accessed by CCMD.
+ * The unit of I/O Address is 4 Bytes. It is transmitted in MSB first, LSB last.
+ */
+#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)
+
+#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)
+
+/* 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_N_LANES_POS 8
+#define UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD 0x0
+#define UHS2_DEV_CONFIG_GEN_SET_2D1U_FD 0x2
+#define UHS2_DEV_CONFIG_GEN_SET_1D2U_FD 0x3
+#define UHS2_DEV_CONFIG_GEN_SET_2D2U_FD 0x4
+#define UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE BIT(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 BIT(7)
+#define UHS2_DEV_CMD_DEVICE_INIT (UHS2_DEV_CMD_REG + 0x002)
+#define UHS2_DEV_INIT_COMPLETE_FLAG BIT(11)
+#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.25.1
From: Victor Shih <[email protected]>
This patch prepares for adding UHS-II support at a specific UHS-II
capable sdhci-pci controller, GL9755 for now.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V8:
- Add config select MMC_SDHCI_UHS2 in Kconfig.
---
drivers/mmc/host/Kconfig | 1 +
drivers/mmc/host/sdhci-pci-core.c | 16 +++++++++++++++-
drivers/mmc/host/sdhci-pci.h | 3 +++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1aeded48ec99..f6a7bfaa67e4 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -111,6 +111,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-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 7c14feb5db77..22698119ecf2 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -40,6 +40,7 @@
#include "sdhci.h"
#include "sdhci-cqhci.h"
#include "sdhci-pci.h"
+#include "sdhci-uhs2.h"
static void sdhci_pci_hw_reset(struct sdhci_host *host);
@@ -2160,7 +2161,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);
@@ -2168,6 +2172,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 153704f812ed..e807c039a8b1 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -145,6 +145,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 *);
@@ -189,6 +190,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.25.1
From: Victor Shih <[email protected]>
This is a UHS-II version of sdhci's add_host/remove_host operation.
Any sdhci drivers which are capable of handling UHS-II cards must
call those functions instead of the corresponding sdhci's.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V11:
- Remove unused ocr_avail_uhs2.
Updates in V10:
- Move some definitions of PatchV9[05/23] to PatchV10[20/23].
Updates in V8:
- Change return type to void for __sdhci_uhs2_add_host_v4().
- Remove unused variables in __sdhci_uhs2_add_host_v4().
Updates in V7:
- __sdhci_add_host() to instead of __sdhci_uhs2_add_host()
in sdhci_uhs2_add_host().
- Cancel export state of some functions.
Updates in V6:
- Add complete_work_fn/thread_irq_fn variables in struct sdhci_host.
- Use complete_work_fn/thread_irq_fn variables in
sdhci_alloc_host() and sdhci_uhs2_add_host().
- Use sdhci_uhs2_mode() to simplify code in __sdhci_uhs2_remove_host().
---
drivers/mmc/host/sdhci-uhs2.c | 100 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
drivers/mmc/host/sdhci.c | 7 ++-
drivers/mmc/host/sdhci.h | 3 +
4 files changed, 110 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 7e4265c404ac..add0eea55b24 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -16,6 +16,7 @@
#include <linux/bitfield.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
+#include <linux/regulator/consumer.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -997,6 +998,105 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*****************************************************************************\
+ *
+ * Device allocation/registration *
+ * *
+\*****************************************************************************/
+
+static void __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
+{
+ struct mmc_host *mmc;
+ u32 max_current_caps2;
+
+ mmc = host->mmc;
+
+ /* Support UHS2 */
+ if (caps1 & SDHCI_SUPPORT_UHS2)
+ mmc->caps2 |= MMC_CAP2_SD_UHS2;
+
+ max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
+
+ if ((caps1 & SDHCI_CAN_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_CAN_VDD2_180))
+ mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+}
+
+static int sdhci_uhs2_host_ops_init(struct sdhci_host *host);
+
+static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+ if (!sdhci_uhs2_mode(host))
+ return;
+
+ if (!dead)
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
+}
+
+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)
+ __sdhci_uhs2_add_host_v4(host, host->caps1);
+
+ if ((mmc->caps2 & MMC_CAP2_SD_UHS2) && !host->v4_mode)
+ /* host doesn't want to enable UHS2 support */
+ mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+
+ /* overwrite ops */
+ if (mmc->caps2 & MMC_CAP2_SD_UHS2)
+ sdhci_uhs2_host_ops_init(host);
+
+ host->complete_work_fn = sdhci_uhs2_complete_work;
+ host->thread_irq_fn = sdhci_uhs2_thread_irq;
+
+ /* LED support not implemented for UHS2 */
+ host->quirks |= SDHCI_QUIRK_NO_LED;
+
+ ret = __sdhci_add_host(host);
+ if (ret)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ if (host->version >= SDHCI_SPEC_400)
+ __sdhci_uhs2_remove_host(host, 0);
+
+ 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_remove_host(host, dead);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_remove_host);
+
void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host = mmc_priv(mmc);
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 3aa2cb4b39d6..bd5aae054c6f 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -186,5 +186,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 318d4830732f..b3de7e30ba54 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -4104,6 +4104,9 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->max_timeout_count = 0xE;
+ host->complete_work_fn = sdhci_complete_work;
+ host->thread_irq_fn = sdhci_thread_irq;
+
return host;
}
@@ -4853,7 +4856,7 @@ int __sdhci_add_host(struct sdhci_host *host)
if (!host->complete_wq)
return -ENOMEM;
- INIT_WORK(&host->complete_work, sdhci_complete_work);
+ INIT_WORK(&host->complete_work, host->complete_work_fn);
timer_setup(&host->timer, sdhci_timeout_timer, 0);
timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
@@ -4862,7 +4865,7 @@ int __sdhci_add_host(struct sdhci_host *host)
sdhci_init(host, 0);
- ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
+ ret = request_threaded_irq(host->irq, sdhci_irq, host->thread_irq_fn,
IRQF_SHARED, mmc_hostname(mmc), host);
if (ret) {
pr_err("%s: Failed to request IRQ %d: %d\n",
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 6bbb9f073f29..5235f2da6568 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -626,6 +626,9 @@ struct sdhci_host {
struct timer_list timer; /* Timer for timeouts */
struct timer_list data_timer; /* Timer for data timeouts */
+ void (*complete_work_fn)(struct work_struct *work);
+ irqreturn_t (*thread_irq_fn)(int irq, void *dev_id);
+
#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
struct dma_chan *rx_chan;
struct dma_chan *tx_chan;
--
2.25.1
From: Ulf Hansson <[email protected]>
The SD UHS-II interface was introduced to the SD spec v4.00 several years
ago. The interface is fundamentally different from an electrical and a
protocol point of view, comparing to the legacy SD interface.
However, the legacy SD protocol is supported through a specific transport
layer (SD-TRAN) defined in the UHS-II addendum of the spec. This allows the
SD card to be managed in a very similar way as a legacy SD card, hence a
lot of code can be re-used to support these new types of cards through the
mmc subsystem.
Moreover, an SD card that supports the UHS-II interface shall also be
backwards compatible with the legacy SD interface, which allows a UHS-II
card to be inserted into a legacy slot. As a matter of fact, this is
already supported by mmc subsystem as of today.
To prepare to add support for UHS-II, this change puts the basic foundation
in the mmc core in place, allowing it to be more easily reviewed before
subsequent changes implements the actual support.
Basically, the approach here adds a new UHS-II bus_ops type and adds a
separate initialization path for the UHS-II card. The intent is to avoid us
from sprinkling the legacy initialization path, but also to simplify
implementation of the UHS-II specific bits.
At this point, there is only one new host ops added to manage the various
ios settings needed for UHS-II. Additional host ops that are needed, are
being added from subsequent changes.
Signed-off-by: Ulf Hansson <[email protected]>
---
Updates in V10:
- Drop unnecessary definitions and code.
Updates in V9:
- move sd_uhs2_operation definition of PatchV8[05/23] to
PatchV9[02/23] for avoid compilation errors.
- move uhs2_control definition of PatchV8[05/23] to
PatchV9[02/23] for avoid compilation errors.
- move mmc_host flags definition of PatchV8[05/23] to
PatchV9[02/23] for avoid compilation errors.
- move mmc_host flags MMC_UHS2_SUPPORT definition of PatchV8[05/23] to
PatchV9[02/23] for avoid compilation errors.
- move mmc_host flags MMC_UHS2_SD_TRAN definition of PatchV8[05/23] to
PatchV9[02/23] for avoid compilation errors.
Updates in V7:
- Drop sd_uhs2_set_ios function.
- Used ->uhs2_control() callback for uhs2_set_ios in sd_uhs2_power_up().
- Used ->uhs2_control() callback for uhs2_set_ios in sd_uhs2_power_off().
- Drop MMC_TIMING_SD_UHS2 in favor of MMC_TIMING_UHS2_SPEED_A.
- Modify sd_uhs2_legacy_init to avoid sd_uhs2_reinit cycle issue.
Updates in V4:
- Re-based, updated a comment and removed white-space.
- Moved MMC_VQMMC2_VOLTAGE_180 into a later patch in the series.
---
drivers/mmc/core/Makefile | 2 +-
drivers/mmc/core/core.c | 17 ++-
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/sd_uhs2.c | 292 +++++++++++++++++++++++++++++++++++++
include/linux/mmc/card.h | 7 +
include/linux/mmc/host.h | 23 +++
6 files changed, 340 insertions(+), 2 deletions(-)
create mode 100644 drivers/mmc/core/sd_uhs2.c
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 6a907736cd7a..15b067e8b0d1 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_MMC) += mmc_core.o
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 \
+ sdio_cis.o sdio_io.o sdio_irq.o sd_uhs2.o\
slot-gpio.o regulator.o
mmc_core-$(CONFIG_OF) += pwrseq.o
obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3d3e0ca52614..ba8808cd9318 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2244,6 +2244,18 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+ /*
+ * Ideally we should favor initialization of legacy SD cards and defer
+ * UHS-II enumeration. However, it seems like cards doesn't reliably
+ * announce their support for UHS-II in the response to the ACMD41,
+ * while initializing the legacy SD interface. Therefore, let's start
+ * with UHS-II for now.
+ */
+ if (!mmc_attach_sd_uhs2(host)) {
+ mmc_release_host(host);
+ goto out;
+ }
+
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
unsigned int freq = freqs[i];
if (freq > host->f_max) {
@@ -2276,10 +2288,13 @@ void mmc_rescan(struct work_struct *work)
void mmc_start_host(struct mmc_host *host)
{
+ bool power_up = !(host->caps2 &
+ (MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_SD_UHS2));
+
host->f_init = max(min(freqs[0], host->f_max), host->f_min);
host->rescan_disable = 0;
- if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
+ if (power_up) {
mmc_claim_host(host);
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 37091a6589ed..920323faa834 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -81,6 +81,7 @@ int mmc_detect_card_removed(struct mmc_host *host);
int mmc_attach_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host);
int mmc_attach_sdio(struct mmc_host *host);
+int mmc_attach_sd_uhs2(struct mmc_host *host);
/* Module parameters */
extern bool use_spi_crc;
diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
new file mode 100644
index 000000000000..beb2541338ff
--- /dev/null
+++ b/drivers/mmc/core/sd_uhs2.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Linaro Ltd
+ *
+ * Author: Ulf Hansson <[email protected]>
+ *
+ * Support for SD UHS-II cards
+ */
+#include <linux/err.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+#include "core.h"
+#include "bus.h"
+#include "sd.h"
+#include "mmc_ops.h"
+
+static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
+
+static int sd_uhs2_power_up(struct mmc_host *host)
+{
+ int err;
+
+ if (host->ios.power_mode == MMC_POWER_ON)
+ return 0;
+
+ host->ios.vdd = fls(host->ocr_avail) - 1;
+ host->ios.clock = host->f_init;
+ host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
+ host->ios.power_mode = MMC_POWER_ON;
+
+ err = host->ops->uhs2_control(host, UHS2_SET_IOS);
+
+ return err;
+}
+
+static int sd_uhs2_power_off(struct mmc_host *host)
+{
+ if (host->ios.power_mode == MMC_POWER_OFF)
+ return 0;
+
+ host->ios.vdd = 0;
+ host->ios.clock = 0;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ host->ios.power_mode = MMC_POWER_OFF;
+
+ return host->ops->uhs2_control(host, UHS2_SET_IOS);
+}
+
+/*
+ * Run the phy initialization sequence, which mainly relies on the UHS-II host
+ * to check that we reach the expected electrical state, between the host and
+ * the card.
+ */
+static int sd_uhs2_phy_init(struct mmc_host *host)
+{
+ return 0;
+}
+
+/*
+ * Do the early initialization of the card, by sending the device init broadcast
+ * command and wait for the process to be completed.
+ */
+static int sd_uhs2_dev_init(struct mmc_host *host)
+{
+ return 0;
+}
+
+/*
+ * Run the enumeration process by sending the enumerate command to the card.
+ * Note that, we currently support only the point to point connection, which
+ * means only one card can be attached per host/slot.
+ */
+static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id)
+{
+ return 0;
+}
+
+/*
+ * Read the UHS-II configuration registers (CFG_REG) of the card, by sending it
+ * commands and by parsing the responses. Store a copy of the relevant data in
+ * card->uhs2_config.
+ */
+static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
+{
+ return 0;
+}
+
+/*
+ * Based on the card's and host's UHS-II capabilities, let's update the
+ * configuration of the card and the host. This may also include to move to a
+ * greater speed range/mode. Depending on the updated configuration, we may need
+ * to do a soft reset of the card via sending it a GO_DORMANT_STATE command.
+ *
+ * In the final step, let's check if the card signals "config completion", which
+ * indicates that the card has moved from config state into active state.
+ */
+static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
+{
+ return 0;
+}
+
+/*
+ * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
+ * commands/requests to be backwards compatible through the legacy SD protocol.
+ * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
+ * be set through a legacy CMD6. Note that, the power limit that becomes set,
+ * survives a soft reset through the GO_DORMANT_STATE command.
+ */
+static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
+{
+ return 0;
+}
+
+/*
+ * Allocate the data structure for the mmc_card and run the UHS-II specific
+ * initialization sequence.
+ */
+static int sd_uhs2_init_card(struct mmc_host *host)
+{
+ struct mmc_card *card;
+ u32 node_id;
+ int err;
+
+ err = sd_uhs2_dev_init(host);
+ if (err)
+ return err;
+
+ err = sd_uhs2_enum(host, &node_id);
+ if (err)
+ return err;
+
+ card = mmc_alloc_card(host, &sd_type);
+ if (IS_ERR(card))
+ return PTR_ERR(card);
+
+ card->uhs2_config.node_id = node_id;
+ card->type = MMC_TYPE_SD;
+
+ err = sd_uhs2_config_read(host, card);
+ if (err)
+ goto err;
+
+ err = sd_uhs2_config_write(host, card);
+ if (err)
+ goto err;
+
+ host->card = card;
+ return 0;
+
+err:
+ mmc_remove_card(card);
+ return err;
+}
+
+static void sd_uhs2_remove(struct mmc_host *host)
+{
+ mmc_remove_card(host->card);
+ host->card = NULL;
+}
+
+static int sd_uhs2_alive(struct mmc_host *host)
+{
+ return mmc_send_status(host->card, NULL);
+}
+
+static void sd_uhs2_detect(struct mmc_host *host)
+{
+ int err;
+
+ mmc_get_card(host->card, NULL);
+ err = _mmc_detect_card_removed(host);
+ mmc_put_card(host->card, NULL);
+
+ if (err) {
+ sd_uhs2_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ sd_uhs2_power_off(host);
+ mmc_release_host(host);
+ }
+}
+
+static int sd_uhs2_suspend(struct mmc_host *host)
+{
+ return 0;
+}
+
+static int sd_uhs2_resume(struct mmc_host *host)
+{
+ return 0;
+}
+
+static int sd_uhs2_runtime_suspend(struct mmc_host *host)
+{
+ return 0;
+}
+
+static int sd_uhs2_runtime_resume(struct mmc_host *host)
+{
+ return 0;
+}
+
+static int sd_uhs2_shutdown(struct mmc_host *host)
+{
+ return 0;
+}
+
+static int sd_uhs2_hw_reset(struct mmc_host *host)
+{
+ return 0;
+}
+
+static const struct mmc_bus_ops sd_uhs2_ops = {
+ .remove = sd_uhs2_remove,
+ .alive = sd_uhs2_alive,
+ .detect = sd_uhs2_detect,
+ .suspend = sd_uhs2_suspend,
+ .resume = sd_uhs2_resume,
+ .runtime_suspend = sd_uhs2_runtime_suspend,
+ .runtime_resume = sd_uhs2_runtime_resume,
+ .shutdown = sd_uhs2_shutdown,
+ .hw_reset = sd_uhs2_hw_reset,
+};
+
+static int sd_uhs2_attach(struct mmc_host *host)
+{
+ int err;
+
+ err = sd_uhs2_power_up(host);
+ if (err)
+ goto err;
+
+ err = sd_uhs2_phy_init(host);
+ if (err)
+ goto err;
+
+ err = sd_uhs2_init_card(host);
+ if (err)
+ goto err;
+
+ err = sd_uhs2_legacy_init(host, host->card);
+ if (err)
+ goto err;
+
+ mmc_attach_bus(host, &sd_uhs2_ops);
+
+ mmc_release_host(host);
+
+ err = mmc_add_card(host->card);
+ if (err)
+ goto remove_card;
+
+ mmc_claim_host(host);
+ return 0;
+
+remove_card:
+ mmc_remove_card(host->card);
+ host->card = NULL;
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+err:
+ sd_uhs2_power_off(host);
+ return err;
+}
+
+int mmc_attach_sd_uhs2(struct mmc_host *host)
+{
+ int i, err = 0;
+
+ if (!(host->caps2 & MMC_CAP2_SD_UHS2))
+ return -EOPNOTSUPP;
+
+ /* Turn off the legacy SD interface before trying with UHS-II. */
+ mmc_power_off(host);
+
+ /*
+ * Start UHS-II initialization at 52MHz and possibly make a retry at
+ * 26MHz according to the spec. It's required that the host driver
+ * validates ios->clock, to set a rate within the correct range.
+ */
+ for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) {
+ host->f_init = sd_uhs2_freqs[i];
+ err = sd_uhs2_attach(host);
+ if (!err)
+ break;
+ }
+
+ return err;
+}
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index daa2f40d9ce6..469fd68f854f 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -211,6 +211,11 @@ struct sd_ext_reg {
#define SD_EXT_PERF_CMD_QUEUE (1<<4)
};
+struct sd_uhs2_config {
+ u32 node_id;
+ /* TODO: Extend with more register configs. */
+};
+
struct sdio_cccr {
unsigned int sdio_vsn;
unsigned int sd_vsn;
@@ -318,6 +323,8 @@ struct mmc_card {
struct sd_ext_reg ext_power; /* SD extension reg for PM */
struct sd_ext_reg ext_perf; /* SD extension reg for PERF */
+ struct sd_uhs2_config uhs2_config; /* SD UHS-II config */
+
unsigned int sdio_funcs; /* number of SDIO functions */
atomic_t sdio_funcs_probed; /* number of probed SDIO funcs */
struct sdio_cccr cccr; /* common card info */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 62a6847a3b6f..505b30935f48 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -63,6 +63,10 @@ struct mmc_ios {
#define MMC_TIMING_MMC_HS400 10
#define MMC_TIMING_SD_EXP 11
#define MMC_TIMING_SD_EXP_1_2V 12
+#define MMC_TIMING_UHS2_SPEED_A 13
+#define MMC_TIMING_UHS2_SPEED_A_HD 14
+#define MMC_TIMING_UHS2_SPEED_B 15
+#define MMC_TIMING_UHS2_SPEED_B_HD 16
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
@@ -91,6 +95,14 @@ struct mmc_clk_phase_map {
struct mmc_clk_phase phase[MMC_NUM_CLK_PHASES];
};
+struct sd_uhs2_caps {
+ /* TODO: Add UHS-II capabilities for the host. */
+};
+
+enum sd_uhs2_operation {
+ UHS2_SET_IOS,
+};
+
struct mmc_host;
enum mmc_err_stat {
@@ -218,6 +230,14 @@ struct mmc_host_ops {
/* Initialize an SD express card, mandatory for MMC_CAP2_SD_EXP. */
int (*init_sd_express)(struct mmc_host *host, struct mmc_ios *ios);
+
+ /*
+ * The uhs2_control callback is used to execute SD UHS-II specific
+ * operations. It's mandatory to implement for hosts that supports the
+ * SD UHS-II interface (MMC_CAP2_SD_UHS2). Expected return values are a
+ * negative errno in case of a failure or zero for success.
+ */
+ int (*uhs2_control)(struct mmc_host *host, enum sd_uhs2_operation op);
};
struct mmc_cqe_ops {
@@ -402,6 +422,7 @@ struct mmc_host {
MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP2_SD_EXP (1 << 7) /* SD express via PCIe */
#define MMC_CAP2_SD_EXP_1_2V (1 << 8) /* SD express 1.2V */
+#define MMC_CAP2_SD_UHS2 (1 << 9) /* SD UHS-II support */
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
@@ -428,6 +449,8 @@ struct mmc_host {
#endif
#define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */
+ struct sd_uhs2_caps uhs2_caps; /* Host UHS-II capabilities */
+
int fixed_drv_type; /* fixed driver type for non-removable media */
mmc_pm_flag_t pm_caps; /* supported pm features */
--
2.25.1
From: Victor Shih <[email protected]>
This is a UHS-II version of sdhci's set_power operation.
VDD2, as well as VDD, is handled here.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V10:
- Move some definitions of PatchV9[05/23] to PatchV10[11/23].
Updates in V9:
- Modify annotations in sdhci_get_vdd_value().
Updates in V8:
- Adjust the position of matching brackets.
- Add the initial value of the pwr in sdhci_uhs2_set_power().
Updates in V7:
- Add clear the power reg before setting a new value
in sdhci_uhs2_set_power().
- Add MMC_VDD_34_35 case and MMC_VDD_35_36 case in sdhci_get_vdd_value().
- Drop pwr variable in sdhci_get_vdd_value().
Updates in V6:
- Add mmc_opt_regulator_set_ocr().
- Remove unnecessary functions.
---
drivers/mmc/host/sdhci-uhs2.c | 48 +++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 61 +++++++++++++++++++----------------
drivers/mmc/host/sdhci.h | 1 +
include/linux/mmc/host.h | 1 +
4 files changed, 83 insertions(+), 28 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index dfc80a7f1bad..fc37a34629c2 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -57,6 +57,13 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
* *
\*****************************************************************************/
+static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
+ struct regulator *supply,
+ unsigned short vdd_bit)
+{
+ return IS_ERR_OR_NULL(supply) ? 0 : mmc_regulator_set_ocr(mmc, supply, vdd_bit);
+}
+
bool sdhci_uhs2_mode(struct sdhci_host *host)
{
return host->mmc->flags & MMC_UHS2_SUPPORT;
@@ -94,6 +101,47 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
+{
+ struct mmc_host *mmc = host->mmc;
+ u8 pwr = 0;
+
+ 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);
+
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
+ } else {
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ /* support 1.8v only for now */
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc2, fls(MMC_VDD_165_195) - 1);
+
+ /* Clear the power reg before setting a new value */
+ sdhci_writeb(host, 0, 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);
+ }
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 753b251179f2..eca54a16e7fc 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -23,7 +23,7 @@
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
-
+#include <linux/bug.h>
#include <linux/leds.h>
#include <linux/mmc/mmc.h>
@@ -2061,41 +2061,46 @@ 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)
+{
+ 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:
+ return SDHCI_POWER_180;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ return SDHCI_POWER_300;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ /*
+ * 3.4V ~ 3.6V are valid only for those platforms where it's
+ * known that the voltage range is supported by hardware.
+ */
+ case MMC_VDD_34_35:
+ case MMC_VDD_35_36:
+ return SDHCI_POWER_330;
+ default:
+ return 0;
+ }
+}
+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:
- /*
- * 3.4 ~ 3.6V are valid only for those platforms where it's
- * known that the voltage range is supported by hardware.
- */
- case MMC_VDD_34_35:
- case MMC_VDD_35_36:
- 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;
}
}
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 43ad3f4b7672..f3bd558b337f 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -837,6 +837,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);
int sdhci_get_cd_nogpio(struct mmc_host *mmc);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 6c8258310641..610644a0ace5 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -363,6 +363,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 */
struct regulator *vqmmc2; /* Optional supply for phy */
};
--
2.25.1
From: Victor Shih <[email protected]>
This is a UHS-II version of sdhci's set_timeout() operation.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V8:
- Initialization be combined with declaration and realigned
in sdhci_calc_timeout_uhs2().
- Forward declare struct mmc_command in sdhci_uhs2.h.
Updates in V6:
- Use GENMASK() and FIELD_PREP() in some case.
- Use sdhci_uhs2_mode() to simplify code.
- Remove unnecessary functions.
---
drivers/mmc/host/sdhci-uhs2.c | 72 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
2 files changed, 74 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 92fb69b7e209..d519e6ce6199 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/module.h>
#include <linux/iopoll.h>
+#include <linux/bitfield.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -142,6 +143,77 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
}
}
+static u8 sdhci_calc_timeout_uhs2(struct sdhci_host *host, u8 *cmd_res, u8 *dead_lock)
+{
+ /* timeout in us */
+ unsigned int dead_lock_timeout = 1 * 1000 * 1000;
+ unsigned int cmd_res_timeout = 5 * 1000;
+ unsigned int current_timeout;
+ u8 count;
+
+ /*
+ * 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 |= FIELD_PREP(SDHCI_UHS2_TIMER_CTRL_DEADLOCK_MASK, dead_lock);
+ 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 (sdhci_uhs2_mode(host))
+ __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 8253d50f7852..ccf4e1834c2d 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -175,9 +175,11 @@
#define SDHCI_UHS2_VENDOR_PTR 0xE8
struct sdhci_host;
+struct mmc_command;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
bool sdhci_uhs2_mode(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
+void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
#endif /* __SDHCI_UHS2_H */
--
2.25.1
From: Victor Shih <[email protected]>
Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init
operation. After detected, the host's UHS-II capabilities will be set up
here and interrupts will also be enabled.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V8:
- usleep_range() to instead of udelay() in sdhci_uhs2_interface_detect().
- read_poll_timeout() to instead of read_poll_timeout_atomic()
in sdhci_uhs2_interface_detect().
- Modify return value in sdhci_uhs2_do_detect_init().
Updates in V7:
- Drop using uhs2_reset ops and use sdhci_uhs2_reset()
in sdhci_uhs2_do_detect_init().
Updates in V6:
- Remove unnecessary functions.
- Wrap at 100 columns in some functions.
---
drivers/mmc/host/sdhci-uhs2.c | 112 ++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index ad791c48f681..4c2a56629ab3 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -335,6 +335,118 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* *
\*****************************************************************************/
+static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
+{
+ int timeout = 100000; /* 100ms */
+ u32 val;
+
+ usleep_range(50, 200); /* wait for 50us - 200us before check */
+
+ if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT),
+ 100, timeout, true, host, SDHCI_PRESENT_STATE)) {
+ pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+
+ /* Enable UHS2 error interrupts */
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK, SDHCI_UHS2_INT_ERROR_MASK);
+
+ /* 150ms */
+ timeout = 150000;
+ if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC),
+ 100, timeout, true, host, SDHCI_PRESENT_STATE)) {
+ pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+
+ 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;
+
+ caps_ptr = sdhci_readw(host, SDHCI_UHS2_CAPS_PTR);
+ if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
+ pr_err("%s: SDHCI_UHS2_CAPS_PTR(%d) is wrong.\n",
+ mmc_hostname(mmc), caps_ptr);
+ return -ENODEV;
+ }
+ caps_gen = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_OFFSET);
+ caps_phy = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_PHY_OFFSET);
+ caps_tran[0] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_OFFSET);
+ caps_tran[1] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_1_OFFSET);
+
+ /* General Caps */
+ mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_CAPS_DAP_MASK;
+ mmc->uhs2_caps.gap = FIELD_GET(SDHCI_UHS2_CAPS_GAP_MASK, caps_gen);
+ mmc->uhs2_caps.n_lanes = FIELD_GET(SDHCI_UHS2_CAPS_LANE_MASK, caps_gen);
+ mmc->uhs2_caps.addr64 = (caps_gen & SDHCI_UHS2_CAPS_ADDR_64) ? 1 : 0;
+ mmc->uhs2_caps.card_type = FIELD_GET(SDHCI_UHS2_CAPS_DEV_TYPE_MASK, caps_gen);
+
+ /* PHY Caps */
+ mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_CAPS_PHY_REV_MASK;
+ mmc->uhs2_caps.speed_range = FIELD_GET(SDHCI_UHS2_CAPS_PHY_RANGE_MASK, caps_phy);
+ mmc->uhs2_caps.n_lss_sync = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_SYN_MASK, caps_phy);
+ mmc->uhs2_caps.n_lss_dir = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_DIR_MASK, caps_phy);
+ 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_CAPS_TRAN_LINK_REV_MASK;
+ mmc->uhs2_caps.n_fcu = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_N_FCU_MASK, caps_tran[0]);
+ if (mmc->uhs2_caps.n_fcu == 0)
+ mmc->uhs2_caps.n_fcu = 256;
+ mmc->uhs2_caps.host_type = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_HOST_TYPE_MASK, caps_tran[0]);
+ mmc->uhs2_caps.maxblk_len = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_BLK_LEN_MASK, caps_tran[0]);
+ mmc->uhs2_caps.n_data_gap = caps_tran[1] & SDHCI_UHS2_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);
+
+ DBG("Begin do uhs2 detect init.\n");
+
+ if (sdhci_uhs2_interface_detect(host)) {
+ pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
+ return -EIO;
+ }
+
+ if (sdhci_uhs2_init(host)) {
+ pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
+ return -EIO;
+ }
+
+ /* 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_INT_ERROR_MASK);
+ /*
+ * N.B 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);
+
+ return 0;
+}
+
static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
{
host->mmc_host_ops.start_signal_voltage_switch =
--
2.25.1
From: Victor Shih <[email protected]>
This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V8:
- Remove unnecessary include file.
- read_poll_timeout() to instead of read_poll_timeout_atomic()
in sdhci_uhs2_enable_clk().
- Put the comment on the end and put the lines in descending
line length in sdhci_uhs2_enable_clk().
- Modify return value in sdhci_uhs2_enable_clk().
Updates in V6:
- Remove unnecessary functions.
---
drivers/mmc/host/sdhci-uhs2.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 4c2a56629ab3..af1b0c5e48fd 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -329,6 +329,36 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
return 0;
}
+static int 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);
+
+ return 0;
+}
+
+static int sdhci_uhs2_enable_clk(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ int timeout_us = 20000; /* 20ms */
+ u32 val;
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ if (read_poll_timeout(sdhci_readw, val, (val & SDHCI_CLOCK_INT_STABLE),
+ 10, timeout_us, true, host, SDHCI_CLOCK_CONTROL)) {
+ pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+ return 0;
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
--
2.25.1
From: Victor Shih <[email protected]>
For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
so no voltage switch required.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V5:
- Use sdhci_uhs2_mode() to simplify code in
sdhci_uhs2_start_signal_voltage_switch().
---
drivers/mmc/host/sdhci-uhs2.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index fc37a34629c2..92fb69b7e209 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -142,6 +142,27 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
}
}
+/*****************************************************************************\
+ * *
+ * 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 (sdhci_uhs2_mode(host))
+ return 0;
+
+ return sdhci_start_signal_voltage_switch(mmc, ios);
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -150,6 +171,9 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
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.25.1
From: Victor Shih <[email protected]>
This patch adds sdhci-uhs2.c as a module for UHS-II support.
This is a skeleton for further development in this patch series.
This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
in the following commits to indicate UHS-II specific code in sdhci
controllers.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V9:
- Modify the commit message.
Updates in V8:
- Modify MODULE_LICENSE from "GPL v2" to "GPL".
Updates in V6:
- Merage V5 of patch[7] and patch[9] in to V6 of patch[8].
---
drivers/mmc/host/Kconfig | 9 +++++++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-uhs2.c | 46 +++++++++++++++++++++++++++++++++++
3 files changed, 56 insertions(+)
create mode 100644 drivers/mmc/host/sdhci-uhs2.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2e8476db2381..1aeded48ec99 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -98,6 +98,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
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index a693fa3d3f1c..799f21d1f81f 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..608f8ad5aaed
--- /dev/null
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/drivers/mmc/host/sdhci_uhs2.c - Secure Digital Host Controller
+ * Interface driver
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ * Copyright (C) 2020 Genesys Logic, Inc.
+ * Authors: Ben Chuang <[email protected]>
+ * Copyright (C) 2020 Linaro Limited
+ * Author: AKASHI Takahiro <[email protected]>
+ */
+
+#include <linux/module.h>
+
+#include "sdhci.h"
+#include "sdhci-uhs2.h"
+
+#define DRIVER_NAME "sdhci_uhs2"
+#define DBG(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
+{
+ return 0;
+}
+
+static int __init sdhci_uhs2_mod_init(void)
+{
+ return 0;
+}
+module_init(sdhci_uhs2_mod_init);
+
+static void __exit sdhci_uhs2_mod_exit(void)
+{
+}
+module_exit(sdhci_uhs2_mod_exit);
+
+MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
+MODULE_DESCRIPTION("MMC UHS-II Support");
+MODULE_LICENSE("GPL");
--
2.25.1
From: Victor Shih <[email protected]>
This is a sdhci version of mmc's request operation.
It covers both UHS-I and UHS-II.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
---
Updates in V11:
- Drop the check mmc_card_uhs2_hd_mode(host->mmc)
in sdhci_uhs2_set_transfer_mode().
Updates in V10:
- Use tmode_half_duplex to instead of uhs2_tmode0_flag
in sdhci_uhs2_set_transfer_mode().
Updates in V9:
- Modify the annotations in __sdhci_uhs2_send_command().
Updates in V8:
- Adjust the position of matching brackets in
sdhci_uhs2_send_command_retry().
- Modify CameCase definition in __sdhci_uhs2_finish_command().
- Modify error message in __sdhci_uhs2_finish_command().
- sdhci_uhs2_send_command_retry() to instead of sdhci_uhs2_send_command()
in sdhci_uhs2_request().
- Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_request_atomic().
- Add forward declaration for sdhci_send_command().
Updates in V7:
- Cancel export state of some functions.
- Remove unnecessary whitespace changes.
Updates in V6:
- Add uhs2_dev_cmd() to simplify code.
- Remove unnecessary functions.
- Cancel export state of some functions.
- Drop use CONFIG_MMC_DEBUG().
- Wrap at 100 columns in some functions.
---
drivers/mmc/host/sdhci-uhs2.c | 412 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 49 ++--
drivers/mmc/host/sdhci.h | 8 +
3 files changed, 454 insertions(+), 15 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 09b86fec9f7b..1f8d527424fd 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -14,6 +14,8 @@
#include <linux/module.h>
#include <linux/iopoll.h>
#include <linux/bitfield.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -24,6 +26,8 @@
#define SDHCI_UHS2_DUMP(f, x...) \
pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+#define UHS2_ARG_IOADR_MASK 0xfff
+
void sdhci_uhs2_dump_regs(struct sdhci_host *host)
{
if (!(sdhci_uhs2_mode(host)))
@@ -58,6 +62,11 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
* *
\*****************************************************************************/
+static inline u16 uhs2_dev_cmd(struct mmc_command *cmd)
+{
+ return be16_to_cpu((__be16)cmd->uhs2_cmd->arg) & UHS2_ARG_IOADR_MASK;
+}
+
static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
struct regulator *supply,
unsigned short vdd_bit)
@@ -446,6 +455,408 @@ static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
return err;
}
+/*****************************************************************************\
+ * *
+ * 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);
+}
+
+static void sdhci_uhs2_finish_data(struct sdhci_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ __sdhci_finish_data_common(host);
+
+ __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;
+
+ if (!data) {
+ /* clear Auto CMD settings for no data CMDs */
+ if (uhs2_dev_cmd(cmd) == 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;
+ }
+
+ 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 (cmd->uhs2_cmd->tmode_half_duplex)
+ mode |= SDHCI_UHS2_TRNS_2L_HD;
+
+ sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
+
+ 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;
+
+ 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, payload (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);
+
+ 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 = FIELD_PREP(SDHCI_UHS2_CMD_PACK_LEN_MASK, cmd->uhs2_cmd->packet_len);
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
+ cmd_reg |= SDHCI_UHS2_CMD_DATA;
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd_reg |= SDHCI_UHS2_CMD_CMD12;
+
+ /* UHS2 Native ABORT */
+ if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
+ (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT))
+ cmd_reg |= SDHCI_UHS2_CMD_TRNS_ABORT;
+
+ /* UHS2 Native DORMANT */
+ if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
+ (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_GO_DORMANT_STATE))
+ cmd_reg |= SDHCI_UHS2_CMD_DORMANT;
+
+ DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
+
+ sdhci_writew(host, cmd_reg, SDHCI_UHS2_CMD);
+}
+
+static bool sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ int flags;
+ u32 mask;
+ unsigned long timeout;
+
+ WARN_ON(host->cmd);
+
+ /* Initially, a command has no error */
+ cmd->error = 0;
+
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd->flags |= MMC_RSP_BUSY;
+
+ mask = SDHCI_CMD_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)
+ 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;
+
+ 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);
+
+ __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_SD_TRAN) {
+ 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 response, 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;
+
+ __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;
+ }
+ }
+
+ /* Processed actual command. */
+ if (host->data && host->data_early)
+ sdhci_uhs2_finish_data(host);
+
+ if (!cmd->data)
+ __sdhci_finish_mrq(host, cmd->mrq);
+}
+
+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;
+
+ if (!(sdhci_uhs2_mode(host))) {
+ sdhci_request(mmc, mrq);
+ return;
+ }
+
+ mrq->stop = NULL;
+ mrq->sbc = NULL;
+ if (mrq->data)
+ mrq->data->stop = NULL;
+
+ /* Firstly check card presence */
+ present = mmc->ops->get_cd(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (sdhci_present_error(host, mrq->cmd, present))
+ goto out_finish;
+
+ cmd = 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 (!sdhci_uhs2_mode(host))
+ 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 = 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;
+
+out_finish:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -569,6 +980,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.uhs2_control = sdhci_uhs2_control;
+ host->mmc_host_ops.request = sdhci_uhs2_request;
return 0;
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 57209accbb03..9d031e83b6ba 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -147,10 +147,11 @@ 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);
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
@@ -502,14 +503,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)
{
@@ -1076,8 +1078,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);
@@ -1090,6 +1091,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)
@@ -1112,12 +1114,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;
@@ -1202,6 +1200,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);
}
@@ -1519,7 +1527,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;
@@ -1543,15 +1551,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;
@@ -1585,6 +1595,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 -
@@ -1719,8 +1737,8 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
return true;
}
-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;
@@ -1729,6 +1747,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,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 03d29423a678..9a2bd319d94c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -828,6 +828,14 @@ 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_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);
+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_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.25.1
From: Victor Shih <[email protected]>
Embed UHS-II access/control functionality into the MMC request
processing flow.
Signed-off-by: Ulf Hansson <[email protected]>
Signed-off-by: Jason Lai <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
---
Updates in V12:
- Use mmc_op_multi() to check DCMD which supports multi read/write
in mmc_uhs2_prepare_cmd().
Updates in V10:
- Move some definitions of PatchV9[02/23] to PatchV10[06/23].
- Move some definitions of PatchV9[05/23] to PatchV10[06/23].
- Drop do_multi in the mmc_blk_rw_rq_prep().
- Use tmode_half_duplex to instead of uhs2_tmode0_flag.
- Move entire control of the tmode into mmc_uhs2_prepare_cmd().
Updates in V8:
- Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
- Modify return value in sd_uhs2_attach().
Updates in V7:
- Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
- Drop uhs2_state in favor of ios->timing.
- Remove unnecessary functions.
---
drivers/mmc/core/core.c | 8 +
drivers/mmc/core/mmc_ops.c | 24 +-
drivers/mmc/core/mmc_ops.h | 1 +
drivers/mmc/core/sd.c | 13 +-
drivers/mmc/core/sd.h | 4 +
drivers/mmc/core/sd_ops.c | 9 +
drivers/mmc/core/sd_ops.h | 18 +
drivers/mmc/core/sd_uhs2.c | 1152 +++++++++++++++++++++++++++++++++++-
include/linux/mmc/core.h | 13 +
include/linux/mmc/host.h | 18 +
10 files changed, 1211 insertions(+), 49 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ba8808cd9318..f5dc653eafb0 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -334,6 +334,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;
+ __be32 payload[4]; /* for maximum size */
int err;
init_completion(&mrq->cmd_completion);
@@ -351,6 +353,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
if (err)
return err;
+ mmc_uhs2_card_prepare_cmd(host, mrq, uhs2_cmd, payload);
+
led_trigger_event(host->led, LED_FULL);
__mmc_start_request(host, mrq);
@@ -430,6 +434,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;
+ __be32 payload[4]; /* for maximum size */
int err;
/*
@@ -450,6 +456,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
if (err)
goto out_err;
+ mmc_uhs2_card_prepare_cmd(host, mrq, uhs2_cmd, payload);
+
err = host->cqe_ops->cqe_request(host, mrq);
if (err)
goto out_err;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 3b3adbddf664..5c8e62e8f331 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -144,10 +144,24 @@ int mmc_set_dsr(struct mmc_host *host)
return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
}
+int __mmc_go_idle(struct mmc_host *host)
+{
+ struct mmc_command cmd = {};
+ int err;
+
+ cmd.opcode = MMC_GO_IDLE_STATE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ mmc_delay(1);
+
+ return err;
+}
+
int mmc_go_idle(struct mmc_host *host)
{
int err;
- struct mmc_command cmd = {};
/*
* Non-SPI hosts need to prevent chipselect going active during
@@ -163,13 +177,7 @@ int mmc_go_idle(struct mmc_host *host)
mmc_delay(1);
}
- cmd.opcode = MMC_GO_IDLE_STATE;
- cmd.arg = 0;
- cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
-
- err = mmc_wait_for_cmd(host, &cmd, 0);
-
- mmc_delay(1);
+ err = __mmc_go_idle(host);
if (!mmc_host_is_spi(host)) {
mmc_set_chip_select(host, MMC_CS_DONTCARE);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 92d4194c7893..3eaefe989c80 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -25,6 +25,7 @@ struct mmc_command;
int mmc_select_card(struct mmc_card *card);
int mmc_deselect_cards(struct mmc_host *host);
int mmc_set_dsr(struct mmc_host *host);
+int __mmc_go_idle(struct mmc_host *host);
int mmc_go_idle(struct mmc_host *host);
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_set_relative_addr(struct mmc_card *card);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index c3e554344c99..8d2e739a7c5c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -207,7 +207,7 @@ static int mmc_decode_csd(struct mmc_card *card)
/*
* Given a 64-bit response, decode to our card SCR structure.
*/
-static int mmc_decode_scr(struct mmc_card *card)
+int mmc_decode_scr(struct mmc_card *card)
{
struct sd_scr *scr = &card->scr;
unsigned int scr_struct;
@@ -904,7 +904,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
return 0;
}
-static int mmc_sd_get_ro(struct mmc_host *host)
+int mmc_sd_get_ro(struct mmc_host *host)
{
int ro;
@@ -1630,11 +1630,6 @@ static void mmc_sd_detect(struct mmc_host *host)
}
}
-static int sd_can_poweroff_notify(struct mmc_card *card)
-{
- return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
-}
-
static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
{
struct sd_busy_data *data = cb_data;
@@ -1658,7 +1653,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
return 0;
}
-static int sd_poweroff_notify(struct mmc_card *card)
+int sd_poweroff_notify(struct mmc_card *card)
{
struct sd_busy_data cb_data;
u8 *reg_buf;
@@ -1706,7 +1701,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
if (mmc_card_suspended(card))
goto out;
- if (sd_can_poweroff_notify(card))
+ if (mmc_sd_can_poweroff_notify(card))
err = sd_poweroff_notify(card);
else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index 1af5a038bae9..d31259919ee5 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -11,10 +11,14 @@ struct mmc_card;
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
int mmc_sd_get_csd(struct mmc_card *card);
+int mmc_sd_get_ro(struct mmc_host *host);
void mmc_decode_cid(struct mmc_card *card);
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit);
unsigned mmc_sd_get_max_clock(struct mmc_card *card);
int mmc_sd_switch_hs(struct mmc_card *card);
+/* These call back functions were also used by UHS2 sd card */
+int sd_poweroff_notify(struct mmc_card *card);
+
#endif
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index a59cd592f06e..b0cba0cd24a5 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -27,6 +27,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_SD_TRAN) {
+ host->uhs2_app_cmd = true;
+ return 0;
+ }
+
cmd.opcode = MMC_APP_CMD;
if (card) {
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index 7667fc223b74..b8ff4713c914 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -11,6 +11,7 @@
#include <linux/types.h>
struct mmc_card;
+struct mmc_command;
struct mmc_host;
int mmc_app_set_bus_width(struct mmc_card *card, int width);
@@ -19,8 +20,25 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
int mmc_send_if_cond_pcie(struct mmc_host *host, u32 ocr);
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
int mmc_app_send_scr(struct mmc_card *card);
+int mmc_decode_scr(struct mmc_card *card);
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
+void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
+
+static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq,
+ struct uhs2_command uhs2_cmd, __be32 payload[4])
+{
+ if (host->flags & MMC_UHS2_SD_TRAN) {
+ uhs2_cmd.payload = payload;
+ mrq->cmd->uhs2_cmd = &uhs2_cmd;
+ mmc_uhs2_prepare_cmd(host, mrq);
+ }
+}
+
+static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
+{
+ return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
+}
#endif
diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
index beb2541338ff..5578a820428a 100644
--- a/drivers/mmc/core/sd_uhs2.c
+++ b/drivers/mmc/core/sd_uhs2.c
@@ -1,23 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 Linaro Ltd
- *
* Author: Ulf Hansson <[email protected]>
*
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ * Author: Yi Sun <[email protected]>
+ *
+ * Copyright (C) 2020 Genesys Logic, Inc.
+ * Authors: Ben Chuang <[email protected]>
+ *
+ * Copyright (C) 2020 Linaro Limited
+ * Author: AKASHI Takahiro <[email protected]>
+ *
+ * Copyright (C) 2022 Genesys Logic, Inc.
+ * Authors: Jason Lai <[email protected]>
+ *
+ * Copyright (C) 2023 Genesys Logic, Inc.
+ * Authors: Victor Shih <[email protected]>
+ *
* Support for SD UHS-II cards
*/
#include <linux/err.h>
+#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sd_uhs2.h>
+#include "card.h"
#include "core.h"
#include "bus.h"
#include "sd.h"
+#include "sd_ops.h"
#include "mmc_ops.h"
+#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US (1 * 1000) /* 1ms */
+#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100 /* 100ms */
+
static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
+struct sd_uhs2_wait_active_state_data {
+ struct mmc_host *host;
+ struct mmc_command *cmd;
+};
+
static int sd_uhs2_power_up(struct mmc_host *host)
{
int err;
@@ -44,10 +72,49 @@ static int sd_uhs2_power_off(struct mmc_host *host)
host->ios.clock = 0;
host->ios.timing = MMC_TIMING_LEGACY;
host->ios.power_mode = MMC_POWER_OFF;
+ if (host->flags & MMC_UHS2_SD_TRAN)
+ host->flags &= ~MMC_UHS2_SD_TRAN;
return host->ops->uhs2_control(host, UHS2_SET_IOS);
}
+/*
+ * sd_uhs2_cmd_assemble() - build up UHS-II command packet which is embedded in
+ * mmc_command structure
+ * @cmd: MMC command to executed
+ * @uhs2_cmd: UHS2 command corresponded to MMC command
+ * @header: Header field of UHS-II command cxpacket
+ * @arg: Argument field of UHS-II command packet
+ * @payload: Payload field of UHS-II command packet
+ * @plen: Payload length
+ * @resp: Response buffer is allocated by caller and it is used to keep
+ * the response of CM-TRAN command. For SD-TRAN command, uhs2_resp
+ * should be null and SD-TRAN command response should be stored in
+ * resp of mmc_command.
+ * @resp_len: Response buffer length
+ *
+ * The uhs2_command structure contains message packets which are transmited/
+ * received on UHS-II bus. This function fills in the contents of uhs2_command
+ * structure and embededs UHS2 command into mmc_command structure, which is used
+ * in legacy SD operation functions.
+ *
+ */
+static void sd_uhs2_cmd_assemble(struct mmc_command *cmd,
+ struct uhs2_command *uhs2_cmd,
+ u16 header, u16 arg, __be32 *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;
+}
+
/*
* Run the phy initialization sequence, which mainly relies on the UHS-II host
* to check that we reach the expected electrical state, between the host and
@@ -55,7 +122,15 @@ static int sd_uhs2_power_off(struct mmc_host *host)
*/
static int sd_uhs2_phy_init(struct mmc_host *host)
{
- return 0;
+ int err = 0;
+
+ err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
+ if (err) {
+ pr_err("%s: failed to initial phy for UHS-II!\n",
+ mmc_hostname(host));
+ }
+
+ return err;
}
/*
@@ -64,6 +139,82 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
*/
static int sd_uhs2_dev_init(struct mmc_host *host)
{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u32 cnt;
+ u32 dap, gap, resp_gap;
+ u16 header, arg;
+ __be32 payload[UHS2_DEV_INIT_PAYLOAD_LEN];
+ u8 gd = 0;
+ u8 resp[UHS2_DEV_ENUM_RESP_LEN] = {0};
+ int err;
+
+ dap = host->uhs2_caps.dap;
+ gap = host->uhs2_caps.gap;
+
+ /*
+ * Refer to UHS-II Addendum Version 1.02 Figure 6-21 to see DEVICE_INIT CCMD format.
+ * Head:
+ * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+ * - IOADR = CMD_BASE + 002h
+ * Payload:
+ * - bit [3:0] : GAP(Group Allocated Power)
+ * - bit [7:4] : GD(Group Descriptor)
+ * - bit [11] : Complete Flag
+ * - bit [15:12]: DAP(Device Allocated Power)
+ */
+ 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);
+
+ /*
+ * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
+ * Max. time from DEVICE_INIT CCMD EOP reception on Device
+ * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
+ * 1 second.
+ */
+ cmd.busy_timeout = 1000;
+
+ /*
+ * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
+ * When the number of the DEVICE_INIT commands is reach to
+ * 30 tiems, Host shall stop issuing DEVICE_INIT command
+ * and regard it as an error.
+ */
+ for (cnt = 0; cnt < 30; cnt++) {
+ payload[0] = ((dap & 0xF) << 12) |
+ UHS2_DEV_INIT_COMPLETE_FLAG |
+ ((gd & 0xF) << 4) |
+ (gap & 0xF);
+
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,
+ payload, UHS2_DEV_INIT_PAYLOAD_LEN,
+ resp, UHS2_DEV_INIT_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 err;
+ }
+
+ 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) {
+ host->uhs2_caps.group_desc = gd;
+ return 0;
+ }
+ resp_gap = resp[4] & 0x0F;
+ if (gap == resp_gap)
+ gd++;
+ }
+
return 0;
}
@@ -74,6 +225,52 @@ static int sd_uhs2_dev_init(struct mmc_host *host)
*/
static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id)
{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header, arg;
+ __be32 payload[UHS2_DEV_ENUM_PAYLOAD_LEN];
+ u8 id_f = 0xF, id_l = 0x0;
+ u8 resp[UHS2_DEV_ENUM_RESP_LEN] = {0};
+ int err;
+
+ /*
+ * Refer to UHS-II Addendum Version 1.02 Figure 6-28 to see ENUMERATE CCMD format.
+ * Header:
+ * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+ * - IOADR = CMD_BASE + 003h
+ * Payload:
+ * - bit [3:0]: ID_L(Last Node ID)
+ * - bit [7:4]: ID_F(First Node ID)
+ */
+ 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;
+ payload[0] = cpu_to_be32(payload[0]);
+
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_DEV_ENUM_PAYLOAD_LEN,
+ resp, UHS2_DEV_ENUM_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 err;
+ }
+
+ 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;
+ *node_id = id_f;
+
return 0;
}
@@ -84,6 +281,181 @@ static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id)
*/
static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header, arg;
+ u32 cap;
+ int err;
+
+ /*
+ * Use Control Read CCMD to read Generic Capability from Configuration Register.
+ * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+ * - IOADR = Generic Capability Register(CFG_BASE + 000h)
+ */
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | card->uhs2_config.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);
+
+ /*
+ * 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).
+ */
+ sd_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 err;
+ }
+
+ /*
+ * Generic Capability Register:
+ * bit [7:0] : Reserved
+ * bit [13:8] : Device-Specific Number of Lanes and Functionality
+ * bit 8: 2L-HD
+ * bit 9: 2D-1U FD
+ * bit 10: 1D-2U FD
+ * bit 11: 2D-2U FD
+ * Others: Reserved
+ * bit [14] : DADR Length
+ * 0: 4 bytes
+ * 1: Reserved
+ * bit [23:16]: Application Type
+ * bit 16: 0=Non-SD memory, 1=SD memory
+ * bit 17: 0=Non-SDIO, 1=SDIO
+ * bit 18: 0=Card, 1=Embedded
+ * bit [63:24]: Reserved
+ */
+ cap = cmd.resp[0];
+ card->uhs2_config.n_lanes =
+ (cap >> UHS2_DEV_CONFIG_N_LANES_POS) &
+ UHS2_DEV_CONFIG_N_LANES_MASK;
+ card->uhs2_config.dadr_len =
+ (cap >> UHS2_DEV_CONFIG_DADR_POS) &
+ UHS2_DEV_CONFIG_DADR_MASK;
+ card->uhs2_config.app_type =
+ (cap >> UHS2_DEV_CONFIG_APP_POS) &
+ UHS2_DEV_CONFIG_APP_MASK;
+
+ /*
+ * Use Control Read CCMD to read PHY Capability from Configuration Register.
+ * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+ * - IOADR = PHY Capability Register(CFG_BASE + 002h)
+ */
+ arg = ((UHS2_DEV_CONFIG_PHY_CAPS & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_READ |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_PHY_CAPS >> 8);
+
+ sd_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 err;
+ }
+
+ /*
+ * PHY Capability Register:
+ * bit [3:0] : PHY Minor Revision
+ * bit [5:4] : PHY Major Revision
+ * bit [15] : Support Hibernate Mode
+ * 0: Not support Hibernate Mode
+ * 1: Support Hibernate Mode
+ * bit [31:16]: Reserved
+ * bit [35:32]: Device-Specific N_LSS_SYN
+ * bit [39:36]: Device-Specific N_LSS_DIR
+ * bit [63:40]: Reserved
+ */
+ cap = cmd.resp[0];
+ card->uhs2_config.phy_minor_rev =
+ cap & UHS2_DEV_CONFIG_PHY_MINOR_MASK;
+ card->uhs2_config.phy_major_rev =
+ (cap >> UHS2_DEV_CONFIG_PHY_MAJOR_POS) &
+ UHS2_DEV_CONFIG_PHY_MAJOR_MASK;
+ card->uhs2_config.can_hibernate =
+ (cap >> UHS2_DEV_CONFIG_CAN_HIBER_POS) &
+ UHS2_DEV_CONFIG_CAN_HIBER_MASK;
+
+ cap = cmd.resp[1];
+ card->uhs2_config.n_lss_sync =
+ cap & UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
+ card->uhs2_config.n_lss_dir =
+ (cap >> UHS2_DEV_CONFIG_N_LSS_DIR_POS) &
+ UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+ if (card->uhs2_config.n_lss_sync == 0)
+ card->uhs2_config.n_lss_sync = 16 << 2;
+ else
+ card->uhs2_config.n_lss_sync <<= 2;
+
+ if (card->uhs2_config.n_lss_dir == 0)
+ card->uhs2_config.n_lss_dir = 16 << 3;
+ else
+ card->uhs2_config.n_lss_dir <<= 3;
+
+ /*
+ * Use Control Read CCMD to read LINK/TRAN Capability from Configuration Register.
+ * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+ * - IOADR = LINK/TRAN Capability Register(CFG_BASE + 004h)
+ */
+ 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);
+
+ sd_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 err;
+ }
+
+ /*
+ * LINK/TRAN Capability Register:
+ * bit [3:0] : LINK_TRAN Minor Revision
+ * bit [5:4] : LINK/TRAN Major Revision
+ * bit [7:6] : Reserved
+ * bit [15:8] : Device-Specific N_FCU
+ * bit [18:16]: Device Type
+ * 001b=Host
+ * 010b=Device
+ * 011b=Reserved for CMD issuable Device
+ * bit [19] : Reserved
+ * bit [31:20]: Device-Specific MAX_BLKLEN
+ * bit [39:32]: Device-Specific N_DATA_GAP
+ * bit [63:40]: Reserved
+ */
+ cap = cmd.resp[0];
+ card->uhs2_config.link_minor_rev =
+ cap & UHS2_DEV_CONFIG_LT_MINOR_MASK;
+ card->uhs2_config.link_major_rev =
+ (cap >> UHS2_DEV_CONFIG_LT_MAJOR_POS) &
+ UHS2_DEV_CONFIG_LT_MAJOR_MASK;
+ card->uhs2_config.n_fcu =
+ (cap >> UHS2_DEV_CONFIG_N_FCU_POS) &
+ UHS2_DEV_CONFIG_N_FCU_MASK;
+ card->uhs2_config.dev_type =
+ (cap >> UHS2_DEV_CONFIG_DEV_TYPE_POS) &
+ UHS2_DEV_CONFIG_DEV_TYPE_MASK;
+ card->uhs2_config.maxblk_len =
+ (cap >> UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) &
+ UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK;
+
+ cap = cmd.resp[1];
+ card->uhs2_config.n_data_gap =
+ cap & UHS2_DEV_CONFIG_N_DATA_GAP_MASK;
+ if (card->uhs2_config.n_fcu == 0)
+ card->uhs2_config.n_fcu = 256;
+
return 0;
}
@@ -98,26 +470,357 @@ static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
*/
static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header, arg;
+ __be32 payload[UHS2_CFG_WRITE_PAYLOAD_LEN];
+ u8 nMinDataGap;
+ int err;
+ u8 resp[5] = {0};
+
+ /*
+ * Use Control Write CCMD to set Generic Setting in Configuration Register.
+ * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+ * - IOADR = Generic Setting Register(CFG_BASE + 008h)
+ * - Payload = New contents to be written to Generic Setting Register
+ */
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | card->uhs2_config.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);
+
+ /*
+ * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
+ * defined in UHS-II addendem Ver1.01 are optional.
+ */
+ host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+ card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+
+ payload[0] = card->uhs2_config.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]);
+
+ /*
+ * 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).
+ */
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
+ 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 err;
+ }
+
+ /*
+ * Use Control Write CCMD to set PHY Setting in Configuration Register.
+ * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+ * - IOADR = PHY Setting Register(CFG_BASE + 00Ah)
+ * - Payload = New contents to be written to PHY Setting Register
+ */
+ arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_PHY_SET >> 8);
+
+ if (host->uhs2_caps.speed_range == UHS2_DEV_CONFIG_PHY_SET_SPEED_B) {
+ if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
+ host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
+ /* Support HD */
+ host->ios.timing = MMC_TIMING_UHS2_SPEED_B_HD;
+ nMinDataGap = 1;
+ } else {
+ /* Only support 2L-FD so far */
+ host->ios.timing = MMC_TIMING_UHS2_SPEED_B;
+ nMinDataGap = 3;
+ }
+ card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_B;
+ } else {
+ if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
+ host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
+ /* Support HD */
+ host->ios.timing = MMC_TIMING_UHS2_SPEED_A_HD;
+ nMinDataGap = 1;
+ } else {
+ /* Only support 2L-FD so far */
+ host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
+ nMinDataGap = 3;
+ }
+ card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_A;
+ }
+
+ payload[0] = card->uhs2_config.speed_range_set << UHS2_DEV_CONFIG_PHY_SET_SPEED_POS;
+
+ card->uhs2_config.n_lss_sync_set = (max(card->uhs2_config.n_lss_sync,
+ host->uhs2_caps.n_lss_sync) >> 2) &
+ UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
+ host->uhs2_caps.n_lss_sync_set = card->uhs2_config.n_lss_sync_set;
+
+ card->uhs2_config.n_lss_dir_set = (max(card->uhs2_config.n_lss_dir,
+ host->uhs2_caps.n_lss_dir) >> 3) &
+ UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+ host->uhs2_caps.n_lss_dir_set = card->uhs2_config.n_lss_dir_set;
+
+ payload[1] = (card->uhs2_config.n_lss_dir_set << UHS2_DEV_CONFIG_N_LSS_DIR_POS) |
+ card->uhs2_config.n_lss_sync_set;
+ payload[0] = cpu_to_be32(payload[0]);
+ payload[1] = cpu_to_be32(payload[1]);
+
+ memset(resp, 0, sizeof(resp));
+
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
+ resp, UHS2_CFG_WRITE_PHY_SET_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 err;
+ }
+
+ if ((resp[2] & 0x80)) {
+ pr_err("%s: %s: UHS2 CMD not accepted, resp= 0x%x!\n",
+ mmc_hostname(host), __func__, resp[2]);
+ return -EIO;
+ }
+
+ /*
+ * Use Control Write CCMD to set LINK/TRAN Setting in Configuration Register.
+ * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+ * - IOADR = LINK/TRAN Setting Register(CFG_BASE + 00Ch)
+ * - Payload = New contents to be written to LINK/TRAN Setting Register
+ */
+ 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);
+
+ if (card->uhs2_config.app_type == UHS2_DEV_CONFIG_APP_SD_MEM)
+ card->uhs2_config.maxblk_len_set = UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN;
+ else
+ card->uhs2_config.maxblk_len_set = min(card->uhs2_config.maxblk_len,
+ host->uhs2_caps.maxblk_len);
+ host->uhs2_caps.maxblk_len_set = card->uhs2_config.maxblk_len_set;
+
+ card->uhs2_config.n_fcu_set = min(card->uhs2_config.n_fcu, host->uhs2_caps.n_fcu);
+ host->uhs2_caps.n_fcu_set = card->uhs2_config.n_fcu_set;
+
+ card->uhs2_config.n_data_gap_set = max(nMinDataGap, card->uhs2_config.n_data_gap);
+ host->uhs2_caps.n_data_gap_set = card->uhs2_config.n_data_gap_set;
+
+ host->uhs2_caps.max_retry_set = 3;
+ card->uhs2_config.max_retry_set = host->uhs2_caps.max_retry_set;
+
+ payload[0] = (card->uhs2_config.maxblk_len_set << UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) |
+ (card->uhs2_config.max_retry_set << UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) |
+ (card->uhs2_config.n_fcu_set << UHS2_DEV_CONFIG_N_FCU_POS);
+ payload[1] = card->uhs2_config.n_data_gap_set;
+ payload[0] = cpu_to_be32(payload[0]);
+ payload[1] = cpu_to_be32(payload[1]);
+
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
+ 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 err;
+ }
+
+ /*
+ * Use Control Write CCMD to set Config Completion(payload bit 63) in Generic Setting
+ * Register.
+ * Header:
+ * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+ * - IOADR = PGeneric Setting Register(CFG_BASE + 008h)
+ * Payload:
+ * - bit [63]: Config Completion
+ *
+ * DLSM transits to Active state immediately when Config Completion is set to 1.
+ */
+ arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_WRITE |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+ 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]);
+
+ memset(resp, 0, sizeof(resp));
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN,
+ resp, UHS2_CFG_WRITE_GENERIC_SET_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 err;
+ }
+
+ /* Set host Config Setting registers */
+ err = host->ops->uhs2_control(host, UHS2_SET_CONFIG);
+ if (err) {
+ pr_err("%s: %s: UHS2 SET_CONFIG fail!\n", mmc_hostname(host), __func__);
+ return err;
+ }
+
return 0;
}
-/*
- * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
- * commands/requests to be backwards compatible through the legacy SD protocol.
- * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
- * be set through a legacy CMD6. Note that, the power limit that becomes set,
- * survives a soft reset through the GO_DORMANT_STATE command.
- */
-static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
+static int sd_uhs2_go_dormant(struct mmc_host *host, u32 node_id)
{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header, arg;
+ __be32 payload[1];
+ int err;
+
+ /* Disable Normal INT */
+ err = host->ops->uhs2_control(host, UHS2_DISABLE_INT);
+ if (err) {
+ pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
+ mmc_hostname(host), __func__);
+ return err;
+ }
+
+ /*
+ * Refer to UHS-II Addendum Version 1.02 Figure 6-17 to see GO_DORMANT_STATE CCMD format.
+ * Header:
+ * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+ * - IOADR = CMD_BASE + 001h
+ * Payload:
+ * - bit [7]: HBR(Entry to Hibernate Mode)
+ * 1: Host intends to enter Hibernate mode during Dormant state.
+ * The default setting is 0 because hibernate is currently not supported.
+ */
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | 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);
+
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_GO_DORMANT_PAYLOAD_LEN,
+ 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 err;
+ }
+
+ /* Check Dormant State in Present */
+ err = host->ops->uhs2_control(host, UHS2_CHECK_DORMANT);
+ if (err)
+ return err;
+
+ /* Disable UHS2 card clock */
+ err = host->ops->uhs2_control(host, UHS2_DISABLE_CLK);
+ if (err)
+ return err;
+
+ /* Restore sd clock */
+ mmc_delay(5);
+ err = host->ops->uhs2_control(host, UHS2_ENABLE_CLK);
+ if (err)
+ return err;
+
+ /* Enable Normal INT */
+ err = host->ops->uhs2_control(host, UHS2_ENABLE_INT);
+ if (err)
+ return err;
+
+ /* Detect UHS2 */
+ err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int __sd_uhs2_wait_active_state_cb(void *cb_data, bool *busy)
+{
+ struct sd_uhs2_wait_active_state_data *data = cb_data;
+ struct mmc_host *host = data->host;
+ struct mmc_command *cmd = data->cmd;
+ int err;
+
+ err = mmc_wait_for_cmd(host, cmd, 0);
+ if (err)
+ return err;
+
+ if (cmd->resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
+ *busy = false;
+ else
+ *busy = true;
+
+ return 0;
+}
+
+static int sd_uhs2_go_dormant_state(struct mmc_host *host, u32 node_id)
+{
+ struct mmc_command cmd = {0};
+ struct uhs2_command uhs2_cmd = {};
+ u16 header, arg;
+ int err;
+ struct sd_uhs2_wait_active_state_data cb_data = {
+ .host = host,
+ .cmd = &cmd
+ };
+
+ err = sd_uhs2_go_dormant(host, node_id);
+ if (err) {
+ pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ /*
+ * Use Control Read CCMD to check Config Completion(bit 63) in Generic Setting Register.
+ * - Control Read(R/W=0) with 8-Byte payload(PLEN=10b).
+ * - IOADR = Generic Setting Register(CFG_BASE + 008h)
+ *
+ * When UHS-II card been switched to new speed mode, it will set Config Completion to 1.
+ */
+ header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
+ arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+ UHS2_NATIVE_CMD_READ |
+ UHS2_NATIVE_CMD_PLEN_8B |
+ (UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+ sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+ err = __mmc_poll_for_busy(host, UHS2_WAIT_CFG_COMPLETE_PERIOD_US,
+ UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS,
+ &__sd_uhs2_wait_active_state_cb, &cb_data);
+ if (err) {
+ pr_err("%s: %s: Not switch to Active in 100 ms\n", mmc_hostname(host), __func__);
+ return err;
+ }
+
return 0;
}
+static void sd_uhs2_remove(struct mmc_host *host)
+{
+ mmc_remove_card(host->card);
+ host->card = NULL;
+}
+
/*
* Allocate the data structure for the mmc_card and run the UHS-II specific
* initialization sequence.
*/
-static int sd_uhs2_init_card(struct mmc_host *host)
+static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
{
struct mmc_card *card;
u32 node_id;
@@ -131,9 +834,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
if (err)
return err;
- card = mmc_alloc_card(host, &sd_type);
- if (IS_ERR(card))
- return PTR_ERR(card);
+ if (oldcard) {
+ card = oldcard;
+ } else {
+ card = mmc_alloc_card(host, &sd_type);
+ if (IS_ERR(card))
+ return PTR_ERR(card);
+ }
+ host->card = card;
card->uhs2_config.node_id = node_id;
card->type = MMC_TYPE_SD;
@@ -146,18 +854,226 @@ static int sd_uhs2_init_card(struct mmc_host *host)
if (err)
goto err;
- host->card = card;
+ /* If change speed to Range B, need to GO_DORMANT_STATE */
+ if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
+ host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
+ err = sd_uhs2_go_dormant_state(host, node_id);
+ if (err)
+ return err;
+ }
+
+ host->flags |= MMC_UHS2_SD_TRAN;
+
return 0;
err:
- mmc_remove_card(card);
+ sd_uhs2_remove(host);
return err;
}
-static void sd_uhs2_remove(struct mmc_host *host)
+int sd_uhs2_reinit(struct mmc_host *host)
{
- mmc_remove_card(host->card);
- host->card = NULL;
+ struct mmc_card *card = host->card;
+ int err;
+
+ sd_uhs2_power_up(host);
+ err = sd_uhs2_phy_init(host);
+ if (err)
+ return err;
+
+ err = sd_uhs2_init_card(host, card);
+ if (err)
+ return err;
+
+ mmc_card_set_present(card);
+ return err;
+}
+
+/*
+ * Mask off any voltages we don't support and select
+ * the lowest voltage
+ */
+u32 sd_uhs2_select_voltage(struct mmc_host *host, u32 ocr)
+{
+ int bit;
+ int err;
+
+ /*
+ * Sanity check the voltages that the card claims to
+ * support.
+ */
+ if (ocr & 0x7F) {
+ dev_warn(mmc_dev(host), "card claims to support voltages below defined range\n");
+ ocr &= ~0x7F;
+ }
+
+ ocr &= host->ocr_avail;
+ if (!ocr) {
+ dev_warn(mmc_dev(host), "no support for card's volts\n");
+ return 0;
+ }
+
+ if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
+ bit = ffs(ocr) - 1;
+ ocr &= 3 << bit;
+ /* Power cycle */
+ err = sd_uhs2_power_off(host);
+ if (err)
+ return 0;
+ err = sd_uhs2_reinit(host);
+ if (err)
+ return 0;
+ } else {
+ bit = fls(ocr) - 1;
+ ocr &= 3 << bit;
+ if (bit != host->ios.vdd)
+ dev_warn(mmc_dev(host), "exceeding card's volts\n");
+ }
+
+ return ocr;
+}
+
+/*
+ * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
+ * commands/requests to be backwards compatible through the legacy SD protocol.
+ * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
+ * be set through a legacy CMD6. Note that, the power limit that becomes set,
+ * survives a soft reset through the GO_DORMANT_STATE command.
+ */
+static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
+{
+ int err;
+ u32 cid[4];
+ u32 ocr;
+ u32 rocr;
+ u8 *status;
+ int ro;
+
+ /* Send CMD0 to reset SD card */
+ err = __mmc_go_idle(host);
+ if (err)
+ return err;
+
+ mmc_delay(1);
+
+ /* Send CMD8 to communicate SD interface operation condition */
+ err = mmc_send_if_cond(host, host->ocr_avail);
+ if (err) {
+ dev_warn(mmc_dev(host), "CMD8 error\n");
+ goto err;
+ }
+
+ /*
+ * Probe SD card working voltage.
+ */
+ err = mmc_send_app_op_cond(host, 0, &ocr);
+ if (err)
+ goto err;
+
+ card->ocr = ocr;
+
+ /*
+ * Some SD cards claims an out of spec VDD voltage range. Let's treat
+ * these bits as being in-valid and especially also bit7.
+ */
+ ocr &= ~0x7FFF;
+ rocr = sd_uhs2_select_voltage(host, ocr);
+ /*
+ * Some cards have zero value of rocr in UHS-II mode. Assign host's
+ * ocr value to rocr.
+ */
+ if (!rocr)
+ rocr = host->ocr_avail;
+
+ rocr |= (SD_OCR_CCS | SD_OCR_XPC);
+
+ /* Wait SD power on ready */
+ ocr = rocr;
+
+ err = mmc_send_app_op_cond(host, ocr, &rocr);
+ if (err)
+ goto err;
+
+ err = mmc_send_cid(host, cid);
+ if (err)
+ goto err;
+
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ mmc_decode_cid(card);
+
+ /*
+ * For native busses: get card RCA and quit open drain mode.
+ */
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ goto err;
+
+ err = mmc_sd_get_csd(card);
+ if (err)
+ goto err;
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ err = mmc_select_card(card);
+ if (err)
+ goto err;
+
+ /*
+ * Fetch SCR from card.
+ */
+ err = mmc_app_send_scr(card);
+ if (err)
+ goto err;
+
+ err = mmc_decode_scr(card);
+ if (err)
+ goto err;
+
+ /*
+ * Switch to high power consumption mode.
+ * Even switch failed, sd card can still work at lower power consumption mode, but
+ * performance will be lower than high power consumption mode.
+ */
+ status = kmalloc(64, GFP_KERNEL);
+ if (!status)
+ return -ENOMEM;
+
+ if (!(card->csd.cmdclass & CCC_SWITCH)) {
+ pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
+ mmc_hostname(card->host));
+ } else {
+ /* send CMD6 to set Maximum Power Consumption to get better performance */
+ err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
+ if (!err)
+ err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
+
+ err = 0;
+ }
+
+ /*
+ * Check if read-only switch is active.
+ */
+ ro = mmc_sd_get_ro(host);
+ if (ro < 0) {
+ pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
+ mmc_hostname(host));
+ } else if (ro > 0) {
+ mmc_card_set_readonly(card);
+ }
+
+ /*
+ * NOTE:
+ * Should we read Externsion Register to check power notification feature here?
+ */
+
+ kfree(status);
+
+ return 0;
+
+err:
+ sd_uhs2_remove(host);
+ return err;
}
static int sd_uhs2_alive(struct mmc_host *host)
@@ -179,38 +1095,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
mmc_claim_host(host);
mmc_detach_bus(host);
sd_uhs2_power_off(host);
+ host->flags &= ~MMC_UHS2_SUPPORT;
mmc_release_host(host);
}
}
+static int _sd_uhs2_suspend(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ int err = 0;
+
+ mmc_claim_host(host);
+
+ if (mmc_card_suspended(card))
+ goto out;
+
+ if (mmc_sd_can_poweroff_notify(card))
+ err = sd_poweroff_notify(card);
+
+ if (!err) {
+ sd_uhs2_power_off(host);
+ mmc_card_set_suspended(card);
+ }
+
+out:
+ mmc_release_host(host);
+ return err;
+}
+
+/*
+ * Callback for suspend
+ */
static int sd_uhs2_suspend(struct mmc_host *host)
{
- return 0;
+ int err;
+
+ err = _sd_uhs2_suspend(host);
+ if (!err) {
+ pm_runtime_disable(&host->card->dev);
+ pm_runtime_set_suspended(&host->card->dev);
+ }
+
+ return err;
}
+/*
+ * This function tries to determine if the same card is still present
+ * and, if so, restore all state to it.
+ */
+static int _mmc_sd_uhs2_resume(struct mmc_host *host)
+{
+ int err = 0;
+
+ mmc_claim_host(host);
+
+ if (!mmc_card_suspended(host->card))
+ goto out;
+
+ /* Power up UHS2 SD card and re-initialize it. */
+ err = sd_uhs2_reinit(host);
+ mmc_card_clr_suspended(host->card);
+
+out:
+ mmc_release_host(host);
+ return err;
+}
+
+/*
+ * Callback for resume
+ */
static int sd_uhs2_resume(struct mmc_host *host)
{
+ pm_runtime_enable(&host->card->dev);
return 0;
}
+/*
+ * Callback for runtime_suspend.
+ */
static int sd_uhs2_runtime_suspend(struct mmc_host *host)
{
- return 0;
+ int err;
+
+ if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+ return 0;
+
+ err = _sd_uhs2_suspend(host);
+ if (err)
+ pr_err("%s: error %d doing aggressive suspend\n", mmc_hostname(host), err);
+
+ return err;
}
static int sd_uhs2_runtime_resume(struct mmc_host *host)
{
- return 0;
+ int err;
+
+ err = _mmc_sd_uhs2_resume(host);
+ if (err && err != -ENOMEDIUM)
+ pr_err("%s: error %d doing runtime resume\n", mmc_hostname(host), err);
+
+ return err;
}
-static int sd_uhs2_shutdown(struct mmc_host *host)
+static int sd_uhs2_hw_reset(struct mmc_host *host)
{
- return 0;
+ int err;
+
+ sd_uhs2_power_off(host);
+ /* Wait at least 1 ms according to SD spec */
+ mmc_delay(1);
+ sd_uhs2_power_up(host);
+
+ err = sd_uhs2_reinit(host);
+
+ return err;
}
-static int sd_uhs2_hw_reset(struct mmc_host *host)
+/*
+ * mmc_uhs2_prepare_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
+ */
+void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
{
- return 0;
+ struct mmc_command *cmd;
+ struct uhs2_command *uhs2_cmd;
+ u16 header, arg;
+ __be32 *payload;
+ u8 plen;
+
+ cmd = mrq->cmd;
+ header = host->card->uhs2_config.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;
+ if (host->uhs2_app_cmd) {
+ arg |= UHS2_SD_CMD_APP;
+ host->uhs2_app_cmd = false;
+ }
+
+ /*
+ * UHS-II Addendum 7.2.1.2
+ * Host may set DM to 1 for DCMD which supports multi-block read/write regardless of
+ * data transfer length (e.g., CMD18, CMD25). Otherwise, it shall not set DM to 1.
+ * (e.g., CMD6, CMD17, CMD24). These rules are also applied to other multi-block read/write
+ * commands defined in other Part of SD specifications (for example, Host may set DM to 1
+ * for ACMD18 or ACMD25).
+ */
+ if (mmc_op_multi(cmd->opcode))
+ cmd->uhs2_cmd->tmode_half_duplex = mmc_card_uhs2_hd_mode(host);
+ else
+ cmd->uhs2_cmd->tmode_half_duplex = 0;
+
+ 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_cmd->tmode_half_duplex) {
+ if (mmc_card_uhs2_hd_mode(host))
+ 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] = cpu_to_be32(cmd->data->blksz);
+ } else {
+ payload[1] = cpu_to_be32(cmd->data->blocks);
+ }
+ } else {
+ plen = 1;
+ }
+
+ payload[0] = cpu_to_be32(cmd->arg);
+ sd_uhs2_cmd_assemble(cmd, uhs2_cmd, header, arg, payload, plen, NULL, 0);
}
static const struct mmc_bus_ops sd_uhs2_ops = {
@@ -221,7 +1293,7 @@ static const struct mmc_bus_ops sd_uhs2_ops = {
.resume = sd_uhs2_resume,
.runtime_suspend = sd_uhs2_runtime_suspend,
.runtime_resume = sd_uhs2_runtime_resume,
- .shutdown = sd_uhs2_shutdown,
+ .shutdown = sd_uhs2_suspend,
.hw_reset = sd_uhs2_hw_reset,
};
@@ -229,6 +1301,8 @@ static int sd_uhs2_attach(struct mmc_host *host)
{
int err;
+ host->flags |= MMC_UHS2_SUPPORT;
+
err = sd_uhs2_power_up(host);
if (err)
goto err;
@@ -237,7 +1311,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
if (err)
goto err;
- err = sd_uhs2_init_card(host);
+ err = sd_uhs2_init_card(host, NULL);
if (err)
goto err;
@@ -254,21 +1328,32 @@ static int sd_uhs2_attach(struct mmc_host *host)
goto remove_card;
mmc_claim_host(host);
+
return 0;
remove_card:
- mmc_remove_card(host->card);
- host->card = NULL;
+ sd_uhs2_remove(host);
mmc_claim_host(host);
- mmc_detach_bus(host);
+
err:
+ mmc_detach_bus(host);
sd_uhs2_power_off(host);
+ host->flags &= ~MMC_UHS2_SUPPORT;
return err;
}
+/**
+ * mmc_attach_sd_uhs2 - select UHS2 interface
+ * @host: MMC host
+ *
+ * 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_attach_sd_uhs2(struct mmc_host *host)
{
- int i, err = 0;
+ int i, err;
if (!(host->caps2 & MMC_CAP2_SD_UHS2))
return -EOPNOTSUPP;
@@ -283,6 +1368,9 @@ int mmc_attach_sd_uhs2(struct mmc_host *host)
*/
for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) {
host->f_init = sd_uhs2_freqs[i];
+ pr_info("%s: %s: trying to init UHS-II card at %u Hz\n",
+ mmc_hostname(host), __func__, host->f_init);
+
err = sd_uhs2_attach(host);
if (!err)
break;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 6efec0b9820c..4623651037dd 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -23,6 +23,15 @@ enum mmc_blk_status {
MMC_BLK_NEW_REQUEST,
};
+struct uhs2_command {
+ u16 header;
+ u16 arg;
+ __be32 *payload;
+ u32 payload_len;
+ u32 packet_len;
+ u8 tmode_half_duplex;
+};
+
struct mmc_command {
u32 opcode;
u32 arg;
@@ -109,6 +118,10 @@ 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 */
};
struct mmc_data {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0d1a3e9ad93c..6c8258310641 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -126,6 +126,13 @@ struct sd_uhs2_caps {
};
enum sd_uhs2_operation {
+ UHS2_PHY_INIT = 0,
+ UHS2_SET_CONFIG,
+ UHS2_ENABLE_INT,
+ UHS2_DISABLE_INT,
+ UHS2_ENABLE_CLK,
+ UHS2_DISABLE_CLK,
+ UHS2_CHECK_DORMANT,
UHS2_SET_IOS,
};
@@ -476,6 +483,11 @@ struct mmc_host {
#endif
#define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */
+ int flags;
+#define MMC_UHS2_SUPPORT (1 << 0)
+#define MMC_UHS2_SD_TRAN (1 << 1)
+
+ bool uhs2_app_cmd; /* Host UHS-II APP Command */
struct sd_uhs2_caps uhs2_caps; /* Host UHS-II capabilities */
int fixed_drv_type; /* fixed driver type for non-removable media */
@@ -728,6 +740,12 @@ static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
host->err_stats[stat] += 1;
}
+static inline int mmc_card_uhs2_hd_mode(struct mmc_host *host)
+{
+ return host->ios.timing == MMC_TIMING_UHS2_SPEED_A_HD ||
+ host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD;
+}
+
int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp);
int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
--
2.25.1
From: Victor Shih <[email protected]>
Dump UHS-II specific registers, if available, in sdhci_dumpregs()
for informative/debugging use.
Signed-off-by: Ben Chuang <[email protected]>
Signed-off-by: AKASHI Takahiro <[email protected]>
Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
---
Updates in V7:
- Use sdhci_uhs2_mode() to simplify code.
Updates in V6:
- Remove unnecessary code.
---
drivers/mmc/host/sdhci-uhs2.c | 30 ++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 4 ++++
drivers/mmc/host/sdhci.c | 3 +++
drivers/mmc/host/sdhci.h | 1 +
4 files changed, 38 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 608f8ad5aaed..e339821d3504 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 (!(sdhci_uhs2_mode(host)))
+ 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_CMD),
+ 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_INT_STATUS),
+ sdhci_readl(host, SDHCI_UHS2_INT_STATUS_ENABLE));
+ SDHCI_UHS2_DUMP("ErrSigEn: 0x%08x\n",
+ sdhci_readl(host, SDHCI_UHS2_INT_SIGNAL_ENABLE));
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
/*****************************************************************************\
* *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index e993f41ffb7f..2bfe18d29bca 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -174,4 +174,8 @@
#define SDHCI_UHS2_EMBED_CTRL_PTR 0xE6
#define SDHCI_UHS2_VENDOR_PTR 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 ff41aa56564e..753b251179f2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -110,6 +110,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);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 4f23d54a7557..43ad3f4b7672 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -720,6 +720,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.25.1
From: Ulf Hansson <[email protected]>
To allow an additional external regulator to be controlled by an mmc host
driver, let's add support for a vqmmc2 regulator to the mmc core.
For an SD UHS-II interface the vqmmc2 regulator may correspond to the so
called vdd2 supply, as described by the SD spec. Initially, only 1.8V is
needed, hence limit the new helper function, mmc_regulator_set_vqmmc2() to
this too.
Note that, to allow for flexibility mmc host drivers need to manage the
enable/disable of the vqmmc2 regulator themselves, while the regulator is
looked up through the common mmc_regulator_get_supply().
Signed-off-by: Ulf Hansson <[email protected]>
---
Updates in V10:
- Modify the commit message.
Updates in V4:
- Moved the voltage defines into this patch.
---
drivers/mmc/core/regulator.c | 34 ++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 11 +++++++++++
2 files changed, 45 insertions(+)
diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 005247a49e51..208c27cfa505 100644
--- a/drivers/mmc/core/regulator.c
+++ b/drivers/mmc/core/regulator.c
@@ -226,6 +226,33 @@ int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
}
EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
+/**
+ * mmc_regulator_set_vqmmc2 - Set vqmmc2 as per the ios->vqmmc2_voltage
+ * @mmc: The mmc host to regulate
+ * @ios: The io bus settings
+ *
+ * Sets a new voltage level for the vqmmc2 regulator, which may correspond to
+ * the vdd2 regulator for an SD UHS-II interface. This function is expected to
+ * be called by mmc host drivers.
+ *
+ * Returns a negative error code on failure, zero if the voltage level was
+ * changed successfully or a positive value if the level didn't need to change.
+ */
+int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ if (IS_ERR(mmc->supply.vqmmc2))
+ return -EINVAL;
+
+ switch (ios->vqmmc2_voltage) {
+ case MMC_VQMMC2_VOLTAGE_180:
+ return mmc_regulator_set_voltage_if_supported(
+ mmc->supply.vqmmc2, 1700000, 1800000, 1950000);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc2);
+
#else
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
@@ -252,6 +279,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.vqmmc2 = devm_regulator_get_optional(dev, "vqmmc2");
if (IS_ERR(mmc->supply.vmmc)) {
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
@@ -271,6 +299,12 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
dev_dbg(dev, "No vqmmc regulator found\n");
}
+ if (IS_ERR(mmc->supply.vqmmc2)) {
+ if (PTR_ERR(mmc->supply.vqmmc2) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_dbg(dev, "No vqmmc2 regulator found\n");
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 505b30935f48..5dbc4f23797a 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -74,6 +74,9 @@ struct mmc_ios {
#define MMC_SIGNAL_VOLTAGE_180 1
#define MMC_SIGNAL_VOLTAGE_120 2
+ unsigned char vqmmc2_voltage;
+#define MMC_VQMMC2_VOLTAGE_180 0
+
unsigned char drv_type; /* driver type (A, B, C, D) */
#define MMC_SET_DRIVER_TYPE_B 0
@@ -331,6 +334,7 @@ struct mmc_pwrseq;
struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
struct regulator *vqmmc; /* Optional Vccq supply */
+ struct regulator *vqmmc2; /* Optional supply for phy */
};
struct mmc_ctx {
@@ -612,6 +616,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
struct regulator *supply,
unsigned short vdd_bit);
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);
+int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, struct mmc_ios *ios);
#else
static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
struct regulator *supply,
@@ -625,6 +630,12 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
{
return -EINVAL;
}
+
+static inline int mmc_regulator_set_vqmmc2(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ return -EINVAL;
+}
#endif
int mmc_regulator_get_supply(struct mmc_host *mmc);
--
2.25.1
On 15/09/23 12:43, Victor Shih wrote:
> From: Victor Shih <[email protected]>
>
> This is a sdhci version of mmc's request operation.
> It covers both UHS-I and UHS-II.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
Acked-by: Adrian Hunter <[email protected]>
> ---
>
> Updates in V11:
> - Drop the check mmc_card_uhs2_hd_mode(host->mmc)
> in sdhci_uhs2_set_transfer_mode().
>
> Updates in V10:
> - Use tmode_half_duplex to instead of uhs2_tmode0_flag
> in sdhci_uhs2_set_transfer_mode().
>
> Updates in V9:
> - Modify the annotations in __sdhci_uhs2_send_command().
>
> Updates in V8:
> - Adjust the position of matching brackets in
> sdhci_uhs2_send_command_retry().
> - Modify CameCase definition in __sdhci_uhs2_finish_command().
> - Modify error message in __sdhci_uhs2_finish_command().
> - sdhci_uhs2_send_command_retry() to instead of sdhci_uhs2_send_command()
> in sdhci_uhs2_request().
> - Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_request_atomic().
> - Add forward declaration for sdhci_send_command().
>
> Updates in V7:
> - Cancel export state of some functions.
> - Remove unnecessary whitespace changes.
>
> Updates in V6:
> - Add uhs2_dev_cmd() to simplify code.
> - Remove unnecessary functions.
> - Cancel export state of some functions.
> - Drop use CONFIG_MMC_DEBUG().
> - Wrap at 100 columns in some functions.
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 412 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 49 ++--
> drivers/mmc/host/sdhci.h | 8 +
> 3 files changed, 454 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 09b86fec9f7b..1f8d527424fd 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -14,6 +14,8 @@
> #include <linux/module.h>
> #include <linux/iopoll.h>
> #include <linux/bitfield.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/host.h>
>
> #include "sdhci.h"
> #include "sdhci-uhs2.h"
> @@ -24,6 +26,8 @@
> #define SDHCI_UHS2_DUMP(f, x...) \
> pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
>
> +#define UHS2_ARG_IOADR_MASK 0xfff
> +
> void sdhci_uhs2_dump_regs(struct sdhci_host *host)
> {
> if (!(sdhci_uhs2_mode(host)))
> @@ -58,6 +62,11 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
> * *
> \*****************************************************************************/
>
> +static inline u16 uhs2_dev_cmd(struct mmc_command *cmd)
> +{
> + return be16_to_cpu((__be16)cmd->uhs2_cmd->arg) & UHS2_ARG_IOADR_MASK;
> +}
> +
> static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
> struct regulator *supply,
> unsigned short vdd_bit)
> @@ -446,6 +455,408 @@ static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
> return err;
> }
>
> +/*****************************************************************************\
> + * *
> + * 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);
> +}
> +
> +static void sdhci_uhs2_finish_data(struct sdhci_host *host)
> +{
> + struct mmc_data *data = host->data;
> +
> + __sdhci_finish_data_common(host);
> +
> + __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;
> +
> + if (!data) {
> + /* clear Auto CMD settings for no data CMDs */
> + if (uhs2_dev_cmd(cmd) == 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;
> + }
> +
> + 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 (cmd->uhs2_cmd->tmode_half_duplex)
> + mode |= SDHCI_UHS2_TRNS_2L_HD;
> +
> + sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
> +
> + 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;
> +
> + 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, payload (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);
> +
> + 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 = FIELD_PREP(SDHCI_UHS2_CMD_PACK_LEN_MASK, cmd->uhs2_cmd->packet_len);
> + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
> + cmd_reg |= SDHCI_UHS2_CMD_DATA;
> + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> + cmd_reg |= SDHCI_UHS2_CMD_CMD12;
> +
> + /* UHS2 Native ABORT */
> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> + (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT))
> + cmd_reg |= SDHCI_UHS2_CMD_TRNS_ABORT;
> +
> + /* UHS2 Native DORMANT */
> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> + (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_GO_DORMANT_STATE))
> + cmd_reg |= SDHCI_UHS2_CMD_DORMANT;
> +
> + DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
> +
> + sdhci_writew(host, cmd_reg, SDHCI_UHS2_CMD);
> +}
> +
> +static bool sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> +{
> + int flags;
> + u32 mask;
> + unsigned long timeout;
> +
> + WARN_ON(host->cmd);
> +
> + /* Initially, a command has no error */
> + cmd->error = 0;
> +
> + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> + cmd->flags |= MMC_RSP_BUSY;
> +
> + mask = SDHCI_CMD_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)
> + 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;
> +
> + 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);
> +
> + __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_SD_TRAN) {
> + 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 response, 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;
> +
> + __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;
> + }
> + }
> +
> + /* Processed actual command. */
> + if (host->data && host->data_early)
> + sdhci_uhs2_finish_data(host);
> +
> + if (!cmd->data)
> + __sdhci_finish_mrq(host, cmd->mrq);
> +}
> +
> +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;
> +
> + if (!(sdhci_uhs2_mode(host))) {
> + sdhci_request(mmc, mrq);
> + return;
> + }
> +
> + mrq->stop = NULL;
> + mrq->sbc = NULL;
> + if (mrq->data)
> + mrq->data->stop = NULL;
> +
> + /* Firstly check card presence */
> + present = mmc->ops->get_cd(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (sdhci_present_error(host, mrq->cmd, present))
> + goto out_finish;
> +
> + cmd = 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 (!sdhci_uhs2_mode(host))
> + 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 = 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;
> +
> +out_finish:
> + spin_unlock_irqrestore(&host->lock, flags);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -569,6 +980,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.uhs2_control = sdhci_uhs2_control;
> + host->mmc_host_ops.request = sdhci_uhs2_request;
>
> return 0;
> }
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 57209accbb03..9d031e83b6ba 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -147,10 +147,11 @@ 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);
>
> static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
> {
> @@ -502,14 +503,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)
> {
> @@ -1076,8 +1078,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);
>
> @@ -1090,6 +1091,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)
> @@ -1112,12 +1114,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;
> @@ -1202,6 +1200,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);
> }
> @@ -1519,7 +1527,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;
> @@ -1543,15 +1551,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;
> @@ -1585,6 +1595,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 -
> @@ -1719,8 +1737,8 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> return true;
> }
>
> -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;
> @@ -1729,6 +1747,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,
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 03d29423a678..9a2bd319d94c 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -828,6 +828,14 @@ 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_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);
> +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_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);
Hi, Ulf
Excuse me, may I know the patch's status?
If you already have a closer look at a paragraph in the series,
could you let me know your comments first, let me check it first.
I look forward to your reply.
Thanks, Victor Shih
On Fri, Sep 15, 2023 at 5:43 PM Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> 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-#6: for core
> patch#7-#22: for sdhci
> patch#23: for GL9755
>
> Changes in v12 (September. 15, 2023)
> * rebased to the linux-kernel-v6.6.0-rc1 in Ulf Hansson next branch.
> * according to the comments provided by Adrian Hunter to modify the
> patches base on the [V11 00/23] Add support UHS-II for GL9755.
> * according to the comments provided by Ulf Hansson to modify the
> patches base on the [V11 00/23] Add support UHS-II for GL9755.
> * Patch#5: Remove unused max_current_180_vdd2.
> * Patch#6: Use mmc_op_multi() to check DCMD which supports multi
> read/write in mmc_uhs2_prepare_cmd().
>
> Reference
> =========
> [1] https://gitlab.com/VictorShih/linux-uhs2.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v11 -----------------
> 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-#6: for core
> patch#7-#22: for sdhci
> patch#23: for GL9755
>
> Changes in v11 (September. 08, 2023)
> * rebased to the linux-kernel-v6.5.0-rc5 in Ulf Hansson next branch.
> * according to the comments provided by Adrian Hunter to modify the
> patches base on the [V10 00/23] Add support UHS-II for GL9755.
> * Patch#18: Drop the check mmc_card_uhs2_hd_mode(host->mmc)
> in sdhci_uhs2_set_transfer_mode().
> * Patch#20: Remove unused ocr_avail_uhs2.
>
> Reference
> =========
> [1] https://gitlab.com/VictorShih/linux-uhs2.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v10 -----------------
> 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-#6: for core
> patch#7-#22: for sdhci
> patch#23: for GL9755
>
> Changes in v10 (August. 18, 2023)
> * rebased to the linux-kernel-v6.5.0-rc5 in Ulf Hansson next branch.
> * according to the comments provided by Ulf Hansson to modify the
> patches base on the [V9 00/23] Add support UHS-II for GL9755.
> * Patch#2: Drop unnecessary definitions and code.
> * Patch#3: Modify the commit message.
> * Patch#4: Modify the commit message.
> * Patch#5: Drop unnecessary definitions.
> * Patch#6: Move some definitions of PatchV9[02/23] to PatchV10[06/23].
> Move some definitions of PatchV9[05/23] to PatchV10[06/23].
> Drop do_multi in the mmc_blk_rw_rq_prep().
> Use tmode_half_duplex to instead of uhs2_tmode0_flag.
> Move entire control of the tmode into mmc_uhs2_prepare_cmd().
> * Patch#11: Move some definitions of PatchV9[05/23] to PatchV10[11/23].
> * Patch#18: Use tmode_half_duplex to instead of uhs2_tmode0_flag
> in sdhci_uhs2_set_transfer_mode().
> * Patch#20: Move some definitions of PatchV9[05/23] to PatchV10[20/23].
>
> Reference
> =========
> [1] https://gitlab.com/VictorShih/linux-uhs2.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v9 -----------------
> 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-#6: for core
> patch#7-#22: for sdhci
> patch#23: for GL9755
>
> Changes in v9 (July. 21, 2023)
> * rebased to the linux-kernel-v6.5.0-rc1 in Ulf Hansson next branch.
> * according to the comments provided by Adrian Hunter to modify the
> patches base on the [V8 00/23] Add support UHS-II for GL9755.
> * Patch#2: move sd_uhs2_operation definition of PatchV8[05/23]
> to PatchV9[02/23] for avoid compilation errors.
> move uhs2_control definition of PatchV8[05/23]
> to PatchV9[02/23] for avoid compilation errors.
> move mmc_host flags definition of PatchV8[05/23]
> to PatchV9[02/23] for avoid compilation errors.
> move mmc_host flags MMC_UHS2_SUPPORT definition of
> PatchV8[05/23] to PatchV9[02/23] for avoid compilation errors.
> move mmc_host flags MMC_UHS2_SD_TRAN definition of
> PatchV8[05/23] to PatchV9[02/23] for avoid compilation errors.
> * Patch#7: Modify the commit message.
> * Patch#8: Modify the commit message.
> * Patch#11: Modify annotations in sdhci_get_vdd_value().
> * Patch#14: Simplity the turning_on_clk in sdhci_set_ios().
> * Patch#18: Modify the annotations in __sdhci_uhs2_send_command().
> * Patch#19: Cancel export state of sdhci_set_mrq_done() function.
> * Patch#23: Rename gl9755_pre_detect_init() to sdhci_gli_pre_detect_init().
> Rename gl9755_uhs2_reset_sd_tran() to
> sdhci_gli_uhs2_reset_sd_tran().
>
> Reference
> =========
> [1] https://gitlab.com/VictorShih/linux-uhs2.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v8 -----------------
> 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-#6: for core
> patch#7-#22: for sdhci
> patch#23: for GL9755
>
> Changes in v8 (June. 21, 2023)
> * rebased to the linux-kernel-v6.4.0-rc6 in Ulf Hansson next branch.
> * fix most of checkpatch warnings/errors.
> * according to the comments provided by Adrian Hunter to modify the
> patches base on the [V7 00/23] Add support UHS-II for GL9755.
> * Patch#6: Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
> Modify return value in sd_uhs2_attach().
> * Patch#7: Use tabs instead of spaces.
> * Patch#8: Modify MODULE_LICENSE from "GPL v2" to "GPL".
> * Patch#10: Adjust the position of matching brackets.
> * Patch#11: Adjust the position of matching brackets.
> Add the initial value of the pwr in sdhci_uhs2_set_power().
> * Patch#13: Initialization be combined with declaration and realigned
> in sdhci_calc_timeout_uhs2().
> Forward declare struct mmc_command in sdhci_uhs2.h.
> * Patch#14: Add the judgment formula for MMC_TIMING_SPEED_A_HD,
> MMC_TIMING_SPEED_B and MMC_TIMING_SPEED_B_HD in
> __sdhci_uhs2_set_ios().
> Add the switch case for MMC_TIMING_SPEED_A_HD,
> MMC_TIMING_SPEED_B and MMC_TIMING_SPEED_B_HD in
> sdhci_get_preset_value().
> mmc_opt_regulator_set_ocr() to instead of
> mmc_regulator_set_ocr() in sdhci_uhs2_set_ios().
> * Patch#15: usleep_range() to instead of udelay() in
> sdhci_uhs2_interface_detect().
> read_poll_timeout() to instead of read_poll_timeout_atomic()
> in sdhci_uhs2_interface_detect().
> Modify return value in sdhci_uhs2_do_detect_init().
> * Patch#16: Remove unnecessary include file.
> read_poll_timeout() to instead of read_poll_timeout_atomic()
> in sdhci_uhs2_enable_clk().
> Put the comment on the end and put the lines in descending
> line length in sdhci_uhs2_enable_clk().
> Modify return value in sdhci_uhs2_enable_clk().
> * Patch#17: Reorder the definitions and lose the parentheses in
> sdhci_uhs2_set_config().
> read_poll_timeout() to instead of read_poll_timeout_atomic()
> in sdhci_uhs2_check_dormant().
> * Patch#18: Adjust the position of matching brackets in
> sdhci_uhs2_send_command_retry().
> Modify CameCase definition in __sdhci_uhs2_finish_command().
> Modify error message in __sdhci_uhs2_finish_command().
> sdhci_uhs2_send_command_retry() to instead of
> sdhci_uhs2_send_command() in sdhci_uhs2_request().
> Use sdhci_uhs2_mode() to simplify code in
> sdhci_uhs2_request_atomic().
> Add forward declaration for sdhci_send_command().
> * Patch#19: Forward declare struct mmc_request in sdhci_uhs2.h.
> Remove forward declaration of sdhci_send_command().
> Use mmc_dev() to simplify code in sdhci_request_done_dma().
> * Patch#20: Change return type to void for __sdhci_uhs2_add_host_v4().
> Remove unused variables in __sdhci_uhs2_add_host_v4().
> * Patch#22: Add config select MMC_SDHCI_UHS2 in Kconfig.
> * Patch#23: Use sdhci_get_vdd_value() to simplify code in
> gl9755_set_power().
> Use read_poll_timeout_atomic() to simplify code in
> sdhci_wait_clock_stable().
> Use read_poll_timeout_atomic() to simplify code in
> sdhci_gl9755_reset().
>
> Reference
> =========
> [1] https://gitlab.com/VictorShih/linux-uhs2.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v7 -----------------
> 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-#6: for core
> patch#7-#22: for sdhci
> patch#23: for GL9755
>
> Changes in v7 (Mar. 31, 2023)
> * rebased to the linux-kernel-v6.3.0-rc3 in Ulf Hansson next branch.
> * according to the guidance and overall architecture provided
> by Ulf Hansson, Ben Chuang and Jason Lai to implement the
> UHS-2 Core function based on the patches of the [V4,0/6]
> Preparations to support SD UHS-II cards[5].
> * according to the guidance and comments provided by
> Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
> the UHS-2 Host function based on the patches of the
> [RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
> * implement the necessary function to let the UHS-2 Core/Host
> work properly.
> * fix most of checkpatch warnings/errors.
> * according to the guidance and comments provided by
> Adrian Hunter, Ben Chuang to implement the UHS-2
> Host function based on the patches of the
> [V5,00/26] Add support UHS-II for GL9755[6].
> * according to the guidance and comments provided by
> Ulf Hanssion, Adrian Hunter, Ben Chuang to implement the UHS-2
> Host function based on the patches of the
> [V6,00/24] Add support UHS-II for GL9755[7].
> * The uhs2_post_attach_sd() function is no longer needed so drop
> the V6 version of the Patch#22.
> * Modifies the usage of the flags used by the sdhci host for
> MMC_UHS2_INITIALIZED.
> * Patch#1: Drop unnecessary bracket.
> * Patch#2: Drop sd_uhs2_set_ios function.
> Used ->uhs2_control() callback for uhs2_set_ios
> in sd_uhs2_power_up().
> Used ->uhs2_control() callback for uhs2_set_ios
> in sd_uhs2_power_off().
> Drop MMC_TIMING_SD_UHS2 in favor of MMC_TIMING_UHS2_SPEED_A.
> Modify sd_uhs2_legacy_init to avoid the
> sd_uhs2_reinit cycle issue.
> * Patch#5: Drop unnecessary definitions.
> * Patch#6: Drop unnecessary function.
> Drop uhs2_state in favor of ios->timing.
> * Patch#7: Reorder values and positions of definitions.
> * Patch#9: Used sdhci_uhs2_mode function to simplify.
> * Patch#11: Drop pwr variable in sdhci_uhs2_set_power function.
> * Patch#14: Modify some descriptions.
> Drop unnecessary function.
> * Patch#15: Drop using uhs2_reset ops and use sdhci_uhs2_reset function
> in the sdhci_do_detect_init function.
> * Patch#17: Drop unnecessary function.
> * Patch#18: Drop unnecessary whitespace changes.
> Cancel the export state of some functions.
> * Patch#19: Drop unnecessary function.
> Used sdhci_uhs2_mode function to simplify.
> Modify some descriptions.
> Cancel the export state of some functions.
> * Patch#20: Drop using __sdhci_uhs2_host function and use
> __sdhci_add_host function in sdhci_uhs2_add_host function.
> Cancel the export state of some functions.
> * Patch#23: Drop using uhs2_post_attach_sd function.
>
> 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
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> [5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> [6] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> [7] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v6 -----------------
> 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-#6: for core
> patch#7-#23: for sdhci
> patch#24: for GL9755
>
> Changes in v6 (Dec. 12, 2022)
> * rebased to the linux-kernel-v6.1.0-rc8 in Ulf Hansson next branch.
> * according to the guidance and overall architecture provided
> by Ulf Hansson, Ben Chuang and Jason Lai to implement the
> UHS-2 Core function based on the patches of the [V4,0/6]
> Preparations to support SD UHS-II cards[5].
> * according to the guidance and comments provided by
> Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
> the UHS-2 Host function based on the patches of the
> [RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
> * implement the necessary function to let the UHS-2 Core/Host
> work properly.
> * fix most of checkpatch warnings/errors.
> * according to the guidance and comments provided by
> Adrian Hunter, Ben Chuang to implement the UHS-2
> Host function based on the patches of the
> [V5,00/26] Add support UHS-II for GL9755[6].
> * The uhs2_post_attach_sd() has implemented in Patch#6 and
> Patch#17 so drop the V5 version of the Patch#23.
> * Modifies the usage of the flags used by the sdhci host for
> MMC_UHS2_INITIALIZED.
> * Patch#5: Drop unused definitions and functions.
> * Patch#7: Rename definitions.
> Use BIT() GENMASK() in some cases.
> * Patch#8: Merge V5 version of Patch[7] and Patch[9] into
> V6 version of Patch[8].
> * Patch#9: Drop unnecessary function.
> Rename used definitions.
> * Patch#10: Drop unnecessary function and simplify some code.
> * Patch#11: Drop unnecessary function.
> Add new mmc_opt_regulator_set_ocr function.
> * Patch#13: Drop unnecessary function.
> Use GENMASK() and FIELD_PREP() in some cases.
> * Patch#14: Drop unnecessary function.
> Modify return value in some function.
> Use GENMASK() and FIELD_PREP() in some cases.
> * Patch#15: Drop unnecessary function.
> Rename used definitions.
> Use GENMASK() and FIELD_GET() in some cases.
> Wrap at 100 columns in some functions.
> * Patch#16: Drop unnecessary function.
> * Patch#17: Drop unnecessary function.
> Drop the unnecessary parameter when call the DBG()
> function.
> Rename used definitions.
> Cancel the export state of some functions.
> Use GENMASK() and FIELD_PREP() in some cases.
> * Patch#18: Drop unnecessary function.
> Add uhs2_dev_cmd function to simplify some functions.
> Rename used definitions.
> Cancel the export state of some functions.
> Use GENMASK() and FIELD_PREP() in some cases.
> * Patch#19: Drop unnecessary function.
> Add sdhci_uhs2_mode() in some functions.
> Rename used definitions.
> Cancel the export state of some functions.
> * Patch#20: Add new complete_work_fn/thread_irq_fn variables in
> struct sdhci_host.
> Use complete_work_fn/thread_irq_fn variables in
> sdhci_alloc_host()/sdhci_uhs2_add_host().
> Rename used definitions.
> * Patch[24]: Rename used definitions.
>
> 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
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> [5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> [6] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v5 -----------------
> 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-#6: for core
> patch#7-#25: for sdhci
> patch#26: for GL9755
>
> Changes in v5 (Oct. 19, 2022)
> * rebased to the linux-kernel-v6.1-rc1 in Ulf Hansson next branch.
> * according to the guidance and overall architecture provided
> by Ulf Hansson, Ben Chuang and Jason Lai to implement the
> UHS-2 Core function based on the patches of the [V4,0/6]
> Preparations to support SD UHS-II cards[5].
> * according to the guidance and comments provided by
> Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
> the UHS-2 Host function based on the patches of the
> [RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
> * implement the necessary function to let the UHS-2 Core/Host
> work properly.
> * fix most of checkpatch warnings/errors
>
> 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
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> [5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
>
> ----------------- original cover letter from v3.1 -----------------
> 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
>
> Ben Chuang (1):
> mmc: sdhci-uhs2: add pre-detect_init hook
>
> Ulf Hansson (4):
> mmc: core: Cleanup printing of speed mode at card insertion
> mmc: core: Prepare to support SD UHS-II cards
> mmc: core: Announce successful insertion of an SD UHS-II card
> mmc: core: Extend support for mmc regulators with a vqmmc2
>
> Victor Shih (18):
> mmc: core: Add definitions for SD UHS-II cards
> mmc: core: Support UHS-II card control and access
> mmc: sdhci: add UHS-II related definitions in headers
> mmc: sdhci: add UHS-II module and add a kernel configuration
> mmc: sdhci-uhs2: dump UHS-II registers
> mmc: sdhci-uhs2: add reset function and uhs2_mode 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 uhs2_control() 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: sdhci-pci: add UHS-II support framework
> mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
>
> drivers/mmc/core/Makefile | 2 +-
> drivers/mmc/core/bus.c | 38 +-
> drivers/mmc/core/core.c | 25 +-
> drivers/mmc/core/core.h | 1 +
> drivers/mmc/core/host.h | 7 +
> drivers/mmc/core/mmc_ops.c | 24 +-
> drivers/mmc/core/mmc_ops.h | 1 +
> drivers/mmc/core/regulator.c | 34 +
> drivers/mmc/core/sd.c | 13 +-
> drivers/mmc/core/sd.h | 4 +
> drivers/mmc/core/sd_ops.c | 9 +
> drivers/mmc/core/sd_ops.h | 18 +
> drivers/mmc/core/sd_uhs2.c | 1380 +++++++++++++++++++++++++++++
> 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 | 233 ++++-
> drivers/mmc/host/sdhci-pci.h | 3 +
> drivers/mmc/host/sdhci-uhs2.c | 1319 +++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 192 ++++
> drivers/mmc/host/sdhci.c | 274 +++---
> drivers/mmc/host/sdhci.h | 74 +-
> include/linux/mmc/card.h | 36 +
> include/linux/mmc/core.h | 13 +
> include/linux/mmc/host.h | 76 ++
> include/linux/mmc/sd_uhs2.h | 240 +++++
> 26 files changed, 3894 insertions(+), 149 deletions(-)
> create mode 100644 drivers/mmc/core/sd_uhs2.c
> create mode 100644 drivers/mmc/host/sdhci-uhs2.c
> create mode 100644 drivers/mmc/host/sdhci-uhs2.h
> create mode 100644 include/linux/mmc/sd_uhs2.h
>
> --
> 2.25.1
>
On Fri, 29 Sept 2023 at 20:56, Victor Shih <[email protected]> wrote:
>
> Hi, Ulf
>
> Excuse me, may I know the patch's status?
>
> If you already have a closer look at a paragraph in the series,
> could you let me know your comments first, let me check it first.
>
> I look forward to your reply.
FYI: I will re-start doing the review tomorrow and continue during this week.
Kind regards
Uffe
>
> Thanks, Victor Shih
>
> On Fri, Sep 15, 2023 at 5:43 PM Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > 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-#6: for core
> > patch#7-#22: for sdhci
> > patch#23: for GL9755
> >
> > Changes in v12 (September. 15, 2023)
> > * rebased to the linux-kernel-v6.6.0-rc1 in Ulf Hansson next branch.
> > * according to the comments provided by Adrian Hunter to modify the
> > patches base on the [V11 00/23] Add support UHS-II for GL9755.
> > * according to the comments provided by Ulf Hansson to modify the
> > patches base on the [V11 00/23] Add support UHS-II for GL9755.
> > * Patch#5: Remove unused max_current_180_vdd2.
> > * Patch#6: Use mmc_op_multi() to check DCMD which supports multi
> > read/write in mmc_uhs2_prepare_cmd().
> >
> > Reference
> > =========
> > [1] https://gitlab.com/VictorShih/linux-uhs2.git
> > [2] SD Host Controller Simplified Specification 4.20
> > [3] UHS-II Simplified Addendum 1.02
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v11 -----------------
> > 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-#6: for core
> > patch#7-#22: for sdhci
> > patch#23: for GL9755
> >
> > Changes in v11 (September. 08, 2023)
> > * rebased to the linux-kernel-v6.5.0-rc5 in Ulf Hansson next branch.
> > * according to the comments provided by Adrian Hunter to modify the
> > patches base on the [V10 00/23] Add support UHS-II for GL9755.
> > * Patch#18: Drop the check mmc_card_uhs2_hd_mode(host->mmc)
> > in sdhci_uhs2_set_transfer_mode().
> > * Patch#20: Remove unused ocr_avail_uhs2.
> >
> > Reference
> > =========
> > [1] https://gitlab.com/VictorShih/linux-uhs2.git
> > [2] SD Host Controller Simplified Specification 4.20
> > [3] UHS-II Simplified Addendum 1.02
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v10 -----------------
> > 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-#6: for core
> > patch#7-#22: for sdhci
> > patch#23: for GL9755
> >
> > Changes in v10 (August. 18, 2023)
> > * rebased to the linux-kernel-v6.5.0-rc5 in Ulf Hansson next branch.
> > * according to the comments provided by Ulf Hansson to modify the
> > patches base on the [V9 00/23] Add support UHS-II for GL9755.
> > * Patch#2: Drop unnecessary definitions and code.
> > * Patch#3: Modify the commit message.
> > * Patch#4: Modify the commit message.
> > * Patch#5: Drop unnecessary definitions.
> > * Patch#6: Move some definitions of PatchV9[02/23] to PatchV10[06/23].
> > Move some definitions of PatchV9[05/23] to PatchV10[06/23].
> > Drop do_multi in the mmc_blk_rw_rq_prep().
> > Use tmode_half_duplex to instead of uhs2_tmode0_flag.
> > Move entire control of the tmode into mmc_uhs2_prepare_cmd().
> > * Patch#11: Move some definitions of PatchV9[05/23] to PatchV10[11/23].
> > * Patch#18: Use tmode_half_duplex to instead of uhs2_tmode0_flag
> > in sdhci_uhs2_set_transfer_mode().
> > * Patch#20: Move some definitions of PatchV9[05/23] to PatchV10[20/23].
> >
> > Reference
> > =========
> > [1] https://gitlab.com/VictorShih/linux-uhs2.git
> > [2] SD Host Controller Simplified Specification 4.20
> > [3] UHS-II Simplified Addendum 1.02
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v9 -----------------
> > 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-#6: for core
> > patch#7-#22: for sdhci
> > patch#23: for GL9755
> >
> > Changes in v9 (July. 21, 2023)
> > * rebased to the linux-kernel-v6.5.0-rc1 in Ulf Hansson next branch.
> > * according to the comments provided by Adrian Hunter to modify the
> > patches base on the [V8 00/23] Add support UHS-II for GL9755.
> > * Patch#2: move sd_uhs2_operation definition of PatchV8[05/23]
> > to PatchV9[02/23] for avoid compilation errors.
> > move uhs2_control definition of PatchV8[05/23]
> > to PatchV9[02/23] for avoid compilation errors.
> > move mmc_host flags definition of PatchV8[05/23]
> > to PatchV9[02/23] for avoid compilation errors.
> > move mmc_host flags MMC_UHS2_SUPPORT definition of
> > PatchV8[05/23] to PatchV9[02/23] for avoid compilation errors.
> > move mmc_host flags MMC_UHS2_SD_TRAN definition of
> > PatchV8[05/23] to PatchV9[02/23] for avoid compilation errors.
> > * Patch#7: Modify the commit message.
> > * Patch#8: Modify the commit message.
> > * Patch#11: Modify annotations in sdhci_get_vdd_value().
> > * Patch#14: Simplity the turning_on_clk in sdhci_set_ios().
> > * Patch#18: Modify the annotations in __sdhci_uhs2_send_command().
> > * Patch#19: Cancel export state of sdhci_set_mrq_done() function.
> > * Patch#23: Rename gl9755_pre_detect_init() to sdhci_gli_pre_detect_init().
> > Rename gl9755_uhs2_reset_sd_tran() to
> > sdhci_gli_uhs2_reset_sd_tran().
> >
> > Reference
> > =========
> > [1] https://gitlab.com/VictorShih/linux-uhs2.git
> > [2] SD Host Controller Simplified Specification 4.20
> > [3] UHS-II Simplified Addendum 1.02
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v8 -----------------
> > 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-#6: for core
> > patch#7-#22: for sdhci
> > patch#23: for GL9755
> >
> > Changes in v8 (June. 21, 2023)
> > * rebased to the linux-kernel-v6.4.0-rc6 in Ulf Hansson next branch.
> > * fix most of checkpatch warnings/errors.
> > * according to the comments provided by Adrian Hunter to modify the
> > patches base on the [V7 00/23] Add support UHS-II for GL9755.
> > * Patch#6: Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
> > Modify return value in sd_uhs2_attach().
> > * Patch#7: Use tabs instead of spaces.
> > * Patch#8: Modify MODULE_LICENSE from "GPL v2" to "GPL".
> > * Patch#10: Adjust the position of matching brackets.
> > * Patch#11: Adjust the position of matching brackets.
> > Add the initial value of the pwr in sdhci_uhs2_set_power().
> > * Patch#13: Initialization be combined with declaration and realigned
> > in sdhci_calc_timeout_uhs2().
> > Forward declare struct mmc_command in sdhci_uhs2.h.
> > * Patch#14: Add the judgment formula for MMC_TIMING_SPEED_A_HD,
> > MMC_TIMING_SPEED_B and MMC_TIMING_SPEED_B_HD in
> > __sdhci_uhs2_set_ios().
> > Add the switch case for MMC_TIMING_SPEED_A_HD,
> > MMC_TIMING_SPEED_B and MMC_TIMING_SPEED_B_HD in
> > sdhci_get_preset_value().
> > mmc_opt_regulator_set_ocr() to instead of
> > mmc_regulator_set_ocr() in sdhci_uhs2_set_ios().
> > * Patch#15: usleep_range() to instead of udelay() in
> > sdhci_uhs2_interface_detect().
> > read_poll_timeout() to instead of read_poll_timeout_atomic()
> > in sdhci_uhs2_interface_detect().
> > Modify return value in sdhci_uhs2_do_detect_init().
> > * Patch#16: Remove unnecessary include file.
> > read_poll_timeout() to instead of read_poll_timeout_atomic()
> > in sdhci_uhs2_enable_clk().
> > Put the comment on the end and put the lines in descending
> > line length in sdhci_uhs2_enable_clk().
> > Modify return value in sdhci_uhs2_enable_clk().
> > * Patch#17: Reorder the definitions and lose the parentheses in
> > sdhci_uhs2_set_config().
> > read_poll_timeout() to instead of read_poll_timeout_atomic()
> > in sdhci_uhs2_check_dormant().
> > * Patch#18: Adjust the position of matching brackets in
> > sdhci_uhs2_send_command_retry().
> > Modify CameCase definition in __sdhci_uhs2_finish_command().
> > Modify error message in __sdhci_uhs2_finish_command().
> > sdhci_uhs2_send_command_retry() to instead of
> > sdhci_uhs2_send_command() in sdhci_uhs2_request().
> > Use sdhci_uhs2_mode() to simplify code in
> > sdhci_uhs2_request_atomic().
> > Add forward declaration for sdhci_send_command().
> > * Patch#19: Forward declare struct mmc_request in sdhci_uhs2.h.
> > Remove forward declaration of sdhci_send_command().
> > Use mmc_dev() to simplify code in sdhci_request_done_dma().
> > * Patch#20: Change return type to void for __sdhci_uhs2_add_host_v4().
> > Remove unused variables in __sdhci_uhs2_add_host_v4().
> > * Patch#22: Add config select MMC_SDHCI_UHS2 in Kconfig.
> > * Patch#23: Use sdhci_get_vdd_value() to simplify code in
> > gl9755_set_power().
> > Use read_poll_timeout_atomic() to simplify code in
> > sdhci_wait_clock_stable().
> > Use read_poll_timeout_atomic() to simplify code in
> > sdhci_gl9755_reset().
> >
> > Reference
> > =========
> > [1] https://gitlab.com/VictorShih/linux-uhs2.git
> > [2] SD Host Controller Simplified Specification 4.20
> > [3] UHS-II Simplified Addendum 1.02
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v7 -----------------
> > 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-#6: for core
> > patch#7-#22: for sdhci
> > patch#23: for GL9755
> >
> > Changes in v7 (Mar. 31, 2023)
> > * rebased to the linux-kernel-v6.3.0-rc3 in Ulf Hansson next branch.
> > * according to the guidance and overall architecture provided
> > by Ulf Hansson, Ben Chuang and Jason Lai to implement the
> > UHS-2 Core function based on the patches of the [V4,0/6]
> > Preparations to support SD UHS-II cards[5].
> > * according to the guidance and comments provided by
> > Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
> > the UHS-2 Host function based on the patches of the
> > [RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
> > * implement the necessary function to let the UHS-2 Core/Host
> > work properly.
> > * fix most of checkpatch warnings/errors.
> > * according to the guidance and comments provided by
> > Adrian Hunter, Ben Chuang to implement the UHS-2
> > Host function based on the patches of the
> > [V5,00/26] Add support UHS-II for GL9755[6].
> > * according to the guidance and comments provided by
> > Ulf Hanssion, Adrian Hunter, Ben Chuang to implement the UHS-2
> > Host function based on the patches of the
> > [V6,00/24] Add support UHS-II for GL9755[7].
> > * The uhs2_post_attach_sd() function is no longer needed so drop
> > the V6 version of the Patch#22.
> > * Modifies the usage of the flags used by the sdhci host for
> > MMC_UHS2_INITIALIZED.
> > * Patch#1: Drop unnecessary bracket.
> > * Patch#2: Drop sd_uhs2_set_ios function.
> > Used ->uhs2_control() callback for uhs2_set_ios
> > in sd_uhs2_power_up().
> > Used ->uhs2_control() callback for uhs2_set_ios
> > in sd_uhs2_power_off().
> > Drop MMC_TIMING_SD_UHS2 in favor of MMC_TIMING_UHS2_SPEED_A.
> > Modify sd_uhs2_legacy_init to avoid the
> > sd_uhs2_reinit cycle issue.
> > * Patch#5: Drop unnecessary definitions.
> > * Patch#6: Drop unnecessary function.
> > Drop uhs2_state in favor of ios->timing.
> > * Patch#7: Reorder values and positions of definitions.
> > * Patch#9: Used sdhci_uhs2_mode function to simplify.
> > * Patch#11: Drop pwr variable in sdhci_uhs2_set_power function.
> > * Patch#14: Modify some descriptions.
> > Drop unnecessary function.
> > * Patch#15: Drop using uhs2_reset ops and use sdhci_uhs2_reset function
> > in the sdhci_do_detect_init function.
> > * Patch#17: Drop unnecessary function.
> > * Patch#18: Drop unnecessary whitespace changes.
> > Cancel the export state of some functions.
> > * Patch#19: Drop unnecessary function.
> > Used sdhci_uhs2_mode function to simplify.
> > Modify some descriptions.
> > Cancel the export state of some functions.
> > * Patch#20: Drop using __sdhci_uhs2_host function and use
> > __sdhci_add_host function in sdhci_uhs2_add_host function.
> > Cancel the export state of some functions.
> > * Patch#23: Drop using uhs2_post_attach_sd function.
> >
> > 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
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> > [5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> > [6] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> > [7] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v6 -----------------
> > 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-#6: for core
> > patch#7-#23: for sdhci
> > patch#24: for GL9755
> >
> > Changes in v6 (Dec. 12, 2022)
> > * rebased to the linux-kernel-v6.1.0-rc8 in Ulf Hansson next branch.
> > * according to the guidance and overall architecture provided
> > by Ulf Hansson, Ben Chuang and Jason Lai to implement the
> > UHS-2 Core function based on the patches of the [V4,0/6]
> > Preparations to support SD UHS-II cards[5].
> > * according to the guidance and comments provided by
> > Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
> > the UHS-2 Host function based on the patches of the
> > [RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
> > * implement the necessary function to let the UHS-2 Core/Host
> > work properly.
> > * fix most of checkpatch warnings/errors.
> > * according to the guidance and comments provided by
> > Adrian Hunter, Ben Chuang to implement the UHS-2
> > Host function based on the patches of the
> > [V5,00/26] Add support UHS-II for GL9755[6].
> > * The uhs2_post_attach_sd() has implemented in Patch#6 and
> > Patch#17 so drop the V5 version of the Patch#23.
> > * Modifies the usage of the flags used by the sdhci host for
> > MMC_UHS2_INITIALIZED.
> > * Patch#5: Drop unused definitions and functions.
> > * Patch#7: Rename definitions.
> > Use BIT() GENMASK() in some cases.
> > * Patch#8: Merge V5 version of Patch[7] and Patch[9] into
> > V6 version of Patch[8].
> > * Patch#9: Drop unnecessary function.
> > Rename used definitions.
> > * Patch#10: Drop unnecessary function and simplify some code.
> > * Patch#11: Drop unnecessary function.
> > Add new mmc_opt_regulator_set_ocr function.
> > * Patch#13: Drop unnecessary function.
> > Use GENMASK() and FIELD_PREP() in some cases.
> > * Patch#14: Drop unnecessary function.
> > Modify return value in some function.
> > Use GENMASK() and FIELD_PREP() in some cases.
> > * Patch#15: Drop unnecessary function.
> > Rename used definitions.
> > Use GENMASK() and FIELD_GET() in some cases.
> > Wrap at 100 columns in some functions.
> > * Patch#16: Drop unnecessary function.
> > * Patch#17: Drop unnecessary function.
> > Drop the unnecessary parameter when call the DBG()
> > function.
> > Rename used definitions.
> > Cancel the export state of some functions.
> > Use GENMASK() and FIELD_PREP() in some cases.
> > * Patch#18: Drop unnecessary function.
> > Add uhs2_dev_cmd function to simplify some functions.
> > Rename used definitions.
> > Cancel the export state of some functions.
> > Use GENMASK() and FIELD_PREP() in some cases.
> > * Patch#19: Drop unnecessary function.
> > Add sdhci_uhs2_mode() in some functions.
> > Rename used definitions.
> > Cancel the export state of some functions.
> > * Patch#20: Add new complete_work_fn/thread_irq_fn variables in
> > struct sdhci_host.
> > Use complete_work_fn/thread_irq_fn variables in
> > sdhci_alloc_host()/sdhci_uhs2_add_host().
> > Rename used definitions.
> > * Patch[24]: Rename used definitions.
> >
> > 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
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> > [5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> > [6] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v5 -----------------
> > 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-#6: for core
> > patch#7-#25: for sdhci
> > patch#26: for GL9755
> >
> > Changes in v5 (Oct. 19, 2022)
> > * rebased to the linux-kernel-v6.1-rc1 in Ulf Hansson next branch.
> > * according to the guidance and overall architecture provided
> > by Ulf Hansson, Ben Chuang and Jason Lai to implement the
> > UHS-2 Core function based on the patches of the [V4,0/6]
> > Preparations to support SD UHS-II cards[5].
> > * according to the guidance and comments provided by
> > Adrian Hunter, Ben Chuang and AKASHI Takahiro to implement
> > the UHS-2 Host function based on the patches of the
> > [RFC,v3.1,00/27] Add support UHS-II for GL9755[4].
> > * implement the necessary function to let the UHS-2 Core/Host
> > work properly.
> > * fix most of checkpatch warnings/errors
> >
> > 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
> > [4] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> > [5] https://patchwork.kernel.org/project/linux-mmc/cover/[email protected]/
> >
> > ----------------- original cover letter from v3.1 -----------------
> > 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
> >
> > Ben Chuang (1):
> > mmc: sdhci-uhs2: add pre-detect_init hook
> >
> > Ulf Hansson (4):
> > mmc: core: Cleanup printing of speed mode at card insertion
> > mmc: core: Prepare to support SD UHS-II cards
> > mmc: core: Announce successful insertion of an SD UHS-II card
> > mmc: core: Extend support for mmc regulators with a vqmmc2
> >
> > Victor Shih (18):
> > mmc: core: Add definitions for SD UHS-II cards
> > mmc: core: Support UHS-II card control and access
> > mmc: sdhci: add UHS-II related definitions in headers
> > mmc: sdhci: add UHS-II module and add a kernel configuration
> > mmc: sdhci-uhs2: dump UHS-II registers
> > mmc: sdhci-uhs2: add reset function and uhs2_mode 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 uhs2_control() 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: sdhci-pci: add UHS-II support framework
> > mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
> >
> > drivers/mmc/core/Makefile | 2 +-
> > drivers/mmc/core/bus.c | 38 +-
> > drivers/mmc/core/core.c | 25 +-
> > drivers/mmc/core/core.h | 1 +
> > drivers/mmc/core/host.h | 7 +
> > drivers/mmc/core/mmc_ops.c | 24 +-
> > drivers/mmc/core/mmc_ops.h | 1 +
> > drivers/mmc/core/regulator.c | 34 +
> > drivers/mmc/core/sd.c | 13 +-
> > drivers/mmc/core/sd.h | 4 +
> > drivers/mmc/core/sd_ops.c | 9 +
> > drivers/mmc/core/sd_ops.h | 18 +
> > drivers/mmc/core/sd_uhs2.c | 1380 +++++++++++++++++++++++++++++
> > 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 | 233 ++++-
> > drivers/mmc/host/sdhci-pci.h | 3 +
> > drivers/mmc/host/sdhci-uhs2.c | 1319 +++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 192 ++++
> > drivers/mmc/host/sdhci.c | 274 +++---
> > drivers/mmc/host/sdhci.h | 74 +-
> > include/linux/mmc/card.h | 36 +
> > include/linux/mmc/core.h | 13 +
> > include/linux/mmc/host.h | 76 ++
> > include/linux/mmc/sd_uhs2.h | 240 +++++
> > 26 files changed, 3894 insertions(+), 149 deletions(-)
> > create mode 100644 drivers/mmc/core/sd_uhs2.c
> > create mode 100644 drivers/mmc/host/sdhci-uhs2.c
> > create mode 100644 drivers/mmc/host/sdhci-uhs2.h
> > create mode 100644 include/linux/mmc/sd_uhs2.h
> >
> > --
> > 2.25.1
> >
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> This is a UHS-II version of sdhci's set_power operation.
> VDD2, as well as VDD, is handled here.
Okay, but why?
Please justify the change in the commit messages, don't just tell what
is being done. This applied to the whole series - and I believe I have
said this before too.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> Acked-by: Adrian Hunter <[email protected]>
> ---
>
> Updates in V10:
> - Move some definitions of PatchV9[05/23] to PatchV10[11/23].
>
> Updates in V9:
> - Modify annotations in sdhci_get_vdd_value().
>
> Updates in V8:
> - Adjust the position of matching brackets.
> - Add the initial value of the pwr in sdhci_uhs2_set_power().
>
> Updates in V7:
> - Add clear the power reg before setting a new value
> in sdhci_uhs2_set_power().
> - Add MMC_VDD_34_35 case and MMC_VDD_35_36 case in sdhci_get_vdd_value().
> - Drop pwr variable in sdhci_get_vdd_value().
>
> Updates in V6:
> - Add mmc_opt_regulator_set_ocr().
> - Remove unnecessary functions.
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 48 +++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 61 +++++++++++++++++++----------------
> drivers/mmc/host/sdhci.h | 1 +
> include/linux/mmc/host.h | 1 +
> 4 files changed, 83 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index dfc80a7f1bad..fc37a34629c2 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -57,6 +57,13 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
> * *
> \*****************************************************************************/
>
> +static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
> + struct regulator *supply,
> + unsigned short vdd_bit)
> +{
> + return IS_ERR_OR_NULL(supply) ? 0 : mmc_regulator_set_ocr(mmc, supply, vdd_bit);
> +}
> +
> bool sdhci_uhs2_mode(struct sdhci_host *host)
> {
> return host->mmc->flags & MMC_UHS2_SUPPORT;
> @@ -94,6 +101,47 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
> }
> EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
>
> +static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
> +{
> + struct mmc_host *mmc = host->mmc;
> + u8 pwr = 0;
> +
> + 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);
> +
> + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
We added mmc_regulator_set_vqmmc2() in patch4. Please use that instead.
> + } else {
> + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
> + /* support 1.8v only for now */
> + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc2, fls(MMC_VDD_165_195) - 1);
> +
> + /* Clear the power reg before setting a new value */
> + sdhci_writeb(host, 0, 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);
> + }
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 753b251179f2..eca54a16e7fc 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -23,7 +23,7 @@
> #include <linux/regulator/consumer.h>
> #include <linux/pm_runtime.h>
> #include <linux/of.h>
> -
> +#include <linux/bug.h>
> #include <linux/leds.h>
>
> #include <linux/mmc/mmc.h>
> @@ -2061,41 +2061,46 @@ 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)
> +{
> + 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:
> + return SDHCI_POWER_180;
> + case MMC_VDD_29_30:
> + case MMC_VDD_30_31:
> + return SDHCI_POWER_300;
> + case MMC_VDD_32_33:
> + case MMC_VDD_33_34:
> + /*
> + * 3.4V ~ 3.6V are valid only for those platforms where it's
> + * known that the voltage range is supported by hardware.
> + */
> + case MMC_VDD_34_35:
> + case MMC_VDD_35_36:
> + return SDHCI_POWER_330;
> + default:
> + return 0;
> + }
> +}
> +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:
> - /*
> - * 3.4 ~ 3.6V are valid only for those platforms where it's
> - * known that the voltage range is supported by hardware.
> - */
> - case MMC_VDD_34_35:
> - case MMC_VDD_35_36:
> - 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;
> }
> }
>
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 43ad3f4b7672..f3bd558b337f 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -837,6 +837,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);
> int sdhci_get_cd_nogpio(struct mmc_host *mmc);
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 6c8258310641..610644a0ace5 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -363,6 +363,7 @@ struct mmc_pwrseq;
>
> struct mmc_supply {
> struct regulator *vmmc; /* Card power supply */
> + struct regulator *vmmc2; /* UHS2 VDD2 power supply */
In patch4 we added vqmmc2. Please use that instead.
> struct regulator *vqmmc; /* Optional Vccq supply */
> struct regulator *vqmmc2; /* Optional supply for phy */
> };
> --
> 2.25.1
>
Kind regards
Uffe
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
> so no voltage switch required.
Can you please elaborate on this? I don't get anything of the above, sorry.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> Acked-by: Adrian Hunter <[email protected]>
> ---
>
> Updates in V5:
> - Use sdhci_uhs2_mode() to simplify code in
> sdhci_uhs2_start_signal_voltage_switch().
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index fc37a34629c2..92fb69b7e209 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -142,6 +142,27 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> }
> }
>
> +/*****************************************************************************\
> + * *
> + * 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 (sdhci_uhs2_mode(host))
> + return 0;
This is just wrong. If we are initializing a uhs2 card, we certainly
should call ->start_signal_voltage_switch() callback at all. This is
for UHS-I cards, right?
> +
> + return sdhci_start_signal_voltage_switch(mmc, ios);
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
> @@ -150,6 +171,9 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
>
> 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;
> }
>
Kind regards
Uffe
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> This is a UHS-II version of sdhci's set_timeout() operation.
This needs more explanations and justifications.
Other than that, the patch looks good to me!
Kind regards
Uffe
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> Acked-by: Adrian Hunter <[email protected]>
> ---
>
> Updates in V8:
> - Initialization be combined with declaration and realigned
> in sdhci_calc_timeout_uhs2().
> - Forward declare struct mmc_command in sdhci_uhs2.h.
>
> Updates in V6:
> - Use GENMASK() and FIELD_PREP() in some case.
> - Use sdhci_uhs2_mode() to simplify code.
> - Remove unnecessary functions.
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 72 +++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-uhs2.h | 2 +
> 2 files changed, 74 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 92fb69b7e209..d519e6ce6199 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/module.h>
> #include <linux/iopoll.h>
> +#include <linux/bitfield.h>
>
> #include "sdhci.h"
> #include "sdhci-uhs2.h"
> @@ -142,6 +143,77 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> }
> }
>
> +static u8 sdhci_calc_timeout_uhs2(struct sdhci_host *host, u8 *cmd_res, u8 *dead_lock)
> +{
> + /* timeout in us */
> + unsigned int dead_lock_timeout = 1 * 1000 * 1000;
> + unsigned int cmd_res_timeout = 5 * 1000;
> + unsigned int current_timeout;
> + u8 count;
> +
> + /*
> + * 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 |= FIELD_PREP(SDHCI_UHS2_TIMER_CTRL_DEADLOCK_MASK, dead_lock);
> + 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 (sdhci_uhs2_mode(host))
> + __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 8253d50f7852..ccf4e1834c2d 100644
> --- a/drivers/mmc/host/sdhci-uhs2.h
> +++ b/drivers/mmc/host/sdhci-uhs2.h
> @@ -175,9 +175,11 @@
> #define SDHCI_UHS2_VENDOR_PTR 0xE8
>
> struct sdhci_host;
> +struct mmc_command;
>
> void sdhci_uhs2_dump_regs(struct sdhci_host *host);
> bool sdhci_uhs2_mode(struct sdhci_host *host);
> void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> +void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
>
> #endif /* __SDHCI_UHS2_H */
> --
> 2.25.1
>
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> 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.
$subject patch is adding a bunch of static functions, which isn't
really being used until later. If you compile this patch it will
trigger warnings about unused function, we don't want that. Each patch
in the series should build nicely without warning and errors.
To deal with these problems, I suggest that you move the introduction
of the sdhci_uhs2_control() from patch17 to $subject patch - or
possibly make that as a standalone patch, preceeding $subject patch.
Step by step you can then add support for each of the "enum
sd_uhs2_operation" to sdhci_uhs2_control().
Moreover, please work at the commit message a bit, it's not entirely
easy to understand by reading what goes on here.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> Acked-by: Adrian Hunter <[email protected]>
> ---
>
> Updates in V8:
> - usleep_range() to instead of udelay() in sdhci_uhs2_interface_detect().
> - read_poll_timeout() to instead of read_poll_timeout_atomic()
> in sdhci_uhs2_interface_detect().
> - Modify return value in sdhci_uhs2_do_detect_init().
>
> Updates in V7:
> - Drop using uhs2_reset ops and use sdhci_uhs2_reset()
> in sdhci_uhs2_do_detect_init().
>
> Updates in V6:
> - Remove unnecessary functions.
> - Wrap at 100 columns in some functions.
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 112 ++++++++++++++++++++++++++++++++++
> 1 file changed, 112 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index ad791c48f681..4c2a56629ab3 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -335,6 +335,118 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> * *
> \*****************************************************************************/
>
> +static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
> +{
> + int timeout = 100000; /* 100ms */
Please use define instead.
> + u32 val;
> +
> + usleep_range(50, 200); /* wait for 50us - 200us before check */
Why? Comment?
And use defines.
> +
> + if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT),
> + 100, timeout, true, host, SDHCI_PRESENT_STATE)) {
> + pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return -EIO;
> + }
> +
> + /* Enable UHS2 error interrupts */
> + sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK, SDHCI_UHS2_INT_ERROR_MASK);
> +
> + /* 150ms */
> + timeout = 150000;
Ditto.
> + if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC),
> + 100, timeout, true, host, SDHCI_PRESENT_STATE)) {
> + pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return -EIO;
> + }
> +
> + 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;
> +
> + caps_ptr = sdhci_readw(host, SDHCI_UHS2_CAPS_PTR);
> + if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
> + pr_err("%s: SDHCI_UHS2_CAPS_PTR(%d) is wrong.\n",
> + mmc_hostname(mmc), caps_ptr);
> + return -ENODEV;
> + }
> + caps_gen = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_OFFSET);
> + caps_phy = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_PHY_OFFSET);
> + caps_tran[0] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_OFFSET);
> + caps_tran[1] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_1_OFFSET);
> +
> + /* General Caps */
> + mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_CAPS_DAP_MASK;
> + mmc->uhs2_caps.gap = FIELD_GET(SDHCI_UHS2_CAPS_GAP_MASK, caps_gen);
> + mmc->uhs2_caps.n_lanes = FIELD_GET(SDHCI_UHS2_CAPS_LANE_MASK, caps_gen);
> + mmc->uhs2_caps.addr64 = (caps_gen & SDHCI_UHS2_CAPS_ADDR_64) ? 1 : 0;
> + mmc->uhs2_caps.card_type = FIELD_GET(SDHCI_UHS2_CAPS_DEV_TYPE_MASK, caps_gen);
> +
> + /* PHY Caps */
> + mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_CAPS_PHY_REV_MASK;
> + mmc->uhs2_caps.speed_range = FIELD_GET(SDHCI_UHS2_CAPS_PHY_RANGE_MASK, caps_phy);
> + mmc->uhs2_caps.n_lss_sync = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_SYN_MASK, caps_phy);
> + mmc->uhs2_caps.n_lss_dir = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_DIR_MASK, caps_phy);
> + 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_CAPS_TRAN_LINK_REV_MASK;
> + mmc->uhs2_caps.n_fcu = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_N_FCU_MASK, caps_tran[0]);
> + if (mmc->uhs2_caps.n_fcu == 0)
> + mmc->uhs2_caps.n_fcu = 256;
> + mmc->uhs2_caps.host_type = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_HOST_TYPE_MASK, caps_tran[0]);
> + mmc->uhs2_caps.maxblk_len = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_BLK_LEN_MASK, caps_tran[0]);
> + mmc->uhs2_caps.n_data_gap = caps_tran[1] & SDHCI_UHS2_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);
> +
> + DBG("Begin do uhs2 detect init.\n");
> +
> + if (sdhci_uhs2_interface_detect(host)) {
> + pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
Does this really deserve a warning to be printed to the log?
> + return -EIO;
> + }
> +
> + if (sdhci_uhs2_init(host)) {
> + pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
> + return -EIO;
> + }
> +
> + /* 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_INT_ERROR_MASK);
> + /*
> + * N.B 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);
> +
> + return 0;
> +}
> +
> static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> {
> host->mmc_host_ops.start_signal_voltage_switch =
Kind regards
Uffe
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> Acked-by: Adrian Hunter <[email protected]>
The similar comments as posted for patch 15 applies to $subject patch
too. Please have a look at those and fix these for the $subject patch
too.
> ---
>
> Updates in V8:
> - Remove unnecessary include file.
> - read_poll_timeout() to instead of read_poll_timeout_atomic()
> in sdhci_uhs2_enable_clk().
> - Put the comment on the end and put the lines in descending
> line length in sdhci_uhs2_enable_clk().
> - Modify return value in sdhci_uhs2_enable_clk().
>
> Updates in V6:
> - Remove unnecessary functions.
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 4c2a56629ab3..af1b0c5e48fd 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -329,6 +329,36 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> return 0;
> }
>
> +static int 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);
> +
> + return 0;
> +}
> +
> +static int sdhci_uhs2_enable_clk(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + int timeout_us = 20000; /* 20ms */
> + u32 val;
> +
> + clk |= SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> + if (read_poll_timeout(sdhci_readw, val, (val & SDHCI_CLOCK_INT_STABLE),
> + 10, timeout_us, true, host, SDHCI_CLOCK_CONTROL)) {
> + pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc));
> + sdhci_dumpregs(host);
> + return -EIO;
> + }
> + return 0;
> +}
> +
> /*****************************************************************************\
> * *
> * Driver init/exit *
Kind regards
Uffe
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> This is a sdhci version of mmc's request operation.
> It covers both UHS-I and UHS-II.
Okay, but again, please elaborate on why we need/want this.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> ---
>
> Updates in V11:
> - Drop the check mmc_card_uhs2_hd_mode(host->mmc)
> in sdhci_uhs2_set_transfer_mode().
>
> Updates in V10:
> - Use tmode_half_duplex to instead of uhs2_tmode0_flag
> in sdhci_uhs2_set_transfer_mode().
>
> Updates in V9:
> - Modify the annotations in __sdhci_uhs2_send_command().
>
> Updates in V8:
> - Adjust the position of matching brackets in
> sdhci_uhs2_send_command_retry().
> - Modify CameCase definition in __sdhci_uhs2_finish_command().
> - Modify error message in __sdhci_uhs2_finish_command().
> - sdhci_uhs2_send_command_retry() to instead of sdhci_uhs2_send_command()
> in sdhci_uhs2_request().
> - Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_request_atomic().
> - Add forward declaration for sdhci_send_command().
>
> Updates in V7:
> - Cancel export state of some functions.
> - Remove unnecessary whitespace changes.
>
> Updates in V6:
> - Add uhs2_dev_cmd() to simplify code.
> - Remove unnecessary functions.
> - Cancel export state of some functions.
> - Drop use CONFIG_MMC_DEBUG().
> - Wrap at 100 columns in some functions.
>
> ---
>
> drivers/mmc/host/sdhci-uhs2.c | 412 ++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 49 ++--
> drivers/mmc/host/sdhci.h | 8 +
> 3 files changed, 454 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> index 09b86fec9f7b..1f8d527424fd 100644
> --- a/drivers/mmc/host/sdhci-uhs2.c
> +++ b/drivers/mmc/host/sdhci-uhs2.c
[...]
> +
> +static void __sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> +{
> + int i, j;
> + int cmd_reg;
> +
> + 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, payload (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);
> +
> + 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));
We are ignoring what we just read. Isn't there something we need to verify?
Moreover, the whole thing with i,j and the +4 thing above looks a bit
odd to me. I am not sure whether the above can be simplified, but I
leave that for you to have a second look at.
> +
> + cmd_reg = FIELD_PREP(SDHCI_UHS2_CMD_PACK_LEN_MASK, cmd->uhs2_cmd->packet_len);
> + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
> + cmd_reg |= SDHCI_UHS2_CMD_DATA;
> + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> + cmd_reg |= SDHCI_UHS2_CMD_CMD12;
> +
> + /* UHS2 Native ABORT */
> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> + (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT))
> + cmd_reg |= SDHCI_UHS2_CMD_TRNS_ABORT;
> +
> + /* UHS2 Native DORMANT */
> + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> + (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_GO_DORMANT_STATE))
> + cmd_reg |= SDHCI_UHS2_CMD_DORMANT;
> +
> + DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
> +
> + sdhci_writew(host, cmd_reg, SDHCI_UHS2_CMD);
> +}
[...]
> +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;
Why do we need a retry mechanism at this level? The mmc core sometimes
retries commands when it seems reasonable, why isn't that sufficient?
> +
> + 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 the retry is needed, would it be possible to convert into using
read_poll_timeout() for the above while loop instead? If so, please
make that conversion.
> +
> + 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;
Nitpick: Maybe find some better variable names. Like error_code...
> + int i;
> +
> + if (host->mmc->flags & MMC_UHS2_SD_TRAN) {
> + 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 response, 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) {
Again we do sdhci_readl above but just ignore the data. I assume
that's deliberate, as we are probably just interested in the remaining
pieces.
Moreover, the whole thing with +4 things continues to look a bit odd
to me. I am not sure whether it can be simplified, but I leave that
for you to have a second look at.
> + 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);
> + }
> + }
> +}
[...]
> +
> +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + struct mmc_command *cmd;
> + unsigned long flags;
> + bool present;
> +
> + if (!(sdhci_uhs2_mode(host))) {
> + sdhci_request(mmc, mrq);
> + return;
> + }
> +
> + mrq->stop = NULL;
> + mrq->sbc = NULL;
> + if (mrq->data)
> + mrq->data->stop = NULL;
> +
> + /* Firstly check card presence */
> + present = mmc->ops->get_cd(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (sdhci_present_error(host, mrq->cmd, present))
> + goto out_finish;
> +
> + cmd = 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);
Drop this, it's not used outside this module.
> +
> +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
This function is entirely unused. Did you actually test this with an
hsq enabled host? Or perhaps you have just added this for
completeness?
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + struct mmc_command *cmd;
> + unsigned long flags;
> + int ret = 0;
> +
> + if (!sdhci_uhs2_mode(host))
> + 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 = 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;
> +
> +out_finish:
> + spin_unlock_irqrestore(&host->lock, flags);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
> +
[...]
Kind regards
Uffe
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> This is a UHS-II version of sdhci's add_host/remove_host operation.
> Any sdhci drivers which are capable of handling UHS-II cards must
> call those functions instead of the corresponding sdhci's.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Signed-off-by: AKASHI Takahiro <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> Acked-by: Adrian Hunter <[email protected]>
[...]
Just a few nitpicks, see below.
> +static void __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
> +{
> + struct mmc_host *mmc;
> + u32 max_current_caps2;
> +
> + mmc = host->mmc;
> +
> + /* Support UHS2 */
> + if (caps1 & SDHCI_SUPPORT_UHS2)
> + mmc->caps2 |= MMC_CAP2_SD_UHS2;
> +
> + max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
> +
> + if ((caps1 & SDHCI_CAN_VDD2_180) &&
> + !max_current_caps2 &&
> + !IS_ERR(mmc->supply.vmmc2)) {
> + /* UHS2 - VDD2 */
> + int curr = regulator_get_current_limit(mmc->supply.vmmc2);
As I also stated in another reply, please use vqmmc2 instead, which we
added in patch4.
> +
> + 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_CAN_VDD2_180))
> + mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
> +}
> +
> +static int sdhci_uhs2_host_ops_init(struct sdhci_host *host);
Please try to re-order the code so this declaration isn't needed.
> +
> +static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
> +{
> + if (!sdhci_uhs2_mode(host))
> + return;
> +
> + if (!dead)
> + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
> +}
> +
[...]
Kind regards
Uffe
On Tue, Oct 3, 2023 at 5:58 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
> > so no voltage switch required.
>
> Can you please elaborate on this? I don't get anything of the above, sorry.
>
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > Acked-by: Adrian Hunter <[email protected]>
> > ---
> >
> > Updates in V5:
> > - Use sdhci_uhs2_mode() to simplify code in
> > sdhci_uhs2_start_signal_voltage_switch().
> >
> > ---
> >
> > drivers/mmc/host/sdhci-uhs2.c | 24 ++++++++++++++++++++++++
> > 1 file changed, 24 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index fc37a34629c2..92fb69b7e209 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -142,6 +142,27 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> > }
> > }
> >
> > +/*****************************************************************************\
> > + * *
> > + * 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 (sdhci_uhs2_mode(host))
> > + return 0;
>
> This is just wrong. If we are initializing a uhs2 card, we certainly
> should call ->start_signal_voltage_switch() callback at all. This is
> for UHS-I cards, right?
>
Hi, Ulf
UHS-II does not need single_voltage.
I will modify the commit message in the next version.
sdhci_uhs2_start_signal_voltage_switch() is under
mmc_host_ops.start_signal_voltage_switch host ops,
therefore, we need to keep the UHS-I path here.
Thanks, Victor Shih
> > +
> > + return sdhci_start_signal_voltage_switch(mmc, ios);
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > @@ -150,6 +171,9 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> >
> > 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;
> > }
> >
>
> Kind regards
> Uffe
On 6/10/23 13:30, Victor Shih wrote:
> On Tue, Oct 3, 2023 at 5:58 PM Ulf Hansson <[email protected]> wrote:
>>
>> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>>>
>>> From: Victor Shih <[email protected]>
>>>
>>> For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
>>> so no voltage switch required.
>>
>> Can you please elaborate on this? I don't get anything of the above, sorry.
>>
>>>
>>> Signed-off-by: Ben Chuang <[email protected]>
>>> Signed-off-by: AKASHI Takahiro <[email protected]>
>>> Signed-off-by: Victor Shih <[email protected]>
>>> Acked-by: Adrian Hunter <[email protected]>
>>> ---
>>>
>>> Updates in V5:
>>> - Use sdhci_uhs2_mode() to simplify code in
>>> sdhci_uhs2_start_signal_voltage_switch().
>>>
>>> ---
>>>
>>> drivers/mmc/host/sdhci-uhs2.c | 24 ++++++++++++++++++++++++
>>> 1 file changed, 24 insertions(+)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
>>> index fc37a34629c2..92fb69b7e209 100644
>>> --- a/drivers/mmc/host/sdhci-uhs2.c
>>> +++ b/drivers/mmc/host/sdhci-uhs2.c
>>> @@ -142,6 +142,27 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
>>> }
>>> }
>>>
>>> +/*****************************************************************************\
>>> + * *
>>> + * 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 (sdhci_uhs2_mode(host))
>>> + return 0;
>>
>> This is just wrong. If we are initializing a uhs2 card, we certainly
>> should call ->start_signal_voltage_switch() callback at all. This is
>> for UHS-I cards, right?
>>
>
> Hi, Ulf
>
> UHS-II does not need single_voltage.
> I will modify the commit message in the next version.
> sdhci_uhs2_start_signal_voltage_switch() is under
> mmc_host_ops.start_signal_voltage_switch host ops,
> therefore, we need to keep the UHS-I path here.
You should be able to leave out the patch entirely
because ->start_signal_voltage_switch() is not called
for UHS2 mode
>
> Thanks, Victor Shih
>
>>> +
>>> + return sdhci_start_signal_voltage_switch(mmc, ios);
>>> +}
>>> +
>>> /*****************************************************************************\
>>> * *
>>> * Driver init/exit *
>>> @@ -150,6 +171,9 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
>>>
>>> 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;
>>> }
>>>
>>
>> Kind regards
>> Uffe
On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
>
> From: Victor Shih <[email protected]>
>
> Embed UHS-II access/control functionality into the MMC request
> processing flow.
>
> Signed-off-by: Ulf Hansson <[email protected]>
> Signed-off-by: Jason Lai <[email protected]>
> Signed-off-by: Victor Shih <[email protected]>
> ---
>
> Updates in V12:
> - Use mmc_op_multi() to check DCMD which supports multi read/write
> in mmc_uhs2_prepare_cmd().
>
> Updates in V10:
> - Move some definitions of PatchV9[02/23] to PatchV10[06/23].
> - Move some definitions of PatchV9[05/23] to PatchV10[06/23].
> - Drop do_multi in the mmc_blk_rw_rq_prep().
> - Use tmode_half_duplex to instead of uhs2_tmode0_flag.
> - Move entire control of the tmode into mmc_uhs2_prepare_cmd().
>
> Updates in V8:
> - Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
> - Modify return value in sd_uhs2_attach().
>
> Updates in V7:
> - Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
> - Drop uhs2_state in favor of ios->timing.
> - Remove unnecessary functions.
>
> ---
>
> drivers/mmc/core/core.c | 8 +
> drivers/mmc/core/mmc_ops.c | 24 +-
> drivers/mmc/core/mmc_ops.h | 1 +
> drivers/mmc/core/sd.c | 13 +-
> drivers/mmc/core/sd.h | 4 +
> drivers/mmc/core/sd_ops.c | 9 +
> drivers/mmc/core/sd_ops.h | 18 +
> drivers/mmc/core/sd_uhs2.c | 1152 +++++++++++++++++++++++++++++++++++-
> include/linux/mmc/core.h | 13 +
> include/linux/mmc/host.h | 18 +
> 10 files changed, 1211 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index ba8808cd9318..f5dc653eafb0 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -334,6 +334,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;
> + __be32 payload[4]; /* for maximum size */
> int err;
>
> init_completion(&mrq->cmd_completion);
> @@ -351,6 +353,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> if (err)
> return err;
>
> + mmc_uhs2_card_prepare_cmd(host, mrq, uhs2_cmd, payload);
> +
> led_trigger_event(host->led, LED_FULL);
> __mmc_start_request(host, mrq);
>
> @@ -430,6 +434,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
> */
> int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> {
I think it's important to understand whether you actually tested this
for an hsq enabled host driver - or you just added this for
completeness? I believe I raised the similar question for some of the
sdhci patches too.
If the code is untested, we need to make a judgement if this is worth
carrying from a maintenance point of view.
> + struct uhs2_command uhs2_cmd;
> + __be32 payload[4]; /* for maximum size */
> int err;
>
> /*
> @@ -450,6 +456,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> if (err)
> goto out_err;
>
> + mmc_uhs2_card_prepare_cmd(host, mrq, uhs2_cmd, payload);
> +
> err = host->cqe_ops->cqe_request(host, mrq);
> if (err)
> goto out_err;
> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> index 3b3adbddf664..5c8e62e8f331 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -144,10 +144,24 @@ int mmc_set_dsr(struct mmc_host *host)
> return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
> }
>
> +int __mmc_go_idle(struct mmc_host *host)
> +{
> + struct mmc_command cmd = {};
> + int err;
> +
> + cmd.opcode = MMC_GO_IDLE_STATE;
> + cmd.arg = 0;
> + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
> +
> + err = mmc_wait_for_cmd(host, &cmd, 0);
> + mmc_delay(1);
> +
> + return err;
> +}
> +
> int mmc_go_idle(struct mmc_host *host)
> {
> int err;
> - struct mmc_command cmd = {};
>
> /*
> * Non-SPI hosts need to prevent chipselect going active during
> @@ -163,13 +177,7 @@ int mmc_go_idle(struct mmc_host *host)
> mmc_delay(1);
> }
>
> - cmd.opcode = MMC_GO_IDLE_STATE;
> - cmd.arg = 0;
> - cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
> -
> - err = mmc_wait_for_cmd(host, &cmd, 0);
> -
> - mmc_delay(1);
> + err = __mmc_go_idle(host);
>
> if (!mmc_host_is_spi(host)) {
> mmc_set_chip_select(host, MMC_CS_DONTCARE);
> diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
> index 92d4194c7893..3eaefe989c80 100644
> --- a/drivers/mmc/core/mmc_ops.h
> +++ b/drivers/mmc/core/mmc_ops.h
> @@ -25,6 +25,7 @@ struct mmc_command;
> int mmc_select_card(struct mmc_card *card);
> int mmc_deselect_cards(struct mmc_host *host);
> int mmc_set_dsr(struct mmc_host *host);
> +int __mmc_go_idle(struct mmc_host *host);
The changes above for __mmc_go_idle() looks good to me.
However, I would prefer these and similar changes to be done as a
separate re-factoring patch, preceding $subject patch. Can you please
make that?
> int mmc_go_idle(struct mmc_host *host);
> int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
> int mmc_set_relative_addr(struct mmc_card *card);
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index c3e554344c99..8d2e739a7c5c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -207,7 +207,7 @@ static int mmc_decode_csd(struct mmc_card *card)
> /*
> * Given a 64-bit response, decode to our card SCR structure.
> */
> -static int mmc_decode_scr(struct mmc_card *card)
> +int mmc_decode_scr(struct mmc_card *card)
> {
> struct sd_scr *scr = &card->scr;
> unsigned int scr_struct;
> @@ -904,7 +904,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
> return 0;
> }
>
> -static int mmc_sd_get_ro(struct mmc_host *host)
> +int mmc_sd_get_ro(struct mmc_host *host)
> {
> int ro;
>
> @@ -1630,11 +1630,6 @@ static void mmc_sd_detect(struct mmc_host *host)
> }
> }
>
> -static int sd_can_poweroff_notify(struct mmc_card *card)
> -{
> - return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> -}
I prefer if we can leave the implementation of the above function here
and please don't to rename it. Instead, just share it by adding a
declaration of it in sd.d, very similar to what is being done for the
below sd_poweroff_notify().
> -
> static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
> {
> struct sd_busy_data *data = cb_data;
> @@ -1658,7 +1653,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
> return 0;
> }
>
> -static int sd_poweroff_notify(struct mmc_card *card)
> +int sd_poweroff_notify(struct mmc_card *card)
> {
> struct sd_busy_data cb_data;
> u8 *reg_buf;
> @@ -1706,7 +1701,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
> if (mmc_card_suspended(card))
> goto out;
>
> - if (sd_can_poweroff_notify(card))
> + if (mmc_sd_can_poweroff_notify(card))
> err = sd_poweroff_notify(card);
> else if (!mmc_host_is_spi(host))
> err = mmc_deselect_cards(host);
> diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
> index 1af5a038bae9..d31259919ee5 100644
> --- a/drivers/mmc/core/sd.h
> +++ b/drivers/mmc/core/sd.h
> @@ -11,10 +11,14 @@ struct mmc_card;
>
> int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
> int mmc_sd_get_csd(struct mmc_card *card);
> +int mmc_sd_get_ro(struct mmc_host *host);
> void mmc_decode_cid(struct mmc_card *card);
> int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
> bool reinit);
> unsigned mmc_sd_get_max_clock(struct mmc_card *card);
> int mmc_sd_switch_hs(struct mmc_card *card);
>
> +/* These call back functions were also used by UHS2 sd card */
I don't think the above comment adds much value. There are other
functions being used for uhs2 too, so please just drop it.
> +int sd_poweroff_notify(struct mmc_card *card);
> +
> #endif
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index a59cd592f06e..b0cba0cd24a5 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -27,6 +27,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_SD_TRAN) {
> + host->uhs2_app_cmd = true;
> + return 0;
> + }
> +
> cmd.opcode = MMC_APP_CMD;
>
> if (card) {
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index 7667fc223b74..b8ff4713c914 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -11,6 +11,7 @@
> #include <linux/types.h>
>
> struct mmc_card;
> +struct mmc_command;
> struct mmc_host;
>
> int mmc_app_set_bus_width(struct mmc_card *card, int width);
> @@ -19,8 +20,25 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
> int mmc_send_if_cond_pcie(struct mmc_host *host, u32 ocr);
> int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
> int mmc_app_send_scr(struct mmc_card *card);
> +int mmc_decode_scr(struct mmc_card *card);
I understand that it's not entirely obvious where we prefer this
declaration to be.
The general rule of thumb is that in sd.h we specify those functions
being implemented in sd.c. And vice verse for sd_ops.h|c. So, please
move the above change to sd.h.
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
> +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
> +
> +static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq,
> + struct uhs2_command uhs2_cmd, __be32 payload[4])
> +{
> + if (host->flags & MMC_UHS2_SD_TRAN) {
According to the discussion around patch10, it seems like the other
bit, MMC_UHS2_SUPPORT, can be removed, which leaves us with
MMC_UHS2_SD_TRAN only.
That said, please drop host->flags and MMC_UHS2_SD_TRAN. Let's instead
convert into using a bool variable in the struct mmc_host, perhaps
name it "uhs2_sd_tran".
> + uhs2_cmd.payload = payload;
> + mrq->cmd->uhs2_cmd = &uhs2_cmd;
> + mmc_uhs2_prepare_cmd(host, mrq);
> + }
> +}
There are no other callers of mmc_uhs2_prepare_cmd() but
mmc_uhs2_card_prepare_cmd().
Therefore let's just drop the inline function above and move the
corresponding additional code into the implementation of
mmc_uhs2_prepare_cmd(). Thus all callers of
mmc_uhs2_card_prepare_cmd() need to move to use mmc_uhs2_prepare_cmd()
instead.
> +
> +static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
> +{
> + return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> +}
>
> #endif
>
> diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> index beb2541338ff..5578a820428a 100644
> --- a/drivers/mmc/core/sd_uhs2.c
> +++ b/drivers/mmc/core/sd_uhs2.c
> @@ -1,23 +1,51 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (C) 2021 Linaro Ltd
> - *
> * Author: Ulf Hansson <[email protected]>
> *
> + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> + * Author: Yi Sun <[email protected]>
> + *
> + * Copyright (C) 2020 Genesys Logic, Inc.
> + * Authors: Ben Chuang <[email protected]>
> + *
> + * Copyright (C) 2020 Linaro Limited
> + * Author: AKASHI Takahiro <[email protected]>
> + *
> + * Copyright (C) 2022 Genesys Logic, Inc.
> + * Authors: Jason Lai <[email protected]>
> + *
> + * Copyright (C) 2023 Genesys Logic, Inc.
> + * Authors: Victor Shih <[email protected]>
> + *
> * Support for SD UHS-II cards
> */
> #include <linux/err.h>
> +#include <linux/pm_runtime.h>
>
> #include <linux/mmc/host.h>
> #include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/mmc/sd_uhs2.h>
>
> +#include "card.h"
> #include "core.h"
> #include "bus.h"
> #include "sd.h"
> +#include "sd_ops.h"
> #include "mmc_ops.h"
>
> +#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US (1 * 1000) /* 1ms */
> +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100 /* 100ms */
Nitpick: The comments "1ms" and "100ms" are kind of superfluous, due
to the well chosen names for the defines. Perhaps drop them?
> +
> static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
>
> +struct sd_uhs2_wait_active_state_data {
> + struct mmc_host *host;
> + struct mmc_command *cmd;
> +};
> +
> static int sd_uhs2_power_up(struct mmc_host *host)
> {
> int err;
> @@ -44,10 +72,49 @@ static int sd_uhs2_power_off(struct mmc_host *host)
> host->ios.clock = 0;
> host->ios.timing = MMC_TIMING_LEGACY;
> host->ios.power_mode = MMC_POWER_OFF;
> + if (host->flags & MMC_UHS2_SD_TRAN)
There is no need to check that the flag/bit is set. Just clear it.
> + host->flags &= ~MMC_UHS2_SD_TRAN;
>
> return host->ops->uhs2_control(host, UHS2_SET_IOS);
> }
>
> +/*
> + * sd_uhs2_cmd_assemble() - build up UHS-II command packet which is embedded in
> + * mmc_command structure
> + * @cmd: MMC command to executed
> + * @uhs2_cmd: UHS2 command corresponded to MMC command
> + * @header: Header field of UHS-II command cxpacket
> + * @arg: Argument field of UHS-II command packet
> + * @payload: Payload field of UHS-II command packet
> + * @plen: Payload length
> + * @resp: Response buffer is allocated by caller and it is used to keep
> + * the response of CM-TRAN command. For SD-TRAN command, uhs2_resp
> + * should be null and SD-TRAN command response should be stored in
> + * resp of mmc_command.
> + * @resp_len: Response buffer length
> + *
> + * The uhs2_command structure contains message packets which are transmited/
> + * received on UHS-II bus. This function fills in the contents of uhs2_command
> + * structure and embededs UHS2 command into mmc_command structure, which is used
> + * in legacy SD operation functions.
> + *
> + */
> +static void sd_uhs2_cmd_assemble(struct mmc_command *cmd,
> + struct uhs2_command *uhs2_cmd,
> + u16 header, u16 arg, __be32 *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;
> +}
> +
> /*
> * Run the phy initialization sequence, which mainly relies on the UHS-II host
> * to check that we reach the expected electrical state, between the host and
> @@ -55,7 +122,15 @@ static int sd_uhs2_power_off(struct mmc_host *host)
> */
> static int sd_uhs2_phy_init(struct mmc_host *host)
> {
> - return 0;
> + int err = 0;
No need to assign err to 0.
> +
> + err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
> + if (err) {
> + pr_err("%s: failed to initial phy for UHS-II!\n",
> + mmc_hostname(host));
> + }
> +
> + return err;
> }
>
> /*
> @@ -64,6 +139,82 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> */
> static int sd_uhs2_dev_init(struct mmc_host *host)
> {
> + struct mmc_command cmd = {0};
> + struct uhs2_command uhs2_cmd = {};
> + u32 cnt;
> + u32 dap, gap, resp_gap;
> + u16 header, arg;
> + __be32 payload[UHS2_DEV_INIT_PAYLOAD_LEN];
> + u8 gd = 0;
> + u8 resp[UHS2_DEV_ENUM_RESP_LEN] = {0};
> + int err;
> +
> + dap = host->uhs2_caps.dap;
> + gap = host->uhs2_caps.gap;
> +
> + /*
> + * Refer to UHS-II Addendum Version 1.02 Figure 6-21 to see DEVICE_INIT CCMD format.
> + * Head:
> + * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
> + * - IOADR = CMD_BASE + 002h
> + * Payload:
> + * - bit [3:0] : GAP(Group Allocated Power)
> + * - bit [7:4] : GD(Group Descriptor)
> + * - bit [11] : Complete Flag
> + * - bit [15:12]: DAP(Device Allocated Power)
> + */
> + 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);
> +
> + /*
> + * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> + * Max. time from DEVICE_INIT CCMD EOP reception on Device
> + * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> + * 1 second.
> + */
> + cmd.busy_timeout = 1000;
> +
> + /*
> + * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> + * When the number of the DEVICE_INIT commands is reach to
> + * 30 tiems, Host shall stop issuing DEVICE_INIT command
> + * and regard it as an error.
To clarify the above comment a bit, may I suggest we rephrase this into:
Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
Let's retry the DEVICE_INIT command no more than 30 times.
> + */
> + for (cnt = 0; cnt < 30; cnt++) {
> + payload[0] = ((dap & 0xF) << 12) |
> + UHS2_DEV_INIT_COMPLETE_FLAG |
> + ((gd & 0xF) << 4) |
> + (gap & 0xF);
> +
> + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,
> + payload, UHS2_DEV_INIT_PAYLOAD_LEN,
> + resp, UHS2_DEV_INIT_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 err;
> + }
> +
> + 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) {
> + host->uhs2_caps.group_desc = gd;
> + return 0;
> + }
> + resp_gap = resp[4] & 0x0F;
> + if (gap == resp_gap)
> + gd++;
> + }
> +
> return 0;
> }
>
[...]
>
> -/*
> - * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> - * commands/requests to be backwards compatible through the legacy SD protocol.
> - * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
> - * be set through a legacy CMD6. Note that, the power limit that becomes set,
> - * survives a soft reset through the GO_DORMANT_STATE command.
> - */
> -static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> +static int sd_uhs2_go_dormant(struct mmc_host *host, u32 node_id)
> {
> + struct mmc_command cmd = {0};
> + struct uhs2_command uhs2_cmd = {};
> + u16 header, arg;
> + __be32 payload[1];
> + int err;
> +
> + /* Disable Normal INT */
> + err = host->ops->uhs2_control(host, UHS2_DISABLE_INT);
> + if (err) {
> + pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> + mmc_hostname(host), __func__);
> + return err;
> + }
> +
> + /*
> + * Refer to UHS-II Addendum Version 1.02 Figure 6-17 to see GO_DORMANT_STATE CCMD format.
> + * Header:
> + * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
> + * - IOADR = CMD_BASE + 001h
> + * Payload:
> + * - bit [7]: HBR(Entry to Hibernate Mode)
> + * 1: Host intends to enter Hibernate mode during Dormant state.
> + * The default setting is 0 because hibernate is currently not supported.
> + */
> + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | 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);
> +
> + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_GO_DORMANT_PAYLOAD_LEN,
> + 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 err;
> + }
> +
> + /* Check Dormant State in Present */
> + err = host->ops->uhs2_control(host, UHS2_CHECK_DORMANT);
> + if (err)
> + return err;
> +
> + /* Disable UHS2 card clock */
> + err = host->ops->uhs2_control(host, UHS2_DISABLE_CLK);
> + if (err)
> + return err;
> +
> + /* Restore sd clock */
> + mmc_delay(5);
> + err = host->ops->uhs2_control(host, UHS2_ENABLE_CLK);
> + if (err)
> + return err;
> +
> + /* Enable Normal INT */
> + err = host->ops->uhs2_control(host, UHS2_ENABLE_INT);
> + if (err)
> + return err;
> +
> + /* Detect UHS2 */
> + err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static int __sd_uhs2_wait_active_state_cb(void *cb_data, bool *busy)
Please drop the "__" prefix for this function.
> +{
> + struct sd_uhs2_wait_active_state_data *data = cb_data;
> + struct mmc_host *host = data->host;
> + struct mmc_command *cmd = data->cmd;
> + int err;
> +
> + err = mmc_wait_for_cmd(host, cmd, 0);
> + if (err)
> + return err;
> +
> + if (cmd->resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> + *busy = false;
> + else
> + *busy = true;
> +
> + return 0;
> +}
> +
> +static int sd_uhs2_go_dormant_state(struct mmc_host *host, u32 node_id)
> +{
> + struct mmc_command cmd = {0};
> + struct uhs2_command uhs2_cmd = {};
> + u16 header, arg;
> + int err;
> + struct sd_uhs2_wait_active_state_data cb_data = {
> + .host = host,
> + .cmd = &cmd
> + };
> +
> + err = sd_uhs2_go_dormant(host, node_id);
> + if (err) {
> + pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> + mmc_hostname(host), __func__, err);
> + return err;
> + }
> +
> + /*
> + * Use Control Read CCMD to check Config Completion(bit 63) in Generic Setting Register.
> + * - Control Read(R/W=0) with 8-Byte payload(PLEN=10b).
> + * - IOADR = Generic Setting Register(CFG_BASE + 008h)
> + *
> + * When UHS-II card been switched to new speed mode, it will set Config Completion to 1.
> + */
> + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> + UHS2_NATIVE_CMD_READ |
> + UHS2_NATIVE_CMD_PLEN_8B |
> + (UHS2_DEV_CONFIG_GEN_SET >> 8);
> +
> + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> + err = __mmc_poll_for_busy(host, UHS2_WAIT_CFG_COMPLETE_PERIOD_US,
> + UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS,
> + &__sd_uhs2_wait_active_state_cb, &cb_data);
> + if (err) {
> + pr_err("%s: %s: Not switch to Active in 100 ms\n", mmc_hostname(host), __func__);
> + return err;
> + }
> +
> return 0;
> }
>
> +static void sd_uhs2_remove(struct mmc_host *host)
> +{
> + mmc_remove_card(host->card);
> + host->card = NULL;
> +}
> +
> /*
> * Allocate the data structure for the mmc_card and run the UHS-II specific
> * initialization sequence.
> */
> -static int sd_uhs2_init_card(struct mmc_host *host)
> +static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> {
> struct mmc_card *card;
> u32 node_id;
> @@ -131,9 +834,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> if (err)
> return err;
>
> - card = mmc_alloc_card(host, &sd_type);
> - if (IS_ERR(card))
> - return PTR_ERR(card);
> + if (oldcard) {
> + card = oldcard;
> + } else {
> + card = mmc_alloc_card(host, &sd_type);
> + if (IS_ERR(card))
> + return PTR_ERR(card);
> + }
> + host->card = card;
I think it would be better to leave the assignment of host->card until
a bit later in this function.
We don't want to make it look like we need the host->card to be
assigned to complete the initialization in this function. See more
about this below.
>
> card->uhs2_config.node_id = node_id;
> card->type = MMC_TYPE_SD;
> @@ -146,18 +854,226 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> if (err)
> goto err;
>
> - host->card = card;
> + /* If change speed to Range B, need to GO_DORMANT_STATE */
> + if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
> + host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
> + err = sd_uhs2_go_dormant_state(host, node_id);
> + if (err)
> + return err;
This is wrong, as we need to check if we should free the allocated
struct mmc_card too.
A "goto err;" should fix this.
> + }
> +
> + host->flags |= MMC_UHS2_SD_TRAN;
> +
> return 0;
>
> err:
> - mmc_remove_card(card);
> + sd_uhs2_remove(host);
If we have an "oldcard" we must not remove it in this path as it
simply doesn't work. In this case the removal is managed through the
mmc_rescan() work, when it calls the bus_ops->detect() callback
(sd_uhs2_detect()).
So we need to add a check for "!oldcard" and then we should call
mmc_remove_card() instead. That is also why it makes sense to assign
host->card as late as possible, just before returning 0 a few lines
above.
> return err;
> }
>
> -static void sd_uhs2_remove(struct mmc_host *host)
> +int sd_uhs2_reinit(struct mmc_host *host)
> {
> - mmc_remove_card(host->card);
> - host->card = NULL;
> + struct mmc_card *card = host->card;
> + int err;
> +
> + sd_uhs2_power_up(host);
> + err = sd_uhs2_phy_init(host);
> + if (err)
> + return err;
> +
> + err = sd_uhs2_init_card(host, card);
> + if (err)
> + return err;
By looking at the above there seems to be some pieces missing to make this work.
*) We need to re-initialize the card to work in SD_TRAN again, hence
we need to call sd_uhs2_legacy_init() and make it work for this case
too.
**) If the card gets removed and replaced by another card during
system suspend, we must make sure to drop the original allocated
struct mmc_card. For legacy SD cards we compare the cid register and
if there is no match, we return an error during system resume and
leave the card to be removed by mmc_rescan(). Perhaps we can use a
similar approach for uhs2 or maybe something more clever?
> +
> + mmc_card_set_present(card);
No, this is wrong. This is already managed by mmc_add_card() that has
already been called for the card earlier.
> + return err;
> +}
> +
> +/*
> + * Mask off any voltages we don't support and select
> + * the lowest voltage
> + */
> +u32 sd_uhs2_select_voltage(struct mmc_host *host, u32 ocr)
As discussed earlier, it's better to use mmc_select_voltage() for uhs2
too. So, please drop this function altogether.
To make mmc_select_voltage() to work for uhs2, we should prevent the
path for MMC_CAP2_FULL_PWR_CYCLE. In other words something like the
below "diff" in mmc_select_voltage() should work, I think.
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index f5dc653eafb0..92a6e3c762af 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1135,7 +1135,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return 0;
}
- if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
+ if (!mmc_card_uhs2(host) && host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
bit = ffs(ocr) - 1;
ocr &= 3 << bit;
mmc_power_cycle(host, ocr);
> +{
> + int bit;
> + int err;
> +
> + /*
> + * Sanity check the voltages that the card claims to
> + * support.
> + */
> + if (ocr & 0x7F) {
> + dev_warn(mmc_dev(host), "card claims to support voltages below defined range\n");
> + ocr &= ~0x7F;
> + }
> +
> + ocr &= host->ocr_avail;
> + if (!ocr) {
> + dev_warn(mmc_dev(host), "no support for card's volts\n");
> + return 0;
> + }
> +
> + if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> + bit = ffs(ocr) - 1;
> + ocr &= 3 << bit;
> + /* Power cycle */
> + err = sd_uhs2_power_off(host);
> + if (err)
> + return 0;
> + err = sd_uhs2_reinit(host);
> + if (err)
> + return 0;
> + } else {
> + bit = fls(ocr) - 1;
> + ocr &= 3 << bit;
> + if (bit != host->ios.vdd)
> + dev_warn(mmc_dev(host), "exceeding card's volts\n");
> + }
> +
> + return ocr;
> +}
> +
> +/*
> + * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> + * commands/requests to be backwards compatible through the legacy SD protocol.
> + * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
> + * be set through a legacy CMD6. Note that, the power limit that becomes set,
> + * survives a soft reset through the GO_DORMANT_STATE command.
> + */
> +static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> +{
> + int err;
> + u32 cid[4];
> + u32 ocr;
> + u32 rocr;
> + u8 *status;
> + int ro;
> +
> + /* Send CMD0 to reset SD card */
> + err = __mmc_go_idle(host);
> + if (err)
> + return err;
> +
> + mmc_delay(1);
> +
> + /* Send CMD8 to communicate SD interface operation condition */
> + err = mmc_send_if_cond(host, host->ocr_avail);
> + if (err) {
> + dev_warn(mmc_dev(host), "CMD8 error\n");
> + goto err;
> + }
> +
> + /*
> + * Probe SD card working voltage.
> + */
> + err = mmc_send_app_op_cond(host, 0, &ocr);
> + if (err)
> + goto err;
> +
> + card->ocr = ocr;
I don't think this is the ocr we should store in the card->ocr.
Instead it should be the ocr that has been negotiated through the
mmc_select_voltage().
> +
> + /*
> + * Some SD cards claims an out of spec VDD voltage range. Let's treat
> + * these bits as being in-valid and especially also bit7.
> + */
> + ocr &= ~0x7FFF;
> + rocr = sd_uhs2_select_voltage(host, ocr);
> + /*
> + * Some cards have zero value of rocr in UHS-II mode. Assign host's
> + * ocr value to rocr.
> + */
That sounds weird to me. What card did this happen for? Did you print
the ocr that you received from the card and compared it with
host->ocr_avail?
In any case, if I understand correctly, this will trigger
sd_uhs2_select_voltage() to print "no support for card's volts",
right?
If this is a common case for uhs2 cards, maybe we should teach
mmc_select_voltage() about this problem too. We don't want to print
error messages to the log to worry the user, unless it's really a
problem.
> + if (!rocr)
> + rocr = host->ocr_avail;
> +
> + rocr |= (SD_OCR_CCS | SD_OCR_XPC);
> +
> + /* Wait SD power on ready */
> + ocr = rocr;
> +
> + err = mmc_send_app_op_cond(host, ocr, &rocr);
> + if (err)
> + goto err;
> +
> + err = mmc_send_cid(host, cid);
> + if (err)
> + goto err;
> +
> + memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> + mmc_decode_cid(card);
> +
> + /*
> + * For native busses: get card RCA and quit open drain mode.
> + */
> + err = mmc_send_relative_addr(host, &card->rca);
> + if (err)
> + goto err;
> +
> + err = mmc_sd_get_csd(card);
> + if (err)
> + goto err;
> +
> + /*
> + * Select card, as all following commands rely on that.
> + */
> + err = mmc_select_card(card);
> + if (err)
> + goto err;
> +
> + /*
> + * Fetch SCR from card.
> + */
> + err = mmc_app_send_scr(card);
> + if (err)
> + goto err;
> +
> + err = mmc_decode_scr(card);
> + if (err)
> + goto err;
> +
> + /*
> + * Switch to high power consumption mode.
> + * Even switch failed, sd card can still work at lower power consumption mode, but
> + * performance will be lower than high power consumption mode.
> + */
> + status = kmalloc(64, GFP_KERNEL);
> + if (!status)
> + return -ENOMEM;
> +
> + if (!(card->csd.cmdclass & CCC_SWITCH)) {
> + pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
> + mmc_hostname(card->host));
> + } else {
> + /* send CMD6 to set Maximum Power Consumption to get better performance */
> + err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> + if (!err)
> + err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> +
> + err = 0;
> + }
> +
> + /*
> + * Check if read-only switch is active.
> + */
> + ro = mmc_sd_get_ro(host);
> + if (ro < 0) {
> + pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> + mmc_hostname(host));
> + } else if (ro > 0) {
> + mmc_card_set_readonly(card);
> + }
> +
> + /*
> + * NOTE:
> + * Should we read Externsion Register to check power notification feature here?
> + */
Yes, that seems reasonable.
Although, I think it's a good idea to make the change as an
incremental patch on top of $subject patch. In fact, the whole system
wide suspend/resume and runtime PM support look far from ready to me.
Maybe I am wrong - or did you test those code paths too?
That said, as an option to more easily move things forward, may I
suggest that you move the implementation of the entire PM support into
incremental patches on top of $subject patch?
To make it clear, I am referring to sd_uhs2_runtime_suspend,
sd_uhs2_runtime_resume, sd_uhs2_shutdown, sd_uhs2_suspend,
sd_uhs2_resume, sd_uhs2_hw_reset, sd_uhs2_reinit.
In this way, we could perhaps get the foundation for UHS2 support in
place while we continue to work on the PM parts as improvements on
top. What do you think?
> +
> + kfree(status);
> +
> + return 0;
> +
> +err:
> + sd_uhs2_remove(host);
> + return err;
> }
>
> static int sd_uhs2_alive(struct mmc_host *host)
> @@ -179,38 +1095,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
> mmc_claim_host(host);
> mmc_detach_bus(host);
> sd_uhs2_power_off(host);
> + host->flags &= ~MMC_UHS2_SUPPORT;
> mmc_release_host(host);
> }
> }
[...]
> -static int sd_uhs2_shutdown(struct mmc_host *host)
> +static int sd_uhs2_hw_reset(struct mmc_host *host)
> {
> - return 0;
> + int err;
> +
> + sd_uhs2_power_off(host);
> + /* Wait at least 1 ms according to SD spec */
> + mmc_delay(1);
> + sd_uhs2_power_up(host);
> +
> + err = sd_uhs2_reinit(host);
Unless I am missing something, sd_uhs2_reinit() isn't sufficient to
re-initialize the card. We need to move to SD_TRAN too, right?
> +
> + return err;
> }
>
> -static int sd_uhs2_hw_reset(struct mmc_host *host)
> +/*
> + * mmc_uhs2_prepare_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
> + */
> +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
> {
> - return 0;
> + struct mmc_command *cmd;
> + struct uhs2_command *uhs2_cmd;
> + u16 header, arg;
> + __be32 *payload;
> + u8 plen;
> +
> + cmd = mrq->cmd;
> + header = host->card->uhs2_config.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;
> + if (host->uhs2_app_cmd) {
> + arg |= UHS2_SD_CMD_APP;
> + host->uhs2_app_cmd = false;
> + }
> +
> + /*
> + * UHS-II Addendum 7.2.1.2
> + * Host may set DM to 1 for DCMD which supports multi-block read/write regardless of
> + * data transfer length (e.g., CMD18, CMD25). Otherwise, it shall not set DM to 1.
> + * (e.g., CMD6, CMD17, CMD24). These rules are also applied to other multi-block read/write
> + * commands defined in other Part of SD specifications (for example, Host may set DM to 1
> + * for ACMD18 or ACMD25).
> + */
> + if (mmc_op_multi(cmd->opcode))
> + cmd->uhs2_cmd->tmode_half_duplex = mmc_card_uhs2_hd_mode(host);
> + else
> + cmd->uhs2_cmd->tmode_half_duplex = 0;
> +
> + 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_cmd->tmode_half_duplex) {
> + if (mmc_card_uhs2_hd_mode(host))
> + 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] = cpu_to_be32(cmd->data->blksz);
This case isn't supported by the mmc block device driver.
Perhaps this is this needed to support the uhs2 card initialization
sequence? If not, I would prefer if we can drop this path/code.
> + } else {
> + payload[1] = cpu_to_be32(cmd->data->blocks);
> + }
> + } else {
> + plen = 1;
> + }
> +
> + payload[0] = cpu_to_be32(cmd->arg);
> + sd_uhs2_cmd_assemble(cmd, uhs2_cmd, header, arg, payload, plen, NULL, 0);
> }
>
> static const struct mmc_bus_ops sd_uhs2_ops = {
> @@ -221,7 +1293,7 @@ static const struct mmc_bus_ops sd_uhs2_ops = {
> .resume = sd_uhs2_resume,
> .runtime_suspend = sd_uhs2_runtime_suspend,
> .runtime_resume = sd_uhs2_runtime_resume,
> - .shutdown = sd_uhs2_shutdown,
> + .shutdown = sd_uhs2_suspend,
> .hw_reset = sd_uhs2_hw_reset,
> };
>
> @@ -229,6 +1301,8 @@ static int sd_uhs2_attach(struct mmc_host *host)
> {
> int err;
>
> + host->flags |= MMC_UHS2_SUPPORT;
> +
> err = sd_uhs2_power_up(host);
> if (err)
> goto err;
> @@ -237,7 +1311,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
> if (err)
> goto err;
>
> - err = sd_uhs2_init_card(host);
> + err = sd_uhs2_init_card(host, NULL);
> if (err)
> goto err;
>
> @@ -254,21 +1328,32 @@ static int sd_uhs2_attach(struct mmc_host *host)
> goto remove_card;
>
> mmc_claim_host(host);
> +
> return 0;
>
> remove_card:
> - mmc_remove_card(host->card);
> - host->card = NULL;
> + sd_uhs2_remove(host);
> mmc_claim_host(host);
> - mmc_detach_bus(host);
> +
> err:
> + mmc_detach_bus(host);
> sd_uhs2_power_off(host);
> + host->flags &= ~MMC_UHS2_SUPPORT;
> return err;
> }
>
> +/**
> + * mmc_attach_sd_uhs2 - select UHS2 interface
> + * @host: MMC host
> + *
> + * 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_attach_sd_uhs2(struct mmc_host *host)
> {
> - int i, err = 0;
> + int i, err;
>
> if (!(host->caps2 & MMC_CAP2_SD_UHS2))
> return -EOPNOTSUPP;
> @@ -283,6 +1368,9 @@ int mmc_attach_sd_uhs2(struct mmc_host *host)
> */
> for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) {
> host->f_init = sd_uhs2_freqs[i];
> + pr_info("%s: %s: trying to init UHS-II card at %u Hz\n",
> + mmc_hostname(host), __func__, host->f_init);
> +
Please move this to pr_dbg.
> err = sd_uhs2_attach(host);
> if (!err)
> break;
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 6efec0b9820c..4623651037dd 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -23,6 +23,15 @@ enum mmc_blk_status {
> MMC_BLK_NEW_REQUEST,
> };
>
> +struct uhs2_command {
> + u16 header;
> + u16 arg;
> + __be32 *payload;
> + u32 payload_len;
> + u32 packet_len;
> + u8 tmode_half_duplex;
> +};
> +
> struct mmc_command {
> u32 opcode;
> u32 arg;
> @@ -109,6 +118,10 @@ 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 */
I think it would be better to keep the responses (uhs2_resp,
uhs2_resp_len) into the struct uhs2_command too. In this way,
everything for uhs2 is kept behind one single pointer, the ->uhs2_cmd.
Would that work?
> };
>
> struct mmc_data {
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 0d1a3e9ad93c..6c8258310641 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -126,6 +126,13 @@ struct sd_uhs2_caps {
> };
>
> enum sd_uhs2_operation {
> + UHS2_PHY_INIT = 0,
> + UHS2_SET_CONFIG,
> + UHS2_ENABLE_INT,
> + UHS2_DISABLE_INT,
> + UHS2_ENABLE_CLK,
> + UHS2_DISABLE_CLK,
> + UHS2_CHECK_DORMANT,
> UHS2_SET_IOS,
> };
>
> @@ -476,6 +483,11 @@ struct mmc_host {
> #endif
> #define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */
>
> + int flags;
> +#define MMC_UHS2_SUPPORT (1 << 0)
> +#define MMC_UHS2_SD_TRAN (1 << 1)
> +
> + bool uhs2_app_cmd; /* Host UHS-II APP Command */
> struct sd_uhs2_caps uhs2_caps; /* Host UHS-II capabilities */
>
> int fixed_drv_type; /* fixed driver type for non-removable media */
> @@ -728,6 +740,12 @@ static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
> host->err_stats[stat] += 1;
> }
>
> +static inline int mmc_card_uhs2_hd_mode(struct mmc_host *host)
> +{
> + return host->ios.timing == MMC_TIMING_UHS2_SPEED_A_HD ||
> + host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD;
> +}
> +
> int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp);
> int mmc_send_status(struct mmc_card *card, u32 *status);
> int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
Kind regards
Uffe
Hi, Ulf
In this patch, I have made modifications to some suggestions, but some
still need to be discussed with you.
I will submit the v13 version later, and we can continue to discuss
and modify it in the v13 version.
Please also help review whether there are still problems with the
parts I modified.
Thanks, Victor Shih
On Sat, Oct 7, 2023 at 12:28 AM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > Embed UHS-II access/control functionality into the MMC request
> > processing flow.
> >
> > Signed-off-by: Ulf Hansson <[email protected]>
> > Signed-off-by: Jason Lai <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > ---
> >
> > Updates in V12:
> > - Use mmc_op_multi() to check DCMD which supports multi read/write
> > in mmc_uhs2_prepare_cmd().
> >
> > Updates in V10:
> > - Move some definitions of PatchV9[02/23] to PatchV10[06/23].
> > - Move some definitions of PatchV9[05/23] to PatchV10[06/23].
> > - Drop do_multi in the mmc_blk_rw_rq_prep().
> > - Use tmode_half_duplex to instead of uhs2_tmode0_flag.
> > - Move entire control of the tmode into mmc_uhs2_prepare_cmd().
> >
> > Updates in V8:
> > - Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
> > - Modify return value in sd_uhs2_attach().
> >
> > Updates in V7:
> > - Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
> > - Drop uhs2_state in favor of ios->timing.
> > - Remove unnecessary functions.
> >
> > ---
> >
> > drivers/mmc/core/core.c | 8 +
> > drivers/mmc/core/mmc_ops.c | 24 +-
> > drivers/mmc/core/mmc_ops.h | 1 +
> > drivers/mmc/core/sd.c | 13 +-
> > drivers/mmc/core/sd.h | 4 +
> > drivers/mmc/core/sd_ops.c | 9 +
> > drivers/mmc/core/sd_ops.h | 18 +
> > drivers/mmc/core/sd_uhs2.c | 1152 +++++++++++++++++++++++++++++++++++-
> > include/linux/mmc/core.h | 13 +
> > include/linux/mmc/host.h | 18 +
> > 10 files changed, 1211 insertions(+), 49 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index ba8808cd9318..f5dc653eafb0 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -334,6 +334,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;
> > + __be32 payload[4]; /* for maximum size */
> > int err;
> >
> > init_completion(&mrq->cmd_completion);
> > @@ -351,6 +353,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> > if (err)
> > return err;
> >
> > + mmc_uhs2_card_prepare_cmd(host, mrq, uhs2_cmd, payload);
> > +
> > led_trigger_event(host->led, LED_FULL);
> > __mmc_start_request(host, mrq);
> >
> > @@ -430,6 +434,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
> > */
> > int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> > {
>
> I think it's important to understand whether you actually tested this
> for an hsq enabled host driver - or you just added this for
> completeness? I believe I raised the similar question for some of the
> sdhci patches too.
>
> If the code is untested, we need to make a judgement if this is worth
> carrying from a maintenance point of view.
>
Hi, Ulf
This part has not been actually tested. Should I drop it or keep it?
Thanks, Victor Shih
> > + struct uhs2_command uhs2_cmd;
> > + __be32 payload[4]; /* for maximum size */
> > int err;
> >
> > /*
> > @@ -450,6 +456,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> > if (err)
> > goto out_err;
> >
> > + mmc_uhs2_card_prepare_cmd(host, mrq, uhs2_cmd, payload);
> > +
> > err = host->cqe_ops->cqe_request(host, mrq);
> > if (err)
> > goto out_err;
> > diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> > index 3b3adbddf664..5c8e62e8f331 100644
> > --- a/drivers/mmc/core/mmc_ops.c
> > +++ b/drivers/mmc/core/mmc_ops.c
> > @@ -144,10 +144,24 @@ int mmc_set_dsr(struct mmc_host *host)
> > return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
> > }
> >
> > +int __mmc_go_idle(struct mmc_host *host)
> > +{
> > + struct mmc_command cmd = {};
> > + int err;
> > +
> > + cmd.opcode = MMC_GO_IDLE_STATE;
> > + cmd.arg = 0;
> > + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
> > +
> > + err = mmc_wait_for_cmd(host, &cmd, 0);
> > + mmc_delay(1);
> > +
> > + return err;
> > +}
> > +
> > int mmc_go_idle(struct mmc_host *host)
> > {
> > int err;
> > - struct mmc_command cmd = {};
> >
> > /*
> > * Non-SPI hosts need to prevent chipselect going active during
> > @@ -163,13 +177,7 @@ int mmc_go_idle(struct mmc_host *host)
> > mmc_delay(1);
> > }
> >
> > - cmd.opcode = MMC_GO_IDLE_STATE;
> > - cmd.arg = 0;
> > - cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
> > -
> > - err = mmc_wait_for_cmd(host, &cmd, 0);
> > -
> > - mmc_delay(1);
> > + err = __mmc_go_idle(host);
> >
> > if (!mmc_host_is_spi(host)) {
> > mmc_set_chip_select(host, MMC_CS_DONTCARE);
> > diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
> > index 92d4194c7893..3eaefe989c80 100644
> > --- a/drivers/mmc/core/mmc_ops.h
> > +++ b/drivers/mmc/core/mmc_ops.h
> > @@ -25,6 +25,7 @@ struct mmc_command;
> > int mmc_select_card(struct mmc_card *card);
> > int mmc_deselect_cards(struct mmc_host *host);
> > int mmc_set_dsr(struct mmc_host *host);
> > +int __mmc_go_idle(struct mmc_host *host);
>
> The changes above for __mmc_go_idle() looks good to me.
>
> However, I would prefer these and similar changes to be done as a
> separate re-factoring patch, preceding $subject patch. Can you please
> make that?
>
Hi, Ulf
I will separate this part into one patch and update it in version 13.
Thanks, Victor Shih
> > int mmc_go_idle(struct mmc_host *host);
> > int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
> > int mmc_set_relative_addr(struct mmc_card *card);
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index c3e554344c99..8d2e739a7c5c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -207,7 +207,7 @@ static int mmc_decode_csd(struct mmc_card *card)
> > /*
> > * Given a 64-bit response, decode to our card SCR structure.
> > */
> > -static int mmc_decode_scr(struct mmc_card *card)
> > +int mmc_decode_scr(struct mmc_card *card)
> > {
> > struct sd_scr *scr = &card->scr;
> > unsigned int scr_struct;
> > @@ -904,7 +904,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
> > return 0;
> > }
> >
> > -static int mmc_sd_get_ro(struct mmc_host *host)
> > +int mmc_sd_get_ro(struct mmc_host *host)
> > {
> > int ro;
> >
> > @@ -1630,11 +1630,6 @@ static void mmc_sd_detect(struct mmc_host *host)
> > }
> > }
> >
> > -static int sd_can_poweroff_notify(struct mmc_card *card)
> > -{
> > - return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> > -}
>
> I prefer if we can leave the implementation of the above function here
> and please don't to rename it. Instead, just share it by adding a
> declaration of it in sd.d, very similar to what is being done for the
> below sd_poweroff_notify().
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > -
> > static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
> > {
> > struct sd_busy_data *data = cb_data;
> > @@ -1658,7 +1653,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
> > return 0;
> > }
> >
> > -static int sd_poweroff_notify(struct mmc_card *card)
> > +int sd_poweroff_notify(struct mmc_card *card)
> > {
> > struct sd_busy_data cb_data;
> > u8 *reg_buf;
> > @@ -1706,7 +1701,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
> > if (mmc_card_suspended(card))
> > goto out;
> >
> > - if (sd_can_poweroff_notify(card))
> > + if (mmc_sd_can_poweroff_notify(card))
> > err = sd_poweroff_notify(card);
> > else if (!mmc_host_is_spi(host))
> > err = mmc_deselect_cards(host);
> > diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
> > index 1af5a038bae9..d31259919ee5 100644
> > --- a/drivers/mmc/core/sd.h
> > +++ b/drivers/mmc/core/sd.h
> > @@ -11,10 +11,14 @@ struct mmc_card;
> >
> > int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
> > int mmc_sd_get_csd(struct mmc_card *card);
> > +int mmc_sd_get_ro(struct mmc_host *host);
> > void mmc_decode_cid(struct mmc_card *card);
> > int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
> > bool reinit);
> > unsigned mmc_sd_get_max_clock(struct mmc_card *card);
> > int mmc_sd_switch_hs(struct mmc_card *card);
> >
> > +/* These call back functions were also used by UHS2 sd card */
>
> I don't think the above comment adds much value. There are other
> functions being used for uhs2 too, so please just drop it.
>
Hi, Ulf and Adrian
I will drop this in version 13.
Thanks, Victor Shih
> > +int sd_poweroff_notify(struct mmc_card *card);
> > +
> > #endif
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index a59cd592f06e..b0cba0cd24a5 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -27,6 +27,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_SD_TRAN) {
> > + host->uhs2_app_cmd = true;
> > + return 0;
> > + }
> > +
> > cmd.opcode = MMC_APP_CMD;
> >
> > if (card) {
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index 7667fc223b74..b8ff4713c914 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -11,6 +11,7 @@
> > #include <linux/types.h>
> >
> > struct mmc_card;
> > +struct mmc_command;
> > struct mmc_host;
> >
> > int mmc_app_set_bus_width(struct mmc_card *card, int width);
> > @@ -19,8 +20,25 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
> > int mmc_send_if_cond_pcie(struct mmc_host *host, u32 ocr);
> > int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
> > int mmc_app_send_scr(struct mmc_card *card);
> > +int mmc_decode_scr(struct mmc_card *card);
>
> I understand that it's not entirely obvious where we prefer this
> declaration to be.
>
> The general rule of thumb is that in sd.h we specify those functions
> being implemented in sd.c. And vice verse for sd_ops.h|c. So, please
> move the above change to sd.h.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
> > +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
> > +
> > +static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq,
> > + struct uhs2_command uhs2_cmd, __be32 payload[4])
> > +{
> > + if (host->flags & MMC_UHS2_SD_TRAN) {
>
> According to the discussion around patch10, it seems like the other
> bit, MMC_UHS2_SUPPORT, can be removed, which leaves us with
> MMC_UHS2_SD_TRAN only.
>
> That said, please drop host->flags and MMC_UHS2_SD_TRAN. Let's instead
> convert into using a bool variable in the struct mmc_host, perhaps
> name it "uhs2_sd_tran".
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + uhs2_cmd.payload = payload;
> > + mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > + mmc_uhs2_prepare_cmd(host, mrq);
> > + }
> > +}
>
> There are no other callers of mmc_uhs2_prepare_cmd() but
> mmc_uhs2_card_prepare_cmd().
>
> Therefore let's just drop the inline function above and move the
> corresponding additional code into the implementation of
> mmc_uhs2_prepare_cmd(). Thus all callers of
> mmc_uhs2_card_prepare_cmd() need to move to use mmc_uhs2_prepare_cmd()
> instead.
>
Hi, Ulf
I need a little more time to try this part, and I will continue to
work on it in version 13.
Thanks, Victor Shih
> > +
> > +static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
> > +{
> > + return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> > +}
> >
> > #endif
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index beb2541338ff..5578a820428a 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -1,23 +1,51 @@
> > // SPDX-License-Identifier: GPL-2.0-only
> > /*
> > * Copyright (C) 2021 Linaro Ltd
> > - *
> > * Author: Ulf Hansson <[email protected]>
> > *
> > + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> > + * Author: Yi Sun <[email protected]>
> > + *
> > + * Copyright (C) 2020 Genesys Logic, Inc.
> > + * Authors: Ben Chuang <[email protected]>
> > + *
> > + * Copyright (C) 2020 Linaro Limited
> > + * Author: AKASHI Takahiro <[email protected]>
> > + *
> > + * Copyright (C) 2022 Genesys Logic, Inc.
> > + * Authors: Jason Lai <[email protected]>
> > + *
> > + * Copyright (C) 2023 Genesys Logic, Inc.
> > + * Authors: Victor Shih <[email protected]>
> > + *
> > * Support for SD UHS-II cards
> > */
> > #include <linux/err.h>
> > +#include <linux/pm_runtime.h>
> >
> > #include <linux/mmc/host.h>
> > #include <linux/mmc/card.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sd.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> > +#include "card.h"
> > #include "core.h"
> > #include "bus.h"
> > #include "sd.h"
> > +#include "sd_ops.h"
> > #include "mmc_ops.h"
> >
> > +#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US (1 * 1000) /* 1ms */
> > +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100 /* 100ms */
>
> Nitpick: The comments "1ms" and "100ms" are kind of superfluous, due
> to the well chosen names for the defines. Perhaps drop them?
>
Hi, Ulf
I will drop this in version 13.
Thanks, Victor Shih
> > +
> > static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> >
> > +struct sd_uhs2_wait_active_state_data {
> > + struct mmc_host *host;
> > + struct mmc_command *cmd;
> > +};
> > +
> > static int sd_uhs2_power_up(struct mmc_host *host)
> > {
> > int err;
> > @@ -44,10 +72,49 @@ static int sd_uhs2_power_off(struct mmc_host *host)
> > host->ios.clock = 0;
> > host->ios.timing = MMC_TIMING_LEGACY;
> > host->ios.power_mode = MMC_POWER_OFF;
> > + if (host->flags & MMC_UHS2_SD_TRAN)
>
> There is no need to check that the flag/bit is set. Just clear it.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + host->flags &= ~MMC_UHS2_SD_TRAN;
> >
> > return host->ops->uhs2_control(host, UHS2_SET_IOS);
> > }
> >
> > +/*
> > + * sd_uhs2_cmd_assemble() - build up UHS-II command packet which is embedded in
> > + * mmc_command structure
> > + * @cmd: MMC command to executed
> > + * @uhs2_cmd: UHS2 command corresponded to MMC command
> > + * @header: Header field of UHS-II command cxpacket
> > + * @arg: Argument field of UHS-II command packet
> > + * @payload: Payload field of UHS-II command packet
> > + * @plen: Payload length
> > + * @resp: Response buffer is allocated by caller and it is used to keep
> > + * the response of CM-TRAN command. For SD-TRAN command, uhs2_resp
> > + * should be null and SD-TRAN command response should be stored in
> > + * resp of mmc_command.
> > + * @resp_len: Response buffer length
> > + *
> > + * The uhs2_command structure contains message packets which are transmited/
> > + * received on UHS-II bus. This function fills in the contents of uhs2_command
> > + * structure and embededs UHS2 command into mmc_command structure, which is used
> > + * in legacy SD operation functions.
> > + *
> > + */
> > +static void sd_uhs2_cmd_assemble(struct mmc_command *cmd,
> > + struct uhs2_command *uhs2_cmd,
> > + u16 header, u16 arg, __be32 *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;
> > +}
> > +
> > /*
> > * Run the phy initialization sequence, which mainly relies on the UHS-II host
> > * to check that we reach the expected electrical state, between the host and
> > @@ -55,7 +122,15 @@ static int sd_uhs2_power_off(struct mmc_host *host)
> > */
> > static int sd_uhs2_phy_init(struct mmc_host *host)
> > {
> > - return 0;
> > + int err = 0;
>
> No need to assign err to 0.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > +
> > + err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
> > + if (err) {
> > + pr_err("%s: failed to initial phy for UHS-II!\n",
> > + mmc_hostname(host));
> > + }
> > +
> > + return err;
> > }
> >
> > /*
> > @@ -64,6 +139,82 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> > */
> > static int sd_uhs2_dev_init(struct mmc_host *host)
> > {
> > + struct mmc_command cmd = {0};
> > + struct uhs2_command uhs2_cmd = {};
> > + u32 cnt;
> > + u32 dap, gap, resp_gap;
> > + u16 header, arg;
> > + __be32 payload[UHS2_DEV_INIT_PAYLOAD_LEN];
> > + u8 gd = 0;
> > + u8 resp[UHS2_DEV_ENUM_RESP_LEN] = {0};
> > + int err;
> > +
> > + dap = host->uhs2_caps.dap;
> > + gap = host->uhs2_caps.gap;
> > +
> > + /*
> > + * Refer to UHS-II Addendum Version 1.02 Figure 6-21 to see DEVICE_INIT CCMD format.
> > + * Head:
> > + * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
> > + * - IOADR = CMD_BASE + 002h
> > + * Payload:
> > + * - bit [3:0] : GAP(Group Allocated Power)
> > + * - bit [7:4] : GD(Group Descriptor)
> > + * - bit [11] : Complete Flag
> > + * - bit [15:12]: DAP(Device Allocated Power)
> > + */
> > + 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);
> > +
> > + /*
> > + * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > + * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > + * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > + * 1 second.
> > + */
> > + cmd.busy_timeout = 1000;
> > +
> > + /*
> > + * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > + * When the number of the DEVICE_INIT commands is reach to
> > + * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > + * and regard it as an error.
>
> To clarify the above comment a bit, may I suggest we rephrase this into:
>
> Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> Let's retry the DEVICE_INIT command no more than 30 times.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + */
> > + for (cnt = 0; cnt < 30; cnt++) {
> > + payload[0] = ((dap & 0xF) << 12) |
> > + UHS2_DEV_INIT_COMPLETE_FLAG |
> > + ((gd & 0xF) << 4) |
> > + (gap & 0xF);
> > +
> > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,
> > + payload, UHS2_DEV_INIT_PAYLOAD_LEN,
> > + resp, UHS2_DEV_INIT_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 err;
> > + }
> > +
> > + 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) {
> > + host->uhs2_caps.group_desc = gd;
> > + return 0;
> > + }
> > + resp_gap = resp[4] & 0x0F;
> > + if (gap == resp_gap)
> > + gd++;
> > + }
> > +
> > return 0;
> > }
> >
>
> [...]
>
> >
> > -/*
> > - * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> > - * commands/requests to be backwards compatible through the legacy SD protocol.
> > - * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
> > - * be set through a legacy CMD6. Note that, the power limit that becomes set,
> > - * survives a soft reset through the GO_DORMANT_STATE command.
> > - */
> > -static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > +static int sd_uhs2_go_dormant(struct mmc_host *host, u32 node_id)
> > {
> > + struct mmc_command cmd = {0};
> > + struct uhs2_command uhs2_cmd = {};
> > + u16 header, arg;
> > + __be32 payload[1];
> > + int err;
> > +
> > + /* Disable Normal INT */
> > + err = host->ops->uhs2_control(host, UHS2_DISABLE_INT);
> > + if (err) {
> > + pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > + mmc_hostname(host), __func__);
> > + return err;
> > + }
> > +
> > + /*
> > + * Refer to UHS-II Addendum Version 1.02 Figure 6-17 to see GO_DORMANT_STATE CCMD format.
> > + * Header:
> > + * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
> > + * - IOADR = CMD_BASE + 001h
> > + * Payload:
> > + * - bit [7]: HBR(Entry to Hibernate Mode)
> > + * 1: Host intends to enter Hibernate mode during Dormant state.
> > + * The default setting is 0 because hibernate is currently not supported.
> > + */
> > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | 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);
> > +
> > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_GO_DORMANT_PAYLOAD_LEN,
> > + 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 err;
> > + }
> > +
> > + /* Check Dormant State in Present */
> > + err = host->ops->uhs2_control(host, UHS2_CHECK_DORMANT);
> > + if (err)
> > + return err;
> > +
> > + /* Disable UHS2 card clock */
> > + err = host->ops->uhs2_control(host, UHS2_DISABLE_CLK);
> > + if (err)
> > + return err;
> > +
> > + /* Restore sd clock */
> > + mmc_delay(5);
> > + err = host->ops->uhs2_control(host, UHS2_ENABLE_CLK);
> > + if (err)
> > + return err;
> > +
> > + /* Enable Normal INT */
> > + err = host->ops->uhs2_control(host, UHS2_ENABLE_INT);
> > + if (err)
> > + return err;
> > +
> > + /* Detect UHS2 */
> > + err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
> > + if (err)
> > + return err;
> > +
> > + return 0;
> > +}
> > +
> > +static int __sd_uhs2_wait_active_state_cb(void *cb_data, bool *busy)
>
> Please drop the "__" prefix for this function.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > +{
> > + struct sd_uhs2_wait_active_state_data *data = cb_data;
> > + struct mmc_host *host = data->host;
> > + struct mmc_command *cmd = data->cmd;
> > + int err;
> > +
> > + err = mmc_wait_for_cmd(host, cmd, 0);
> > + if (err)
> > + return err;
> > +
> > + if (cmd->resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > + *busy = false;
> > + else
> > + *busy = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int sd_uhs2_go_dormant_state(struct mmc_host *host, u32 node_id)
> > +{
> > + struct mmc_command cmd = {0};
> > + struct uhs2_command uhs2_cmd = {};
> > + u16 header, arg;
> > + int err;
> > + struct sd_uhs2_wait_active_state_data cb_data = {
> > + .host = host,
> > + .cmd = &cmd
> > + };
> > +
> > + err = sd_uhs2_go_dormant(host, node_id);
> > + if (err) {
> > + pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > + mmc_hostname(host), __func__, err);
> > + return err;
> > + }
> > +
> > + /*
> > + * Use Control Read CCMD to check Config Completion(bit 63) in Generic Setting Register.
> > + * - Control Read(R/W=0) with 8-Byte payload(PLEN=10b).
> > + * - IOADR = Generic Setting Register(CFG_BASE + 008h)
> > + *
> > + * When UHS-II card been switched to new speed mode, it will set Config Completion to 1.
> > + */
> > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > + UHS2_NATIVE_CMD_READ |
> > + UHS2_NATIVE_CMD_PLEN_8B |
> > + (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +
> > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > + err = __mmc_poll_for_busy(host, UHS2_WAIT_CFG_COMPLETE_PERIOD_US,
> > + UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS,
> > + &__sd_uhs2_wait_active_state_cb, &cb_data);
> > + if (err) {
> > + pr_err("%s: %s: Not switch to Active in 100 ms\n", mmc_hostname(host), __func__);
> > + return err;
> > + }
> > +
> > return 0;
> > }
> >
> > +static void sd_uhs2_remove(struct mmc_host *host)
> > +{
> > + mmc_remove_card(host->card);
> > + host->card = NULL;
> > +}
> > +
> > /*
> > * Allocate the data structure for the mmc_card and run the UHS-II specific
> > * initialization sequence.
> > */
> > -static int sd_uhs2_init_card(struct mmc_host *host)
> > +static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> > {
> > struct mmc_card *card;
> > u32 node_id;
> > @@ -131,9 +834,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > if (err)
> > return err;
> >
> > - card = mmc_alloc_card(host, &sd_type);
> > - if (IS_ERR(card))
> > - return PTR_ERR(card);
> > + if (oldcard) {
> > + card = oldcard;
> > + } else {
> > + card = mmc_alloc_card(host, &sd_type);
> > + if (IS_ERR(card))
> > + return PTR_ERR(card);
> > + }
> > + host->card = card;
>
> I think it would be better to leave the assignment of host->card until
> a bit later in this function.
>
> We don't want to make it look like we need the host->card to be
> assigned to complete the initialization in this function. See more
> about this below.
>
> >
> > card->uhs2_config.node_id = node_id;
> > card->type = MMC_TYPE_SD;
> > @@ -146,18 +854,226 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > if (err)
> > goto err;
> >
> > - host->card = card;
> > + /* If change speed to Range B, need to GO_DORMANT_STATE */
> > + if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
> > + host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
> > + err = sd_uhs2_go_dormant_state(host, node_id);
> > + if (err)
> > + return err;
>
> This is wrong, as we need to check if we should free the allocated
> struct mmc_card too.
>
> A "goto err;" should fix this.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + }
> > +
> > + host->flags |= MMC_UHS2_SD_TRAN;
> > +
> > return 0;
> >
> > err:
> > - mmc_remove_card(card);
> > + sd_uhs2_remove(host);
>
> If we have an "oldcard" we must not remove it in this path as it
> simply doesn't work. In this case the removal is managed through the
> mmc_rescan() work, when it calls the bus_ops->detect() callback
> (sd_uhs2_detect()).
>
> So we need to add a check for "!oldcard" and then we should call
> mmc_remove_card() instead. That is also why it makes sense to assign
> host->card as late as possible, just before returning 0 a few lines
> above.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > return err;
> > }
> >
> > -static void sd_uhs2_remove(struct mmc_host *host)
> > +int sd_uhs2_reinit(struct mmc_host *host)
> > {
> > - mmc_remove_card(host->card);
> > - host->card = NULL;
> > + struct mmc_card *card = host->card;
> > + int err;
> > +
> > + sd_uhs2_power_up(host);
> > + err = sd_uhs2_phy_init(host);
> > + if (err)
> > + return err;
> > +
> > + err = sd_uhs2_init_card(host, card);
> > + if (err)
> > + return err;
>
> By looking at the above there seems to be some pieces missing to make this work.
>
> *) We need to re-initialize the card to work in SD_TRAN again, hence
> we need to call sd_uhs2_legacy_init() and make it work for this case
> too.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> **) If the card gets removed and replaced by another card during
> system suspend, we must make sure to drop the original allocated
> struct mmc_card. For legacy SD cards we compare the cid register and
> if there is no match, we return an error during system resume and
> leave the card to be removed by mmc_rescan(). Perhaps we can use a
> similar approach for uhs2 or maybe something more clever?
>
Hi, Ulf
Sorry, I don't have any ideas on this, could you please give me some ideas?
Thanks, Victor Shih
> > +
> > + mmc_card_set_present(card);
>
> No, this is wrong. This is already managed by mmc_add_card() that has
> already been called for the card earlier.
>
Hi, Ulf
I'm not sure what you mean, do I need to drop this(mmc_card_set_present(card))?
Thanks, Victor Shih
> > + return err;
> > +}
> > +
> > +/*
> > + * Mask off any voltages we don't support and select
> > + * the lowest voltage
> > + */
> > +u32 sd_uhs2_select_voltage(struct mmc_host *host, u32 ocr)
>
> As discussed earlier, it's better to use mmc_select_voltage() for uhs2
> too. So, please drop this function altogether.
>
> To make mmc_select_voltage() to work for uhs2, we should prevent the
> path for MMC_CAP2_FULL_PWR_CYCLE. In other words something like the
> below "diff" in mmc_select_voltage() should work, I think.
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index f5dc653eafb0..92a6e3c762af 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -1135,7 +1135,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
> return 0;
> }
>
> - if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> + if (!mmc_card_uhs2(host) && host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> bit = ffs(ocr) - 1;
> ocr &= 3 << bit;
> mmc_power_cycle(host, ocr);
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > +{
> > + int bit;
> > + int err;
> > +
> > + /*
> > + * Sanity check the voltages that the card claims to
> > + * support.
> > + */
> > + if (ocr & 0x7F) {
> > + dev_warn(mmc_dev(host), "card claims to support voltages below defined range\n");
> > + ocr &= ~0x7F;
> > + }
> > +
> > + ocr &= host->ocr_avail;
> > + if (!ocr) {
> > + dev_warn(mmc_dev(host), "no support for card's volts\n");
> > + return 0;
> > + }
> > +
> > + if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> > + bit = ffs(ocr) - 1;
> > + ocr &= 3 << bit;
> > + /* Power cycle */
> > + err = sd_uhs2_power_off(host);
> > + if (err)
> > + return 0;
> > + err = sd_uhs2_reinit(host);
> > + if (err)
> > + return 0;
> > + } else {
> > + bit = fls(ocr) - 1;
> > + ocr &= 3 << bit;
> > + if (bit != host->ios.vdd)
> > + dev_warn(mmc_dev(host), "exceeding card's volts\n");
> > + }
> > +
> > + return ocr;
> > +}
> > +
> > +/*
> > + * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> > + * commands/requests to be backwards compatible through the legacy SD protocol.
> > + * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
> > + * be set through a legacy CMD6. Note that, the power limit that becomes set,
> > + * survives a soft reset through the GO_DORMANT_STATE command.
> > + */
> > +static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > +{
> > + int err;
> > + u32 cid[4];
> > + u32 ocr;
> > + u32 rocr;
> > + u8 *status;
> > + int ro;
> > +
> > + /* Send CMD0 to reset SD card */
> > + err = __mmc_go_idle(host);
> > + if (err)
> > + return err;
> > +
> > + mmc_delay(1);
> > +
> > + /* Send CMD8 to communicate SD interface operation condition */
> > + err = mmc_send_if_cond(host, host->ocr_avail);
> > + if (err) {
> > + dev_warn(mmc_dev(host), "CMD8 error\n");
> > + goto err;
> > + }
> > +
> > + /*
> > + * Probe SD card working voltage.
> > + */
> > + err = mmc_send_app_op_cond(host, 0, &ocr);
> > + if (err)
> > + goto err;
> > +
> > + card->ocr = ocr;
>
> I don't think this is the ocr we should store in the card->ocr.
> Instead it should be the ocr that has been negotiated through the
> mmc_select_voltage().
>
Hi, Ulf
Sorry, I'm not sure what you mean on this, could you please give me some ideas?
Thanks, Victor Shih
> > +
> > + /*
> > + * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > + * these bits as being in-valid and especially also bit7.
> > + */
> > + ocr &= ~0x7FFF;
> > + rocr = sd_uhs2_select_voltage(host, ocr);
> > + /*
> > + * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > + * ocr value to rocr.
> > + */
>
> That sounds weird to me. What card did this happen for? Did you print
> the ocr that you received from the card and compared it with
> host->ocr_avail?
>
> In any case, if I understand correctly, this will trigger
> sd_uhs2_select_voltage() to print "no support for card's volts",
> right?
>
> If this is a common case for uhs2 cards, maybe we should teach
> mmc_select_voltage() about this problem too. We don't want to print
> error messages to the log to worry the user, unless it's really a
> problem.
>
Hi, Ulf
I will continue to check this part in version 13.
Thanks, Victor Shih
> > + if (!rocr)
> > + rocr = host->ocr_avail;
> > +
> > + rocr |= (SD_OCR_CCS | SD_OCR_XPC);
> > +
> > + /* Wait SD power on ready */
> > + ocr = rocr;
> > +
> > + err = mmc_send_app_op_cond(host, ocr, &rocr);
> > + if (err)
> > + goto err;
> > +
> > + err = mmc_send_cid(host, cid);
> > + if (err)
> > + goto err;
> > +
> > + memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > + mmc_decode_cid(card);
> > +
> > + /*
> > + * For native busses: get card RCA and quit open drain mode.
> > + */
> > + err = mmc_send_relative_addr(host, &card->rca);
> > + if (err)
> > + goto err;
> > +
> > + err = mmc_sd_get_csd(card);
> > + if (err)
> > + goto err;
> > +
> > + /*
> > + * Select card, as all following commands rely on that.
> > + */
> > + err = mmc_select_card(card);
> > + if (err)
> > + goto err;
> > +
> > + /*
> > + * Fetch SCR from card.
> > + */
> > + err = mmc_app_send_scr(card);
> > + if (err)
> > + goto err;
> > +
> > + err = mmc_decode_scr(card);
> > + if (err)
> > + goto err;
> > +
> > + /*
> > + * Switch to high power consumption mode.
> > + * Even switch failed, sd card can still work at lower power consumption mode, but
> > + * performance will be lower than high power consumption mode.
> > + */
> > + status = kmalloc(64, GFP_KERNEL);
> > + if (!status)
> > + return -ENOMEM;
> > +
> > + if (!(card->csd.cmdclass & CCC_SWITCH)) {
> > + pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
> > + mmc_hostname(card->host));
> > + } else {
> > + /* send CMD6 to set Maximum Power Consumption to get better performance */
> > + err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> > + if (!err)
> > + err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> > +
> > + err = 0;
> > + }
> > +
> > + /*
> > + * Check if read-only switch is active.
> > + */
> > + ro = mmc_sd_get_ro(host);
> > + if (ro < 0) {
> > + pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > + mmc_hostname(host));
> > + } else if (ro > 0) {
> > + mmc_card_set_readonly(card);
> > + }
> > +
> > + /*
> > + * NOTE:
> > + * Should we read Externsion Register to check power notification feature here?
> > + */
>
> Yes, that seems reasonable.
>
> Although, I think it's a good idea to make the change as an
> incremental patch on top of $subject patch. In fact, the whole system
> wide suspend/resume and runtime PM support look far from ready to me.
> Maybe I am wrong - or did you test those code paths too?
>
> That said, as an option to more easily move things forward, may I
> suggest that you move the implementation of the entire PM support into
> incremental patches on top of $subject patch?
>
> To make it clear, I am referring to sd_uhs2_runtime_suspend,
> sd_uhs2_runtime_resume, sd_uhs2_shutdown, sd_uhs2_suspend,
> sd_uhs2_resume, sd_uhs2_hw_reset, sd_uhs2_reinit.
>
> In this way, we could perhaps get the foundation for UHS2 support in
> place while we continue to work on the PM parts as improvements on
> top. What do you think?
>
> > +
> > + kfree(status);
> > +
> > + return 0;
> > +
> > +err:
> > + sd_uhs2_remove(host);
> > + return err;
> > }
> >
> > static int sd_uhs2_alive(struct mmc_host *host)
> > @@ -179,38 +1095,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
> > mmc_claim_host(host);
> > mmc_detach_bus(host);
> > sd_uhs2_power_off(host);
> > + host->flags &= ~MMC_UHS2_SUPPORT;
> > mmc_release_host(host);
> > }
> > }
>
> [...]
>
> > -static int sd_uhs2_shutdown(struct mmc_host *host)
> > +static int sd_uhs2_hw_reset(struct mmc_host *host)
> > {
> > - return 0;
> > + int err;
> > +
> > + sd_uhs2_power_off(host);
> > + /* Wait at least 1 ms according to SD spec */
> > + mmc_delay(1);
> > + sd_uhs2_power_up(host);
> > +
> > + err = sd_uhs2_reinit(host);
>
> Unless I am missing something, sd_uhs2_reinit() isn't sufficient to
> re-initialize the card. We need to move to SD_TRAN too, right?
>
> > +
> > + return err;
> > }
> >
> > -static int sd_uhs2_hw_reset(struct mmc_host *host)
> > +/*
> > + * mmc_uhs2_prepare_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
> > + */
> > +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
> > {
> > - return 0;
> > + struct mmc_command *cmd;
> > + struct uhs2_command *uhs2_cmd;
> > + u16 header, arg;
> > + __be32 *payload;
> > + u8 plen;
> > +
> > + cmd = mrq->cmd;
> > + header = host->card->uhs2_config.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;
> > + if (host->uhs2_app_cmd) {
> > + arg |= UHS2_SD_CMD_APP;
> > + host->uhs2_app_cmd = false;
> > + }
> > +
> > + /*
> > + * UHS-II Addendum 7.2.1.2
> > + * Host may set DM to 1 for DCMD which supports multi-block read/write regardless of
> > + * data transfer length (e.g., CMD18, CMD25). Otherwise, it shall not set DM to 1.
> > + * (e.g., CMD6, CMD17, CMD24). These rules are also applied to other multi-block read/write
> > + * commands defined in other Part of SD specifications (for example, Host may set DM to 1
> > + * for ACMD18 or ACMD25).
> > + */
> > + if (mmc_op_multi(cmd->opcode))
> > + cmd->uhs2_cmd->tmode_half_duplex = mmc_card_uhs2_hd_mode(host);
> > + else
> > + cmd->uhs2_cmd->tmode_half_duplex = 0;
> > +
> > + 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_cmd->tmode_half_duplex) {
> > + if (mmc_card_uhs2_hd_mode(host))
> > + 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] = cpu_to_be32(cmd->data->blksz);
>
> This case isn't supported by the mmc block device driver.
>
> Perhaps this is this needed to support the uhs2 card initialization
> sequence? If not, I would prefer if we can drop this path/code.
>
Hi, Ulf
I will continue to check this part in version 13.
Thanks, Victor Shih
> > + } else {
> > + payload[1] = cpu_to_be32(cmd->data->blocks);
> > + }
> > + } else {
> > + plen = 1;
> > + }
> > +
> > + payload[0] = cpu_to_be32(cmd->arg);
> > + sd_uhs2_cmd_assemble(cmd, uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > }
> >
> > static const struct mmc_bus_ops sd_uhs2_ops = {
> > @@ -221,7 +1293,7 @@ static const struct mmc_bus_ops sd_uhs2_ops = {
> > .resume = sd_uhs2_resume,
> > .runtime_suspend = sd_uhs2_runtime_suspend,
> > .runtime_resume = sd_uhs2_runtime_resume,
> > - .shutdown = sd_uhs2_shutdown,
> > + .shutdown = sd_uhs2_suspend,
> > .hw_reset = sd_uhs2_hw_reset,
> > };
> >
> > @@ -229,6 +1301,8 @@ static int sd_uhs2_attach(struct mmc_host *host)
> > {
> > int err;
> >
> > + host->flags |= MMC_UHS2_SUPPORT;
> > +
> > err = sd_uhs2_power_up(host);
> > if (err)
> > goto err;
> > @@ -237,7 +1311,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
> > if (err)
> > goto err;
> >
> > - err = sd_uhs2_init_card(host);
> > + err = sd_uhs2_init_card(host, NULL);
> > if (err)
> > goto err;
> >
> > @@ -254,21 +1328,32 @@ static int sd_uhs2_attach(struct mmc_host *host)
> > goto remove_card;
> >
> > mmc_claim_host(host);
> > +
> > return 0;
> >
> > remove_card:
> > - mmc_remove_card(host->card);
> > - host->card = NULL;
> > + sd_uhs2_remove(host);
> > mmc_claim_host(host);
> > - mmc_detach_bus(host);
> > +
> > err:
> > + mmc_detach_bus(host);
> > sd_uhs2_power_off(host);
> > + host->flags &= ~MMC_UHS2_SUPPORT;
> > return err;
> > }
> >
> > +/**
> > + * mmc_attach_sd_uhs2 - select UHS2 interface
> > + * @host: MMC host
> > + *
> > + * 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_attach_sd_uhs2(struct mmc_host *host)
> > {
> > - int i, err = 0;
> > + int i, err;
> >
> > if (!(host->caps2 & MMC_CAP2_SD_UHS2))
> > return -EOPNOTSUPP;
> > @@ -283,6 +1368,9 @@ int mmc_attach_sd_uhs2(struct mmc_host *host)
> > */
> > for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) {
> > host->f_init = sd_uhs2_freqs[i];
> > + pr_info("%s: %s: trying to init UHS-II card at %u Hz\n",
> > + mmc_hostname(host), __func__, host->f_init);
> > +
>
> Please move this to pr_dbg.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > err = sd_uhs2_attach(host);
> > if (!err)
> > break;
> > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> > index 6efec0b9820c..4623651037dd 100644
> > --- a/include/linux/mmc/core.h
> > +++ b/include/linux/mmc/core.h
> > @@ -23,6 +23,15 @@ enum mmc_blk_status {
> > MMC_BLK_NEW_REQUEST,
> > };
> >
> > +struct uhs2_command {
> > + u16 header;
> > + u16 arg;
> > + __be32 *payload;
> > + u32 payload_len;
> > + u32 packet_len;
> > + u8 tmode_half_duplex;
> > +};
> > +
> > struct mmc_command {
> > u32 opcode;
> > u32 arg;
> > @@ -109,6 +118,10 @@ 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 */
>
> I think it would be better to keep the responses (uhs2_resp,
> uhs2_resp_len) into the struct uhs2_command too. In this way,
> everything for uhs2 is kept behind one single pointer, the ->uhs2_cmd.
>
> Would that work?
>
Hi, Ulf
I tried it and this doesn't work.
Thanks, Victor Shih
> > };
> >
> > struct mmc_data {
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index 0d1a3e9ad93c..6c8258310641 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -126,6 +126,13 @@ struct sd_uhs2_caps {
> > };
> >
> > enum sd_uhs2_operation {
> > + UHS2_PHY_INIT = 0,
> > + UHS2_SET_CONFIG,
> > + UHS2_ENABLE_INT,
> > + UHS2_DISABLE_INT,
> > + UHS2_ENABLE_CLK,
> > + UHS2_DISABLE_CLK,
> > + UHS2_CHECK_DORMANT,
> > UHS2_SET_IOS,
> > };
> >
> > @@ -476,6 +483,11 @@ struct mmc_host {
> > #endif
> > #define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */
> >
> > + int flags;
> > +#define MMC_UHS2_SUPPORT (1 << 0)
> > +#define MMC_UHS2_SD_TRAN (1 << 1)
> > +
> > + bool uhs2_app_cmd; /* Host UHS-II APP Command */
> > struct sd_uhs2_caps uhs2_caps; /* Host UHS-II capabilities */
> >
> > int fixed_drv_type; /* fixed driver type for non-removable media */
> > @@ -728,6 +740,12 @@ static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
> > host->err_stats[stat] += 1;
> > }
> >
> > +static inline int mmc_card_uhs2_hd_mode(struct mmc_host *host)
> > +{
> > + return host->ios.timing == MMC_TIMING_UHS2_SPEED_A_HD ||
> > + host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD;
> > +}
> > +
> > int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp);
> > int mmc_send_status(struct mmc_card *card, u32 *status);
> > int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
>
> Kind regards
> Uffe
On Tue, Oct 3, 2023 at 5:46 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > This is a UHS-II version of sdhci's set_power operation.
> > VDD2, as well as VDD, is handled here.
>
> Okay, but why?
>
> Please justify the change in the commit messages, don't just tell what
> is being done. This applied to the whole series - and I believe I have
> said this before too.
>
Hi, Ulf
I will try to update this in version 13.
Thanks, Victor Shih
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > Acked-by: Adrian Hunter <[email protected]>
> > ---
> >
> > Updates in V10:
> > - Move some definitions of PatchV9[05/23] to PatchV10[11/23].
> >
> > Updates in V9:
> > - Modify annotations in sdhci_get_vdd_value().
> >
> > Updates in V8:
> > - Adjust the position of matching brackets.
> > - Add the initial value of the pwr in sdhci_uhs2_set_power().
> >
> > Updates in V7:
> > - Add clear the power reg before setting a new value
> > in sdhci_uhs2_set_power().
> > - Add MMC_VDD_34_35 case and MMC_VDD_35_36 case in sdhci_get_vdd_value().
> > - Drop pwr variable in sdhci_get_vdd_value().
> >
> > Updates in V6:
> > - Add mmc_opt_regulator_set_ocr().
> > - Remove unnecessary functions.
> >
> > ---
> >
> > drivers/mmc/host/sdhci-uhs2.c | 48 +++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 61 +++++++++++++++++++----------------
> > drivers/mmc/host/sdhci.h | 1 +
> > include/linux/mmc/host.h | 1 +
> > 4 files changed, 83 insertions(+), 28 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index dfc80a7f1bad..fc37a34629c2 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -57,6 +57,13 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
> > * *
> > \*****************************************************************************/
> >
> > +static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
> > + struct regulator *supply,
> > + unsigned short vdd_bit)
> > +{
> > + return IS_ERR_OR_NULL(supply) ? 0 : mmc_regulator_set_ocr(mmc, supply, vdd_bit);
> > +}
> > +
> > bool sdhci_uhs2_mode(struct sdhci_host *host)
> > {
> > return host->mmc->flags & MMC_UHS2_SUPPORT;
> > @@ -94,6 +101,47 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
> > }
> > EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
> >
> > +static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
> > +{
> > + struct mmc_host *mmc = host->mmc;
> > + u8 pwr = 0;
> > +
> > + 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);
> > +
> > + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> > + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc2, 0);
>
> We added mmc_regulator_set_vqmmc2() in patch4. Please use that instead.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + } else {
> > + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
> > + /* support 1.8v only for now */
> > + mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc2, fls(MMC_VDD_165_195) - 1);
> > +
> > + /* Clear the power reg before setting a new value */
> > + sdhci_writeb(host, 0, 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);
> > + }
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 753b251179f2..eca54a16e7fc 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -23,7 +23,7 @@
> > #include <linux/regulator/consumer.h>
> > #include <linux/pm_runtime.h>
> > #include <linux/of.h>
> > -
> > +#include <linux/bug.h>
> > #include <linux/leds.h>
> >
> > #include <linux/mmc/mmc.h>
> > @@ -2061,41 +2061,46 @@ 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)
> > +{
> > + 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:
> > + return SDHCI_POWER_180;
> > + case MMC_VDD_29_30:
> > + case MMC_VDD_30_31:
> > + return SDHCI_POWER_300;
> > + case MMC_VDD_32_33:
> > + case MMC_VDD_33_34:
> > + /*
> > + * 3.4V ~ 3.6V are valid only for those platforms where it's
> > + * known that the voltage range is supported by hardware.
> > + */
> > + case MMC_VDD_34_35:
> > + case MMC_VDD_35_36:
> > + return SDHCI_POWER_330;
> > + default:
> > + return 0;
> > + }
> > +}
> > +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:
> > - /*
> > - * 3.4 ~ 3.6V are valid only for those platforms where it's
> > - * known that the voltage range is supported by hardware.
> > - */
> > - case MMC_VDD_34_35:
> > - case MMC_VDD_35_36:
> > - 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;
> > }
> > }
> >
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 43ad3f4b7672..f3bd558b337f 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -837,6 +837,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);
> > int sdhci_get_cd_nogpio(struct mmc_host *mmc);
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index 6c8258310641..610644a0ace5 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -363,6 +363,7 @@ struct mmc_pwrseq;
> >
> > struct mmc_supply {
> > struct regulator *vmmc; /* Card power supply */
> > + struct regulator *vmmc2; /* UHS2 VDD2 power supply */
>
> In patch4 we added vqmmc2. Please use that instead.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > struct regulator *vqmmc; /* Optional Vccq supply */
> > struct regulator *vqmmc2; /* Optional supply for phy */
> > };
> > --
> > 2.25.1
> >
>
> Kind regards
> Uffe
On Fri, Oct 6, 2023 at 6:51 PM Adrian Hunter <[email protected]> wrote:
>
> On 6/10/23 13:30, Victor Shih wrote:
> > On Tue, Oct 3, 2023 at 5:58 PM Ulf Hansson <[email protected]> wrote:
> >>
> >> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >>>
> >>> From: Victor Shih <[email protected]>
> >>>
> >>> For UHS2, the signal voltage is supplied by vdd2 which is already 1.8v,
> >>> so no voltage switch required.
> >>
> >> Can you please elaborate on this? I don't get anything of the above, sorry.
> >>
> >>>
> >>> Signed-off-by: Ben Chuang <[email protected]>
> >>> Signed-off-by: AKASHI Takahiro <[email protected]>
> >>> Signed-off-by: Victor Shih <[email protected]>
> >>> Acked-by: Adrian Hunter <[email protected]>
> >>> ---
> >>>
> >>> Updates in V5:
> >>> - Use sdhci_uhs2_mode() to simplify code in
> >>> sdhci_uhs2_start_signal_voltage_switch().
> >>>
> >>> ---
> >>>
> >>> drivers/mmc/host/sdhci-uhs2.c | 24 ++++++++++++++++++++++++
> >>> 1 file changed, 24 insertions(+)
> >>>
> >>> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> >>> index fc37a34629c2..92fb69b7e209 100644
> >>> --- a/drivers/mmc/host/sdhci-uhs2.c
> >>> +++ b/drivers/mmc/host/sdhci-uhs2.c
> >>> @@ -142,6 +142,27 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> >>> }
> >>> }
> >>>
> >>> +/*****************************************************************************\
> >>> + * *
> >>> + * 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 (sdhci_uhs2_mode(host))
> >>> + return 0;
> >>
> >> This is just wrong. If we are initializing a uhs2 card, we certainly
> >> should call ->start_signal_voltage_switch() callback at all. This is
> >> for UHS-I cards, right?
> >>
> >
> > Hi, Ulf
> >
> > UHS-II does not need single_voltage.
> > I will modify the commit message in the next version.
> > sdhci_uhs2_start_signal_voltage_switch() is under
> > mmc_host_ops.start_signal_voltage_switch host ops,
> > therefore, we need to keep the UHS-I path here.
>
> You should be able to leave out the patch entirely
> because ->start_signal_voltage_switch() is not called
> for UHS2 mode
>
Hi, Ulf and Adrian
I will drop this patch in version 13.
Thanks, Victor Shih
> >
> > Thanks, Victor Shih
> >
> >>> +
> >>> + return sdhci_start_signal_voltage_switch(mmc, ios);
> >>> +}
> >>> +
> >>> /*****************************************************************************\
> >>> * *
> >>> * Driver init/exit *
> >>> @@ -150,6 +171,9 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> >>>
> >>> 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;
> >>> }
> >>>
> >>
> >> Kind regards
> >> Uffe
>
On Tue, Oct 3, 2023 at 7:13 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > This is a sdhci version of mmc's uhs2_[enable|disable]_clk operations.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > Acked-by: Adrian Hunter <[email protected]>
>
> The similar comments as posted for patch 15 applies to $subject patch
> too. Please have a look at those and fix these for the $subject patch
> too.
>
Hi, Ulf
I will merge patch#15, patch#16 and patch#17 into one patch(patch#15)
for version 13.
Thanks, Victor Shih
> > ---
> >
> > Updates in V8:
> > - Remove unnecessary include file.
> > - read_poll_timeout() to instead of read_poll_timeout_atomic()
> > in sdhci_uhs2_enable_clk().
> > - Put the comment on the end and put the lines in descending
> > line length in sdhci_uhs2_enable_clk().
> > - Modify return value in sdhci_uhs2_enable_clk().
> >
> > Updates in V6:
> > - Remove unnecessary functions.
> >
> > ---
> >
> > drivers/mmc/host/sdhci-uhs2.c | 30 ++++++++++++++++++++++++++++++
> > 1 file changed, 30 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 4c2a56629ab3..af1b0c5e48fd 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -329,6 +329,36 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > return 0;
> > }
> >
> > +static int 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);
> > +
> > + return 0;
> > +}
> > +
> > +static int sdhci_uhs2_enable_clk(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > + int timeout_us = 20000; /* 20ms */
> > + u32 val;
> > +
> > + clk |= SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > + if (read_poll_timeout(sdhci_readw, val, (val & SDHCI_CLOCK_INT_STABLE),
> > + 10, timeout_us, true, host, SDHCI_CLOCK_CONTROL)) {
> > + pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return -EIO;
> > + }
> > + return 0;
> > +}
> > +
> > /*****************************************************************************\
> > * *
> > * Driver init/exit *
>
> Kind regards
> Uffe
On Tue, Oct 3, 2023 at 7:10 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > 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.
>
> $subject patch is adding a bunch of static functions, which isn't
> really being used until later. If you compile this patch it will
> trigger warnings about unused function, we don't want that. Each patch
> in the series should build nicely without warning and errors.
>
> To deal with these problems, I suggest that you move the introduction
> of the sdhci_uhs2_control() from patch17 to $subject patch - or
> possibly make that as a standalone patch, preceeding $subject patch.
> Step by step you can then add support for each of the "enum
> sd_uhs2_operation" to sdhci_uhs2_control().
>
> Moreover, please work at the commit message a bit, it's not entirely
> easy to understand by reading what goes on here.
>
Hi, Ulf
I will merge patch#15, patch#16 and patch#17 into one patch(patch#15)
for version 13.
Thanks, Victor Shih
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > Acked-by: Adrian Hunter <[email protected]>
> > ---
> >
> > Updates in V8:
> > - usleep_range() to instead of udelay() in sdhci_uhs2_interface_detect().
> > - read_poll_timeout() to instead of read_poll_timeout_atomic()
> > in sdhci_uhs2_interface_detect().
> > - Modify return value in sdhci_uhs2_do_detect_init().
> >
> > Updates in V7:
> > - Drop using uhs2_reset ops and use sdhci_uhs2_reset()
> > in sdhci_uhs2_do_detect_init().
> >
> > Updates in V6:
> > - Remove unnecessary functions.
> > - Wrap at 100 columns in some functions.
> >
> > ---
> >
> > drivers/mmc/host/sdhci-uhs2.c | 112 ++++++++++++++++++++++++++++++++++
> > 1 file changed, 112 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index ad791c48f681..4c2a56629ab3 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -335,6 +335,118 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> > * *
> > \*****************************************************************************/
> >
> > +static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
> > +{
> > + int timeout = 100000; /* 100ms */
>
> Please use define instead.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + u32 val;
> > +
> > + usleep_range(50, 200); /* wait for 50us - 200us before check */
>
> Why? Comment?
>
> And use defines.
>
Hi, Ulf
I will drop this in version 13.
Thanks, Victor Shih
> > +
> > + if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT),
> > + 100, timeout, true, host, SDHCI_PRESENT_STATE)) {
> > + pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return -EIO;
> > + }
> > +
> > + /* Enable UHS2 error interrupts */
> > + sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK, SDHCI_UHS2_INT_ERROR_MASK);
> > +
> > + /* 150ms */
> > + timeout = 150000;
>
> Ditto.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC),
> > + 100, timeout, true, host, SDHCI_PRESENT_STATE)) {
> > + pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
> > + sdhci_dumpregs(host);
> > + return -EIO;
> > + }
> > +
> > + 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;
> > +
> > + caps_ptr = sdhci_readw(host, SDHCI_UHS2_CAPS_PTR);
> > + if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
> > + pr_err("%s: SDHCI_UHS2_CAPS_PTR(%d) is wrong.\n",
> > + mmc_hostname(mmc), caps_ptr);
> > + return -ENODEV;
> > + }
> > + caps_gen = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_OFFSET);
> > + caps_phy = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_PHY_OFFSET);
> > + caps_tran[0] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_OFFSET);
> > + caps_tran[1] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_1_OFFSET);
> > +
> > + /* General Caps */
> > + mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_CAPS_DAP_MASK;
> > + mmc->uhs2_caps.gap = FIELD_GET(SDHCI_UHS2_CAPS_GAP_MASK, caps_gen);
> > + mmc->uhs2_caps.n_lanes = FIELD_GET(SDHCI_UHS2_CAPS_LANE_MASK, caps_gen);
> > + mmc->uhs2_caps.addr64 = (caps_gen & SDHCI_UHS2_CAPS_ADDR_64) ? 1 : 0;
> > + mmc->uhs2_caps.card_type = FIELD_GET(SDHCI_UHS2_CAPS_DEV_TYPE_MASK, caps_gen);
> > +
> > + /* PHY Caps */
> > + mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_CAPS_PHY_REV_MASK;
> > + mmc->uhs2_caps.speed_range = FIELD_GET(SDHCI_UHS2_CAPS_PHY_RANGE_MASK, caps_phy);
> > + mmc->uhs2_caps.n_lss_sync = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_SYN_MASK, caps_phy);
> > + mmc->uhs2_caps.n_lss_dir = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_DIR_MASK, caps_phy);
> > + 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_CAPS_TRAN_LINK_REV_MASK;
> > + mmc->uhs2_caps.n_fcu = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_N_FCU_MASK, caps_tran[0]);
> > + if (mmc->uhs2_caps.n_fcu == 0)
> > + mmc->uhs2_caps.n_fcu = 256;
> > + mmc->uhs2_caps.host_type = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_HOST_TYPE_MASK, caps_tran[0]);
> > + mmc->uhs2_caps.maxblk_len = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_BLK_LEN_MASK, caps_tran[0]);
> > + mmc->uhs2_caps.n_data_gap = caps_tran[1] & SDHCI_UHS2_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);
> > +
> > + DBG("Begin do uhs2 detect init.\n");
> > +
> > + if (sdhci_uhs2_interface_detect(host)) {
> > + pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
>
> Does this really deserve a warning to be printed to the log?
>
Hi, Ulf
I have no special opinion on this part. What do you think?
Thanks, Victor Shih
> > + return -EIO;
> > + }
> > +
> > + if (sdhci_uhs2_init(host)) {
> > + pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
> > + return -EIO;
> > + }
> > +
> > + /* 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_INT_ERROR_MASK);
> > + /*
> > + * N.B 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);
> > +
> > + return 0;
> > +}
> > +
> > static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
> > {
> > host->mmc_host_ops.start_signal_voltage_switch =
>
> Kind regards
> Uffe
On Tue, Oct 3, 2023 at 6:55 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > This is a UHS-II version of sdhci's set_timeout() operation.
>
> This needs more explanations and justifications.
>
> Other than that, the patch looks good to me!
>
> Kind regards
> Uffe
>
Hi, Ulf
I will try to update this in version 13.
Thanks, Victor Shih
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > Acked-by: Adrian Hunter <[email protected]>
> > ---
> >
> > Updates in V8:
> > - Initialization be combined with declaration and realigned
> > in sdhci_calc_timeout_uhs2().
> > - Forward declare struct mmc_command in sdhci_uhs2.h.
> >
> > Updates in V6:
> > - Use GENMASK() and FIELD_PREP() in some case.
> > - Use sdhci_uhs2_mode() to simplify code.
> > - Remove unnecessary functions.
> >
> > ---
> >
> > drivers/mmc/host/sdhci-uhs2.c | 72 +++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-uhs2.h | 2 +
> > 2 files changed, 74 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 92fb69b7e209..d519e6ce6199 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/module.h>
> > #include <linux/iopoll.h>
> > +#include <linux/bitfield.h>
> >
> > #include "sdhci.h"
> > #include "sdhci-uhs2.h"
> > @@ -142,6 +143,77 @@ static void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, un
> > }
> > }
> >
> > +static u8 sdhci_calc_timeout_uhs2(struct sdhci_host *host, u8 *cmd_res, u8 *dead_lock)
> > +{
> > + /* timeout in us */
> > + unsigned int dead_lock_timeout = 1 * 1000 * 1000;
> > + unsigned int cmd_res_timeout = 5 * 1000;
> > + unsigned int current_timeout;
> > + u8 count;
> > +
> > + /*
> > + * 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 |= FIELD_PREP(SDHCI_UHS2_TIMER_CTRL_DEADLOCK_MASK, dead_lock);
> > + 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 (sdhci_uhs2_mode(host))
> > + __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 8253d50f7852..ccf4e1834c2d 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.h
> > +++ b/drivers/mmc/host/sdhci-uhs2.h
> > @@ -175,9 +175,11 @@
> > #define SDHCI_UHS2_VENDOR_PTR 0xE8
> >
> > struct sdhci_host;
> > +struct mmc_command;
> >
> > void sdhci_uhs2_dump_regs(struct sdhci_host *host);
> > bool sdhci_uhs2_mode(struct sdhci_host *host);
> > void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
> > +void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
> >
> > #endif /* __SDHCI_UHS2_H */
> > --
> > 2.25.1
> >
On Tue, Oct 3, 2023 at 8:15 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > This is a sdhci version of mmc's request operation.
> > It covers both UHS-I and UHS-II.
>
> Okay, but again, please elaborate on why we need/want this.
>
Hi, Ulf
I will try to update this in version 13.
Thanks, Victor Shih
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > ---
> >
> > Updates in V11:
> > - Drop the check mmc_card_uhs2_hd_mode(host->mmc)
> > in sdhci_uhs2_set_transfer_mode().
> >
> > Updates in V10:
> > - Use tmode_half_duplex to instead of uhs2_tmode0_flag
> > in sdhci_uhs2_set_transfer_mode().
> >
> > Updates in V9:
> > - Modify the annotations in __sdhci_uhs2_send_command().
> >
> > Updates in V8:
> > - Adjust the position of matching brackets in
> > sdhci_uhs2_send_command_retry().
> > - Modify CameCase definition in __sdhci_uhs2_finish_command().
> > - Modify error message in __sdhci_uhs2_finish_command().
> > - sdhci_uhs2_send_command_retry() to instead of sdhci_uhs2_send_command()
> > in sdhci_uhs2_request().
> > - Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_request_atomic().
> > - Add forward declaration for sdhci_send_command().
> >
> > Updates in V7:
> > - Cancel export state of some functions.
> > - Remove unnecessary whitespace changes.
> >
> > Updates in V6:
> > - Add uhs2_dev_cmd() to simplify code.
> > - Remove unnecessary functions.
> > - Cancel export state of some functions.
> > - Drop use CONFIG_MMC_DEBUG().
> > - Wrap at 100 columns in some functions.
> >
> > ---
> >
> > drivers/mmc/host/sdhci-uhs2.c | 412 ++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 49 ++--
> > drivers/mmc/host/sdhci.h | 8 +
> > 3 files changed, 454 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
> > index 09b86fec9f7b..1f8d527424fd 100644
> > --- a/drivers/mmc/host/sdhci-uhs2.c
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
>
> [...]
>
> > +
> > +static void __sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> > +{
> > + int i, j;
> > + int cmd_reg;
> > +
> > + 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, payload (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);
> > +
> > + 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));
>
> We are ignoring what we just read. Isn't there something we need to verify?
>
> Moreover, the whole thing with i,j and the +4 thing above looks a bit
> odd to me. I am not sure whether the above can be simplified, but I
> leave that for you to have a second look at.
>
Hi, Ulf
Sorry, I don't have any ideas on this, could you please give me some ideas?
Thanks, Victor Shih
> > +
> > + cmd_reg = FIELD_PREP(SDHCI_UHS2_CMD_PACK_LEN_MASK, cmd->uhs2_cmd->packet_len);
> > + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
> > + cmd_reg |= SDHCI_UHS2_CMD_DATA;
> > + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> > + cmd_reg |= SDHCI_UHS2_CMD_CMD12;
> > +
> > + /* UHS2 Native ABORT */
> > + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> > + (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT))
> > + cmd_reg |= SDHCI_UHS2_CMD_TRNS_ABORT;
> > +
> > + /* UHS2 Native DORMANT */
> > + if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
> > + (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_GO_DORMANT_STATE))
> > + cmd_reg |= SDHCI_UHS2_CMD_DORMANT;
> > +
> > + DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
> > +
> > + sdhci_writew(host, cmd_reg, SDHCI_UHS2_CMD);
> > +}
>
> [...]
>
> > +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;
>
> Why do we need a retry mechanism at this level? The mmc core sometimes
> retries commands when it seems reasonable, why isn't that sufficient?
>
Hi, Ulf
According to my testing experience, this function is sometimes called
to resend the command.
Thanks, Victor Shih
> > +
> > + 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 the retry is needed, would it be possible to convert into using
> read_poll_timeout() for the above while loop instead? If so, please
> make that conversion.
>
Hi, Ulf
I have no idea how to use read_poll_timeout() instead of while loop,
because there are still other functions running inside the while loop.
Can you give me some ideas?
Thanks, Victor Shih
> > +
> > + 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;
>
> Nitpick: Maybe find some better variable names. Like error_code...
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > + int i;
> > +
> > + if (host->mmc->flags & MMC_UHS2_SD_TRAN) {
> > + 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 response, 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) {
>
> Again we do sdhci_readl above but just ignore the data. I assume
> that's deliberate, as we are probably just interested in the remaining
> pieces.
>
> Moreover, the whole thing with +4 things continues to look a bit odd
> to me. I am not sure whether it can be simplified, but I leave that
> for you to have a second look at.
>
Hi, Ulf
Sorry, I don't have any ideas on this, could you please give me some ideas?
Thanks, Victor Shih
> > + 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);
> > + }
> > + }
> > +}
>
> [...]
>
> > +
> > +void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
>
> static void
>
Hi, Ulf
I will try to update this in version 13.
Thanks, Victor Shih
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + struct mmc_command *cmd;
> > + unsigned long flags;
> > + bool present;
> > +
> > + if (!(sdhci_uhs2_mode(host))) {
> > + sdhci_request(mmc, mrq);
> > + return;
> > + }
> > +
> > + mrq->stop = NULL;
> > + mrq->sbc = NULL;
> > + if (mrq->data)
> > + mrq->data->stop = NULL;
> > +
> > + /* Firstly check card presence */
> > + present = mmc->ops->get_cd(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (sdhci_present_error(host, mrq->cmd, present))
> > + goto out_finish;
> > +
> > + cmd = 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);
>
> Drop this, it's not used outside this module.
>
Hi, Ulf
I will try to update this in version 13.
Thanks, Victor Shih
> > +
> > +int sdhci_uhs2_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
>
> This function is entirely unused. Did you actually test this with an
> hsq enabled host? Or perhaps you have just added this for
> completeness?
>
Hi, Ulf
I will drop this in version 13.
Thanks, Victor Shih
> > +{
> > + struct sdhci_host *host = mmc_priv(mmc);
> > + struct mmc_command *cmd;
> > + unsigned long flags;
> > + int ret = 0;
> > +
> > + if (!sdhci_uhs2_mode(host))
> > + 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 = 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;
> > +
> > +out_finish:
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(sdhci_uhs2_request_atomic);
> > +
>
> [...]
>
> Kind regards
> Uffe
On Thu, Oct 5, 2023 at 7:39 PM Ulf Hansson <[email protected]> wrote:
>
> On Fri, 15 Sept 2023 at 11:44, Victor Shih <[email protected]> wrote:
> >
> > From: Victor Shih <[email protected]>
> >
> > This is a UHS-II version of sdhci's add_host/remove_host operation.
> > Any sdhci drivers which are capable of handling UHS-II cards must
> > call those functions instead of the corresponding sdhci's.
> >
> > Signed-off-by: Ben Chuang <[email protected]>
> > Signed-off-by: AKASHI Takahiro <[email protected]>
> > Signed-off-by: Victor Shih <[email protected]>
> > Acked-by: Adrian Hunter <[email protected]>
>
> [...]
>
> Just a few nitpicks, see below.
>
> > +static void __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
> > +{
> > + struct mmc_host *mmc;
> > + u32 max_current_caps2;
> > +
> > + mmc = host->mmc;
> > +
> > + /* Support UHS2 */
> > + if (caps1 & SDHCI_SUPPORT_UHS2)
> > + mmc->caps2 |= MMC_CAP2_SD_UHS2;
> > +
> > + max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
> > +
> > + if ((caps1 & SDHCI_CAN_VDD2_180) &&
> > + !max_current_caps2 &&
> > + !IS_ERR(mmc->supply.vmmc2)) {
> > + /* UHS2 - VDD2 */
> > + int curr = regulator_get_current_limit(mmc->supply.vmmc2);
>
> As I also stated in another reply, please use vqmmc2 instead, which we
> added in patch4.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > +
> > + 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_CAN_VDD2_180))
> > + mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
> > +}
> > +
> > +static int sdhci_uhs2_host_ops_init(struct sdhci_host *host);
>
> Please try to re-order the code so this declaration isn't needed.
>
Hi, Ulf
I will update this in version 13.
Thanks, Victor Shih
> > +
> > +static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
> > +{
> > + if (!sdhci_uhs2_mode(host))
> > + return;
> > +
> > + if (!dead)
> > + sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
> > +}
> > +
>
> [...]
>
> Kind regards
> Uffe