bcm63xx-power.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * BCM63xx Power Domain Controller Driver
  4. *
  5. * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
  6. */
  7. #include <dt-bindings/soc/bcm6318-pm.h>
  8. #include <dt-bindings/soc/bcm6328-pm.h>
  9. #include <dt-bindings/soc/bcm6362-pm.h>
  10. #include <dt-bindings/soc/bcm63268-pm.h>
  11. #include <linux/io.h>
  12. #include <linux/module.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/pm_domain.h>
  15. #include <linux/of.h>
  16. struct bcm63xx_power_dev {
  17. struct generic_pm_domain genpd;
  18. struct bcm63xx_power *power;
  19. uint32_t mask;
  20. };
  21. struct bcm63xx_power {
  22. void __iomem *base;
  23. spinlock_t lock;
  24. struct bcm63xx_power_dev *dev;
  25. struct genpd_onecell_data genpd_data;
  26. struct generic_pm_domain **genpd;
  27. };
  28. struct bcm63xx_power_data {
  29. const char * const name;
  30. uint8_t bit;
  31. unsigned int flags;
  32. };
  33. static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on)
  34. {
  35. struct bcm63xx_power *power = pmd->power;
  36. if (!pmd->mask) {
  37. *is_on = false;
  38. return -EINVAL;
  39. }
  40. *is_on = !(__raw_readl(power->base) & pmd->mask);
  41. return 0;
  42. }
  43. static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on)
  44. {
  45. struct bcm63xx_power *power = pmd->power;
  46. unsigned long flags;
  47. uint32_t val;
  48. if (!pmd->mask)
  49. return -EINVAL;
  50. spin_lock_irqsave(&power->lock, flags);
  51. val = __raw_readl(power->base);
  52. if (on)
  53. val &= ~pmd->mask;
  54. else
  55. val |= pmd->mask;
  56. __raw_writel(val, power->base);
  57. spin_unlock_irqrestore(&power->lock, flags);
  58. return 0;
  59. }
  60. static int bcm63xx_power_on(struct generic_pm_domain *genpd)
  61. {
  62. struct bcm63xx_power_dev *pmd = container_of(genpd,
  63. struct bcm63xx_power_dev, genpd);
  64. return bcm63xx_power_set_state(pmd, true);
  65. }
  66. static int bcm63xx_power_off(struct generic_pm_domain *genpd)
  67. {
  68. struct bcm63xx_power_dev *pmd = container_of(genpd,
  69. struct bcm63xx_power_dev, genpd);
  70. return bcm63xx_power_set_state(pmd, false);
  71. }
  72. static int bcm63xx_power_probe(struct platform_device *pdev)
  73. {
  74. struct device *dev = &pdev->dev;
  75. struct device_node *np = dev->of_node;
  76. const struct bcm63xx_power_data *entry, *table;
  77. struct bcm63xx_power *power;
  78. unsigned int ndom;
  79. uint8_t max_bit = 0;
  80. int ret;
  81. power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
  82. if (!power)
  83. return -ENOMEM;
  84. power->base = devm_platform_ioremap_resource(pdev, 0);
  85. if (IS_ERR(power->base))
  86. return PTR_ERR(power->base);
  87. table = of_device_get_match_data(dev);
  88. if (!table)
  89. return -EINVAL;
  90. power->genpd_data.num_domains = 0;
  91. ndom = 0;
  92. for (entry = table; entry->name; entry++) {
  93. max_bit = max(max_bit, entry->bit);
  94. ndom++;
  95. }
  96. if (!ndom)
  97. return -ENODEV;
  98. power->genpd_data.num_domains = max_bit + 1;
  99. power->dev = devm_kcalloc(dev, power->genpd_data.num_domains,
  100. sizeof(struct bcm63xx_power_dev),
  101. GFP_KERNEL);
  102. if (!power->dev)
  103. return -ENOMEM;
  104. power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains,
  105. sizeof(struct generic_pm_domain *),
  106. GFP_KERNEL);
  107. if (!power->genpd)
  108. return -ENOMEM;
  109. power->genpd_data.domains = power->genpd;
  110. ndom = 0;
  111. for (entry = table; entry->name; entry++) {
  112. struct bcm63xx_power_dev *pmd = &power->dev[ndom];
  113. bool is_on;
  114. pmd->power = power;
  115. pmd->mask = BIT(entry->bit);
  116. pmd->genpd.name = entry->name;
  117. pmd->genpd.flags = entry->flags;
  118. ret = bcm63xx_power_get_state(pmd, &is_on);
  119. if (ret)
  120. dev_warn(dev, "unable to get current state for %s\n",
  121. pmd->genpd.name);
  122. pmd->genpd.power_on = bcm63xx_power_on;
  123. pmd->genpd.power_off = bcm63xx_power_off;
  124. pm_genpd_init(&pmd->genpd, NULL, !is_on);
  125. power->genpd[entry->bit] = &pmd->genpd;
  126. ndom++;
  127. }
  128. spin_lock_init(&power->lock);
  129. ret = of_genpd_add_provider_onecell(np, &power->genpd_data);
  130. if (ret) {
  131. dev_err(dev, "failed to register genpd driver: %d\n", ret);
  132. return ret;
  133. }
  134. dev_info(dev, "registered %u power domains\n", ndom);
  135. return 0;
  136. }
  137. static const struct bcm63xx_power_data bcm6318_power_domains[] = {
  138. {
  139. .name = "pcie",
  140. .bit = BCM6318_POWER_DOMAIN_PCIE,
  141. }, {
  142. .name = "usb",
  143. .bit = BCM6318_POWER_DOMAIN_USB,
  144. }, {
  145. .name = "ephy0",
  146. .bit = BCM6318_POWER_DOMAIN_EPHY0,
  147. }, {
  148. .name = "ephy1",
  149. .bit = BCM6318_POWER_DOMAIN_EPHY1,
  150. }, {
  151. .name = "ephy2",
  152. .bit = BCM6318_POWER_DOMAIN_EPHY2,
  153. }, {
  154. .name = "ephy3",
  155. .bit = BCM6318_POWER_DOMAIN_EPHY3,
  156. }, {
  157. .name = "ldo2p5",
  158. .bit = BCM6318_POWER_DOMAIN_LDO2P5,
  159. .flags = GENPD_FLAG_ALWAYS_ON,
  160. }, {
  161. .name = "ldo2p9",
  162. .bit = BCM6318_POWER_DOMAIN_LDO2P9,
  163. .flags = GENPD_FLAG_ALWAYS_ON,
  164. }, {
  165. .name = "sw1p0",
  166. .bit = BCM6318_POWER_DOMAIN_SW1P0,
  167. .flags = GENPD_FLAG_ALWAYS_ON,
  168. }, {
  169. .name = "pad",
  170. .bit = BCM6318_POWER_DOMAIN_PAD,
  171. .flags = GENPD_FLAG_ALWAYS_ON,
  172. }, {
  173. /* sentinel */
  174. },
  175. };
  176. static const struct bcm63xx_power_data bcm6328_power_domains[] = {
  177. {
  178. .name = "adsl2-mips",
  179. .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS,
  180. }, {
  181. .name = "adsl2-phy",
  182. .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY,
  183. }, {
  184. .name = "adsl2-afe",
  185. .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE,
  186. }, {
  187. .name = "sar",
  188. .bit = BCM6328_POWER_DOMAIN_SAR,
  189. }, {
  190. .name = "pcm",
  191. .bit = BCM6328_POWER_DOMAIN_PCM,
  192. }, {
  193. .name = "usbd",
  194. .bit = BCM6328_POWER_DOMAIN_USBD,
  195. }, {
  196. .name = "usbh",
  197. .bit = BCM6328_POWER_DOMAIN_USBH,
  198. }, {
  199. .name = "pcie",
  200. .bit = BCM6328_POWER_DOMAIN_PCIE,
  201. }, {
  202. .name = "robosw",
  203. .bit = BCM6328_POWER_DOMAIN_ROBOSW,
  204. }, {
  205. .name = "ephy",
  206. .bit = BCM6328_POWER_DOMAIN_EPHY,
  207. }, {
  208. /* sentinel */
  209. },
  210. };
  211. static const struct bcm63xx_power_data bcm6362_power_domains[] = {
  212. {
  213. .name = "sar",
  214. .bit = BCM6362_POWER_DOMAIN_SAR,
  215. }, {
  216. .name = "ipsec",
  217. .bit = BCM6362_POWER_DOMAIN_IPSEC,
  218. }, {
  219. .name = "mips",
  220. .bit = BCM6362_POWER_DOMAIN_MIPS,
  221. .flags = GENPD_FLAG_ALWAYS_ON,
  222. }, {
  223. .name = "dect",
  224. .bit = BCM6362_POWER_DOMAIN_DECT,
  225. }, {
  226. .name = "usbh",
  227. .bit = BCM6362_POWER_DOMAIN_USBH,
  228. }, {
  229. .name = "usbd",
  230. .bit = BCM6362_POWER_DOMAIN_USBD,
  231. }, {
  232. .name = "robosw",
  233. .bit = BCM6362_POWER_DOMAIN_ROBOSW,
  234. }, {
  235. .name = "pcm",
  236. .bit = BCM6362_POWER_DOMAIN_PCM,
  237. }, {
  238. .name = "periph",
  239. .bit = BCM6362_POWER_DOMAIN_PERIPH,
  240. .flags = GENPD_FLAG_ALWAYS_ON,
  241. }, {
  242. .name = "adsl-phy",
  243. .bit = BCM6362_POWER_DOMAIN_ADSL_PHY,
  244. }, {
  245. .name = "gmii-pads",
  246. .bit = BCM6362_POWER_DOMAIN_GMII_PADS,
  247. }, {
  248. .name = "fap",
  249. .bit = BCM6362_POWER_DOMAIN_FAP,
  250. }, {
  251. .name = "pcie",
  252. .bit = BCM6362_POWER_DOMAIN_PCIE,
  253. }, {
  254. .name = "wlan-pads",
  255. .bit = BCM6362_POWER_DOMAIN_WLAN_PADS,
  256. }, {
  257. /* sentinel */
  258. },
  259. };
  260. static const struct bcm63xx_power_data bcm63268_power_domains[] = {
  261. {
  262. .name = "sar",
  263. .bit = BCM63268_POWER_DOMAIN_SAR,
  264. }, {
  265. .name = "ipsec",
  266. .bit = BCM63268_POWER_DOMAIN_IPSEC,
  267. }, {
  268. .name = "mips",
  269. .bit = BCM63268_POWER_DOMAIN_MIPS,
  270. .flags = GENPD_FLAG_ALWAYS_ON,
  271. }, {
  272. .name = "dect",
  273. .bit = BCM63268_POWER_DOMAIN_DECT,
  274. }, {
  275. .name = "usbh",
  276. .bit = BCM63268_POWER_DOMAIN_USBH,
  277. }, {
  278. .name = "usbd",
  279. .bit = BCM63268_POWER_DOMAIN_USBD,
  280. }, {
  281. .name = "robosw",
  282. .bit = BCM63268_POWER_DOMAIN_ROBOSW,
  283. }, {
  284. .name = "pcm",
  285. .bit = BCM63268_POWER_DOMAIN_PCM,
  286. }, {
  287. .name = "periph",
  288. .bit = BCM63268_POWER_DOMAIN_PERIPH,
  289. .flags = GENPD_FLAG_ALWAYS_ON,
  290. }, {
  291. .name = "vdsl-phy",
  292. .bit = BCM63268_POWER_DOMAIN_VDSL_PHY,
  293. }, {
  294. .name = "vdsl-mips",
  295. .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS,
  296. }, {
  297. .name = "fap",
  298. .bit = BCM63268_POWER_DOMAIN_FAP,
  299. }, {
  300. .name = "pcie",
  301. .bit = BCM63268_POWER_DOMAIN_PCIE,
  302. }, {
  303. .name = "wlan-pads",
  304. .bit = BCM63268_POWER_DOMAIN_WLAN_PADS,
  305. }, {
  306. /* sentinel */
  307. },
  308. };
  309. static const struct of_device_id bcm63xx_power_of_match[] = {
  310. {
  311. .compatible = "brcm,bcm6318-power-controller",
  312. .data = &bcm6318_power_domains,
  313. }, {
  314. .compatible = "brcm,bcm6328-power-controller",
  315. .data = &bcm6328_power_domains,
  316. }, {
  317. .compatible = "brcm,bcm6362-power-controller",
  318. .data = &bcm6362_power_domains,
  319. }, {
  320. .compatible = "brcm,bcm63268-power-controller",
  321. .data = &bcm63268_power_domains,
  322. }, {
  323. /* sentinel */
  324. }
  325. };
  326. static struct platform_driver bcm63xx_power_driver = {
  327. .driver = {
  328. .name = "bcm63xx-power-controller",
  329. .of_match_table = bcm63xx_power_of_match,
  330. },
  331. .probe = bcm63xx_power_probe,
  332. };
  333. builtin_platform_driver(bcm63xx_power_driver);