Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 22 Apr 2019 15:04:12 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r346557 - in stable/11: share/man/man4 sys/conf sys/dev/fdt sys/dev/flash sys/dev/spibus sys/modules sys/modules/fdt sys/modules/fdt/fdt_slicer sys/sys
Message-ID:  <201904221504.x3MF4COV062860@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Mon Apr 22 15:04:11 2019
New Revision: 346557
URL: https://svnweb.freebsd.org/changeset/base/346557

Log:
  MFC r335159, r344505-r344507, r344523, r344525-r344526, r344529, r344556,
  r344606-r344612, r344614-r344616, r344681, r344684-r344686, r344728,
  r344733-r344734, r344981
  
  A large set of changes that collectively modernize the at45d and mx25l
  (DataFlash and SpiFlash) drivers, add FDT support, and add geom_flashmap
  support to them.
  
  r335159 by manu:
  mx25l: Add pnp info
  
  r344505:
  Add a functional detach() implementation to make module unloading possible.
  
  r344506:
  Add support for probing/attaching on FDT-based systems.
  
  r344507:
  Switch to using config_intrhook_oneshot().  That allows the error handling
  in the delayed attach to use early returns, which allows reducing the level
  of indentation.  So all in all, what looks like a lot of changes is really
  no change in behavior, mostly just moving whitespace around.
  
  r344523:
  Include the jedec "extended device information string" in the criteria used
  to match a chip to our table of metadata describing the chips. At least one
  new DataFlash chip has a 3-byte jedec ID identical to its predecessors and
  differs only in the extended info, and it has different metadata requiring a
  unique entry in the table.  This paves the way for supporting such chips.
  
  The metadata table now includes two new fields, extmask and extid.  The two
  bytes of extended info obtained from the chip are ANDed with extmask then
  compared to extid, so it's possible to use only a subset of the extended
  info in the matching.
  
  We now always read 6 bytes of jedec ID info. Most chips don't return any
  extended info, and the values read back for those two bytes may be
  indeterminate, but such chips have extmask and extid values of 0x0000 in the
  table, so the extid effectively doesn't participate in the matching on those
  chips and it doesn't matter what they return in the extended info bytes.
  
  r344525:
  Add a metadata entry for the AT45DB641E chip.  This chip has the same 3-byte
  jedec ID as its older cousin the AT45DB642D, but uses a different page size.
  The only way to distinguish between the two chips is that the 2D chip has
  0 bytes of extended ID info and the new 1E has 1 byte of extended ID.  The
  actual value of the extended ID byte is all zeroes.  In other words, it's
  the presence of the extended info that identifies this chip. (Presumably
  a future upgrade might define non-zero values for the extended ID byte.)
  
  r344526:
  Resolve a name conflict when both SpiFlash and DataFlash devices are present.
  
  Both SpiFlash (mx25l) and DataFlash (at45d) drivers create a disk device
  with a name of /dev/flash/spiN where N is the driver's unit number.  If
  both types of devices are present in the same system, this creates a fatal
  conflict that prevents attachment of whichever device attaches second
  (because mx25l0 and at45d0 both try to create a spi0).
  
  This gives each type of device a unique name (mx25lN or at45dN respectively)
  and also adds an alias of spiN for compatibility.  When both device types
  appear in the same system, only the first to attach gets the spiN alias.
  When the second device attaches there is a non-fatal warning that the alias
  can't be created, but both devices are still accessible via their primary
  names (and there is no need for the spiN name to work for backwards
  compatibility on such a system, because it has never been possible to use
  the spiN names when both devices exist).
  
  r344529:
  Fix a paste-o that broke the build on all arches.
  
  r344556:
  Set maximum bus clock speed from hints when attaching hinted spibus(4) children.
  
  Some devices (such as spigen(4)) document that this works, but it appears that the
  code to implement it never got added.
  
  r344606:
  Add support for geom_flashmap by providing a getattr() for "SPI:device".
  
  r344607:
  Compile fdt_slicer and geom_flashmap when the at45d device is included.
  
  r344608:
  Update a comment to reflect reality; no functional changes.
  
  r344609:
  Make it possible to load fdt_slicer as a module (unloading works too fwiw).
  
  r344610:
  Add manpages for at45d(4) and mx25l(4).
  
  r344611:
  Add a module dependency on fdt_slicer.
  
  r344612:
  Add a module dependency on fdt_slicer.  Also, move the PNP_INFO to its more
  usual location, down near the DRIVER_MODULE() stuff.
  
  r344614:
  Rename some functions and variables to have shorter names, which allows
  unwrapping multiple lines of code.  Also, convert some short multiline
  comments into single-line comments.  Change old-school FALSE to false.
  
  All in all, no functional changes, it's just more compact and readable.
  
  r344615:
  Child nodes with a compatible property are not slices, according to the
  devicetree/bindings/mtd/partitions.txt document, so just ignore them.
  
  r344616:
  Add support to fdt_slicer for the new style partition data documented in
  devicetree/bindings/mtd/partition.txt.
  
  In the old style, all the children of the device node which did not have a
  compatible property were the partitions.  In the new style, there is a child
  node of the device which has a compatible string of "fixed-partitions", and
  its children are the individual partitions.
  
  Also, support the read-only property by setting the corresponding slice flag.
  
  r344681:
  Build fdt support modules on systems that use fdt data.
  
  kern.opts.mk sets make var OPT_FDT to a non-empty value if platform.h
  contains OPT_FDT.
  
  r344684:
  Undo accidental part of r344681.
  
  I think I must have accidentally mouse-click pasted while scrolling and
  didn't notice it.
  
  r344685:
  Add required header file to SRCS.
  
  r344686:
  Add another required header file.
  
  For some reason this seems to be required on aarch64, but I can build armv7
  from clean without needing this in the list.  (The file does get included,
  so the mystery is why armv7 works.)
  
  r344728:
  Bugfix: use a dummy buffer for the inactive side of a transfer.
  
  This is especially important for writes.  SPI is inherently a bidirectional
  bus; you receive data (even if it's garbage) while writing.  We should not
  receive that data into the same buffer we're writing to the device.
  
  When reading it doesn't matter what we send to the device, but using the
  dummy buffer for that as well is pleasingly symmetrical.
  
  r344733:
  Add some comments.  Give #define'd names to some scattered numbers.  Change
  some #define'd names to be more descriptive.  When reporting a post-write
  compare failure, report the page number, not the byte address of the page.
  The latter is the only functional change, it makes the number match the
  words of the error message.
  
  r344734:
  Allow the sector size of the disk device to be configured using hints or
  FDT data.  The sector size must be a multiple of the device's page size.
  If not configured, use the historical default of the device page size.
  
  Setting the disk sector size to 512 or 4096 allows a variety of standard
  filesystems to be used on the device.  Of course you wouldn't want to be
  writing frequently to a SPI flash chip like it was a disk drive, but for
  data that gets written once (or rarely) and read often, using a standard
  filesystem is a nice convenient thing.
  
  r344981:
  Give the mx25l device sole ownership of the name /dev/flash/spi* instead of
  trying to use disk_add_alias() to make spi* an alias for mx25l*.  It turns
  out disk_add_alias() works for partitions, but not slices, and that's hard
  to fix.
  
  This change is, in effect, a partial revert of r344526.
  
  The mips world relies on the existence of flashmap names formatted as
  /dev/flash/spi0s.name, whereas pretty much nothing relies on at45d devices
  using the /dev/spi* names (because until recently the at45d driver didn't
  even work reliably). So this change makes mx25l devices the sole owner of
  the /dev/flash/spi* namespace, which actually makes some sense because it is
  a SpiFlash(tm) device, so flash/spi isn't a horrible name.

Added:
  stable/11/share/man/man4/at45d.4
     - copied, changed from r344612, head/share/man/man4/at45d.4
  stable/11/share/man/man4/mx25l.4
     - copied unchanged from r344612, head/share/man/man4/mx25l.4
  stable/11/sys/modules/fdt/
     - copied from r344612, head/sys/modules/fdt/
Modified:
  stable/11/share/man/man4/Makefile
  stable/11/sys/conf/files
  stable/11/sys/dev/fdt/fdt_slicer.c
  stable/11/sys/dev/flash/at45d.c
  stable/11/sys/dev/flash/mx25l.c
  stable/11/sys/dev/spibus/spibus.c
  stable/11/sys/modules/Makefile
  stable/11/sys/modules/fdt/fdt_slicer/Makefile
  stable/11/sys/sys/slicer.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/share/man/man4/Makefile
==============================================================================
--- stable/11/share/man/man4/Makefile	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/share/man/man4/Makefile	Mon Apr 22 15:04:11 2019	(r346557)
@@ -55,6 +55,7 @@ MAN=	aac.4 \
 	${_apic.4} \
 	arcmsr.4 \
 	${_asmc.4} \
+	at45d.4 \
 	ata.4 \
 	ath.4 \
 	ath_ahb.4 \
@@ -314,6 +315,7 @@ MAN=	aac.4 \
 	mvs.4 \
 	mwl.4 \
 	mwlfw.4 \
+	mx25l.4 \
 	mxge.4 \
 	my.4 \
 	nand.4 \

Copied and modified: stable/11/share/man/man4/at45d.4 (from r344612, head/share/man/man4/at45d.4)
==============================================================================
--- head/share/man/man4/at45d.4	Tue Feb 26 22:52:41 2019	(r344612, copy source)
+++ stable/11/share/man/man4/at45d.4	Mon Apr 22 15:04:11 2019	(r346557)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 26, 2019
+.Dd March 2, 2019
 .Dt AT45D 4
 .Os
 .Sh NAME
@@ -126,6 +126,10 @@ The following properties are optional for the
 .Nm
 device subnode:
 .Bl -tag -width indent
+.It Va freebsd,sectorsize
+The sector size of the disk created for this storage device.
+It must be a multiple of the device's page size.
+The default is the device page size.
 .It Va spi-cpha
 Empty property indicating the slave device requires shifted clock
 phase (CPHA) mode.
@@ -156,6 +160,10 @@ The chip-select number to assert when performing I/O f
 Set the high bit (1 << 31) to invert the logic level of the chip select line.
 .It Va hint.at45d.%d.mode
 The SPI mode (0-3) to use when communicating with this device.
+.It Va hint.at45d.%d.sectorsize
+The sector size of the disk created for this storage device.
+It must be a multiple of the device's page size.
+The default is the device page size.
 .El
 .Sh FILES
 .Bl -tag -width /dev/flash/at45d?

Copied: stable/11/share/man/man4/mx25l.4 (from r344612, head/share/man/man4/mx25l.4)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/11/share/man/man4/mx25l.4	Mon Apr 22 15:04:11 2019	(r346557, copy of r344612, head/share/man/man4/mx25l.4)
@@ -0,0 +1,209 @@
+.\"
+.\" Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 26, 2019
+.Dt MX25L 4
+.Os
+.Sh NAME
+.Nm mx25l
+.Nd driver for SpiFlash(tm) compatible non-volatile storage devices
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device mx25l"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+mx25l_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the family of non-volatile storage
+devices known collectively as SpiFlash(tm).
+SpiFlash chips typically have part numbers beginning with EN25,
+IS25, MX25, S25, SST25, or W25.
+.Pp
+The
+.Nm
+driver uses opcode 0x9f to read the manufacturer and device ID
+data to determine whether the device is supported.
+The device ID is looked up using a table of data within the driver
+which describes the attributes of each supported device,
+such as block size, sector size, and device capacity.
+When a supported device is found, the
+.Nm
+driver creates a disk device and makes it accessible at
+.Pa /dev/flash/mx25l? .
+The new disk device is then tasted by the available
+.Xr geom 4
+modules as with any disk device.
+.Sh HARDWARE
+The
+.Nm
+driver provides support for the following devices:
+.Pp
+.Bl -bullet -compact
+.It
+AT25DF641
+.It
+EN25F32
+.It
+EN25P32
+.It
+EN25P64
+.It
+EN25Q32
+.It
+EN25Q64
+.It
+GD25Q64
+.It
+M25P32
+.It
+M25P64
+.It
+MX25L1606E
+.It
+MX25LL128
+.It
+MX25LL256
+.It
+MX25LL32
+.It
+MX25LL64
+.It
+S25FL032
+.It
+S25FL064
+.It
+S25FL128
+.It
+S25FL256S
+.It
+SST25VF010A
+.It
+SST25VF032B
+.It
+W25Q128
+.It
+W25Q256
+.It
+W25Q32
+.It
+W25Q64
+.It
+W25Q64BV
+.It
+W25X32
+.It
+W25X64
+.El
+.Sh FDT CONFIGURATION
+On an
+.Xr fdt 4
+based system, the
+.Nm
+device is defined as a slave device subnode
+of the SPI bus controller node.
+All properties documented in the
+.Va spibus.txt
+bindings document can be used with the
+.Nm
+device.
+The most commonly-used ones are documented below.
+.Pp
+The following properties are required in the
+.Nm
+device subnode:
+.Bl -tag -width indent
+.It Va compatible
+Must be the string "jedec,spi-nor".
+.It Va reg
+Chip select address of device.
+.It Va spi-max-frequency
+The maximum bus frequency to use when communicating with this slave device.
+Actual bus speed may be lower, depending on the capabilities of the SPI
+bus controller hardware.
+.El
+.Pp
+The following properties are optional for the
+.Nm
+device subnode:
+.Bl -tag -width indent
+.It Va spi-cpha
+Empty property indicating the slave device requires shifted clock
+phase (CPHA) mode.
+.It Va spi-cpol
+Empty property indicating the slave device requires inverse clock
+polarity (CPOL) mode.
+.It Va spi-cs-high
+Empty property indicating the slave device requires chip select active high.
+.El
+.Sh HINTS CONFIGURATION
+On a
+.Xr device.hints 5
+based system, such as
+.Li MIPS ,
+these values are configurable for
+.Nm :
+.Bl -tag -width indent
+.It Va hint.mx25l.%d.at
+The spibus the
+.Nm
+instance is attached to.
+.It Va hint.mx25l.%d.clock
+The maximum bus frequency to use when communicating with this device.
+Actual bus speed may be lower, depending on the capabilities of the SPI
+bus controller hardware.
+.It Va hint.mx25l.%d.cs
+The chip-select number to assert when performing I/O for this device.
+Set the high bit (1 << 31) to invert the logic level of the chip select line.
+.It Va hint.mx25l.%d.mode
+The SPI mode (0-3) to use when communicating with this device.
+.El
+.Sh FILES
+.Bl -tag -width /dev/flash/mx25l?
+.It Pa /dev/flash/mx25l?
+Provides read/write access to the storage device.
+.It Pa /dev/flash/spi?
+An alias for the
+.Pa /dev/mx25l?
+device, for backwards compatibility with older versions of the driver.
+.El
+.Sh SEE ALSO
+.Xr fdt 4 ,
+.Xr geom 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 8.0 .

Modified: stable/11/sys/conf/files
==============================================================================
--- stable/11/sys/conf/files	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/sys/conf/files	Mon Apr 22 15:04:11 2019	(r346557)
@@ -1724,7 +1724,7 @@ dev/fdt/fdt_clock_if.m		optional fdt fdt_clock
 dev/fdt/fdt_common.c		optional fdt
 dev/fdt/fdt_pinctrl.c		optional fdt fdt_pinctrl
 dev/fdt/fdt_pinctrl_if.m	optional fdt fdt_pinctrl
-dev/fdt/fdt_slicer.c		optional fdt cfi | fdt nand | fdt mx25l
+dev/fdt/fdt_slicer.c		optional fdt cfi | fdt nand | fdt mx25l | fdt at45d
 dev/fdt/fdt_static_dtb.S	optional fdt fdt_dtb_static \
 	dependency	"fdt_dtb_file"
 dev/fdt/simplebus.c		optional fdt
@@ -3456,7 +3456,7 @@ geom/geom_disk.c		standard
 geom/geom_dump.c		standard
 geom/geom_event.c		standard
 geom/geom_fox.c			optional geom_fox
-geom/geom_flashmap.c		optional fdt cfi | fdt nand | fdt mx25l | mmcsd
+geom/geom_flashmap.c		optional fdt cfi | fdt nand | fdt mx25l | mmcsd | fdt at45d
 geom/geom_io.c			standard
 geom/geom_kern.c		standard
 geom/geom_map.c			optional geom_map

Modified: stable/11/sys/dev/fdt/fdt_slicer.c
==============================================================================
--- stable/11/sys/dev/fdt/fdt_slicer.c	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/sys/dev/fdt/fdt_slicer.c	Mon Apr 22 15:04:11 2019	(r346557)
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/module.h>
 #include <sys/slicer.h>
 
 #include <dev/fdt/fdt_common.h>
@@ -43,42 +44,34 @@ __FBSDID("$FreeBSD$");
 #define debugf(fmt, args...)
 #endif
 
-static int fdt_flash_fill_slices(device_t dev, const char *provider,
+static int fill_slices(device_t dev, const char *provider,
     struct flash_slice *slices, int *slices_num);
 static void fdt_slicer_init(void);
 
 static int
-fdt_flash_fill_slices(device_t dev, const char *provider __unused,
-    struct flash_slice *slices, int *slices_num)
+fill_slices_from_node(phandle_t node, struct flash_slice *slices, int *count)
 {
-	char *slice_name;
-	phandle_t dt_node, dt_child;
+	char *label;
+	phandle_t child;
 	u_long base, size;
-	int i;
-	ssize_t name_len;
+	int flags, i;
+	ssize_t nmlen;
 
-	/*
-	 * We assume the caller provides buffer for FLASH_SLICES_MAX_NUM
-	 * flash_slice structures.
-	 */
-	if (slices == NULL) {
-		*slices_num = 0;
-		return (ENOMEM);
-	}
+	i = 0;
+	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+		flags = FLASH_SLICES_FLAG_NONE;
 
-	dt_node = ofw_bus_get_node(dev);
-	for (dt_child = OF_child(dt_node), i = 0; dt_child != 0;
-	    dt_child = OF_peer(dt_child)) {
+		/* Nodes with a compatible property are not slices. */
+		if (OF_hasprop(child, "compatible"))
+			continue;
 
 		if (i == FLASH_SLICES_MAX_NUM) {
 			debugf("not enough buffer for slice i=%d\n", i);
 			break;
 		}
 
-		/*
-		 * Retrieve start and size of the slice.
-		 */
-		if (fdt_regsize(dt_child, &base, &size) != 0) {
+		/* Retrieve start and size of the slice. */
+		if (fdt_regsize(child, &base, &size) != 0) {
 			debugf("error during processing reg property, i=%d\n",
 			    i);
 			continue;
@@ -89,50 +82,104 @@ fdt_flash_fill_slices(device_t dev, const char *provid
 			continue;
 		}
 
-		/*
-		 * Retrieve label.
-		 */
-		name_len = OF_getprop_alloc(dt_child, "label", sizeof(char),
-		    (void **)&slice_name);
-		if (name_len <= 0) {
+		/* Retrieve label. */
+		nmlen = OF_getprop_alloc(child, "label", sizeof(char),
+		    (void **)&label);
+		if (nmlen <= 0) {
 			/* Use node name if no label defined */
-			name_len = OF_getprop_alloc(dt_child, "name",
-			    sizeof(char), (void **)&slice_name);
-			if (name_len <= 0) {
+			nmlen = OF_getprop_alloc(child, "name", sizeof(char),
+			    (void **)&label);
+			if (nmlen <= 0) {
 				debugf("slice i=%d with no name\n", i);
-				slice_name = NULL;
+				label = NULL;
 			}
 		}
 
-		/*
-		 * Fill slice entry data.
-		 */
+		if (OF_hasprop(child, "read-only"))
+			flags |= FLASH_SLICES_FLAG_RO;
+
+		/* Fill slice entry data. */
 		slices[i].base = base;
 		slices[i].size = size;
-		slices[i].label = slice_name;
+		slices[i].label = label;
+		slices[i].flags = flags;
 		i++;
 	}
 
-	*slices_num = i;
+	*count = i;
 	return (0);
 }
 
+static int
+fill_slices(device_t dev, const char *provider __unused,
+    struct flash_slice *slices, int *slices_num)
+{
+	phandle_t child, node;
+
+	/*
+	 * We assume the caller provides buffer for FLASH_SLICES_MAX_NUM
+	 * flash_slice structures.
+	 */
+	if (slices == NULL) {
+		*slices_num = 0;
+		return (ENOMEM);
+	}
+
+	node = ofw_bus_get_node(dev);
+
+	/*
+	 * If there is a child node whose compatible is "fixed-partitions" then
+	 * we have new-style data where all partitions are the children of that
+	 * node.  Otherwise we have old-style data where all the children of the
+	 * device node are the partitions.
+	 */
+	child = fdt_find_compatible(node, "fixed-partitions", false);
+	if (child == 0)
+		return fill_slices_from_node(node, slices, slices_num);
+	else
+		return fill_slices_from_node(child, slices, slices_num);
+}
+
 static void
 fdt_slicer_init(void)
 {
 
-	flash_register_slicer(fdt_flash_fill_slices, FLASH_SLICES_TYPE_NAND,
-	   FALSE);
-	flash_register_slicer(fdt_flash_fill_slices, FLASH_SLICES_TYPE_CFI,
-	   FALSE);
-	flash_register_slicer(fdt_flash_fill_slices, FLASH_SLICES_TYPE_SPI,
-	   FALSE);
+	flash_register_slicer(fill_slices, FLASH_SLICES_TYPE_NAND, false);
+	flash_register_slicer(fill_slices, FLASH_SLICES_TYPE_CFI, false);
+	flash_register_slicer(fill_slices, FLASH_SLICES_TYPE_SPI, false);
 }
 
+static void
+fdt_slicer_cleanup(void)
+{
+
+	flash_register_slicer(NULL, FLASH_SLICES_TYPE_NAND, true);
+	flash_register_slicer(NULL, FLASH_SLICES_TYPE_CFI, true);
+	flash_register_slicer(NULL, FLASH_SLICES_TYPE_SPI, true);
+}
+
 /*
  * Must be initialized after GEOM classes (SI_SUB_DRIVERS/SI_ORDER_FIRST),
  * i. e. after g_init() is called, due to the use of the GEOM topology_lock
  * in flash_register_slicer().  However, must be before SI_SUB_CONFIGURE.
  */
-SYSINIT(fdt_slicer_rootconf, SI_SUB_DRIVERS, SI_ORDER_SECOND, fdt_slicer_init,
-    NULL);
+SYSINIT(fdt_slicer, SI_SUB_DRIVERS, SI_ORDER_SECOND, fdt_slicer_init, NULL);
+SYSUNINIT(fdt_slicer, SI_SUB_DRIVERS, SI_ORDER_SECOND, fdt_slicer_cleanup, NULL);
+
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+
+	/*
+	 * Nothing to do here: the SYSINIT/SYSUNINIT defined above run
+	 * automatically at module load/unload time.
+	 */
+	return (0);
+}
+
+static moduledata_t fdt_slicer_mod = {
+	"fdt_slicer", mod_handler, NULL
+};
+
+DECLARE_MODULE(fdt_slicer, fdt_slicer_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(fdt_slicer, 1);

Modified: stable/11/sys/dev/flash/at45d.c
==============================================================================
--- stable/11/sys/dev/flash/at45d.c	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/sys/dev/flash/at45d.c	Mon Apr 22 15:04:11 2019	(r346557)
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/bio.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
+#include <sys/endian.h>
 #include <sys/kernel.h>
 #include <sys/kthread.h>
 #include <sys/lock.h>
@@ -45,10 +46,36 @@ __FBSDID("$FreeBSD$");
 #include <dev/spibus/spi.h>
 #include "spibus_if.h"
 
+#include "opt_platform.h"
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+static struct ofw_compat_data compat_data[] = {
+	{ "atmel,at45",		1 },
+	{ "atmel,dataflash",	1 },
+	{ NULL,			0 },
+};
+#endif
+
+/* This is the information returned by the MANUFACTURER_ID command. */
+struct at45d_mfg_info {
+	uint32_t	jedec_id; /* Mfg ID, DevId1, DevId2, ExtLen */
+	uint16_t	ext_id;   /* ExtId1, ExtId2 */
+};
+
+/*
+ * This is an entry in our table of metadata describing the chips.  We match on
+ * both jedec id and extended id info returned by the MANUFACTURER_ID command.
+ */
 struct at45d_flash_ident
 {
 	const char	*name;
 	uint32_t	jedec;
+	uint16_t	extid;
+	uint16_t	extmask;
 	uint16_t	pagecount;
 	uint16_t	pageoffset;
 	uint16_t	pagesize;
@@ -61,13 +88,18 @@ struct at45d_softc
 	struct mtx		sc_mtx;
 	struct disk		*disk;
 	struct proc		*p;
-	struct intr_config_hook	config_intrhook;
 	device_t		dev;
+	u_int			taskstate;
 	uint16_t		pagecount;
 	uint16_t		pageoffset;
 	uint16_t		pagesize;
+	void			*dummybuf;
 };
 
+#define	TSTATE_STOPPED	0
+#define	TSTATE_STOPPING	1
+#define	TSTATE_RUNNING	2
+
 #define	AT45D_LOCK(_sc)			mtx_lock(&(_sc)->sc_mtx)
 #define	AT45D_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
 #define	AT45D_LOCK_INIT(_sc) \
@@ -85,36 +117,51 @@ static device_probe_t at45d_probe;
 /* disk routines */
 static int at45d_close(struct disk *dp);
 static int at45d_open(struct disk *dp);
+static int at45d_getattr(struct bio *bp);
 static void at45d_strategy(struct bio *bp);
 static void at45d_task(void *arg);
 
 /* helper routines */
 static void at45d_delayed_attach(void *xsc);
-static int at45d_get_mfg_info(device_t dev, uint8_t *resp);
+static int at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp);
 static int at45d_get_status(device_t dev, uint8_t *status);
 static int at45d_wait_ready(device_t dev, uint8_t *status);
 
-#define	BUFFER_TRANSFER			0x53
-#define	BUFFER_COMPARE			0x60
+#define	PAGE_TO_BUFFER_TRANSFER		0x53
+#define	PAGE_TO_BUFFER_COMPARE		0x60
 #define	PROGRAM_THROUGH_BUFFER		0x82
 #define	MANUFACTURER_ID			0x9f
 #define	STATUS_REGISTER_READ		0xd7
 #define	CONTINUOUS_ARRAY_READ		0xe8
 
+#define	STATUS_READY			(1u << 7)
+#define	STATUS_CMPFAIL			(1u << 6)
+#define	STATUS_PAGE2N			(1u << 0)
+
 /*
+ * Metadata for supported chips.
+ *
+ * The jedec id in this table includes the extended id length byte.  A match is
+ * based on both jedec id and extended id matching.  The chip's extended id (not
+ * present in most chips) is ANDed with ExtMask and the result is compared to
+ * ExtId.  If a chip only returns 1 ext id byte it will be in the upper 8 bits
+ * of ExtId in this table.
+ *
  * A sectorsize2n != 0 is used to indicate that a device optionally supports
  * 2^N byte pages.  If support for the latter is enabled, the sector offset
  * has to be reduced by one.
  */
 static const struct at45d_flash_ident at45d_flash_devices[] = {
-	{ "AT45DB011B", 0x1f2200, 512, 9, 264, 256 },
-	{ "AT45DB021B", 0x1f2300, 1024, 9, 264, 256 },
-	{ "AT45DB041x", 0x1f2400, 2028, 9, 264, 256 },
-	{ "AT45DB081B", 0x1f2500, 4096, 9, 264, 256 },
-	{ "AT45DB161x", 0x1f2600, 4096, 10, 528, 512 },
-	{ "AT45DB321x", 0x1f2700, 8192, 10, 528, 0 },
-	{ "AT45DB321x", 0x1f2701, 8192, 10, 528, 512 },
-	{ "AT45DB642x", 0x1f2800, 8192, 11, 1056, 1024 }
+	/* Part Name    Jedec ID    ExtId   ExtMask PgCnt Offs PgSz PgSz2n */
+	{ "AT45DB011B", 0x1f220000, 0x0000, 0x0000,   512,  9,  264,  256 },
+	{ "AT45DB021B", 0x1f230000, 0x0000, 0x0000,  1024,  9,  264,  256 },
+	{ "AT45DB041x", 0x1f240000, 0x0000, 0x0000,  2028,  9,  264,  256 },
+	{ "AT45DB081B", 0x1f250000, 0x0000, 0x0000,  4096,  9,  264,  256 },
+	{ "AT45DB161x", 0x1f260000, 0x0000, 0x0000,  4096, 10,  528,  512 },
+	{ "AT45DB321x", 0x1f270000, 0x0000, 0x0000,  8192, 10,  528,    0 },
+	{ "AT45DB321x", 0x1f270100, 0x0000, 0x0000,  8192, 10,  528,  512 },
+	{ "AT45DB641E", 0x1f280001, 0x0000, 0xff00, 32768,  9,  264,  256 },
+	{ "AT45DB642x", 0x1f280000, 0x0000, 0x0000,  8192, 11, 1056, 1024 },
 };
 
 static int
@@ -138,7 +185,7 @@ at45d_get_status(device_t dev, uint8_t *status)
 }
 
 static int
-at45d_get_mfg_info(device_t dev, uint8_t *resp)
+at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp)
 {
 	uint8_t rxBuf[8], txBuf[8];
 	struct spi_command cmd;
@@ -151,11 +198,14 @@ at45d_get_mfg_info(device_t dev, uint8_t *resp)
 	txBuf[0] = MANUFACTURER_ID;
 	cmd.tx_cmd = &txBuf;
 	cmd.rx_cmd = &rxBuf;
-	cmd.tx_cmd_sz = cmd.rx_cmd_sz = 5;
+	cmd.tx_cmd_sz = cmd.rx_cmd_sz = 7;
 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
 	if (err)
 		return (err);
-	memcpy(resp, rxBuf + 1, 4);
+
+	resp->jedec_id = be32dec(rxBuf + 1);
+	resp->ext_id   = be16dec(rxBuf + 5);
+
 	return (0);
 }
 
@@ -173,16 +223,29 @@ at45d_wait_ready(device_t dev, uint8_t *status)
 			err = ETIMEDOUT;
 		else
 			err = at45d_get_status(dev, status);
-	} while (err == 0 && (*status & 0x80) == 0);
+	} while (err == 0 && !(*status & STATUS_READY));
 	return (err);
 }
 
 static int
 at45d_probe(device_t dev)
 {
+	int rv;
 
+#ifdef FDT
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	rv = BUS_PROBE_DEFAULT;
+#else
+	rv = BUS_PROBE_NOWILDCARD;
+#endif
+
 	device_set_desc(dev, "AT45D Flash Family");
-	return (0);
+	return (rv);
 }
 
 static int
@@ -194,81 +257,135 @@ at45d_attach(device_t dev)
 	sc->dev = dev;
 	AT45D_LOCK_INIT(sc);
 
-	/* We'll see what kind of flash we have later... */
-	sc->config_intrhook.ich_func = at45d_delayed_attach;
-	sc->config_intrhook.ich_arg = sc;
-	if (config_intrhook_establish(&sc->config_intrhook) != 0) {
-		device_printf(dev, "config_intrhook_establish failed\n");
-		return (ENOMEM);
-	}
+	config_intrhook_oneshot(at45d_delayed_attach, sc);
 	return (0);
 }
 
 static int
 at45d_detach(device_t dev)
 {
+	struct at45d_softc *sc;
+	int err;
 
-	return (EBUSY) /* XXX */;
+	sc = device_get_softc(dev);
+	err = 0;
+
+	AT45D_LOCK(sc);
+	if (sc->taskstate == TSTATE_RUNNING) {
+		sc->taskstate = TSTATE_STOPPING;
+		wakeup(sc);
+		while (err == 0 && sc->taskstate != TSTATE_STOPPED) {
+			err = msleep(sc, &sc->sc_mtx, 0, "at45dt", hz * 3);
+			if (err != 0) {
+				sc->taskstate = TSTATE_RUNNING;
+				device_printf(sc->dev,
+				    "Failed to stop queue task\n");
+			}
+		}
+	}
+	AT45D_UNLOCK(sc);
+
+	if (err == 0 && sc->taskstate == TSTATE_STOPPED) {
+		if (sc->disk) {
+			disk_destroy(sc->disk);
+			bioq_flush(&sc->bio_queue, NULL, ENXIO);
+			free(sc->dummybuf, M_DEVBUF);
+		}
+		AT45D_LOCK_DESTROY(sc);
+	}
+	return (err);
 }
 
 static void
 at45d_delayed_attach(void *xsc)
 {
 	struct at45d_softc *sc;
+	struct at45d_mfg_info mfginfo;
 	const struct at45d_flash_ident *ident;
 	u_int i;
+	int sectorsize;
 	uint32_t jedec;
 	uint16_t pagesize;
-	uint8_t buf[4], status;
+	uint8_t status;
 
 	sc = xsc;
 	ident = NULL;
 	jedec = 0;
 
-	if (at45d_wait_ready(sc->dev, &status) != 0)
+	if (at45d_wait_ready(sc->dev, &status) != 0) {
 		device_printf(sc->dev, "Error waiting for device-ready.\n");
-	else if (at45d_get_mfg_info(sc->dev, buf) != 0)
+		return;
+	}
+	if (at45d_get_mfg_info(sc->dev, &mfginfo) != 0) {
 		device_printf(sc->dev, "Failed to get ID.\n");
-	else {
-		jedec = buf[0] << 16 | buf[1] << 8 | buf[2];
-		for (i = 0; i < nitems(at45d_flash_devices); i++) {
-			if (at45d_flash_devices[i].jedec == jedec) {
-				ident = &at45d_flash_devices[i];
-				break;
-			}
+		return;
+	}
+	for (i = 0; i < nitems(at45d_flash_devices); i++) {
+		ident = &at45d_flash_devices[i];
+		if (mfginfo.jedec_id == ident->jedec && 
+		    (mfginfo.ext_id & ident->extmask) == ident->extid) {
+			break;
 		}
 	}
-	if (ident == NULL)
+	if (i == nitems(at45d_flash_devices)) {
 		device_printf(sc->dev, "JEDEC 0x%x not in list.\n", jedec);
-	else {
-		sc->pagecount = ident->pagecount;
-		sc->pageoffset = ident->pageoffset;
-		if (ident->pagesize2n != 0 && (status & 0x01) != 0) {
-			sc->pageoffset -= 1;
-			pagesize = ident->pagesize2n;
-		} else
-			pagesize = ident->pagesize;
-		sc->pagesize = pagesize;
+		return;
+	}
 
-		sc->disk = disk_alloc();
-		sc->disk->d_open = at45d_open;
-		sc->disk->d_close = at45d_close;
-		sc->disk->d_strategy = at45d_strategy;
-		sc->disk->d_name = "flash/spi";
-		sc->disk->d_drv1 = sc;
-		sc->disk->d_maxsize = DFLTPHYS;
-		sc->disk->d_sectorsize = pagesize;
-		sc->disk->d_mediasize = pagesize * ident->pagecount;
-		sc->disk->d_unit = device_get_unit(sc->dev);
-		disk_create(sc->disk, DISK_VERSION);
-		bioq_init(&sc->bio_queue);
-		kproc_create(&at45d_task, sc, &sc->p, 0, 0,
-		    "task: at45d flash");
-		device_printf(sc->dev, "%s, %d bytes per page, %d pages\n",
-		    ident->name, pagesize, ident->pagecount);
+	sc->pagecount = ident->pagecount;
+	sc->pageoffset = ident->pageoffset;
+	if (ident->pagesize2n != 0 && (status & STATUS_PAGE2N)) {
+		sc->pageoffset -= 1;
+		pagesize = ident->pagesize2n;
+	} else
+		pagesize = ident->pagesize;
+	sc->pagesize = pagesize;
+
+	/*
+	 * By default we set up a disk with a sector size that matches the
+	 * device page size.  If there is a device hint or fdt property
+	 * requesting a different size, use that, as long as it is a multiple of
+	 * the device page size).
+	 */
+	sectorsize = pagesize;
+#ifdef FDT
+	{
+		pcell_t size;
+		if (OF_getencprop(ofw_bus_get_node(sc->dev),
+		    "freebsd,sectorsize", &size, sizeof(size)) > 0)
+			sectorsize = size;
 	}
+#endif
+	resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev),
+	    "sectorsize", &sectorsize);
 
-	config_intrhook_disestablish(&sc->config_intrhook);
+	if ((sectorsize % pagesize) != 0) {
+		device_printf(sc->dev, "Invalid sectorsize %d, "
+		    "must be a multiple of %d\n", sectorsize, pagesize);
+		return;
+	}
+
+	sc->dummybuf = malloc(pagesize, M_DEVBUF, M_WAITOK | M_ZERO);
+
+	sc->disk = disk_alloc();
+	sc->disk->d_open = at45d_open;
+	sc->disk->d_close = at45d_close;
+	sc->disk->d_strategy = at45d_strategy;
+	sc->disk->d_getattr = at45d_getattr;
+	sc->disk->d_name = "flash/at45d";
+	sc->disk->d_drv1 = sc;
+	sc->disk->d_maxsize = DFLTPHYS;
+	sc->disk->d_sectorsize = sectorsize;
+	sc->disk->d_mediasize = pagesize * ident->pagecount;
+	sc->disk->d_unit = device_get_unit(sc->dev);
+	disk_create(sc->disk, DISK_VERSION);
+	bioq_init(&sc->bio_queue);
+	kproc_create(&at45d_task, sc, &sc->p, 0, 0, "task: at45d flash");
+	sc->taskstate = TSTATE_RUNNING;
+	device_printf(sc->dev,
+	    "%s, %d bytes per page, %d pages; %d KBytes; disk sector size %d\n",
+	    ident->name, pagesize, ident->pagecount,
+	    (pagesize * ident->pagecount) / 1024, sectorsize);
 }
 
 static int
@@ -285,6 +402,26 @@ at45d_close(struct disk *dp)
 	return (0);
 }
 
+static int
+at45d_getattr(struct bio *bp)
+{
+	struct at45d_softc *sc;
+
+	/*
+	 * This function exists to support geom_flashmap and fdt_slicer.
+	 */
+
+	if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
+		return (ENXIO);
+	if (strcmp(bp->bio_attribute, "SPI::device") != 0)
+		return (-1);
+	sc = bp->bio_disk->d_drv1;
+	if (bp->bio_length != sizeof(sc->dev))
+		return (EFAULT);
+	bcopy(&sc->dev, bp->bio_data, sizeof(sc->dev));
+	return (0);
+}
+
 static void
 at45d_strategy(struct bio *bp)
 {
@@ -322,9 +459,15 @@ at45d_task(void *arg)
 	for (;;) {
 		AT45D_LOCK(sc);
 		do {
+			if (sc->taskstate == TSTATE_STOPPING) {
+				sc->taskstate = TSTATE_STOPPED;
+				AT45D_UNLOCK(sc);
+				wakeup(sc);
+				kproc_exit(0);
+			}
 			bp = bioq_takefirst(&sc->bio_queue);
 			if (bp == NULL)
-				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
+				msleep(sc, &sc->sc_mtx, PRIBIO, "at45dq", 0);
 		} while (bp == NULL);
 		AT45D_UNLOCK(sc);
 
@@ -338,11 +481,13 @@ at45d_task(void *arg)
 		case BIO_READ:
 			txBuf[0] = CONTINUOUS_ARRAY_READ;
 			cmd.tx_cmd_sz = cmd.rx_cmd_sz = 8;
-			cmd.tx_data = cmd.rx_data = buf;
+			cmd.tx_data = sc->dummybuf;
+			cmd.rx_data = buf;
 			break;
 		case BIO_WRITE:
 			cmd.tx_cmd_sz = cmd.rx_cmd_sz = 4;
-			cmd.tx_data = cmd.rx_data = buf;
+			cmd.tx_data = buf;
+			cmd.rx_data = sc->dummybuf;
 			if (resid + offset > sc->pagesize)
 				len = sc->pagesize - offset;
 			break;
@@ -361,14 +506,19 @@ at45d_task(void *arg)
 			}
 			addr = page << sc->pageoffset;
 			if (bp->bio_cmd == BIO_WRITE) {
+				/*
+				 * If writing less than a full page, transfer
+				 * the existing page to the buffer, so that our
+				 * PROGRAM_THROUGH_BUFFER below will preserve
+				 * the parts of the page we're not writing.
+				 */
 				if (len != sc->pagesize) {
-					txBuf[0] = BUFFER_TRANSFER;
+					txBuf[0] = PAGE_TO_BUFFER_TRANSFER;
 					txBuf[1] = ((addr >> 16) & 0xff);
 					txBuf[2] = ((addr >> 8) & 0xff);
 					txBuf[3] = 0;
 					cmd.tx_data_sz = cmd.rx_data_sz = 0;
-					err = SPIBUS_TRANSFER(pdev, dev,
-					    &cmd);
+					err = SPIBUS_TRANSFER(pdev, dev, &cmd);
 					if (err == 0)
 						err = at45d_wait_ready(dev,
 						    &status);
@@ -394,7 +544,7 @@ at45d_task(void *arg)
 			}
 			if (bp->bio_cmd == BIO_WRITE) {
 				addr = page << sc->pageoffset;
-				txBuf[0] = BUFFER_COMPARE;
+				txBuf[0] = PAGE_TO_BUFFER_COMPARE;
 				txBuf[1] = ((addr >> 16) & 0xff);
 				txBuf[2] = ((addr >> 8) & 0xff);
 				txBuf[3] = 0;
@@ -402,9 +552,9 @@ at45d_task(void *arg)
 				err = SPIBUS_TRANSFER(pdev, dev, &cmd);
 				if (err == 0)
 					err = at45d_wait_ready(dev, &status);
-				if (err != 0 || (status & 0x40) != 0) {
+				if (err != 0 || (status & STATUS_CMPFAIL)) {
 					device_printf(dev, "comparing page "
-					    "%d failed (status=0x%x)\n", addr,
+					    "%d failed (status=0x%x)\n", page,
 					    status);
 					berr = EIO;
 					goto out;
@@ -418,7 +568,10 @@ at45d_task(void *arg)
 				len = sc->pagesize;
 			else
 				len = resid;
-			cmd.tx_data = cmd.rx_data = buf;
+			if (bp->bio_cmd == BIO_READ)
+				cmd.rx_data = buf;
+			else
+				cmd.tx_data = buf;
 		}
  out:
 		if (berr != 0) {
@@ -449,3 +602,7 @@ static driver_t at45d_driver = {
 
 DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, NULL, NULL);
 MODULE_DEPEND(at45d, spibus, 1, 1, 1);
+#ifdef FDT
+MODULE_DEPEND(at45d, fdt_slicer, 1, 1, 1);
+#endif
+

Modified: stable/11/sys/dev/flash/mx25l.c
==============================================================================
--- stable/11/sys/dev/flash/mx25l.c	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/sys/dev/flash/mx25l.c	Mon Apr 22 15:04:11 2019	(r346557)
@@ -681,3 +681,4 @@ static driver_t mx25l_driver = {
 
 DRIVER_MODULE(mx25l, spibus, mx25l_driver, mx25l_devclass, 0, 0);
 MODULE_DEPEND(mx25l, spibus, 1, 1, 1);
+MODULE_DEPEND(mx25l, fdt_slicer, 1, 1, 1);

Modified: stable/11/sys/dev/spibus/spibus.c
==============================================================================
--- stable/11/sys/dev/spibus/spibus.c	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/sys/dev/spibus/spibus.c	Mon Apr 22 15:04:11 2019	(r346557)
@@ -216,6 +216,7 @@ spibus_hinted_child(device_t bus, const char *dname, i
 	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
 	devi = SPIBUS_IVAR(child);
 	devi->mode = SPIBUS_MODE_NONE;
+	resource_int_value(dname, dunit, "clock", &devi->clock);
 	resource_int_value(dname, dunit, "cs", &devi->cs);
 	resource_int_value(dname, dunit, "mode", &devi->mode);
 }

Modified: stable/11/sys/modules/Makefile
==============================================================================
--- stable/11/sys/modules/Makefile	Mon Apr 22 14:53:53 2019	(r346556)
+++ stable/11/sys/modules/Makefile	Mon Apr 22 15:04:11 2019	(r346557)
@@ -500,6 +500,10 @@ SUBDIR+=	iscsi
 SUBDIR+=	iscsi_initiator
 .endif
 
+.if !empty(OPT_FDT)
+SUBDIR+=	fdt
+.endif
+
 .if ${MK_NAND} != "no" || defined(ALL_MODULES)
 _nandfs=	nandfs

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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