diff options
author | Barry Song <21cnbao@gmail.com> | 2013-07-30 05:44:34 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2013-08-13 07:31:01 -0400 |
commit | 2a76689bcaecf35391e6ddc8a00c09c79d6d5857 (patch) | |
tree | 8723262ec9fdcc44d057636bd461e8e998f19e72 /drivers/dma/sirf-dma.c | |
parent | d4adcc0160404c3237fe6ffa09dd2dd039dd3975 (diff) |
dmaengine: sirf: add PM entries for sleep and runtime
this patch adds PM ops entries in sirf-dma drivers, so that this
driver can support suspend/resume, hibernation and runtime PM.
while suspending, sirf-dma will lose all registers, so we save
them at suspend and restore in resume for active channels.
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/sirf-dma.c')
-rw-r--r-- | drivers/dma/sirf-dma.c | 132 |
1 files changed, 129 insertions, 3 deletions
diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 973ecd05dffd..6aec3ad814d3 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/dmaengine.h> | 10 | #include <linux/dmaengine.h> |
11 | #include <linux/dma-mapping.h> | 11 | #include <linux/dma-mapping.h> |
12 | #include <linux/pm_runtime.h> | ||
12 | #include <linux/interrupt.h> | 13 | #include <linux/interrupt.h> |
13 | #include <linux/io.h> | 14 | #include <linux/io.h> |
14 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
@@ -73,6 +74,11 @@ struct sirfsoc_dma_chan { | |||
73 | int mode; | 74 | int mode; |
74 | }; | 75 | }; |
75 | 76 | ||
77 | struct sirfsoc_dma_regs { | ||
78 | u32 ctrl[SIRFSOC_DMA_CHANNELS]; | ||
79 | u32 interrupt_en; | ||
80 | }; | ||
81 | |||
76 | struct sirfsoc_dma { | 82 | struct sirfsoc_dma { |
77 | struct dma_device dma; | 83 | struct dma_device dma; |
78 | struct tasklet_struct tasklet; | 84 | struct tasklet_struct tasklet; |
@@ -81,10 +87,13 @@ struct sirfsoc_dma { | |||
81 | int irq; | 87 | int irq; |
82 | struct clk *clk; | 88 | struct clk *clk; |
83 | bool is_marco; | 89 | bool is_marco; |
90 | struct sirfsoc_dma_regs regs_save; | ||
84 | }; | 91 | }; |
85 | 92 | ||
86 | #define DRV_NAME "sirfsoc_dma" | 93 | #define DRV_NAME "sirfsoc_dma" |
87 | 94 | ||
95 | static int sirfsoc_dma_runtime_suspend(struct device *dev); | ||
96 | |||
88 | /* Convert struct dma_chan to struct sirfsoc_dma_chan */ | 97 | /* Convert struct dma_chan to struct sirfsoc_dma_chan */ |
89 | static inline | 98 | static inline |
90 | struct sirfsoc_dma_chan *dma_chan_to_sirfsoc_dma_chan(struct dma_chan *c) | 99 | struct sirfsoc_dma_chan *dma_chan_to_sirfsoc_dma_chan(struct dma_chan *c) |
@@ -393,6 +402,8 @@ static int sirfsoc_dma_alloc_chan_resources(struct dma_chan *chan) | |||
393 | LIST_HEAD(descs); | 402 | LIST_HEAD(descs); |
394 | int i; | 403 | int i; |
395 | 404 | ||
405 | pm_runtime_get_sync(sdma->dma.dev); | ||
406 | |||
396 | /* Alloc descriptors for this channel */ | 407 | /* Alloc descriptors for this channel */ |
397 | for (i = 0; i < SIRFSOC_DMA_DESCRIPTORS; i++) { | 408 | for (i = 0; i < SIRFSOC_DMA_DESCRIPTORS; i++) { |
398 | sdesc = kzalloc(sizeof(*sdesc), GFP_KERNEL); | 409 | sdesc = kzalloc(sizeof(*sdesc), GFP_KERNEL); |
@@ -425,6 +436,7 @@ static int sirfsoc_dma_alloc_chan_resources(struct dma_chan *chan) | |||
425 | static void sirfsoc_dma_free_chan_resources(struct dma_chan *chan) | 436 | static void sirfsoc_dma_free_chan_resources(struct dma_chan *chan) |
426 | { | 437 | { |
427 | struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); | 438 | struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); |
439 | struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan); | ||
428 | struct sirfsoc_dma_desc *sdesc, *tmp; | 440 | struct sirfsoc_dma_desc *sdesc, *tmp; |
429 | unsigned long flags; | 441 | unsigned long flags; |
430 | LIST_HEAD(descs); | 442 | LIST_HEAD(descs); |
@@ -445,6 +457,8 @@ static void sirfsoc_dma_free_chan_resources(struct dma_chan *chan) | |||
445 | /* Free descriptors */ | 457 | /* Free descriptors */ |
446 | list_for_each_entry_safe(sdesc, tmp, &descs, node) | 458 | list_for_each_entry_safe(sdesc, tmp, &descs, node) |
447 | kfree(sdesc); | 459 | kfree(sdesc); |
460 | |||
461 | pm_runtime_put(sdma->dma.dev); | ||
448 | } | 462 | } |
449 | 463 | ||
450 | /* Send pending descriptor to hardware */ | 464 | /* Send pending descriptor to hardware */ |
@@ -723,14 +737,14 @@ static int sirfsoc_dma_probe(struct platform_device *op) | |||
723 | 737 | ||
724 | tasklet_init(&sdma->tasklet, sirfsoc_dma_tasklet, (unsigned long)sdma); | 738 | tasklet_init(&sdma->tasklet, sirfsoc_dma_tasklet, (unsigned long)sdma); |
725 | 739 | ||
726 | clk_prepare_enable(sdma->clk); | ||
727 | |||
728 | /* Register DMA engine */ | 740 | /* Register DMA engine */ |
729 | dev_set_drvdata(dev, sdma); | 741 | dev_set_drvdata(dev, sdma); |
742 | |||
730 | ret = dma_async_device_register(dma); | 743 | ret = dma_async_device_register(dma); |
731 | if (ret) | 744 | if (ret) |
732 | goto free_irq; | 745 | goto free_irq; |
733 | 746 | ||
747 | pm_runtime_enable(&op->dev); | ||
734 | dev_info(dev, "initialized SIRFSOC DMAC driver\n"); | 748 | dev_info(dev, "initialized SIRFSOC DMAC driver\n"); |
735 | 749 | ||
736 | return 0; | 750 | return 0; |
@@ -747,13 +761,124 @@ static int sirfsoc_dma_remove(struct platform_device *op) | |||
747 | struct device *dev = &op->dev; | 761 | struct device *dev = &op->dev; |
748 | struct sirfsoc_dma *sdma = dev_get_drvdata(dev); | 762 | struct sirfsoc_dma *sdma = dev_get_drvdata(dev); |
749 | 763 | ||
750 | clk_disable_unprepare(sdma->clk); | ||
751 | dma_async_device_unregister(&sdma->dma); | 764 | dma_async_device_unregister(&sdma->dma); |
752 | free_irq(sdma->irq, sdma); | 765 | free_irq(sdma->irq, sdma); |
753 | irq_dispose_mapping(sdma->irq); | 766 | irq_dispose_mapping(sdma->irq); |
767 | pm_runtime_disable(&op->dev); | ||
768 | if (!pm_runtime_status_suspended(&op->dev)) | ||
769 | sirfsoc_dma_runtime_suspend(&op->dev); | ||
770 | |||
771 | return 0; | ||
772 | } | ||
773 | |||
774 | static int sirfsoc_dma_runtime_suspend(struct device *dev) | ||
775 | { | ||
776 | struct sirfsoc_dma *sdma = dev_get_drvdata(dev); | ||
777 | |||
778 | clk_disable_unprepare(sdma->clk); | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | static int sirfsoc_dma_runtime_resume(struct device *dev) | ||
783 | { | ||
784 | struct sirfsoc_dma *sdma = dev_get_drvdata(dev); | ||
785 | int ret; | ||
786 | |||
787 | ret = clk_prepare_enable(sdma->clk); | ||
788 | if (ret < 0) { | ||
789 | dev_err(dev, "clk_enable failed: %d\n", ret); | ||
790 | return ret; | ||
791 | } | ||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static int sirfsoc_dma_pm_suspend(struct device *dev) | ||
796 | { | ||
797 | struct sirfsoc_dma *sdma = dev_get_drvdata(dev); | ||
798 | struct sirfsoc_dma_regs *save = &sdma->regs_save; | ||
799 | struct sirfsoc_dma_desc *sdesc; | ||
800 | struct sirfsoc_dma_chan *schan; | ||
801 | int ch; | ||
802 | int ret; | ||
803 | |||
804 | /* | ||
805 | * if we were runtime-suspended before, resume to enable clock | ||
806 | * before accessing register | ||
807 | */ | ||
808 | if (pm_runtime_status_suspended(dev)) { | ||
809 | ret = sirfsoc_dma_runtime_resume(dev); | ||
810 | if (ret < 0) | ||
811 | return ret; | ||
812 | } | ||
813 | |||
814 | /* | ||
815 | * DMA controller will lose all registers while suspending | ||
816 | * so we need to save registers for active channels | ||
817 | */ | ||
818 | for (ch = 0; ch < SIRFSOC_DMA_CHANNELS; ch++) { | ||
819 | schan = &sdma->channels[ch]; | ||
820 | if (list_empty(&schan->active)) | ||
821 | continue; | ||
822 | sdesc = list_first_entry(&schan->active, | ||
823 | struct sirfsoc_dma_desc, | ||
824 | node); | ||
825 | save->ctrl[ch] = readl_relaxed(sdma->base + | ||
826 | ch * 0x10 + SIRFSOC_DMA_CH_CTRL); | ||
827 | } | ||
828 | save->interrupt_en = readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN); | ||
829 | |||
830 | /* Disable clock */ | ||
831 | sirfsoc_dma_runtime_suspend(dev); | ||
832 | |||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | static int sirfsoc_dma_pm_resume(struct device *dev) | ||
837 | { | ||
838 | struct sirfsoc_dma *sdma = dev_get_drvdata(dev); | ||
839 | struct sirfsoc_dma_regs *save = &sdma->regs_save; | ||
840 | struct sirfsoc_dma_desc *sdesc; | ||
841 | struct sirfsoc_dma_chan *schan; | ||
842 | int ch; | ||
843 | int ret; | ||
844 | |||
845 | /* Enable clock before accessing register */ | ||
846 | ret = sirfsoc_dma_runtime_resume(dev); | ||
847 | if (ret < 0) | ||
848 | return ret; | ||
849 | |||
850 | writel_relaxed(save->interrupt_en, sdma->base + SIRFSOC_DMA_INT_EN); | ||
851 | for (ch = 0; ch < SIRFSOC_DMA_CHANNELS; ch++) { | ||
852 | schan = &sdma->channels[ch]; | ||
853 | if (list_empty(&schan->active)) | ||
854 | continue; | ||
855 | sdesc = list_first_entry(&schan->active, | ||
856 | struct sirfsoc_dma_desc, | ||
857 | node); | ||
858 | writel_relaxed(sdesc->width, | ||
859 | sdma->base + SIRFSOC_DMA_WIDTH_0 + ch * 4); | ||
860 | writel_relaxed(sdesc->xlen, | ||
861 | sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_XLEN); | ||
862 | writel_relaxed(sdesc->ylen, | ||
863 | sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_YLEN); | ||
864 | writel_relaxed(save->ctrl[ch], | ||
865 | sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_CTRL); | ||
866 | writel_relaxed(sdesc->addr >> 2, | ||
867 | sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_ADDR); | ||
868 | } | ||
869 | |||
870 | /* if we were runtime-suspended before, suspend again */ | ||
871 | if (pm_runtime_status_suspended(dev)) | ||
872 | sirfsoc_dma_runtime_suspend(dev); | ||
873 | |||
754 | return 0; | 874 | return 0; |
755 | } | 875 | } |
756 | 876 | ||
877 | static const struct dev_pm_ops sirfsoc_dma_pm_ops = { | ||
878 | SET_RUNTIME_PM_OPS(sirfsoc_dma_runtime_suspend, sirfsoc_dma_runtime_resume, NULL) | ||
879 | SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_dma_pm_suspend, sirfsoc_dma_pm_resume) | ||
880 | }; | ||
881 | |||
757 | static struct of_device_id sirfsoc_dma_match[] = { | 882 | static struct of_device_id sirfsoc_dma_match[] = { |
758 | { .compatible = "sirf,prima2-dmac", }, | 883 | { .compatible = "sirf,prima2-dmac", }, |
759 | { .compatible = "sirf,marco-dmac", }, | 884 | { .compatible = "sirf,marco-dmac", }, |
@@ -766,6 +891,7 @@ static struct platform_driver sirfsoc_dma_driver = { | |||
766 | .driver = { | 891 | .driver = { |
767 | .name = DRV_NAME, | 892 | .name = DRV_NAME, |
768 | .owner = THIS_MODULE, | 893 | .owner = THIS_MODULE, |
894 | .pm = &sirfsoc_dma_pm_ops, | ||
769 | .of_match_table = sirfsoc_dma_match, | 895 | .of_match_table = sirfsoc_dma_match, |
770 | }, | 896 | }, |
771 | }; | 897 | }; |