airoha-cpufreq.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/bitfield.h>
  3. #include <linux/cpufreq.h>
  4. #include <linux/module.h>
  5. #include <linux/platform_device.h>
  6. #include <linux/pm_domain.h>
  7. #include <linux/pm_runtime.h>
  8. #include <linux/slab.h>
  9. #include "cpufreq-dt.h"
  10. struct airoha_cpufreq_priv {
  11. int opp_token;
  12. struct dev_pm_domain_list *pd_list;
  13. struct platform_device *cpufreq_dt;
  14. };
  15. static struct platform_device *cpufreq_pdev;
  16. /* NOP function to disable OPP from setting clock */
  17. static int airoha_cpufreq_config_clks_nop(struct device *dev,
  18. struct opp_table *opp_table,
  19. struct dev_pm_opp *opp,
  20. void *data, bool scaling_down)
  21. {
  22. return 0;
  23. }
  24. static const char * const airoha_cpufreq_clk_names[] = { "cpu", NULL };
  25. static const char * const airoha_cpufreq_pd_names[] = { "perf" };
  26. static int airoha_cpufreq_probe(struct platform_device *pdev)
  27. {
  28. const struct dev_pm_domain_attach_data attach_data = {
  29. .pd_names = airoha_cpufreq_pd_names,
  30. .num_pd_names = ARRAY_SIZE(airoha_cpufreq_pd_names),
  31. .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
  32. };
  33. struct dev_pm_opp_config config = {
  34. .clk_names = airoha_cpufreq_clk_names,
  35. .config_clks = airoha_cpufreq_config_clks_nop,
  36. };
  37. struct platform_device *cpufreq_dt;
  38. struct airoha_cpufreq_priv *priv;
  39. struct device *dev = &pdev->dev;
  40. struct device *cpu_dev;
  41. int ret;
  42. /* CPUs refer to the same OPP table */
  43. cpu_dev = get_cpu_device(0);
  44. if (!cpu_dev)
  45. return -ENODEV;
  46. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  47. if (!priv)
  48. return -ENOMEM;
  49. /* Set OPP table conf with NOP config_clks */
  50. priv->opp_token = dev_pm_opp_set_config(cpu_dev, &config);
  51. if (priv->opp_token < 0)
  52. return dev_err_probe(dev, priv->opp_token, "Failed to set OPP config\n");
  53. /* Attach PM for OPP */
  54. ret = dev_pm_domain_attach_list(cpu_dev, &attach_data,
  55. &priv->pd_list);
  56. if (ret)
  57. goto clear_opp_config;
  58. cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
  59. ret = PTR_ERR_OR_ZERO(cpufreq_dt);
  60. if (ret) {
  61. dev_err(dev, "failed to create cpufreq-dt device: %d\n", ret);
  62. goto detach_pm;
  63. }
  64. priv->cpufreq_dt = cpufreq_dt;
  65. platform_set_drvdata(pdev, priv);
  66. return 0;
  67. detach_pm:
  68. dev_pm_domain_detach_list(priv->pd_list);
  69. clear_opp_config:
  70. dev_pm_opp_clear_config(priv->opp_token);
  71. return ret;
  72. }
  73. static void airoha_cpufreq_remove(struct platform_device *pdev)
  74. {
  75. struct airoha_cpufreq_priv *priv = platform_get_drvdata(pdev);
  76. platform_device_unregister(priv->cpufreq_dt);
  77. dev_pm_domain_detach_list(priv->pd_list);
  78. dev_pm_opp_clear_config(priv->opp_token);
  79. }
  80. static struct platform_driver airoha_cpufreq_driver = {
  81. .probe = airoha_cpufreq_probe,
  82. .remove = airoha_cpufreq_remove,
  83. .driver = {
  84. .name = "airoha-cpufreq",
  85. },
  86. };
  87. static const struct of_device_id airoha_cpufreq_match_list[] __initconst = {
  88. { .compatible = "airoha,an7583" },
  89. { .compatible = "airoha,en7581" },
  90. {},
  91. };
  92. MODULE_DEVICE_TABLE(of, airoha_cpufreq_match_list);
  93. static int __init airoha_cpufreq_init(void)
  94. {
  95. struct device_node *np = of_find_node_by_path("/");
  96. const struct of_device_id *match;
  97. int ret;
  98. if (!np)
  99. return -ENODEV;
  100. match = of_match_node(airoha_cpufreq_match_list, np);
  101. of_node_put(np);
  102. if (!match)
  103. return -ENODEV;
  104. ret = platform_driver_register(&airoha_cpufreq_driver);
  105. if (unlikely(ret < 0))
  106. return ret;
  107. cpufreq_pdev = platform_device_register_data(NULL, "airoha-cpufreq",
  108. -1, match, sizeof(*match));
  109. ret = PTR_ERR_OR_ZERO(cpufreq_pdev);
  110. if (ret)
  111. platform_driver_unregister(&airoha_cpufreq_driver);
  112. return ret;
  113. }
  114. module_init(airoha_cpufreq_init);
  115. static void __exit airoha_cpufreq_exit(void)
  116. {
  117. platform_device_unregister(cpufreq_pdev);
  118. platform_driver_unregister(&airoha_cpufreq_driver);
  119. }
  120. module_exit(airoha_cpufreq_exit);
  121. MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
  122. MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs");
  123. MODULE_LICENSE("GPL");