This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 56070e94df1 drivers/mmcsd: Switch SD cards into high speed.
56070e94df1 is described below

commit 56070e94df14cd43416d57a53dd39c84d70d6d9f
Author: Justin Hammond <[email protected]>
AuthorDate: Mon Aug 3 16:14:55 2026 +0800

    drivers/mmcsd: Switch SD cards into high speed.
    
    The SD path never performs the CMD6 switch its eMMC counterpart has
    performed for years, so an SD card is left in default speed and every
    host clocked accordingly, at 25MHz rather than the 50MHz the card
    supports.  A TODO in this file has asked for it since 2010; this removes
    it.
    
    A host asks for the switch by reporting SDIO_CAPS_SD_HS_MODE, which
    mirrors the eMMC capability beside it.  The switch is attempted once the
    bus is at the default transfer rate and the wide bus is selected, and
    the card's own answer decides the outcome: the 64 byte status block
    reports the function actually selected, and a card that cannot do what
    was asked says so there rather than failing the command.  Cards below
    version 1.10 of the physical layer specification are not asked, since
    CMD6 postdates them.
    
    Only a confirmed switch reaches the host, as the new
    CLOCK_SD_TRANSFER_4BIT_HS rate.  That is a rate rather than a flag on an
    existing one because the host is clocked twice during initialization,
    once before the switch can have happened, and a host that cannot tell
    the two apart would run a card in default speed past its rated 25MHz.
    The enumerator is added last, so no existing driver's switch statement
    changes meaning, and the rate reaches only a host that reported the new
    capability, which none in tree does.
    
    Every failure path is survivable: a card that declines, a card too old
    to ask, and a host that never asks all stay at the default rate.
    
    Documents the two capabilities and the clock rates a lower half has to
    handle.
    
    The MMC/SD documentation was three sentences and a pointer to the SDIO
    page, so it said nothing about how a card is registered, how the bus width
    and clock are negotiated, or what any of the configuration options do.  It
    now covers those, the ioctl interface and /proc/mmcsd, and the high speed
    switch this commit adds is described where somebody looking for it would
    look rather than only in the SDIO lower half page.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Justin Hammond <[email protected]>
---
 Documentation/components/drivers/special/mmcsd.rst | 163 ++++++++++++++++++++-
 Documentation/components/drivers/special/sdio.rst  |  26 ++++
 drivers/mmcsd/mmcsd.h                              |   1 +
 drivers/mmcsd/mmcsd_sdio.c                         | 137 ++++++++++++++++-
 include/nuttx/sdio.h                               |  11 +-
 5 files changed, 329 insertions(+), 9 deletions(-)

diff --git a/Documentation/components/drivers/special/mmcsd.rst 
b/Documentation/components/drivers/special/mmcsd.rst
index 2cd2b5d9d0e..e43090760c9 100644
--- a/Documentation/components/drivers/special/mmcsd.rst
+++ b/Documentation/components/drivers/special/mmcsd.rst
@@ -2,9 +2,168 @@
 MMCSD Device Drivers
 ====================
 
--  ``include/nuttx/mmcsd.h``. All structures and APIs needed to
-   work with MMCSD drivers are provided in this header file.
+NuttX presents an MMC or SD card as a block device, ``/dev/mmcsdN``, which a
+file system is then mounted on. The driver is in two parts: a generic upper
+half that knows the card protocols, and a lower half that moves bytes over
+whichever bus the card is wired to.
+
+-  ``include/nuttx/mmcsd.h``. All structures and APIs needed to work with
+   MMCSD drivers are provided in this header file.
+
+There are two upper halves, because the two buses are not variations of one
+protocol:
+
+-  ``drivers/mmcsd/mmcsd_sdio.c`` drives a card through a real SD/MMC host
+   controller, over the :doc:`SDIO interface <sdio>`. This is the one that can
+   use a wide bus and high speed timing.
+-  ``drivers/mmcsd/mmcsd_spi.c`` drives a card over SPI, which every card
+   supports as a fallback. It is one bit wide, slower, and has no notion of
+   bus width or speed switching.
 
 Implementers of SDIO lower-half drivers should refer to the
 :doc:`SDIO Driver Documentation <sdio>` for details on R2/CSD response
 handling and correctly implementing the lower-half interface.
+
+Registering a card slot
+=======================
+
+Board logic registers one slot per card, once the bus it is on exists. For a
+card on an SD host controller:
+
+.. code-block:: c
+
+   #include <nuttx/mmcsd.h>
+
+   struct sdio_dev_s *sdio = sdio_initialize(0);
+
+   mmcsd_slotinitialize(0, sdio);      /* becomes /dev/mmcsd0 */
+
+The first argument is the minor number, which names the device. For a card
+on SPI instead:
+
+.. code-block:: c
+
+   struct spi_dev_s *spi = board_spibus_initialize(0);
+
+   mmcsd_spislotinitialize(0, 0, spi);
+
+where the second argument is the slot number, meaningful only on boards with
+more than one, and bounded by ``CONFIG_MMCSD_NSLOTS``.
+
+Neither call needs a card to be present. An empty slot is not an error: the
+driver reports the slot as empty, arms the media inserted event and returns
+success, so a board does not have to know at start up whether a card is
+fitted.
+
+Bus width and speed
+===================
+
+A card comes out of reset on a one bit bus at a low clock, because that is
+all a card is guaranteed to answer on before anything is known about it. The
+upper half then negotiates upward, and each step is taken only if the card
+and the host both allow it:
+
+#. **Identification.** ``CLOCK_IDMODE``, below 400kHz, while the card is
+   identified and its CID and CSD are read.
+#. **Bus width.** If the card's SCR says it supports four bits and the host
+   did not report ``SDIO_CAPS_1BIT_ONLY``, ACMD6 widens the bus and the clock
+   moves to ``CLOCK_SD_TRANSFER_4BIT``. Otherwise the card stays at
+   ``CLOCK_SD_TRANSFER_1BIT``.
+#. **High speed timing.** See below.
+
+An eMMC part follows the same shape with its own commands and its own
+``CLOCK_MMC_TRANSFER_*`` rates, and can be eight bits wide where the host
+reports ``SDIO_CAPS_8BIT``.
+
+High speed timing
+-----------------
+
+A card in default speed is rated to 25MHz. High speed timing doubles that,
+and is usually the difference between a card that transfers at 10MB/s and one
+that transfers at 20MB/s.
+
+Getting there needs agreement from three parties, and the upper half checks
+each in turn:
+
+**The host must be able to clock it.** A lower half that can drive 50MHz says
+so by reporting ``SDIO_CAPS_SD_HS_MODE`` from ``SDIO_CAPSET``. A host that
+omits the capability is never asked, and its cards stay at the default rate.
+The capability exists rather than the upper half simply trying, because
+nothing in the card protocol tells the upper half what a particular board's
+wiring will stand.
+
+**The card must support it.** The upper half sends CMD6, SWITCH_FUNC, asking
+for function 1 of function group 1, the access mode group. Every other group
+is passed 0xF, "no influence", so exactly one function is changed and nothing
+else about the card's configuration moves.
+
+**The card must confirm it.** CMD6 returns a 512 bit status data structure,
+in which the field for function group 1 reports the function the card
+actually selected. A card that cannot comply reports 0xF there and the
+command still succeeds, so the R1 response alone proves nothing. Only when
+that field reads 1 does the upper half raise the clock to
+``CLOCK_SD_TRANSFER_4BIT_HS``.
+
+Anything else, at any step, leaves the card at ``CLOCK_SD_TRANSFER_4BIT``.
+That includes a card predating the switch: CMD6 arrived in version 1.10 of
+the physical layer specification, and the SCR says which version a card
+implements, so older cards are not asked at all.
+
+High speed is attempted only on the four bit path. A card narrow enough to
+need the one bit path predates the switch command in any case.
+
+.. note::
+
+   A lower half that reports ``SDIO_CAPS_SD_HS_MODE`` **must** handle
+   ``CLOCK_SD_TRANSFER_4BIT_HS`` in its clock method. The card is clocked
+   before the switch can have happened and again afterwards, so a driver that
+   treated the two rates alike would run a card still in default speed timing
+   past the 25MHz it is rated for.
+
+Configuration
+=============
+
+``CONFIG_MMCSD`` builds the family. The options worth knowing:
+
+============================== ===============================================
+``MMCSD_NSLOTS``               Slots per host, for boards with more than one
+``MMCSD_MMCSUPPORT``           Recognise eMMC and MMC parts, not only SD
+``MMCSD_READONLY``             Refuse writes; the card is still readable
+``MMCSD_MULTIBLOCK_LIMIT``     Largest multi-block transfer, 0 for no limit
+``MMCSD_IOCSUPPORT``           ``MMC_IOC_CMD`` and ``MMC_IOC_MULTI_CMD``
+``MMCSD_HAVE_CARDDETECT``      The board wires a card detect pin
+``MMCSD_HAVE_WRITEPROTECT``    The board wires a write protect pin
+``MMCSD_SPI``                  Build the SPI upper half
+``MMCSD_SPICLOCK``             Clock for the SPI path, default 20MHz
+``MMCSD_PROCFS``               Add ``/proc/mmcsd``
+============================== ===============================================
+
+Passing commands to a card
+==========================
+
+With ``CONFIG_MMCSD_IOCSUPPORT``, ``MMC_IOC_CMD`` sends one command to the
+card and ``MMC_IOC_MULTI_CMD`` sends a sequence of up to
+``MMC_IOC_MAX_CMDS`` (255) of them without releasing the card in between.
+Both take the same descriptor as Linux, ``struct mmc_ioc_cmd``, so tooling
+written against that interface works unchanged. One transfer is bounded by
+``MMC_IOC_MAX_BYTES``, 512KiB.
+
+This is how RPMB and other vendor specific facilities are reached, and it is
+deliberately low level: it hands a command to the card and does not interpret
+the result.
+
+Inspecting a card
+=================
+
+``CONFIG_MMCSD_PROCFS`` adds ``/proc/mmcsd``, with a directory per registered
+card holding:
+
+============= =================================================
+``type``      ``SD`` or ``MMC``
+``cid``       The card identification register, as raw hex
+``csd``       The card specific data register, as raw hex
+============= =================================================
+
+The registers are reported as the card gives them rather than decoded, so
+reading them means having the specification to hand. They are the quickest
+way to tell whether the card the driver found is the card that was expected.
diff --git a/Documentation/components/drivers/special/sdio.rst 
b/Documentation/components/drivers/special/sdio.rst
index ba62f204957..0f780068b0c 100644
--- a/Documentation/components/drivers/special/sdio.rst
+++ b/Documentation/components/drivers/special/sdio.rst
@@ -38,6 +38,32 @@ Implementing an SDIO lower-half
 When implementing a new SDMMC controller driver (SDIO lower-half), it must
 provide the interface defined in ``struct sdio_dev_s``.
 
+High speed timing
+-----------------
+
+A lower-half that can clock a card above the default rate says so in the
+capabilities it reports from ``SDIO_CAPSET``:
+
+* ``SDIO_CAPS_MMC_HS_MODE`` for eMMC, and
+* ``SDIO_CAPS_SD_HS_MODE`` for SD cards.
+
+The upper-half only performs the CMD6 switch that puts a card into high
+speed timing if the corresponding capability is reported, so a host that
+cannot clock 50MHz simply omits it and its cards stay at the default rate.
+
+``SDIO_CLOCK`` is then called with one of two additional rates once the
+card has confirmed the switch:
+
+* ``CLOCK_MMC_TRANSFER_4BIT`` and ``CLOCK_SD_TRANSFER_4BIT`` are the
+  default speed rates, up to 25MHz for SD and 26MHz for eMMC.
+* ``CLOCK_SD_TRANSFER_4BIT_HS`` is high speed, up to 50MHz.
+
+A lower-half reporting ``SDIO_CAPS_SD_HS_MODE`` must handle the high speed
+rate in its clock method.  The card is clocked twice during
+initialization, once before the switch can have happened, so a driver that
+treats the two rates alike would run a card in default speed past the
+25MHz it is rated for.
+
 Call-flow (simplified example)
 ------------------------------
 
diff --git a/drivers/mmcsd/mmcsd.h b/drivers/mmcsd/mmcsd.h
index 648ede8cfa4..df47982a5f4 100644
--- a/drivers/mmcsd/mmcsd.h
+++ b/drivers/mmcsd/mmcsd.h
@@ -102,6 +102,7 @@ struct mmcsd_state_s
   uint8_t type:4;                  /* Card type (See MMCSD_CARDTYPE_* 
definitions) */
   uint8_t buswidth:4;              /* Bus widths supported (SD only) */
   uint8_t cmd23support:1;          /* CMD23 supported (SD only) */
+  uint8_t sdversion:4;             /* SD physical layer version (SD only) */
   sdio_capset_t caps;              /* SDIO driver capabilities/limitations */
   uint32_t cid[4];                 /* CID register */
   uint32_t csd[4];                 /* CSD register */
diff --git a/drivers/mmcsd/mmcsd_sdio.c b/drivers/mmcsd/mmcsd_sdio.c
index 6c172e5f10f..6f2402128b0 100644
--- a/drivers/mmcsd/mmcsd_sdio.c
+++ b/drivers/mmcsd/mmcsd_sdio.c
@@ -90,6 +90,33 @@
  */
 
 #define MMCSD_SCR_DATADELAY     (100)      /* Wait up to 100MS to get SCR */
+#define MMCSD_SWITCH_DATADELAY  (100)      /* Wait up to 100MS for switch 
status */
+
+/* CMD6, SWITCH_FUNC.  The card answers with a sixty four byte status block
+ * describing what it did.  Bit 31 makes the command a switch rather than a
+ * query, the nibble per function group selects the function wanted, and
+ * the group's own value of fifteen leaves that group alone.  So: switch,
+ * everything unchanged except access mode, which becomes function one,
+ * high speed.
+ */
+
+#define MMCSD_SWITCH_BLOCKLEN   (64)
+#define MMCSD_SWITCH_HIGHSPEED  (0x80fffff1)
+
+/* The status block reports the function actually selected for each group,
+ * a nibble each, most significant byte first.  Access mode is group one,
+ * bits 379:376 of the block, which is the low nibble of byte sixteen.  A
+ * card that could not do what was asked reports fifteen there instead.
+ */
+
+#define MMCSD_SWITCH_STATUS_MODE     (16)
+#define MMCSD_SWITCH_MODE_HIGHSPEED  (1)
+
+/* CMD6 was introduced by version 1.10 of the physical layer specification.
+ * Earlier cards answer it as an illegal command, so they are not asked.
+ */
+
+#define MMCSD_SCR_SPEC_1_10          (1)
 #define MMCSD_BLOCK_RDATADELAY  (100)      /* Wait up to 100MS to get one data 
block */
 
 /* Wait timeout to write one data block */
@@ -1058,9 +1085,11 @@ static void mmcsd_decode_scr(FAR struct mmcsd_state_s 
*priv, uint32_t scr[2])
 #ifdef CONFIG_ENDIAN_BIG  /* Card transfers SCR in big-endian order */
   priv->buswidth     = (scr[0] >> 16) & 15;
   priv->cmd23support = (scr[0] >> 1)  & 1;
+  priv->sdversion    = (scr[0] >> 24) & 15;
 #else
   priv->buswidth     = (scr[0] >> 8)  & 15;
   priv->cmd23support = (scr[0] >> 25) & 1;
+  priv->sdversion    =  scr[0]        & 15;
 #endif
 
 #ifdef CONFIG_DEBUG_FS_INFO
@@ -2760,6 +2789,92 @@ static void mmcsd_mediachange(FAR void *arg)
   mmcsd_unlock(priv);
 }
 
+/****************************************************************************
+ * Name: mmcsd_sd_highspeed
+ *
+ * Description:
+ *   Switch an SD card from default speed into high speed timing with CMD6,
+ *   doubling the rate the bus may then be clocked at.
+ *
+ *   The card's answer is believed rather than the command's: the status
+ *   block it returns reports the function it actually selected, and a card
+ *   that cannot do what was asked says so there instead of failing the
+ *   command.  Only a card that confirms the switch is reported switched,
+ *   because the caller raises the clock on the strength of this and a card
+ *   still in default speed is out of specification above twenty five
+ *   megahertz.
+ *
+ * Assumptions:
+ *   Called once per card, from the initialization sequence, with the card
+ *   selected and the bus already at the default transfer clock.  The
+ *   sixty four byte status block is read through the interrupt path rather
+ *   than by DMA: it is smaller than the setup it would take, and it keeps
+ *   the caller's stack off the requirements a DMA capable buffer has.
+ *
+ * Returned Value:
+ *   OK if the card confirms the switch, a negated errno otherwise.  Every
+ *   failure is survivable: the caller stays at the default rate.
+ *
+ ****************************************************************************/
+
+static int mmcsd_sd_highspeed(FAR struct mmcsd_state_s *priv)
+{
+  uint8_t status[MMCSD_SWITCH_BLOCKLEN];
+  int ret;
+
+  if (priv->sdversion < MMCSD_SCR_SPEC_1_10)
+    {
+      finfo("Card predates CMD6, staying at default speed\n");
+      return -ENOTSUP;
+    }
+
+  ret = mmcsd_setblocklen(priv, MMCSD_SWITCH_BLOCKLEN);
+  if (ret != OK)
+    {
+      ferr("ERROR: mmcsd_setblocklen failed: %d\n", ret);
+      return ret;
+    }
+
+  SDIO_BLOCKSETUP(priv->dev, MMCSD_SWITCH_BLOCKLEN, 1);
+  SDIO_RECVSETUP(priv->dev, status, MMCSD_SWITCH_BLOCKLEN);
+  SDIO_WAITENABLE(priv->dev,
+                  SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
+                  MMCSD_SWITCH_DATADELAY);
+
+  mmcsd_sendcmdpoll(priv, SD_CMD6, MMCSD_SWITCH_HIGHSPEED);
+  ret = mmcsd_recv_r1(priv, SD_CMD6);
+  if (ret != OK)
+    {
+      ferr("ERROR: RECVR1 for CMD6 failed: %d\n", ret);
+      SDIO_CANCEL(priv->dev);
+      return ret;
+    }
+
+  ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR);
+  if (ret != OK)
+    {
+      ferr("ERROR: mmcsd_eventwait for switch status failed: %d\n", ret);
+      return ret;
+    }
+
+  if ((status[MMCSD_SWITCH_STATUS_MODE] & 15) !=
+      MMCSD_SWITCH_MODE_HIGHSPEED)
+    {
+      finfo("Card declined high speed, staying at default speed\n");
+      return -EIO;
+    }
+
+  /* The specification asks for eight clocks after the switch before the
+   * card is spoken to again.  This reuses the driver's clock change delay,
+   * which is far longer than that.
+   */
+
+  MMCSD_USLEEP(MMCSD_CLK_DELAY);
+
+  finfo("Card switched to high speed\n");
+  return OK;
+}
+
 /****************************************************************************
  * Name: mmcsd_widebus
  *
@@ -2921,7 +3036,22 @@ static int mmcsd_widebus(FAR struct mmcsd_state_s *priv)
     {
       if ((priv->buswidth & MMCSD_SCR_BUSWIDTH_4BIT) != 0)
         {
-          SDIO_CLOCK(priv->dev, CLOCK_SD_TRANSFER_4BIT);
+          /* Switch the card into high speed before the host is told to
+           * clock it there, and only if the host asked for high speed by
+           * its capabilities.  High speed is offered on the wide bus
+           * alone: a card narrow enough to want the other path predates
+           * the switch command anyway.
+           */
+
+          if ((priv->caps & SDIO_CAPS_SD_HS_MODE) != 0 &&
+              mmcsd_sd_highspeed(priv) == OK)
+            {
+              SDIO_CLOCK(priv->dev, CLOCK_SD_TRANSFER_4BIT_HS);
+            }
+          else
+            {
+              SDIO_CLOCK(priv->dev, CLOCK_SD_TRANSFER_4BIT);
+            }
         }
       else
         {
@@ -3929,11 +4059,6 @@ static int mmcsd_sdinitialize(FAR struct mmcsd_state_s 
*priv)
         }
     }
 
-  /* TODO: If wide-bus selected, then send CMD6 to see if the card supports
-   * high speed mode.  A new SDIO method will be needed to set high speed
-   * mode.
-   */
-
   return OK;
 }
 
diff --git a/include/nuttx/sdio.h b/include/nuttx/sdio.h
index 2a8904f2f75..ec3e6243ed9 100644
--- a/include/nuttx/sdio.h
+++ b/include/nuttx/sdio.h
@@ -299,6 +299,7 @@
 #define MMC_CMD5        (MMC_CMDIDX5   |MMCSD_R1B_RESPONSE|MMCSD_NODATAXFR)
 #define SDIO_CMD5       (SDIO_CMDIDX5  |MMCSD_R4_RESPONSE |MMCSD_NODATAXFR)
 #define MMCSD_CMD6      (MMCSD_CMDIDX6 |MMCSD_R1B_RESPONSE|MMCSD_NODATAXFR)
+#define SD_CMD6         (MMCSD_CMDIDX6 |MMCSD_R1_RESPONSE |MMCSD_RDDATAXFR)  
/* SWITCH_FUNC: 64-byte status follows */
 #define MMCSD_CMD7S     (MMCSD_CMDIDX7 |MMCSD_R1B_RESPONSE|MMCSD_NODATAXFR)
 #define MMCSD_CMD7D     (MMCSD_CMDIDX7 |MMCSD_NO_RESPONSE |MMCSD_NODATAXFR)  
/* No response when de-selecting card */
 #define MMC_CMD8        (MMC_CMDIDX8   |MMCSD_R1_RESPONSE |MMCSD_RDDATAXFR)
@@ -482,6 +483,7 @@
 #define SDIO_CAPS_8BIT            0x10 /* Bit 4=1: Supports 8 bit operation */
 #define SDIO_CAPS_4BIT_ONLY       0x20 /* Bit 5=1: Supports 4-bit only 
operation */
 #define SDIO_CAPS_MMC_HS_MODE     0x40 /* Bit 6=1: Supports eMMC high speed 
mode */
+#define SDIO_CAPS_SD_HS_MODE      0x80 /* Bit 7=1: Supports SD card high speed 
mode */
 
 /****************************************************************************
  * Name: SDIO_STATUS
@@ -945,7 +947,14 @@ enum sdio_clock_e
   CLOCK_MMC_TRANSFER,      /* MMC normal operation clocking (narrow 1-bit 
mode) */
   CLOCK_SD_TRANSFER_1BIT,  /* SD normal operation clocking (narrow 1-bit mode) 
*/
   CLOCK_SD_TRANSFER_4BIT,  /* SD normal operation clocking (wide 4-bit mode) */
-  CLOCK_MMC_TRANSFER_4BIT  /* MMC normal operation clocking (wide 4-bit mode) 
*/
+  CLOCK_MMC_TRANSFER_4BIT, /* MMC normal operation clocking (wide 4-bit mode) 
*/
+
+  /* SD high speed clocking (wide 4-bit mode).  Sent only to a host that
+   * reported SDIO_CAPS_SD_HS_MODE, and only once the card has confirmed
+   * the switch.
+   */
+
+  CLOCK_SD_TRANSFER_4BIT_HS
 };
 
 /* Event set.  A uint8_t is big enough to hold a set of 8-events.  If more

Reply via email to