Loading drivers/spi/spi-s3c64xx.c +103 −11 Original line number Diff line number Diff line Loading @@ -20,10 +20,12 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> #include <mach/dma.h> Loading Loading @@ -153,6 +155,7 @@ struct s3c64xx_spi_dma_data { * @tx_dmach: Controller's DMA channel for Tx. * @sfr_start: BUS address of SPI controller regs. * @regs: Pointer to ioremap'ed controller registers. * @irq: interrupt * @xfer_completion: To indicate completion of xfer task. * @cur_mode: Stores the active configuration of the controller. * @cur_bpw: Stores the active bits per word settings. Loading Loading @@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work) while (!acquire_dma(sdd)) msleep(10); pm_runtime_get_sync(&sdd->pdev->dev); spin_lock_irqsave(&sdd->lock, flags); while (!list_empty(&sdd->queue) Loading Loading @@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work) /* Free DMA channels */ sdd->ops->release(sdd->rx_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, Loading Loading @@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) goto setup_exit; } pm_runtime_get_sync(&sdd->pdev->dev); /* Check if we can provide the requested rate */ if (!sci->clk_from_cmu) { u32 psr, speed; Loading Loading @@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) err = -EINVAL; } pm_runtime_put(&sdd->pdev->dev); setup_exit: /* setup() returns with device de-selected */ Loading @@ -930,6 +941,33 @@ static int s3c64xx_spi_setup(struct spi_device *spi) 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) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; Loading Loading @@ -970,7 +1008,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; struct spi_master *master; int ret; int ret, irq; char clk_name[16]; if (pdev->id < 0) { Loading Loading @@ -1006,6 +1044,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) 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, sizeof(struct s3c64xx_spi_driver_data)); if (master == NULL) { Loading Loading @@ -1100,10 +1144,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) INIT_WORK(&sdd->work, s3c64xx_spi_work); 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)) { dev_err(&pdev->dev, "cannot register SPI master\n"); ret = -EBUSY; goto err8; goto err9; } dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " Loading @@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) mem_res->end, mem_res->start, sdd->rx_dma.dmach, sdd->tx_dma.dmach); pm_runtime_enable(&pdev->dev); return 0; err9: free_irq(irq, sdd); err8: destroy_workqueue(sdd->workqueue); err7: Loading Loading @@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) struct resource *mem_res; unsigned long flags; pm_runtime_disable(&pdev->dev); spin_lock_irqsave(&sdd->lock, flags); sdd->state |= SUSPND; spin_unlock_irqrestore(&sdd->lock, flags); Loading @@ -1153,6 +1214,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) spi_unregister_master(master); writel(0, sdd->regs + S3C64XX_SPI_INT_EN); free_irq(platform_get_irq(pdev, 0), sdd); destroy_workqueue(sdd->workqueue); clk_disable(sdd->src_clk); Loading @@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) } #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); unsigned long flags; Loading @@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) 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_info *sci = sdd->cntrlr_info; unsigned long flags; Loading @@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev) return 0; } #else #define s3c64xx_spi_suspend NULL #define s3c64xx_spi_resume NULL #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 = { .driver = { .name = "s3c64xx-spi", .owner = THIS_MODULE, .pm = &s3c64xx_spi_pm, }, .remove = s3c64xx_spi_remove, .suspend = s3c64xx_spi_suspend, .resume = s3c64xx_spi_resume, }; MODULE_ALIAS("platform:s3c64xx-spi"); Loading Loading
drivers/spi/spi-s3c64xx.c +103 −11 Original line number Diff line number Diff line Loading @@ -20,10 +20,12 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> #include <mach/dma.h> Loading Loading @@ -153,6 +155,7 @@ struct s3c64xx_spi_dma_data { * @tx_dmach: Controller's DMA channel for Tx. * @sfr_start: BUS address of SPI controller regs. * @regs: Pointer to ioremap'ed controller registers. * @irq: interrupt * @xfer_completion: To indicate completion of xfer task. * @cur_mode: Stores the active configuration of the controller. * @cur_bpw: Stores the active bits per word settings. Loading Loading @@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work) while (!acquire_dma(sdd)) msleep(10); pm_runtime_get_sync(&sdd->pdev->dev); spin_lock_irqsave(&sdd->lock, flags); while (!list_empty(&sdd->queue) Loading Loading @@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work) /* Free DMA channels */ sdd->ops->release(sdd->rx_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, Loading Loading @@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) goto setup_exit; } pm_runtime_get_sync(&sdd->pdev->dev); /* Check if we can provide the requested rate */ if (!sci->clk_from_cmu) { u32 psr, speed; Loading Loading @@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) err = -EINVAL; } pm_runtime_put(&sdd->pdev->dev); setup_exit: /* setup() returns with device de-selected */ Loading @@ -930,6 +941,33 @@ static int s3c64xx_spi_setup(struct spi_device *spi) 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) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; Loading Loading @@ -970,7 +1008,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; struct spi_master *master; int ret; int ret, irq; char clk_name[16]; if (pdev->id < 0) { Loading Loading @@ -1006,6 +1044,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) 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, sizeof(struct s3c64xx_spi_driver_data)); if (master == NULL) { Loading Loading @@ -1100,10 +1144,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) INIT_WORK(&sdd->work, s3c64xx_spi_work); 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)) { dev_err(&pdev->dev, "cannot register SPI master\n"); ret = -EBUSY; goto err8; goto err9; } dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " Loading @@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) mem_res->end, mem_res->start, sdd->rx_dma.dmach, sdd->tx_dma.dmach); pm_runtime_enable(&pdev->dev); return 0; err9: free_irq(irq, sdd); err8: destroy_workqueue(sdd->workqueue); err7: Loading Loading @@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) struct resource *mem_res; unsigned long flags; pm_runtime_disable(&pdev->dev); spin_lock_irqsave(&sdd->lock, flags); sdd->state |= SUSPND; spin_unlock_irqrestore(&sdd->lock, flags); Loading @@ -1153,6 +1214,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) spi_unregister_master(master); writel(0, sdd->regs + S3C64XX_SPI_INT_EN); free_irq(platform_get_irq(pdev, 0), sdd); destroy_workqueue(sdd->workqueue); clk_disable(sdd->src_clk); Loading @@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) } #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); unsigned long flags; Loading @@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) 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_info *sci = sdd->cntrlr_info; unsigned long flags; Loading @@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev) return 0; } #else #define s3c64xx_spi_suspend NULL #define s3c64xx_spi_resume NULL #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 = { .driver = { .name = "s3c64xx-spi", .owner = THIS_MODULE, .pm = &s3c64xx_spi_pm, }, .remove = s3c64xx_spi_remove, .suspend = s3c64xx_spi_suspend, .resume = s3c64xx_spi_resume, }; MODULE_ALIAS("platform:s3c64xx-spi"); Loading