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>
This commit is contained in:
parent
a2285b8c75
commit
865f6d1974
@ -82,10 +82,11 @@ struct spidev_data {
|
|||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct list_head device_entry;
|
struct list_head device_entry;
|
||||||
|
|
||||||
/* buffer is NULL unless this device is open (users > 0) */
|
/* TX/RX buffers are NULL unless this device is open (users > 0) */
|
||||||
struct mutex buf_lock;
|
struct mutex buf_lock;
|
||||||
unsigned users;
|
unsigned users;
|
||||||
u8 *buffer;
|
u8 *tx_buffer;
|
||||||
|
u8 *rx_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
static LIST_HEAD(device_list);
|
static LIST_HEAD(device_list);
|
||||||
@ -135,7 +136,7 @@ static inline ssize_t
|
|||||||
spidev_sync_write(struct spidev_data *spidev, size_t len)
|
spidev_sync_write(struct spidev_data *spidev, size_t len)
|
||||||
{
|
{
|
||||||
struct spi_transfer t = {
|
struct spi_transfer t = {
|
||||||
.tx_buf = spidev->buffer,
|
.tx_buf = spidev->tx_buffer,
|
||||||
.len = len,
|
.len = len,
|
||||||
};
|
};
|
||||||
struct spi_message m;
|
struct spi_message m;
|
||||||
@ -149,7 +150,7 @@ static inline ssize_t
|
|||||||
spidev_sync_read(struct spidev_data *spidev, size_t len)
|
spidev_sync_read(struct spidev_data *spidev, size_t len)
|
||||||
{
|
{
|
||||||
struct spi_transfer t = {
|
struct spi_transfer t = {
|
||||||
.rx_buf = spidev->buffer,
|
.rx_buf = spidev->rx_buffer,
|
||||||
.len = len,
|
.len = len,
|
||||||
};
|
};
|
||||||
struct spi_message m;
|
struct spi_message m;
|
||||||
@ -179,7 +180,7 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
|
|||||||
if (status > 0) {
|
if (status > 0) {
|
||||||
unsigned long missing;
|
unsigned long missing;
|
||||||
|
|
||||||
missing = copy_to_user(buf, spidev->buffer, status);
|
missing = copy_to_user(buf, spidev->rx_buffer, status);
|
||||||
if (missing == status)
|
if (missing == status)
|
||||||
status = -EFAULT;
|
status = -EFAULT;
|
||||||
else
|
else
|
||||||
@ -206,7 +207,7 @@ spidev_write(struct file *filp, const char __user *buf,
|
|||||||
spidev = filp->private_data;
|
spidev = filp->private_data;
|
||||||
|
|
||||||
mutex_lock(&spidev->buf_lock);
|
mutex_lock(&spidev->buf_lock);
|
||||||
missing = copy_from_user(spidev->buffer, buf, count);
|
missing = copy_from_user(spidev->tx_buffer, buf, count);
|
||||||
if (missing == 0)
|
if (missing == 0)
|
||||||
status = spidev_sync_write(spidev, count);
|
status = spidev_sync_write(spidev, count);
|
||||||
else
|
else
|
||||||
@ -224,7 +225,7 @@ static int spidev_message(struct spidev_data *spidev,
|
|||||||
struct spi_transfer *k_tmp;
|
struct spi_transfer *k_tmp;
|
||||||
struct spi_ioc_transfer *u_tmp;
|
struct spi_ioc_transfer *u_tmp;
|
||||||
unsigned n, total;
|
unsigned n, total;
|
||||||
u8 *buf;
|
u8 *tx_buf, *rx_buf;
|
||||||
int status = -EFAULT;
|
int status = -EFAULT;
|
||||||
|
|
||||||
spi_message_init(&msg);
|
spi_message_init(&msg);
|
||||||
@ -236,7 +237,8 @@ static int spidev_message(struct spidev_data *spidev,
|
|||||||
* We walk the array of user-provided transfers, using each one
|
* We walk the array of user-provided transfers, using each one
|
||||||
* to initialize a kernel version of the same transfer.
|
* to initialize a kernel version of the same transfer.
|
||||||
*/
|
*/
|
||||||
buf = spidev->buffer;
|
tx_buf = spidev->tx_buffer;
|
||||||
|
rx_buf = spidev->rx_buffer;
|
||||||
total = 0;
|
total = 0;
|
||||||
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
|
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
|
||||||
n;
|
n;
|
||||||
@ -250,20 +252,21 @@ static int spidev_message(struct spidev_data *spidev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (u_tmp->rx_buf) {
|
if (u_tmp->rx_buf) {
|
||||||
k_tmp->rx_buf = buf;
|
k_tmp->rx_buf = rx_buf;
|
||||||
if (!access_ok(VERIFY_WRITE, (u8 __user *)
|
if (!access_ok(VERIFY_WRITE, (u8 __user *)
|
||||||
(uintptr_t) u_tmp->rx_buf,
|
(uintptr_t) u_tmp->rx_buf,
|
||||||
u_tmp->len))
|
u_tmp->len))
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (u_tmp->tx_buf) {
|
if (u_tmp->tx_buf) {
|
||||||
k_tmp->tx_buf = buf;
|
k_tmp->tx_buf = tx_buf;
|
||||||
if (copy_from_user(buf, (const u8 __user *)
|
if (copy_from_user(tx_buf, (const u8 __user *)
|
||||||
(uintptr_t) u_tmp->tx_buf,
|
(uintptr_t) u_tmp->tx_buf,
|
||||||
u_tmp->len))
|
u_tmp->len))
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
buf += k_tmp->len;
|
tx_buf += k_tmp->len;
|
||||||
|
rx_buf += k_tmp->len;
|
||||||
|
|
||||||
k_tmp->cs_change = !!u_tmp->cs_change;
|
k_tmp->cs_change = !!u_tmp->cs_change;
|
||||||
k_tmp->tx_nbits = u_tmp->tx_nbits;
|
k_tmp->tx_nbits = u_tmp->tx_nbits;
|
||||||
@ -290,17 +293,17 @@ static int spidev_message(struct spidev_data *spidev,
|
|||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* copy any rx data out of bounce buffer */
|
/* copy any rx data out of bounce buffer */
|
||||||
buf = spidev->buffer;
|
rx_buf = spidev->rx_buffer;
|
||||||
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
|
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
|
||||||
if (u_tmp->rx_buf) {
|
if (u_tmp->rx_buf) {
|
||||||
if (__copy_to_user((u8 __user *)
|
if (__copy_to_user((u8 __user *)
|
||||||
(uintptr_t) u_tmp->rx_buf, buf,
|
(uintptr_t) u_tmp->rx_buf, rx_buf,
|
||||||
u_tmp->len)) {
|
u_tmp->len)) {
|
||||||
status = -EFAULT;
|
status = -EFAULT;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf += u_tmp->len;
|
rx_buf += u_tmp->len;
|
||||||
}
|
}
|
||||||
status = total;
|
status = total;
|
||||||
|
|
||||||
@ -508,22 +511,41 @@ static int spidev_open(struct inode *inode, struct file *filp)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (status == 0) {
|
|
||||||
if (!spidev->buffer) {
|
if (status) {
|
||||||
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
|
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
|
||||||
if (!spidev->buffer) {
|
goto err_find_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spidev->tx_buffer) {
|
||||||
|
spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
||||||
|
if (!spidev->tx_buffer) {
|
||||||
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
|
goto err_find_dev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (status == 0) {
|
|
||||||
spidev->users++;
|
|
||||||
filp->private_data = spidev;
|
|
||||||
nonseekable_open(inode, filp);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
|
|
||||||
|
|
||||||
|
if (!spidev->rx_buffer) {
|
||||||
|
spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
||||||
|
if (!spidev->rx_buffer) {
|
||||||
|
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
||||||
|
status = -ENOMEM;
|
||||||
|
goto err_alloc_rx_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spidev->users++;
|
||||||
|
filp->private_data = spidev;
|
||||||
|
nonseekable_open(inode, filp);
|
||||||
|
|
||||||
|
mutex_unlock(&device_list_lock);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_alloc_rx_buf:
|
||||||
|
kfree(spidev->tx_buffer);
|
||||||
|
spidev->tx_buffer = NULL;
|
||||||
|
err_find_dev:
|
||||||
mutex_unlock(&device_list_lock);
|
mutex_unlock(&device_list_lock);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -542,8 +564,11 @@ static int spidev_release(struct inode *inode, struct file *filp)
|
|||||||
if (!spidev->users) {
|
if (!spidev->users) {
|
||||||
int dofree;
|
int dofree;
|
||||||
|
|
||||||
kfree(spidev->buffer);
|
kfree(spidev->tx_buffer);
|
||||||
spidev->buffer = NULL;
|
spidev->tx_buffer = NULL;
|
||||||
|
|
||||||
|
kfree(spidev->rx_buffer);
|
||||||
|
spidev->rx_buffer = NULL;
|
||||||
|
|
||||||
/* ... after we unbound from the underlying device? */
|
/* ... after we unbound from the underlying device? */
|
||||||
spin_lock_irq(&spidev->spi_lock);
|
spin_lock_irq(&spidev->spi_lock);
|
||||||
|
Loading…
Reference in New Issue
Block a user