public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection
@ 2020-06-29 18:36 Pete Batard
  2020-06-30  1:37 ` Andrei Warkentin
       [not found] ` <161D2E94C55C04ED.28420@groups.io>
  0 siblings, 2 replies; 4+ messages in thread
From: Pete Batard @ 2020-06-29 18:36 UTC (permalink / raw)
  To: devel; +Cc: ard.biesheuvel, leif, awarkentin

The Raspberry Pi 3 and Pi 4 platforms (with latest EEPROM) can boot
straight from USB, without the need for an SD card being present.
However, the IsCardPresent () calls from the ArasanMmcHost and SdHost
drivers are currently hardwired to return TRUE, which results in
straight to USB boot failing due to the SD drivers looping on
errors while trying to poke at a non-existent card...

Ideally, we would use the Card Detect signal from the uSD slot, to
report on the presence or absence of a card, but the Raspberry Pi
Foundation did not wire those signals in the Pi 2 and subsequent
models, leaving us with only potentially interfering SD commands
as means to perform card detection.

As a result of this, we are left with no other choice but limit
detection to occurring only once, prior to formal SD card init,
and then return the detected value for subsequent calls. This,
however, is more than good enough for the intended purpose, which
is to allow straight to USB boot.

Tested on Raspberry Pi 3 and 4, and for both SD controllers.

Addresses pftf/RPi3#13, pftf/RPi3#14, pftf/RPi4#37.

Signed-off-by: Pete Batard <pete@akeo.ie>
---
 Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c | 66 ++++++++++++++++---
 Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c               | 69 +++++++++++++++++---
 Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h               |  6 ++
 3 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
index 6d706af6f276..08e5be1f015f 100644
--- a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
@@ -11,7 +11,8 @@
 
 #define DEBUG_MMCHOST_SD DEBUG_VERBOSE
 
-BOOLEAN PreviousIsCardPresent = FALSE;
+BOOLEAN CardIsPresent = FALSE;
+CARD_DETECT_STATE CardDetectState = CardDetectRequired;
 UINT32 LastExecutedCommand = (UINT32) -1;
 
 STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
@@ -239,14 +240,6 @@ CalculateClockFrequencyDivisor (
   return EFI_SUCCESS;
 }
 
-BOOLEAN
-MMCIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-)
-{
-  return TRUE;
-}
-
 BOOLEAN
 MMCIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
@@ -418,6 +411,10 @@ MMCNotifyState (
 
   DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State));
 
+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     {
@@ -489,6 +486,57 @@ MMCNotifyState (
   return EFI_SUCCESS;
 }
 
+BOOLEAN
+MMCIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+)
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = MMCNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = MMCSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = MMCSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "MMCIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 EFI_STATUS
 MMCReceiveResponse (
   IN EFI_MMC_HOST_PROTOCOL    *This,
diff --git a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
index 2f31c5eb8c46..d96344fd0f8e 100644
--- a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
@@ -64,7 +64,9 @@ STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata",
                                     "genpulses", "writewait2", "?",
                                     "startpowdown" };
 #endif /* NDEBUG */
-STATIC UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);
+STATIC BOOLEAN CardIsPresent = FALSE;
+STATIC CARD_DETECT_STATE CardDetectState = CardDetectRequired;
+static UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);
 
 STATIC inline BOOLEAN
 IsAppCmd (
@@ -264,15 +266,7 @@ SdHostSetClockFrequency (
   return Status;
 }
 
-STATIC BOOLEAN
-SdIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-  )
-{
-  return TRUE;
-}
-
-STATIC BOOLEAN
+static BOOLEAN
 SdIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
   )
@@ -639,6 +633,10 @@ SdNotifyState (
 {
   DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", State));
 
+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     DEBUG ((DEBUG_MMCHOST_SD, "MmcHwInitializationState\n", State));
@@ -718,6 +716,57 @@ SdNotifyState (
   return EFI_SUCCESS;
 }
 
+STATIC BOOLEAN
+SdIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = SdNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = SdSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = SdSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "SdIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "SdIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 BOOLEAN
 SdIsMultiBlock (
   IN EFI_MMC_HOST_PROTOCOL *This
diff --git a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
index c558e00bf500..78514a31bc4e 100644
--- a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
+++ b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
@@ -82,6 +82,12 @@ typedef enum _MMC_STATE {
     MmcDisconnectState,
 } MMC_STATE;
 
+typedef enum _CARD_DETECT_STATE {
+    CardDetectRequired = 0,
+    CardDetectInProgress,
+    CardDetectCompleted
+} CARD_DETECT_STATE;
+
 #define EMMCBACKWARD         (0)
 #define EMMCHS26             (1 << 0)      // High-Speed @26MHz at rated device voltages
 #define EMMCHS52             (1 << 1)      // High-Speed @52MHz at rated device voltages
-- 
2.21.0.windows.1


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

* Re: [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection
  2020-06-29 18:36 [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection Pete Batard
@ 2020-06-30  1:37 ` Andrei Warkentin
       [not found] ` <161D2E94C55C04ED.28420@groups.io>
  1 sibling, 0 replies; 4+ messages in thread
From: Andrei Warkentin @ 2020-06-30  1:37 UTC (permalink / raw)
  To: Pete Batard, devel@edk2.groups.io
  Cc: ard.biesheuvel@arm.com, leif@nuviainc.com

[-- Attachment #1: Type: text/plain, Size: 8722 bytes --]

Looks good to me, but I would like to (personally) first test on CM3 (which has eMMC), before this is merged. Stay tuned...

And thanks again for working on this. I like the approach taken.

A
________________________________
From: Pete Batard <pete@akeo.ie>
Sent: Monday, June 29, 2020 1:36 PM
To: devel@edk2.groups.io <devel@edk2.groups.io>
Cc: ard.biesheuvel@arm.com <ard.biesheuvel@arm.com>; leif@nuviainc.com <leif@nuviainc.com>; Andrei Warkentin <awarkentin@vmware.com>
Subject: [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection

The Raspberry Pi 3 and Pi 4 platforms (with latest EEPROM) can boot
straight from USB, without the need for an SD card being present.
However, the IsCardPresent () calls from the ArasanMmcHost and SdHost
drivers are currently hardwired to return TRUE, which results in
straight to USB boot failing due to the SD drivers looping on
errors while trying to poke at a non-existent card...

Ideally, we would use the Card Detect signal from the uSD slot, to
report on the presence or absence of a card, but the Raspberry Pi
Foundation did not wire those signals in the Pi 2 and subsequent
models, leaving us with only potentially interfering SD commands
as means to perform card detection.

As a result of this, we are left with no other choice but limit
detection to occurring only once, prior to formal SD card init,
and then return the detected value for subsequent calls. This,
however, is more than good enough for the intended purpose, which
is to allow straight to USB boot.

Tested on Raspberry Pi 3 and 4, and for both SD controllers.

Addresses pftf/RPi3#13, pftf/RPi3#14, pftf/RPi4#37.

Signed-off-by: Pete Batard <pete@akeo.ie>
---
 Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c | 66 ++++++++++++++++---
 Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c               | 69 +++++++++++++++++---
 Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h               |  6 ++
 3 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
index 6d706af6f276..08e5be1f015f 100644
--- a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
@@ -11,7 +11,8 @@

 #define DEBUG_MMCHOST_SD DEBUG_VERBOSE

-BOOLEAN PreviousIsCardPresent = FALSE;
+BOOLEAN CardIsPresent = FALSE;
+CARD_DETECT_STATE CardDetectState = CardDetectRequired;
 UINT32 LastExecutedCommand = (UINT32) -1;

 STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
@@ -239,14 +240,6 @@ CalculateClockFrequencyDivisor (
   return EFI_SUCCESS;
 }

-BOOLEAN
-MMCIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-)
-{
-  return TRUE;
-}
-
 BOOLEAN
 MMCIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
@@ -418,6 +411,10 @@ MMCNotifyState (

   DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State));

+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     {
@@ -489,6 +486,57 @@ MMCNotifyState (
   return EFI_SUCCESS;
 }

+BOOLEAN
+MMCIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+)
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = MMCNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = MMCSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = MMCSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "MMCIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 EFI_STATUS
 MMCReceiveResponse (
   IN EFI_MMC_HOST_PROTOCOL    *This,
diff --git a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
index 2f31c5eb8c46..d96344fd0f8e 100644
--- a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
@@ -64,7 +64,9 @@ STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata",
                                     "genpulses", "writewait2", "?",
                                     "startpowdown" };
 #endif /* NDEBUG */
-STATIC UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);
+STATIC BOOLEAN CardIsPresent = FALSE;
+STATIC CARD_DETECT_STATE CardDetectState = CardDetectRequired;
+static UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);

 STATIC inline BOOLEAN
 IsAppCmd (
@@ -264,15 +266,7 @@ SdHostSetClockFrequency (
   return Status;
 }

-STATIC BOOLEAN
-SdIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-  )
-{
-  return TRUE;
-}
-
-STATIC BOOLEAN
+static BOOLEAN
 SdIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
   )
@@ -639,6 +633,10 @@ SdNotifyState (
 {
   DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", State));

+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     DEBUG ((DEBUG_MMCHOST_SD, "MmcHwInitializationState\n", State));
@@ -718,6 +716,57 @@ SdNotifyState (
   return EFI_SUCCESS;
 }

+STATIC BOOLEAN
+SdIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = SdNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = SdSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = SdSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "SdIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "SdIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 BOOLEAN
 SdIsMultiBlock (
   IN EFI_MMC_HOST_PROTOCOL *This
diff --git a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
index c558e00bf500..78514a31bc4e 100644
--- a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
+++ b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
@@ -82,6 +82,12 @@ typedef enum _MMC_STATE {
     MmcDisconnectState,
 } MMC_STATE;

+typedef enum _CARD_DETECT_STATE {
+    CardDetectRequired = 0,
+    CardDetectInProgress,
+    CardDetectCompleted
+} CARD_DETECT_STATE;
+
 #define EMMCBACKWARD         (0)
 #define EMMCHS26             (1 << 0)      // High-Speed @26MHz at rated device voltages
 #define EMMCHS52             (1 << 1)      // High-Speed @52MHz at rated device voltages
--
2.21.0.windows.1


[-- Attachment #2: Type: text/html, Size: 13568 bytes --]

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

* Re: [edk2-devel] [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection
       [not found] ` <161D2E94C55C04ED.28420@groups.io>
@ 2020-07-02  7:09   ` Andrei Warkentin
       [not found]   ` <161DDDD713DDBF22.12013@groups.io>
  1 sibling, 0 replies; 4+ messages in thread
From: Andrei Warkentin @ 2020-07-02  7:09 UTC (permalink / raw)
  To: Pete Batard, devel@edk2.groups.io, Andrei Warkentin
  Cc: ard.biesheuvel@arm.com, leif@nuviainc.com

[-- Attachment #1: Type: text/plain, Size: 9543 bytes --]

Testing on the CM3, I don't have MMC storage working. So that's a regression. I also tested Pi 2 and it was fine.

I'll take a look (with no timeline). I am okay if you consider merging Pete's patch. I don't think there are that many CM3 UEFI users out there, and Pete's change greatly improves the USB boot story for Pi 3 and Pi 4.

A
________________________________
From: devel@edk2.groups.io <devel@edk2.groups.io> on behalf of Andrei Warkentin via groups.io <awarkentin=vmware.com@groups.io>
Sent: Monday, June 29, 2020 8:37 PM
To: Pete Batard <pete@akeo.ie>; devel@edk2.groups.io <devel@edk2.groups.io>
Cc: ard.biesheuvel@arm.com <ard.biesheuvel@arm.com>; leif@nuviainc.com <leif@nuviainc.com>
Subject: Re: [edk2-devel] [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection

Looks good to me, but I would like to (personally) first test on CM3 (which has eMMC), before this is merged. Stay tuned...

And thanks again for working on this. I like the approach taken.

A
________________________________
From: Pete Batard <pete@akeo.ie>
Sent: Monday, June 29, 2020 1:36 PM
To: devel@edk2.groups.io <devel@edk2.groups.io>
Cc: ard.biesheuvel@arm.com <ard.biesheuvel@arm.com>; leif@nuviainc.com <leif@nuviainc.com>; Andrei Warkentin <awarkentin@vmware.com>
Subject: [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection

The Raspberry Pi 3 and Pi 4 platforms (with latest EEPROM) can boot
straight from USB, without the need for an SD card being present.
However, the IsCardPresent () calls from the ArasanMmcHost and SdHost
drivers are currently hardwired to return TRUE, which results in
straight to USB boot failing due to the SD drivers looping on
errors while trying to poke at a non-existent card...

Ideally, we would use the Card Detect signal from the uSD slot, to
report on the presence or absence of a card, but the Raspberry Pi
Foundation did not wire those signals in the Pi 2 and subsequent
models, leaving us with only potentially interfering SD commands
as means to perform card detection.

As a result of this, we are left with no other choice but limit
detection to occurring only once, prior to formal SD card init,
and then return the detected value for subsequent calls. This,
however, is more than good enough for the intended purpose, which
is to allow straight to USB boot.

Tested on Raspberry Pi 3 and 4, and for both SD controllers.

Addresses pftf/RPi3#13, pftf/RPi3#14, pftf/RPi4#37.

Signed-off-by: Pete Batard <pete@akeo.ie>
---
 Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c | 66 ++++++++++++++++---
 Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c               | 69 +++++++++++++++++---
 Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h               |  6 ++
 3 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
index 6d706af6f276..08e5be1f015f 100644
--- a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
@@ -11,7 +11,8 @@

 #define DEBUG_MMCHOST_SD DEBUG_VERBOSE

-BOOLEAN PreviousIsCardPresent = FALSE;
+BOOLEAN CardIsPresent = FALSE;
+CARD_DETECT_STATE CardDetectState = CardDetectRequired;
 UINT32 LastExecutedCommand = (UINT32) -1;

 STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
@@ -239,14 +240,6 @@ CalculateClockFrequencyDivisor (
   return EFI_SUCCESS;
 }

-BOOLEAN
-MMCIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-)
-{
-  return TRUE;
-}
-
 BOOLEAN
 MMCIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
@@ -418,6 +411,10 @@ MMCNotifyState (

   DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State));

+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     {
@@ -489,6 +486,57 @@ MMCNotifyState (
   return EFI_SUCCESS;
 }

+BOOLEAN
+MMCIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+)
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = MMCNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = MMCSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = MMCSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "MMCIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 EFI_STATUS
 MMCReceiveResponse (
   IN EFI_MMC_HOST_PROTOCOL    *This,
diff --git a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
index 2f31c5eb8c46..d96344fd0f8e 100644
--- a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
@@ -64,7 +64,9 @@ STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata",
                                     "genpulses", "writewait2", "?",
                                     "startpowdown" };
 #endif /* NDEBUG */
-STATIC UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);
+STATIC BOOLEAN CardIsPresent = FALSE;
+STATIC CARD_DETECT_STATE CardDetectState = CardDetectRequired;
+static UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);

 STATIC inline BOOLEAN
 IsAppCmd (
@@ -264,15 +266,7 @@ SdHostSetClockFrequency (
   return Status;
 }

-STATIC BOOLEAN
-SdIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-  )
-{
-  return TRUE;
-}
-
-STATIC BOOLEAN
+static BOOLEAN
 SdIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
   )
@@ -639,6 +633,10 @@ SdNotifyState (
 {
   DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", State));

+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     DEBUG ((DEBUG_MMCHOST_SD, "MmcHwInitializationState\n", State));
@@ -718,6 +716,57 @@ SdNotifyState (
   return EFI_SUCCESS;
 }

+STATIC BOOLEAN
+SdIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = SdNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = SdSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = SdSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "SdIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "SdIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 BOOLEAN
 SdIsMultiBlock (
   IN EFI_MMC_HOST_PROTOCOL *This
diff --git a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
index c558e00bf500..78514a31bc4e 100644
--- a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
+++ b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
@@ -82,6 +82,12 @@ typedef enum _MMC_STATE {
     MmcDisconnectState,
 } MMC_STATE;

+typedef enum _CARD_DETECT_STATE {
+    CardDetectRequired = 0,
+    CardDetectInProgress,
+    CardDetectCompleted
+} CARD_DETECT_STATE;
+
 #define EMMCBACKWARD         (0)
 #define EMMCHS26             (1 << 0)      // High-Speed @26MHz at rated device voltages
 #define EMMCHS52             (1 << 1)      // High-Speed @52MHz at rated device voltages
--
2.21.0.windows.1



[-- Attachment #2: Type: text/html, Size: 15329 bytes --]

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

* Re: [edk2-devel] [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection
       [not found]   ` <161DDDD713DDBF22.12013@groups.io>
@ 2020-07-02 15:01     ` Andrei Warkentin
  0 siblings, 0 replies; 4+ messages in thread
From: Andrei Warkentin @ 2020-07-02 15:01 UTC (permalink / raw)
  To: Pete Batard, devel@edk2.groups.io, Andrei Warkentin
  Cc: ard.biesheuvel@arm.com, leif@nuviainc.com

[-- Attachment #1: Type: text/plain, Size: 10291 bytes --]

Reviewed-by: Andrei Warkentin <andrey.warkentin@gmail.com>
Tested-by: Andrei Warkentin <andrey.warkentin@gmail.com>

Like I said, I am gonna handle the CM3 regresso, so please feel free to merge without waiting on me.

A
________________________________
From: devel@edk2.groups.io <devel@edk2.groups.io> on behalf of Andrei Warkentin via groups.io <awarkentin=vmware.com@groups.io>
Sent: Thursday, July 2, 2020 2:09 AM
To: Pete Batard <pete@akeo.ie>; devel@edk2.groups.io <devel@edk2.groups.io>; Andrei Warkentin <awarkentin@vmware.com>
Cc: ard.biesheuvel@arm.com <ard.biesheuvel@arm.com>; leif@nuviainc.com <leif@nuviainc.com>
Subject: Re: [edk2-devel] [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection

Testing on the CM3, I don't have MMC storage working. So that's a regression. I also tested Pi 2 and it was fine.

I'll take a look (with no timeline). I am okay if you consider merging Pete's patch. I don't think there are that many CM3 UEFI users out there, and Pete's change greatly improves the USB boot story for Pi 3 and Pi 4.

A
________________________________
From: devel@edk2.groups.io <devel@edk2.groups.io> on behalf of Andrei Warkentin via groups.io <awarkentin=vmware.com@groups.io>
Sent: Monday, June 29, 2020 8:37 PM
To: Pete Batard <pete@akeo.ie>; devel@edk2.groups.io <devel@edk2.groups.io>
Cc: ard.biesheuvel@arm.com <ard.biesheuvel@arm.com>; leif@nuviainc.com <leif@nuviainc.com>
Subject: Re: [edk2-devel] [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection

Looks good to me, but I would like to (personally) first test on CM3 (which has eMMC), before this is merged. Stay tuned...

And thanks again for working on this. I like the approach taken.

A
________________________________
From: Pete Batard <pete@akeo.ie>
Sent: Monday, June 29, 2020 1:36 PM
To: devel@edk2.groups.io <devel@edk2.groups.io>
Cc: ard.biesheuvel@arm.com <ard.biesheuvel@arm.com>; leif@nuviainc.com <leif@nuviainc.com>; Andrei Warkentin <awarkentin@vmware.com>
Subject: [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection

The Raspberry Pi 3 and Pi 4 platforms (with latest EEPROM) can boot
straight from USB, without the need for an SD card being present.
However, the IsCardPresent () calls from the ArasanMmcHost and SdHost
drivers are currently hardwired to return TRUE, which results in
straight to USB boot failing due to the SD drivers looping on
errors while trying to poke at a non-existent card...

Ideally, we would use the Card Detect signal from the uSD slot, to
report on the presence or absence of a card, but the Raspberry Pi
Foundation did not wire those signals in the Pi 2 and subsequent
models, leaving us with only potentially interfering SD commands
as means to perform card detection.

As a result of this, we are left with no other choice but limit
detection to occurring only once, prior to formal SD card init,
and then return the detected value for subsequent calls. This,
however, is more than good enough for the intended purpose, which
is to allow straight to USB boot.

Tested on Raspberry Pi 3 and 4, and for both SD controllers.

Addresses pftf/RPi3#13, pftf/RPi3#14, pftf/RPi4#37.

Signed-off-by: Pete Batard <pete@akeo.ie>
---
 Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c | 66 ++++++++++++++++---
 Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c               | 69 +++++++++++++++++---
 Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h               |  6 ++
 3 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
index 6d706af6f276..08e5be1f015f 100644
--- a/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
@@ -11,7 +11,8 @@

 #define DEBUG_MMCHOST_SD DEBUG_VERBOSE

-BOOLEAN PreviousIsCardPresent = FALSE;
+BOOLEAN CardIsPresent = FALSE;
+CARD_DETECT_STATE CardDetectState = CardDetectRequired;
 UINT32 LastExecutedCommand = (UINT32) -1;

 STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
@@ -239,14 +240,6 @@ CalculateClockFrequencyDivisor (
   return EFI_SUCCESS;
 }

-BOOLEAN
-MMCIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-)
-{
-  return TRUE;
-}
-
 BOOLEAN
 MMCIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
@@ -418,6 +411,10 @@ MMCNotifyState (

   DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State));

+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     {
@@ -489,6 +486,57 @@ MMCNotifyState (
   return EFI_SUCCESS;
 }

+BOOLEAN
+MMCIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+)
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = MMCNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = MMCSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = MMCSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "MMCIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 EFI_STATUS
 MMCReceiveResponse (
   IN EFI_MMC_HOST_PROTOCOL    *This,
diff --git a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
index 2f31c5eb8c46..d96344fd0f8e 100644
--- a/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
+++ b/Platform/RaspberryPi/Drivers/SdHostDxe/SdHostDxe.c
@@ -64,7 +64,9 @@ STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata",
                                     "genpulses", "writewait2", "?",
                                     "startpowdown" };
 #endif /* NDEBUG */
-STATIC UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);
+STATIC BOOLEAN CardIsPresent = FALSE;
+STATIC CARD_DETECT_STATE CardDetectState = CardDetectRequired;
+static UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);

 STATIC inline BOOLEAN
 IsAppCmd (
@@ -264,15 +266,7 @@ SdHostSetClockFrequency (
   return Status;
 }

-STATIC BOOLEAN
-SdIsCardPresent (
-  IN EFI_MMC_HOST_PROTOCOL *This
-  )
-{
-  return TRUE;
-}
-
-STATIC BOOLEAN
+static BOOLEAN
 SdIsReadOnly (
   IN EFI_MMC_HOST_PROTOCOL *This
   )
@@ -639,6 +633,10 @@ SdNotifyState (
 {
   DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", State));

+  // Stall all operations except init until card detection has occurred.
+  if (State != MmcHwInitializationState && CardDetectState != CardDetectCompleted)
+    return EFI_NOT_READY;
+
   switch (State) {
   case MmcHwInitializationState:
     DEBUG ((DEBUG_MMCHOST_SD, "MmcHwInitializationState\n", State));
@@ -718,6 +716,57 @@ SdNotifyState (
   return EFI_SUCCESS;
 }

+STATIC BOOLEAN
+SdIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // If we are already in progress (we may get concurrent calls)
+  // or completed the detection, just return the current value.
+  //
+  if (CardDetectState != CardDetectRequired)
+    return CardIsPresent;
+
+  CardDetectState = CardDetectInProgress;
+  CardIsPresent = FALSE;
+
+  //
+  // The two following commands should succeed even if no card is present.
+  //
+  Status = SdNotifyState (This, MmcHwInitializationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
+    // If we failed init, go back to requiring card detection
+    CardDetectState = CardDetectRequired;
+    return FALSE;
+  }
+
+  Status = SdSendCommand (This, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "SdIsCardPresent: CMD0 Error, Status=%r.\n", Status));
+    goto out;
+  }
+
+  //
+  // CMD8 should tell us if a card is present.
+  //
+  Status = SdSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "SdIsCardPresent: No card detected, Status=%r.\n", Status));
+    goto out;
+  }
+
+  DEBUG ((DEBUG_INFO, "SdIsCardPresent: Card detected.\n"));
+  CardIsPresent = TRUE;
+
+out:
+  CardDetectState = CardDetectCompleted;
+  return CardIsPresent;
+}
+
 BOOLEAN
 SdIsMultiBlock (
   IN EFI_MMC_HOST_PROTOCOL *This
diff --git a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
index c558e00bf500..78514a31bc4e 100644
--- a/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
+++ b/Platform/RaspberryPi/Include/Protocol/RpiMmcHost.h
@@ -82,6 +82,12 @@ typedef enum _MMC_STATE {
     MmcDisconnectState,
 } MMC_STATE;

+typedef enum _CARD_DETECT_STATE {
+    CardDetectRequired = 0,
+    CardDetectInProgress,
+    CardDetectCompleted
+} CARD_DETECT_STATE;
+
 #define EMMCBACKWARD         (0)
 #define EMMCHS26             (1 << 0)      // High-Speed @26MHz at rated device voltages
 #define EMMCHS52             (1 << 1)      // High-Speed @52MHz at rated device voltages
--
2.21.0.windows.1



[-- Attachment #2: Type: text/html, Size: 17104 bytes --]

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

end of thread, other threads:[~2020-07-02 15:01 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-06-29 18:36 [edk2-platforms][PATCH 1/1] Platform/RaspberryPi/Drivers: Add SD card detection Pete Batard
2020-06-30  1:37 ` Andrei Warkentin
     [not found] ` <161D2E94C55C04ED.28420@groups.io>
2020-07-02  7:09   ` [edk2-devel] " Andrei Warkentin
     [not found]   ` <161DDDD713DDBF22.12013@groups.io>
2020-07-02 15:01     ` Andrei Warkentin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox