tunable.c 2.2 KB

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