Merge remote-tracking branches 'spi/fix/dw', 'spi/fix/orion', 'spi/fix/pl022', 'spi/fix/rockchip' and 'spi/fix/spidev' into spi-linus
This commit is contained in:
commit
1e2cf73e6b
5 changed files with 94 additions and 42 deletions
|
@ -669,6 +669,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||||
master->cleanup = dw_spi_cleanup;
|
master->cleanup = dw_spi_cleanup;
|
||||||
master->transfer_one_message = dw_spi_transfer_one_message;
|
master->transfer_one_message = dw_spi_transfer_one_message;
|
||||||
master->max_speed_hz = dws->max_freq;
|
master->max_speed_hz = dws->max_freq;
|
||||||
|
master->dev.of_node = dev->of_node;
|
||||||
|
|
||||||
/* Basic HW init */
|
/* Basic HW init */
|
||||||
spi_hw_init(dws);
|
spi_hw_init(dws);
|
||||||
|
|
|
@ -454,7 +454,7 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||||
spi->master = master;
|
spi->master = master;
|
||||||
|
|
||||||
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
||||||
devdata = of_id->data;
|
devdata = (of_id) ? of_id->data : &orion_spi_dev_data;
|
||||||
spi->devdata = devdata;
|
spi->devdata = devdata;
|
||||||
|
|
||||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
|
|
@ -1106,7 +1106,7 @@ err_rxdesc:
|
||||||
pl022->sgt_tx.nents, DMA_TO_DEVICE);
|
pl022->sgt_tx.nents, DMA_TO_DEVICE);
|
||||||
err_tx_sgmap:
|
err_tx_sgmap:
|
||||||
dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
|
dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
|
||||||
pl022->sgt_tx.nents, DMA_FROM_DEVICE);
|
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
|
||||||
err_rx_sgmap:
|
err_rx_sgmap:
|
||||||
sg_free_table(&pl022->sgt_tx);
|
sg_free_table(&pl022->sgt_tx);
|
||||||
err_alloc_tx_sg:
|
err_alloc_tx_sg:
|
||||||
|
|
|
@ -145,6 +145,9 @@
|
||||||
#define RXBUSY (1 << 0)
|
#define RXBUSY (1 << 0)
|
||||||
#define TXBUSY (1 << 1)
|
#define TXBUSY (1 << 1)
|
||||||
|
|
||||||
|
/* sclk_out: spi master internal logic in rk3x can support 50Mhz */
|
||||||
|
#define MAX_SCLK_OUT 50000000
|
||||||
|
|
||||||
enum rockchip_ssi_type {
|
enum rockchip_ssi_type {
|
||||||
SSI_MOTO_SPI = 0,
|
SSI_MOTO_SPI = 0,
|
||||||
SSI_TI_SSP,
|
SSI_TI_SSP,
|
||||||
|
@ -325,6 +328,8 @@ static int rockchip_spi_unprepare_message(struct spi_master *master,
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rs->lock, flags);
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
|
||||||
|
spi_enable_chip(rs, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,6 +386,8 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
|
||||||
if (rs->tx)
|
if (rs->tx)
|
||||||
wait_for_idle(rs);
|
wait_for_idle(rs);
|
||||||
|
|
||||||
|
spi_enable_chip(rs, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,8 +399,10 @@ static void rockchip_spi_dma_rxcb(void *data)
|
||||||
spin_lock_irqsave(&rs->lock, flags);
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
|
||||||
rs->state &= ~RXBUSY;
|
rs->state &= ~RXBUSY;
|
||||||
if (!(rs->state & TXBUSY))
|
if (!(rs->state & TXBUSY)) {
|
||||||
|
spi_enable_chip(rs, 0);
|
||||||
spi_finalize_current_transfer(rs->master);
|
spi_finalize_current_transfer(rs->master);
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rs->lock, flags);
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -409,8 +418,10 @@ static void rockchip_spi_dma_txcb(void *data)
|
||||||
spin_lock_irqsave(&rs->lock, flags);
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
|
||||||
rs->state &= ~TXBUSY;
|
rs->state &= ~TXBUSY;
|
||||||
if (!(rs->state & RXBUSY))
|
if (!(rs->state & RXBUSY)) {
|
||||||
|
spi_enable_chip(rs, 0);
|
||||||
spi_finalize_current_transfer(rs->master);
|
spi_finalize_current_transfer(rs->master);
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rs->lock, flags);
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -496,12 +507,19 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||||
dmacr |= RF_DMA_EN;
|
dmacr |= RF_DMA_EN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (WARN_ON(rs->speed > MAX_SCLK_OUT))
|
||||||
|
rs->speed = MAX_SCLK_OUT;
|
||||||
|
|
||||||
|
/* the minimum divsor is 2 */
|
||||||
|
if (rs->max_freq < 2 * rs->speed) {
|
||||||
|
clk_set_rate(rs->spiclk, 2 * rs->speed);
|
||||||
|
rs->max_freq = clk_get_rate(rs->spiclk);
|
||||||
|
}
|
||||||
|
|
||||||
/* div doesn't support odd number */
|
/* div doesn't support odd number */
|
||||||
div = max_t(u32, rs->max_freq / rs->speed, 1);
|
div = max_t(u32, rs->max_freq / rs->speed, 1);
|
||||||
div = (div + 1) & 0xfffe;
|
div = (div + 1) & 0xfffe;
|
||||||
|
|
||||||
spi_enable_chip(rs, 0);
|
|
||||||
|
|
||||||
writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
|
writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
|
||||||
|
|
||||||
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||||
|
@ -515,8 +533,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||||
spi_set_clk(rs, div);
|
spi_set_clk(rs, div);
|
||||||
|
|
||||||
dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div);
|
dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div);
|
||||||
|
|
||||||
spi_enable_chip(rs, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rockchip_spi_transfer_one(
|
static int rockchip_spi_transfer_one(
|
||||||
|
@ -524,7 +540,7 @@ static int rockchip_spi_transfer_one(
|
||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 1;
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
|
WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
|
||||||
|
@ -556,17 +572,27 @@ static int rockchip_spi_transfer_one(
|
||||||
rs->tmode = CR0_XFM_RO;
|
rs->tmode = CR0_XFM_RO;
|
||||||
|
|
||||||
/* we need prepare dma before spi was enabled */
|
/* we need prepare dma before spi was enabled */
|
||||||
if (master->can_dma && master->can_dma(master, spi, xfer)) {
|
if (master->can_dma && master->can_dma(master, spi, xfer))
|
||||||
rs->use_dma = 1;
|
rs->use_dma = 1;
|
||||||
rockchip_spi_prepare_dma(rs);
|
else
|
||||||
} else {
|
|
||||||
rs->use_dma = 0;
|
rs->use_dma = 0;
|
||||||
}
|
|
||||||
|
|
||||||
rockchip_spi_config(rs);
|
rockchip_spi_config(rs);
|
||||||
|
|
||||||
if (!rs->use_dma)
|
if (rs->use_dma) {
|
||||||
|
if (rs->tmode == CR0_XFM_RO) {
|
||||||
|
/* rx: dma must be prepared first */
|
||||||
|
rockchip_spi_prepare_dma(rs);
|
||||||
|
spi_enable_chip(rs, 1);
|
||||||
|
} else {
|
||||||
|
/* tx or tr: spi must be enabled first */
|
||||||
|
spi_enable_chip(rs, 1);
|
||||||
|
rockchip_spi_prepare_dma(rs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spi_enable_chip(rs, 1);
|
||||||
ret = rockchip_spi_pio_transfer(rs);
|
ret = rockchip_spi_pio_transfer(rs);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue