Re: I could use some help

From: Mark Millard <marklmi_at_yahoo.com>
Date: Mon, 14 Nov 2022 09:34:13 UTC
My understanding is that HS400 and HS200 requires "tuining the phase"
to be supported and this involves CMD21 to generate test patterns
used for the tuning process.

https://lists.freebsd.org/archives/freebsd-hackers/2022-November/001648.html

reports:

mmc0: CMD7 RESULT: 1
mmc0: CMD7 failed, RESULT: 1
sdhci_fdt0-slot0: sdhci_generic_write_ivar: var=0
mmc0: setting transfer rate to 50.000MHz (HS400 with enhanced strobe timing)
mmc0: REQUEST: CMD7 arg 0 flags 0

but shows not even one CMD21: no actual tuning for HS400
(or HS200) was done. An old quote of mine was:

QUOTE
HS400 needs CMD21 use for synchronizing the command responses on
the CMD line to the CLK (a temporary use of HS200 mode to do the
tuning).
. . .

But there is more context that I should have referenced:

For HS400, when Enhanced Strobe is disabled, the CMD-out line
(from device to host) has no matching strobe to go with it in
any fixed signal-timing relationship from what I've read.

. . .

(Data Out and CRC Response always are synced to the Strobe.)
END QUOTE

This issue exists on Rock64 and in an exchange about that
Andriy Gapon wrote:

QUOTE
On 2021-Dec-10, at 02:35, Andriy Gapon <avg@FreeBSD.org> wrote:

> On 10/12/2021 11:51, Kornel Dulęba wrote:
>> On Thu, Dec 9, 2021 at 11:54 PM Mark Millard <marklmi@yahoo.com> wrote:
>>> Note the "tuned phase to 245" as part of that.
>> Yep, it looks like in Linux they're doing some custom tuning logic
>> specific to this controller.
>> FreeBSD only executes generic tuning code, which apparently is not enough.
> 
> 
> AFAICS, we do not have any support for setting clock phases at all.
END QUOTE

I came to the same conclusion that there is not such thing
as generic tuning involved and there is no support for any
specific controller for the tuning needed for HS200 and HS400.

(There is also a Drive Strength setting involved for HS200
--and HS400 has possibly one more Drive Strength alternative by
count. More missing support?)

I eventually got into the JEDEC standard and wrote . . .

QUOTE
Looking some more, it looks like the Tuning-Process-Completed-No
loop for HS200 in JESD84-B51 that can involve CMD21 is implicitly
always there to allow adjusting implicit parameters. Otherwise,
nothing changes: CMD21 use of itself does not adjust anything.

. . .

In fact there is a sequence of steps listed in another place
about the concept-explanation that is explicit about:

    Sampling Control Block of Host is incremented by one step.

in the looping (along with comparisons to a known tuning block
pattern). . . . It also does report that any other
implementation may be used: it is just an example for illustration.
Possibly using the center of an observed valid window is mentioned.

So I would expect that any time there is not a device-specific
definition for how to do the adjustment for the loop, HS200
and HS400 should be disabled for lack of device specific driver
software to support the activity. JESD84-B51 does not define
any specific method of adjustment of itself as far as I can tell:
there is no general/default technique.
END QUOTE

This traced back to FreeBSD using null_tune and null_return, which
were (from old notes):

      static int
      null_retune(device_t brdev __unused, device_t reqdev __unused,
          bool reset __unused)
      {

              return (0);
      }

      static int
      null_tune(device_t brdev __unused, device_t reqdev __unused,
          bool hs400 __unused)
      {

              return (0);
      }

e.MMC support can be made operational (with a speed tradeoff)
by just not picking to use HS200 or HS400 so that the
tune/return activity is not involved. I use a patch for
that to allow the Rock64 to use its user-replacable e.MMC .
(I've no other context where I use e.MMC these days.)

QUOTE
What I've done in my patch is analogous to what the the code shown
after the #define SDHCI_CAP_MODES_TUNING above does, translated to
fit the mmc's pre-existing code structure.
END QUOTE

My patch to main [so: 14] looks like (some whitespace details
may not survive):

# git -C /usr/main-src/ diff sys/dev/mmc/
diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c
index 5fce6cbf47a1..ff6896f35678 100644
--- a/sys/dev/mmc/mmc.c
+++ b/sys/dev/mmc/mmc.c
@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/kobj.h>
 #include <sys/malloc.h>
 #include <sys/lock.h>
 #include <sys/module.h>
@@ -1512,6 +1513,8 @@ mmc_timing_to_string(enum mmc_bus_timing timing)
 static bool
 mmc_host_timing(device_t dev, enum mmc_bus_timing timing)
 {
+       kobjop_desc_t kobj_desc;
+       kobj_method_t *kobj_method;
        int host_caps;
 
        host_caps = mmcbr_get_caps(dev);
@@ -1543,14 +1546,37 @@ mmc_host_timing(device_t dev, enum mmc_bus_timing timing)
        case bus_timing_mmc_ddr52:
                return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_DDR52));
        case bus_timing_mmc_hs200:
-               return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200_120) ||
-                       HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200_180));
        case bus_timing_mmc_hs400:
-               return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400_120) ||
-                       HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400_180));
        case bus_timing_mmc_hs400es:
-               return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400 |
-                   MMC_CAP_MMC_ENH_STROBE));
+               /*
+                * Disable eMMC modes that require use of
+                * MMC_SEND_TUNING_BLOCK_HS200 to set things up if either the
+                * tune or re-tune method is the default NULL implementation.
+                */
+               kobj_desc = &mmcbr_tune_desc;
+               kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
+                   kobj_desc);
+               if (kobj_method == &kobj_desc->deflt)
+                       return (false);
+               kobj_desc = &mmcbr_retune_desc;
+               kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
+                   kobj_desc);
+               if (kobj_method == &kobj_desc->deflt) {
+                       return (false);
+               }
+
+               /*
+                * Otherwise track the host capabilities.
+                */
+               if (timing == bus_timing_mmc_hs200)
+                       return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200_120) ||
+                               HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200_180));
+               if (timing == bus_timing_mmc_hs400)
+                       return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400_120) ||
+                               HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400_180));
+               if (timing == bus_timing_mmc_hs400es)
+                       return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400 |
+                               MMC_CAP_MMC_ENH_STROBE));
        }
 
 #undef HOST_TIMING_CAP

===
Mark Millard
marklmi at yahoo.com