Commit bc98d13f authored by Steven King's avatar Steven King Committed by Grant Likely

spi: refactor spi-coldfire-qspi to use SPI queue framework.

Use the new SPI queue framework; remove use of workqueue, replace
mcfqspi_transfer with mcfqspi_transfer_one_message, add
mcfqspi_prepare_transfer_hw and mcfqspi_unprepare_transfer_hw, update power
management routines.
Signed-off-by: default avatarSteven King <sfking@fdwdc.com>
Acked-by: default avatarGreg Ungerer <gerg@snapgear.com>
Acked-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
parent 5fda88f5
...@@ -25,12 +25,12 @@ ...@@ -25,12 +25,12 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/pm_runtime.h>
#include <asm/coldfire.h> #include <asm/coldfire.h>
#include <asm/mcfsim.h> #include <asm/mcfsim.h>
...@@ -78,10 +78,7 @@ struct mcfqspi { ...@@ -78,10 +78,7 @@ struct mcfqspi {
wait_queue_head_t waitq; wait_queue_head_t waitq;
struct work_struct work; struct device *dev;
struct workqueue_struct *workq;
spinlock_t lock;
struct list_head msgq;
}; };
static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val) static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val)
...@@ -303,120 +300,80 @@ static void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count, ...@@ -303,120 +300,80 @@ static void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count,
} }
} }
static void mcfqspi_work(struct work_struct *work) static int mcfqspi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{ {
struct mcfqspi *mcfqspi = container_of(work, struct mcfqspi, work); struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
unsigned long flags; struct spi_device *spi = msg->spi;
struct spi_transfer *t;
spin_lock_irqsave(&mcfqspi->lock, flags); int status = 0;
while (!list_empty(&mcfqspi->msgq)) {
struct spi_message *msg; list_for_each_entry(t, &msg->transfers, transfer_list) {
struct spi_device *spi; bool cs_high = spi->mode & SPI_CS_HIGH;
struct spi_transfer *xfer; u16 qmr = MCFQSPI_QMR_MSTR;
int status = 0;
if (t->bits_per_word)
msg = container_of(mcfqspi->msgq.next, struct spi_message, qmr |= t->bits_per_word << 10;
queue); else
qmr |= spi->bits_per_word << 10;
list_del_init(&msg->queue); if (spi->mode & SPI_CPHA)
spin_unlock_irqrestore(&mcfqspi->lock, flags); qmr |= MCFQSPI_QMR_CPHA;
if (spi->mode & SPI_CPOL)
spi = msg->spi; qmr |= MCFQSPI_QMR_CPOL;
if (t->speed_hz)
list_for_each_entry(xfer, &msg->transfers, transfer_list) { qmr |= mcfqspi_qmr_baud(t->speed_hz);
bool cs_high = spi->mode & SPI_CS_HIGH; else
u16 qmr = MCFQSPI_QMR_MSTR; qmr |= mcfqspi_qmr_baud(spi->max_speed_hz);
mcfqspi_wr_qmr(mcfqspi, qmr);
if (xfer->bits_per_word)
qmr |= xfer->bits_per_word << 10; mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high);
else
qmr |= spi->bits_per_word << 10; mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE);
if (spi->mode & SPI_CPHA) if ((t->bits_per_word ? t->bits_per_word :
qmr |= MCFQSPI_QMR_CPHA; spi->bits_per_word) == 8)
if (spi->mode & SPI_CPOL) mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf,
qmr |= MCFQSPI_QMR_CPOL; t->rx_buf);
if (xfer->speed_hz) else
qmr |= mcfqspi_qmr_baud(xfer->speed_hz); mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf,
else t->rx_buf);
qmr |= mcfqspi_qmr_baud(spi->max_speed_hz); mcfqspi_wr_qir(mcfqspi, 0);
mcfqspi_wr_qmr(mcfqspi, qmr);
if (t->delay_usecs)
mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high); udelay(t->delay_usecs);
if (t->cs_change) {
mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE); if (!list_is_last(&t->transfer_list, &msg->transfers))
if ((xfer->bits_per_word ? xfer->bits_per_word : mcfqspi_cs_deselect(mcfqspi, spi->chip_select,
spi->bits_per_word) == 8) cs_high);
mcfqspi_transfer_msg8(mcfqspi, xfer->len, } else {
xfer->tx_buf, if (list_is_last(&t->transfer_list, &msg->transfers))
xfer->rx_buf); mcfqspi_cs_deselect(mcfqspi, spi->chip_select,
else cs_high);
mcfqspi_transfer_msg16(mcfqspi, xfer->len / 2,
xfer->tx_buf,
xfer->rx_buf);
mcfqspi_wr_qir(mcfqspi, 0);
if (xfer->delay_usecs)
udelay(xfer->delay_usecs);
if (xfer->cs_change) {
if (!list_is_last(&xfer->transfer_list,
&msg->transfers))
mcfqspi_cs_deselect(mcfqspi,
spi->chip_select,
cs_high);
} else {
if (list_is_last(&xfer->transfer_list,
&msg->transfers))
mcfqspi_cs_deselect(mcfqspi,
spi->chip_select,
cs_high);
}
msg->actual_length += xfer->len;
} }
msg->status = status; msg->actual_length += t->len;
msg->complete(msg->context);
spin_lock_irqsave(&mcfqspi->lock, flags);
} }
spin_unlock_irqrestore(&mcfqspi->lock, flags); msg->status = status;
spi_finalize_current_message(master);
return status;
} }
static int mcfqspi_transfer(struct spi_device *spi, struct spi_message *msg) static int mcfqspi_prepare_transfer_hw(struct spi_master *master)
{ {
struct mcfqspi *mcfqspi; struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
struct spi_transfer *xfer;
unsigned long flags;
mcfqspi = spi_master_get_devdata(spi->master);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
if (xfer->bits_per_word && ((xfer->bits_per_word < 8)
|| (xfer->bits_per_word > 16))) {
dev_dbg(&spi->dev,
"%d bits per word is not supported\n",
xfer->bits_per_word);
goto fail;
}
if (xfer->speed_hz) {
u32 real_speed = MCFQSPI_BUSCLK /
mcfqspi_qmr_baud(xfer->speed_hz);
if (real_speed != xfer->speed_hz)
dev_dbg(&spi->dev,
"using speed %d instead of %d\n",
real_speed, xfer->speed_hz);
}
}
msg->status = -EINPROGRESS;
msg->actual_length = 0;
spin_lock_irqsave(&mcfqspi->lock, flags); pm_runtime_get_sync(mcfqspi->dev);
list_add_tail(&msg->queue, &mcfqspi->msgq);
queue_work(mcfqspi->workq, &mcfqspi->work); return 0;
spin_unlock_irqrestore(&mcfqspi->lock, flags); }
static int mcfqspi_unprepare_transfer_hw(struct spi_master *master)
{
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
pm_runtime_put_sync(mcfqspi->dev);
return 0; return 0;
fail:
msg->status = -EINVAL;
return -EINVAL;
} }
static int mcfqspi_setup(struct spi_device *spi) static int mcfqspi_setup(struct spi_device *spi)
...@@ -502,21 +459,10 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev) ...@@ -502,21 +459,10 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev)
} }
clk_enable(mcfqspi->clk); clk_enable(mcfqspi->clk);
mcfqspi->workq = create_singlethread_workqueue(dev_name(master->dev.parent));
if (!mcfqspi->workq) {
dev_dbg(&pdev->dev, "create_workqueue failed\n");
status = -ENOMEM;
goto fail4;
}
INIT_WORK(&mcfqspi->work, mcfqspi_work);
spin_lock_init(&mcfqspi->lock);
INIT_LIST_HEAD(&mcfqspi->msgq);
init_waitqueue_head(&mcfqspi->waitq);
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
if (!pdata) { if (!pdata) {
dev_dbg(&pdev->dev, "platform data is missing\n"); dev_dbg(&pdev->dev, "platform data is missing\n");
goto fail5; goto fail4;
} }
master->bus_num = pdata->bus_num; master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->num_chipselect; master->num_chipselect = pdata->num_chipselect;
...@@ -525,28 +471,33 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev) ...@@ -525,28 +471,33 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev)
status = mcfqspi_cs_setup(mcfqspi); status = mcfqspi_cs_setup(mcfqspi);
if (status) { if (status) {
dev_dbg(&pdev->dev, "error initializing cs_control\n"); dev_dbg(&pdev->dev, "error initializing cs_control\n");
goto fail5; goto fail4;
} }
init_waitqueue_head(&mcfqspi->waitq);
mcfqspi->dev = &pdev->dev;
master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA;
master->setup = mcfqspi_setup; master->setup = mcfqspi_setup;
master->transfer = mcfqspi_transfer; master->transfer_one_message = mcfqspi_transfer_one_message;
master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw;
master->unprepare_transfer_hardware = mcfqspi_unprepare_transfer_hw;
platform_set_drvdata(pdev, master); platform_set_drvdata(pdev, master);
status = spi_register_master(master); status = spi_register_master(master);
if (status) { if (status) {
dev_dbg(&pdev->dev, "spi_register_master failed\n"); dev_dbg(&pdev->dev, "spi_register_master failed\n");
goto fail6; goto fail5;
} }
pm_runtime_enable(mcfqspi->dev);
dev_info(&pdev->dev, "Coldfire QSPI bus driver\n"); dev_info(&pdev->dev, "Coldfire QSPI bus driver\n");
return 0; return 0;
fail6:
mcfqspi_cs_teardown(mcfqspi);
fail5: fail5:
destroy_workqueue(mcfqspi->workq); mcfqspi_cs_teardown(mcfqspi);
fail4: fail4:
clk_disable(mcfqspi->clk); clk_disable(mcfqspi->clk);
clk_put(mcfqspi->clk); clk_put(mcfqspi->clk);
...@@ -570,12 +521,12 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev) ...@@ -570,12 +521,12 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev)
struct mcfqspi *mcfqspi = spi_master_get_devdata(master); struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pm_runtime_disable(mcfqspi->dev);
/* disable the hardware (set the baud rate to 0) */ /* disable the hardware (set the baud rate to 0) */
mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
mcfqspi_cs_teardown(mcfqspi); mcfqspi_cs_teardown(mcfqspi);
destroy_workqueue(mcfqspi->workq);
clk_disable(mcfqspi->clk); clk_disable(mcfqspi->clk);
clk_put(mcfqspi->clk); clk_put(mcfqspi->clk);
free_irq(mcfqspi->irq, mcfqspi); free_irq(mcfqspi->irq, mcfqspi);
...@@ -587,11 +538,13 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev) ...@@ -587,11 +538,13 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int mcfqspi_suspend(struct device *dev) static int mcfqspi_suspend(struct device *dev)
{ {
struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev)); struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
spi_master_suspend(master);
clk_disable(mcfqspi->clk); clk_disable(mcfqspi->clk);
...@@ -600,27 +553,47 @@ static int mcfqspi_suspend(struct device *dev) ...@@ -600,27 +553,47 @@ static int mcfqspi_suspend(struct device *dev)
static int mcfqspi_resume(struct device *dev) static int mcfqspi_resume(struct device *dev)
{ {
struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev)); struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
spi_master_resume(master);
clk_enable(mcfqspi->clk); clk_enable(mcfqspi->clk);
return 0; return 0;
} }
#endif
static struct dev_pm_ops mcfqspi_dev_pm_ops = { #ifdef CONFIG_PM_RUNTIME
.suspend = mcfqspi_suspend, static int mcfqspi_runtime_suspend(struct device *dev)
.resume = mcfqspi_resume, {
}; struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
#define MCFQSPI_DEV_PM_OPS (&mcfqspi_dev_pm_ops) clk_disable(mcfqspi->clk);
#else
#define MCFQSPI_DEV_PM_OPS NULL return 0;
}
static int mcfqspi_runtime_resume(struct device *dev)
{
struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
clk_enable(mcfqspi->clk);
return 0;
}
#endif #endif
static const struct dev_pm_ops mcfqspi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume)
SET_RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume,
NULL)
};
static struct platform_driver mcfqspi_driver = { static struct platform_driver mcfqspi_driver = {
.driver.name = DRIVER_NAME, .driver.name = DRIVER_NAME,
.driver.owner = THIS_MODULE, .driver.owner = THIS_MODULE,
.driver.pm = MCFQSPI_DEV_PM_OPS, .driver.pm = &mcfqspi_pm,
.probe = mcfqspi_probe, .probe = mcfqspi_probe,
.remove = __devexit_p(mcfqspi_remove), .remove = __devexit_p(mcfqspi_remove),
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment