diff options
author | Mika Westerberg <mika.westerberg@linux.intel.com> | 2013-01-22 05:26:30 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-02-08 07:15:32 -0500 |
commit | 7d94a50585884180a487ce17e8ddbfa37f7694b6 (patch) | |
tree | f165ad3bce743f1a902fbe7deaed07fa465403ba /drivers/spi | |
parent | 5928808ef623347e0d4aa22327b992e9e125b6ad (diff) |
spi/pxa2xx: add support for runtime PM
Drivers should put the device into low power states proactively whenever the
device is not in use. Thus implement support for runtime PM and use the
autosuspend feature to make sure that we can still perform well in case we see
lots of SPI traffic within short period of time.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: Lu Cao <lucao@marvell.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 73 |
1 files changed, 67 insertions, 6 deletions
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index c3d78073d1ad..7f8f939835fd 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/gpio.h> | 30 | #include <linux/gpio.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/clk.h> | 32 | #include <linux/clk.h> |
33 | #include <linux/pm_runtime.h> | ||
33 | 34 | ||
34 | #include <asm/io.h> | 35 | #include <asm/io.h> |
35 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
@@ -417,10 +418,20 @@ static irqreturn_t ssp_int(int irq, void *dev_id) | |||
417 | { | 418 | { |
418 | struct driver_data *drv_data = dev_id; | 419 | struct driver_data *drv_data = dev_id; |
419 | void __iomem *reg = drv_data->ioaddr; | 420 | void __iomem *reg = drv_data->ioaddr; |
420 | u32 sccr1_reg = read_SSCR1(reg); | 421 | u32 sccr1_reg; |
421 | u32 mask = drv_data->mask_sr; | 422 | u32 mask = drv_data->mask_sr; |
422 | u32 status; | 423 | u32 status; |
423 | 424 | ||
425 | /* | ||
426 | * The IRQ might be shared with other peripherals so we must first | ||
427 | * check that are we RPM suspended or not. If we are we assume that | ||
428 | * the IRQ was not for us (we shouldn't be RPM suspended when the | ||
429 | * interrupt is enabled). | ||
430 | */ | ||
431 | if (pm_runtime_suspended(&drv_data->pdev->dev)) | ||
432 | return IRQ_NONE; | ||
433 | |||
434 | sccr1_reg = read_SSCR1(reg); | ||
424 | status = read_SSSR(reg); | 435 | status = read_SSSR(reg); |
425 | 436 | ||
426 | /* Ignore possible writes if we don't need to write */ | 437 | /* Ignore possible writes if we don't need to write */ |
@@ -678,6 +689,27 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master, | |||
678 | return 0; | 689 | return 0; |
679 | } | 690 | } |
680 | 691 | ||
692 | static int pxa2xx_spi_prepare_transfer(struct spi_master *master) | ||
693 | { | ||
694 | struct driver_data *drv_data = spi_master_get_devdata(master); | ||
695 | |||
696 | pm_runtime_get_sync(&drv_data->pdev->dev); | ||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) | ||
701 | { | ||
702 | struct driver_data *drv_data = spi_master_get_devdata(master); | ||
703 | |||
704 | /* Disable the SSP now */ | ||
705 | write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE, | ||
706 | drv_data->ioaddr); | ||
707 | |||
708 | pm_runtime_mark_last_busy(&drv_data->pdev->dev); | ||
709 | pm_runtime_put_autosuspend(&drv_data->pdev->dev); | ||
710 | return 0; | ||
711 | } | ||
712 | |||
681 | static int setup_cs(struct spi_device *spi, struct chip_data *chip, | 713 | static int setup_cs(struct spi_device *spi, struct chip_data *chip, |
682 | struct pxa2xx_spi_chip *chip_info) | 714 | struct pxa2xx_spi_chip *chip_info) |
683 | { | 715 | { |
@@ -915,6 +947,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) | |||
915 | master->cleanup = cleanup; | 947 | master->cleanup = cleanup; |
916 | master->setup = setup; | 948 | master->setup = setup; |
917 | master->transfer_one_message = pxa2xx_spi_transfer_one_message; | 949 | master->transfer_one_message = pxa2xx_spi_transfer_one_message; |
950 | master->prepare_transfer_hardware = pxa2xx_spi_prepare_transfer; | ||
951 | master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; | ||
918 | 952 | ||
919 | drv_data->ssp_type = ssp->type; | 953 | drv_data->ssp_type = ssp->type; |
920 | drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); | 954 | drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); |
@@ -980,6 +1014,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) | |||
980 | goto out_error_clock_enabled; | 1014 | goto out_error_clock_enabled; |
981 | } | 1015 | } |
982 | 1016 | ||
1017 | pm_runtime_set_autosuspend_delay(&pdev->dev, 50); | ||
1018 | pm_runtime_use_autosuspend(&pdev->dev); | ||
1019 | pm_runtime_set_active(&pdev->dev); | ||
1020 | pm_runtime_enable(&pdev->dev); | ||
1021 | |||
983 | return status; | 1022 | return status; |
984 | 1023 | ||
985 | out_error_clock_enabled: | 1024 | out_error_clock_enabled: |
@@ -1002,6 +1041,8 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) | |||
1002 | return 0; | 1041 | return 0; |
1003 | ssp = drv_data->ssp; | 1042 | ssp = drv_data->ssp; |
1004 | 1043 | ||
1044 | pm_runtime_get_sync(&pdev->dev); | ||
1045 | |||
1005 | /* Disable the SSP at the peripheral and SOC level */ | 1046 | /* Disable the SSP at the peripheral and SOC level */ |
1006 | write_SSCR0(0, drv_data->ioaddr); | 1047 | write_SSCR0(0, drv_data->ioaddr); |
1007 | clk_disable_unprepare(ssp->clk); | 1048 | clk_disable_unprepare(ssp->clk); |
@@ -1010,6 +1051,9 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) | |||
1010 | if (drv_data->master_info->enable_dma) | 1051 | if (drv_data->master_info->enable_dma) |
1011 | pxa2xx_spi_dma_release(drv_data); | 1052 | pxa2xx_spi_dma_release(drv_data); |
1012 | 1053 | ||
1054 | pm_runtime_put_noidle(&pdev->dev); | ||
1055 | pm_runtime_disable(&pdev->dev); | ||
1056 | |||
1013 | /* Release IRQ */ | 1057 | /* Release IRQ */ |
1014 | free_irq(ssp->irq, drv_data); | 1058 | free_irq(ssp->irq, drv_data); |
1015 | 1059 | ||
@@ -1069,20 +1113,37 @@ static int pxa2xx_spi_resume(struct device *dev) | |||
1069 | 1113 | ||
1070 | return 0; | 1114 | return 0; |
1071 | } | 1115 | } |
1116 | #endif | ||
1117 | |||
1118 | #ifdef CONFIG_PM_RUNTIME | ||
1119 | static int pxa2xx_spi_runtime_suspend(struct device *dev) | ||
1120 | { | ||
1121 | struct driver_data *drv_data = dev_get_drvdata(dev); | ||
1122 | |||
1123 | clk_disable_unprepare(drv_data->ssp->clk); | ||
1124 | return 0; | ||
1125 | } | ||
1126 | |||
1127 | static int pxa2xx_spi_runtime_resume(struct device *dev) | ||
1128 | { | ||
1129 | struct driver_data *drv_data = dev_get_drvdata(dev); | ||
1130 | |||
1131 | clk_prepare_enable(drv_data->ssp->clk); | ||
1132 | return 0; | ||
1133 | } | ||
1134 | #endif | ||
1072 | 1135 | ||
1073 | static const struct dev_pm_ops pxa2xx_spi_pm_ops = { | 1136 | static const struct dev_pm_ops pxa2xx_spi_pm_ops = { |
1074 | .suspend = pxa2xx_spi_suspend, | 1137 | SET_SYSTEM_SLEEP_PM_OPS(pxa2xx_spi_suspend, pxa2xx_spi_resume) |
1075 | .resume = pxa2xx_spi_resume, | 1138 | SET_RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, |
1139 | pxa2xx_spi_runtime_resume, NULL) | ||
1076 | }; | 1140 | }; |
1077 | #endif | ||
1078 | 1141 | ||
1079 | static struct platform_driver driver = { | 1142 | static struct platform_driver driver = { |
1080 | .driver = { | 1143 | .driver = { |
1081 | .name = "pxa2xx-spi", | 1144 | .name = "pxa2xx-spi", |
1082 | .owner = THIS_MODULE, | 1145 | .owner = THIS_MODULE, |
1083 | #ifdef CONFIG_PM | ||
1084 | .pm = &pxa2xx_spi_pm_ops, | 1146 | .pm = &pxa2xx_spi_pm_ops, |
1085 | #endif | ||
1086 | }, | 1147 | }, |
1087 | .probe = pxa2xx_spi_probe, | 1148 | .probe = pxa2xx_spi_probe, |
1088 | .remove = pxa2xx_spi_remove, | 1149 | .remove = pxa2xx_spi_remove, |