diff options
author | Kedareswara rao Appana <appana.durga.rao@xilinx.com> | 2017-12-07 00:29:57 -0500 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2017-12-17 22:58:09 -0500 |
commit | 64c6f7da8c2cab6bcf9c04b2eb8ea9afca31c186 (patch) | |
tree | 6a6f89228181bdc8ba5105606769e35137ac121a | |
parent | 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff) |
dmaengine: zynqmp_dma: Add runtime pm support
This patch adds runtime pm support in the driver.
Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r-- | drivers/dma/xilinx/zynqmp_dma.c | 160 |
1 files changed, 128 insertions, 32 deletions
diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index 1ee1241ca797..4fa14bf91073 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/io-64-nonatomic-lo-hi.h> | 25 | #include <linux/io-64-nonatomic-lo-hi.h> |
26 | #include <linux/pm_runtime.h> | ||
26 | 27 | ||
27 | #include "../dmaengine.h" | 28 | #include "../dmaengine.h" |
28 | 29 | ||
@@ -138,6 +139,8 @@ | |||
138 | #define ZYNQMP_DMA_BUS_WIDTH_64 64 | 139 | #define ZYNQMP_DMA_BUS_WIDTH_64 64 |
139 | #define ZYNQMP_DMA_BUS_WIDTH_128 128 | 140 | #define ZYNQMP_DMA_BUS_WIDTH_128 128 |
140 | 141 | ||
142 | #define ZDMA_PM_TIMEOUT 100 | ||
143 | |||
141 | #define ZYNQMP_DMA_DESC_SIZE(chan) (chan->desc_size) | 144 | #define ZYNQMP_DMA_DESC_SIZE(chan) (chan->desc_size) |
142 | 145 | ||
143 | #define to_chan(chan) container_of(chan, struct zynqmp_dma_chan, \ | 146 | #define to_chan(chan) container_of(chan, struct zynqmp_dma_chan, \ |
@@ -211,8 +214,6 @@ struct zynqmp_dma_desc_sw { | |||
211 | * @bus_width: Bus width | 214 | * @bus_width: Bus width |
212 | * @src_burst_len: Source burst length | 215 | * @src_burst_len: Source burst length |
213 | * @dst_burst_len: Dest burst length | 216 | * @dst_burst_len: Dest burst length |
214 | * @clk_main: Pointer to main clock | ||
215 | * @clk_apb: Pointer to apb clock | ||
216 | */ | 217 | */ |
217 | struct zynqmp_dma_chan { | 218 | struct zynqmp_dma_chan { |
218 | struct zynqmp_dma_device *zdev; | 219 | struct zynqmp_dma_device *zdev; |
@@ -237,8 +238,6 @@ struct zynqmp_dma_chan { | |||
237 | u32 bus_width; | 238 | u32 bus_width; |
238 | u32 src_burst_len; | 239 | u32 src_burst_len; |
239 | u32 dst_burst_len; | 240 | u32 dst_burst_len; |
240 | struct clk *clk_main; | ||
241 | struct clk *clk_apb; | ||
242 | }; | 241 | }; |
243 | 242 | ||
244 | /** | 243 | /** |
@@ -246,11 +245,15 @@ struct zynqmp_dma_chan { | |||
246 | * @dev: Device Structure | 245 | * @dev: Device Structure |
247 | * @common: DMA device structure | 246 | * @common: DMA device structure |
248 | * @chan: Driver specific DMA channel | 247 | * @chan: Driver specific DMA channel |
248 | * @clk_main: Pointer to main clock | ||
249 | * @clk_apb: Pointer to apb clock | ||
249 | */ | 250 | */ |
250 | struct zynqmp_dma_device { | 251 | struct zynqmp_dma_device { |
251 | struct device *dev; | 252 | struct device *dev; |
252 | struct dma_device common; | 253 | struct dma_device common; |
253 | struct zynqmp_dma_chan *chan; | 254 | struct zynqmp_dma_chan *chan; |
255 | struct clk *clk_main; | ||
256 | struct clk *clk_apb; | ||
254 | }; | 257 | }; |
255 | 258 | ||
256 | static inline void zynqmp_dma_writeq(struct zynqmp_dma_chan *chan, u32 reg, | 259 | static inline void zynqmp_dma_writeq(struct zynqmp_dma_chan *chan, u32 reg, |
@@ -461,7 +464,11 @@ static int zynqmp_dma_alloc_chan_resources(struct dma_chan *dchan) | |||
461 | { | 464 | { |
462 | struct zynqmp_dma_chan *chan = to_chan(dchan); | 465 | struct zynqmp_dma_chan *chan = to_chan(dchan); |
463 | struct zynqmp_dma_desc_sw *desc; | 466 | struct zynqmp_dma_desc_sw *desc; |
464 | int i; | 467 | int i, ret; |
468 | |||
469 | ret = pm_runtime_get_sync(chan->dev); | ||
470 | if (ret < 0) | ||
471 | return ret; | ||
465 | 472 | ||
466 | chan->sw_desc_pool = kzalloc(sizeof(*desc) * ZYNQMP_DMA_NUM_DESCS, | 473 | chan->sw_desc_pool = kzalloc(sizeof(*desc) * ZYNQMP_DMA_NUM_DESCS, |
467 | GFP_KERNEL); | 474 | GFP_KERNEL); |
@@ -664,6 +671,8 @@ static void zynqmp_dma_free_chan_resources(struct dma_chan *dchan) | |||
664 | (2 * ZYNQMP_DMA_DESC_SIZE(chan) * ZYNQMP_DMA_NUM_DESCS), | 671 | (2 * ZYNQMP_DMA_DESC_SIZE(chan) * ZYNQMP_DMA_NUM_DESCS), |
665 | chan->desc_pool_v, chan->desc_pool_p); | 672 | chan->desc_pool_v, chan->desc_pool_p); |
666 | kfree(chan->sw_desc_pool); | 673 | kfree(chan->sw_desc_pool); |
674 | pm_runtime_mark_last_busy(chan->dev); | ||
675 | pm_runtime_put_autosuspend(chan->dev); | ||
667 | } | 676 | } |
668 | 677 | ||
669 | /** | 678 | /** |
@@ -841,8 +850,6 @@ static void zynqmp_dma_chan_remove(struct zynqmp_dma_chan *chan) | |||
841 | devm_free_irq(chan->zdev->dev, chan->irq, chan); | 850 | devm_free_irq(chan->zdev->dev, chan->irq, chan); |
842 | tasklet_kill(&chan->tasklet); | 851 | tasklet_kill(&chan->tasklet); |
843 | list_del(&chan->common.device_node); | 852 | list_del(&chan->common.device_node); |
844 | clk_disable_unprepare(chan->clk_apb); | ||
845 | clk_disable_unprepare(chan->clk_main); | ||
846 | } | 853 | } |
847 | 854 | ||
848 | /** | 855 | /** |
@@ -907,30 +914,6 @@ static int zynqmp_dma_chan_probe(struct zynqmp_dma_device *zdev, | |||
907 | "zynqmp-dma", chan); | 914 | "zynqmp-dma", chan); |
908 | if (err) | 915 | if (err) |
909 | return err; | 916 | return err; |
910 | chan->clk_main = devm_clk_get(&pdev->dev, "clk_main"); | ||
911 | if (IS_ERR(chan->clk_main)) { | ||
912 | dev_err(&pdev->dev, "main clock not found.\n"); | ||
913 | return PTR_ERR(chan->clk_main); | ||
914 | } | ||
915 | |||
916 | chan->clk_apb = devm_clk_get(&pdev->dev, "clk_apb"); | ||
917 | if (IS_ERR(chan->clk_apb)) { | ||
918 | dev_err(&pdev->dev, "apb clock not found.\n"); | ||
919 | return PTR_ERR(chan->clk_apb); | ||
920 | } | ||
921 | |||
922 | err = clk_prepare_enable(chan->clk_main); | ||
923 | if (err) { | ||
924 | dev_err(&pdev->dev, "Unable to enable main clock.\n"); | ||
925 | return err; | ||
926 | } | ||
927 | |||
928 | err = clk_prepare_enable(chan->clk_apb); | ||
929 | if (err) { | ||
930 | clk_disable_unprepare(chan->clk_main); | ||
931 | dev_err(&pdev->dev, "Unable to enable apb clock.\n"); | ||
932 | return err; | ||
933 | } | ||
934 | 917 | ||
935 | chan->desc_size = sizeof(struct zynqmp_dma_desc_ll); | 918 | chan->desc_size = sizeof(struct zynqmp_dma_desc_ll); |
936 | chan->idle = true; | 919 | chan->idle = true; |
@@ -953,6 +936,87 @@ static struct dma_chan *of_zynqmp_dma_xlate(struct of_phandle_args *dma_spec, | |||
953 | } | 936 | } |
954 | 937 | ||
955 | /** | 938 | /** |
939 | * zynqmp_dma_suspend - Suspend method for the driver | ||
940 | * @dev: Address of the device structure | ||
941 | * | ||
942 | * Put the driver into low power mode. | ||
943 | * Return: 0 on success and failure value on error | ||
944 | */ | ||
945 | static int __maybe_unused zynqmp_dma_suspend(struct device *dev) | ||
946 | { | ||
947 | if (!device_may_wakeup(dev)) | ||
948 | return pm_runtime_force_suspend(dev); | ||
949 | |||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | /** | ||
954 | * zynqmp_dma_resume - Resume from suspend | ||
955 | * @dev: Address of the device structure | ||
956 | * | ||
957 | * Resume operation after suspend. | ||
958 | * Return: 0 on success and failure value on error | ||
959 | */ | ||
960 | static int __maybe_unused zynqmp_dma_resume(struct device *dev) | ||
961 | { | ||
962 | if (!device_may_wakeup(dev)) | ||
963 | return pm_runtime_force_resume(dev); | ||
964 | |||
965 | return 0; | ||
966 | } | ||
967 | |||
968 | /** | ||
969 | * zynqmp_dma_runtime_suspend - Runtime suspend method for the driver | ||
970 | * @dev: Address of the device structure | ||
971 | * | ||
972 | * Put the driver into low power mode. | ||
973 | * Return: 0 always | ||
974 | */ | ||
975 | static int __maybe_unused zynqmp_dma_runtime_suspend(struct device *dev) | ||
976 | { | ||
977 | struct zynqmp_dma_device *zdev = dev_get_drvdata(dev); | ||
978 | |||
979 | clk_disable_unprepare(zdev->clk_main); | ||
980 | clk_disable_unprepare(zdev->clk_apb); | ||
981 | |||
982 | return 0; | ||
983 | } | ||
984 | |||
985 | /** | ||
986 | * zynqmp_dma_runtime_resume - Runtime suspend method for the driver | ||
987 | * @dev: Address of the device structure | ||
988 | * | ||
989 | * Put the driver into low power mode. | ||
990 | * Return: 0 always | ||
991 | */ | ||
992 | static int __maybe_unused zynqmp_dma_runtime_resume(struct device *dev) | ||
993 | { | ||
994 | struct zynqmp_dma_device *zdev = dev_get_drvdata(dev); | ||
995 | int err; | ||
996 | |||
997 | err = clk_prepare_enable(zdev->clk_main); | ||
998 | if (err) { | ||
999 | dev_err(dev, "Unable to enable main clock.\n"); | ||
1000 | return err; | ||
1001 | } | ||
1002 | |||
1003 | err = clk_prepare_enable(zdev->clk_apb); | ||
1004 | if (err) { | ||
1005 | dev_err(dev, "Unable to enable apb clock.\n"); | ||
1006 | clk_disable_unprepare(zdev->clk_main); | ||
1007 | return err; | ||
1008 | } | ||
1009 | |||
1010 | return 0; | ||
1011 | } | ||
1012 | |||
1013 | static const struct dev_pm_ops zynqmp_dma_dev_pm_ops = { | ||
1014 | SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dma_suspend, zynqmp_dma_resume) | ||
1015 | SET_RUNTIME_PM_OPS(zynqmp_dma_runtime_suspend, | ||
1016 | zynqmp_dma_runtime_resume, NULL) | ||
1017 | }; | ||
1018 | |||
1019 | /** | ||
956 | * zynqmp_dma_probe - Driver probe function | 1020 | * zynqmp_dma_probe - Driver probe function |
957 | * @pdev: Pointer to the platform_device structure | 1021 | * @pdev: Pointer to the platform_device structure |
958 | * | 1022 | * |
@@ -984,12 +1048,33 @@ static int zynqmp_dma_probe(struct platform_device *pdev) | |||
984 | p->device_config = zynqmp_dma_device_config; | 1048 | p->device_config = zynqmp_dma_device_config; |
985 | p->dev = &pdev->dev; | 1049 | p->dev = &pdev->dev; |
986 | 1050 | ||
1051 | zdev->clk_main = devm_clk_get(&pdev->dev, "clk_main"); | ||
1052 | if (IS_ERR(zdev->clk_main)) { | ||
1053 | dev_err(&pdev->dev, "main clock not found.\n"); | ||
1054 | return PTR_ERR(zdev->clk_main); | ||
1055 | } | ||
1056 | |||
1057 | zdev->clk_apb = devm_clk_get(&pdev->dev, "clk_apb"); | ||
1058 | if (IS_ERR(zdev->clk_apb)) { | ||
1059 | dev_err(&pdev->dev, "apb clock not found.\n"); | ||
1060 | return PTR_ERR(zdev->clk_apb); | ||
1061 | } | ||
1062 | |||
987 | platform_set_drvdata(pdev, zdev); | 1063 | platform_set_drvdata(pdev, zdev); |
1064 | pm_runtime_set_autosuspend_delay(zdev->dev, ZDMA_PM_TIMEOUT); | ||
1065 | pm_runtime_use_autosuspend(zdev->dev); | ||
1066 | pm_runtime_enable(zdev->dev); | ||
1067 | pm_runtime_get_sync(zdev->dev); | ||
1068 | if (!pm_runtime_enabled(zdev->dev)) { | ||
1069 | ret = zynqmp_dma_runtime_resume(zdev->dev); | ||
1070 | if (ret) | ||
1071 | return ret; | ||
1072 | } | ||
988 | 1073 | ||
989 | ret = zynqmp_dma_chan_probe(zdev, pdev); | 1074 | ret = zynqmp_dma_chan_probe(zdev, pdev); |
990 | if (ret) { | 1075 | if (ret) { |
991 | dev_err(&pdev->dev, "Probing channel failed\n"); | 1076 | dev_err(&pdev->dev, "Probing channel failed\n"); |
992 | goto free_chan_resources; | 1077 | goto err_disable_pm; |
993 | } | 1078 | } |
994 | 1079 | ||
995 | p->dst_addr_widths = BIT(zdev->chan->bus_width / 8); | 1080 | p->dst_addr_widths = BIT(zdev->chan->bus_width / 8); |
@@ -1005,12 +1090,19 @@ static int zynqmp_dma_probe(struct platform_device *pdev) | |||
1005 | goto free_chan_resources; | 1090 | goto free_chan_resources; |
1006 | } | 1091 | } |
1007 | 1092 | ||
1093 | pm_runtime_mark_last_busy(zdev->dev); | ||
1094 | pm_runtime_put_sync_autosuspend(zdev->dev); | ||
1095 | |||
1008 | dev_info(&pdev->dev, "ZynqMP DMA driver Probe success\n"); | 1096 | dev_info(&pdev->dev, "ZynqMP DMA driver Probe success\n"); |
1009 | 1097 | ||
1010 | return 0; | 1098 | return 0; |
1011 | 1099 | ||
1012 | free_chan_resources: | 1100 | free_chan_resources: |
1013 | zynqmp_dma_chan_remove(zdev->chan); | 1101 | zynqmp_dma_chan_remove(zdev->chan); |
1102 | err_disable_pm: | ||
1103 | if (!pm_runtime_enabled(zdev->dev)) | ||
1104 | zynqmp_dma_runtime_suspend(zdev->dev); | ||
1105 | pm_runtime_disable(zdev->dev); | ||
1014 | return ret; | 1106 | return ret; |
1015 | } | 1107 | } |
1016 | 1108 | ||
@@ -1028,6 +1120,9 @@ static int zynqmp_dma_remove(struct platform_device *pdev) | |||
1028 | dma_async_device_unregister(&zdev->common); | 1120 | dma_async_device_unregister(&zdev->common); |
1029 | 1121 | ||
1030 | zynqmp_dma_chan_remove(zdev->chan); | 1122 | zynqmp_dma_chan_remove(zdev->chan); |
1123 | pm_runtime_disable(zdev->dev); | ||
1124 | if (!pm_runtime_enabled(zdev->dev)) | ||
1125 | zynqmp_dma_runtime_suspend(zdev->dev); | ||
1031 | 1126 | ||
1032 | return 0; | 1127 | return 0; |
1033 | } | 1128 | } |
@@ -1042,6 +1137,7 @@ static struct platform_driver zynqmp_dma_driver = { | |||
1042 | .driver = { | 1137 | .driver = { |
1043 | .name = "xilinx-zynqmp-dma", | 1138 | .name = "xilinx-zynqmp-dma", |
1044 | .of_match_table = zynqmp_dma_of_match, | 1139 | .of_match_table = zynqmp_dma_of_match, |
1140 | .pm = &zynqmp_dma_dev_pm_ops, | ||
1045 | }, | 1141 | }, |
1046 | .probe = zynqmp_dma_probe, | 1142 | .probe = zynqmp_dma_probe, |
1047 | .remove = zynqmp_dma_remove, | 1143 | .remove = zynqmp_dma_remove, |