184 lines
6.3 KiB
Diff
184 lines
6.3 KiB
Diff
From edc0e47f43d73d9164c69688932d989971738b20 Mon Sep 17 00:00:00 2001
|
|
From: Lukas Wunner <lukas@wunner.de>
|
|
Date: Wed, 11 Nov 2020 20:07:10 +0100
|
|
Subject: [PATCH 102/109] spi: Introduce device-managed SPI controller
|
|
allocation
|
|
|
|
mainline inclusion
|
|
from mainline-v5.10-rc5
|
|
commit 5e844cc37a5cbaa460e68f9a989d321d63088a89
|
|
category: feature
|
|
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I8DDF5
|
|
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5e844cc37a5cbaa460e68f9a989d321d63088a89
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
SPI driver probing currently comprises two steps, whereas removal
|
|
comprises only one step:
|
|
|
|
spi_alloc_master()
|
|
spi_register_controller()
|
|
|
|
spi_unregister_controller()
|
|
|
|
That's because spi_unregister_controller() calls device_unregister()
|
|
instead of device_del(), thereby releasing the reference on the
|
|
spi_controller which was obtained by spi_alloc_master().
|
|
|
|
An SPI driver's private data is contained in the same memory allocation
|
|
as the spi_controller struct. Thus, once spi_unregister_controller()
|
|
has been called, the private data is inaccessible. But some drivers
|
|
need to access it after spi_unregister_controller() to perform further
|
|
teardown steps.
|
|
|
|
Introduce devm_spi_alloc_master() and devm_spi_alloc_slave(), which
|
|
release a reference on the spi_controller struct only after the driver
|
|
has unbound, thereby keeping the memory allocation accessible. Change
|
|
spi_unregister_controller() to not release a reference if the
|
|
spi_controller was allocated by one of these new devm functions.
|
|
|
|
The present commit is small enough to be backportable to stable.
|
|
It allows fixing drivers which use the private data in their ->remove()
|
|
hook after it's been freed. It also allows fixing drivers which neglect
|
|
to release a reference on the spi_controller in the probe error path.
|
|
|
|
Long-term, most SPI drivers shall be moved over to the devm functions
|
|
introduced herein. The few that can't shall be changed in a treewide
|
|
commit to explicitly release the last reference on the controller.
|
|
That commit shall amend spi_unregister_controller() to no longer release
|
|
a reference, thereby completing the migration.
|
|
|
|
As a result, the behaviour will be less surprising and more consistent
|
|
with subsystems such as IIO, which also includes the private data in the
|
|
allocation of the generic iio_dev struct, but calls device_del() in
|
|
iio_device_unregister().
|
|
|
|
Signed-off-by: Lukas Wunner <lukas@wunner.de>
|
|
Link: https://lore.kernel.org/r/272bae2ef08abd21388c98e23729886663d19192.1605121038.git.lukas@wunner.de
|
|
Signed-off-by: Mark Brown <broonie@kernel.org>
|
|
Signed-off-by: YunYi Yang <yangyunyi2@huawei.com>
|
|
|
|
Conflicts:
|
|
drivers/spi/spi.c
|
|
---
|
|
drivers/spi/spi.c | 58 ++++++++++++++++++++++++++++++++++++++++-
|
|
include/linux/spi/spi.h | 19 ++++++++++++++
|
|
2 files changed, 76 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
|
|
index c79bf015b4b2..aa50cc5380cf 100644
|
|
--- a/drivers/spi/spi.c
|
|
+++ b/drivers/spi/spi.c
|
|
@@ -2051,6 +2051,49 @@ struct spi_controller *__spi_alloc_controller(struct device *dev,
|
|
}
|
|
EXPORT_SYMBOL_GPL(__spi_alloc_controller);
|
|
|
|
+static void devm_spi_release_controller(struct device *dev, void *ctlr)
|
|
+{
|
|
+ spi_controller_put(*(struct spi_controller **)ctlr);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * __devm_spi_alloc_controller - resource-managed __spi_alloc_controller()
|
|
+ * @dev: physical device of SPI controller
|
|
+ * @size: how much zeroed driver-private data to allocate
|
|
+ * @slave: whether to allocate an SPI master (false) or SPI slave (true)
|
|
+ * Context: can sleep
|
|
+ *
|
|
+ * Allocate an SPI controller and automatically release a reference on it
|
|
+ * when @dev is unbound from its driver. Drivers are thus relieved from
|
|
+ * having to call spi_controller_put().
|
|
+ *
|
|
+ * The arguments to this function are identical to __spi_alloc_controller().
|
|
+ *
|
|
+ * Return: the SPI controller structure on success, else NULL.
|
|
+ */
|
|
+struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
|
|
+ unsigned int size,
|
|
+ bool slave)
|
|
+{
|
|
+ struct spi_controller **ptr, *ctlr;
|
|
+
|
|
+ ptr = devres_alloc(devm_spi_release_controller, sizeof(*ptr),
|
|
+ GFP_KERNEL);
|
|
+ if (!ptr)
|
|
+ return NULL;
|
|
+
|
|
+ ctlr = __spi_alloc_controller(dev, size, slave);
|
|
+ if (ctlr) {
|
|
+ *ptr = ctlr;
|
|
+ devres_add(dev, ptr);
|
|
+ } else {
|
|
+ devres_free(ptr);
|
|
+ }
|
|
+
|
|
+ return ctlr;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(__devm_spi_alloc_controller);
|
|
+
|
|
static int __spi_register_controller(struct spi_controller *ctlr)
|
|
{
|
|
int nb, i, *cs;
|
|
@@ -2301,6 +2344,11 @@ int devm_spi_register_controller(struct device *dev,
|
|
}
|
|
EXPORT_SYMBOL_GPL(devm_spi_register_controller);
|
|
|
|
+static int devm_spi_match_controller(struct device *dev, void *res, void *ctlr)
|
|
+{
|
|
+ return *(struct spi_controller **)res == ctlr;
|
|
+}
|
|
+
|
|
static int __unregister(struct device *dev, void *null)
|
|
{
|
|
spi_unregister_device(to_spi_device(dev));
|
|
@@ -2342,7 +2390,15 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|
list_del(&ctlr->list);
|
|
mutex_unlock(&board_lock);
|
|
|
|
- device_unregister(&ctlr->dev);
|
|
+ device_del(&ctlr->dev);
|
|
+
|
|
+ /* Release the last reference on the controller if its driver
|
|
+ * has not yet been converted to devm_spi_alloc_master/slave().
|
|
+ */
|
|
+ if (!devres_find(ctlr->dev.parent, devm_spi_release_controller,
|
|
+ devm_spi_match_controller, ctlr))
|
|
+ put_device(&ctlr->dev);
|
|
+
|
|
/* free bus id */
|
|
mutex_lock(&board_lock);
|
|
if (found == ctlr)
|
|
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
|
|
index ec795c5304f0..085c8e0e0864 100644
|
|
--- a/include/linux/spi/spi.h
|
|
+++ b/include/linux/spi/spi.h
|
|
@@ -634,6 +634,25 @@ static inline struct spi_controller *spi_alloc_slave(struct device *host,
|
|
return __spi_alloc_controller(host, size, true);
|
|
}
|
|
|
|
+struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
|
|
+ unsigned int size,
|
|
+ bool slave);
|
|
+
|
|
+static inline struct spi_controller *devm_spi_alloc_master(struct device *dev,
|
|
+ unsigned int size)
|
|
+{
|
|
+ return __devm_spi_alloc_controller(dev, size, false);
|
|
+}
|
|
+
|
|
+static inline struct spi_controller *devm_spi_alloc_slave(struct device *dev,
|
|
+ unsigned int size)
|
|
+{
|
|
+ if (!IS_ENABLED(CONFIG_SPI_SLAVE))
|
|
+ return NULL;
|
|
+
|
|
+ return __devm_spi_alloc_controller(dev, size, true);
|
|
+}
|
|
+
|
|
extern int spi_register_controller(struct spi_controller *ctlr);
|
|
extern int devm_spi_register_controller(struct device *dev,
|
|
struct spi_controller *ctlr);
|
|
--
|
|
2.23.0
|
|
|