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