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