meson-gx-pwrc-vpu.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /*
  2. * Copyright (c) 2017 BayLibre, SAS
  3. * Author: Neil Armstrong <narmstrong@baylibre.com>
  4. *
  5. * SPDX-License-Identifier: GPL-2.0+
  6. */
  7. #include <linux/of_address.h>
  8. #include <linux/platform_device.h>
  9. #include <linux/pm_domain.h>
  10. #include <linux/bitfield.h>
  11. #include <linux/regmap.h>
  12. #include <linux/mfd/syscon.h>
  13. #include <linux/reset.h>
  14. #include <linux/clk.h>
  15. /* AO Offsets */
  16. #define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
  17. #define GEN_PWR_VPU_HDMI BIT(8)
  18. #define GEN_PWR_VPU_HDMI_ISO BIT(9)
  19. /* HHI Offsets */
  20. #define HHI_MEM_PD_REG0 (0x40 << 2)
  21. #define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
  22. #define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
  23. struct meson_gx_pwrc_vpu {
  24. struct generic_pm_domain genpd;
  25. struct regmap *regmap_ao;
  26. struct regmap *regmap_hhi;
  27. struct reset_control *rstc;
  28. struct clk *vpu_clk;
  29. struct clk *vapb_clk;
  30. };
  31. static inline
  32. struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
  33. {
  34. return container_of(d, struct meson_gx_pwrc_vpu, genpd);
  35. }
  36. static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
  37. {
  38. struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
  39. int i;
  40. regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
  41. GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
  42. udelay(20);
  43. /* Power Down Memories */
  44. for (i = 0; i < 32; i += 2) {
  45. regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
  46. 0x3 << i, 0x3 << i);
  47. udelay(5);
  48. }
  49. for (i = 0; i < 32; i += 2) {
  50. regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
  51. 0x3 << i, 0x3 << i);
  52. udelay(5);
  53. }
  54. for (i = 8; i < 16; i++) {
  55. regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
  56. BIT(i), BIT(i));
  57. udelay(5);
  58. }
  59. udelay(20);
  60. regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
  61. GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
  62. msleep(20);
  63. clk_disable_unprepare(pd->vpu_clk);
  64. clk_disable_unprepare(pd->vapb_clk);
  65. return 0;
  66. }
  67. static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
  68. {
  69. int ret;
  70. ret = clk_prepare_enable(pd->vpu_clk);
  71. if (ret)
  72. return ret;
  73. ret = clk_prepare_enable(pd->vapb_clk);
  74. if (ret)
  75. clk_disable_unprepare(pd->vpu_clk);
  76. return ret;
  77. }
  78. static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
  79. {
  80. struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
  81. int ret;
  82. int i;
  83. regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
  84. GEN_PWR_VPU_HDMI, 0);
  85. udelay(20);
  86. /* Power Up Memories */
  87. for (i = 0; i < 32; i += 2) {
  88. regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
  89. 0x3 << i, 0);
  90. udelay(5);
  91. }
  92. for (i = 0; i < 32; i += 2) {
  93. regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
  94. 0x3 << i, 0);
  95. udelay(5);
  96. }
  97. for (i = 8; i < 16; i++) {
  98. regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
  99. BIT(i), 0);
  100. udelay(5);
  101. }
  102. udelay(20);
  103. ret = reset_control_assert(pd->rstc);
  104. if (ret)
  105. return ret;
  106. regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
  107. GEN_PWR_VPU_HDMI_ISO, 0);
  108. ret = reset_control_deassert(pd->rstc);
  109. if (ret)
  110. return ret;
  111. ret = meson_gx_pwrc_vpu_setup_clk(pd);
  112. if (ret)
  113. return ret;
  114. return 0;
  115. }
  116. static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
  117. {
  118. u32 reg;
  119. regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
  120. return (reg & GEN_PWR_VPU_HDMI);
  121. }
  122. static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
  123. .genpd = {
  124. .name = "vpu_hdmi",
  125. .power_off = meson_gx_pwrc_vpu_power_off,
  126. .power_on = meson_gx_pwrc_vpu_power_on,
  127. },
  128. };
  129. static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
  130. {
  131. struct regmap *regmap_ao, *regmap_hhi;
  132. struct reset_control *rstc;
  133. struct clk *vpu_clk;
  134. struct clk *vapb_clk;
  135. bool powered_off;
  136. int ret;
  137. regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
  138. if (IS_ERR(regmap_ao)) {
  139. dev_err(&pdev->dev, "failed to get regmap\n");
  140. return PTR_ERR(regmap_ao);
  141. }
  142. regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
  143. "amlogic,hhi-sysctrl");
  144. if (IS_ERR(regmap_hhi)) {
  145. dev_err(&pdev->dev, "failed to get HHI regmap\n");
  146. return PTR_ERR(regmap_hhi);
  147. }
  148. rstc = devm_reset_control_array_get(&pdev->dev, false, false);
  149. if (IS_ERR(rstc)) {
  150. if (PTR_ERR(rstc) != -EPROBE_DEFER)
  151. dev_err(&pdev->dev, "failed to get reset lines\n");
  152. return PTR_ERR(rstc);
  153. }
  154. vpu_clk = devm_clk_get(&pdev->dev, "vpu");
  155. if (IS_ERR(vpu_clk)) {
  156. dev_err(&pdev->dev, "vpu clock request failed\n");
  157. return PTR_ERR(vpu_clk);
  158. }
  159. vapb_clk = devm_clk_get(&pdev->dev, "vapb");
  160. if (IS_ERR(vapb_clk)) {
  161. dev_err(&pdev->dev, "vapb clock request failed\n");
  162. return PTR_ERR(vapb_clk);
  163. }
  164. vpu_hdmi_pd.regmap_ao = regmap_ao;
  165. vpu_hdmi_pd.regmap_hhi = regmap_hhi;
  166. vpu_hdmi_pd.rstc = rstc;
  167. vpu_hdmi_pd.vpu_clk = vpu_clk;
  168. vpu_hdmi_pd.vapb_clk = vapb_clk;
  169. powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd);
  170. /* If already powered, sync the clock states */
  171. if (!powered_off) {
  172. ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd);
  173. if (ret)
  174. return ret;
  175. }
  176. pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov,
  177. powered_off);
  178. return of_genpd_add_provider_simple(pdev->dev.of_node,
  179. &vpu_hdmi_pd.genpd);
  180. }
  181. static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
  182. {
  183. bool powered_off;
  184. powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd);
  185. if (!powered_off)
  186. meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd);
  187. }
  188. static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
  189. { .compatible = "amlogic,meson-gx-pwrc-vpu" },
  190. { /* sentinel */ }
  191. };
  192. static struct platform_driver meson_gx_pwrc_vpu_driver = {
  193. .probe = meson_gx_pwrc_vpu_probe,
  194. .shutdown = meson_gx_pwrc_vpu_shutdown,
  195. .driver = {
  196. .name = "meson_gx_pwrc_vpu",
  197. .of_match_table = meson_gx_pwrc_vpu_match_table,
  198. },
  199. };
  200. builtin_platform_driver(meson_gx_pwrc_vpu_driver);