Blame view

drivers/spi/spidev.c 21.5 KB
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
1
  /*
ca632f556   Grant Likely   spi: reorganize d...
2
   * Simple synchronous userspace interface to SPI devices
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   *
   * 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.
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
17
18
19
20
21
22
23
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/ioctl.h>
  #include <linux/fs.h>
  #include <linux/device.h>
b2c8daddc   David Brownell   spi: fix refcount...
24
  #include <linux/err.h>
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
25
26
27
28
  #include <linux/list.h>
  #include <linux/errno.h>
  #include <linux/mutex.h>
  #include <linux/slab.h>
7d48ec369   Bernhard Walle   spi/spidev: Add 3...
29
  #include <linux/compat.h>
880cfd43e   Maxime Ripard   spi: spidev: Add ...
30
31
  #include <linux/of.h>
  #include <linux/of_device.h>
cf9f4327a   Mika Westerberg   spi: spidev: Add ...
32
  #include <linux/acpi.h>
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
33
34
35
  
  #include <linux/spi/spi.h>
  #include <linux/spi/spidev.h>
95c63cfba   Jingoo Han   spi: spidev: Fix ...
36
  #include <linux/uaccess.h>
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
37
38
39
  
  
  /*
b595076a1   Uwe Kleine-König   tree-wide: fix co...
40
   * This supports access to SPI devices using normal userspace I/O calls.
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
41
42
   * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
   * and often mask message boundaries, full SPI support requires full duplex
137f1188e   Thadeu Lima de Souza Cascardo   spidev: fix doubl...
43
   * transfers.  There are several kinds of internal message boundaries to
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
44
45
46
47
48
49
50
51
52
53
   * 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 */
8ae1c9248   Thadeu Lima de Souza Cascardo   spidev: use DECLA...
54
  static DECLARE_BITMAP(minors, N_SPI_MINORS);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
55

6f166e383   Anton Vorontsov   spidev supports m...
56
  /* Bit masks for spi_device.mode management.  Note that incorrect
b55f627fe   David Brownell   spi: new spi->mod...
57
58
   * settings for some settings can cause *lots* of trouble for other
   * devices on a shared bus:
6f166e383   Anton Vorontsov   spidev supports m...
59
   *
b55f627fe   David Brownell   spi: new spi->mod...
60
61
62
63
64
65
66
   *  - 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?
6f166e383   Anton Vorontsov   spidev supports m...
67
68
   */
  #define SPI_MODE_MASK		(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
b55f627fe   David Brownell   spi: new spi->mod...
69
  				| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
70
71
  				| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
  				| SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
72
73
  
  struct spidev_data {
b2c8daddc   David Brownell   spi: fix refcount...
74
  	dev_t			devt;
25d5cb4b0   David Brownell   spi: remove some ...
75
  	spinlock_t		spi_lock;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
76
77
  	struct spi_device	*spi;
  	struct list_head	device_entry;
865f6d197   Ray Jui   spi: spidev: Use ...
78
  	/* TX/RX buffers are NULL unless this device is open (users > 0) */
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
79
80
  	struct mutex		buf_lock;
  	unsigned		users;
865f6d197   Ray Jui   spi: spidev: Use ...
81
82
  	u8			*tx_buffer;
  	u8			*rx_buffer;
916905161   Mark Brown   spi: spidev: Don'...
83
  	u32			speed_hz;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
84
85
86
87
88
89
90
91
92
93
  };
  
  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");
  
  /*-------------------------------------------------------------------------*/
25d5cb4b0   David Brownell   spi: remove some ...
94
95
96
  static ssize_t
  spidev_sync(struct spidev_data *spidev, struct spi_message *message)
  {
25d5cb4b0   David Brownell   spi: remove some ...
97
  	int status;
98d6f4795   Martin Sperl   spi: spidev: use ...
98
  	struct spi_device *spi;
25d5cb4b0   David Brownell   spi: remove some ...
99
100
  
  	spin_lock_irq(&spidev->spi_lock);
98d6f4795   Martin Sperl   spi: spidev: use ...
101
102
103
104
  	spi = spidev->spi;
  	spin_unlock_irq(&spidev->spi_lock);
  
  	if (spi == NULL)
25d5cb4b0   David Brownell   spi: remove some ...
105
106
  		status = -ESHUTDOWN;
  	else
98d6f4795   Martin Sperl   spi: spidev: use ...
107
108
109
110
  		status = spi_sync(spi, message);
  
  	if (status == 0)
  		status = message->actual_length;
25d5cb4b0   David Brownell   spi: remove some ...
111

25d5cb4b0   David Brownell   spi: remove some ...
112
113
114
115
116
117
118
  	return status;
  }
  
  static inline ssize_t
  spidev_sync_write(struct spidev_data *spidev, size_t len)
  {
  	struct spi_transfer	t = {
865f6d197   Ray Jui   spi: spidev: Use ...
119
  			.tx_buf		= spidev->tx_buffer,
25d5cb4b0   David Brownell   spi: remove some ...
120
  			.len		= len,
916905161   Mark Brown   spi: spidev: Don'...
121
  			.speed_hz	= spidev->speed_hz,
25d5cb4b0   David Brownell   spi: remove some ...
122
123
124
125
126
127
128
129
130
131
132
133
  		};
  	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 = {
865f6d197   Ray Jui   spi: spidev: Use ...
134
  			.rx_buf		= spidev->rx_buffer,
25d5cb4b0   David Brownell   spi: remove some ...
135
  			.len		= len,
916905161   Mark Brown   spi: spidev: Don'...
136
  			.speed_hz	= spidev->speed_hz,
25d5cb4b0   David Brownell   spi: remove some ...
137
138
139
140
141
142
143
144
145
  		};
  	struct spi_message	m;
  
  	spi_message_init(&m);
  	spi_message_add_tail(&t, &m);
  	return spidev_sync(spidev, &m);
  }
  
  /*-------------------------------------------------------------------------*/
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
146
147
148
149
150
  /* 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;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
151
152
153
154
155
156
157
  	ssize_t			status = 0;
  
  	/* chipselect only toggles at start or end of operation */
  	if (count > bufsiz)
  		return -EMSGSIZE;
  
  	spidev = filp->private_data;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
158
159
  
  	mutex_lock(&spidev->buf_lock);
25d5cb4b0   David Brownell   spi: remove some ...
160
  	status = spidev_sync_read(spidev, count);
4b1295b0d   Sebastian Siewior   spi: fix the read...
161
  	if (status > 0) {
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
162
  		unsigned long	missing;
865f6d197   Ray Jui   spi: spidev: Use ...
163
  		missing = copy_to_user(buf, spidev->rx_buffer, status);
4b1295b0d   Sebastian Siewior   spi: fix the read...
164
  		if (missing == status)
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
165
166
  			status = -EFAULT;
  		else
4b1295b0d   Sebastian Siewior   spi: fix the read...
167
  			status = status - missing;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
168
169
170
171
172
173
174
175
176
177
178
179
  	}
  	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;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
180
181
182
183
184
185
186
187
  	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;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
188
189
  
  	mutex_lock(&spidev->buf_lock);
865f6d197   Ray Jui   spi: spidev: Use ...
190
  	missing = copy_from_user(spidev->tx_buffer, buf, count);
95c63cfba   Jingoo Han   spi: spidev: Fix ...
191
  	if (missing == 0)
25d5cb4b0   David Brownell   spi: remove some ...
192
  		status = spidev_sync_write(spidev, count);
95c63cfba   Jingoo Han   spi: spidev: Fix ...
193
  	else
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
194
195
196
197
198
199
200
201
202
203
204
205
206
  		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;
9a12bff7c   Ian Abbott   spi: spidev: only...
207
  	unsigned		n, total, tx_total, rx_total;
865f6d197   Ray Jui   spi: spidev: Use ...
208
  	u8			*tx_buf, *rx_buf;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
209
210
211
212
213
214
215
216
217
218
219
  	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.
  	 */
865f6d197   Ray Jui   spi: spidev: Use ...
220
221
  	tx_buf = spidev->tx_buffer;
  	rx_buf = spidev->rx_buffer;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
222
  	total = 0;
9a12bff7c   Ian Abbott   spi: spidev: only...
223
224
  	tx_total = 0;
  	rx_total = 0;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
225
226
227
228
  	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
  			n;
  			n--, k_tmp++, u_tmp++) {
  		k_tmp->len = u_tmp->len;
da90fa8ff   Domen Puncer   spi/spidev: check...
229
  		total += k_tmp->len;
9a12bff7c   Ian Abbott   spi: spidev: only...
230
231
  		/* Since the function returns the total length of transfers
  		 * on success, restrict the total to positive int values to
f20fbaad7   Ian Abbott   spi: spidev: fix ...
232
233
  		 * avoid the return value looking like an error.  Also check
  		 * each transfer length to avoid arithmetic overflow.
9a12bff7c   Ian Abbott   spi: spidev: only...
234
  		 */
f20fbaad7   Ian Abbott   spi: spidev: fix ...
235
  		if (total > INT_MAX || k_tmp->len > INT_MAX) {
da90fa8ff   Domen Puncer   spi/spidev: check...
236
237
238
  			status = -EMSGSIZE;
  			goto done;
  		}
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
239
  		if (u_tmp->rx_buf) {
9a12bff7c   Ian Abbott   spi: spidev: only...
240
241
242
243
244
245
  			/* this transfer needs space in RX bounce buffer */
  			rx_total += k_tmp->len;
  			if (rx_total > bufsiz) {
  				status = -EMSGSIZE;
  				goto done;
  			}
865f6d197   Ray Jui   spi: spidev: Use ...
246
  			k_tmp->rx_buf = rx_buf;
9a12bff7c   Ian Abbott   spi: spidev: only...
247
  			rx_buf += k_tmp->len;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
248
249
  		}
  		if (u_tmp->tx_buf) {
9a12bff7c   Ian Abbott   spi: spidev: only...
250
251
252
253
254
255
  			/* this transfer needs space in TX bounce buffer */
  			tx_total += k_tmp->len;
  			if (tx_total > bufsiz) {
  				status = -EMSGSIZE;
  				goto done;
  			}
865f6d197   Ray Jui   spi: spidev: Use ...
256
257
  			k_tmp->tx_buf = tx_buf;
  			if (copy_from_user(tx_buf, (const u8 __user *)
142956af5   Al Viro   fix abuses of ptr...
258
  						(uintptr_t) u_tmp->tx_buf,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
259
260
  					u_tmp->len))
  				goto done;
9a12bff7c   Ian Abbott   spi: spidev: only...
261
  			tx_buf += k_tmp->len;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
262
  		}
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
263
264
  
  		k_tmp->cs_change = !!u_tmp->cs_change;
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
265
266
  		k_tmp->tx_nbits = u_tmp->tx_nbits;
  		k_tmp->rx_nbits = u_tmp->rx_nbits;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
267
268
269
  		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;
916905161   Mark Brown   spi: spidev: Don'...
270
271
  		if (!k_tmp->speed_hz)
  			k_tmp->speed_hz = spidev->speed_hz;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
272
  #ifdef VERBOSE
41df70d9a   Florian Fainelli   spi: fix spidev c...
273
  		dev_dbg(&spidev->spi->dev,
f15c58410   Geert Uytterhoeven   spi: spidev: Use ...
274
275
  			"  xfer len %u %s%s%s%dbits %u usec %uHz
  ",
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
276
277
278
279
  			u_tmp->len,
  			u_tmp->rx_buf ? "rx " : "",
  			u_tmp->tx_buf ? "tx " : "",
  			u_tmp->cs_change ? "cs " : "",
41df70d9a   Florian Fainelli   spi: fix spidev c...
280
  			u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
281
  			u_tmp->delay_usecs,
41df70d9a   Florian Fainelli   spi: fix spidev c...
282
  			u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
283
284
285
  #endif
  		spi_message_add_tail(k_tmp, &msg);
  	}
25d5cb4b0   David Brownell   spi: remove some ...
286
  	status = spidev_sync(spidev, &msg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
287
288
289
290
  	if (status < 0)
  		goto done;
  
  	/* copy any rx data out of bounce buffer */
865f6d197   Ray Jui   spi: spidev: Use ...
291
  	rx_buf = spidev->rx_buffer;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
292
293
  	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
  		if (u_tmp->rx_buf) {
251d59515   Al Viro   spidev: quit mess...
294
  			if (copy_to_user((u8 __user *)
865f6d197   Ray Jui   spi: spidev: Use ...
295
  					(uintptr_t) u_tmp->rx_buf, rx_buf,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
296
297
298
299
  					u_tmp->len)) {
  				status = -EFAULT;
  				goto done;
  			}
9a12bff7c   Ian Abbott   spi: spidev: only...
300
  			rx_buf += u_tmp->len;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
301
  		}
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
302
303
304
305
  	}
  	status = total;
  
  done:
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
306
307
308
  	kfree(k_xfers);
  	return status;
  }
7782a1a94   Ian Abbott   spi: spidev: Conv...
309
310
311
312
  static struct spi_ioc_transfer *
  spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc,
  		unsigned *n_ioc)
  {
7782a1a94   Ian Abbott   spi: spidev: Conv...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  	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 */
f7929436a   Geliang Tang   spi: spidev: use ...
329
  	return memdup_user(u_ioc, tmp);
7782a1a94   Ian Abbott   spi: spidev: Conv...
330
  }
4ef754b7d   Alan Cox   spidev: BKL removal
331
332
  static long
  spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
333
  {
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
334
335
336
337
338
339
340
341
342
343
  	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;
25d5cb4b0   David Brownell   spi: remove some ...
344
345
346
  	/* guard against device removal before, or while,
  	 * we issue this ioctl.
  	 */
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
347
  	spidev = filp->private_data;
25d5cb4b0   David Brownell   spi: remove some ...
348
349
350
351
352
353
  	spin_lock_irq(&spidev->spi_lock);
  	spi = spi_dev_get(spidev->spi);
  	spin_unlock_irq(&spidev->spi_lock);
  
  	if (spi == NULL)
  		return -ESHUTDOWN;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
354

4ef754b7d   Alan Cox   spidev: BKL removal
355
356
357
358
359
360
361
  	/* 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);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
362
363
364
  	switch (cmd) {
  	/* read requests */
  	case SPI_IOC_RD_MODE:
251d59515   Al Viro   spidev: quit mess...
365
  		retval = put_user(spi->mode & SPI_MODE_MASK,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
366
367
  					(__u8 __user *)arg);
  		break;
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
368
  	case SPI_IOC_RD_MODE32:
251d59515   Al Viro   spidev: quit mess...
369
  		retval = put_user(spi->mode & SPI_MODE_MASK,
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
370
371
  					(__u32 __user *)arg);
  		break;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
372
  	case SPI_IOC_RD_LSB_FIRST:
251d59515   Al Viro   spidev: quit mess...
373
  		retval = put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
374
375
376
  					(__u8 __user *)arg);
  		break;
  	case SPI_IOC_RD_BITS_PER_WORD:
251d59515   Al Viro   spidev: quit mess...
377
  		retval = put_user(spi->bits_per_word, (__u8 __user *)arg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
378
379
  		break;
  	case SPI_IOC_RD_MAX_SPEED_HZ:
251d59515   Al Viro   spidev: quit mess...
380
  		retval = put_user(spidev->speed_hz, (__u32 __user *)arg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
381
382
383
384
  		break;
  
  	/* write requests */
  	case SPI_IOC_WR_MODE:
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
385
386
  	case SPI_IOC_WR_MODE32:
  		if (cmd == SPI_IOC_WR_MODE)
251d59515   Al Viro   spidev: quit mess...
387
  			retval = get_user(tmp, (u8 __user *)arg);
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
388
  		else
251d59515   Al Viro   spidev: quit mess...
389
  			retval = get_user(tmp, (u32 __user *)arg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
390
  		if (retval == 0) {
e6456186c   Geert Uytterhoeven   spi: spidev: Rest...
391
  			u32	save = spi->mode;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
392
393
394
395
396
397
398
  
  			if (tmp & ~SPI_MODE_MASK) {
  				retval = -EINVAL;
  				break;
  			}
  
  			tmp |= spi->mode & ~SPI_MODE_MASK;
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
399
  			spi->mode = (u16)tmp;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
400
401
402
403
  			retval = spi_setup(spi);
  			if (retval < 0)
  				spi->mode = save;
  			else
dc64d39b5   Geert Uytterhoeven   spi: spidev: Add ...
404
405
  				dev_dbg(&spi->dev, "spi mode %x
  ", tmp);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
406
407
408
  		}
  		break;
  	case SPI_IOC_WR_LSB_FIRST:
251d59515   Al Viro   spidev: quit mess...
409
  		retval = get_user(tmp, (__u8 __user *)arg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
410
  		if (retval == 0) {
e6456186c   Geert Uytterhoeven   spi: spidev: Rest...
411
  			u32	save = spi->mode;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  
  			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
  ",
  						tmp ? 'l' : 'm');
  		}
  		break;
  	case SPI_IOC_WR_BITS_PER_WORD:
251d59515   Al Viro   spidev: quit mess...
427
  		retval = get_user(tmp, (__u8 __user *)arg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
428
429
430
431
432
433
434
435
436
437
438
439
440
  		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
  ", tmp);
  		}
  		break;
  	case SPI_IOC_WR_MAX_SPEED_HZ:
251d59515   Al Viro   spidev: quit mess...
441
  		retval = get_user(tmp, (__u32 __user *)arg);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
442
443
444
445
446
  		if (retval == 0) {
  			u32	save = spi->max_speed_hz;
  
  			spi->max_speed_hz = tmp;
  			retval = spi_setup(spi);
916905161   Mark Brown   spi: spidev: Don'...
447
448
  			if (retval >= 0)
  				spidev->speed_hz = tmp;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
449
450
451
  			else
  				dev_dbg(&spi->dev, "%d Hz (max)
  ", tmp);
916905161   Mark Brown   spi: spidev: Don'...
452
  			spi->max_speed_hz = save;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
453
454
455
456
457
  		}
  		break;
  
  	default:
  		/* segmented and/or full-duplex I/O request */
7782a1a94   Ian Abbott   spi: spidev: Conv...
458
459
460
461
462
  		/* 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);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
463
464
  			break;
  		}
7782a1a94   Ian Abbott   spi: spidev: Conv...
465
466
  		if (!ioc)
  			break;	/* n_ioc is also 0 */
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
467
468
469
470
471
472
  
  		/* translate to spi_message, execute */
  		retval = spidev_message(spidev, ioc, n_ioc);
  		kfree(ioc);
  		break;
  	}
4ef754b7d   Alan Cox   spidev: BKL removal
473
474
  
  	mutex_unlock(&spidev->buf_lock);
25d5cb4b0   David Brownell   spi: remove some ...
475
  	spi_dev_put(spi);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
476
477
  	return retval;
  }
7d48ec369   Bernhard Walle   spi/spidev: Add 3...
478
479
  #ifdef CONFIG_COMPAT
  static long
7782a1a94   Ian Abbott   spi: spidev: Conv...
480
481
482
483
484
485
486
487
488
489
490
  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);
7782a1a94   Ian Abbott   spi: spidev: Conv...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  
  	/* 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
7d48ec369   Bernhard Walle   spi/spidev: Add 3...
532
533
  spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  {
7782a1a94   Ian Abbott   spi: spidev: Conv...
534
535
536
537
  	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);
7d48ec369   Bernhard Walle   spi/spidev: Add 3...
538
539
540
541
542
  	return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
  }
  #else
  #define spidev_compat_ioctl NULL
  #endif /* CONFIG_COMPAT */
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
543
544
545
546
547
548
549
550
  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) {
b2c8daddc   David Brownell   spi: fix refcount...
551
  		if (spidev->devt == inode->i_rdev) {
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
552
553
554
555
  			status = 0;
  			break;
  		}
  	}
865f6d197   Ray Jui   spi: spidev: Use ...
556
557
558
559
560
561
562
563
564
565
  
  	if (status) {
  		pr_debug("spidev: nothing for minor %d
  ", iminor(inode));
  		goto err_find_dev;
  	}
  
  	if (!spidev->tx_buffer) {
  		spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
  		if (!spidev->tx_buffer) {
89a635667   Colin Ian King   spi: spidev: fix ...
566
567
568
  			dev_dbg(&spidev->spi->dev, "open/ENOMEM
  ");
  			status = -ENOMEM;
865f6d197   Ray Jui   spi: spidev: Use ...
569
  			goto err_find_dev;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
570
  		}
89a635667   Colin Ian King   spi: spidev: fix ...
571
  	}
865f6d197   Ray Jui   spi: spidev: Use ...
572
573
574
575
576
577
578
579
  
  	if (!spidev->rx_buffer) {
  		spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
  		if (!spidev->rx_buffer) {
  			dev_dbg(&spidev->spi->dev, "open/ENOMEM
  ");
  			status = -ENOMEM;
  			goto err_alloc_rx_buf;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
580
  		}
865f6d197   Ray Jui   spi: spidev: Use ...
581
582
583
584
585
  	}
  
  	spidev->users++;
  	filp->private_data = spidev;
  	nonseekable_open(inode, filp);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
586
587
  
  	mutex_unlock(&device_list_lock);
865f6d197   Ray Jui   spi: spidev: Use ...
588
589
590
591
592
593
594
  	return 0;
  
  err_alloc_rx_buf:
  	kfree(spidev->tx_buffer);
  	spidev->tx_buffer = NULL;
  err_find_dev:
  	mutex_unlock(&device_list_lock);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
595
596
597
598
599
600
  	return status;
  }
  
  static int spidev_release(struct inode *inode, struct file *filp)
  {
  	struct spidev_data	*spidev;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
601
602
603
604
  
  	mutex_lock(&device_list_lock);
  	spidev = filp->private_data;
  	filp->private_data = NULL;
b2c8daddc   David Brownell   spi: fix refcount...
605
606
  
  	/* last close? */
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
607
608
  	spidev->users--;
  	if (!spidev->users) {
b2c8daddc   David Brownell   spi: fix refcount...
609
  		int		dofree;
865f6d197   Ray Jui   spi: spidev: Use ...
610
611
612
613
614
  		kfree(spidev->tx_buffer);
  		spidev->tx_buffer = NULL;
  
  		kfree(spidev->rx_buffer);
  		spidev->rx_buffer = NULL;
b2c8daddc   David Brownell   spi: fix refcount...
615

56ea1075e   Mark Brown   spi: spidev: Hold...
616
  		spin_lock_irq(&spidev->spi_lock);
dd85ebf68   Sudip Mukherjee   spi: spidev: fix ...
617
618
  		if (spidev->spi)
  			spidev->speed_hz = spidev->spi->max_speed_hz;
916905161   Mark Brown   spi: spidev: Don'...
619

b2c8daddc   David Brownell   spi: fix refcount...
620
  		/* ... after we unbound from the underlying device? */
b2c8daddc   David Brownell   spi: fix refcount...
621
622
623
624
625
  		dofree = (spidev->spi == NULL);
  		spin_unlock_irq(&spidev->spi_lock);
  
  		if (dofree)
  			kfree(spidev);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
626
627
  	}
  	mutex_unlock(&device_list_lock);
99472cc08   Fabio Estevam   spi: spidev: Remo...
628
  	return 0;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
629
  }
828c09509   Alexey Dobriyan   const: constify r...
630
  static const struct file_operations spidev_fops = {
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
631
632
633
634
635
636
637
  	.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,
4ef754b7d   Alan Cox   spidev: BKL removal
638
  	.unlocked_ioctl = spidev_ioctl,
7d48ec369   Bernhard Walle   spi/spidev: Add 3...
639
  	.compat_ioctl = spidev_compat_ioctl,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
640
641
  	.open =		spidev_open,
  	.release =	spidev_release,
6038f373a   Arnd Bergmann   llseek: automatic...
642
  	.llseek =	no_llseek,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
643
644
645
646
647
648
649
650
  };
  
  /*-------------------------------------------------------------------------*/
  
  /* 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.
   */
b2c8daddc   David Brownell   spi: fix refcount...
651
  static struct class *spidev_class;
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
652

956b200a8   Mark Brown   spi: spidev: Warn...
653
654
655
  #ifdef CONFIG_OF
  static const struct of_device_id spidev_dt_ids[] = {
  	{ .compatible = "rohm,dh2228fv" },
6fec919b6   Jiri Prchal   spi: spidev: add ...
656
  	{ .compatible = "lineartechnology,ltc2488" },
144235ea7   Fabien Lahoudere   spi: spidev: Add ...
657
  	{ .compatible = "ge,achc" },
91b463463   Ben Whitten   spi: spidev: Add ...
658
  	{ .compatible = "semtech,sx1301" },
956b200a8   Mark Brown   spi: spidev: Warn...
659
660
661
662
  	{},
  };
  MODULE_DEVICE_TABLE(of, spidev_dt_ids);
  #endif
cf9f4327a   Mika Westerberg   spi: spidev: Add ...
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
  #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!
  ");
  }
  #else
  static inline void spidev_probe_acpi(struct spi_device *spi) {}
  #endif
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
700
  /*-------------------------------------------------------------------------*/
fd4a319bc   Grant Likely   spi: Remove HOTPL...
701
  static int spidev_probe(struct spi_device *spi)
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
702
703
704
705
  {
  	struct spidev_data	*spidev;
  	int			status;
  	unsigned long		minor;
956b200a8   Mark Brown   spi: spidev: Warn...
706
707
  	/*
  	 * spidev should never be referenced in DT without a specific
ffe228882   Fabio Estevam   spi: spidev: Fix ...
708
  	 * compatible string, it is a Linux implementation thing
956b200a8   Mark Brown   spi: spidev: Warn...
709
710
711
712
713
714
715
716
  	 * 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
  ");
  		WARN_ON(spi->dev.of_node &&
  			!of_match_device(spidev_dt_ids, &spi->dev));
  	}
cf9f4327a   Mika Westerberg   spi: spidev: Add ...
717
  	spidev_probe_acpi(spi);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
718
719
720
721
722
723
724
  	/* Allocate driver data */
  	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
  	if (!spidev)
  		return -ENOMEM;
  
  	/* Initialize the driver data */
  	spidev->spi = spi;
25d5cb4b0   David Brownell   spi: remove some ...
725
  	spin_lock_init(&spidev->spi_lock);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
726
727
728
729
730
731
732
733
  	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);
0a4dd7783   Domen Puncer   spi: fix spidev f...
734
  	minor = find_first_zero_bit(minors, N_SPI_MINORS);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
735
  	if (minor < N_SPI_MINORS) {
b2c8daddc   David Brownell   spi: fix refcount...
736
737
738
  		struct device *dev;
  
  		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
a9b12619f   Greg Kroah-Hartman   device create: mi...
739
740
741
  		dev = device_create(spidev_class, &spi->dev, spidev->devt,
  				    spidev, "spidev%d.%d",
  				    spi->master->bus_num, spi->chip_select);
8c6ffba0e   Rusty Russell   PTR_RET is now PT...
742
  		status = PTR_ERR_OR_ZERO(dev);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
743
744
745
746
747
748
749
  	} else {
  		dev_dbg(&spi->dev, "no minor number available!
  ");
  		status = -ENODEV;
  	}
  	if (status == 0) {
  		set_bit(minor, minors);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
750
751
752
  		list_add(&spidev->device_entry, &device_list);
  	}
  	mutex_unlock(&device_list_lock);
916905161   Mark Brown   spi: spidev: Don'...
753
  	spidev->speed_hz = spi->max_speed_hz;
aaacf4bb5   Wolfgang Ocker   spi: avoid spidev...
754
755
756
  	if (status == 0)
  		spi_set_drvdata(spi, spidev);
  	else
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
757
758
759
760
  		kfree(spidev);
  
  	return status;
  }
fd4a319bc   Grant Likely   spi: Remove HOTPL...
761
  static int spidev_remove(struct spi_device *spi)
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
762
  {
b2c8daddc   David Brownell   spi: fix refcount...
763
  	struct spidev_data	*spidev = spi_get_drvdata(spi);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
764

25d5cb4b0   David Brownell   spi: remove some ...
765
766
767
768
  	/* make sure ops on existing fds can abort cleanly */
  	spin_lock_irq(&spidev->spi_lock);
  	spidev->spi = NULL;
  	spin_unlock_irq(&spidev->spi_lock);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
769

25d5cb4b0   David Brownell   spi: remove some ...
770
771
  	/* prevent new opens */
  	mutex_lock(&device_list_lock);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
772
  	list_del(&spidev->device_entry);
b2c8daddc   David Brownell   spi: fix refcount...
773
774
775
776
  	device_destroy(spidev_class, spidev->devt);
  	clear_bit(MINOR(spidev->devt), minors);
  	if (spidev->users == 0)
  		kfree(spidev);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
777
778
779
780
  	mutex_unlock(&device_list_lock);
  
  	return 0;
  }
db389b614   Mike Frysinger   spidev: add prope...
781
  static struct spi_driver spidev_spi_driver = {
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
782
783
  	.driver = {
  		.name =		"spidev",
880cfd43e   Maxime Ripard   spi: spidev: Add ...
784
  		.of_match_table = of_match_ptr(spidev_dt_ids),
cf9f4327a   Mika Westerberg   spi: spidev: Add ...
785
  		.acpi_match_table = ACPI_PTR(spidev_acpi_ids),
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
786
787
  	},
  	.probe =	spidev_probe,
fd4a319bc   Grant Likely   spi: Remove HOTPL...
788
  	.remove =	spidev_remove,
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
  
  	/* 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;
b2c8daddc   David Brownell   spi: fix refcount...
811
812
  	spidev_class = class_create(THIS_MODULE, "spidev");
  	if (IS_ERR(spidev_class)) {
db389b614   Mike Frysinger   spidev: add prope...
813
  		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
b2c8daddc   David Brownell   spi: fix refcount...
814
  		return PTR_ERR(spidev_class);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
815
  	}
db389b614   Mike Frysinger   spidev: add prope...
816
  	status = spi_register_driver(&spidev_spi_driver);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
817
  	if (status < 0) {
b2c8daddc   David Brownell   spi: fix refcount...
818
  		class_destroy(spidev_class);
db389b614   Mike Frysinger   spidev: add prope...
819
  		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
820
821
822
823
824
825
826
  	}
  	return status;
  }
  module_init(spidev_init);
  
  static void __exit spidev_exit(void)
  {
db389b614   Mike Frysinger   spidev: add prope...
827
  	spi_unregister_driver(&spidev_spi_driver);
b2c8daddc   David Brownell   spi: fix refcount...
828
  	class_destroy(spidev_class);
db389b614   Mike Frysinger   spidev: add prope...
829
  	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
814a8d50e   Andrea Paterniani   /dev/spidevB.C in...
830
831
832
833
834
835
  }
  module_exit(spidev_exit);
  
  MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
  MODULE_DESCRIPTION("User mode SPI device interface");
  MODULE_LICENSE("GPL");
e0626e384   Anton Vorontsov   spi: prefix modal...
836
  MODULE_ALIAS("spi:spidev");