* [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface
@ 2026-08-03 10:46 Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 1/2] " Vishwaroop A
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Vishwaroop A @ 2026-08-03 10:46 UTC (permalink / raw)
To: broonie
Cc: linux-spi, jonathanh, thierry.reding, corbet, linux-doc,
smangipudi, va
Add I2C-style new_device/delete_device sysfs attributes to SPI host
controllers, allowing userspace to instantiate and remove SPI devices
at runtime without device-tree changes.
Patch 1 adds the new_device/delete_device attributes and the supporting
infrastructure (userspace_clients list, manual sysfs group registration).
Patch 2 adds the ABI and user-facing documentation.
Changes since v8:
- Restrict the interface to SPI host controllers. (Mark Brown)
- Emit a change uevent after the late-created sysfs attributes become
available, so userspace can detect when the interface is ready.
(Mark Brown)
- Update the ABI date and target kernel version, and clarify the
host-only interface and change notification in the documentation.
(Mark Brown)
- Rebase on next-20260731 (v7.2-rc5).
Changes since v7:
- Remove the redundant CONFIG_SPI_DYNAMIC guard around
spi_controller_groups[]. (Jon Hunter)
- Restore the original add_lock release point; the userspace sysfs
group is removed and drained before the lock is taken. (Jon Hunter)
- Remove the redundant dead flag and retain the base-tree
device_is_registered() teardown check. (Jon Hunter)
- Simplify parsing, diagnostics and comments. (Jon Hunter)
Changes since v6:
- new_device_store(): reject empty device names. (Mark Brown)
- Move sysfs userspace group registration out of the automatic
ctlr->dev.groups path; create it manually at the end of
spi_register_controller() and remove it at the start of
spi_unregister_controller(). (Mark Brown)
- Drop userspace_clients_lock; reuse the existing add_lock.
new_device_store() calls __spi_add_device() directly under
add_lock; delete_device_store() also takes add_lock.
new_device is DEVICE_ATTR_IGNORE_LOCKDEP alongside delete_device
to silence the class-level splat. (Jon Hunter)
- kernel-doc / inline comment updates for the new struct fields.
Changes since v5:
- Rebased on next-20260727 (v7.2-rc5); applies cleanly.
- kernel-doc entries for the new struct spi_controller /
struct spi_device fields.
- new_device_store(): reject chip_select > U8_MAX; parse with %u.
- new_device_store(): reject mode bits outside SPI_MODE_USER_MASK.
- new_device_store(): drop the unused 'n' / '%n' from sscanf().
Changes since v4:
- Removed spi_unregister_device() call from new_device_store()'s
controller-teardown error path.
Changes since v3:
- Replaced holding add_lock across __spi_add_device() + list
insertion with the current design: the sysfs group is removed
before add_lock is taken, so kernfs_drain() serialises stores
with controller unregister.
Changes since v2:
- Gated sysfs attributes and locking on CONFIG_SPI_DYNAMIC.
Changes since v1:
- Added locking to prevent races between new_device_store() and
concurrent spi_unregister_controller().
Link: https://lore.kernel.org/linux-spi/20260728191056.2337791-1-va@nvidia.com/ # v8
Link: https://lore.kernel.org/linux-spi/20260728030541.2279518-1-va@nvidia.com/ # v7
Link: https://lore.kernel.org/linux-spi/cover.1784000000.git.va@nvidia.com/ # v6
Link: https://lore.kernel.org/linux-spi/20260517201602.498135-1-va@nvidia.com/ # v5
Link: https://lore.kernel.org/linux-tegra/909f0c92-d110-4253-903e-5c81e21e12c9@nvidia.com/
Vishwaroop A (2):
spi: add new_device/delete_device sysfs interface
docs: spi: add documentation for userspace device instantiation
.../ABI/testing/sysfs-class-spi-master | 34 +++
Documentation/spi/index.rst | 1 +
Documentation/spi/instantiating-devices.rst | 88 +++++++
drivers/spi/spi.c | 246 ++++++++++++++++++
include/linux/spi/spi.h | 18 ++
5 files changed, 387 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-spi-master
create mode 100644 Documentation/spi/instantiating-devices.rst
base-commit: 415606a7be939835db9b0d6b711887586646346d
--
2.17.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v9 1/2] spi: add new_device/delete_device sysfs interface
2026-08-03 10:46 [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Vishwaroop A
@ 2026-08-03 10:46 ` Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 2/2] docs: spi: add documentation for userspace device instantiation Vishwaroop A
2026-08-06 18:14 ` [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Mark Brown
2 siblings, 0 replies; 4+ messages in thread
From: Vishwaroop A @ 2026-08-03 10:46 UTC (permalink / raw)
To: broonie
Cc: linux-spi, jonathanh, thierry.reding, corbet, linux-doc,
smangipudi, va
Development boards such as the Jetson AGX Orin expose SPI buses
on expansion headers (e.g. the 40-pin header) so that users can
connect and interact with SPI peripherals from userspace. The
standard way to get /dev/spidevB.C character device nodes for
this purpose is to register spi_device instances backed by the
spidev driver.
Today there is no viable way to do this on upstream kernels:
- The spidev driver rejects the bare "spidev" compatible
string in DT, since spidev is a Linux software interface
and not a description of real hardware.
- Vendor-specific compatible strings (e.g. "nvidia,tegra-spidev")
have been rejected by DT maintainers for the same reason.
The I2C subsystem solved an analogous problem by exposing
new_device/delete_device sysfs attributes on each adapter. Add
the same interface to SPI host controllers, so that userspace
(e.g. a systemd unit at boot) can instantiate SPI devices at
runtime without needing anything in device-tree.
The new_device file accepts:
<modalias> <chip_select> [<max_speed_hz> [<mode>]]
where chip_select is required, while max_speed_hz and mode are
optional and default to 0 if omitted. max_speed_hz == 0 is
clamped to the controller's maximum by spi_setup(); mode == 0
selects SPI mode 0 (CPOL=0, CPHA=0).
The modalias is used both as the device identifier and as a
driver_override, so that the device binds to the named driver
directly. This is necessary because some drivers like spidev
deliberately exclude generic names from their id_table.
Devices created this way are limited compared to those declared
via DT or board files:
- No IRQ is assigned (the device gets IRQ 0 / no interrupt).
- No platform_data or device properties are attached.
- No OF node is associated with the device.
These limitations are acceptable for spidev, which only needs a
registered spi_device to expose a character device to userspace.
Only devices created via new_device can be removed through
delete_device; DT and platform devices are unaffected.
The sysfs attributes are gated behind CONFIG_SPI_DYNAMIC since
this feature adds a new way of dynamically instantiating and
removing SPI devices, and the add_lock locking in
spi_unregister_controller() is already conditional on
CONFIG_SPI_DYNAMIC.
The userspace sysfs group is created manually as the last step
of spi_register_controller() and removed as the first step of
spi_unregister_controller(). Removing the group before taking
add_lock means kernfs_drain() completes any in-flight
new_device_store()/delete_device_store() calls before
add_lock is acquired, so unregister never blocks on a store
that is itself waiting for add_lock and no store can be
touching an spi_device that is about to be torn down.
Non-sysfs callers of __spi_add_device() (DT/ACPI dynamic add,
ancillary registration) continue to be protected by the
pre-existing !device_is_registered(&ctlr->dev) check added in
commit ddf75be47ca7 ("spi: Prevent adding devices below an
unregistering controller"): device_del(&ctlr->dev) runs inside
add_lock in spi_unregister_controller() so state_in_sysfs flips
to 0 before add_lock is released.
Link: https://lore.kernel.org/linux-tegra/909f0c92-d110-4253-903e-5c81e21e12c9@nvidia.com/
Signed-off-by: Vishwaroop A <va@nvidia.com>
---
drivers/spi/spi.c | 246 ++++++++++++++++++++++++++++++++++++++++
include/linux/spi/spi.h | 18 +++
2 files changed, 264 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d9e6b4b87c89..07c572e1df5d 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -43,6 +43,7 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop);
#include "internals.h"
static int __spi_setup(struct spi_device *spi, bool initial_setup);
+static int __spi_add_device(struct spi_device *spi, struct spi_device *parent);
static DEFINE_IDR(spi_controller_idr);
@@ -297,6 +298,192 @@ static const struct attribute_group spi_controller_statistics_group = {
.attrs = spi_controller_statistics_attrs,
};
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+
+/*
+ * new_device_store - instantiate a new SPI device from userspace
+ *
+ * Takes parameters: <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+ *
+ * Examples:
+ * echo spidev 0 > new_device
+ * echo spidev 0 10000000 > new_device
+ * echo spidev 0 10000000 3 > new_device
+ */
+static ssize_t
+new_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct spi_controller *ctlr = container_of(dev, struct spi_controller,
+ dev);
+ struct spi_device *spi;
+ char modalias[SPI_NAME_SIZE];
+ unsigned int chip_select;
+ u32 max_speed_hz = 0;
+ u32 mode = 0;
+ char *blank;
+ int status;
+
+ blank = strchr(buf, ' ');
+ if (!blank) {
+ dev_err(dev, "new_device: Missing parameters\n");
+ return -EINVAL;
+ }
+
+ if (blank == buf || blank - buf > SPI_NAME_SIZE - 1) {
+ dev_err(dev, "new_device: Invalid device name\n");
+ return -EINVAL;
+ }
+
+ memset(modalias, 0, sizeof(modalias));
+ memcpy(modalias, buf, blank - buf);
+
+ /*
+ * sscanf fills only the fields it matches; unmatched optional
+ * fields (max_speed_hz, mode) stay zero from initialisation above.
+ * max_speed_hz == 0 is clamped to the controller max by spi_setup().
+ * mode == 0 selects SPI mode 0 (CPOL=0, CPHA=0).
+ */
+ if (sscanf(++blank, "%u %u %u", &chip_select, &max_speed_hz, &mode) < 1) {
+ dev_err(dev, "new_device: Can't parse chip select\n");
+ return -EINVAL;
+ }
+
+ /*
+ * spi_device.chip_select[] is u8, so cap at U8_MAX independently of
+ * ctlr->num_chipselect (which is u16 and may exceed 255). Without
+ * this, values in (U8_MAX, num_chipselect) would silently truncate
+ * inside spi_set_chipselect() and select the wrong CS.
+ */
+ if (chip_select > U8_MAX || chip_select >= ctlr->num_chipselect) {
+ dev_err(dev, "new_device: Chip select %u out of range (num_chipselect=%u)\n",
+ chip_select, ctlr->num_chipselect);
+ return -EINVAL;
+ }
+
+ /*
+ * Reject kernel-internal mode bits (SPI_NO_TX, SPI_NO_RX,
+ * SPI_TPM_HW_FLOW, ...). These are set only by in-kernel drivers
+ * that know they are safe on their controller/device pair and must
+ * not be settable through a userspace-writable sysfs. Matches
+ * spidev's SPI_IOC_WR_MODE32 handling (drivers/spi/spidev.c).
+ */
+ if (mode & ~(u32)SPI_MODE_USER_MASK) {
+ dev_err(dev, "new_device: Invalid mode bits 0x%x\n",
+ mode & ~(u32)SPI_MODE_USER_MASK);
+ return -EINVAL;
+ }
+
+ spi = spi_alloc_device(ctlr);
+ if (!spi)
+ return -ENOMEM;
+
+ spi_set_chipselect(spi, 0, chip_select);
+ spi->max_speed_hz = max_speed_hz;
+ spi->mode = mode;
+ spi->cs_index_mask = BIT(0);
+ strscpy(spi->modalias, modalias, sizeof(spi->modalias));
+
+ /*
+ * Set driver_override so that the device binds to the driver
+ * named by modalias regardless of whether that driver's
+ * id_table contains a matching entry. This is needed because
+ * some drivers (e.g. spidev) deliberately omit generic names
+ * from their id_table.
+ */
+ status = device_set_driver_override(&spi->dev, modalias);
+ if (status) {
+ spi_dev_put(spi);
+ return status;
+ }
+
+ /*
+ * spi_unregister_controller() removes the new_device/delete_device
+ * sysfs group before taking add_lock, so kernfs_drain() has already
+ * completed by the time we get here and we cannot be racing with
+ * teardown. Take add_lock to serialise the __spi_add_device() and
+ * list insertion with respect to non-sysfs callers of
+ * __spi_add_device() (DT/ACPI, ancillary), which check
+ * device_is_registered(&ctlr->dev) under the same lock.
+ */
+ mutex_lock(&ctlr->add_lock);
+
+ status = __spi_add_device(spi, NULL);
+ if (status) {
+ mutex_unlock(&ctlr->add_lock);
+ spi_dev_put(spi);
+ return status;
+ }
+
+ list_add_tail(&spi->userspace_node, &ctlr->userspace_clients);
+ mutex_unlock(&ctlr->add_lock);
+
+ dev_info(dev, "new_device: Instantiated device %s at CS%u\n",
+ modalias, chip_select);
+ return count;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(new_device, 0200, NULL, new_device_store);
+
+static ssize_t
+delete_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct spi_controller *ctlr = container_of(dev, struct spi_controller,
+ dev);
+ struct spi_device *spi, *next;
+ unsigned short cs;
+ char end;
+ int res;
+
+ res = sscanf(buf, "%hu%c", &cs, &end);
+ if (res < 1) {
+ dev_err(dev, "delete_device: Can't parse chip select\n");
+ return -EINVAL;
+ }
+ if (res > 1 && end != '\n') {
+ dev_err(dev, "delete_device: Unexpected parameters\n");
+ return -EINVAL;
+ }
+
+ res = -ENOENT;
+ mutex_lock(&ctlr->add_lock);
+ list_for_each_entry_safe(spi, next, &ctlr->userspace_clients,
+ userspace_node) {
+ if (spi_get_chipselect(spi, 0) == cs) {
+ dev_info(dev, "delete_device: Deleting device %s at CS%u\n",
+ spi->modalias, cs);
+ list_del(&spi->userspace_node);
+ spi_unregister_device(spi);
+ res = count;
+ break;
+ }
+ }
+ mutex_unlock(&ctlr->add_lock);
+
+ if (res < 0)
+ dev_err(dev, "delete_device: Can't find device in list\n");
+ return res;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+ delete_device_store);
+
+static struct attribute *spi_controller_userspace_attrs[] = {
+ &dev_attr_new_device.attr,
+ &dev_attr_delete_device.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_controller_userspace_group = {
+ .attrs = spi_controller_userspace_attrs,
+};
+
+#endif /* CONFIG_SPI_DYNAMIC */
+
+/*
+ * spi_controller_userspace_group is registered manually for host controllers
+ * at the end of spi_register_controller() so new_device/delete_device only
+ * appear after DT/ACPI children and the queue are set up.
+ */
static const struct attribute_group *spi_controller_groups[] = {
&spi_controller_statistics_group,
NULL,
@@ -3259,6 +3446,9 @@ struct spi_controller *__spi_alloc_controller(struct device *dev,
mutex_init(&ctlr->bus_lock_mutex);
mutex_init(&ctlr->io_mutex);
mutex_init(&ctlr->add_lock);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ INIT_LIST_HEAD(&ctlr->userspace_clients);
+#endif
ctlr->bus_num = -1;
ctlr->num_chipselect = 1;
ctlr->num_data_lanes = 1;
@@ -3552,6 +3742,29 @@ int spi_register_controller(struct spi_controller *ctlr)
of_register_spi_devices(ctlr);
acpi_register_spi_devices(ctlr);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ /*
+ * Register the new_device/delete_device sysfs interface as the
+ * final step of host controller bringup, only after the queue,
+ * boardinfo matching and DT/ACPI enumeration have all completed.
+ * If this fails, the controller is otherwise usable, so log and
+ * carry on rather than tearing everything down.
+ */
+ if (!spi_controller_is_target(ctlr)) {
+ status = sysfs_create_group(&ctlr->dev.kobj,
+ &spi_controller_userspace_group);
+ if (status) {
+ dev_warn(&ctlr->dev,
+ "Failed to create userspace client interface: %d\n",
+ status);
+ } else {
+ ctlr->userspace_registered = true;
+ /* Notify userspace that the new attributes are available. */
+ kobject_uevent(&ctlr->dev.kobj, KOBJ_CHANGE);
+ }
+ }
+#endif
+
return 0;
del_ctrl:
@@ -3616,10 +3829,43 @@ void spi_unregister_controller(struct spi_controller *ctlr)
struct spi_controller *found;
int id = ctlr->bus_num;
+ /*
+ * Drain in-flight new_device/delete_device sysfs stores and
+ * prevent new ones from starting. Must happen before we take
+ * add_lock so kernfs_drain doesn't wait on a store that is
+ * itself blocked on add_lock.
+ */
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ if (ctlr->userspace_registered) {
+ sysfs_remove_group(&ctlr->dev.kobj,
+ &spi_controller_userspace_group);
+ ctlr->userspace_registered = false;
+ }
+#endif
+
/* Prevent addition of new devices, unregister existing ones */
if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
mutex_lock(&ctlr->add_lock);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ /*
+ * Drain userspace_clients before __unregister since
+ * spi_unregister_device() doesn't do list_del() itself. The
+ * userspace sysfs group has already been removed above and
+ * kernfs_drain() has completed, so no new entries can appear
+ * here.
+ */
+ while (!list_empty(&ctlr->userspace_clients)) {
+ struct spi_device *spi;
+
+ spi = list_first_entry(&ctlr->userspace_clients,
+ struct spi_device,
+ userspace_node);
+ list_del(&spi->userspace_node);
+ spi_unregister_device(spi);
+ }
+#endif
+
device_for_each_child(&ctlr->dev, NULL, __unregister);
/* First make sure that this controller was ever added */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 4c285d3ede1d..88d17fce02dc 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -179,6 +179,8 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
* @num_tx_lanes: Number of transmit lanes wired up.
* @rx_lane_map: Map of peripheral lanes (index) to controller lanes (value).
* @num_rx_lanes: Number of receive lanes wired up.
+ * @userspace_node: entry on the parent controller's userspace_clients list
+ * when this device was instantiated via the sysfs new_device interface
*
* A @spi_device is used to interchange data between an SPI target device
* (usually a discrete chip) and CPU memory.
@@ -252,6 +254,10 @@ struct spi_device {
u8 rx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX];
u8 num_rx_lanes;
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ struct list_head userspace_node;
+#endif
+
/*
* Likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
@@ -555,6 +561,11 @@ extern struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi,
* @defer_optimize_message: set to true if controller cannot pre-optimize messages
* and needs to defer the optimization step until the message is actually
* being transferred
+ * @userspace_clients: list of SPI devices instantiated from userspace via
+ * the sysfs new_device interface; protected by @add_lock
+ * @userspace_registered: true once the new_device/delete_device sysfs
+ * group has been added by spi_register_controller(); used by
+ * spi_unregister_controller() to know whether to remove it
*
* Each SPI controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals
@@ -807,6 +818,13 @@ struct spi_controller {
bool queue_empty;
bool must_async;
bool defer_optimize_message;
+
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ /* List of userspace-instantiated devices; protected by @add_lock */
+ struct list_head userspace_clients;
+ /* True after new_device/delete_device sysfs group is created */
+ bool userspace_registered;
+#endif
};
static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
--
2.17.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v9 2/2] docs: spi: add documentation for userspace device instantiation
2026-08-03 10:46 [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 1/2] " Vishwaroop A
@ 2026-08-03 10:46 ` Vishwaroop A
2026-08-06 18:14 ` [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Mark Brown
2 siblings, 0 replies; 4+ messages in thread
From: Vishwaroop A @ 2026-08-03 10:46 UTC (permalink / raw)
To: broonie
Cc: linux-spi, jonathanh, thierry.reding, corbet, linux-doc,
smangipudi, va
Document the new_device and delete_device sysfs attributes on SPI
controllers:
- Documentation/spi/instantiating-devices.rst: describes when and
why this interface is needed, accepted parameters, usage examples,
and limitations.
- Documentation/ABI/testing/sysfs-class-spi-master: formal ABI
entry for both attributes.
Signed-off-by: Vishwaroop A <va@nvidia.com>
---
.../ABI/testing/sysfs-class-spi-master | 34 +++++++
Documentation/spi/index.rst | 1 +
Documentation/spi/instantiating-devices.rst | 88 +++++++++++++++++++
3 files changed, 123 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-spi-master
create mode 100644 Documentation/spi/instantiating-devices.rst
diff --git a/Documentation/ABI/testing/sysfs-class-spi-master b/Documentation/ABI/testing/sysfs-class-spi-master
new file mode 100644
index 000000000000..0a524fcd96d2
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-spi-master
@@ -0,0 +1,34 @@
+What: /sys/class/spi_master/spiB/new_device
+Date: July 2026
+KernelVersion: 7.3
+Contact: linux-spi@vger.kernel.org
+Description: (WO) Instantiate a new SPI device on bus B, where B
+ is the bus number (0, 1, 2, ...). Takes parameters
+ in the format:
+
+ <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+
+ where modalias is the driver name, chip_select is the
+ CS line number, and max_speed_hz and mode are optional.
+
+ The device can later be removed with delete_device.
+
+ Only devices created via this interface can be removed
+ with delete_device; platform and DT devices are not
+ affected.
+
+ Example:
+ # echo spidev 0 > /sys/class/spi_master/spi0/new_device
+ # echo spidev 0 10000000 > /sys/class/spi_master/spi0/new_device
+ # echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
+
+What: /sys/class/spi_master/spiB/delete_device
+Date: July 2026
+KernelVersion: 7.3
+Contact: linux-spi@vger.kernel.org
+Description: (WO) Remove a SPI device previously created via
+ new_device. Takes a single parameter: the chip select
+ number of the device to remove.
+
+ Example:
+ # echo 0 > /sys/class/spi_master/spi0/delete_device
diff --git a/Documentation/spi/index.rst b/Documentation/spi/index.rst
index ac0c2233ce48..3f723e2c07da 100644
--- a/Documentation/spi/index.rst
+++ b/Documentation/spi/index.rst
@@ -8,6 +8,7 @@ Serial Peripheral Interface (SPI)
:maxdepth: 1
spi-summary
+ instantiating-devices
spidev
multiple-data-lanes
butterfly
diff --git a/Documentation/spi/instantiating-devices.rst b/Documentation/spi/instantiating-devices.rst
new file mode 100644
index 000000000000..ec07353b425d
--- /dev/null
+++ b/Documentation/spi/instantiating-devices.rst
@@ -0,0 +1,88 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+How to instantiate SPI devices
+==============================
+
+SPI devices are normally declared statically via device-tree, ACPI, or
+board files. When the SPI controller is registered, these devices are
+instantiated automatically by the SPI core. This is the preferred method
+for any device with a proper kernel driver.
+
+Instantiate from user-space
+---------------------------
+
+In certain cases a SPI device cannot be declared statically:
+
+* The ``spidev`` driver, which provides raw userspace access to SPI
+ buses, explicitly rejects the bare ``"spidev"`` compatible string in
+ device-tree because spidev is a Linux implementation detail, not a
+ hardware description. Vendor-specific compatible strings for spidev
+ (e.g. ``"vendor,board-spidev"``) are also generally not accepted
+ upstream. Device-tree overlays do not help here either, since the
+ spidev driver performs the same compatible check regardless of how
+ the DT node was loaded.
+
+* You are developing or testing a SPI device on a development board
+ where the SPI bus is exposed on expansion headers, and the connected
+ device may change frequently.
+
+For these cases, a sysfs interface is provided on each SPI host controller
+(similar to the I2C ``new_device``/``delete_device`` interface described
+in Documentation/i2c/instantiating-devices.rst). Two write-only
+attribute files are created in every SPI host controller directory:
+``new_device`` and ``delete_device``.
+
+File ``new_device`` takes 2 to 4 parameters: the name of the SPI
+device (a string), the chip select number, and optionally
+``max_speed_hz`` and ``mode``::
+
+ <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+
+The modalias is set both as the device's ``modalias`` field and as its
+``driver_override``. This ensures that the device binds to the named
+driver directly, bypassing the normal bus matching logic (OF, ACPI,
+and ``id_table``). This is necessary because drivers like ``spidev``
+deliberately exclude generic names from their ``id_table``.
+
+If ``max_speed_hz`` is omitted or 0, ``spi_setup()`` clamps it to
+the controller's maximum speed. If ``mode`` is omitted, SPI mode 0
+(CPOL=0, CPHA=0) is used.
+
+File ``delete_device`` takes a single parameter: the chip select
+number. As no two devices can share a chip select on a given SPI bus,
+the chip select is sufficient to uniquely identify the device.
+
+Examples::
+
+ # Create a spidev device on SPI bus 0, chip select 0
+ echo spidev 0 > /sys/class/spi_master/spi0/new_device
+
+ # Create with explicit clock rate and SPI mode
+ echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
+
+ # Remove the device
+ echo 0 > /sys/class/spi_master/spi0/delete_device
+
+The attributes are added after the host controller and its firmware-described
+devices have been registered. Their addition emits a ``change`` uevent,
+allowing a udev rule to write to ``new_device`` when the interface is ready.
+
+Limitations
+^^^^^^^^^^^
+
+Devices created through this interface have the following limitations
+compared to devices declared via device-tree:
+
+* No interrupt (IRQ) support.
+* No additional properties such as ``spi-max-frequency`` DT bindings
+ or controller-specific configuration.
+* No platform data or software nodes.
+
+For ``spidev`` usage these limitations are not relevant, since spidev
+provides a raw byte-level interface that does not require any of these
+features.
+
+Only devices created via ``new_device`` can be removed through
+``delete_device``. Devices declared via device-tree, ACPI, or board
+files are not affected by this interface.
--
2.17.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface
2026-08-03 10:46 [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 1/2] " Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 2/2] docs: spi: add documentation for userspace device instantiation Vishwaroop A
@ 2026-08-06 18:14 ` Mark Brown
2 siblings, 0 replies; 4+ messages in thread
From: Mark Brown @ 2026-08-06 18:14 UTC (permalink / raw)
To: Vishwaroop A
Cc: linux-spi, jonathanh, thierry.reding, corbet, linux-doc,
smangipudi
On Mon, 03 Aug 2026 10:46:12 +0000, Vishwaroop A wrote:
> spi: add new_device/delete_device sysfs interface
>
> Add I2C-style new_device/delete_device sysfs attributes to SPI host
> controllers, allowing userspace to instantiate and remove SPI devices
> at runtime without device-tree changes.
>
> Patch 1 adds the new_device/delete_device attributes and the supporting
> infrastructure (userspace_clients list, manual sysfs group registration).
> Patch 2 adds the ABI and user-facing documentation.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-7.3
Thanks!
[1/2] spi: add new_device/delete_device sysfs interface
https://git.kernel.org/broonie/spi/c/e0db3f62dbe6
[2/2] docs: spi: add documentation for userspace device instantiation
https://git.kernel.org/broonie/spi/c/036d4b059be0
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-07 11:24 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 10:46 [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 1/2] " Vishwaroop A
2026-08-03 10:46 ` [PATCH v9 2/2] docs: spi: add documentation for userspace device instantiation Vishwaroop A
2026-08-06 18:14 ` [PATCH v9 0/2] spi: add new_device/delete_device sysfs interface Mark Brown
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.