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