pxa1908-power-controller.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2025 Duje Mihanović <duje@dujemihanovic.xyz>
  4. */
  5. #include <linux/auxiliary_bus.h>
  6. #include <linux/container_of.h>
  7. #include <linux/dev_printk.h>
  8. #include <linux/device.h>
  9. #include <linux/mfd/syscon.h>
  10. #include <linux/mod_devicetable.h>
  11. #include <linux/module.h>
  12. #include <linux/pm_domain.h>
  13. #include <linux/regmap.h>
  14. #include <dt-bindings/power/marvell,pxa1908-power.h>
  15. /* VPU, GPU, ISP */
  16. #define APMU_PWR_CTRL_REG 0xd8
  17. #define APMU_PWR_BLK_TMR_REG 0xdc
  18. #define APMU_PWR_STATUS_REG 0xf0
  19. /* DSI */
  20. #define APMU_DEBUG 0x88
  21. #define DSI_PHY_DVM_MASK BIT(31)
  22. #define POWER_ON_LATENCY_US 300
  23. #define POWER_OFF_LATENCY_US 20
  24. #define POWER_POLL_TIMEOUT_US (25 * USEC_PER_MSEC)
  25. #define POWER_POLL_SLEEP_US 6
  26. #define NR_DOMAINS 5
  27. #define to_pxa1908_pd(_genpd) container_of(_genpd, struct pxa1908_pd, genpd)
  28. struct pxa1908_pd_ctrl {
  29. struct generic_pm_domain *domains[NR_DOMAINS];
  30. struct genpd_onecell_data onecell_data;
  31. struct regmap *base;
  32. struct device *dev;
  33. };
  34. struct pxa1908_pd_data {
  35. u32 reg_clk_res_ctrl;
  36. u32 pwr_state;
  37. u32 hw_mode;
  38. bool keep_on;
  39. int id;
  40. };
  41. struct pxa1908_pd {
  42. const struct pxa1908_pd_data data;
  43. struct pxa1908_pd_ctrl *ctrl;
  44. struct generic_pm_domain genpd;
  45. bool initialized;
  46. };
  47. static inline bool pxa1908_pd_is_on(struct pxa1908_pd *pd)
  48. {
  49. struct pxa1908_pd_ctrl *ctrl = pd->ctrl;
  50. return pd->data.id != PXA1908_POWER_DOMAIN_DSI
  51. ? regmap_test_bits(ctrl->base, APMU_PWR_STATUS_REG, pd->data.pwr_state)
  52. : regmap_test_bits(ctrl->base, APMU_DEBUG, DSI_PHY_DVM_MASK);
  53. }
  54. static int pxa1908_pd_power_on(struct generic_pm_domain *genpd)
  55. {
  56. struct pxa1908_pd *pd = to_pxa1908_pd(genpd);
  57. const struct pxa1908_pd_data *data = &pd->data;
  58. struct pxa1908_pd_ctrl *ctrl = pd->ctrl;
  59. unsigned int status;
  60. int ret = 0;
  61. regmap_set_bits(ctrl->base, data->reg_clk_res_ctrl, data->hw_mode);
  62. if (data->id != PXA1908_POWER_DOMAIN_ISP)
  63. regmap_write(ctrl->base, APMU_PWR_BLK_TMR_REG, 0x20001fff);
  64. regmap_set_bits(ctrl->base, APMU_PWR_CTRL_REG, data->pwr_state);
  65. ret = regmap_read_poll_timeout(ctrl->base, APMU_PWR_STATUS_REG, status,
  66. status & data->pwr_state, POWER_POLL_SLEEP_US,
  67. POWER_ON_LATENCY_US + POWER_POLL_TIMEOUT_US);
  68. if (ret == -ETIMEDOUT)
  69. dev_err(ctrl->dev, "timed out powering on domain '%s'\n", pd->genpd.name);
  70. return ret;
  71. }
  72. static int pxa1908_pd_power_off(struct generic_pm_domain *genpd)
  73. {
  74. struct pxa1908_pd *pd = to_pxa1908_pd(genpd);
  75. const struct pxa1908_pd_data *data = &pd->data;
  76. struct pxa1908_pd_ctrl *ctrl = pd->ctrl;
  77. unsigned int status;
  78. int ret;
  79. regmap_clear_bits(ctrl->base, APMU_PWR_CTRL_REG, data->pwr_state);
  80. ret = regmap_read_poll_timeout(ctrl->base, APMU_PWR_STATUS_REG, status,
  81. !(status & data->pwr_state), POWER_POLL_SLEEP_US,
  82. POWER_OFF_LATENCY_US + POWER_POLL_TIMEOUT_US);
  83. if (ret == -ETIMEDOUT) {
  84. dev_err(ctrl->dev, "timed out powering off domain '%s'\n", pd->genpd.name);
  85. return ret;
  86. }
  87. return regmap_clear_bits(ctrl->base, data->reg_clk_res_ctrl, data->hw_mode);
  88. }
  89. static inline int pxa1908_dsi_power_on(struct generic_pm_domain *genpd)
  90. {
  91. struct pxa1908_pd *pd = to_pxa1908_pd(genpd);
  92. struct pxa1908_pd_ctrl *ctrl = pd->ctrl;
  93. return regmap_set_bits(ctrl->base, APMU_DEBUG, DSI_PHY_DVM_MASK);
  94. }
  95. static inline int pxa1908_dsi_power_off(struct generic_pm_domain *genpd)
  96. {
  97. struct pxa1908_pd *pd = to_pxa1908_pd(genpd);
  98. struct pxa1908_pd_ctrl *ctrl = pd->ctrl;
  99. return regmap_clear_bits(ctrl->base, APMU_DEBUG, DSI_PHY_DVM_MASK);
  100. }
  101. #define DOMAIN(_id, _name, ctrl, mode, state) \
  102. [_id] = { \
  103. .data = { \
  104. .reg_clk_res_ctrl = ctrl, \
  105. .hw_mode = BIT(mode), \
  106. .pwr_state = BIT(state), \
  107. .id = _id, \
  108. }, \
  109. .genpd = { \
  110. .name = _name, \
  111. .power_on = pxa1908_pd_power_on, \
  112. .power_off = pxa1908_pd_power_off, \
  113. }, \
  114. }
  115. static struct pxa1908_pd domains[NR_DOMAINS] = {
  116. DOMAIN(PXA1908_POWER_DOMAIN_VPU, "vpu", 0xa4, 19, 2),
  117. DOMAIN(PXA1908_POWER_DOMAIN_GPU, "gpu", 0xcc, 11, 0),
  118. DOMAIN(PXA1908_POWER_DOMAIN_GPU2D, "gpu2d", 0xf4, 11, 6),
  119. DOMAIN(PXA1908_POWER_DOMAIN_ISP, "isp", 0x38, 15, 4),
  120. [PXA1908_POWER_DOMAIN_DSI] = {
  121. .genpd = {
  122. .name = "dsi",
  123. .power_on = pxa1908_dsi_power_on,
  124. .power_off = pxa1908_dsi_power_off,
  125. /*
  126. * TODO: There is no DSI driver written yet and until then we probably
  127. * don't want to power off the DSI PHY ever.
  128. */
  129. .flags = GENPD_FLAG_ALWAYS_ON,
  130. },
  131. .data = {
  132. /* See above. */
  133. .keep_on = true,
  134. },
  135. },
  136. };
  137. static void pxa1908_pd_remove(struct auxiliary_device *auxdev)
  138. {
  139. struct pxa1908_pd *pd;
  140. int ret;
  141. for (int i = NR_DOMAINS - 1; i >= 0; i--) {
  142. pd = &domains[i];
  143. if (!pd->initialized)
  144. continue;
  145. if (pxa1908_pd_is_on(pd) && !pd->data.keep_on)
  146. pxa1908_pd_power_off(&pd->genpd);
  147. ret = pm_genpd_remove(&pd->genpd);
  148. if (ret)
  149. dev_err(&auxdev->dev, "failed to remove domain '%s': %d\n",
  150. pd->genpd.name, ret);
  151. }
  152. }
  153. static int
  154. pxa1908_pd_init(struct pxa1908_pd_ctrl *ctrl, int id, struct device *dev)
  155. {
  156. struct pxa1908_pd *pd = &domains[id];
  157. int ret;
  158. ctrl->domains[id] = &pd->genpd;
  159. pd->ctrl = ctrl;
  160. /* Make sure the state of the hardware is synced with the domain table above. */
  161. if (pd->data.keep_on) {
  162. ret = pd->genpd.power_on(&pd->genpd);
  163. if (ret)
  164. return dev_err_probe(dev, ret, "failed to power on domain '%s'\n",
  165. pd->genpd.name);
  166. } else {
  167. if (pxa1908_pd_is_on(pd)) {
  168. dev_warn(dev,
  169. "domain '%s' is on despite being default off; powering off\n",
  170. pd->genpd.name);
  171. ret = pd->genpd.power_off(&pd->genpd);
  172. if (ret)
  173. return dev_err_probe(dev, ret,
  174. "failed to power off domain '%s'\n",
  175. pd->genpd.name);
  176. }
  177. }
  178. ret = pm_genpd_init(&pd->genpd, NULL, !pd->data.keep_on);
  179. if (ret)
  180. return dev_err_probe(dev, ret, "domain '%s' failed to initialize\n",
  181. pd->genpd.name);
  182. pd->initialized = true;
  183. return 0;
  184. }
  185. static int
  186. pxa1908_pd_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *aux_id)
  187. {
  188. struct pxa1908_pd_ctrl *ctrl;
  189. struct device *dev = &auxdev->dev;
  190. int ret;
  191. ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
  192. if (!ctrl)
  193. return -ENOMEM;
  194. auxiliary_set_drvdata(auxdev, ctrl);
  195. ctrl->base = syscon_node_to_regmap(dev->parent->of_node);
  196. if (IS_ERR(ctrl->base))
  197. return dev_err_probe(dev, PTR_ERR(ctrl->base), "no regmap available\n");
  198. ctrl->dev = dev;
  199. ctrl->onecell_data.domains = ctrl->domains;
  200. ctrl->onecell_data.num_domains = NR_DOMAINS;
  201. for (int i = 0; i < NR_DOMAINS; i++) {
  202. ret = pxa1908_pd_init(ctrl, i, dev);
  203. if (ret)
  204. goto err;
  205. }
  206. return of_genpd_add_provider_onecell(dev->parent->of_node, &ctrl->onecell_data);
  207. err:
  208. pxa1908_pd_remove(auxdev);
  209. return ret;
  210. }
  211. static const struct auxiliary_device_id pxa1908_pd_id[] = {
  212. { .name = "clk_pxa1908_apmu.power" },
  213. { }
  214. };
  215. MODULE_DEVICE_TABLE(auxiliary, pxa1908_pd_id);
  216. static struct auxiliary_driver pxa1908_pd_driver = {
  217. .probe = pxa1908_pd_probe,
  218. .remove = pxa1908_pd_remove,
  219. .id_table = pxa1908_pd_id,
  220. };
  221. module_auxiliary_driver(pxa1908_pd_driver);
  222. MODULE_AUTHOR("Duje Mihanović <duje@dujemihanovic.xyz>");
  223. MODULE_DESCRIPTION("Marvell PXA1908 power domain driver");
  224. MODULE_LICENSE("GPL");