1*82e703ddSChristian Marangi // SPDX-License-Identifier: GPL-2.0 2*82e703ddSChristian Marangi 3*82e703ddSChristian Marangi #include <linux/arm-smccc.h> 4*82e703ddSChristian Marangi #include <linux/bitfield.h> 5*82e703ddSChristian Marangi #include <linux/clk-provider.h> 6*82e703ddSChristian Marangi #include <linux/module.h> 7*82e703ddSChristian Marangi #include <linux/platform_device.h> 8*82e703ddSChristian Marangi #include <linux/pm_domain.h> 9*82e703ddSChristian Marangi #include <linux/slab.h> 10*82e703ddSChristian Marangi 11*82e703ddSChristian Marangi #define AIROHA_SIP_AVS_HANDLE 0x82000301 12*82e703ddSChristian Marangi #define AIROHA_AVS_OP_BASE 0xddddddd0 13*82e703ddSChristian Marangi #define AIROHA_AVS_OP_MASK GENMASK(1, 0) 14*82e703ddSChristian Marangi #define AIROHA_AVS_OP_FREQ_DYN_ADJ (AIROHA_AVS_OP_BASE | \ 15*82e703ddSChristian Marangi FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1)) 16*82e703ddSChristian Marangi #define AIROHA_AVS_OP_GET_FREQ (AIROHA_AVS_OP_BASE | \ 17*82e703ddSChristian Marangi FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2)) 18*82e703ddSChristian Marangi 19*82e703ddSChristian Marangi struct airoha_cpu_pmdomain_priv { 20*82e703ddSChristian Marangi struct clk_hw hw; 21*82e703ddSChristian Marangi struct generic_pm_domain pd; 22*82e703ddSChristian Marangi }; 23*82e703ddSChristian Marangi 24*82e703ddSChristian Marangi static long airoha_cpu_pmdomain_clk_round(struct clk_hw *hw, unsigned long rate, 25*82e703ddSChristian Marangi unsigned long *parent_rate) 26*82e703ddSChristian Marangi { 27*82e703ddSChristian Marangi return rate; 28*82e703ddSChristian Marangi } 29*82e703ddSChristian Marangi 30*82e703ddSChristian Marangi static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw, 31*82e703ddSChristian Marangi unsigned long parent_rate) 32*82e703ddSChristian Marangi { 33*82e703ddSChristian Marangi struct arm_smccc_res res; 34*82e703ddSChristian Marangi 35*82e703ddSChristian Marangi arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_GET_FREQ, 36*82e703ddSChristian Marangi 0, 0, 0, 0, 0, 0, &res); 37*82e703ddSChristian Marangi 38*82e703ddSChristian Marangi /* SMCCC returns freq in MHz */ 39*82e703ddSChristian Marangi return (int)(res.a0 * 1000 * 1000); 40*82e703ddSChristian Marangi } 41*82e703ddSChristian Marangi 42*82e703ddSChristian Marangi /* Airoha CPU clk SMCC is always enabled */ 43*82e703ddSChristian Marangi static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw) 44*82e703ddSChristian Marangi { 45*82e703ddSChristian Marangi return true; 46*82e703ddSChristian Marangi } 47*82e703ddSChristian Marangi 48*82e703ddSChristian Marangi static const struct clk_ops airoha_cpu_pmdomain_clk_ops = { 49*82e703ddSChristian Marangi .recalc_rate = airoha_cpu_pmdomain_clk_get, 50*82e703ddSChristian Marangi .is_enabled = airoha_cpu_pmdomain_clk_is_enabled, 51*82e703ddSChristian Marangi .round_rate = airoha_cpu_pmdomain_clk_round, 52*82e703ddSChristian Marangi }; 53*82e703ddSChristian Marangi 54*82e703ddSChristian Marangi static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain, 55*82e703ddSChristian Marangi unsigned int state) 56*82e703ddSChristian Marangi { 57*82e703ddSChristian Marangi struct arm_smccc_res res; 58*82e703ddSChristian Marangi 59*82e703ddSChristian Marangi arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_FREQ_DYN_ADJ, 60*82e703ddSChristian Marangi 0, state, 0, 0, 0, 0, &res); 61*82e703ddSChristian Marangi 62*82e703ddSChristian Marangi /* SMC signal correct apply by unsetting BIT 0 */ 63*82e703ddSChristian Marangi return res.a0 & BIT(0) ? -EINVAL : 0; 64*82e703ddSChristian Marangi } 65*82e703ddSChristian Marangi 66*82e703ddSChristian Marangi static int airoha_cpu_pmdomain_probe(struct platform_device *pdev) 67*82e703ddSChristian Marangi { 68*82e703ddSChristian Marangi struct airoha_cpu_pmdomain_priv *priv; 69*82e703ddSChristian Marangi struct device *dev = &pdev->dev; 70*82e703ddSChristian Marangi const struct clk_init_data init = { 71*82e703ddSChristian Marangi .name = "cpu", 72*82e703ddSChristian Marangi .ops = &airoha_cpu_pmdomain_clk_ops, 73*82e703ddSChristian Marangi /* Clock with no set_rate, can't cache */ 74*82e703ddSChristian Marangi .flags = CLK_GET_RATE_NOCACHE, 75*82e703ddSChristian Marangi }; 76*82e703ddSChristian Marangi struct generic_pm_domain *pd; 77*82e703ddSChristian Marangi int ret; 78*82e703ddSChristian Marangi 79*82e703ddSChristian Marangi priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 80*82e703ddSChristian Marangi if (!priv) 81*82e703ddSChristian Marangi return -ENOMEM; 82*82e703ddSChristian Marangi 83*82e703ddSChristian Marangi /* Init and register a get-only clk for Cpufreq */ 84*82e703ddSChristian Marangi priv->hw.init = &init; 85*82e703ddSChristian Marangi ret = devm_clk_hw_register(dev, &priv->hw); 86*82e703ddSChristian Marangi if (ret) 87*82e703ddSChristian Marangi return ret; 88*82e703ddSChristian Marangi 89*82e703ddSChristian Marangi ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, 90*82e703ddSChristian Marangi &priv->hw); 91*82e703ddSChristian Marangi if (ret) 92*82e703ddSChristian Marangi return ret; 93*82e703ddSChristian Marangi 94*82e703ddSChristian Marangi /* Init and register a PD for CPU */ 95*82e703ddSChristian Marangi pd = &priv->pd; 96*82e703ddSChristian Marangi pd->name = "cpu_pd"; 97*82e703ddSChristian Marangi pd->flags = GENPD_FLAG_ALWAYS_ON; 98*82e703ddSChristian Marangi pd->set_performance_state = airoha_cpu_pmdomain_set_performance_state; 99*82e703ddSChristian Marangi 100*82e703ddSChristian Marangi ret = pm_genpd_init(pd, NULL, false); 101*82e703ddSChristian Marangi if (ret) 102*82e703ddSChristian Marangi return ret; 103*82e703ddSChristian Marangi 104*82e703ddSChristian Marangi ret = of_genpd_add_provider_simple(dev->of_node, pd); 105*82e703ddSChristian Marangi if (ret) 106*82e703ddSChristian Marangi goto err_add_provider; 107*82e703ddSChristian Marangi 108*82e703ddSChristian Marangi platform_set_drvdata(pdev, priv); 109*82e703ddSChristian Marangi 110*82e703ddSChristian Marangi return 0; 111*82e703ddSChristian Marangi 112*82e703ddSChristian Marangi err_add_provider: 113*82e703ddSChristian Marangi pm_genpd_remove(pd); 114*82e703ddSChristian Marangi 115*82e703ddSChristian Marangi return ret; 116*82e703ddSChristian Marangi } 117*82e703ddSChristian Marangi 118*82e703ddSChristian Marangi static void airoha_cpu_pmdomain_remove(struct platform_device *pdev) 119*82e703ddSChristian Marangi { 120*82e703ddSChristian Marangi struct airoha_cpu_pmdomain_priv *priv = platform_get_drvdata(pdev); 121*82e703ddSChristian Marangi 122*82e703ddSChristian Marangi of_genpd_del_provider(pdev->dev.of_node); 123*82e703ddSChristian Marangi pm_genpd_remove(&priv->pd); 124*82e703ddSChristian Marangi } 125*82e703ddSChristian Marangi 126*82e703ddSChristian Marangi static const struct of_device_id airoha_cpu_pmdomain_of_match[] = { 127*82e703ddSChristian Marangi { .compatible = "airoha,en7581-cpufreq" }, 128*82e703ddSChristian Marangi { }, 129*82e703ddSChristian Marangi }; 130*82e703ddSChristian Marangi MODULE_DEVICE_TABLE(of, airoha_cpu_pmdomain_of_match); 131*82e703ddSChristian Marangi 132*82e703ddSChristian Marangi static struct platform_driver airoha_cpu_pmdomain_driver = { 133*82e703ddSChristian Marangi .probe = airoha_cpu_pmdomain_probe, 134*82e703ddSChristian Marangi .remove = airoha_cpu_pmdomain_remove, 135*82e703ddSChristian Marangi .driver = { 136*82e703ddSChristian Marangi .name = "airoha-cpu-pmdomain", 137*82e703ddSChristian Marangi .of_match_table = airoha_cpu_pmdomain_of_match, 138*82e703ddSChristian Marangi }, 139*82e703ddSChristian Marangi }; 140*82e703ddSChristian Marangi module_platform_driver(airoha_cpu_pmdomain_driver); 141*82e703ddSChristian Marangi 142*82e703ddSChristian Marangi MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); 143*82e703ddSChristian Marangi MODULE_DESCRIPTION("CPU PM domain driver for Airoha SoCs"); 144*82e703ddSChristian Marangi MODULE_LICENSE("GPL"); 145