xref: /linux/drivers/soc/apple/tunable.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*a722de30SSven Peter // SPDX-License-Identifier: GPL-2.0-only OR MIT
2*a722de30SSven Peter /*
3*a722de30SSven Peter  * Apple Silicon hardware tunable support
4*a722de30SSven Peter  *
5*a722de30SSven Peter  * Each tunable is a list with each entry containing a offset into the MMIO
6*a722de30SSven Peter  * region, a mask of bits to be cleared and a set of bits to be set. These
7*a722de30SSven Peter  * tunables are passed along by the previous boot stages and vary from device
8*a722de30SSven Peter  * to device such that they cannot be hardcoded in the individual drivers.
9*a722de30SSven Peter  *
10*a722de30SSven Peter  * Copyright (C) The Asahi Linux Contributors
11*a722de30SSven Peter  */
12*a722de30SSven Peter 
13*a722de30SSven Peter #include <linux/io.h>
14*a722de30SSven Peter #include <linux/module.h>
15*a722de30SSven Peter #include <linux/of.h>
16*a722de30SSven Peter #include <linux/overflow.h>
17*a722de30SSven Peter #include <linux/soc/apple/tunable.h>
18*a722de30SSven Peter 
19*a722de30SSven Peter struct apple_tunable *devm_apple_tunable_parse(struct device *dev,
20*a722de30SSven Peter 					       struct device_node *np,
21*a722de30SSven Peter 					       const char *name,
22*a722de30SSven Peter 					       struct resource *res)
23*a722de30SSven Peter {
24*a722de30SSven Peter 	struct apple_tunable *tunable;
25*a722de30SSven Peter 	struct property *prop;
26*a722de30SSven Peter 	const __be32 *p;
27*a722de30SSven Peter 	size_t sz;
28*a722de30SSven Peter 	int i;
29*a722de30SSven Peter 
30*a722de30SSven Peter 	if (resource_size(res) < 4)
31*a722de30SSven Peter 		return ERR_PTR(-EINVAL);
32*a722de30SSven Peter 
33*a722de30SSven Peter 	prop = of_find_property(np, name, NULL);
34*a722de30SSven Peter 	if (!prop)
35*a722de30SSven Peter 		return ERR_PTR(-ENOENT);
36*a722de30SSven Peter 
37*a722de30SSven Peter 	if (prop->length % (3 * sizeof(u32)))
38*a722de30SSven Peter 		return ERR_PTR(-EINVAL);
39*a722de30SSven Peter 	sz = prop->length / (3 * sizeof(u32));
40*a722de30SSven Peter 
41*a722de30SSven Peter 	tunable = devm_kzalloc(dev, struct_size(tunable, values, sz), GFP_KERNEL);
42*a722de30SSven Peter 	if (!tunable)
43*a722de30SSven Peter 		return ERR_PTR(-ENOMEM);
44*a722de30SSven Peter 	tunable->sz = sz;
45*a722de30SSven Peter 
46*a722de30SSven Peter 	for (i = 0, p = NULL; i < tunable->sz; ++i) {
47*a722de30SSven Peter 		p = of_prop_next_u32(prop, p, &tunable->values[i].offset);
48*a722de30SSven Peter 		p = of_prop_next_u32(prop, p, &tunable->values[i].mask);
49*a722de30SSven Peter 		p = of_prop_next_u32(prop, p, &tunable->values[i].value);
50*a722de30SSven Peter 
51*a722de30SSven Peter 		/* Sanity checks to catch bugs in our bootloader */
52*a722de30SSven Peter 		if (tunable->values[i].offset % 4)
53*a722de30SSven Peter 			return ERR_PTR(-EINVAL);
54*a722de30SSven Peter 		if (tunable->values[i].offset > (resource_size(res) - 4))
55*a722de30SSven Peter 			return ERR_PTR(-EINVAL);
56*a722de30SSven Peter 	}
57*a722de30SSven Peter 
58*a722de30SSven Peter 	return tunable;
59*a722de30SSven Peter }
60*a722de30SSven Peter EXPORT_SYMBOL(devm_apple_tunable_parse);
61*a722de30SSven Peter 
62*a722de30SSven Peter void apple_tunable_apply(void __iomem *regs, struct apple_tunable *tunable)
63*a722de30SSven Peter {
64*a722de30SSven Peter 	size_t i;
65*a722de30SSven Peter 
66*a722de30SSven Peter 	for (i = 0; i < tunable->sz; ++i) {
67*a722de30SSven Peter 		u32 val, old_val;
68*a722de30SSven Peter 
69*a722de30SSven Peter 		old_val = readl(regs + tunable->values[i].offset);
70*a722de30SSven Peter 		val = old_val & ~tunable->values[i].mask;
71*a722de30SSven Peter 		val |= tunable->values[i].value;
72*a722de30SSven Peter 		if (val != old_val)
73*a722de30SSven Peter 			writel(val, regs + tunable->values[i].offset);
74*a722de30SSven Peter 	}
75*a722de30SSven Peter }
76*a722de30SSven Peter EXPORT_SYMBOL(apple_tunable_apply);
77*a722de30SSven Peter 
78*a722de30SSven Peter MODULE_LICENSE("Dual MIT/GPL");
79*a722de30SSven Peter MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
80*a722de30SSven Peter MODULE_DESCRIPTION("Apple Silicon hardware tunable support");
81