bcm2835-pm.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * PM MFD driver for Broadcom BCM2835
  4. *
  5. * This driver binds to the PM block and creates the MFD device for
  6. * the WDT and power drivers.
  7. */
  8. #include <linux/delay.h>
  9. #include <linux/io.h>
  10. #include <linux/mfd/bcm2835-pm.h>
  11. #include <linux/mfd/core.h>
  12. #include <linux/module.h>
  13. #include <linux/of_address.h>
  14. #include <linux/of_platform.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/types.h>
  17. #include <linux/watchdog.h>
  18. static const struct mfd_cell bcm2835_pm_devs[] = {
  19. { .name = "bcm2835-wdt" },
  20. };
  21. static const struct mfd_cell bcm2835_power_devs[] = {
  22. { .name = "bcm2835-power" },
  23. };
  24. static int bcm2835_pm_get_pdata(struct platform_device *pdev,
  25. struct bcm2835_pm *pm)
  26. {
  27. if (of_find_property(pm->dev->of_node, "reg-names", NULL)) {
  28. struct resource *res;
  29. pm->base = devm_platform_ioremap_resource_byname(pdev, "pm");
  30. if (IS_ERR(pm->base))
  31. return PTR_ERR(pm->base);
  32. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "asb");
  33. if (res) {
  34. pm->asb = devm_ioremap_resource(&pdev->dev, res);
  35. if (IS_ERR(pm->asb))
  36. pm->asb = NULL;
  37. }
  38. res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
  39. "rpivid_asb");
  40. if (res) {
  41. pm->rpivid_asb = devm_ioremap_resource(&pdev->dev, res);
  42. if (IS_ERR(pm->rpivid_asb))
  43. pm->rpivid_asb = NULL;
  44. }
  45. return 0;
  46. }
  47. /* If no 'reg-names' property is found we can assume we're using old DTB. */
  48. pm->base = devm_platform_ioremap_resource(pdev, 0);
  49. if (IS_ERR(pm->base))
  50. return PTR_ERR(pm->base);
  51. pm->asb = devm_platform_ioremap_resource(pdev, 1);
  52. if (IS_ERR(pm->asb))
  53. pm->asb = NULL;
  54. pm->rpivid_asb = devm_platform_ioremap_resource(pdev, 2);
  55. if (IS_ERR(pm->rpivid_asb))
  56. pm->rpivid_asb = NULL;
  57. return 0;
  58. }
  59. static int bcm2835_pm_probe(struct platform_device *pdev)
  60. {
  61. struct device *dev = &pdev->dev;
  62. struct bcm2835_pm *pm;
  63. int ret;
  64. pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
  65. if (!pm)
  66. return -ENOMEM;
  67. platform_set_drvdata(pdev, pm);
  68. pm->dev = dev;
  69. ret = bcm2835_pm_get_pdata(pdev, pm);
  70. if (ret)
  71. return ret;
  72. ret = devm_mfd_add_devices(dev, -1,
  73. bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs),
  74. NULL, 0, NULL);
  75. if (ret)
  76. return ret;
  77. /*
  78. * We'll use the presence of the AXI ASB regs in the
  79. * bcm2835-pm binding as the key for whether we can reference
  80. * the full PM register range and support power domains.
  81. */
  82. if (pm->asb)
  83. return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
  84. ARRAY_SIZE(bcm2835_power_devs),
  85. NULL, 0, NULL);
  86. return 0;
  87. }
  88. static const struct of_device_id bcm2835_pm_of_match[] = {
  89. { .compatible = "brcm,bcm2835-pm-wdt", },
  90. { .compatible = "brcm,bcm2835-pm", },
  91. { .compatible = "brcm,bcm2711-pm", },
  92. {},
  93. };
  94. MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
  95. static struct platform_driver bcm2835_pm_driver = {
  96. .probe = bcm2835_pm_probe,
  97. .driver = {
  98. .name = "bcm2835-pm",
  99. .of_match_table = bcm2835_pm_of_match,
  100. },
  101. };
  102. module_platform_driver(bcm2835_pm_driver);
  103. MODULE_AUTHOR("Eric Anholt <[email protected]>");
  104. MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM MFD");
  105. MODULE_LICENSE("GPL");