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,en7581" }, 111 {}, 112 }; 113 MODULE_DEVICE_TABLE(of, airoha_cpufreq_match_list); 114 115 static int __init airoha_cpufreq_init(void) 116 { 117 struct device_node *np = of_find_node_by_path("/"); 118 const struct of_device_id *match; 119 int ret; 120 121 if (!np) 122 return -ENODEV; 123 124 match = of_match_node(airoha_cpufreq_match_list, np); 125 of_node_put(np); 126 if (!match) 127 return -ENODEV; 128 129 ret = platform_driver_register(&airoha_cpufreq_driver); 130 if (unlikely(ret < 0)) 131 return ret; 132 133 cpufreq_pdev = platform_device_register_data(NULL, "airoha-cpufreq", 134 -1, match, sizeof(*match)); 135 ret = PTR_ERR_OR_ZERO(cpufreq_pdev); 136 if (ret) 137 platform_driver_unregister(&airoha_cpufreq_driver); 138 139 return ret; 140 } 141 module_init(airoha_cpufreq_init); 142 143 static void __exit airoha_cpufreq_exit(void) 144 { 145 platform_device_unregister(cpufreq_pdev); 146 platform_driver_unregister(&airoha_cpufreq_driver); 147 } 148 module_exit(airoha_cpufreq_exit); 149 150 MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); 151 MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs"); 152 MODULE_LICENSE("GPL"); 153