Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 2 Sep 2012 09:37:24 -0300
From:      Luiz Otavio O Souza <loos.br@gmail.com>
To:        freebsd-arch@freebsd.org
Subject:   spibus access serialization
Message-ID:  <B54C4C9A-76F4-45CC-94C3-628DDF901051@gmail.com>

next in thread | raw e-mail | index | archive | help

--Apple-Mail-58-1062540785
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=us-ascii

Hi,

I've some embedded systems with spi devices that share the same spibus =
and because of that i'm working to get some kind of access serialization =
on spibus and also protection to devices which need a series of spi =
transfers to achieve some goal.

The SPI usage (with all the patches applied) goes like this:

/* Wait until the spibus is free. When free, acquire the bus and select =
the device */
SPIBUS_ACQUIRE_BUS(spibus, device);

/* Program the CPLD to read data from NAND */
SPIBUS_TRANSFER(spibus, device, &cmd);

/* Read 'n' bytes from CPLD */
SPIBUS_TRANSFER(spibus, device, &cmd);

/* Release the spibus and deselect the device */
SPIBUS_RELEASE_BUS(spibus, device);

While today everything is done inside SPIBUS_TRANSFER().

The patch spibus-01.diff adds the bus methods and the default methods.

The spibus-02-devices.diff adds the needed glue to all spi devices =
(dev/flash/at45d.c, dev/flash/mx25l.c, arm/lpc/ssd1289.c, =
mips/atheros/pcf2123_rtc.c). As the default methods are just nops, there =
are no functional changes.

On spibus-03-controllers.diff we finally add the serialization methods =
to spi controllers (mips/atheros/ar71xx_spi.c and arm/lpc/lpc_spi.c) and =
change the device CS to happen on bus acquire and release and not on =
start and end of each transfer.

The spi controller on arm/at91/at91_spi.c wasn't changed because looks =
like it will be need to move the device CS control from the controller =
and use it as a GPIO pin. I need to read more of at91 code before i can =
suggest some change there. Until there it will work just as now (no =
serialization and selecting/deselecting the device within each =
transfer).

Comments ?

Thanks,
Luiz




--Apple-Mail-58-1062540785
Content-Disposition: attachment;
	filename=spibus-01.diff
Content-Type: application/octet-stream;
	name="spibus-01.diff"
Content-Transfer-Encoding: 7bit

Index: dev/spibus/spibus.c
===================================================================
--- dev/spibus/spibus.c	(revision 239916)
+++ dev/spibus/spibus.c	(working copy)
@@ -155,6 +155,20 @@
 	resource_int_value(dname, dunit, "cs", &devi->cs);
 }
 
+static void
+spibus_acquire_bus_impl(device_t dev, device_t child)
+{
+
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), child);
+}
+
+static void
+spibus_release_bus_impl(device_t dev, device_t child)
+{
+
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), child);
+}
+
 static int
 spibus_transfer_impl(device_t dev, device_t child, struct spi_command *cmd)
 {
@@ -180,6 +194,8 @@
 	DEVMETHOD(bus_hinted_child,	spibus_hinted_child),
 
 	/* spibus interface */
+	DEVMETHOD(spibus_acquire_bus,	spibus_acquire_bus_impl),
+	DEVMETHOD(spibus_release_bus,	spibus_release_bus_impl),
 	DEVMETHOD(spibus_transfer,	spibus_transfer_impl),
 
 	DEVMETHOD_END
Index: dev/spibus/spibus_if.m
===================================================================
--- dev/spibus/spibus_if.m	(revision 239916)
+++ dev/spibus/spibus_if.m	(working copy)
@@ -32,6 +32,37 @@
 INTERFACE spibus;
 
 #
+# Default implementation
+#
+CODE {
+	static void
+	null_acquire_bus(device_t dev, device_t child)
+	{
+	}
+
+	static void
+	null_release_bus(device_t dev, device_t child)
+	{
+	}
+};
+
+#
+# Acquire bus and select the device
+#
+METHOD void acquire_bus {
+	device_t dev;
+	device_t child;
+} DEFAULT null_acquire_bus;
+
+#
+# Release bus and deselect the device
+#
+METHOD void release_bus {
+	device_t dev;
+	device_t child;
+} DEFAULT null_release_bus;
+
+#
 # Do a spi command
 #
 METHOD int transfer {

--Apple-Mail-58-1062540785
Content-Disposition: attachment;
	filename=spibus-02-devices.diff
Content-Type: application/octet-stream;
	name="spibus-02-devices.diff"
Content-Transfer-Encoding: 7bit

Index: dev/flash/at45d.c
===================================================================
--- dev/flash/at45d.c	(revision 239916)
+++ dev/flash/at45d.c	(working copy)
@@ -132,7 +132,9 @@
 	cmd.tx_cmd = txBuf;
 	cmd.rx_cmd = rxBuf;
 	cmd.rx_cmd_sz = cmd.tx_cmd_sz = 2;
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	*status = rxBuf[1];
 	return (err);
 }
@@ -152,7 +154,9 @@
 	cmd.tx_cmd = &txBuf;
 	cmd.rx_cmd = &rxBuf;
 	cmd.tx_cmd_sz = cmd.rx_cmd_sz = 5;
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	if (err)
 		return (err);
 	memcpy(resp, rxBuf + 1, 4);
@@ -365,8 +369,10 @@
 					txBuf[2] = ((addr >> 8) & 0xff);
 					txBuf[3] = 0;
 					cmd.tx_data_sz = cmd.rx_data_sz = 0;
+					SPIBUS_ACQUIRE_BUS(pdev, dev);
 					err = SPIBUS_TRANSFER(pdev, dev,
 					    &cmd);
+					SPIBUS_RELEASE_BUS(pdev, dev);
 					if (err == 0)
 						err = at45d_wait_ready(dev,
 						    &status);
@@ -383,7 +389,9 @@
 			txBuf[2] = ((addr >> 8) & 0xff);
 			txBuf[3] = (addr & 0xff);
 			cmd.tx_data_sz = cmd.rx_data_sz = len;
+			SPIBUS_ACQUIRE_BUS(pdev, dev);
 			err = SPIBUS_TRANSFER(pdev, dev, &cmd);
+			SPIBUS_RELEASE_BUS(pdev, dev);
 			if (err == 0 && bp->bio_cmd != BIO_READ)
 				err = at45d_wait_ready(dev, &status);
 			if (err != 0) {
@@ -397,7 +405,9 @@
 				txBuf[2] = ((addr >> 8) & 0xff);
 				txBuf[3] = 0;
 				cmd.tx_data_sz = cmd.rx_data_sz = 0;
+				SPIBUS_ACQUIRE_BUS(pdev, dev);
 				err = SPIBUS_TRANSFER(pdev, dev, &cmd);
+				SPIBUS_RELEASE_BUS(pdev, dev);
 				if (err == 0)
 					err = at45d_wait_ready(dev, &status);
 				if (err != 0 || (status & 0x40) != 0) {
Index: dev/flash/mx25l.c
===================================================================
--- dev/flash/mx25l.c	(revision 239916)
+++ dev/flash/mx25l.c	(working copy)
@@ -123,7 +123,9 @@
 	cmd.rx_cmd = rxBuf;
 	cmd.rx_cmd_sz = 2;
 	cmd.tx_cmd_sz = 2;
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	return (rxBuf[1]);
 }
 
@@ -157,7 +159,9 @@
 	 */
 	cmd.tx_cmd_sz = 4;
 	cmd.rx_cmd_sz = 4;
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	if (err)
 		return (NULL);
 
@@ -192,7 +196,9 @@
 	cmd.rx_cmd = rxBuf;
 	cmd.rx_cmd_sz = 1;
 	cmd.tx_cmd_sz = 1;
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 }
 
 static void
@@ -217,7 +223,9 @@
 	txBuf[1] = ((sector >> 16) & 0xff);
 	txBuf[2] = ((sector >> 8) & 0xff);
 	txBuf[3] = (sector & 0xff);
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 }
 
 static int
@@ -290,7 +298,9 @@
 		mx25l_wait_for_device_ready(dev);
 		mx25l_set_writable(dev, 1);
 
+		SPIBUS_ACQUIRE_BUS(pdev, dev);
 		err = SPIBUS_TRANSFER(pdev, dev, &cmd);
+		SPIBUS_RELEASE_BUS(pdev, dev);
 		if (err)
 			break;
 
@@ -339,7 +349,9 @@
 	cmd.rx_data = data;
 	cmd.rx_data_sz = count;
 
+	SPIBUS_ACQUIRE_BUS(pdev, dev);
 	err = SPIBUS_TRANSFER(pdev, dev, &cmd);
+	SPIBUS_RELEASE_BUS(pdev, dev);
 
 	return (err);
 }
@@ -480,7 +492,7 @@
 	DEVMETHOD(device_attach,	mx25l_attach),
 	DEVMETHOD(device_detach,	mx25l_detach),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static driver_t mx25l_driver = {
Index: arm/lpc/ssd1289.c
===================================================================
--- arm/lpc/ssd1289.c	(revision 239916)
+++ arm/lpc/ssd1289.c	(working copy)
@@ -179,6 +179,7 @@
 {
 	uint8_t buffer[2];
 
+	SPIBUS_ACQUIRE_BUS(device_get_parent(sc->ss_dev), sc->ss_dev);
 	ssd1289_set_dc(sc, 0);
 	buffer[0] = 0x00;
 	buffer[1] = addr & 0xff;
@@ -188,6 +189,7 @@
 	buffer[0] = (value >> 8) & 0xff;
 	buffer[1] = value & 0xff;
 	ssd1289_spi_send(sc, buffer, 2);
+	SPIBUS_RELEASE_BUS(device_get_parent(sc->ss_dev), sc->ss_dev);
 }
 
 static device_method_t ssd1289_methods[] = {
@@ -195,7 +197,7 @@
 	DEVMETHOD(device_probe,		ssd1289_probe),
 	DEVMETHOD(device_attach,	ssd1289_attach),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static devclass_t ssd1289_devclass;
Index: mips/atheros/pcf2123_rtc.c
===================================================================
--- mips/atheros/pcf2123_rtc.c	(revision 239916)
+++ mips/atheros/pcf2123_rtc.c	(working copy)
@@ -91,7 +91,9 @@
 	cmd.tx_cmd = txBuf;
 	cmd.rx_cmd_sz = sizeof(rxBuf);
 	cmd.tx_cmd_sz = sizeof(txBuf);
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	DELAY(PCF2123_DELAY);
 
 	return (0);
@@ -120,7 +122,9 @@
 	cmd.tx_cmd = txTimedate;
 	cmd.rx_cmd_sz = sizeof(rxTimedate);
 	cmd.tx_cmd_sz = sizeof(txTimedate);
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	DELAY(PCF2123_DELAY);
 
 	ct.nsec = 0;
@@ -178,7 +182,9 @@
 	txTimedate[6] = TOBCD(ct.mon);
 	txTimedate[7] = TOBCD(ct.year - YEAR_BASE);
 
+	SPIBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+	SPIBUS_RELEASE_BUS(device_get_parent(dev), dev);
 	DELAY(PCF2123_DELAY);
 
 	return (err);
@@ -191,7 +197,7 @@
 	DEVMETHOD(clock_gettime,	pcf2123_rtc_gettime),
 	DEVMETHOD(clock_settime,	pcf2123_rtc_settime),
 
-	{ 0, 0 },
+	DEVMETHOD_END
 };
 
 static driver_t pcf2123_rtc_driver = {

--Apple-Mail-58-1062540785
Content-Disposition: attachment;
	filename=spibus-03-controllers.diff
Content-Type: application/octet-stream;
	name="spibus-03-controllers.diff"
Content-Transfer-Encoding: 7bit

Index: mips/atheros/ar71xx_spi.c
===================================================================
--- mips/atheros/ar71xx_spi.c	(revision 239916)
+++ mips/atheros/ar71xx_spi.c	(working copy)
@@ -35,7 +35,9 @@
 #include <sys/interrupt.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
 #include <sys/module.h>
+#include <sys/mutex.h>
 #include <sys/rman.h>
 
 #include <vm/vm.h>
@@ -76,10 +78,21 @@
 
 struct ar71xx_spi_softc {
 	device_t		sc_dev;
+	device_t		sc_owner;
+	struct mtx		sc_mtx;
 	struct resource		*sc_mem_res;
 	uint32_t		sc_reg_ctrl;
 };
 
+#define	AR71XX_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define	AR71XX_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define	AR71XX_SPI_LOCK_INIT(_sc) \
+	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+	    "ar71xx_spi", MTX_DEF)
+#define	AR71XX_SPI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx)
+#define	AR71XX_SPI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
+#define	AR71XX_SPI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED)
+
 static int
 ar71xx_spi_probe(device_t dev)
 {
@@ -102,6 +115,7 @@
 		return (ENXIO);
 	}
 
+	AR71XX_SPI_LOCK_INIT(sc);
 
 	SPI_WRITE(sc, AR71XX_SPI_FS, 1);
 	sc->sc_reg_ctrl  = SPI_READ(sc, AR71XX_SPI_CTRL);
@@ -133,6 +147,49 @@
 	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK);
 }
 
+static void
+ar71xx_spi_acquire_bus(device_t dev, device_t child)
+{
+	struct spibus_ivar *devi = SPIBUS_IVAR(child);
+	struct ar71xx_spi_softc *sc;
+
+	sc = device_get_softc(dev);
+	AR71XX_SPI_ASSERT_UNLOCKED(sc);
+
+	/* Wait until bus is free and then set the new bus owner. */
+	AR71XX_SPI_LOCK(sc);
+	while (sc->sc_owner != NULL) {
+		mtx_sleep(sc, &sc->sc_mtx, 0, "ar71xx_spi", 0);
+	}
+	sc->sc_owner = child;
+	AR71XX_SPI_UNLOCK(sc);
+
+	/* Select the SPI device. */
+	ar71xx_spi_chip_activate(sc, devi->cs);
+}
+
+static void
+ar71xx_spi_release_bus(device_t dev, device_t child)
+{
+	struct spibus_ivar *devi = SPIBUS_IVAR(child);
+	struct ar71xx_spi_softc *sc;
+
+	sc = device_get_softc(dev);
+	AR71XX_SPI_ASSERT_UNLOCKED(sc);
+
+	/* Free the SPI BUS. */
+	AR71XX_SPI_LOCK(sc);
+	if (sc->sc_owner == NULL)
+		panic("ar71xx_spi: releasing unowned bus.");
+	if (sc->sc_owner != child)
+		panic("ar71xx_spi: you don't own the bus. game over.");
+	sc->sc_owner = NULL;
+	AR71XX_SPI_UNLOCK(sc);
+
+	/* Deselect the SPI device. */
+	ar71xx_spi_chip_deactivate(sc, devi->cs);
+}
+
 static uint8_t
 ar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data)
 {
@@ -173,7 +230,7 @@
 
 	sc = device_get_softc(dev);
 
-	ar71xx_spi_chip_activate(sc, devi->cs);
+	KASSERT(sc->sc_owner != NULL, ("SPI transfer on unowned bus"));
 
 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 
 	    ("TX/RX command sizes should be equal"));
@@ -196,8 +253,6 @@
 	for (i = 0; i < cmd->tx_data_sz; i++)
 		buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]);
 
-	ar71xx_spi_chip_deactivate(sc, devi->cs);
-
 	return (0);
 }
 
@@ -209,6 +264,8 @@
 	SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl);
 	SPI_WRITE(sc, AR71XX_SPI_FS, 0);
 
+	AR71XX_SPI_LOCK_DESTROY(sc);
+
 	if (sc->sc_mem_res)
 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
 
@@ -221,9 +278,11 @@
 	DEVMETHOD(device_attach,	ar71xx_spi_attach),
 	DEVMETHOD(device_detach,	ar71xx_spi_detach),
 
+	DEVMETHOD(spibus_acquire_bus,	ar71xx_spi_acquire_bus),
+	DEVMETHOD(spibus_release_bus,	ar71xx_spi_release_bus),
 	DEVMETHOD(spibus_transfer,	ar71xx_spi_transfer),
 
-	{0, 0}
+	DEVMETHOD_END
 };
 
 static driver_t ar71xx_spi_driver = {
Index: arm/lpc/lpc_spi.c
===================================================================
--- arm/lpc/lpc_spi.c	(revision 239916)
+++ arm/lpc/lpc_spi.c	(working copy)
@@ -67,6 +67,8 @@
 struct lpc_spi_softc
 {
 	device_t		ls_dev;
+	device_t		ls_owner;
+	struct mtx		ls_mtx;
 	struct resource *	ls_mem_res;
 	struct resource *	ls_irq_res;
 	bus_space_tag_t		ls_bst;
@@ -83,6 +85,15 @@
 #define	lpc_spi_write_4(_sc, _reg, _val)	\
     bus_space_write_4(_sc->ls_bst, _sc->ls_bsh, _reg, _val)
 
+#define	LPC_SPI_LOCK(_sc) mtx_lock(&(_sc)->ls_mtx)
+#define	LPC_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->ls_mtx)
+#define	LPC_SPI_LOCK_INIT(_sc) \
+	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->ls_dev), \
+	    "lpc_spi", MTX_DEF)
+#define	LPC_SPI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->ls_mtx)
+#define	LPC_SPI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->ls_mtx, MA_OWNED)
+#define	LPC_SPI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->ls_mtx, MA_NOTOWNED)
+
 static int
 lpc_spi_probe(device_t dev)
 {
@@ -126,6 +137,8 @@
 	lpc_spi_write_4(sc, LPC_SSP_CR1, LPC_SSP_CR1_SSE);
 	lpc_spi_write_4(sc, LPC_SSP_CPSR, 128);
 
+	LPC_SPI_LOCK_INIT(sc);
+
 	device_add_child(dev, "spibus", 0);
 	return (bus_generic_attach(dev));
 }
@@ -136,6 +149,49 @@
 	return (EBUSY);
 }
 
+static void
+lpc_spi_acquire_bus(device_t dev, device_t child)
+{
+	struct spibus_ivar *devi = SPIBUS_IVAR(child);
+	struct lpc_spi_softc *sc;
+
+	sc = device_get_softc(dev);
+	LPC_SPI_ASSERT_UNLOCKED(sc);
+
+	/* Wait until bus is free and then set the new bus owner. */
+	LPC_SPI_LOCK(sc);
+	while (sc->ls_owner != NULL) {
+		mtx_sleep(sc, &sc->ls_mtx, 0, "lpc_spi", 0);
+	}
+	sc->ls_owner = child;
+	LPC_SPI_UNLOCK(sc);
+
+	/* Set CS active */
+	lpc_gpio_set_state(child, devi->cs, 0);
+}
+
+static void
+lpc_spi_release_bus(device_t dev, device_t child)
+{
+	struct spibus_ivar *devi = SPIBUS_IVAR(child);
+	struct ar71xx_spi_softc *sc;
+
+	sc = device_get_softc(dev);
+	LPC_SPI_ASSERT_UNLOCKED(sc);
+
+	/* Free the SPI BUS. */
+	LPC_SPI_LOCK(sc);
+	if (sc->ls_owner == NULL)
+		panic("lpc_spi: releasing unowned bus.");
+	if (sc->ls_owner != child)
+		panic("lpc_spi: you don't own the bus. game over.");
+	sc->ls_owner = NULL;
+	LPC_SPI_UNLOCK(sc);
+
+	/* Set CS inactive */
+	lpc_gpio_set_state(child, devi->cs, 1);
+}
+
 static int
 lpc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 {
@@ -144,8 +200,7 @@
 	uint8_t *in_buf, *out_buf;
 	int i;
 
-	/* Set CS active */
-	lpc_gpio_set_state(child, devi->cs, 0);
+	KASSERT(sc->ls_owner != NULL, ("SPI transfer on unowned bus"));
 
 	/* Wait for FIFO to be ready */
 	while ((lpc_spi_read_4(sc, LPC_SSP_SR) & LPC_SSP_SR_TNF) == 0);
@@ -166,9 +221,6 @@
 		in_buf[i] = lpc_spi_read_4(sc, LPC_SSP_DR);
 	}
 
-	/* Set CS inactive */
-	lpc_gpio_set_state(child, devi->cs, 1);
-
 	return (0);
 }
 
@@ -179,9 +231,11 @@
 	DEVMETHOD(device_detach,	lpc_spi_detach),
 
 	/* SPI interface */
+	DEVMETHOD(spibus_acquire_bus,	lpc_spi_acquire_bus),
+	DEVMETHOD(spibus_release_bus,	lpc_spi_release_bus),
 	DEVMETHOD(spibus_transfer,	lpc_spi_transfer),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static devclass_t lpc_spi_devclass;

--Apple-Mail-58-1062540785--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?B54C4C9A-76F4-45CC-94C3-628DDF901051>