diff options
author | Ray Jui <rjui@broadcom.com> | 2014-10-09 14:19:25 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-10-13 07:07:12 -0400 |
commit | 865f6d1974ddd9eff7e10820c4f9f7c7179d6659 (patch) | |
tree | b5ecacca909aafae461e09cd6834daa015c30d4b | |
parent | a2285b8c75bf7e21895f7c2cf75d6a910914517b (diff) |
spi: spidev: Use separate TX and RX bounce buffers
By using separate TX and RX bounce buffers, we avoid potential cache
flush and invalidation sequence issue that may be encountered when a
single bounce buffer is shared between TX and RX
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | drivers/spi/spidev.c | 79 |
1 files changed, 52 insertions, 27 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index e3bc23bb5883..e50039fb1474 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c | |||
@@ -82,10 +82,11 @@ struct spidev_data { | |||
82 | struct spi_device *spi; | 82 | struct spi_device *spi; |
83 | struct list_head device_entry; | 83 | struct list_head device_entry; |
84 | 84 | ||
85 | /* buffer is NULL unless this device is open (users > 0) */ | 85 | /* TX/RX buffers are NULL unless this device is open (users > 0) */ |
86 | struct mutex buf_lock; | 86 | struct mutex buf_lock; |
87 | unsigned users; | 87 | unsigned users; |
88 | u8 *buffer; | 88 | u8 *tx_buffer; |
89 | u8 *rx_buffer; | ||
89 | }; | 90 | }; |
90 | 91 | ||
91 | static LIST_HEAD(device_list); | 92 | static LIST_HEAD(device_list); |
@@ -135,7 +136,7 @@ static inline ssize_t | |||
135 | spidev_sync_write(struct spidev_data *spidev, size_t len) | 136 | spidev_sync_write(struct spidev_data *spidev, size_t len) |
136 | { | 137 | { |
137 | struct spi_transfer t = { | 138 | struct spi_transfer t = { |
138 | .tx_buf = spidev->buffer, | 139 | .tx_buf = spidev->tx_buffer, |
139 | .len = len, | 140 | .len = len, |
140 | }; | 141 | }; |
141 | struct spi_message m; | 142 | struct spi_message m; |
@@ -149,7 +150,7 @@ static inline ssize_t | |||
149 | spidev_sync_read(struct spidev_data *spidev, size_t len) | 150 | spidev_sync_read(struct spidev_data *spidev, size_t len) |
150 | { | 151 | { |
151 | struct spi_transfer t = { | 152 | struct spi_transfer t = { |
152 | .rx_buf = spidev->buffer, | 153 | .rx_buf = spidev->rx_buffer, |
153 | .len = len, | 154 | .len = len, |
154 | }; | 155 | }; |
155 | struct spi_message m; | 156 | struct spi_message m; |
@@ -179,7 +180,7 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | |||
179 | if (status > 0) { | 180 | if (status > 0) { |
180 | unsigned long missing; | 181 | unsigned long missing; |
181 | 182 | ||
182 | missing = copy_to_user(buf, spidev->buffer, status); | 183 | missing = copy_to_user(buf, spidev->rx_buffer, status); |
183 | if (missing == status) | 184 | if (missing == status) |
184 | status = -EFAULT; | 185 | status = -EFAULT; |
185 | else | 186 | else |
@@ -206,7 +207,7 @@ spidev_write(struct file *filp, const char __user *buf, | |||
206 | spidev = filp->private_data; | 207 | spidev = filp->private_data; |
207 | 208 | ||
208 | mutex_lock(&spidev->buf_lock); | 209 | mutex_lock(&spidev->buf_lock); |
209 | missing = copy_from_user(spidev->buffer, buf, count); | 210 | missing = copy_from_user(spidev->tx_buffer, buf, count); |
210 | if (missing == 0) | 211 | if (missing == 0) |
211 | status = spidev_sync_write(spidev, count); | 212 | status = spidev_sync_write(spidev, count); |
212 | else | 213 | else |
@@ -224,7 +225,7 @@ static int spidev_message(struct spidev_data *spidev, | |||
224 | struct spi_transfer *k_tmp; | 225 | struct spi_transfer *k_tmp; |
225 | struct spi_ioc_transfer *u_tmp; | 226 | struct spi_ioc_transfer *u_tmp; |
226 | unsigned n, total; | 227 | unsigned n, total; |
227 | u8 *buf; | 228 | u8 *tx_buf, *rx_buf; |
228 | int status = -EFAULT; | 229 | int status = -EFAULT; |
229 | 230 | ||
230 | spi_message_init(&msg); | 231 | spi_message_init(&msg); |
@@ -236,7 +237,8 @@ static int spidev_message(struct spidev_data *spidev, | |||
236 | * We walk the array of user-provided transfers, using each one | 237 | * We walk the array of user-provided transfers, using each one |
237 | * to initialize a kernel version of the same transfer. | 238 | * to initialize a kernel version of the same transfer. |
238 | */ | 239 | */ |
239 | buf = spidev->buffer; | 240 | tx_buf = spidev->tx_buffer; |
241 | rx_buf = spidev->rx_buffer; | ||
240 | total = 0; | 242 | total = 0; |
241 | for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; | 243 | for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; |
242 | n; | 244 | n; |
@@ -250,20 +252,21 @@ static int spidev_message(struct spidev_data *spidev, | |||
250 | } | 252 | } |
251 | 253 | ||
252 | if (u_tmp->rx_buf) { | 254 | if (u_tmp->rx_buf) { |
253 | k_tmp->rx_buf = buf; | 255 | k_tmp->rx_buf = rx_buf; |
254 | if (!access_ok(VERIFY_WRITE, (u8 __user *) | 256 | if (!access_ok(VERIFY_WRITE, (u8 __user *) |
255 | (uintptr_t) u_tmp->rx_buf, | 257 | (uintptr_t) u_tmp->rx_buf, |
256 | u_tmp->len)) | 258 | u_tmp->len)) |
257 | goto done; | 259 | goto done; |
258 | } | 260 | } |
259 | if (u_tmp->tx_buf) { | 261 | if (u_tmp->tx_buf) { |
260 | k_tmp->tx_buf = buf; | 262 | k_tmp->tx_buf = tx_buf; |
261 | if (copy_from_user(buf, (const u8 __user *) | 263 | if (copy_from_user(tx_buf, (const u8 __user *) |
262 | (uintptr_t) u_tmp->tx_buf, | 264 | (uintptr_t) u_tmp->tx_buf, |
263 | u_tmp->len)) | 265 | u_tmp->len)) |
264 | goto done; | 266 | goto done; |
265 | } | 267 | } |
266 | buf += k_tmp->len; | 268 | tx_buf += k_tmp->len; |
269 | rx_buf += k_tmp->len; | ||
267 | 270 | ||
268 | k_tmp->cs_change = !!u_tmp->cs_change; | 271 | k_tmp->cs_change = !!u_tmp->cs_change; |
269 | k_tmp->tx_nbits = u_tmp->tx_nbits; | 272 | k_tmp->tx_nbits = u_tmp->tx_nbits; |
@@ -290,17 +293,17 @@ static int spidev_message(struct spidev_data *spidev, | |||
290 | goto done; | 293 | goto done; |
291 | 294 | ||
292 | /* copy any rx data out of bounce buffer */ | 295 | /* copy any rx data out of bounce buffer */ |
293 | buf = spidev->buffer; | 296 | rx_buf = spidev->rx_buffer; |
294 | for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { | 297 | for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { |
295 | if (u_tmp->rx_buf) { | 298 | if (u_tmp->rx_buf) { |
296 | if (__copy_to_user((u8 __user *) | 299 | if (__copy_to_user((u8 __user *) |
297 | (uintptr_t) u_tmp->rx_buf, buf, | 300 | (uintptr_t) u_tmp->rx_buf, rx_buf, |
298 | u_tmp->len)) { | 301 | u_tmp->len)) { |
299 | status = -EFAULT; | 302 | status = -EFAULT; |
300 | goto done; | 303 | goto done; |
301 | } | 304 | } |
302 | } | 305 | } |
303 | buf += u_tmp->len; | 306 | rx_buf += u_tmp->len; |
304 | } | 307 | } |
305 | status = total; | 308 | status = total; |
306 | 309 | ||
@@ -508,22 +511,41 @@ static int spidev_open(struct inode *inode, struct file *filp) | |||
508 | break; | 511 | break; |
509 | } | 512 | } |
510 | } | 513 | } |
511 | if (status == 0) { | 514 | |
512 | if (!spidev->buffer) { | 515 | if (status) { |
513 | spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); | 516 | pr_debug("spidev: nothing for minor %d\n", iminor(inode)); |
514 | if (!spidev->buffer) { | 517 | goto err_find_dev; |
518 | } | ||
519 | |||
520 | if (!spidev->tx_buffer) { | ||
521 | spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); | ||
522 | if (!spidev->tx_buffer) { | ||
515 | dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); | 523 | dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); |
516 | status = -ENOMEM; | 524 | status = -ENOMEM; |
525 | goto err_find_dev; | ||
517 | } | 526 | } |
518 | } | 527 | } |
519 | if (status == 0) { | 528 | |
520 | spidev->users++; | 529 | if (!spidev->rx_buffer) { |
521 | filp->private_data = spidev; | 530 | spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); |
522 | nonseekable_open(inode, filp); | 531 | if (!spidev->rx_buffer) { |
532 | dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); | ||
533 | status = -ENOMEM; | ||
534 | goto err_alloc_rx_buf; | ||
523 | } | 535 | } |
524 | } else | 536 | } |
525 | pr_debug("spidev: nothing for minor %d\n", iminor(inode)); | 537 | |
538 | spidev->users++; | ||
539 | filp->private_data = spidev; | ||
540 | nonseekable_open(inode, filp); | ||
541 | |||
542 | mutex_unlock(&device_list_lock); | ||
543 | return 0; | ||
526 | 544 | ||
545 | err_alloc_rx_buf: | ||
546 | kfree(spidev->tx_buffer); | ||
547 | spidev->tx_buffer = NULL; | ||
548 | err_find_dev: | ||
527 | mutex_unlock(&device_list_lock); | 549 | mutex_unlock(&device_list_lock); |
528 | return status; | 550 | return status; |
529 | } | 551 | } |
@@ -542,8 +564,11 @@ static int spidev_release(struct inode *inode, struct file *filp) | |||
542 | if (!spidev->users) { | 564 | if (!spidev->users) { |
543 | int dofree; | 565 | int dofree; |
544 | 566 | ||
545 | kfree(spidev->buffer); | 567 | kfree(spidev->tx_buffer); |
546 | spidev->buffer = NULL; | 568 | spidev->tx_buffer = NULL; |
569 | |||
570 | kfree(spidev->rx_buffer); | ||
571 | spidev->rx_buffer = NULL; | ||
547 | 572 | ||
548 | /* ... after we unbound from the underlying device? */ | 573 | /* ... after we unbound from the underlying device? */ |
549 | spin_lock_irq(&spidev->spi_lock); | 574 | spin_lock_irq(&spidev->spi_lock); |