#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#include <asm/io.h>
#include <asm/irq.h>
{
struct driver_data *drv_data = dev_id;
void __iomem *reg = drv_data->ioaddr;
- u32 sccr1_reg = read_SSCR1(reg);
+ u32 sccr1_reg;
u32 mask = drv_data->mask_sr;
u32 status;
+ /*
+ * The IRQ might be shared with other peripherals so we must first
+ * check that are we RPM suspended or not. If we are we assume that
+ * the IRQ was not for us (we shouldn't be RPM suspended when the
+ * interrupt is enabled).
+ */
+ if (pm_runtime_suspended(&drv_data->pdev->dev))
+ return IRQ_NONE;
+
+ sccr1_reg = read_SSCR1(reg);
status = read_SSSR(reg);
/* Ignore possible writes if we don't need to write */
return 0;
}
+static int pxa2xx_spi_prepare_transfer(struct spi_master *master)
+{
+ struct driver_data *drv_data = spi_master_get_devdata(master);
+
+ pm_runtime_get_sync(&drv_data->pdev->dev);
+ return 0;
+}
+
+static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
+{
+ struct driver_data *drv_data = spi_master_get_devdata(master);
+
+ /* Disable the SSP now */
+ write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE,
+ drv_data->ioaddr);
+
+ pm_runtime_mark_last_busy(&drv_data->pdev->dev);
+ pm_runtime_put_autosuspend(&drv_data->pdev->dev);
+ return 0;
+}
+
static int setup_cs(struct spi_device *spi, struct chip_data *chip,
struct pxa2xx_spi_chip *chip_info)
{
master->cleanup = cleanup;
master->setup = setup;
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
+ master->prepare_transfer_hardware = pxa2xx_spi_prepare_transfer;
+ master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
drv_data->ssp_type = ssp->type;
drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT);
goto out_error_clock_enabled;
}
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
return status;
out_error_clock_enabled:
return 0;
ssp = drv_data->ssp;
+ pm_runtime_get_sync(&pdev->dev);
+
/* Disable the SSP at the peripheral and SOC level */
write_SSCR0(0, drv_data->ioaddr);
clk_disable_unprepare(ssp->clk);
if (drv_data->master_info->enable_dma)
pxa2xx_spi_dma_release(drv_data);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
/* Release IRQ */
free_irq(ssp->irq, drv_data);
return 0;
}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int pxa2xx_spi_runtime_suspend(struct device *dev)
+{
+ struct driver_data *drv_data = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(drv_data->ssp->clk);
+ return 0;
+}
+
+static int pxa2xx_spi_runtime_resume(struct device *dev)
+{
+ struct driver_data *drv_data = dev_get_drvdata(dev);
+
+ clk_prepare_enable(drv_data->ssp->clk);
+ return 0;
+}
+#endif
static const struct dev_pm_ops pxa2xx_spi_pm_ops = {
- .suspend = pxa2xx_spi_suspend,
- .resume = pxa2xx_spi_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(pxa2xx_spi_suspend, pxa2xx_spi_resume)
+ SET_RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend,
+ pxa2xx_spi_runtime_resume, NULL)
};
-#endif
static struct platform_driver driver = {
.driver = {
.name = "pxa2xx-spi",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &pxa2xx_spi_pm_ops,
-#endif
},
.probe = pxa2xx_spi_probe,
.remove = pxa2xx_spi_remove,