summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig1
-rw-r--r--drivers/spi/mpc52xx_psc_spi.c2
-rw-r--r--drivers/spi/pxa2xx_spi.c2
-rw-r--r--drivers/spi/spi_mpc83xx.c411
-rw-r--r--drivers/spi/spidev.c166
5 files changed, 407 insertions, 175 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fae9e8f3d092..66ec5d8808de 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -126,7 +126,6 @@ config SPI_MPC52xx_PSC
config SPI_MPC83xx
tristate "Freescale MPC83xx/QUICC Engine SPI controller"
depends on SPI_MASTER && (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL
- select SPI_BITBANG
help
This enables using the Freescale MPC83xx and QUICC Engine SPI
controllers in master mode.
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c
index 90729469d481..681d62325d3d 100644
--- a/drivers/spi/mpc52xx_psc_spi.c
+++ b/drivers/spi/mpc52xx_psc_spi.c
@@ -1,5 +1,5 @@
/*
- * MPC52xx SPC in SPI mode driver.
+ * MPC52xx PSC in SPI mode driver.
*
* Maintainer: Dragos Carp
*
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index 654bb58be630..0c452c46ab07 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -1567,7 +1567,7 @@ static int pxa2xx_spi_resume(struct platform_device *pdev)
int status = 0;
/* Enable the SSP clock */
- clk_disable(ssp->clk);
+ clk_enable(ssp->clk);
/* Start the queue running */
status = start_queue(drv_data);
diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c
index 189f706b9e4b..6832da6f7109 100644
--- a/drivers/spi/spi_mpc83xx.c
+++ b/drivers/spi/spi_mpc83xx.c
@@ -49,6 +49,7 @@ struct mpc83xx_spi_reg {
#define SPMODE_LEN(x) ((x) << 20)
#define SPMODE_PM(x) ((x) << 16)
#define SPMODE_OP (1 << 14)
+#define SPMODE_CG(x) ((x) << 7)
/*
* Default for SPI Mode:
@@ -67,10 +68,6 @@ struct mpc83xx_spi_reg {
/* SPI Controller driver's private data. */
struct mpc83xx_spi {
- /* bitbang has to be first */
- struct spi_bitbang bitbang;
- struct completion done;
-
struct mpc83xx_spi_reg __iomem *base;
/* rx & tx bufs from the spi_transfer */
@@ -82,7 +79,7 @@ struct mpc83xx_spi {
u32(*get_tx) (struct mpc83xx_spi *);
unsigned int count;
- u32 irq;
+ int irq;
unsigned nsecs; /* (clock cycle time)/2 */
@@ -94,6 +91,25 @@ struct mpc83xx_spi {
void (*activate_cs) (u8 cs, u8 polarity);
void (*deactivate_cs) (u8 cs, u8 polarity);
+
+ u8 busy;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct list_head queue;
+ spinlock_t lock;
+
+ struct completion done;
+};
+
+struct spi_mpc83xx_cs {
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc83xx_spi *);
+ u32 (*get_tx) (struct mpc83xx_spi *);
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+ u32 hw_mode; /* Holds HW mode register settings */
};
static inline void mpc83xx_spi_write_reg(__be32 __iomem * reg, u32 val)
@@ -137,6 +153,7 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value)
{
struct mpc83xx_spi *mpc83xx_spi;
u8 pol = spi->mode & SPI_CS_HIGH ? 1 : 0;
+ struct spi_mpc83xx_cs *cs = spi->controller_state;
mpc83xx_spi = spi_master_get_devdata(spi->master);
@@ -147,50 +164,26 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value)
if (value == BITBANG_CS_ACTIVE) {
u32 regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
- u32 len = spi->bits_per_word;
- u8 pm;
- if (len == 32)
- len = 0;
- else
- len = len - 1;
-
- /* mask out bits we are going to set */
- regval &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
- | SPMODE_LEN(0xF) | SPMODE_DIV16
- | SPMODE_PM(0xF) | SPMODE_REV | SPMODE_LOOP);
-
- if (spi->mode & SPI_CPHA)
- regval |= SPMODE_CP_BEGIN_EDGECLK;
- if (spi->mode & SPI_CPOL)
- regval |= SPMODE_CI_INACTIVEHIGH;
- if (!(spi->mode & SPI_LSB_FIRST))
- regval |= SPMODE_REV;
- if (spi->mode & SPI_LOOP)
- regval |= SPMODE_LOOP;
-
- regval |= SPMODE_LEN(len);
-
- if ((mpc83xx_spi->spibrg / spi->max_speed_hz) >= 64) {
- pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 64) - 1;
- if (pm > 0x0f) {
- dev_err(&spi->dev, "Requested speed is too "
- "low: %d Hz. Will use %d Hz instead.\n",
- spi->max_speed_hz,
- mpc83xx_spi->spibrg / 1024);
- pm = 0x0f;
- }
- regval |= SPMODE_PM(pm) | SPMODE_DIV16;
- } else {
- pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 4);
- if (pm)
- pm--;
- regval |= SPMODE_PM(pm);
+ mpc83xx_spi->rx_shift = cs->rx_shift;
+ mpc83xx_spi->tx_shift = cs->tx_shift;
+ mpc83xx_spi->get_rx = cs->get_rx;
+ mpc83xx_spi->get_tx = cs->get_tx;
+
+ if (cs->hw_mode != regval) {
+ unsigned long flags;
+ void *tmp_ptr = &mpc83xx_spi->base->mode;
+
+ regval = cs->hw_mode;
+ /* Turn off IRQs locally to minimize time that
+ * SPI is disabled
+ */
+ local_irq_save(flags);
+ /* Turn off SPI unit prior changing mode */
+ mpc83xx_spi_write_reg(tmp_ptr, regval & ~SPMODE_ENABLE);
+ mpc83xx_spi_write_reg(tmp_ptr, regval);
+ local_irq_restore(flags);
}
-
- /* Turn off SPI unit prior changing mode */
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
if (mpc83xx_spi->activate_cs)
mpc83xx_spi->activate_cs(spi->chip_select, pol);
}
@@ -201,8 +194,9 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct mpc83xx_spi *mpc83xx_spi;
u32 regval;
- u8 bits_per_word;
+ u8 bits_per_word, pm;
u32 hz;
+ struct spi_mpc83xx_cs *cs = spi->controller_state;
mpc83xx_spi = spi_master_get_devdata(spi->master);
@@ -223,61 +217,191 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|| ((bits_per_word > 16) && (bits_per_word != 32)))
return -EINVAL;
- mpc83xx_spi->rx_shift = 0;
- mpc83xx_spi->tx_shift = 0;
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ cs->rx_shift = 0;
+ cs->tx_shift = 0;
if (bits_per_word <= 8) {
- mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
- mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
+ cs->get_rx = mpc83xx_spi_rx_buf_u8;
+ cs->get_tx = mpc83xx_spi_tx_buf_u8;
if (mpc83xx_spi->qe_mode) {
- mpc83xx_spi->rx_shift = 16;
- mpc83xx_spi->tx_shift = 24;
+ cs->rx_shift = 16;
+ cs->tx_shift = 24;
}
} else if (bits_per_word <= 16) {
- mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u16;
- mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u16;
+ cs->get_rx = mpc83xx_spi_rx_buf_u16;
+ cs->get_tx = mpc83xx_spi_tx_buf_u16;
if (mpc83xx_spi->qe_mode) {
- mpc83xx_spi->rx_shift = 16;
- mpc83xx_spi->tx_shift = 16;
+ cs->rx_shift = 16;
+ cs->tx_shift = 16;
}
} else if (bits_per_word <= 32) {
- mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u32;
- mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u32;
+ cs->get_rx = mpc83xx_spi_rx_buf_u32;
+ cs->get_tx = mpc83xx_spi_tx_buf_u32;
} else
return -EINVAL;
if (mpc83xx_spi->qe_mode && spi->mode & SPI_LSB_FIRST) {
- mpc83xx_spi->tx_shift = 0;
+ cs->tx_shift = 0;
if (bits_per_word <= 8)
- mpc83xx_spi->rx_shift = 8;
+ cs->rx_shift = 8;
else
- mpc83xx_spi->rx_shift = 0;
+ cs->rx_shift = 0;
}
- /* nsecs = (clock period)/2 */
- if (!hz)
- hz = spi->max_speed_hz;
- mpc83xx_spi->nsecs = (1000000000 / 2) / hz;
- if (mpc83xx_spi->nsecs > MAX_UDELAY_MS * 1000)
- return -EINVAL;
+ mpc83xx_spi->rx_shift = cs->rx_shift;
+ mpc83xx_spi->tx_shift = cs->tx_shift;
+ mpc83xx_spi->get_rx = cs->get_rx;
+ mpc83xx_spi->get_tx = cs->get_tx;
if (bits_per_word == 32)
bits_per_word = 0;
else
bits_per_word = bits_per_word - 1;
- regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
-
/* mask out bits we are going to set */
- regval &= ~(SPMODE_LEN(0xF) | SPMODE_REV);
- regval |= SPMODE_LEN(bits_per_word);
- if (!(spi->mode & SPI_LSB_FIRST))
- regval |= SPMODE_REV;
+ cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16
+ | SPMODE_PM(0xF));
+
+ cs->hw_mode |= SPMODE_LEN(bits_per_word);
+
+ if ((mpc83xx_spi->spibrg / hz) >= 64) {
+ pm = mpc83xx_spi->spibrg / (hz * 64) - 1;
+ if (pm > 0x0f) {
+ dev_err(&spi->dev, "Requested speed is too "
+ "low: %d Hz. Will use %d Hz instead.\n",
+ hz, mpc83xx_spi->spibrg / 1024);
+ pm = 0x0f;
+ }
+ cs->hw_mode |= SPMODE_PM(pm) | SPMODE_DIV16;
+ } else {
+ pm = mpc83xx_spi->spibrg / (hz * 4);
+ if (pm)
+ pm--;
+ cs->hw_mode |= SPMODE_PM(pm);
+ }
+ regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
+ if (cs->hw_mode != regval) {
+ unsigned long flags;
+ void *tmp_ptr = &mpc83xx_spi->base->mode;
+
+ regval = cs->hw_mode;
+ /* Turn off IRQs locally to minimize time
+ * that SPI is disabled
+ */
+ local_irq_save(flags);
+ /* Turn off SPI unit prior changing mode */
+ mpc83xx_spi_write_reg(tmp_ptr, regval & ~SPMODE_ENABLE);
+ mpc83xx_spi_write_reg(tmp_ptr, regval);
+ local_irq_restore(flags);
+ }
+ return 0;
+}
- /* Turn off SPI unit prior changing mode */
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
+static int mpc83xx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct mpc83xx_spi *mpc83xx_spi;
+ u32 word, len, bits_per_word;
- return 0;
+ mpc83xx_spi = spi_master_get_devdata(spi->master);
+
+ mpc83xx_spi->tx = t->tx_buf;
+ mpc83xx_spi->rx = t->rx_buf;
+ bits_per_word = spi->bits_per_word;
+ if (t->bits_per_word)
+ bits_per_word = t->bits_per_word;
+ len = t->len;
+ if (bits_per_word > 8)
+ len /= 2;
+ if (bits_per_word > 16)
+ len /= 2;
+ mpc83xx_spi->count = len;
+ INIT_COMPLETION(mpc83xx_spi->done);
+
+ /* enable rx ints */
+ mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, SPIM_NE);
+
+ /* transmit word */
+ word = mpc83xx_spi->get_tx(mpc83xx_spi);
+ mpc83xx_spi_write_reg(&mpc83xx_spi->base->transmit, word);
+
+ wait_for_completion(&mpc83xx_spi->done);
+
+ /* disable rx ints */
+ mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
+
+ return mpc83xx_spi->count;
+}
+
+static void mpc83xx_spi_work(struct work_struct *work)
+{
+ struct mpc83xx_spi *mpc83xx_spi =
+ container_of(work, struct mpc83xx_spi, work);
+
+ spin_lock_irq(&mpc83xx_spi->lock);
+ mpc83xx_spi->busy = 1;
+ while (!list_empty(&mpc83xx_spi->queue)) {
+ struct spi_message *m;
+ struct spi_device *spi;
+ struct spi_transfer *t = NULL;
+ unsigned cs_change;
+ int status, nsecs = 50;
+
+ m = container_of(mpc83xx_spi->queue.next,
+ struct spi_message, queue);
+ list_del_init(&m->queue);
+ spin_unlock_irq(&mpc83xx_spi->lock);
+
+ spi = m->spi;
+ cs_change = 1;
+ status = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->bits_per_word || t->speed_hz) {
+ /* Don't allow changes if CS is active */
+ status = -EINVAL;
+
+ if (cs_change)
+ status = mpc83xx_spi_setup_transfer(spi, t);
+ if (status < 0)
+ break;
+ }
+
+ if (cs_change)
+ mpc83xx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+ cs_change = t->cs_change;
+ if (t->len)
+ status = mpc83xx_spi_bufs(spi, t);
+ if (status) {
+ status = -EMSGSIZE;
+ break;
+ }
+ m->actual_length += t->len;
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ if (cs_change) {
+ ndelay(nsecs);
+ mpc83xx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ ndelay(nsecs);
+ }
+ }
+
+ m->status = status;
+ m->complete(m->context);
+
+ if (status || !cs_change) {
+ ndelay(nsecs);
+ mpc83xx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ }
+
+ mpc83xx_spi_setup_transfer(spi, NULL);
+
+ spin_lock_irq(&mpc83xx_spi->lock);
+ }
+ mpc83xx_spi->busy = 0;
+ spin_unlock_irq(&mpc83xx_spi->lock);
}
/* the spi->mode bits understood by this driver: */
@@ -286,9 +410,10 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
static int mpc83xx_spi_setup(struct spi_device *spi)
{
- struct spi_bitbang *bitbang;
struct mpc83xx_spi *mpc83xx_spi;
int retval;
+ u32 hw_mode;
+ struct spi_mpc83xx_cs *cs = spi->controller_state;
if (spi->mode & ~MODEBITS) {
dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
@@ -299,63 +424,56 @@ static int mpc83xx_spi_setup(struct spi_device *spi)
if (!spi->max_speed_hz)
return -EINVAL;
- bitbang = spi_master_get_devdata(spi->master);
+ if (!cs) {
+ cs = kzalloc(sizeof *cs, GFP_KERNEL);
+ if (!cs)
+ return -ENOMEM;
+ spi->controller_state = cs;
+ }
mpc83xx_spi = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
+ hw_mode = cs->hw_mode; /* Save orginal settings */
+ cs->hw_mode = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
+ /* mask out bits we are going to set */
+ cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
+ | SPMODE_REV | SPMODE_LOOP);
+
+ if (spi->mode & SPI_CPHA)
+ cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK;
+ if (spi->mode & SPI_CPOL)
+ cs->hw_mode |= SPMODE_CI_INACTIVEHIGH;
+ if (!(spi->mode & SPI_LSB_FIRST))
+ cs->hw_mode |= SPMODE_REV;
+ if (spi->mode & SPI_LOOP)
+ cs->hw_mode |= SPMODE_LOOP;
+
retval = mpc83xx_spi_setup_transfer(spi, NULL);
- if (retval < 0)
+ if (retval < 0) {
+ cs->hw_mode = hw_mode; /* Restore settings */
return retval;
+ }
- dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec\n",
+ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u Hz\n",
__func__, spi->mode & (SPI_CPOL | SPI_CPHA),
- spi->bits_per_word, 2 * mpc83xx_spi->nsecs);
-
+ spi->bits_per_word, spi->max_speed_hz);
+#if 0 /* Don't think this is needed */
/* NOTE we _need_ to call chipselect() early, ideally with adapter
* setup, unless the hardware defaults cooperate to avoid confusion
* between normal (active low) and inverted chipselects.
*/
/* deselect chip (low or high) */
- spin_lock(&bitbang->lock);
- if (!bitbang->busy) {
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(mpc83xx_spi->nsecs);
- }
- spin_unlock(&bitbang->lock);
-
+ spin_lock(&mpc83xx_spi->lock);
+ if (!mpc83xx_spi->busy)
+ mpc83xx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ spin_unlock(&mpc83xx_spi->lock);
+#endif
return 0;
}
-static int mpc83xx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
-{
- struct mpc83xx_spi *mpc83xx_spi;
- u32 word;
-
- mpc83xx_spi = spi_master_get_devdata(spi->master);
-
- mpc83xx_spi->tx = t->tx_buf;
- mpc83xx_spi->rx = t->rx_buf;
- mpc83xx_spi->count = t->len;
- INIT_COMPLETION(mpc83xx_spi->done);
-
- /* enable rx ints */
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, SPIM_NE);
-
- /* transmit word */
- word = mpc83xx_spi->get_tx(mpc83xx_spi);
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->transmit, word);
-
- wait_for_completion(&mpc83xx_spi->done);
-
- /* disable rx ints */
- mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
-
- return t->len - mpc83xx_spi->count;
-}
-
irqreturn_t mpc83xx_spi_irq(s32 irq, void *context_data)
{
struct mpc83xx_spi *mpc83xx_spi = context_data;
@@ -395,6 +513,28 @@ irqreturn_t mpc83xx_spi_irq(s32 irq, void *context_data)
return ret;
}
+static int mpc83xx_spi_transfer(struct spi_device *spi,
+ struct spi_message *m)
+{
+ struct mpc83xx_spi *mpc83xx_spi = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ m->actual_length = 0;
+ m->status = -EINPROGRESS;
+
+ spin_lock_irqsave(&mpc83xx_spi->lock, flags);
+ list_add_tail(&m->queue, &mpc83xx_spi->queue);
+ queue_work(mpc83xx_spi->workqueue, &mpc83xx_spi->work);
+ spin_unlock_irqrestore(&mpc83xx_spi->lock, flags);
+
+ return 0;
+}
+
+
+static void mpc83xx_spi_cleanup(struct spi_device *spi)
+{
+ kfree(spi->controller_state);
+}
static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
@@ -426,11 +566,11 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev)
ret = -ENODEV;
goto free_master;
}
+ master->setup = mpc83xx_spi_setup;
+ master->transfer = mpc83xx_spi_transfer;
+ master->cleanup = mpc83xx_spi_cleanup;
+
mpc83xx_spi = spi_master_get_devdata(master);
- mpc83xx_spi->bitbang.master = spi_master_get(master);
- mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
- mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
- mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
mpc83xx_spi->activate_cs = pdata->activate_cs;
mpc83xx_spi->deactivate_cs = pdata->deactivate_cs;
mpc83xx_spi->qe_mode = pdata->qe_mode;
@@ -445,7 +585,6 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev)
mpc83xx_spi->tx_shift = 24;
}
- mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
init_completion(&mpc83xx_spi->done);
mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
@@ -483,11 +622,21 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev)
regval |= SPMODE_OP;
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
+ spin_lock_init(&mpc83xx_spi->lock);
+ init_completion(&mpc83xx_spi->done);
+ INIT_WORK(&mpc83xx_spi->work, mpc83xx_spi_work);
+ INIT_LIST_HEAD(&mpc83xx_spi->queue);
- ret = spi_bitbang_start(&mpc83xx_spi->bitbang);
-
- if (ret != 0)
+ mpc83xx_spi->workqueue = create_singlethread_workqueue(
+ master->dev.parent->bus_id);
+ if (mpc83xx_spi->workqueue == NULL) {
+ ret = -EBUSY;
goto free_irq;
+ }
+
+ ret = spi_register_master(master);
+ if (ret < 0)
+ goto unreg_master;
printk(KERN_INFO
"%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n",
@@ -495,6 +644,8 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev)
return ret;
+unreg_master:
+ destroy_workqueue(mpc83xx_spi->workqueue);
free_irq:
free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
@@ -515,10 +666,12 @@ static int __exit mpc83xx_spi_remove(struct platform_device *dev)
master = platform_get_drvdata(dev);
mpc83xx_spi = spi_master_get_devdata(master);
- spi_bitbang_stop(&mpc83xx_spi->bitbang);
+ flush_workqueue(mpc83xx_spi->workqueue);
+ destroy_workqueue(mpc83xx_spi->workqueue);
+ spi_unregister_master(master);
+
free_irq(mpc83xx_spi->irq, mpc83xx_spi);
iounmap(mpc83xx_spi->base);
- spi_master_put(mpc83xx_spi->bitbang.master);
return 0;
}
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index b3518ca9f04e..799337f7fde1 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -25,6 +25,7 @@
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
@@ -67,10 +68,12 @@ static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG];
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP)
struct spidev_data {
- struct device dev;
+ dev_t devt;
+ spinlock_t spi_lock;
struct spi_device *spi;
struct list_head device_entry;
+ /* buffer is NULL unless this device is open (users > 0) */
struct mutex buf_lock;
unsigned users;
u8 *buffer;
@@ -85,12 +88,75 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
/*-------------------------------------------------------------------------*/
+/*
+ * We can't use the standard synchronous wrappers for file I/O; we
+ * need to protect against async removal of the underlying spi_device.
+ */
+static void spidev_complete(void *arg)
+{
+ complete(arg);
+}
+
+static ssize_t
+spidev_sync(struct spidev_data *spidev, struct spi_message *message)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ int status;
+
+ message->complete = spidev_complete;
+ message->context = &done;
+
+ spin_lock_irq(&spidev->spi_lock);
+ if (spidev->spi == NULL)
+ status = -ESHUTDOWN;
+ else
+ status = spi_async(spidev->spi, message);
+ spin_unlock_irq(&spidev->spi_lock);
+
+ if (status == 0) {
+ wait_for_completion(&done);
+ status = message->status;
+ if (status == 0)
+ status = message->actual_length;
+ }
+ return status;
+}
+
+static inline ssize_t
+spidev_sync_write(struct spidev_data *spidev, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = spidev->buffer,
+ .len = len,
+ };
+ 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 = {
+ .rx_buf = spidev->buffer,
+ .len = len,
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ return spidev_sync(spidev, &m);
+}
+
+/*-------------------------------------------------------------------------*/
+
/* 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;
- struct spi_device *spi;
ssize_t status = 0;
/* chipselect only toggles at start or end of operation */
@@ -98,10 +164,9 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
return -EMSGSIZE;
spidev = filp->private_data;
- spi = spidev->spi;
mutex_lock(&spidev->buf_lock);
- status = spi_read(spi, spidev->buffer, count);
+ status = spidev_sync_read(spidev, count);
if (status == 0) {
unsigned long missing;
@@ -122,7 +187,6 @@ spidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
- struct spi_device *spi;
ssize_t status = 0;
unsigned long missing;
@@ -131,12 +195,11 @@ spidev_write(struct file *filp, const char __user *buf,
return -EMSGSIZE;
spidev = filp->private_data;
- spi = spidev->spi;
mutex_lock(&spidev->buf_lock);
missing = copy_from_user(spidev->buffer, buf, count);
if (missing == 0) {
- status = spi_write(spi, spidev->buffer, count);
+ status = spidev_sync_write(spidev, count);
if (status == 0)
status = count;
} else
@@ -153,7 +216,6 @@ static int spidev_message(struct spidev_data *spidev,
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
- struct spi_device *spi = spidev->spi;
unsigned n, total;
u8 *buf;
int status = -EFAULT;
@@ -215,7 +277,7 @@ static int spidev_message(struct spidev_data *spidev,
spi_message_add_tail(k_tmp, &msg);
}
- status = spi_sync(spi, &msg);
+ status = spidev_sync(spidev, &msg);
if (status < 0)
goto done;
@@ -269,8 +331,16 @@ spidev_ioctl(struct inode *inode, struct file *filp,
if (err)
return -EFAULT;
+ /* guard against device removal before, or while,
+ * we issue this ioctl.
+ */
spidev = filp->private_data;
- spi = spidev->spi;
+ spin_lock_irq(&spidev->spi_lock);
+ spi = spi_dev_get(spidev->spi);
+ spin_unlock_irq(&spidev->spi_lock);
+
+ if (spi == NULL)
+ return -ESHUTDOWN;
switch (cmd) {
/* read requests */
@@ -356,8 +426,10 @@ spidev_ioctl(struct inode *inode, struct file *filp,
default:
/* segmented and/or full-duplex I/O request */
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
- || _IOC_DIR(cmd) != _IOC_WRITE)
- return -ENOTTY;
+ || _IOC_DIR(cmd) != _IOC_WRITE) {
+ retval = -ENOTTY;
+ break;
+ }
tmp = _IOC_SIZE(cmd);
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
@@ -385,6 +457,7 @@ spidev_ioctl(struct inode *inode, struct file *filp,
kfree(ioc);
break;
}
+ spi_dev_put(spi);
return retval;
}
@@ -396,7 +469,7 @@ static int spidev_open(struct inode *inode, struct file *filp)
mutex_lock(&device_list_lock);
list_for_each_entry(spidev, &device_list, device_entry) {
- if (spidev->dev.devt == inode->i_rdev) {
+ if (spidev->devt == inode->i_rdev) {
status = 0;
break;
}
@@ -429,10 +502,22 @@ static int spidev_release(struct inode *inode, struct file *filp)
mutex_lock(&device_list_lock);
spidev = filp->private_data;
filp->private_data = NULL;
+
+ /* last close? */
spidev->users--;
if (!spidev->users) {
+ int dofree;
+
kfree(spidev->buffer);
spidev->buffer = NULL;
+
+ /* ... after we unbound from the underlying device? */
+ spin_lock_irq(&spidev->spi_lock);
+ dofree = (spidev->spi == NULL);
+ spin_unlock_irq(&spidev->spi_lock);
+
+ if (dofree)
+ kfree(spidev);
}
mutex_unlock(&device_list_lock);
@@ -459,19 +544,7 @@ static struct file_operations spidev_fops = {
* It also simplifies memory management.
*/
-static void spidev_classdev_release(struct device *dev)
-{
- struct spidev_data *spidev;
-
- spidev = container_of(dev, struct spidev_data, dev);
- kfree(spidev);
-}
-
-static struct class spidev_class = {
- .name = "spidev",
- .owner = THIS_MODULE,
- .dev_release = spidev_classdev_release,
-};
+static struct class *spidev_class;
/*-------------------------------------------------------------------------*/
@@ -488,6 +561,7 @@ static int spidev_probe(struct spi_device *spi)
/* Initialize the driver data */
spidev->spi = spi;
+ spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);
@@ -498,20 +572,20 @@ static int spidev_probe(struct spi_device *spi)
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS);
if (minor < N_SPI_MINORS) {
- spidev->dev.parent = &spi->dev;
- spidev->dev.class = &spidev_class;
- spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor);
- snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id,
+ struct device *dev;
+
+ spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
+ dev = device_create(spidev_class, &spi->dev, spidev->devt,
"spidev%d.%d",
spi->master->bus_num, spi->chip_select);
- status = device_register(&spidev->dev);
+ status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}
if (status == 0) {
set_bit(minor, minors);
- dev_set_drvdata(&spi->dev, spidev);
+ spi_set_drvdata(spi, spidev);
list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);
@@ -524,15 +598,21 @@ static int spidev_probe(struct spi_device *spi)
static int spidev_remove(struct spi_device *spi)
{
- struct spidev_data *spidev = dev_get_drvdata(&spi->dev);
+ struct spidev_data *spidev = spi_get_drvdata(spi);
- mutex_lock(&device_list_lock);
+ /* make sure ops on existing fds can abort cleanly */
+ spin_lock_irq(&spidev->spi_lock);
+ spidev->spi = NULL;
+ spi_set_drvdata(spi, NULL);
+ spin_unlock_irq(&spidev->spi_lock);
+ /* prevent new opens */
+ mutex_lock(&device_list_lock);
list_del(&spidev->device_entry);
- dev_set_drvdata(&spi->dev, NULL);
- clear_bit(MINOR(spidev->dev.devt), minors);
- device_unregister(&spidev->dev);
-
+ device_destroy(spidev_class, spidev->devt);
+ clear_bit(MINOR(spidev->devt), minors);
+ if (spidev->users == 0)
+ kfree(spidev);
mutex_unlock(&device_list_lock);
return 0;
@@ -568,15 +648,15 @@ static int __init spidev_init(void)
if (status < 0)
return status;
- status = class_register(&spidev_class);
- if (status < 0) {
+ spidev_class = class_create(THIS_MODULE, "spidev");
+ if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
- return status;
+ return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi);
if (status < 0) {
- class_unregister(&spidev_class);
+ class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
}
return status;
@@ -586,7 +666,7 @@ module_init(spidev_init);
static void __exit spidev_exit(void)
{
spi_unregister_driver(&spidev_spi);
- class_unregister(&spidev_class);
+ class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
}
module_exit(spidev_exit);