162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Simple synchronous userspace interface to SPI devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006 SWAPP
662306a36Sopenharmony_ci *	Andrea Paterniani <a.paterniani@swapp-eng.it>
762306a36Sopenharmony_ci * Copyright (C) 2007 David Brownell (simplification, cleanup)
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/ioctl.h>
1262306a36Sopenharmony_ci#include <linux/fs.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/mutex.h>
2062306a36Sopenharmony_ci#include <linux/property.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/compat.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/spi/spi.h>
2562306a36Sopenharmony_ci#include <linux/spi/spidev.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/uaccess.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * This supports access to SPI devices using normal userspace I/O calls.
3262306a36Sopenharmony_ci * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
3362306a36Sopenharmony_ci * and often mask message boundaries, full SPI support requires full duplex
3462306a36Sopenharmony_ci * transfers.  There are several kinds of internal message boundaries to
3562306a36Sopenharmony_ci * handle chipselect management and other protocol options.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * SPI has a character major number assigned.  We allocate minor numbers
3862306a36Sopenharmony_ci * dynamically using a bitmask.  You must use hotplug tools, such as udev
3962306a36Sopenharmony_ci * (or mdev with busybox) to create and destroy the /dev/spidevB.C device
4062306a36Sopenharmony_ci * nodes, since there is no fixed association of minor numbers with any
4162306a36Sopenharmony_ci * particular SPI bus or device.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci#define SPIDEV_MAJOR			153	/* assigned */
4462306a36Sopenharmony_ci#define N_SPI_MINORS			32	/* ... up to 256 */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic DECLARE_BITMAP(minors, N_SPI_MINORS);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Bit masks for spi_device.mode management.  Note that incorrect
5162306a36Sopenharmony_ci * settings for some settings can cause *lots* of trouble for other
5262306a36Sopenharmony_ci * devices on a shared bus:
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci *  - CS_HIGH ... this device will be active when it shouldn't be
5562306a36Sopenharmony_ci *  - 3WIRE ... when active, it won't behave as it should
5662306a36Sopenharmony_ci *  - NO_CS ... there will be no explicit message boundaries; this
5762306a36Sopenharmony_ci *	is completely incompatible with the shared bus model
5862306a36Sopenharmony_ci *  - READY ... transfers may proceed when they shouldn't.
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * REVISIT should changing those flags be privileged?
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci#define SPI_MODE_MASK		(SPI_MODE_X_MASK | SPI_CS_HIGH \
6362306a36Sopenharmony_ci				| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
6462306a36Sopenharmony_ci				| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
6562306a36Sopenharmony_ci				| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \
6662306a36Sopenharmony_ci				| SPI_RX_QUAD | SPI_RX_OCTAL \
6762306a36Sopenharmony_ci				| SPI_RX_CPHA_FLIP | SPI_3WIRE_HIZ \
6862306a36Sopenharmony_ci				| SPI_MOSI_IDLE_LOW)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistruct spidev_data {
7162306a36Sopenharmony_ci	dev_t			devt;
7262306a36Sopenharmony_ci	struct mutex		spi_lock;
7362306a36Sopenharmony_ci	struct spi_device	*spi;
7462306a36Sopenharmony_ci	struct list_head	device_entry;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* TX/RX buffers are NULL unless this device is open (users > 0) */
7762306a36Sopenharmony_ci	struct mutex		buf_lock;
7862306a36Sopenharmony_ci	unsigned		users;
7962306a36Sopenharmony_ci	u8			*tx_buffer;
8062306a36Sopenharmony_ci	u8			*rx_buffer;
8162306a36Sopenharmony_ci	u32			speed_hz;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic LIST_HEAD(device_list);
8562306a36Sopenharmony_cistatic DEFINE_MUTEX(device_list_lock);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic unsigned bufsiz = 4096;
8862306a36Sopenharmony_cimodule_param(bufsiz, uint, S_IRUGO);
8962306a36Sopenharmony_ciMODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic ssize_t
9462306a36Sopenharmony_cispidev_sync_unlocked(struct spi_device *spi, struct spi_message *message)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	ssize_t status;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	status = spi_sync(spi, message);
9962306a36Sopenharmony_ci	if (status == 0)
10062306a36Sopenharmony_ci		status = message->actual_length;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return status;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic ssize_t
10662306a36Sopenharmony_cispidev_sync(struct spidev_data *spidev, struct spi_message *message)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	ssize_t status;
10962306a36Sopenharmony_ci	struct spi_device *spi;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	mutex_lock(&spidev->spi_lock);
11262306a36Sopenharmony_ci	spi = spidev->spi;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (spi == NULL)
11562306a36Sopenharmony_ci		status = -ESHUTDOWN;
11662306a36Sopenharmony_ci	else
11762306a36Sopenharmony_ci		status = spidev_sync_unlocked(spi, message);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	mutex_unlock(&spidev->spi_lock);
12062306a36Sopenharmony_ci	return status;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline ssize_t
12462306a36Sopenharmony_cispidev_sync_write(struct spidev_data *spidev, size_t len)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct spi_transfer	t = {
12762306a36Sopenharmony_ci			.tx_buf		= spidev->tx_buffer,
12862306a36Sopenharmony_ci			.len		= len,
12962306a36Sopenharmony_ci			.speed_hz	= spidev->speed_hz,
13062306a36Sopenharmony_ci		};
13162306a36Sopenharmony_ci	struct spi_message	m;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	spi_message_init(&m);
13462306a36Sopenharmony_ci	spi_message_add_tail(&t, &m);
13562306a36Sopenharmony_ci	return spidev_sync(spidev, &m);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic inline ssize_t
13962306a36Sopenharmony_cispidev_sync_read(struct spidev_data *spidev, size_t len)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct spi_transfer	t = {
14262306a36Sopenharmony_ci			.rx_buf		= spidev->rx_buffer,
14362306a36Sopenharmony_ci			.len		= len,
14462306a36Sopenharmony_ci			.speed_hz	= spidev->speed_hz,
14562306a36Sopenharmony_ci		};
14662306a36Sopenharmony_ci	struct spi_message	m;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	spi_message_init(&m);
14962306a36Sopenharmony_ci	spi_message_add_tail(&t, &m);
15062306a36Sopenharmony_ci	return spidev_sync(spidev, &m);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* Read-only message with current device setup */
15662306a36Sopenharmony_cistatic ssize_t
15762306a36Sopenharmony_cispidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct spidev_data	*spidev;
16062306a36Sopenharmony_ci	ssize_t			status;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* chipselect only toggles at start or end of operation */
16362306a36Sopenharmony_ci	if (count > bufsiz)
16462306a36Sopenharmony_ci		return -EMSGSIZE;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	spidev = filp->private_data;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	mutex_lock(&spidev->buf_lock);
16962306a36Sopenharmony_ci	status = spidev_sync_read(spidev, count);
17062306a36Sopenharmony_ci	if (status > 0) {
17162306a36Sopenharmony_ci		unsigned long	missing;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		missing = copy_to_user(buf, spidev->rx_buffer, status);
17462306a36Sopenharmony_ci		if (missing == status)
17562306a36Sopenharmony_ci			status = -EFAULT;
17662306a36Sopenharmony_ci		else
17762306a36Sopenharmony_ci			status = status - missing;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	mutex_unlock(&spidev->buf_lock);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return status;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/* Write-only message with current device setup */
18562306a36Sopenharmony_cistatic ssize_t
18662306a36Sopenharmony_cispidev_write(struct file *filp, const char __user *buf,
18762306a36Sopenharmony_ci		size_t count, loff_t *f_pos)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct spidev_data	*spidev;
19062306a36Sopenharmony_ci	ssize_t			status;
19162306a36Sopenharmony_ci	unsigned long		missing;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* chipselect only toggles at start or end of operation */
19462306a36Sopenharmony_ci	if (count > bufsiz)
19562306a36Sopenharmony_ci		return -EMSGSIZE;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	spidev = filp->private_data;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	mutex_lock(&spidev->buf_lock);
20062306a36Sopenharmony_ci	missing = copy_from_user(spidev->tx_buffer, buf, count);
20162306a36Sopenharmony_ci	if (missing == 0)
20262306a36Sopenharmony_ci		status = spidev_sync_write(spidev, count);
20362306a36Sopenharmony_ci	else
20462306a36Sopenharmony_ci		status = -EFAULT;
20562306a36Sopenharmony_ci	mutex_unlock(&spidev->buf_lock);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return status;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int spidev_message(struct spidev_data *spidev,
21162306a36Sopenharmony_ci		struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct spi_message	msg;
21462306a36Sopenharmony_ci	struct spi_transfer	*k_xfers;
21562306a36Sopenharmony_ci	struct spi_transfer	*k_tmp;
21662306a36Sopenharmony_ci	struct spi_ioc_transfer *u_tmp;
21762306a36Sopenharmony_ci	unsigned		n, total, tx_total, rx_total;
21862306a36Sopenharmony_ci	u8			*tx_buf, *rx_buf;
21962306a36Sopenharmony_ci	int			status = -EFAULT;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	spi_message_init(&msg);
22262306a36Sopenharmony_ci	k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
22362306a36Sopenharmony_ci	if (k_xfers == NULL)
22462306a36Sopenharmony_ci		return -ENOMEM;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Construct spi_message, copying any tx data to bounce buffer.
22762306a36Sopenharmony_ci	 * We walk the array of user-provided transfers, using each one
22862306a36Sopenharmony_ci	 * to initialize a kernel version of the same transfer.
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci	tx_buf = spidev->tx_buffer;
23162306a36Sopenharmony_ci	rx_buf = spidev->rx_buffer;
23262306a36Sopenharmony_ci	total = 0;
23362306a36Sopenharmony_ci	tx_total = 0;
23462306a36Sopenharmony_ci	rx_total = 0;
23562306a36Sopenharmony_ci	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
23662306a36Sopenharmony_ci			n;
23762306a36Sopenharmony_ci			n--, k_tmp++, u_tmp++) {
23862306a36Sopenharmony_ci		/* Ensure that also following allocations from rx_buf/tx_buf will meet
23962306a36Sopenharmony_ci		 * DMA alignment requirements.
24062306a36Sopenharmony_ci		 */
24162306a36Sopenharmony_ci		unsigned int len_aligned = ALIGN(u_tmp->len, ARCH_DMA_MINALIGN);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		k_tmp->len = u_tmp->len;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		total += k_tmp->len;
24662306a36Sopenharmony_ci		/* Since the function returns the total length of transfers
24762306a36Sopenharmony_ci		 * on success, restrict the total to positive int values to
24862306a36Sopenharmony_ci		 * avoid the return value looking like an error.  Also check
24962306a36Sopenharmony_ci		 * each transfer length to avoid arithmetic overflow.
25062306a36Sopenharmony_ci		 */
25162306a36Sopenharmony_ci		if (total > INT_MAX || k_tmp->len > INT_MAX) {
25262306a36Sopenharmony_ci			status = -EMSGSIZE;
25362306a36Sopenharmony_ci			goto done;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		if (u_tmp->rx_buf) {
25762306a36Sopenharmony_ci			/* this transfer needs space in RX bounce buffer */
25862306a36Sopenharmony_ci			rx_total += len_aligned;
25962306a36Sopenharmony_ci			if (rx_total > bufsiz) {
26062306a36Sopenharmony_ci				status = -EMSGSIZE;
26162306a36Sopenharmony_ci				goto done;
26262306a36Sopenharmony_ci			}
26362306a36Sopenharmony_ci			k_tmp->rx_buf = rx_buf;
26462306a36Sopenharmony_ci			rx_buf += len_aligned;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		if (u_tmp->tx_buf) {
26762306a36Sopenharmony_ci			/* this transfer needs space in TX bounce buffer */
26862306a36Sopenharmony_ci			tx_total += len_aligned;
26962306a36Sopenharmony_ci			if (tx_total > bufsiz) {
27062306a36Sopenharmony_ci				status = -EMSGSIZE;
27162306a36Sopenharmony_ci				goto done;
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci			k_tmp->tx_buf = tx_buf;
27462306a36Sopenharmony_ci			if (copy_from_user(tx_buf, (const u8 __user *)
27562306a36Sopenharmony_ci						(uintptr_t) u_tmp->tx_buf,
27662306a36Sopenharmony_ci					u_tmp->len))
27762306a36Sopenharmony_ci				goto done;
27862306a36Sopenharmony_ci			tx_buf += len_aligned;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		k_tmp->cs_change = !!u_tmp->cs_change;
28262306a36Sopenharmony_ci		k_tmp->tx_nbits = u_tmp->tx_nbits;
28362306a36Sopenharmony_ci		k_tmp->rx_nbits = u_tmp->rx_nbits;
28462306a36Sopenharmony_ci		k_tmp->bits_per_word = u_tmp->bits_per_word;
28562306a36Sopenharmony_ci		k_tmp->delay.value = u_tmp->delay_usecs;
28662306a36Sopenharmony_ci		k_tmp->delay.unit = SPI_DELAY_UNIT_USECS;
28762306a36Sopenharmony_ci		k_tmp->speed_hz = u_tmp->speed_hz;
28862306a36Sopenharmony_ci		k_tmp->word_delay.value = u_tmp->word_delay_usecs;
28962306a36Sopenharmony_ci		k_tmp->word_delay.unit = SPI_DELAY_UNIT_USECS;
29062306a36Sopenharmony_ci		if (!k_tmp->speed_hz)
29162306a36Sopenharmony_ci			k_tmp->speed_hz = spidev->speed_hz;
29262306a36Sopenharmony_ci#ifdef VERBOSE
29362306a36Sopenharmony_ci		dev_dbg(&spidev->spi->dev,
29462306a36Sopenharmony_ci			"  xfer len %u %s%s%s%dbits %u usec %u usec %uHz\n",
29562306a36Sopenharmony_ci			k_tmp->len,
29662306a36Sopenharmony_ci			k_tmp->rx_buf ? "rx " : "",
29762306a36Sopenharmony_ci			k_tmp->tx_buf ? "tx " : "",
29862306a36Sopenharmony_ci			k_tmp->cs_change ? "cs " : "",
29962306a36Sopenharmony_ci			k_tmp->bits_per_word ? : spidev->spi->bits_per_word,
30062306a36Sopenharmony_ci			k_tmp->delay.value,
30162306a36Sopenharmony_ci			k_tmp->word_delay.value,
30262306a36Sopenharmony_ci			k_tmp->speed_hz ? : spidev->spi->max_speed_hz);
30362306a36Sopenharmony_ci#endif
30462306a36Sopenharmony_ci		spi_message_add_tail(k_tmp, &msg);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	status = spidev_sync_unlocked(spidev->spi, &msg);
30862306a36Sopenharmony_ci	if (status < 0)
30962306a36Sopenharmony_ci		goto done;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* copy any rx data out of bounce buffer */
31262306a36Sopenharmony_ci	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
31362306a36Sopenharmony_ci			n;
31462306a36Sopenharmony_ci			n--, k_tmp++, u_tmp++) {
31562306a36Sopenharmony_ci		if (u_tmp->rx_buf) {
31662306a36Sopenharmony_ci			if (copy_to_user((u8 __user *)
31762306a36Sopenharmony_ci					(uintptr_t) u_tmp->rx_buf, k_tmp->rx_buf,
31862306a36Sopenharmony_ci					u_tmp->len)) {
31962306a36Sopenharmony_ci				status = -EFAULT;
32062306a36Sopenharmony_ci				goto done;
32162306a36Sopenharmony_ci			}
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	status = total;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cidone:
32762306a36Sopenharmony_ci	kfree(k_xfers);
32862306a36Sopenharmony_ci	return status;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic struct spi_ioc_transfer *
33262306a36Sopenharmony_cispidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc,
33362306a36Sopenharmony_ci		unsigned *n_ioc)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	u32	tmp;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Check type, command number and direction */
33862306a36Sopenharmony_ci	if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC
33962306a36Sopenharmony_ci			|| _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
34062306a36Sopenharmony_ci			|| _IOC_DIR(cmd) != _IOC_WRITE)
34162306a36Sopenharmony_ci		return ERR_PTR(-ENOTTY);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	tmp = _IOC_SIZE(cmd);
34462306a36Sopenharmony_ci	if ((tmp % sizeof(struct spi_ioc_transfer)) != 0)
34562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
34662306a36Sopenharmony_ci	*n_ioc = tmp / sizeof(struct spi_ioc_transfer);
34762306a36Sopenharmony_ci	if (*n_ioc == 0)
34862306a36Sopenharmony_ci		return NULL;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* copy into scratch area */
35162306a36Sopenharmony_ci	return memdup_user(u_ioc, tmp);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic long
35562306a36Sopenharmony_cispidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	int			retval = 0;
35862306a36Sopenharmony_ci	struct spidev_data	*spidev;
35962306a36Sopenharmony_ci	struct spi_device	*spi;
36062306a36Sopenharmony_ci	u32			tmp;
36162306a36Sopenharmony_ci	unsigned		n_ioc;
36262306a36Sopenharmony_ci	struct spi_ioc_transfer	*ioc;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Check type and command number */
36562306a36Sopenharmony_ci	if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
36662306a36Sopenharmony_ci		return -ENOTTY;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* guard against device removal before, or while,
36962306a36Sopenharmony_ci	 * we issue this ioctl.
37062306a36Sopenharmony_ci	 */
37162306a36Sopenharmony_ci	spidev = filp->private_data;
37262306a36Sopenharmony_ci	mutex_lock(&spidev->spi_lock);
37362306a36Sopenharmony_ci	spi = spi_dev_get(spidev->spi);
37462306a36Sopenharmony_ci	if (spi == NULL) {
37562306a36Sopenharmony_ci		mutex_unlock(&spidev->spi_lock);
37662306a36Sopenharmony_ci		return -ESHUTDOWN;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* use the buffer lock here for triple duty:
38062306a36Sopenharmony_ci	 *  - prevent I/O (from us) so calling spi_setup() is safe;
38162306a36Sopenharmony_ci	 *  - prevent concurrent SPI_IOC_WR_* from morphing
38262306a36Sopenharmony_ci	 *    data fields while SPI_IOC_RD_* reads them;
38362306a36Sopenharmony_ci	 *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
38462306a36Sopenharmony_ci	 */
38562306a36Sopenharmony_ci	mutex_lock(&spidev->buf_lock);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	switch (cmd) {
38862306a36Sopenharmony_ci	/* read requests */
38962306a36Sopenharmony_ci	case SPI_IOC_RD_MODE:
39062306a36Sopenharmony_ci	case SPI_IOC_RD_MODE32:
39162306a36Sopenharmony_ci		tmp = spi->mode;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		{
39462306a36Sopenharmony_ci			struct spi_controller *ctlr = spi->controller;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci			if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
39762306a36Sopenharmony_ci			    ctlr->cs_gpiods[spi_get_chipselect(spi, 0)])
39862306a36Sopenharmony_ci				tmp &= ~SPI_CS_HIGH;
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		if (cmd == SPI_IOC_RD_MODE)
40262306a36Sopenharmony_ci			retval = put_user(tmp & SPI_MODE_MASK,
40362306a36Sopenharmony_ci					  (__u8 __user *)arg);
40462306a36Sopenharmony_ci		else
40562306a36Sopenharmony_ci			retval = put_user(tmp & SPI_MODE_MASK,
40662306a36Sopenharmony_ci					  (__u32 __user *)arg);
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case SPI_IOC_RD_LSB_FIRST:
40962306a36Sopenharmony_ci		retval = put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,
41062306a36Sopenharmony_ci					(__u8 __user *)arg);
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci	case SPI_IOC_RD_BITS_PER_WORD:
41362306a36Sopenharmony_ci		retval = put_user(spi->bits_per_word, (__u8 __user *)arg);
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci	case SPI_IOC_RD_MAX_SPEED_HZ:
41662306a36Sopenharmony_ci		retval = put_user(spidev->speed_hz, (__u32 __user *)arg);
41762306a36Sopenharmony_ci		break;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* write requests */
42062306a36Sopenharmony_ci	case SPI_IOC_WR_MODE:
42162306a36Sopenharmony_ci	case SPI_IOC_WR_MODE32:
42262306a36Sopenharmony_ci		if (cmd == SPI_IOC_WR_MODE)
42362306a36Sopenharmony_ci			retval = get_user(tmp, (u8 __user *)arg);
42462306a36Sopenharmony_ci		else
42562306a36Sopenharmony_ci			retval = get_user(tmp, (u32 __user *)arg);
42662306a36Sopenharmony_ci		if (retval == 0) {
42762306a36Sopenharmony_ci			struct spi_controller *ctlr = spi->controller;
42862306a36Sopenharmony_ci			u32	save = spi->mode;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci			if (tmp & ~SPI_MODE_MASK) {
43162306a36Sopenharmony_ci				retval = -EINVAL;
43262306a36Sopenharmony_ci				break;
43362306a36Sopenharmony_ci			}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci			if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
43662306a36Sopenharmony_ci			    ctlr->cs_gpiods[spi_get_chipselect(spi, 0)])
43762306a36Sopenharmony_ci				tmp |= SPI_CS_HIGH;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci			tmp |= spi->mode & ~SPI_MODE_MASK;
44062306a36Sopenharmony_ci			spi->mode = tmp & SPI_MODE_USER_MASK;
44162306a36Sopenharmony_ci			retval = spi_setup(spi);
44262306a36Sopenharmony_ci			if (retval < 0)
44362306a36Sopenharmony_ci				spi->mode = save;
44462306a36Sopenharmony_ci			else
44562306a36Sopenharmony_ci				dev_dbg(&spi->dev, "spi mode %x\n", tmp);
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	case SPI_IOC_WR_LSB_FIRST:
44962306a36Sopenharmony_ci		retval = get_user(tmp, (__u8 __user *)arg);
45062306a36Sopenharmony_ci		if (retval == 0) {
45162306a36Sopenharmony_ci			u32	save = spi->mode;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci			if (tmp)
45462306a36Sopenharmony_ci				spi->mode |= SPI_LSB_FIRST;
45562306a36Sopenharmony_ci			else
45662306a36Sopenharmony_ci				spi->mode &= ~SPI_LSB_FIRST;
45762306a36Sopenharmony_ci			retval = spi_setup(spi);
45862306a36Sopenharmony_ci			if (retval < 0)
45962306a36Sopenharmony_ci				spi->mode = save;
46062306a36Sopenharmony_ci			else
46162306a36Sopenharmony_ci				dev_dbg(&spi->dev, "%csb first\n",
46262306a36Sopenharmony_ci						tmp ? 'l' : 'm');
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci	case SPI_IOC_WR_BITS_PER_WORD:
46662306a36Sopenharmony_ci		retval = get_user(tmp, (__u8 __user *)arg);
46762306a36Sopenharmony_ci		if (retval == 0) {
46862306a36Sopenharmony_ci			u8	save = spi->bits_per_word;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci			spi->bits_per_word = tmp;
47162306a36Sopenharmony_ci			retval = spi_setup(spi);
47262306a36Sopenharmony_ci			if (retval < 0)
47362306a36Sopenharmony_ci				spi->bits_per_word = save;
47462306a36Sopenharmony_ci			else
47562306a36Sopenharmony_ci				dev_dbg(&spi->dev, "%d bits per word\n", tmp);
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	case SPI_IOC_WR_MAX_SPEED_HZ: {
47962306a36Sopenharmony_ci		u32 save;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		retval = get_user(tmp, (__u32 __user *)arg);
48262306a36Sopenharmony_ci		if (retval)
48362306a36Sopenharmony_ci			break;
48462306a36Sopenharmony_ci		if (tmp == 0) {
48562306a36Sopenharmony_ci			retval = -EINVAL;
48662306a36Sopenharmony_ci			break;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		save = spi->max_speed_hz;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		spi->max_speed_hz = tmp;
49262306a36Sopenharmony_ci		retval = spi_setup(spi);
49362306a36Sopenharmony_ci		if (retval == 0) {
49462306a36Sopenharmony_ci			spidev->speed_hz = tmp;
49562306a36Sopenharmony_ci			dev_dbg(&spi->dev, "%d Hz (max)\n", spidev->speed_hz);
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		spi->max_speed_hz = save;
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci	default:
50262306a36Sopenharmony_ci		/* segmented and/or full-duplex I/O request */
50362306a36Sopenharmony_ci		/* Check message and copy into scratch area */
50462306a36Sopenharmony_ci		ioc = spidev_get_ioc_message(cmd,
50562306a36Sopenharmony_ci				(struct spi_ioc_transfer __user *)arg, &n_ioc);
50662306a36Sopenharmony_ci		if (IS_ERR(ioc)) {
50762306a36Sopenharmony_ci			retval = PTR_ERR(ioc);
50862306a36Sopenharmony_ci			break;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci		if (!ioc)
51162306a36Sopenharmony_ci			break;	/* n_ioc is also 0 */
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		/* translate to spi_message, execute */
51462306a36Sopenharmony_ci		retval = spidev_message(spidev, ioc, n_ioc);
51562306a36Sopenharmony_ci		kfree(ioc);
51662306a36Sopenharmony_ci		break;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	mutex_unlock(&spidev->buf_lock);
52062306a36Sopenharmony_ci	spi_dev_put(spi);
52162306a36Sopenharmony_ci	mutex_unlock(&spidev->spi_lock);
52262306a36Sopenharmony_ci	return retval;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
52662306a36Sopenharmony_cistatic long
52762306a36Sopenharmony_cispidev_compat_ioc_message(struct file *filp, unsigned int cmd,
52862306a36Sopenharmony_ci		unsigned long arg)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct spi_ioc_transfer __user	*u_ioc;
53162306a36Sopenharmony_ci	int				retval = 0;
53262306a36Sopenharmony_ci	struct spidev_data		*spidev;
53362306a36Sopenharmony_ci	struct spi_device		*spi;
53462306a36Sopenharmony_ci	unsigned			n_ioc, n;
53562306a36Sopenharmony_ci	struct spi_ioc_transfer		*ioc;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* guard against device removal before, or while,
54062306a36Sopenharmony_ci	 * we issue this ioctl.
54162306a36Sopenharmony_ci	 */
54262306a36Sopenharmony_ci	spidev = filp->private_data;
54362306a36Sopenharmony_ci	mutex_lock(&spidev->spi_lock);
54462306a36Sopenharmony_ci	spi = spi_dev_get(spidev->spi);
54562306a36Sopenharmony_ci	if (spi == NULL) {
54662306a36Sopenharmony_ci		mutex_unlock(&spidev->spi_lock);
54762306a36Sopenharmony_ci		return -ESHUTDOWN;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
55162306a36Sopenharmony_ci	mutex_lock(&spidev->buf_lock);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* Check message and copy into scratch area */
55462306a36Sopenharmony_ci	ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc);
55562306a36Sopenharmony_ci	if (IS_ERR(ioc)) {
55662306a36Sopenharmony_ci		retval = PTR_ERR(ioc);
55762306a36Sopenharmony_ci		goto done;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci	if (!ioc)
56062306a36Sopenharmony_ci		goto done;	/* n_ioc is also 0 */
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Convert buffer pointers */
56362306a36Sopenharmony_ci	for (n = 0; n < n_ioc; n++) {
56462306a36Sopenharmony_ci		ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf);
56562306a36Sopenharmony_ci		ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* translate to spi_message, execute */
56962306a36Sopenharmony_ci	retval = spidev_message(spidev, ioc, n_ioc);
57062306a36Sopenharmony_ci	kfree(ioc);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cidone:
57362306a36Sopenharmony_ci	mutex_unlock(&spidev->buf_lock);
57462306a36Sopenharmony_ci	spi_dev_put(spi);
57562306a36Sopenharmony_ci	mutex_unlock(&spidev->spi_lock);
57662306a36Sopenharmony_ci	return retval;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic long
58062306a36Sopenharmony_cispidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC
58362306a36Sopenharmony_ci			&& _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0))
58462306a36Sopenharmony_ci			&& _IOC_DIR(cmd) == _IOC_WRITE)
58562306a36Sopenharmony_ci		return spidev_compat_ioc_message(filp, cmd, arg);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci#else
59062306a36Sopenharmony_ci#define spidev_compat_ioctl NULL
59162306a36Sopenharmony_ci#endif /* CONFIG_COMPAT */
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int spidev_open(struct inode *inode, struct file *filp)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct spidev_data	*spidev = NULL, *iter;
59662306a36Sopenharmony_ci	int			status = -ENXIO;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	mutex_lock(&device_list_lock);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	list_for_each_entry(iter, &device_list, device_entry) {
60162306a36Sopenharmony_ci		if (iter->devt == inode->i_rdev) {
60262306a36Sopenharmony_ci			status = 0;
60362306a36Sopenharmony_ci			spidev = iter;
60462306a36Sopenharmony_ci			break;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (!spidev) {
60962306a36Sopenharmony_ci		pr_debug("spidev: nothing for minor %d\n", iminor(inode));
61062306a36Sopenharmony_ci		goto err_find_dev;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (!spidev->tx_buffer) {
61462306a36Sopenharmony_ci		spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
61562306a36Sopenharmony_ci		if (!spidev->tx_buffer) {
61662306a36Sopenharmony_ci			status = -ENOMEM;
61762306a36Sopenharmony_ci			goto err_find_dev;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	if (!spidev->rx_buffer) {
62262306a36Sopenharmony_ci		spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
62362306a36Sopenharmony_ci		if (!spidev->rx_buffer) {
62462306a36Sopenharmony_ci			status = -ENOMEM;
62562306a36Sopenharmony_ci			goto err_alloc_rx_buf;
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	spidev->users++;
63062306a36Sopenharmony_ci	filp->private_data = spidev;
63162306a36Sopenharmony_ci	stream_open(inode, filp);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	mutex_unlock(&device_list_lock);
63462306a36Sopenharmony_ci	return 0;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cierr_alloc_rx_buf:
63762306a36Sopenharmony_ci	kfree(spidev->tx_buffer);
63862306a36Sopenharmony_ci	spidev->tx_buffer = NULL;
63962306a36Sopenharmony_cierr_find_dev:
64062306a36Sopenharmony_ci	mutex_unlock(&device_list_lock);
64162306a36Sopenharmony_ci	return status;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int spidev_release(struct inode *inode, struct file *filp)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct spidev_data	*spidev;
64762306a36Sopenharmony_ci	int			dofree;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	mutex_lock(&device_list_lock);
65062306a36Sopenharmony_ci	spidev = filp->private_data;
65162306a36Sopenharmony_ci	filp->private_data = NULL;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	mutex_lock(&spidev->spi_lock);
65462306a36Sopenharmony_ci	/* ... after we unbound from the underlying device? */
65562306a36Sopenharmony_ci	dofree = (spidev->spi == NULL);
65662306a36Sopenharmony_ci	mutex_unlock(&spidev->spi_lock);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* last close? */
65962306a36Sopenharmony_ci	spidev->users--;
66062306a36Sopenharmony_ci	if (!spidev->users) {
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		kfree(spidev->tx_buffer);
66362306a36Sopenharmony_ci		spidev->tx_buffer = NULL;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		kfree(spidev->rx_buffer);
66662306a36Sopenharmony_ci		spidev->rx_buffer = NULL;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		if (dofree)
66962306a36Sopenharmony_ci			kfree(spidev);
67062306a36Sopenharmony_ci		else
67162306a36Sopenharmony_ci			spidev->speed_hz = spidev->spi->max_speed_hz;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci#ifdef CONFIG_SPI_SLAVE
67462306a36Sopenharmony_ci	if (!dofree)
67562306a36Sopenharmony_ci		spi_slave_abort(spidev->spi);
67662306a36Sopenharmony_ci#endif
67762306a36Sopenharmony_ci	mutex_unlock(&device_list_lock);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic const struct file_operations spidev_fops = {
68362306a36Sopenharmony_ci	.owner =	THIS_MODULE,
68462306a36Sopenharmony_ci	/* REVISIT switch to aio primitives, so that userspace
68562306a36Sopenharmony_ci	 * gets more complete API coverage.  It'll simplify things
68662306a36Sopenharmony_ci	 * too, except for the locking.
68762306a36Sopenharmony_ci	 */
68862306a36Sopenharmony_ci	.write =	spidev_write,
68962306a36Sopenharmony_ci	.read =		spidev_read,
69062306a36Sopenharmony_ci	.unlocked_ioctl = spidev_ioctl,
69162306a36Sopenharmony_ci	.compat_ioctl = spidev_compat_ioctl,
69262306a36Sopenharmony_ci	.open =		spidev_open,
69362306a36Sopenharmony_ci	.release =	spidev_release,
69462306a36Sopenharmony_ci	.llseek =	no_llseek,
69562306a36Sopenharmony_ci};
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/* The main reason to have this class is to make mdev/udev create the
70062306a36Sopenharmony_ci * /dev/spidevB.C character device nodes exposing our userspace API.
70162306a36Sopenharmony_ci * It also simplifies memory management.
70262306a36Sopenharmony_ci */
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic struct class *spidev_class;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic const struct spi_device_id spidev_spi_ids[] = {
70762306a36Sopenharmony_ci	{ .name = "dh2228fv" },
70862306a36Sopenharmony_ci	{ .name = "ltc2488" },
70962306a36Sopenharmony_ci	{ .name = "sx1301" },
71062306a36Sopenharmony_ci	{ .name = "bk4" },
71162306a36Sopenharmony_ci	{ .name = "dhcom-board" },
71262306a36Sopenharmony_ci	{ .name = "m53cpld" },
71362306a36Sopenharmony_ci	{ .name = "spi-petra" },
71462306a36Sopenharmony_ci	{ .name = "spi-authenta" },
71562306a36Sopenharmony_ci	{ .name = "em3581" },
71662306a36Sopenharmony_ci	{ .name = "si3210" },
71762306a36Sopenharmony_ci	{},
71862306a36Sopenharmony_ci};
71962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, spidev_spi_ids);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci/*
72262306a36Sopenharmony_ci * spidev should never be referenced in DT without a specific compatible string,
72362306a36Sopenharmony_ci * it is a Linux implementation thing rather than a description of the hardware.
72462306a36Sopenharmony_ci */
72562306a36Sopenharmony_cistatic int spidev_of_check(struct device *dev)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	if (device_property_match_string(dev, "compatible", "spidev") < 0)
72862306a36Sopenharmony_ci		return 0;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	dev_err(dev, "spidev listed directly in DT is not supported\n");
73162306a36Sopenharmony_ci	return -EINVAL;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic const struct of_device_id spidev_dt_ids[] = {
73562306a36Sopenharmony_ci	{ .compatible = "cisco,spi-petra", .data = &spidev_of_check },
73662306a36Sopenharmony_ci	{ .compatible = "dh,dhcom-board", .data = &spidev_of_check },
73762306a36Sopenharmony_ci	{ .compatible = "lineartechnology,ltc2488", .data = &spidev_of_check },
73862306a36Sopenharmony_ci	{ .compatible = "lwn,bk4", .data = &spidev_of_check },
73962306a36Sopenharmony_ci	{ .compatible = "menlo,m53cpld", .data = &spidev_of_check },
74062306a36Sopenharmony_ci	{ .compatible = "micron,spi-authenta", .data = &spidev_of_check },
74162306a36Sopenharmony_ci	{ .compatible = "rohm,dh2228fv", .data = &spidev_of_check },
74262306a36Sopenharmony_ci	{ .compatible = "semtech,sx1301", .data = &spidev_of_check },
74362306a36Sopenharmony_ci	{ .compatible = "silabs,em3581", .data = &spidev_of_check },
74462306a36Sopenharmony_ci	{ .compatible = "silabs,si3210", .data = &spidev_of_check },
74562306a36Sopenharmony_ci	{},
74662306a36Sopenharmony_ci};
74762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spidev_dt_ids);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/* Dummy SPI devices not to be used in production systems */
75062306a36Sopenharmony_cistatic int spidev_acpi_check(struct device *dev)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	dev_warn(dev, "do not use this driver in production systems!\n");
75362306a36Sopenharmony_ci	return 0;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic const struct acpi_device_id spidev_acpi_ids[] = {
75762306a36Sopenharmony_ci	/*
75862306a36Sopenharmony_ci	 * The ACPI SPT000* devices are only meant for development and
75962306a36Sopenharmony_ci	 * testing. Systems used in production should have a proper ACPI
76062306a36Sopenharmony_ci	 * description of the connected peripheral and they should also use
76162306a36Sopenharmony_ci	 * a proper driver instead of poking directly to the SPI bus.
76262306a36Sopenharmony_ci	 */
76362306a36Sopenharmony_ci	{ "SPT0001", (kernel_ulong_t)&spidev_acpi_check },
76462306a36Sopenharmony_ci	{ "SPT0002", (kernel_ulong_t)&spidev_acpi_check },
76562306a36Sopenharmony_ci	{ "SPT0003", (kernel_ulong_t)&spidev_acpi_check },
76662306a36Sopenharmony_ci	{},
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, spidev_acpi_ids);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic int spidev_probe(struct spi_device *spi)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	int (*match)(struct device *dev);
77562306a36Sopenharmony_ci	struct spidev_data	*spidev;
77662306a36Sopenharmony_ci	int			status;
77762306a36Sopenharmony_ci	unsigned long		minor;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	match = device_get_match_data(&spi->dev);
78062306a36Sopenharmony_ci	if (match) {
78162306a36Sopenharmony_ci		status = match(&spi->dev);
78262306a36Sopenharmony_ci		if (status)
78362306a36Sopenharmony_ci			return status;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* Allocate driver data */
78762306a36Sopenharmony_ci	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
78862306a36Sopenharmony_ci	if (!spidev)
78962306a36Sopenharmony_ci		return -ENOMEM;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* Initialize the driver data */
79262306a36Sopenharmony_ci	spidev->spi = spi;
79362306a36Sopenharmony_ci	mutex_init(&spidev->spi_lock);
79462306a36Sopenharmony_ci	mutex_init(&spidev->buf_lock);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	INIT_LIST_HEAD(&spidev->device_entry);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/* If we can allocate a minor number, hook up this device.
79962306a36Sopenharmony_ci	 * Reusing minors is fine so long as udev or mdev is working.
80062306a36Sopenharmony_ci	 */
80162306a36Sopenharmony_ci	mutex_lock(&device_list_lock);
80262306a36Sopenharmony_ci	minor = find_first_zero_bit(minors, N_SPI_MINORS);
80362306a36Sopenharmony_ci	if (minor < N_SPI_MINORS) {
80462306a36Sopenharmony_ci		struct device *dev;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
80762306a36Sopenharmony_ci		dev = device_create(spidev_class, &spi->dev, spidev->devt,
80862306a36Sopenharmony_ci				    spidev, "spidev%d.%d",
80962306a36Sopenharmony_ci				    spi->master->bus_num, spi_get_chipselect(spi, 0));
81062306a36Sopenharmony_ci		status = PTR_ERR_OR_ZERO(dev);
81162306a36Sopenharmony_ci	} else {
81262306a36Sopenharmony_ci		dev_dbg(&spi->dev, "no minor number available!\n");
81362306a36Sopenharmony_ci		status = -ENODEV;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci	if (status == 0) {
81662306a36Sopenharmony_ci		set_bit(minor, minors);
81762306a36Sopenharmony_ci		list_add(&spidev->device_entry, &device_list);
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci	mutex_unlock(&device_list_lock);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	spidev->speed_hz = spi->max_speed_hz;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (status == 0)
82462306a36Sopenharmony_ci		spi_set_drvdata(spi, spidev);
82562306a36Sopenharmony_ci	else
82662306a36Sopenharmony_ci		kfree(spidev);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	return status;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic void spidev_remove(struct spi_device *spi)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct spidev_data	*spidev = spi_get_drvdata(spi);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* prevent new opens */
83662306a36Sopenharmony_ci	mutex_lock(&device_list_lock);
83762306a36Sopenharmony_ci	/* make sure ops on existing fds can abort cleanly */
83862306a36Sopenharmony_ci	mutex_lock(&spidev->spi_lock);
83962306a36Sopenharmony_ci	spidev->spi = NULL;
84062306a36Sopenharmony_ci	mutex_unlock(&spidev->spi_lock);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	list_del(&spidev->device_entry);
84362306a36Sopenharmony_ci	device_destroy(spidev_class, spidev->devt);
84462306a36Sopenharmony_ci	clear_bit(MINOR(spidev->devt), minors);
84562306a36Sopenharmony_ci	if (spidev->users == 0)
84662306a36Sopenharmony_ci		kfree(spidev);
84762306a36Sopenharmony_ci	mutex_unlock(&device_list_lock);
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic struct spi_driver spidev_spi_driver = {
85162306a36Sopenharmony_ci	.driver = {
85262306a36Sopenharmony_ci		.name =		"spidev",
85362306a36Sopenharmony_ci		.of_match_table = spidev_dt_ids,
85462306a36Sopenharmony_ci		.acpi_match_table = spidev_acpi_ids,
85562306a36Sopenharmony_ci	},
85662306a36Sopenharmony_ci	.probe =	spidev_probe,
85762306a36Sopenharmony_ci	.remove =	spidev_remove,
85862306a36Sopenharmony_ci	.id_table =	spidev_spi_ids,
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* NOTE:  suspend/resume methods are not necessary here.
86162306a36Sopenharmony_ci	 * We don't do anything except pass the requests to/from
86262306a36Sopenharmony_ci	 * the underlying controller.  The refrigerator handles
86362306a36Sopenharmony_ci	 * most issues; the controller driver handles the rest.
86462306a36Sopenharmony_ci	 */
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int __init spidev_init(void)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	int status;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* Claim our 256 reserved device numbers.  Then register a class
87462306a36Sopenharmony_ci	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
87562306a36Sopenharmony_ci	 * the driver which manages those device numbers.
87662306a36Sopenharmony_ci	 */
87762306a36Sopenharmony_ci	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
87862306a36Sopenharmony_ci	if (status < 0)
87962306a36Sopenharmony_ci		return status;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	spidev_class = class_create("spidev");
88262306a36Sopenharmony_ci	if (IS_ERR(spidev_class)) {
88362306a36Sopenharmony_ci		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
88462306a36Sopenharmony_ci		return PTR_ERR(spidev_class);
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	status = spi_register_driver(&spidev_spi_driver);
88862306a36Sopenharmony_ci	if (status < 0) {
88962306a36Sopenharmony_ci		class_destroy(spidev_class);
89062306a36Sopenharmony_ci		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci	return status;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_cimodule_init(spidev_init);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic void __exit spidev_exit(void)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	spi_unregister_driver(&spidev_spi_driver);
89962306a36Sopenharmony_ci	class_destroy(spidev_class);
90062306a36Sopenharmony_ci	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_cimodule_exit(spidev_exit);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ciMODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
90562306a36Sopenharmony_ciMODULE_DESCRIPTION("User mode SPI device interface");
90662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
90762306a36Sopenharmony_ciMODULE_ALIAS("spi:spidev");
908