From: Mark Brown broonie@linaro.org
Allow drivers to avoid implementing their own checks for simple rates by specifying the limits in the master structure.
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi.c | 7 +++++++ include/linux/spi/spi.h | 6 ++++++ 2 files changed, 13 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 978dda2..a52f166 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1387,6 +1387,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) BIT(xfer->bits_per_word - 1))) return -EINVAL; } + + if (xfer->speed_hz && master->min_speed_hz && + xfer->speed_hz < master->min_speed_hz) + return -EINVAL; + if (xfer->speed_hz && master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + return -EINVAL; }
message->spi = spi; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 28e440b..cdf6681 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -233,6 +233,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * suported. If set, the SPI core will reject any transfer with an * unsupported bits_per_word. If not set, this value is simply ignored, * and it's up to the individual driver to perform any validation. + * @min_speed_hz: Lowest supported transfer speed + * @max_speed_hz: Highest supported transfer speed * @flags: other constraints relevant to this driver * @bus_lock_spinlock: spinlock for SPI bus locking * @bus_lock_mutex: mutex for SPI bus locking @@ -312,6 +314,10 @@ struct spi_master { #define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0UL : (BIT(bits) - 1)) #define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
+ /* limits on transfer speed */ + u32 min_speed_hz; + u32 max_speed_hz; + /* other constraints relevant to this driver */ u16 flags; #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
From: Mark Brown broonie@linaro.org
Check that transfers are non-empty and that there is a completion for them.
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index a52f166..c289916 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1351,6 +1351,11 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) struct spi_master *master = spi->master; struct spi_transfer *xfer;
+ if (list_empty(&message->transfers)) + return -EINVAL; + if (!message->complete) + return -EINVAL; + /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where * either MOSI or MISO is missing. They can also be caused by
From: Mark Brown broonie@linaro.org
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 8 -------- 1 file changed, 8 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index ee25670..18bcaf3 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -472,11 +472,6 @@ static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) return 0; }
-static int omap1_spi100k_reset(struct omap1_spi100k *spi100k) -{ - return 0; -} - static int omap1_spi100k_probe(struct platform_device *pdev) { struct spi_master *master; @@ -532,9 +527,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev) goto err2; }
- if (omap1_spi100k_reset(spi100k) < 0) - goto err3; - status = spi_register_master(master); if (status < 0) goto err3;
From: Mark Brown broonie@linaro.org
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 18bcaf3..3b42a4b 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -424,7 +424,6 @@ static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) { struct omap1_spi100k *spi100k; unsigned long flags; - struct spi_transfer *t;
m->actual_length = 0; m->status = -EINPROGRESS; @@ -435,35 +434,6 @@ static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) if (spi100k->state == SPI_SHUTDOWN) return -ESHUTDOWN;
- /* reject invalid messages and transfers */ - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; - - list_for_each_entry(t, &m->transfers, transfer_list) { - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; - - if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ - || (len && !(rx_buf || tx_buf))) { - dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - return -EINVAL; - } - - if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) { - dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", - t->speed_hz, - OMAP1_SPI100K_MAX_FREQ/(1<<16)); - return -EINVAL; - } - - } - spin_lock_irqsave(&spi100k->lock, flags); list_add_tail(&m->queue, &spi100k->msg_queue); queue_work(omap1_spi100k_wq, &spi100k->work); @@ -496,6 +466,8 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->num_chipselect = 2; master->mode_bits = MODEBITS; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16); + master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
platform_set_drvdata(pdev, master);
From: Mark Brown broonie@linaro.org
In preparation for removing the custom workqueue.
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 133 ++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 65 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 3b42a4b..5999285 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -321,10 +321,76 @@ static int omap1_spi100k_setup(struct spi_device *spi) return ret; }
+static int omap1_spi100k_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t = NULL; + int cs_active = 0; + int par_override = 0; + int status = 0; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + status = -EINVAL; + break; + } + if (par_override || t->speed_hz || t->bits_per_word) { + par_override = 1; + status = omap1_spi100k_setup_transfer(spi, t); + if (status < 0) + break; + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; + } + + if (!cs_active) { + omap1_spi100k_force_cs(spi100k, 1); + cs_active = 1; + } + + if (t->len) { + unsigned count; + + count = omap1_spi100k_txrx_pio(spi, t); + m->actual_length += count; + + if (count != t->len) { + status = -EIO; + break; + } + } + + if (t->delay_usecs) + udelay(t->delay_usecs); + + /* ignore the "leave it on after last xfer" hint */ + + if (t->cs_change) { + omap1_spi100k_force_cs(spi100k, 0); + cs_active = 0; + } + } + + /* Restore defaults if they were overriden */ + if (par_override) { + par_override = 0; + status = omap1_spi100k_setup_transfer(spi, NULL); + } + + if (cs_active) + omap1_spi100k_force_cs(spi100k, 0); + + m->status = status; + m->complete(m->context); + + return status; +} + static void omap1_spi100k_work(struct work_struct *work) { struct omap1_spi100k *spi100k; - int status = 0;
spi100k = container_of(work, struct omap1_spi100k, work); spin_lock_irq(&spi100k->lock); @@ -340,11 +406,6 @@ static void omap1_spi100k_work(struct work_struct *work) */ while (!list_empty(&spi100k->msg_queue)) { struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - int cs_active = 0; - struct omap1_spi100k_cs *cs; - int par_override = 0;
m = container_of(spi100k->msg_queue.next, struct spi_message, queue); @@ -352,62 +413,7 @@ static void omap1_spi100k_work(struct work_struct *work) list_del_init(&m->queue); spin_unlock_irq(&spi100k->lock);
- spi = m->spi; - cs = spi->controller_state; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - status = -EINVAL; - break; - } - if (par_override || t->speed_hz || t->bits_per_word) { - par_override = 1; - status = omap1_spi100k_setup_transfer(spi, t); - if (status < 0) - break; - if (!t->speed_hz && !t->bits_per_word) - par_override = 0; - } - - if (!cs_active) { - omap1_spi100k_force_cs(spi100k, 1); - cs_active = 1; - } - - if (t->len) { - unsigned count; - - count = omap1_spi100k_txrx_pio(spi, t); - m->actual_length += count; - - if (count != t->len) { - status = -EIO; - break; - } - } - - if (t->delay_usecs) - udelay(t->delay_usecs); - - /* ignore the "leave it on after last xfer" hint */ - - if (t->cs_change) { - omap1_spi100k_force_cs(spi100k, 0); - cs_active = 0; - } - } - - /* Restore defaults if they were overriden */ - if (par_override) { - par_override = 0; - status = omap1_spi100k_setup_transfer(spi, NULL); - } - - if (cs_active) - omap1_spi100k_force_cs(spi100k, 0); - - m->status = status; - m->complete(m->context); + omap1_spi100k_transfer_one_message(m->spi->master, m);
spin_lock_irq(&spi100k->lock); } @@ -415,9 +421,6 @@ static void omap1_spi100k_work(struct work_struct *work) clk_disable(spi100k->ick); clk_disable(spi100k->fck); spin_unlock_irq(&spi100k->lock); - - if (status < 0) - printk(KERN_WARNING "spi transfer failed with %d\n", status); }
static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
From: Mark Brown broonie@linaro.org
Saves some code duplication and gets us the benefits of any improvements in the core code.
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 103 ++++++++------------------------------------ 1 file changed, 17 insertions(+), 86 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 5999285..d4fcca9 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -83,11 +83,6 @@ #define SPI_SHUTDOWN 1
struct omap1_spi100k { - struct work_struct work; - - /* lock protects queue and registers */ - spinlock_t lock; - struct list_head msg_queue; struct spi_master *master; struct clk *ick; struct clk *fck; @@ -104,8 +99,6 @@ struct omap1_spi100k_cs { int word_len; };
-static struct workqueue_struct *omap1_spi100k_wq; - #define MOD_REG_BIT(val, mask, set) do { \ if (set) \ val |= mask; \ @@ -321,6 +314,16 @@ static int omap1_spi100k_setup(struct spi_device *spi) return ret; }
+static int omap1_spi100k_prepare_hardware(struct spi_master *master) +{ + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + clk_enable(spi100k->ick); + clk_enable(spi100k->fck); + + return 0; +} + static int omap1_spi100k_transfer_one_message(struct spi_master *master, struct spi_message *m) { @@ -383,64 +386,18 @@ static int omap1_spi100k_transfer_one_message(struct spi_master *master, omap1_spi100k_force_cs(spi100k, 0);
m->status = status; - m->complete(m->context); + + spi_finalize_current_message(master);
return status; }
-static void omap1_spi100k_work(struct work_struct *work) +static int omap1_spi100k_unprepare_hardware(struct spi_master *master) { - struct omap1_spi100k *spi100k; - - spi100k = container_of(work, struct omap1_spi100k, work); - spin_lock_irq(&spi100k->lock); - - clk_enable(spi100k->ick); - clk_enable(spi100k->fck); - - /* We only enable one channel at a time -- the one whose message is - * at the head of the queue -- although this controller would gladly - * arbitrate among multiple channels. This corresponds to "single - * channel" master mode. As a side effect, we need to manage the - * chipselect with the FORCE bit ... CS != channel enable. - */ - while (!list_empty(&spi100k->msg_queue)) { - struct spi_message *m; - - m = container_of(spi100k->msg_queue.next, struct spi_message, - queue); - - list_del_init(&m->queue); - spin_unlock_irq(&spi100k->lock); - - omap1_spi100k_transfer_one_message(m->spi->master, m); - - spin_lock_irq(&spi100k->lock); - } + struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
clk_disable(spi100k->ick); clk_disable(spi100k->fck); - spin_unlock_irq(&spi100k->lock); -} - -static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct omap1_spi100k *spi100k; - unsigned long flags; - - m->actual_length = 0; - m->status = -EINPROGRESS; - - spi100k = spi_master_get_devdata(spi->master); - - /* Don't accept new work if we're shutting down */ - if (spi100k->state == SPI_SHUTDOWN) - return -ESHUTDOWN; - - spin_lock_irqsave(&spi100k->lock, flags); - list_add_tail(&m->queue, &spi100k->msg_queue); - queue_work(omap1_spi100k_wq, &spi100k->work); - spin_unlock_irqrestore(&spi100k->lock, flags);
return 0; } @@ -464,7 +421,9 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->bus_num = pdev->id;
master->setup = omap1_spi100k_setup; - master->transfer = omap1_spi100k_transfer; + master->transfer_one_message = omap1_spi100k_transfer_one_message; + master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware; + master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware; master->cleanup = NULL; master->num_chipselect = 2; master->mode_bits = MODEBITS; @@ -484,10 +443,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev) */ spi100k->base = (void __iomem *) pdev->dev.platform_data;
- INIT_WORK(&spi100k->work, omap1_spi100k_work); - - spin_lock_init(&spi100k->lock); - INIT_LIST_HEAD(&spi100k->msg_queue); spi100k->ick = clk_get(&pdev->dev, "ick"); if (IS_ERR(spi100k->ick)) { dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); @@ -524,27 +479,11 @@ static int omap1_spi100k_remove(struct platform_device *pdev) struct spi_master *master; struct omap1_spi100k *spi100k; struct resource *r; - unsigned limit = 500; - unsigned long flags; int status = 0;
master = platform_get_drvdata(pdev); spi100k = spi_master_get_devdata(master);
- spin_lock_irqsave(&spi100k->lock, flags); - - spi100k->state = SPI_SHUTDOWN; - while (!list_empty(&spi100k->msg_queue) && limit--) { - spin_unlock_irqrestore(&spi100k->lock, flags); - msleep(10); - spin_lock_irqsave(&spi100k->lock, flags); - } - - if (!list_empty(&spi100k->msg_queue)) - status = -EBUSY; - - spin_unlock_irqrestore(&spi100k->lock, flags); - if (status != 0) return status;
@@ -569,20 +508,12 @@ static struct platform_driver omap1_spi100k_driver = {
static int __init omap1_spi100k_init(void) { - omap1_spi100k_wq = create_singlethread_workqueue( - omap1_spi100k_driver.driver.name); - - if (omap1_spi100k_wq == NULL) - return -1; - return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); }
static void __exit omap1_spi100k_exit(void) { platform_driver_unregister(&omap1_spi100k_driver); - - destroy_workqueue(omap1_spi100k_wq); }
module_init(omap1_spi100k_init);
From: Mark Brown broonie@linaro.org
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index d4fcca9..691ef3f 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -443,33 +443,29 @@ static int omap1_spi100k_probe(struct platform_device *pdev) */ spi100k->base = (void __iomem *) pdev->dev.platform_data;
- spi100k->ick = clk_get(&pdev->dev, "ick"); + spi100k->ick = devm_clk_get(&pdev->dev, "ick"); if (IS_ERR(spi100k->ick)) { dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); status = PTR_ERR(spi100k->ick); - goto err1; + goto err; }
- spi100k->fck = clk_get(&pdev->dev, "fck"); + spi100k->fck = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(spi100k->fck)) { dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); status = PTR_ERR(spi100k->fck); - goto err2; + goto err; }
status = spi_register_master(master); if (status < 0) - goto err3; + goto err;
spi100k->state = SPI_RUNNING;
return status;
-err3: - clk_put(spi100k->fck); -err2: - clk_put(spi100k->ick); -err1: +err: spi_master_put(master); return status; } @@ -487,9 +483,6 @@ static int omap1_spi100k_remove(struct platform_device *pdev) if (status != 0) return status;
- clk_put(spi100k->fck); - clk_put(spi100k->ick); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_unregister_master(master);
From: Mark Brown broonie@linaro.org
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 691ef3f..3590dd4 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -303,13 +303,13 @@ static int omap1_spi100k_setup(struct spi_device *spi)
spi100k_open(spi->master);
- clk_enable(spi100k->ick); - clk_enable(spi100k->fck); + clk_prepare_enable(spi100k->ick); + clk_prepare_enable(spi100k->fck);
ret = omap1_spi100k_setup_transfer(spi, NULL);
- clk_disable(spi100k->ick); - clk_disable(spi100k->fck); + clk_disable_unprepare(spi100k->ick); + clk_disable_unprepare(spi100k->fck);
return ret; } @@ -318,8 +318,8 @@ static int omap1_spi100k_prepare_hardware(struct spi_master *master) { struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- clk_enable(spi100k->ick); - clk_enable(spi100k->fck); + clk_prepare_enable(spi100k->ick); + clk_prepare_enable(spi100k->fck);
return 0; } @@ -396,8 +396,8 @@ static int omap1_spi100k_unprepare_hardware(struct spi_master *master) { struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- clk_disable(spi100k->ick); - clk_disable(spi100k->fck); + clk_disable_unprepare(spi100k->ick); + clk_disable_unprepare(spi100k->fck);
return 0; }
From: Mark Brown broonie@linaro.org
Signed-off-by: Mark Brown broonie@linaro.org --- drivers/spi/spi-omap-100k.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 3590dd4..cdcc72c 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -495,22 +495,11 @@ static struct platform_driver omap1_spi100k_driver = { .name = "omap1_spi100k", .owner = THIS_MODULE, }, + .probe = omap1_spi100k_probe, .remove = omap1_spi100k_remove, };
- -static int __init omap1_spi100k_init(void) -{ - return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); -} - -static void __exit omap1_spi100k_exit(void) -{ - platform_driver_unregister(&omap1_spi100k_driver); -} - -module_init(omap1_spi100k_init); -module_exit(omap1_spi100k_exit); +module_platform_driver(omap1_spi100k_driver);
MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); MODULE_AUTHOR("Fabrice Crohas fcrohas@gmail.com");
linaro-kernel@lists.linaro.org