Skip to content

Commit eacc31a

Browse files
WhatAmISupposedToPutHerejannau
authored andcommitted
pmdomain: apple: Add PMP reporting interface
This driver manages a shared SRAM area that is used to communicate desired power states of devices that PMP manages. Signed-off-by: Sasha Finkelstein <fnkl.kernel@gmail.com>
1 parent 08f23e1 commit eacc31a

2 files changed

Lines changed: 236 additions & 0 deletions

File tree

drivers/pmdomain/apple/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += pmgr-pwrstate.o
3+
obj-$(CONFIG_APPLE_PMP) += pmp-report.o
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Apple SoC PMP power state reporting driver
4+
*
5+
* Copyright The Asahi Linux Contributors
6+
*/
7+
8+
#include <linux/iopoll.h>
9+
#include <linux/module.h>
10+
#include <linux/of.h>
11+
#include <linux/of_address.h>
12+
#include <linux/of_platform.h>
13+
#include <linux/platform_device.h>
14+
#include <linux/pm_domain.h>
15+
16+
#define PMP_REPORT_READY 0x1
17+
18+
struct apple_pmp_report_offsets {
19+
u32 tgt_read;
20+
u32 tgt_write;
21+
u32 actual;
22+
u32 status;
23+
};
24+
25+
struct apple_pmp_report {
26+
struct device *dev;
27+
const struct apple_pmp_report_offsets *offsets;
28+
void __iomem *base;
29+
spinlock_t lock;
30+
};
31+
32+
static int apple_pmp_report_probe(struct platform_device *pdev)
33+
{
34+
struct device *dev = &pdev->dev;
35+
struct device_node *np = dev->of_node;
36+
struct apple_pmp_report *rep;
37+
int ret;
38+
39+
rep = devm_kzalloc(dev, sizeof(*rep), GFP_KERNEL);
40+
if (!rep)
41+
return -ENOMEM;
42+
43+
rep->dev = dev;
44+
rep->base = devm_platform_ioremap_resource(pdev, 0);
45+
if (IS_ERR(rep->base))
46+
return PTR_ERR(rep->base);
47+
rep->offsets = of_device_get_match_data(dev);
48+
dev_set_drvdata(dev, rep);
49+
ret = of_platform_populate(np, NULL, NULL, dev);
50+
if (ret)
51+
return dev_err_probe(dev, ret, "failed to create child devices\n");
52+
53+
return 0;
54+
}
55+
56+
static const struct apple_pmp_report_offsets apple_pmp_offsets_t600x = {
57+
.tgt_read = 0xf80,
58+
.tgt_write = 0x107c0,
59+
.actual = 0x1000,
60+
.status = 0x10,
61+
};
62+
63+
static const struct apple_pmp_report_offsets apple_pmp_offsets_t602x = {
64+
.tgt_read = 0x2000,
65+
.tgt_write = 0x11000,
66+
.actual = 0x2080,
67+
.status = 0x10,
68+
};
69+
70+
static const struct apple_pmp_report_offsets apple_pmp_offsets_t8112 = {
71+
.tgt_read = 0xa00,
72+
.tgt_write = 0x10500,
73+
.actual = 0xa40,
74+
.status = 0x10,
75+
};
76+
77+
static const struct of_device_id apple_pmp_report_of_match[] = {
78+
{ .compatible = "apple,t6000-pmp-v2-report", .data = &apple_pmp_offsets_t600x },
79+
{ .compatible = "apple,t6020-pmp-v2-report", .data = &apple_pmp_offsets_t602x },
80+
{ .compatible = "apple,t8112-pmp-v2-report", .data = &apple_pmp_offsets_t8112 },
81+
{}
82+
};
83+
84+
static struct platform_driver apple_pmp_report_driver = {
85+
.probe = apple_pmp_report_probe,
86+
.driver = {
87+
.name = "apple-pmp-report",
88+
.of_match_table = apple_pmp_report_of_match,
89+
},
90+
};
91+
92+
struct apple_pmp_report_entry {
93+
struct device *dev;
94+
struct generic_pm_domain genpd;
95+
u32 id;
96+
};
97+
98+
#define genpd_to_apple_pmp_report_entry(_genpd) \
99+
container_of(_genpd, struct apple_pmp_report_entry, genpd)
100+
101+
static int apple_pmp_report_set_state(struct generic_pm_domain *genpd, bool enable)
102+
{
103+
struct apple_pmp_report_entry *ent = genpd_to_apple_pmp_report_entry(genpd);
104+
struct apple_pmp_report *rep = dev_get_drvdata(ent->dev->parent);
105+
u64 bit_val = 1 << ent->id;
106+
u64 val;
107+
unsigned long flags;
108+
109+
spin_lock_irqsave(&rep->lock, flags);
110+
val = readq(rep->base + rep->offsets->tgt_read);
111+
val &= ~bit_val;
112+
if (enable)
113+
val |= bit_val;
114+
writeq(val, rep->base + rep->offsets->tgt_write);
115+
spin_unlock_irqrestore(&rep->lock, flags);
116+
val = readq(rep->base + rep->offsets->status);
117+
if ((val & PMP_REPORT_READY) == 0)
118+
return 0;
119+
return readq_poll_timeout_atomic(
120+
rep->base + rep->offsets->actual,
121+
val,
122+
!!(val & bit_val) == !!enable,
123+
100,
124+
50000);
125+
}
126+
127+
static int apple_pmp_report_entry_power_on(struct generic_pm_domain *genpd)
128+
{
129+
return apple_pmp_report_set_state(genpd, true);
130+
}
131+
132+
static int apple_pmp_report_entry_power_off(struct generic_pm_domain *genpd)
133+
{
134+
return apple_pmp_report_set_state(genpd, false);
135+
}
136+
137+
static int apple_pmp_report_entry_probe(struct platform_device *pdev)
138+
{
139+
struct device *dev = &pdev->dev;
140+
struct device_node *node = dev->of_node;
141+
struct apple_pmp_report_entry *ent;
142+
int ret;
143+
const char *name;
144+
struct of_phandle_iterator it;
145+
146+
ent = devm_kzalloc(dev, sizeof(*ent), GFP_KERNEL);
147+
if (!ent)
148+
return -ENOMEM;
149+
150+
ent->dev = dev;
151+
152+
ret = of_property_read_u32(node, "reg", &ent->id);
153+
if (ret)
154+
return dev_err_probe(dev, ret, "missing reg property\n");
155+
156+
ret = of_property_read_string(node, "label", &name);
157+
if (ret < 0)
158+
return dev_err_probe(dev, ret, "missing label property\n");
159+
160+
if (of_property_read_bool(node, "apple,always-on"))
161+
ent->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
162+
ent->genpd.name = name;
163+
ent->genpd.power_on = apple_pmp_report_entry_power_on;
164+
ent->genpd.power_off = apple_pmp_report_entry_power_off;
165+
166+
ret = pm_genpd_init(&ent->genpd, NULL, true);
167+
if (ret)
168+
return dev_err_probe(dev, ret, "pm_genpd_init failed\n");
169+
170+
ret = of_genpd_add_provider_simple(node, &ent->genpd);
171+
if (ret)
172+
return dev_err_probe(dev, ret, "of_genpd_add_provider_simple failed\n");
173+
174+
of_for_each_phandle(&it, ret, node, "power-domains", "#power-domain-cells", -1) {
175+
struct of_phandle_args parent, child;
176+
177+
parent.np = it.node;
178+
parent.args_count = of_phandle_iterator_args(&it, parent.args, MAX_PHANDLE_ARGS);
179+
child.np = node;
180+
child.args_count = 0;
181+
ret = of_genpd_add_subdomain(&parent, &child);
182+
183+
if (ret == -EPROBE_DEFER) {
184+
of_node_put(parent.np);
185+
goto err_remove;
186+
} else if (ret < 0) {
187+
dev_err(dev, "failed to add to parent domain: %d (%s -> %s)\n",
188+
ret, it.node->name, node->name);
189+
of_node_put(parent.np);
190+
goto err_remove;
191+
}
192+
}
193+
194+
pm_genpd_remove_device(dev);
195+
196+
return 0;
197+
err_remove:
198+
of_genpd_del_provider(node);
199+
pm_genpd_remove(&ent->genpd);
200+
return ret;
201+
}
202+
203+
static const struct of_device_id apple_pmp_report_entry_of_match[] = {
204+
{ .compatible = "apple,t6000-pmp-v2-report-entry" },
205+
{}
206+
};
207+
208+
static struct platform_driver apple_pmp_report_entry_driver = {
209+
.probe = apple_pmp_report_entry_probe,
210+
.driver = {
211+
.name = "apple-pmp-report-entry",
212+
.of_match_table = apple_pmp_report_entry_of_match,
213+
},
214+
};
215+
216+
MODULE_DEVICE_TABLE(of, apple_pmp_report_of_match);
217+
MODULE_DEVICE_TABLE(of, apple_pmp_report_entry_of_match);
218+
219+
static int __init apple_pmp_report_init(void)
220+
{
221+
platform_driver_register(&apple_pmp_report_entry_driver);
222+
platform_driver_register(&apple_pmp_report_driver);
223+
return 0;
224+
}
225+
226+
static void __exit apple_pmp_report_exit(void)
227+
{
228+
platform_driver_unregister(&apple_pmp_report_entry_driver);
229+
platform_driver_unregister(&apple_pmp_report_driver);
230+
}
231+
232+
module_init(apple_pmp_report_init);
233+
module_exit(apple_pmp_report_exit);
234+
235+
MODULE_DESCRIPTION("PMP power state reporting driver for Apple SoCs");

0 commit comments

Comments
 (0)