123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860 |
- /*
- * Simple synchronous userspace interface to SPI devices
- *
- * Copyright (C) 2006 SWAPP
- * Andrea Paterniani <a.paterniani@swapp-eng.it>
- * Copyright (C) 2007 David Brownell (simplification, cleanup)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/ioctl.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/list.h>
- #include <linux/errno.h>
- #include <linux/mutex.h>
- #include <linux/slab.h>
- #include <linux/compat.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/acpi.h>
- #include <linux/spi/spi.h>
- #include <linux/spi/spidev.h>
- #include <linux/uaccess.h>
- /*
- * This supports access to SPI devices using normal userspace I/O calls.
- * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
- * and often mask message boundaries, full SPI support requires full duplex
- * transfers. There are several kinds of internal message boundaries to
- * handle chipselect management and other protocol options.
- *
- * SPI has a character major number assigned. We allocate minor numbers
- * dynamically using a bitmask. You must use hotplug tools, such as udev
- * (or mdev with busybox) to create and destroy the /dev/spidevB.C device
- * nodes, since there is no fixed association of minor numbers with any
- * particular SPI bus or device.
- */
- #define SPIDEV_MAJOR 153 /* assigned */
- #define N_SPI_MINORS 32 /* ... up to 256 */
- static DECLARE_BITMAP(minors, N_SPI_MINORS);
- /* Bit masks for spi_device.mode management. Note that incorrect
- * settings for some settings can cause *lots* of trouble for other
- * devices on a shared bus:
- *
- * - CS_HIGH ... this device will be active when it shouldn't be
- * - 3WIRE ... when active, it won't behave as it should
- * - NO_CS ... there will be no explicit message boundaries; this
- * is completely incompatible with the shared bus model
- * - READY ... transfers may proceed when they shouldn't.
- *
- * REVISIT should changing those flags be privileged?
- */
- #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
- | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
- | 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");
- /*-------------------------------------------------------------------------*/
- static ssize_t
- spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- 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);
- if (status == 0)
- status = message->actual_length;
- return status;
- }
- static inline ssize_t
- spidev_sync_write(struct spidev_data *spidev, size_t len)
- {
- struct spi_transfer t = {
- .tx_buf = spidev->tx_buffer,
- .len = len,
- .speed_hz = spidev->speed_hz,
- };
- struct spi_message m;
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- return spidev_sync(spidev, &m);
- }
- static inline ssize_t
- spidev_sync_read(struct spidev_data *spidev, size_t len)
- {
- struct spi_transfer t = {
- .rx_buf = spidev->rx_buffer,
- .len = len,
- .speed_hz = spidev->speed_hz,
- };
- struct spi_message m;
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- return spidev_sync(spidev, &m);
- }
- /*-------------------------------------------------------------------------*/
- /* Read-only message with current device setup */
- static ssize_t
- spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
- {
- struct spidev_data *spidev;
- ssize_t status = 0;
- /* chipselect only toggles at start or end of operation */
- if (count > bufsiz)
- return -EMSGSIZE;
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
- status = spidev_sync_read(spidev, count);
- if (status > 0) {
- unsigned long missing;
- missing = copy_to_user(buf, spidev->rx_buffer, status);
- if (missing == status)
- status = -EFAULT;
- else
- status = status - missing;
- }
- mutex_unlock(&spidev->buf_lock);
- return status;
- }
- /* Write-only message with current device setup */
- static ssize_t
- spidev_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
- {
- struct spidev_data *spidev;
- ssize_t status = 0;
- unsigned long missing;
- /* chipselect only toggles at start or end of operation */
- if (count > bufsiz)
- return -EMSGSIZE;
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
- missing = copy_from_user(spidev->tx_buffer, buf, count);
- if (missing == 0)
- status = spidev_sync_write(spidev, count);
- else
- status = -EFAULT;
- mutex_unlock(&spidev->buf_lock);
- return status;
- }
- static int spidev_message(struct spidev_data *spidev,
- struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
- {
- struct spi_message msg;
- struct spi_transfer *k_xfers;
- struct spi_transfer *k_tmp;
- struct spi_ioc_transfer *u_tmp;
- unsigned n, total, tx_total, rx_total;
- u8 *tx_buf, *rx_buf;
- int status = -EFAULT;
- spi_message_init(&msg);
- k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
- if (k_xfers == NULL)
- return -ENOMEM;
- /* Construct spi_message, copying any tx data to bounce buffer.
- * 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;
- total = 0;
- tx_total = 0;
- rx_total = 0;
- for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
- n;
- n--, k_tmp++, u_tmp++) {
- k_tmp->len = u_tmp->len;
- total += k_tmp->len;
- /* Since the function returns the total length of transfers
- * on success, restrict the total to positive int values to
- * avoid the return value looking like an error. Also check
- * each transfer length to avoid arithmetic overflow.
- */
- if (total > INT_MAX || k_tmp->len > INT_MAX) {
- status = -EMSGSIZE;
- goto done;
- }
- if (u_tmp->rx_buf) {
- /* this transfer needs space in RX bounce buffer */
- rx_total += k_tmp->len;
- if (rx_total > bufsiz) {
- status = -EMSGSIZE;
- goto done;
- }
- k_tmp->rx_buf = rx_buf;
- rx_buf += k_tmp->len;
- }
- if (u_tmp->tx_buf) {
- /* this transfer needs space in TX bounce buffer */
- tx_total += k_tmp->len;
- if (tx_total > bufsiz) {
- status = -EMSGSIZE;
- goto done;
- }
- k_tmp->tx_buf = tx_buf;
- if (copy_from_user(tx_buf, (const u8 __user *)
- (uintptr_t) u_tmp->tx_buf,
- u_tmp->len))
- goto done;
- tx_buf += k_tmp->len;
- }
- k_tmp->cs_change = !!u_tmp->cs_change;
- k_tmp->tx_nbits = u_tmp->tx_nbits;
- k_tmp->rx_nbits = u_tmp->rx_nbits;
- k_tmp->bits_per_word = u_tmp->bits_per_word;
- 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;
- #ifdef VERBOSE
- dev_dbg(&spidev->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->delay_usecs,
- u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
- #endif
- spi_message_add_tail(k_tmp, &msg);
- }
- status = spidev_sync(spidev, &msg);
- if (status < 0)
- goto done;
- /* copy any rx data out of bounce buffer */
- rx_buf = spidev->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 *)
- (uintptr_t) u_tmp->rx_buf, rx_buf,
- u_tmp->len)) {
- status = -EFAULT;
- goto done;
- }
- rx_buf += u_tmp->len;
- }
- }
- status = total;
- done:
- kfree(k_xfers);
- return status;
- }
- static struct spi_ioc_transfer *
- spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc,
- unsigned *n_ioc)
- {
- u32 tmp;
- /* Check type, command number and direction */
- if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC
- || _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
- || _IOC_DIR(cmd) != _IOC_WRITE)
- return ERR_PTR(-ENOTTY);
- tmp = _IOC_SIZE(cmd);
- if ((tmp % sizeof(struct spi_ioc_transfer)) != 0)
- return ERR_PTR(-EINVAL);
- *n_ioc = tmp / sizeof(struct spi_ioc_transfer);
- if (*n_ioc == 0)
- return NULL;
- /* copy into scratch area */
- return memdup_user(u_ioc, tmp);
- }
- static long
- spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- int retval = 0;
- struct spidev_data *spidev;
- struct spi_device *spi;
- u32 tmp;
- unsigned n_ioc;
- struct spi_ioc_transfer *ioc;
- /* Check type and command number */
- if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
- return -ENOTTY;
- /* 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);
- if (spi == NULL)
- return -ESHUTDOWN;
- /* 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);
- switch (cmd) {
- /* read requests */
- case SPI_IOC_RD_MODE:
- retval = put_user(spi->mode & SPI_MODE_MASK,
- (__u8 __user *)arg);
- break;
- case SPI_IOC_RD_MODE32:
- retval = put_user(spi->mode & SPI_MODE_MASK,
- (__u32 __user *)arg);
- break;
- case SPI_IOC_RD_LSB_FIRST:
- retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
- (__u8 __user *)arg);
- break;
- case SPI_IOC_RD_BITS_PER_WORD:
- 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);
- break;
- /* write requests */
- case SPI_IOC_WR_MODE:
- case SPI_IOC_WR_MODE32:
- if (cmd == SPI_IOC_WR_MODE)
- retval = get_user(tmp, (u8 __user *)arg);
- else
- retval = get_user(tmp, (u32 __user *)arg);
- if (retval == 0) {
- u32 save = spi->mode;
- if (tmp & ~SPI_MODE_MASK) {
- retval = -EINVAL;
- break;
- }
- tmp |= spi->mode & ~SPI_MODE_MASK;
- spi->mode = (u16)tmp;
- retval = spi_setup(spi);
- if (retval < 0)
- spi->mode = save;
- else
- dev_dbg(&spi->dev, "spi mode %x\n", tmp);
- }
- break;
- case SPI_IOC_WR_LSB_FIRST:
- retval = get_user(tmp, (__u8 __user *)arg);
- if (retval == 0) {
- u32 save = spi->mode;
- if (tmp)
- spi->mode |= SPI_LSB_FIRST;
- else
- spi->mode &= ~SPI_LSB_FIRST;
- retval = spi_setup(spi);
- if (retval < 0)
- spi->mode = save;
- else
- dev_dbg(&spi->dev, "%csb first\n",
- tmp ? 'l' : 'm');
- }
- break;
- case SPI_IOC_WR_BITS_PER_WORD:
- retval = get_user(tmp, (__u8 __user *)arg);
- if (retval == 0) {
- u8 save = spi->bits_per_word;
- spi->bits_per_word = tmp;
- retval = spi_setup(spi);
- if (retval < 0)
- spi->bits_per_word = save;
- else
- dev_dbg(&spi->dev, "%d bits per word\n", tmp);
- }
- break;
- case SPI_IOC_WR_MAX_SPEED_HZ:
- 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;
- else
- dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
- spi->max_speed_hz = save;
- }
- break;
- default:
- /* segmented and/or full-duplex I/O request */
- /* Check message and copy into scratch area */
- ioc = spidev_get_ioc_message(cmd,
- (struct spi_ioc_transfer __user *)arg, &n_ioc);
- if (IS_ERR(ioc)) {
- retval = PTR_ERR(ioc);
- break;
- }
- if (!ioc)
- break; /* n_ioc is also 0 */
- /* translate to spi_message, execute */
- retval = spidev_message(spidev, ioc, n_ioc);
- kfree(ioc);
- break;
- }
- mutex_unlock(&spidev->buf_lock);
- spi_dev_put(spi);
- return retval;
- }
- #ifdef CONFIG_COMPAT
- static long
- spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
- unsigned long arg)
- {
- 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;
- u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(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);
- if (spi == NULL)
- return -ESHUTDOWN;
- /* SPI_IOC_MESSAGE needs the buffer locked "normally" */
- mutex_lock(&spidev->buf_lock);
- /* Check message and copy into scratch area */
- ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc);
- if (IS_ERR(ioc)) {
- retval = PTR_ERR(ioc);
- goto done;
- }
- if (!ioc)
- goto done; /* n_ioc is also 0 */
- /* Convert buffer pointers */
- for (n = 0; n < n_ioc; n++) {
- ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf);
- ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf);
- }
- /* translate to spi_message, execute */
- retval = spidev_message(spidev, ioc, n_ioc);
- kfree(ioc);
- done:
- mutex_unlock(&spidev->buf_lock);
- spi_dev_put(spi);
- return retval;
- }
- static long
- spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC
- && _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0))
- && _IOC_DIR(cmd) == _IOC_WRITE)
- return spidev_compat_ioc_message(filp, cmd, arg);
- return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
- }
- #else
- #define spidev_compat_ioctl NULL
- #endif /* CONFIG_COMPAT */
- static int spidev_open(struct inode *inode, struct file *filp)
- {
- struct spidev_data *spidev;
- int status = -ENXIO;
- mutex_lock(&device_list_lock);
- list_for_each_entry(spidev, &device_list, device_entry) {
- if (spidev->devt == inode->i_rdev) {
- status = 0;
- break;
- }
- }
- if (status) {
- pr_debug("spidev: nothing for minor %d\n", iminor(inode));
- goto err_find_dev;
- }
- if (!spidev->tx_buffer) {
- spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
- if (!spidev->tx_buffer) {
- dev_dbg(&spidev->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");
- status = -ENOMEM;
- goto err_alloc_rx_buf;
- }
- }
- spidev->users++;
- filp->private_data = spidev;
- nonseekable_open(inode, filp);
- mutex_unlock(&device_list_lock);
- return 0;
- err_alloc_rx_buf:
- kfree(spidev->tx_buffer);
- spidev->tx_buffer = NULL;
- err_find_dev:
- mutex_unlock(&device_list_lock);
- return status;
- }
- static int spidev_release(struct inode *inode, struct file *filp)
- {
- struct spidev_data *spidev;
- mutex_lock(&device_list_lock);
- spidev = filp->private_data;
- filp->private_data = NULL;
- /* last close? */
- spidev->users--;
- if (!spidev->users) {
- int dofree;
- kfree(spidev->tx_buffer);
- spidev->tx_buffer = NULL;
- kfree(spidev->rx_buffer);
- spidev->rx_buffer = NULL;
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi)
- spidev->speed_hz = spidev->spi->max_speed_hz;
- /* ... after we unbound from the underlying device? */
- dofree = (spidev->spi == NULL);
- spin_unlock_irq(&spidev->spi_lock);
- if (dofree)
- kfree(spidev);
- }
- #ifdef CONFIG_SPI_SLAVE
- spi_slave_abort(spidev->spi);
- #endif
- mutex_unlock(&device_list_lock);
- return 0;
- }
- static const struct file_operations spidev_fops = {
- .owner = THIS_MODULE,
- /* REVISIT switch to aio primitives, so that userspace
- * gets more complete API coverage. It'll simplify things
- * too, except for the locking.
- */
- .write = spidev_write,
- .read = spidev_read,
- .unlocked_ioctl = spidev_ioctl,
- .compat_ioctl = spidev_compat_ioctl,
- .open = spidev_open,
- .release = spidev_release,
- .llseek = no_llseek,
- };
- /*-------------------------------------------------------------------------*/
- /* The main reason to have this class is to make mdev/udev create the
- * /dev/spidevB.C character device nodes exposing our userspace API.
- * It also simplifies memory management.
- */
- static struct class *spidev_class;
- #ifdef CONFIG_OF
- static const struct of_device_id spidev_dt_ids[] = {
- { .compatible = "rohm,dh2228fv" },
- { .compatible = "lineartechnology,ltc2488" },
- { .compatible = "ge,achc" },
- { .compatible = "semtech,sx1301" },
- {},
- };
- MODULE_DEVICE_TABLE(of, spidev_dt_ids);
- #endif
- #ifdef CONFIG_ACPI
- /* Dummy SPI devices not to be used in production systems */
- #define SPIDEV_ACPI_DUMMY 1
- static const struct acpi_device_id spidev_acpi_ids[] = {
- /*
- * The ACPI SPT000* devices are only meant for development and
- * testing. Systems used in production should have a proper ACPI
- * description of the connected peripheral and they should also use
- * a proper driver instead of poking directly to the SPI bus.
- */
- { "SPT0001", SPIDEV_ACPI_DUMMY },
- { "SPT0002", SPIDEV_ACPI_DUMMY },
- { "SPT0003", SPIDEV_ACPI_DUMMY },
- {},
- };
- MODULE_DEVICE_TABLE(acpi, spidev_acpi_ids);
- static void spidev_probe_acpi(struct spi_device *spi)
- {
- const struct acpi_device_id *id;
- if (!has_acpi_companion(&spi->dev))
- return;
- id = acpi_match_device(spidev_acpi_ids, &spi->dev);
- if (WARN_ON(!id))
- return;
- if (id->driver_data == SPIDEV_ACPI_DUMMY)
- dev_warn(&spi->dev, "do not use this driver in production systems!\n");
- }
- #else
- static inline void spidev_probe_acpi(struct spi_device *spi) {}
- #endif
- /*-------------------------------------------------------------------------*/
- static int spidev_probe(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.
- */
- WARN(spi->dev.of_node &&
- of_device_is_compatible(spi->dev.of_node, "spidev"),
- "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node);
- spidev_probe_acpi(spi);
- /* 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);
- /* 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);
- } 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);
- return status;
- }
- static int spidev_remove(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);
- return 0;
- }
- static struct spi_driver spidev_spi_driver = {
- .driver = {
- .name = "spidev",
- .of_match_table = of_match_ptr(spidev_dt_ids),
- .acpi_match_table = ACPI_PTR(spidev_acpi_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 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.
- */
- BUILD_BUG_ON(N_SPI_MINORS > 256);
- status = register_chrdev(SPIDEV_MAJOR, "spi", &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);
- 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);
- }
- module_exit(spidev_exit);
- MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
- MODULE_DESCRIPTION("User mode SPI device interface");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("spi:spidev");
|