bcm63xx-power.c 8.0 KB

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