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