Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 3 Aug 2023 16:12:05 GMT
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 5adcec04b5ab - main - intelspi: Add support for ddb/kdb -compatible polled mode
Message-ID:  <202308031612.373GC5pK006147@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=5adcec04b5abd4971f1678d6ee1369cc9ecaa90d

commit 5adcec04b5abd4971f1678d6ee1369cc9ecaa90d
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2023-08-03 16:10:50 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2023-08-03 16:10:50 +0000

    intelspi: Add support for ddb/kdb -compatible polled mode
    
    Required for Apple and Microsoft -compatible HID-over-SPI drivers.
    
    Most logic was already implemented in commit 3c0867343819
    "spibus: extend API: add cs_delay ivar, KEEP_CS and NO_SLEEP flags".
    It dissallowed driver sleeps in the interrupt context. This commit
    extends this feature to handle ddb/kdb context with following:
    - Skip driver locking if SPI functions were called from kdb/ddb.
    - Reinitialize controller if kdb/ddb initiated SPI transfer has
      interrupted another already running one. Does not work very
      reliable yet.
    
    Reviewed by:    manu
    Differential Revision:  https://reviews.freebsd.org/D41247
---
 sys/dev/intel/spi.c | 57 ++++++++++++++++++++++++++++++++++++++---------------
 sys/dev/intel/spi.h |  2 +-
 2 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/sys/dev/intel/spi.c b/sys/dev/intel/spi.c
index 6dd063511e26..3bcbd8fbd4f4 100644
--- a/sys/dev/intel/spi.c
+++ b/sys/dev/intel/spi.c
@@ -30,6 +30,7 @@
 
 #include <sys/param.h>
 #include <sys/bus.h>
+#include <sys/kdb.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
 #include <sys/proc.h>
@@ -46,14 +47,27 @@
 /**
  *	Macros for driver mutex locking
  */
-#define	INTELSPI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
-#define	INTELSPI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
+#define	INTELSPI_IN_POLLING_MODE()	(SCHEDULER_STOPPED() || kdb_active)
+#define	INTELSPI_LOCK(_sc)		do {		\
+	if(!INTELSPI_IN_POLLING_MODE())			\
+		mtx_lock(&(_sc)->sc_mtx);		\
+} while (0)
+#define	INTELSPI_UNLOCK(_sc)		do {		\
+	if(!INTELSPI_IN_POLLING_MODE())			\
+		mtx_unlock(&(_sc)->sc_mtx);		\
+} while (0)
 #define	INTELSPI_LOCK_INIT(_sc)		\
 	mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
 	    "intelspi", MTX_DEF)
 #define	INTELSPI_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_mtx)
-#define	INTELSPI_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
-#define	INTELSPI_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
+#define	INTELSPI_ASSERT_LOCKED(_sc)	do {		\
+	if(!INTELSPI_IN_POLLING_MODE())			\
+		mtx_assert(&(_sc)->sc_mtx, MA_OWNED);	\
+} while (0)
+#define	INTELSPI_ASSERT_UNLOCKED(_sc)	do {		\
+	if(!INTELSPI_IN_POLLING_MODE())			\
+		mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED);\
+} while (0)
 
 #define INTELSPI_WRITE(_sc, _off, _val)		\
     bus_write_4((_sc)->sc_mem_res, (_off), (_val))
@@ -342,17 +356,26 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 
 	INTELSPI_LOCK(sc);
 
-	/* If the controller is in use wait until it is available. */
-	while (sc->sc_flags & INTELSPI_BUSY) {
-		if ((cmd->flags & SPI_FLAG_NO_SLEEP) == SPI_FLAG_NO_SLEEP) {
-			INTELSPI_UNLOCK(sc);
-			return (EBUSY);
-		}
-		err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", 0);
-		if (err == EINTR) {
-			INTELSPI_UNLOCK(sc);
-			return (err);
+	if (!INTELSPI_IN_POLLING_MODE()) {
+		/* If the controller is in use wait until it is available. */
+		while (sc->sc_flags & INTELSPI_BUSY) {
+			if ((cmd->flags & SPI_FLAG_NO_SLEEP) != 0) {
+				INTELSPI_UNLOCK(sc);
+				return (EBUSY);
+			}
+			err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", 0);
+			if (err == EINTR) {
+				INTELSPI_UNLOCK(sc);
+				return (err);
+			}
 		}
+	} else {
+		/*
+		 * Now we are in the middle of other transfer. Try to reset
+		 * controller state to get predictable context.
+		 */
+		if ((sc->sc_flags & INTELSPI_BUSY) != 0)
+			intelspi_init(sc);
 	}
 
 	/* Now we have control over SPI controller. */
@@ -411,7 +434,8 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 	DELAY(cs_delay);
 
 	/* Transfer as much as possible to FIFOs */
-	if ((cmd->flags & SPI_FLAG_NO_SLEEP) == SPI_FLAG_NO_SLEEP) {
+	if ((cmd->flags & SPI_FLAG_NO_SLEEP) != 0 ||
+	     INTELSPI_IN_POLLING_MODE() || cold) {
 		/* We cannot wait with mtx_sleep if we're called from e.g. an ithread */
 		poll_limit = 2000;
 		while (!intelspi_transact(sc) && poll_limit-- > 0)
@@ -449,7 +473,8 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 
 	/* Release the controller and wakeup the next thread waiting for it. */
 	sc->sc_flags = 0;
-	wakeup_one(dev);
+	if (!INTELSPI_IN_POLLING_MODE())
+		wakeup_one(dev);
 	INTELSPI_UNLOCK(sc);
 
 	/*
diff --git a/sys/dev/intel/spi.h b/sys/dev/intel/spi.h
index bd79ec1e812a..fa145f8f73d7 100644
--- a/sys/dev/intel/spi.h
+++ b/sys/dev/intel/spi.h
@@ -54,7 +54,7 @@ struct intelspi_softc {
 	struct spi_command	*sc_cmd;
 	uint32_t		sc_len;
 	uint32_t		sc_read;
-	uint32_t		sc_flags;
+	volatile uint32_t	sc_flags;
 	uint32_t		sc_written;
 	uint32_t		sc_clock;
 	uint32_t		sc_mode;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202308031612.373GC5pK006147>