Commit bc266185 authored by Grant Likely's avatar Grant Likely

Merge branch 'spi/s3c64xx' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc

Conflicts:
	drivers/spi/spi-s3c64xx.c
parents dcd6c922 b97b6621
...@@ -20,10 +20,12 @@ ...@@ -20,10 +20,12 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <mach/dma.h> #include <mach/dma.h>
...@@ -153,6 +155,7 @@ struct s3c64xx_spi_dma_data { ...@@ -153,6 +155,7 @@ struct s3c64xx_spi_dma_data {
* @tx_dmach: Controller's DMA channel for Tx. * @tx_dmach: Controller's DMA channel for Tx.
* @sfr_start: BUS address of SPI controller regs. * @sfr_start: BUS address of SPI controller regs.
* @regs: Pointer to ioremap'ed controller registers. * @regs: Pointer to ioremap'ed controller registers.
* @irq: interrupt
* @xfer_completion: To indicate completion of xfer task. * @xfer_completion: To indicate completion of xfer task.
* @cur_mode: Stores the active configuration of the controller. * @cur_mode: Stores the active configuration of the controller.
* @cur_bpw: Stores the active bits per word settings. * @cur_bpw: Stores the active bits per word settings.
...@@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work) ...@@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
while (!acquire_dma(sdd)) while (!acquire_dma(sdd))
msleep(10); msleep(10);
pm_runtime_get_sync(&sdd->pdev->dev);
spin_lock_irqsave(&sdd->lock, flags); spin_lock_irqsave(&sdd->lock, flags);
while (!list_empty(&sdd->queue) while (!list_empty(&sdd->queue)
...@@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work) ...@@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
/* Free DMA channels */ /* Free DMA channels */
sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
pm_runtime_put(&sdd->pdev->dev);
} }
static int s3c64xx_spi_transfer(struct spi_device *spi, static int s3c64xx_spi_transfer(struct spi_device *spi,
...@@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
goto setup_exit; goto setup_exit;
} }
pm_runtime_get_sync(&sdd->pdev->dev);
/* Check if we can provide the requested rate */ /* Check if we can provide the requested rate */
if (!sci->clk_from_cmu) { if (!sci->clk_from_cmu) {
u32 psr, speed; u32 psr, speed;
...@@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
err = -EINVAL; err = -EINVAL;
} }
pm_runtime_put(&sdd->pdev->dev);
setup_exit: setup_exit:
/* setup() returns with device de-selected */ /* setup() returns with device de-selected */
...@@ -930,6 +941,33 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -930,6 +941,33 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
return err; return err;
} }
static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
{
struct s3c64xx_spi_driver_data *sdd = data;
struct spi_master *spi = sdd->master;
unsigned int val;
val = readl(sdd->regs + S3C64XX_SPI_PENDING_CLR);
val &= S3C64XX_SPI_PND_RX_OVERRUN_CLR |
S3C64XX_SPI_PND_RX_UNDERRUN_CLR |
S3C64XX_SPI_PND_TX_OVERRUN_CLR |
S3C64XX_SPI_PND_TX_UNDERRUN_CLR;
writel(val, sdd->regs + S3C64XX_SPI_PENDING_CLR);
if (val & S3C64XX_SPI_PND_RX_OVERRUN_CLR)
dev_err(&spi->dev, "RX overrun\n");
if (val & S3C64XX_SPI_PND_RX_UNDERRUN_CLR)
dev_err(&spi->dev, "RX underrun\n");
if (val & S3C64XX_SPI_PND_TX_OVERRUN_CLR)
dev_err(&spi->dev, "TX overrun\n");
if (val & S3C64XX_SPI_PND_TX_UNDERRUN_CLR)
dev_err(&spi->dev, "TX underrun\n");
return IRQ_HANDLED;
}
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
{ {
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
...@@ -970,7 +1008,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -970,7 +1008,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci; struct s3c64xx_spi_info *sci;
struct spi_master *master; struct spi_master *master;
int ret; int ret, irq;
char clk_name[16]; char clk_name[16];
if (pdev->id < 0) { if (pdev->id < 0) {
...@@ -1006,6 +1044,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1006,6 +1044,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
return irq;
}
master = spi_alloc_master(&pdev->dev, master = spi_alloc_master(&pdev->dev,
sizeof(struct s3c64xx_spi_driver_data)); sizeof(struct s3c64xx_spi_driver_data));
if (master == NULL) { if (master == NULL) {
...@@ -1100,10 +1144,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1100,10 +1144,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
INIT_WORK(&sdd->work, s3c64xx_spi_work); INIT_WORK(&sdd->work, s3c64xx_spi_work);
INIT_LIST_HEAD(&sdd->queue); INIT_LIST_HEAD(&sdd->queue);
ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
goto err8;
}
writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
sdd->regs + S3C64XX_SPI_INT_EN);
if (spi_register_master(master)) { if (spi_register_master(master)) {
dev_err(&pdev->dev, "cannot register SPI master\n"); dev_err(&pdev->dev, "cannot register SPI master\n");
ret = -EBUSY; ret = -EBUSY;
goto err8; goto err9;
} }
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
...@@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
mem_res->end, mem_res->start, mem_res->end, mem_res->start,
sdd->rx_dma.dmach, sdd->tx_dma.dmach); sdd->rx_dma.dmach, sdd->tx_dma.dmach);
pm_runtime_enable(&pdev->dev);
return 0; return 0;
err9:
free_irq(irq, sdd);
err8: err8:
destroy_workqueue(sdd->workqueue); destroy_workqueue(sdd->workqueue);
err7: err7:
...@@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
struct resource *mem_res; struct resource *mem_res;
unsigned long flags; unsigned long flags;
pm_runtime_disable(&pdev->dev);
spin_lock_irqsave(&sdd->lock, flags); spin_lock_irqsave(&sdd->lock, flags);
sdd->state |= SUSPND; sdd->state |= SUSPND;
spin_unlock_irqrestore(&sdd->lock, flags); spin_unlock_irqrestore(&sdd->lock, flags);
...@@ -1153,6 +1214,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1153,6 +1214,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
spi_unregister_master(master); spi_unregister_master(master);
writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
free_irq(platform_get_irq(pdev, 0), sdd);
destroy_workqueue(sdd->workqueue); destroy_workqueue(sdd->workqueue);
clk_disable(sdd->src_clk); clk_disable(sdd->src_clk);
...@@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) static int s3c64xx_spi_suspend(struct device *dev)
{ {
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
unsigned long flags; unsigned long flags;
...@@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
return 0; return 0;
} }
static int s3c64xx_spi_resume(struct platform_device *pdev) static int s3c64xx_spi_resume(struct device *dev)
{ {
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
unsigned long flags; unsigned long flags;
...@@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev) ...@@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev)
return 0; return 0;
} }
#else
#define s3c64xx_spi_suspend NULL
#define s3c64xx_spi_resume NULL
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
static int s3c64xx_spi_runtime_suspend(struct device *dev)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
clk_disable(sdd->clk);
clk_disable(sdd->src_clk);
return 0;
}
static int s3c64xx_spi_runtime_resume(struct device *dev)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
clk_enable(sdd->src_clk);
clk_enable(sdd->clk);
return 0;
}
#endif /* CONFIG_PM_RUNTIME */
static const struct dev_pm_ops s3c64xx_spi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
s3c64xx_spi_runtime_resume, NULL)
};
static struct platform_driver s3c64xx_spi_driver = { static struct platform_driver s3c64xx_spi_driver = {
.driver = { .driver = {
.name = "s3c64xx-spi", .name = "s3c64xx-spi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &s3c64xx_spi_pm,
}, },
.remove = s3c64xx_spi_remove, .remove = s3c64xx_spi_remove,
.suspend = s3c64xx_spi_suspend,
.resume = s3c64xx_spi_resume,
}; };
MODULE_ALIAS("platform:s3c64xx-spi"); MODULE_ALIAS("platform:s3c64xx-spi");
......
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