diff mbox

[v3,1/3] spi: spidev: create spidev device for all spi slaves.

Message ID 5bfcceff7b6132cf3447f97c24d1646043224164.1468880530.git.hramrach@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michal Suchanek July 18, 2016, 10:35 p.m. UTC
This patch makes spidev into a bus addon rather than a separate driver.
spidev character device is created for each spi slave. When a spi slave
driver is bound the spidev IOCTLs return -EBUSY.

This is similar to i2c bus which alows access to any address not claimed
by a kernel driver and should provide interface backwards compatible
with existing spidev tools.

Signed-off-by: Michal Suchanek <hramrach@gmail.com>
---
 Documentation/spi/spidev |  40 ++---
 drivers/spi/Kconfig      |   2 +-
 drivers/spi/spi.c        |  19 ++-
 drivers/spi/spidev.c     | 370 ++++++++++++++++++++---------------------------
 include/linux/spi/spi.h  |  14 ++
 5 files changed, 197 insertions(+), 248 deletions(-)

Comments

Mark Brown July 18, 2016, 10:59 p.m. UTC | #1
On Tue, Jul 19, 2016 at 12:35:40AM +0200, Michal Suchanek wrote:

>  config SPI_SPIDEV
> -	tristate "User mode SPI device driver support"
> +	bool "User mode SPI device driver support"

This is a step back, it would require spidev to be built in.

> -	spin_lock_irq(&spidev->spi_lock);
> -	spi = spi_dev_get(spidev->spi);
> -	spin_unlock_irq(&spidev->spi_lock);
> +	spi = filp->private_data;
> +	spi = spi_dev_get(spi);

All this refactoring to move spidev about should've been a separate
patch, it's hard to find the actual content in here.

> -	mutex_lock(&device_list_lock);
> +	dev = bus_find_device(&spi_bus_type, NULL, (void *) inode->i_rdev,
> +			      spidev_devt_match);

...

> -			dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
> +	spi = to_spi_device(dev);
> +
> +	mutex_lock(&spi->buf_lock);
> +	spin_lock_irq(&spi->spidev_lock);
> +	spi->spidev_users++;
> +	spin_unlock_irq(&spi->spidev_lock);

This is broken, it will unconditionally create a spidev for any chip
select regardless of if there's any actual hardware there and (even more
importantly) regardless if that hardware is actually a SPI device.  This
is not safe, especially given some of the ideas people seem to have for
userspaces.

I am getting completely fed up of saying this, it's not OK to just
expose pins to userspace when we have no idea what they are connected
to.
Michal Suchanek July 19, 2016, 11:17 a.m. UTC | #2
Hello,

On 19 July 2016 at 00:59, Mark Brown <broonie@kernel.org> wrote:
> On Tue, Jul 19, 2016 at 12:35:40AM +0200, Michal Suchanek wrote:
>
>>  config SPI_SPIDEV
>> -     tristate "User mode SPI device driver support"
>> +     bool "User mode SPI device driver support"
>
> This is a step back, it would require spidev to be built in.

That's a technical problem so it can be addressed.

>
>> -     spin_lock_irq(&spidev->spi_lock);
>> -     spi = spi_dev_get(spidev->spi);
>> -     spin_unlock_irq(&spidev->spi_lock);
>> +     spi = filp->private_data;
>> +     spi = spi_dev_get(spi);
>
> All this refactoring to move spidev about should've been a separate
> patch, it's hard to find the actual content in here.

And re-modularizing will probably need to move thing back, mostly.

Regarding this change

spidev uses device_create to allocate spidev character devices so
these are not part of some struct spidev_device but live separately
which gives needlessly more objects to manage

- the spi device
- the spidev character device
- a link object that points to both spi and spidev and is manually
  managed by the spidev driver

In order to reduce the amount of objects and management I appended
the link object at the end of struct spi_device.

Is there a better way?

>
>> -     mutex_lock(&device_list_lock);
>> +     dev = bus_find_device(&spi_bus_type, NULL, (void *) inode->i_rdev,
>> +                           spidev_devt_match);
>
> ...
>
>> -                     dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
>> +     spi = to_spi_device(dev);
>> +
>> +     mutex_lock(&spi->buf_lock);
>> +     spin_lock_irq(&spi->spidev_lock);
>> +     spi->spidev_users++;
>> +     spin_unlock_irq(&spi->spidev_lock);
>
> This is broken, it will unconditionally create a spidev for any chip
> select regardless of if there's any actual hardware there and (even more
> importantly) regardless if that hardware is actually a SPI device.  This
> is not safe, especially given some of the ideas people seem to have for
> userspaces.

This is as safe as it gets. For the device probe to happen somebody must
have configured the CS as spi slave device somewhere. What this patchset
accomplishes is creating an userspace interface for accessing SPI bus using
the CS regardless of the probe result (at least for devicetree nodes).

In particular it does not create a spidev device for CS lines which are unused.

Looking into SPI more this whole point is mostly moot, anyway. Most of the
time the unused CS lines will not be multiplexed to the SPI controller so
even if the SPI master tries to use them it does nothing. Also most of the
time all the CS lines can be exported as GPIO regardless of their use for SPI.
And set to any possible level using the GPIO userspace API.

>
> I am getting completely fed up of saying this, it's not OK to just
> expose pins to userspace when we have no idea what they are connected
> to.

I am getting fed up with this argument.

First, this patchset only exposes to userspace pins that are configured as SPI
slaves in devicetree.

It does so even in the cases when the driver for the device in question is not
available in the kernel (because it is not loaded or excluded completely) or
in the case you do not specify which SPI slave is connected to those pins.
This is the only change.

So in addition to saying 'load driver XY with parameters Z=BJ' this allows
saying 'there is possibly a SPI device and I may want to talk to it with an
userspace application'. With overlays this can be later amended to 'load
driver ZY with parameters X=XN' or whatever.

The fact that devicetree does not allow talking to a SPI device from userspace
without telling kernel what kind of protocol userspace is using is a regression
from using board files. The kernel has no business knowing that. When you
use a userspace driver it's userspace's job to manage protocols and devices.
The purpose of this patchset is for the userspace applications to continue doing
so with devicetree based systems without the need to fabricate fake hardware
description. And the hardware description will necessarily be fake in the case
when it is in fact userspace implementing and managing the drivers.

It's not to say that userspace drivers that duplicate kernel drivers cannot
examine the devicetree for configuration data. That's up to the userspace
driver writer to decide, however.

Thanks

Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown July 19, 2016, 12:44 p.m. UTC | #3
On Tue, Jul 19, 2016 at 01:17:52PM +0200, Michal Suchanek wrote:
> On 19 July 2016 at 00:59, Mark Brown <broonie@kernel.org> wrote:

> Looking into SPI more this whole point is mostly moot, anyway. Most of the
> time the unused CS lines will not be multiplexed to the SPI controller so
> even if the SPI master tries to use them it does nothing. Also most of the
> time all the CS lines can be exported as GPIO regardless of their use for SPI.
> And set to any possible level using the GPIO userspace API.

Your system is not the entire world, Linux has to run on other systems
too.  There are actual devices out there with inflexible pinmuxing, we
can't just ignore them.

> In order to reduce the amount of objects and management I appended
> the link object at the end of struct spi_device.

> Is there a better way?

That seems mostly fine, the problem is one of code review - because the
patch does many logically distinct things in a single patch it is much
harder to review than if it were split out.  There's a lot of mechanical
refactoring which obscures the several more substantive changes that are
being made at the same time and since most of those substantive changes
are explicitly described at all it's hard to tell if they're intended or
doing what is expected.

> > I am getting completely fed up of saying this, it's not OK to just
> > expose pins to userspace when we have no idea what they are connected
> > to.

> I am getting fed up with this argument.

> First, this patchset only exposes to userspace pins that are configured as SPI
> slaves in devicetree.

Are you *absolutely* positive your changes won't do anything on non-DT
systems?  That definitely doesn't appear to be the case (and shouldn't
be, not all the world is DT), but now I look in more detail what it is
doing is exposing spidev for each explicitly described slave which is
substantially more safe and does address the main problem with
undescribed devices.

Please split this up so it can be reviewed, providing clear and specific
descriptions of each individual change (as covered in
SubmittingPatches).

This:

> -       status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
> +       status = register_chrdev(SPIDEV_MAJOR, "spidev", &spidev_fops);

also looks like a needless ABI change and the removal of the locking on
minor device allocation seems concerning and at least needs to be
covered in the changelog.

> The fact that devicetree does not allow talking to a SPI device from userspace
> without telling kernel what kind of protocol userspace is using is a regression
> from using board files. The kernel has no business knowing that. When you
> use a userspace driver it's userspace's job to manage protocols and devices.

The system as a whole, including both the kernel and userspace, has
every business knowing what the hardware is so it can understand how to
control it.  Userspace is no more able to discover what the magic
undescribed bits of hardware are automatically than the kernel is
without help from a hardware description, the DT isn't just something
for the kernel once you have userspace drivers.  

Directly enumerating spidev wasn't an especially great idea for board
files either, it was just an expedient hack that worked when the
hardware description was entirely in kernel and much easier to just go
and change but now we have an external ABI for hardware description and
we have to expect that people will use it as such.

> The purpose of this patchset is for the userspace applications to continue doing
> so with devicetree based systems without the need to fabricate fake hardware
> description. And the hardware description will necessarily be fake in the case
> when it is in fact userspace implementing and managing the drivers.

If userspace is managing to figure out how to control the device then
providing a description of the hardware is clearly within the bounds of
possibility and there is no need to fake anything.
Michal Suchanek July 19, 2016, 3:32 p.m. UTC | #4
On 19 July 2016 at 14:44, Mark Brown <broonie@kernel.org> wrote:
> On Tue, Jul 19, 2016 at 01:17:52PM +0200, Michal Suchanek wrote:
>> On 19 July 2016 at 00:59, Mark Brown <broonie@kernel.org> wrote:
>
>> Looking into SPI more this whole point is mostly moot, anyway. Most of the
>> time the unused CS lines will not be multiplexed to the SPI controller so
>> even if the SPI master tries to use them it does nothing. Also most of the
>> time all the CS lines can be exported as GPIO regardless of their use for SPI.
>> And set to any possible level using the GPIO userspace API.
>
> Your system is not the entire world, Linux has to run on other systems
> too.  There are actual devices out there with inflexible pinmuxing, we
> can't just ignore them.

And they are not ignored.

>
>> In order to reduce the amount of objects and management I appended
>> the link object at the end of struct spi_device.
>
>> Is there a better way?
>
> That seems mostly fine, the problem is one of code review - because the
> patch does many logically distinct things in a single patch it is much
> harder to review than if it were split out.  There's a lot of mechanical
> refactoring which obscures the several more substantive changes that are
> being made at the same time and since most of those substantive changes
> are explicitly described at all it's hard to tell if they're intended or
> doing what is expected.
>
>> > I am getting completely fed up of saying this, it's not OK to just
>> > expose pins to userspace when we have no idea what they are connected
>> > to.
>
>> I am getting fed up with this argument.
>
>> First, this patchset only exposes to userspace pins that are configured as SPI
>> slaves in devicetree.
>
> Are you *absolutely* positive your changes won't do anything on non-DT
> systems?  That definitely doesn't appear to be the case (and shouldn't
> be, not all the world is DT), but now I look in more detail what it is
> doing is exposing spidev for each explicitly described slave which is
> substantially more safe and does address the main problem with
> undescribed devices.
>
> Please split this up so it can be reviewed, providing clear and specific
> descriptions of each individual change (as covered in
> SubmittingPatches).

OK, splitting this into multiple incremental patches should work better.

>
> This:
>
>> -       status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
>> +       status = register_chrdev(SPIDEV_MAJOR, "spidev", &spidev_fops);
>
> also looks like a needless ABI change

ABI change to what?

The spidev ABI should be the IOCTLs on /dev/spidev*. These are preserved.

What parts of the kernel or userspace should depend on spidev
devices belonging to class spi?

Changing this is deliberate so SPI devices can be enumerated walking
the bus without seeing the spidev devices. Or spidev devices can be
enumerated walking the spidev class. No need to maintain a list.

> and the removal of the locking on
> minor device allocation seems concerning and at least needs to be
> covered in the changelog.

This particular part should be covered by the SPI device list lock
because the attach/detach function should be only called with
the lock held.

It's certainly worth documenting as a separate change if it's still preserved
once spidev is again modularized.

>
>> The fact that devicetree does not allow talking to a SPI device from userspace
>> without telling kernel what kind of protocol userspace is using is a regression
>> from using board files. The kernel has no business knowing that. When you
>> use a userspace driver it's userspace's job to manage protocols and devices.
>
> The system as a whole, including both the kernel and userspace, has
> every business knowing what the hardware is so it can understand how to
> control it.  Userspace is no more able to discover what the magic
> undescribed bits of hardware are automatically than the kernel is
> without help from a hardware description, the DT isn't just something
> for the kernel once you have userspace drivers.
>
> Directly enumerating spidev wasn't an especially great idea for board
> files either, it was just an expedient hack that worked when the
> hardware description was entirely in kernel and much easier to just go
> and change but now we have an external ABI for hardware description and
> we have to expect that people will use it as such.
>
>> The purpose of this patchset is for the userspace applications to continue doing
>> so with devicetree based systems without the need to fabricate fake hardware
>> description. And the hardware description will necessarily be fake in the case
>> when it is in fact userspace implementing and managing the drivers.
>
> If userspace is managing to figure out how to control the device then
> providing a description of the hardware is clearly within the bounds of
> possibility and there is no need to fake anything.

However. maintaining the hardware description in multiple places is
redundant and error-prone. Since the userspace somehow managed to
figure it out on legacy kernels without devicetree it does not need
the information in devicetree. Users of such software will not want the
hardware description in devicetree and if forced to provide it will stub it out.

Also world is not all devicetree so userspace applications should be free
to consult devicetree information if present or use whatever other means
at their disposal to determine what hardware they are dealing with.
When portability is a concern consulting devicetree may be a secondary
source at best.

Thanks

Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown July 19, 2016, 5:19 p.m. UTC | #5
On Tue, Jul 19, 2016 at 05:32:24PM +0200, Michal Suchanek wrote:
> On 19 July 2016 at 14:44, Mark Brown <broonie@kernel.org> wrote:

> >> -       status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
> >> +       status = register_chrdev(SPIDEV_MAJOR, "spidev", &spidev_fops);

> > also looks like a needless ABI change

> ABI change to what?

> The spidev ABI should be the IOCTLs on /dev/spidev*. These are preserved.

> What parts of the kernel or userspace should depend on spidev
> devices belonging to class spi?

It's a string that's exposed to userspace (via /proc/devices if nothing
else).

> > If userspace is managing to figure out how to control the device then
> > providing a description of the hardware is clearly within the bounds of
> > possibility and there is no need to fake anything.

> However. maintaining the hardware description in multiple places is
> redundant and error-prone. Since the userspace somehow managed to
> figure it out on legacy kernels without devicetree it does not need
> the information in devicetree. Users of such software will not want the
> hardware description in devicetree and if forced to provide it will stub it out.

Userspace is just as capable of looking at the DT as anything else, I'd
expect that where this is a viable way of deploying things people would
be writing udev/systemd magic to fire up userspace drivers automatically
when they see suitable hardware.  The only way this has ever worked in
the past has been with system specific hacks which lead to fragile,
non-portable userpaces that are hard to upgrade.  If we're trying to say
this is an interface that's there for all devices then we shouldn't be
defining it in a way that makes it difficult to automatically configure.

> Also world is not all devicetree so userspace applications should be free
> to consult devicetree information if present or use whatever other means
> at their disposal to determine what hardware they are dealing with.
> When portability is a concern consulting devicetree may be a secondary
> source at best.

ACPI has similar facilities (it's got a direct translation of DT in it
these days so most things with a DT binding also have an ACPI one for
free).
Michal Suchanek July 19, 2016, 6:33 p.m. UTC | #6
On 19 July 2016 at 19:19, Mark Brown <broonie@kernel.org> wrote:
> On Tue, Jul 19, 2016 at 05:32:24PM +0200, Michal Suchanek wrote:
>> On 19 July 2016 at 14:44, Mark Brown <broonie@kernel.org> wrote:
>
>> >> -       status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
>> >> +       status = register_chrdev(SPIDEV_MAJOR, "spidev", &spidev_fops);
>
>> > also looks like a needless ABI change
>
>> ABI change to what?
>
>> The spidev ABI should be the IOCTLs on /dev/spidev*. These are preserved.
>
>> What parts of the kernel or userspace should depend on spidev
>> devices belonging to class spi?
>
> It's a string that's exposed to userspace (via /proc/devices if nothing
> else).

Yes, the change can be detected from userspace. The question is
more like 'Is there any sane reason anyone would rely on seeing
that exact string for something?'

BTW the classes are also exported through /sys so the spidev devices
would look differently there also.

>
>> > If userspace is managing to figure out how to control the device then
>> > providing a description of the hardware is clearly within the bounds of
>> > possibility and there is no need to fake anything.
>
>> However. maintaining the hardware description in multiple places is
>> redundant and error-prone. Since the userspace somehow managed to
>> figure it out on legacy kernels without devicetree it does not need
>> the information in devicetree. Users of such software will not want the
>> hardware description in devicetree and if forced to provide it will stub it out.
>
> Userspace is just as capable of looking at the DT as anything else, I'd
> expect that where this is a viable way of deploying things people would
> be writing udev/systemd magic to fire up userspace drivers automatically
> when they see suitable hardware.  The only way this has ever worked in
> the past has been with system specific hacks which lead to fragile,
> non-portable userpaces that are hard to upgrade.  If we're trying to say
> this is an interface that's there for all devices then we shouldn't be
> defining it in a way that makes it difficult to automatically configure.

Oh sure. Once a device-specific hack turns out to be something
desirable to maintain over long time and deploy on variety of devices we
have infrastructure to automate that.

That does not make device-specific hacks go away. Initially the user
will do only as much as it takes to get the device working right now
and that does not include writing devicetree description of the device
when it is used with userspace driver.

>
>> Also world is not all devicetree so userspace applications should be free
>> to consult devicetree information if present or use whatever other means
>> at their disposal to determine what hardware they are dealing with.
>> When portability is a concern consulting devicetree may be a secondary
>> source at best.
>
> ACPI has similar facilities (it's got a direct translation of DT in it
> these days so most things with a DT binding also have an ACPI one for
> free).

There is even a patch floating around for adding empty DT and some proxy
nodes for existing hardware so dynamically connected devices can be
configured with devicetree overlays on boards without devicetree.

So you can have devicetree description of the hardware. It's just sometimes
not useful.

Thanks

Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Fengguang Wu July 20, 2016, 7:16 a.m. UTC | #7
Hi,

[auto build test WARNING on v4.7-rc7]
[cannot apply to next-20160719]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Michal-Suchanek/Spidev-usability-patchset-update/20160720-055513
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

    include/linux/init.h:1: warning: no structured comments found
    kernel/sched/core.c:2081: warning: No description found for parameter 'cookie'
    kernel/sys.c:1: warning: no structured comments found
    drivers/dma-buf/seqno-fence.c:1: warning: no structured comments found
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'spidev_devt'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'spidev'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'spidev_lock'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'buf_lock'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'spidev_users'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'tx_buffer'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'rx_buffer'
>> include/linux/spi/spi.h:195: warning: No description found for parameter 'spidev_speed_hz'

git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 2797999799fc3b7f10f680b3e3fa34ca56e1385d
vim +/spidev_devt +195 include/linux/spi/spi.h

33e34dc6 David Brownell  2007-05-08  179  	 *  - ...
33e34dc6 David Brownell  2007-05-08  180  	 */
27979997 Michal Suchanek 2016-07-19  181  
27979997 Michal Suchanek 2016-07-19  182  	/* spidev stuff */
27979997 Michal Suchanek 2016-07-19  183  #if config_enabled(CONFIG_SPI_SPIDEV)
27979997 Michal Suchanek 2016-07-19  184  	dev_t                   spidev_devt;
27979997 Michal Suchanek 2016-07-19  185  	struct device *		spidev;
27979997 Michal Suchanek 2016-07-19  186  	spinlock_t              spidev_lock;
27979997 Michal Suchanek 2016-07-19  187  
27979997 Michal Suchanek 2016-07-19  188  	/* TX/RX buffers are NULL unless this device is open (users > 0) */
27979997 Michal Suchanek 2016-07-19  189  	struct mutex            buf_lock;
27979997 Michal Suchanek 2016-07-19  190  	unsigned                spidev_users;
27979997 Michal Suchanek 2016-07-19  191  	u8                      *tx_buffer;
27979997 Michal Suchanek 2016-07-19  192  	u8                      *rx_buffer;
27979997 Michal Suchanek 2016-07-19  193  	u32                     spidev_speed_hz;
27979997 Michal Suchanek 2016-07-19  194  #endif /*CONFIG_SPI_SPIDEV*/
8ae12a0d David Brownell  2006-01-08 @195  };
8ae12a0d David Brownell  2006-01-08  196  
8ae12a0d David Brownell  2006-01-08  197  static inline struct spi_device *to_spi_device(struct device *dev)
8ae12a0d David Brownell  2006-01-08  198  {
b885244e David Brownell  2006-01-08  199  	return dev ? container_of(dev, struct spi_device, dev) : NULL;
8ae12a0d David Brownell  2006-01-08  200  }
8ae12a0d David Brownell  2006-01-08  201  
8ae12a0d David Brownell  2006-01-08  202  /* most drivers won't need to care about device refcounting */
8ae12a0d David Brownell  2006-01-08  203  static inline struct spi_device *spi_dev_get(struct spi_device *spi)



---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Mark Brown July 27, 2016, 6:27 p.m. UTC | #8
On Tue, Jul 19, 2016 at 08:33:12PM +0200, Michal Suchanek wrote:
> On 19 July 2016 at 19:19, Mark Brown <broonie@kernel.org> wrote:

> > It's a string that's exposed to userspace (via /proc/devices if nothing
> > else).

> Yes, the change can be detected from userspace. The question is
> more like 'Is there any sane reason anyone would rely on seeing
> that exact string for something?'

People using it to create device files at runtime or detect board
variants would be the main ones.

> BTW the classes are also exported through /sys so the spidev devices
> would look differently there also.

Yup.

> That does not make device-specific hacks go away. Initially the user
> will do only as much as it takes to get the device working right now
> and that does not include writing devicetree description of the device
> when it is used with userspace driver.

Depends, especially if you see some long term use for your code it's
common to start with the enumeration part.  It's not there currently so
people can't do that but part of getting the good practice out there is
to make it the path of least resistance.
diff mbox

Patch

diff --git a/Documentation/spi/spidev b/Documentation/spi/spidev
index 3d14035..dd8eac2 100644
--- a/Documentation/spi/spidev
+++ b/Documentation/spi/spidev
@@ -21,23 +21,14 @@  they need to access kernel interfaces (such as IRQ handlers or other layers
 of the driver stack) that are not accessible to userspace.
 
 
-DEVICE CREATION, DRIVER BINDING
-===============================
-The simplest way to arrange to use this driver is to just list it in the
-spi_board_info for a device as the driver it should use:  the "modalias"
-entry is "spidev", matching the name of the driver exposing this API.
-Set up the other device characteristics (bits per word, SPI clocking,
-chipselect polarity, etc) as usual, so you won't always need to override
-them later.
-
-(Sysfs also supports userspace driven binding/unbinding of drivers to
-devices.  That mechanism might be supported here in the future.)
-
-When you do that, the sysfs node for the SPI device will include a child
-device node with a "dev" attribute that will be understood by udev or mdev.
-(Larger systems will have "udev".  Smaller ones may configure "mdev" into
-busybox; it's less featureful, but often enough.)  For a SPI device with
-chipselect C on bus B, you should see:
+DEVICE CREATION
+===============
+
+When spidev is included in the kernel, the sysfs node for the SPI device will
+include a child device node with a "dev" attribute that will be understood by
+udev or mdev.  (Larger systems will have "udev".  Smaller ones may configure
+"mdev" into busybox; it's less featureful, but often enough.)  For a SPI device
+with chipselect C on bus B, you should see:
 
     /dev/spidevB.C ... character special device, major number 153 with
 	a dynamically chosen minor device number.  This is the node
@@ -54,18 +45,9 @@  Do not try to manage the /dev character device special file nodes by hand.
 That's error prone, and you'd need to pay careful attention to system
 security issues; udev/mdev should already be configured securely.
 
-If you unbind the "spidev" driver from that device, those two "spidev" nodes
-(in sysfs and in /dev) should automatically be removed (respectively by the
-kernel and by udev/mdev).  You can unbind by removing the "spidev" driver
-module, which will affect all devices using this driver.  You can also unbind
-by having kernel code remove the SPI device, probably by removing the driver
-for its SPI controller (so its spi_master vanishes).
-
-Since this is a standard Linux device driver -- even though it just happens
-to expose a low level API to userspace -- it can be associated with any number
-of devices at a time.  Just provide one spi_board_info record for each such
-SPI device, and you'll get a /dev device node for each device.
-
+When a device driver is loaded for the SPI device operations on the spidev
+character device return EBUSY. You can unbind a driver from the device using
+the driver unbind file in sysfs.
 
 BASIC CHARACTER DEVICE API
 ==========================
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index f2bbb16..3806e3c 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -699,7 +699,7 @@  config SPI_ZYNQMP_GQSPI
 comment "SPI Protocol Masters"
 
 config SPI_SPIDEV
-	tristate "User mode SPI device driver support"
+	bool "User mode SPI device driver support"
 	help
 	  This supports user mode SPI protocol drivers.
 
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 77e6e45..f3ea768 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -41,6 +41,8 @@ 
 #define CREATE_TRACE_POINTS
 #include <trace/events/spi.h>
 
+#include "spidev.h"
+
 static void spidev_release(struct device *dev)
 {
 	struct spi_device	*spi = to_spi_device(dev);
@@ -544,11 +546,15 @@  int spi_add_device(struct spi_device *spi)
 
 	/* Device may be bound to an active driver when this returns */
 	status = device_add(&spi->dev);
-	if (status < 0)
+	if (status < 0) {
 		dev_err(dev, "can't add %s, status %d\n",
 				dev_name(&spi->dev), status);
-	else
-		dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
+		goto done;
+	}
+	dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
+
+	if (IS_ENABLED(CONFIG_SPI_SPIDEV))
+		spi_attach_spidev(spi);
 
 done:
 	mutex_unlock(&spi_add_lock);
@@ -622,6 +628,10 @@  void spi_unregister_device(struct spi_device *spi)
 
 	if (spi->dev.of_node)
 		of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
+
+	if (IS_ENABLED(CONFIG_SPI_SPIDEV))
+		spi_detach_spidev(spi);
+
 	device_unregister(&spi->dev);
 }
 EXPORT_SYMBOL_GPL(spi_unregister_device);
@@ -3128,6 +3138,9 @@  static int __init spi_init(void)
 	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
 		WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
 
+	if (IS_ENABLED(CONFIG_SPI_SPIDEV))
+		WARN_ON(spidev_init());
+
 	return 0;
 
 err2:
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index e3c19f3..57b47d0 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -27,14 +27,14 @@ 
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/compat.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/spidev.h>
 
 #include <linux/uaccess.h>
 
+#include "spidev.h"
+
 
 /*
  * This supports access to SPI devices using normal userspace I/O calls.
@@ -72,23 +72,6 @@  static DECLARE_BITMAP(minors, N_SPI_MINORS);
 				| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
 				| SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)
 
-struct spidev_data {
-	dev_t			devt;
-	spinlock_t		spi_lock;
-	struct spi_device	*spi;
-	struct list_head	device_entry;
-
-	/* TX/RX buffers are NULL unless this device is open (users > 0) */
-	struct mutex		buf_lock;
-	unsigned		users;
-	u8			*tx_buffer;
-	u8			*rx_buffer;
-	u32			speed_hz;
-};
-
-static LIST_HEAD(device_list);
-static DEFINE_MUTEX(device_list_lock);
-
 static unsigned bufsiz = 4096;
 module_param(bufsiz, uint, S_IRUGO);
 MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
@@ -96,20 +79,11 @@  MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
 /*-------------------------------------------------------------------------*/
 
 static ssize_t
-spidev_sync(struct spidev_data *spidev, struct spi_message *message)
+spidev_sync(struct spi_device *spi, struct spi_message *message)
 {
-	DECLARE_COMPLETION_ONSTACK(done);
 	int status;
-	struct spi_device *spi;
-
-	spin_lock_irq(&spidev->spi_lock);
-	spi = spidev->spi;
-	spin_unlock_irq(&spidev->spi_lock);
 
-	if (spi == NULL)
-		status = -ESHUTDOWN;
-	else
-		status = spi_sync(spi, message);
+	status = spi_sync(spi, message);
 
 	if (status == 0)
 		status = message->actual_length;
@@ -118,33 +92,33 @@  spidev_sync(struct spidev_data *spidev, struct spi_message *message)
 }
 
 static inline ssize_t
-spidev_sync_write(struct spidev_data *spidev, size_t len)
+spidev_sync_write(struct spi_device *spi, size_t len)
 {
 	struct spi_transfer	t = {
-			.tx_buf		= spidev->tx_buffer,
+			.tx_buf		= spi->tx_buffer,
 			.len		= len,
-			.speed_hz	= spidev->speed_hz,
+			.speed_hz	= spi->spidev_speed_hz,
 		};
 	struct spi_message	m;
 
 	spi_message_init(&m);
 	spi_message_add_tail(&t, &m);
-	return spidev_sync(spidev, &m);
+	return spidev_sync(spi, &m);
 }
 
 static inline ssize_t
-spidev_sync_read(struct spidev_data *spidev, size_t len)
+spidev_sync_read(struct spi_device *spi, size_t len)
 {
 	struct spi_transfer	t = {
-			.rx_buf		= spidev->rx_buffer,
+			.rx_buf		= spi->rx_buffer,
 			.len		= len,
-			.speed_hz	= spidev->speed_hz,
+			.speed_hz	= spi->spidev_speed_hz,
 		};
 	struct spi_message	m;
 
 	spi_message_init(&m);
 	spi_message_add_tail(&t, &m);
-	return spidev_sync(spidev, &m);
+	return spidev_sync(spi, &m);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -153,27 +127,27 @@  spidev_sync_read(struct spidev_data *spidev, size_t len)
 static ssize_t
 spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
 {
-	struct spidev_data	*spidev;
+	struct spi_device	*spi;
 	ssize_t			status = 0;
 
 	/* chipselect only toggles at start or end of operation */
 	if (count > bufsiz)
 		return -EMSGSIZE;
 
-	spidev = filp->private_data;
+	spi = filp->private_data;
 
-	mutex_lock(&spidev->buf_lock);
-	status = spidev_sync_read(spidev, count);
+	mutex_lock(&spi->buf_lock);
+	status = spidev_sync_read(spi, count);
 	if (status > 0) {
 		unsigned long	missing;
 
-		missing = copy_to_user(buf, spidev->rx_buffer, status);
+		missing = copy_to_user(buf, spi->rx_buffer, status);
 		if (missing == status)
 			status = -EFAULT;
 		else
 			status = status - missing;
 	}
-	mutex_unlock(&spidev->buf_lock);
+	mutex_unlock(&spi->buf_lock);
 
 	return status;
 }
@@ -183,7 +157,7 @@  static ssize_t
 spidev_write(struct file *filp, const char __user *buf,
 		size_t count, loff_t *f_pos)
 {
-	struct spidev_data	*spidev;
+	struct spi_device	*spi;
 	ssize_t			status = 0;
 	unsigned long		missing;
 
@@ -191,20 +165,20 @@  spidev_write(struct file *filp, const char __user *buf,
 	if (count > bufsiz)
 		return -EMSGSIZE;
 
-	spidev = filp->private_data;
+	spi = filp->private_data;
 
-	mutex_lock(&spidev->buf_lock);
-	missing = copy_from_user(spidev->tx_buffer, buf, count);
+	mutex_lock(&spi->buf_lock);
+	missing = copy_from_user(spi->tx_buffer, buf, count);
 	if (missing == 0)
-		status = spidev_sync_write(spidev, count);
+		status = spidev_sync_write(spi, count);
 	else
 		status = -EFAULT;
-	mutex_unlock(&spidev->buf_lock);
+	mutex_unlock(&spi->buf_lock);
 
 	return status;
 }
 
-static int spidev_message(struct spidev_data *spidev,
+static int spidev_message(struct spi_device *spi,
 		struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
 {
 	struct spi_message	msg;
@@ -224,8 +198,8 @@  static int spidev_message(struct spidev_data *spidev,
 	 * We walk the array of user-provided transfers, using each one
 	 * to initialize a kernel version of the same transfer.
 	 */
-	tx_buf = spidev->tx_buffer;
-	rx_buf = spidev->rx_buffer;
+	tx_buf = spi->tx_buffer;
+	rx_buf = spi->rx_buffer;
 	total = 0;
 	tx_total = 0;
 	rx_total = 0;
@@ -281,27 +255,27 @@  static int spidev_message(struct spidev_data *spidev,
 		k_tmp->delay_usecs = u_tmp->delay_usecs;
 		k_tmp->speed_hz = u_tmp->speed_hz;
 		if (!k_tmp->speed_hz)
-			k_tmp->speed_hz = spidev->speed_hz;
+			k_tmp->speed_hz = spi->spidev_speed_hz;
 #ifdef VERBOSE
-		dev_dbg(&spidev->spi->dev,
+		dev_dbg(&spi->dev,
 			"  xfer len %u %s%s%s%dbits %u usec %uHz\n",
 			u_tmp->len,
 			u_tmp->rx_buf ? "rx " : "",
 			u_tmp->tx_buf ? "tx " : "",
 			u_tmp->cs_change ? "cs " : "",
-			u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
+			u_tmp->bits_per_word ? : spi->bits_per_word,
 			u_tmp->delay_usecs,
-			u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
+			u_tmp->speed_hz ? : spi->max_speed_hz);
 #endif
 		spi_message_add_tail(k_tmp, &msg);
 	}
 
-	status = spidev_sync(spidev, &msg);
+	status = spidev_sync(spi, &msg);
 	if (status < 0)
 		goto done;
 
 	/* copy any rx data out of bounce buffer */
-	rx_buf = spidev->rx_buffer;
+	rx_buf = spi->rx_buffer;
 	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
 		if (u_tmp->rx_buf) {
 			if (__copy_to_user((u8 __user *)
@@ -356,7 +330,6 @@  spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	int			err = 0;
 	int			retval = 0;
-	struct spidev_data	*spidev;
 	struct spi_device	*spi;
 	u32			tmp;
 	unsigned		n_ioc;
@@ -382,21 +355,22 @@  spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	/* guard against device removal before, or while,
 	 * we issue this ioctl.
 	 */
-	spidev = filp->private_data;
-	spin_lock_irq(&spidev->spi_lock);
-	spi = spi_dev_get(spidev->spi);
-	spin_unlock_irq(&spidev->spi_lock);
+	spi = filp->private_data;
+	spi = spi_dev_get(spi);
 
 	if (spi == NULL)
 		return -ESHUTDOWN;
 
+	if (spi->dev.driver)
+		return -EBUSY;
+
 	/* use the buffer lock here for triple duty:
 	 *  - prevent I/O (from us) so calling spi_setup() is safe;
 	 *  - prevent concurrent SPI_IOC_WR_* from morphing
 	 *    data fields while SPI_IOC_RD_* reads them;
 	 *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
 	 */
-	mutex_lock(&spidev->buf_lock);
+	mutex_lock(&spi->buf_lock);
 
 	switch (cmd) {
 	/* read requests */
@@ -416,7 +390,7 @@  spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
 		break;
 	case SPI_IOC_RD_MAX_SPEED_HZ:
-		retval = __put_user(spidev->speed_hz, (__u32 __user *)arg);
+		retval = __put_user(spi->spidev_speed_hz, (__u32 __user *)arg);
 		break;
 
 	/* write requests */
@@ -474,14 +448,14 @@  spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		}
 		break;
 	case SPI_IOC_WR_MAX_SPEED_HZ:
-		retval = __get_user(tmp, (__u32 __user *)arg);
+	retval = __get_user(tmp, (__u32 __user *)arg);
 		if (retval == 0) {
 			u32	save = spi->max_speed_hz;
 
 			spi->max_speed_hz = tmp;
 			retval = spi_setup(spi);
 			if (retval >= 0)
-				spidev->speed_hz = tmp;
+				spi->spidev_speed_hz = tmp;
 			else
 				dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
 			spi->max_speed_hz = save;
@@ -501,12 +475,12 @@  spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 			break;	/* n_ioc is also 0 */
 
 		/* translate to spi_message, execute */
-		retval = spidev_message(spidev, ioc, n_ioc);
+		retval = spidev_message(spi, ioc, n_ioc);
 		kfree(ioc);
 		break;
 	}
 
-	mutex_unlock(&spidev->buf_lock);
+	mutex_unlock(&spi->buf_lock);
 	spi_dev_put(spi);
 	return retval;
 }
@@ -518,7 +492,6 @@  spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
 {
 	struct spi_ioc_transfer __user	*u_ioc;
 	int				retval = 0;
-	struct spidev_data		*spidev;
 	struct spi_device		*spi;
 	unsigned			n_ioc, n;
 	struct spi_ioc_transfer		*ioc;
@@ -530,16 +503,17 @@  spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
 	/* guard against device removal before, or while,
 	 * we issue this ioctl.
 	 */
-	spidev = filp->private_data;
-	spin_lock_irq(&spidev->spi_lock);
-	spi = spi_dev_get(spidev->spi);
-	spin_unlock_irq(&spidev->spi_lock);
+	spi = filp->private_data;
+	spi = spi_dev_get(spi);
 
 	if (spi == NULL)
 		return -ESHUTDOWN;
 
+	if (spi->dev.driver)
+		return -EBUSY;
+
 	/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
-	mutex_lock(&spidev->buf_lock);
+	mutex_lock(&spi->buf_lock);
 
 	/* Check message and copy into scratch area */
 	ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc);
@@ -557,11 +531,11 @@  spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
 	}
 
 	/* translate to spi_message, execute */
-	retval = spidev_message(spidev, ioc, n_ioc);
+	retval = spidev_message(spi, ioc, n_ioc);
 	kfree(ioc);
 
 done:
-	mutex_unlock(&spidev->buf_lock);
+	mutex_unlock(&spi->buf_lock);
 	spi_dev_put(spi);
 	return retval;
 }
@@ -580,89 +554,108 @@  spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 #define spidev_compat_ioctl NULL
 #endif /* CONFIG_COMPAT */
 
+static int spidev_devt_match(struct device *dev, void *data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	dev_t devt = (dev_t) data;
+	int res = 0;
+
+	spin_lock_irq(&spi->spidev_lock);
+	if (spi->spidev_devt == devt)
+		res = 1;
+	spin_unlock_irq(&spi->spidev_lock);
+
+	return res;
+}
+
 static int spidev_open(struct inode *inode, struct file *filp)
 {
-	struct spidev_data	*spidev;
+	struct spi_device	*spi;
+	struct device		*dev;
 	int			status = -ENXIO;
 
-	mutex_lock(&device_list_lock);
+	dev = bus_find_device(&spi_bus_type, NULL, (void *) inode->i_rdev,
+			      spidev_devt_match);
 
-	list_for_each_entry(spidev, &device_list, device_entry) {
-		if (spidev->devt == inode->i_rdev) {
-			status = 0;
-			break;
-		}
-	}
-
-	if (status) {
+	if (!dev) {
 		pr_debug("spidev: nothing for minor %d\n", iminor(inode));
-		goto err_find_dev;
+		return status;
 	}
 
-	if (!spidev->tx_buffer) {
-		spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
-		if (!spidev->tx_buffer) {
-			dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
+	spi = to_spi_device(dev);
+
+	mutex_lock(&spi->buf_lock);
+	spin_lock_irq(&spi->spidev_lock);
+	spi->spidev_users++;
+	spin_unlock_irq(&spi->spidev_lock);
+
+	if (!spi->tx_buffer) {
+		spi->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
+		if (!spi->tx_buffer) {
+			dev_dbg(&spi->dev, "open/ENOMEM\n");
 			status = -ENOMEM;
 			goto err_find_dev;
 		}
 	}
 
-	if (!spidev->rx_buffer) {
-		spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
-		if (!spidev->rx_buffer) {
-			dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
+	if (!spi->rx_buffer) {
+		spi->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
+		if (!spi->rx_buffer) {
+			dev_dbg(&spi->dev, "open/ENOMEM\n");
 			status = -ENOMEM;
 			goto err_alloc_rx_buf;
 		}
 	}
 
-	spidev->users++;
-	filp->private_data = spidev;
+	mutex_unlock(&spi->buf_lock);
+
+	filp->private_data = spi;
 	nonseekable_open(inode, filp);
 
-	mutex_unlock(&device_list_lock);
 	return 0;
 
 err_alloc_rx_buf:
-	kfree(spidev->tx_buffer);
-	spidev->tx_buffer = NULL;
+	kfree(spi->tx_buffer);
+	spi->tx_buffer = NULL;
 err_find_dev:
-	mutex_unlock(&device_list_lock);
+	mutex_unlock(&spi->buf_lock);
+	spin_lock_irq(&spi->spidev_lock);
+	spi->spidev_users--;
+	spin_unlock_irq(&spi->spidev_lock);
 	return status;
 }
 
 static int spidev_release(struct inode *inode, struct file *filp)
 {
-	struct spidev_data	*spidev;
+	struct spi_device	*spi;
+	int do_free = 0;
 
-	mutex_lock(&device_list_lock);
-	spidev = filp->private_data;
+	spi = filp->private_data;
 	filp->private_data = NULL;
 
+	mutex_lock(&spi->buf_lock);
+	spin_lock_irq(&spi->spidev_lock);
+	spi->spidev_users--;
+
 	/* last close? */
-	spidev->users--;
-	if (!spidev->users) {
-		int		dofree;
+	if (!spi->spidev_users)
+		do_free = 1;
 
-		kfree(spidev->tx_buffer);
-		spidev->tx_buffer = NULL;
+	spin_unlock_irq(&spi->spidev_lock);
 
-		kfree(spidev->rx_buffer);
-		spidev->rx_buffer = NULL;
+	if (do_free) {
 
-		spin_lock_irq(&spidev->spi_lock);
-		if (spidev->spi)
-			spidev->speed_hz = spidev->spi->max_speed_hz;
+		kfree(spi->tx_buffer);
+		spi->tx_buffer = NULL;
 
-		/* ... after we unbound from the underlying device? */
-		dofree = (spidev->spi == NULL);
-		spin_unlock_irq(&spidev->spi_lock);
+		kfree(spi->rx_buffer);
+		spi->rx_buffer = NULL;
 
-		if (dofree)
-			kfree(spidev);
+		spi->spidev_speed_hz = spi->max_speed_hz;
 	}
-	mutex_unlock(&device_list_lock);
+	mutex_unlock(&spi->buf_lock);
+
+	spi_dev_put(spi);
 
 	return 0;
 }
@@ -691,152 +684,99 @@  static const struct file_operations spidev_fops = {
 
 static struct class *spidev_class;
 
-#ifdef CONFIG_OF
-static const struct of_device_id spidev_dt_ids[] = {
-	{ .compatible = "rohm,dh2228fv" },
-	{ .compatible = "lineartechnology,ltc2488" },
-	{},
-};
-MODULE_DEVICE_TABLE(of, spidev_dt_ids);
-#endif
-
 /*-------------------------------------------------------------------------*/
 
-static int spidev_probe(struct spi_device *spi)
+int spi_attach_spidev(struct spi_device *spi)
 {
-	struct spidev_data	*spidev;
 	int			status;
 	unsigned long		minor;
 
-	/*
-	 * spidev should never be referenced in DT without a specific
-	 * compatible string, it is a Linux implementation thing
-	 * rather than a description of the hardware.
-	 */
-	if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
-		dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
-		WARN_ON(spi->dev.of_node &&
-			!of_match_device(spidev_dt_ids, &spi->dev));
-	}
-
-	/* Allocate driver data */
-	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
-	if (!spidev)
-		return -ENOMEM;
-
 	/* Initialize the driver data */
-	spidev->spi = spi;
-	spin_lock_init(&spidev->spi_lock);
-	mutex_init(&spidev->buf_lock);
-
-	INIT_LIST_HEAD(&spidev->device_entry);
+	spin_lock_init(&spi->spidev_lock);
+	mutex_init(&spi->buf_lock);
 
 	/* If we can allocate a minor number, hook up this device.
 	 * Reusing minors is fine so long as udev or mdev is working.
 	 */
-	mutex_lock(&device_list_lock);
 	minor = find_first_zero_bit(minors, N_SPI_MINORS);
 	if (minor < N_SPI_MINORS) {
-		struct device *dev;
 
-		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
-		dev = device_create(spidev_class, &spi->dev, spidev->devt,
-				    spidev, "spidev%d.%d",
-				    spi->master->bus_num, spi->chip_select);
-		status = PTR_ERR_OR_ZERO(dev);
+		spi->spidev_devt = MKDEV(SPIDEV_MAJOR, minor);
+
+		spi->spidev = device_create(spidev_class, &spi->dev,
+					    spi->spidev_devt,
+					    spi, "spidev%d.%d",
+					    spi->master->bus_num,
+					    spi->chip_select);
+		status = PTR_ERR_OR_ZERO(spi->spidev);
+		if (status != 0) {
+			dev_err(&spi->dev, "Error creating spidev device!\n");
+			spi->spidev_devt = 0;
+			spi->spidev = NULL;
+		}
 	} else {
 		dev_dbg(&spi->dev, "no minor number available!\n");
 		status = -ENODEV;
 	}
 	if (status == 0) {
 		set_bit(minor, minors);
-		list_add(&spidev->device_entry, &device_list);
 	}
-	mutex_unlock(&device_list_lock);
-
-	spidev->speed_hz = spi->max_speed_hz;
-
-	if (status == 0)
-		spi_set_drvdata(spi, spidev);
-	else
-		kfree(spidev);
+	spi->spidev_speed_hz = spi->max_speed_hz;
 
 	return status;
 }
 
-static int spidev_remove(struct spi_device *spi)
+int spi_detach_spidev(struct spi_device *spi)
 {
-	struct spidev_data	*spidev = spi_get_drvdata(spi);
-
-	/* make sure ops on existing fds can abort cleanly */
-	spin_lock_irq(&spidev->spi_lock);
-	spidev->spi = NULL;
-	spin_unlock_irq(&spidev->spi_lock);
-
-	/* prevent new opens */
-	mutex_lock(&device_list_lock);
-	list_del(&spidev->device_entry);
-	device_destroy(spidev_class, spidev->devt);
-	clear_bit(MINOR(spidev->devt), minors);
-	if (spidev->users == 0)
-		kfree(spidev);
-	mutex_unlock(&device_list_lock);
+	dev_t	devt;
+	struct device *spidev;
+
+	/*
+	 * Make sure ops on existing fds can abort cleanly
+	 * and prevent new ones.
+	 */
+	spin_lock_irq(&spi->spidev_lock);
+	devt = spi->spidev_devt;
+	spidev = spi->spidev;
+	spi->spidev_devt = 0;
+	spi->spidev = NULL;
+	spin_unlock_irq(&spi->spidev_lock);
+
+	device_destroy(spidev_class, devt);
+	clear_bit(MINOR(devt), minors);
 
 	return 0;
 }
 
-static struct spi_driver spidev_spi_driver = {
-	.driver = {
-		.name =		"spidev",
-		.of_match_table = of_match_ptr(spidev_dt_ids),
-	},
-	.probe =	spidev_probe,
-	.remove =	spidev_remove,
-
-	/* NOTE:  suspend/resume methods are not necessary here.
-	 * We don't do anything except pass the requests to/from
-	 * the underlying controller.  The refrigerator handles
-	 * most issues; the controller driver handles the rest.
-	 */
-};
-
 /*-------------------------------------------------------------------------*/
 
-static int __init spidev_init(void)
+int __init spidev_init(void)
 {
 	int status;
 
 	/* Claim our 256 reserved device numbers.  Then register a class
-	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
-	 * the driver which manages those device numbers.
+	 * that will key udev/mdev to add/remove /dev nodes.
 	 */
 	BUILD_BUG_ON(N_SPI_MINORS > 256);
-	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
+	status = register_chrdev(SPIDEV_MAJOR, "spidev", &spidev_fops);
 	if (status < 0)
 		return status;
 
 	spidev_class = class_create(THIS_MODULE, "spidev");
 	if (IS_ERR(spidev_class)) {
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
+		unregister_chrdev(SPIDEV_MAJOR, "spidev");
 		return PTR_ERR(spidev_class);
 	}
 
-	status = spi_register_driver(&spidev_spi_driver);
-	if (status < 0) {
-		class_destroy(spidev_class);
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
-	}
 	return status;
 }
-module_init(spidev_init);
 
 static void __exit spidev_exit(void)
 {
-	spi_unregister_driver(&spidev_spi_driver);
 	class_destroy(spidev_class);
-	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
+	unregister_chrdev(SPIDEV_MAJOR, "spidev");
 }
-module_exit(spidev_exit);
+EXPORT_SYMBOL_GPL(spidev_exit);
 
 MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
 MODULE_DESCRIPTION("User mode SPI device interface");
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 1f03483..7154559 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -178,6 +178,20 @@  struct spi_device {
 	 *  - chipselect delays
 	 *  - ...
 	 */
+
+	/* spidev stuff */
+#if config_enabled(CONFIG_SPI_SPIDEV)
+	dev_t                   spidev_devt;
+	struct device *		spidev;
+	spinlock_t              spidev_lock;
+
+	/* TX/RX buffers are NULL unless this device is open (users > 0) */
+	struct mutex            buf_lock;
+	unsigned                spidev_users;
+	u8                      *tx_buffer;
+	u8                      *rx_buffer;
+	u32                     spidev_speed_hz;
+#endif /*CONFIG_SPI_SPIDEV*/
 };
 
 static inline struct spi_device *to_spi_device(struct device *dev)