xref: /openbmc/linux/drivers/mfd/bcm2835-pm.c (revision 1ba58fbb)
15e6acc3eSEric Anholt // SPDX-License-Identifier: GPL-2.0+
25e6acc3eSEric Anholt /*
35e6acc3eSEric Anholt  * PM MFD driver for Broadcom BCM2835
45e6acc3eSEric Anholt  *
55e6acc3eSEric Anholt  * This driver binds to the PM block and creates the MFD device for
6670c6726SEric Anholt  * the WDT and power drivers.
75e6acc3eSEric Anholt  */
85e6acc3eSEric Anholt 
95e6acc3eSEric Anholt #include <linux/delay.h>
105e6acc3eSEric Anholt #include <linux/io.h>
115e6acc3eSEric Anholt #include <linux/mfd/bcm2835-pm.h>
125e6acc3eSEric Anholt #include <linux/mfd/core.h>
135e6acc3eSEric Anholt #include <linux/module.h>
145e6acc3eSEric Anholt #include <linux/of_address.h>
155e6acc3eSEric Anholt #include <linux/of_platform.h>
165e6acc3eSEric Anholt #include <linux/platform_device.h>
175e6acc3eSEric Anholt #include <linux/types.h>
185e6acc3eSEric Anholt #include <linux/watchdog.h>
195e6acc3eSEric Anholt 
205e6acc3eSEric Anholt static const struct mfd_cell bcm2835_pm_devs[] = {
215e6acc3eSEric Anholt 	{ .name = "bcm2835-wdt" },
225e6acc3eSEric Anholt };
235e6acc3eSEric Anholt 
24670c6726SEric Anholt static const struct mfd_cell bcm2835_power_devs[] = {
25670c6726SEric Anholt 	{ .name = "bcm2835-power" },
26670c6726SEric Anholt };
27670c6726SEric Anholt 
bcm2835_pm_get_pdata(struct platform_device * pdev,struct bcm2835_pm * pm)2801e7865dSNicolas Saenz Julienne static int bcm2835_pm_get_pdata(struct platform_device *pdev,
2901e7865dSNicolas Saenz Julienne 				struct bcm2835_pm *pm)
3001e7865dSNicolas Saenz Julienne {
31*1ba58fbbSRob Herring 	if (of_property_present(pm->dev->of_node, "reg-names")) {
3201e7865dSNicolas Saenz Julienne 		struct resource *res;
3301e7865dSNicolas Saenz Julienne 
3401e7865dSNicolas Saenz Julienne 		pm->base = devm_platform_ioremap_resource_byname(pdev, "pm");
3501e7865dSNicolas Saenz Julienne 		if (IS_ERR(pm->base))
3601e7865dSNicolas Saenz Julienne 			return PTR_ERR(pm->base);
3701e7865dSNicolas Saenz Julienne 
3801e7865dSNicolas Saenz Julienne 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "asb");
3901e7865dSNicolas Saenz Julienne 		if (res) {
4001e7865dSNicolas Saenz Julienne 			pm->asb = devm_ioremap_resource(&pdev->dev, res);
4101e7865dSNicolas Saenz Julienne 			if (IS_ERR(pm->asb))
4201e7865dSNicolas Saenz Julienne 				pm->asb = NULL;
4301e7865dSNicolas Saenz Julienne 		}
4401e7865dSNicolas Saenz Julienne 
45df762342SStefan Wahren 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
46df762342SStefan Wahren 						    "rpivid_asb");
47df762342SStefan Wahren 		if (res) {
48df762342SStefan Wahren 			pm->rpivid_asb = devm_ioremap_resource(&pdev->dev, res);
49df762342SStefan Wahren 			if (IS_ERR(pm->rpivid_asb))
50df762342SStefan Wahren 				pm->rpivid_asb = NULL;
51df762342SStefan Wahren 		}
52df762342SStefan Wahren 
5301e7865dSNicolas Saenz Julienne 		return 0;
5401e7865dSNicolas Saenz Julienne 	}
5501e7865dSNicolas Saenz Julienne 
5601e7865dSNicolas Saenz Julienne 	/* If no 'reg-names' property is found we can assume we're using old DTB. */
5701e7865dSNicolas Saenz Julienne 	pm->base = devm_platform_ioremap_resource(pdev, 0);
5801e7865dSNicolas Saenz Julienne 	if (IS_ERR(pm->base))
5901e7865dSNicolas Saenz Julienne 		return PTR_ERR(pm->base);
6001e7865dSNicolas Saenz Julienne 
6101e7865dSNicolas Saenz Julienne 	pm->asb = devm_platform_ioremap_resource(pdev, 1);
6201e7865dSNicolas Saenz Julienne 	if (IS_ERR(pm->asb))
6301e7865dSNicolas Saenz Julienne 		pm->asb = NULL;
6401e7865dSNicolas Saenz Julienne 
65df762342SStefan Wahren 	pm->rpivid_asb = devm_platform_ioremap_resource(pdev, 2);
66df762342SStefan Wahren 	if (IS_ERR(pm->rpivid_asb))
67df762342SStefan Wahren 		pm->rpivid_asb = NULL;
68df762342SStefan Wahren 
6901e7865dSNicolas Saenz Julienne 	return 0;
7001e7865dSNicolas Saenz Julienne }
7101e7865dSNicolas Saenz Julienne 
bcm2835_pm_probe(struct platform_device * pdev)725e6acc3eSEric Anholt static int bcm2835_pm_probe(struct platform_device *pdev)
735e6acc3eSEric Anholt {
745e6acc3eSEric Anholt 	struct device *dev = &pdev->dev;
755e6acc3eSEric Anholt 	struct bcm2835_pm *pm;
76670c6726SEric Anholt 	int ret;
775e6acc3eSEric Anholt 
785e6acc3eSEric Anholt 	pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
795e6acc3eSEric Anholt 	if (!pm)
805e6acc3eSEric Anholt 		return -ENOMEM;
815e6acc3eSEric Anholt 	platform_set_drvdata(pdev, pm);
825e6acc3eSEric Anholt 
835e6acc3eSEric Anholt 	pm->dev = dev;
845e6acc3eSEric Anholt 
8501e7865dSNicolas Saenz Julienne 	ret = bcm2835_pm_get_pdata(pdev, pm);
8601e7865dSNicolas Saenz Julienne 	if (ret)
8701e7865dSNicolas Saenz Julienne 		return ret;
885e6acc3eSEric Anholt 
89670c6726SEric Anholt 	ret = devm_mfd_add_devices(dev, -1,
905e6acc3eSEric Anholt 				   bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs),
915e6acc3eSEric Anholt 				   NULL, 0, NULL);
92670c6726SEric Anholt 	if (ret)
93670c6726SEric Anholt 		return ret;
94670c6726SEric Anholt 
9501e7865dSNicolas Saenz Julienne 	/*
9601e7865dSNicolas Saenz Julienne 	 * We'll use the presence of the AXI ASB regs in the
97670c6726SEric Anholt 	 * bcm2835-pm binding as the key for whether we can reference
98670c6726SEric Anholt 	 * the full PM register range and support power domains.
99670c6726SEric Anholt 	 */
10001e7865dSNicolas Saenz Julienne 	if (pm->asb)
10101e7865dSNicolas Saenz Julienne 		return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
102670c6726SEric Anholt 					    ARRAY_SIZE(bcm2835_power_devs),
103670c6726SEric Anholt 					    NULL, 0, NULL);
104670c6726SEric Anholt 	return 0;
1055e6acc3eSEric Anholt }
1065e6acc3eSEric Anholt 
1075e6acc3eSEric Anholt static const struct of_device_id bcm2835_pm_of_match[] = {
1085e6acc3eSEric Anholt 	{ .compatible = "brcm,bcm2835-pm-wdt", },
109670c6726SEric Anholt 	{ .compatible = "brcm,bcm2835-pm", },
110df762342SStefan Wahren 	{ .compatible = "brcm,bcm2711-pm", },
1115e6acc3eSEric Anholt 	{},
1125e6acc3eSEric Anholt };
1135e6acc3eSEric Anholt MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
1145e6acc3eSEric Anholt 
1155e6acc3eSEric Anholt static struct platform_driver bcm2835_pm_driver = {
1165e6acc3eSEric Anholt 	.probe		= bcm2835_pm_probe,
1175e6acc3eSEric Anholt 	.driver = {
1185e6acc3eSEric Anholt 		.name =	"bcm2835-pm",
1195e6acc3eSEric Anholt 		.of_match_table = bcm2835_pm_of_match,
1205e6acc3eSEric Anholt 	},
1215e6acc3eSEric Anholt };
1225e6acc3eSEric Anholt module_platform_driver(bcm2835_pm_driver);
1235e6acc3eSEric Anholt 
1245e6acc3eSEric Anholt MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
1255e6acc3eSEric Anholt MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM MFD");
126