diff mbox series

cpufreq: airoha: Add EN7581 Cpufreq SMC driver

Message ID 20241016161044.28745-1-ansuelsmth@gmail.com
State Superseded
Headers show
Series cpufreq: airoha: Add EN7581 Cpufreq SMC driver | expand

Commit Message

Christian Marangi Oct. 16, 2024, 4:10 p.m. UTC
Add simple Cpufreq driver for Airoha EN7581 SoC that control CPU
frequency scaling with SMC APIs.

All CPU share the same frequency and can't be controlled independently.
Current shared CPU frequency is returned by the related SMC command.

Add SoC compatible to cpufreq-dt-plat block list as a dedicated cpufreq
driver is needed with OPP v2 nodes declared in DTS.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 drivers/cpufreq/Kconfig.arm          |   8 ++
 drivers/cpufreq/Makefile             |   1 +
 drivers/cpufreq/airoha-cpufreq.c     | 183 +++++++++++++++++++++++++++
 drivers/cpufreq/cpufreq-dt-platdev.c |   2 +
 4 files changed, 194 insertions(+)
 create mode 100644 drivers/cpufreq/airoha-cpufreq.c

Comments

kernel test robot Oct. 17, 2024, 1:11 p.m. UTC | #1
Hi Christian,

kernel test robot noticed the following build errors:

[auto build test ERROR on rafael-pm/linux-next]
[also build test ERROR on rafael-pm/bleeding-edge linus/master v6.12-rc3 next-20241017]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Christian-Marangi/cpufreq-airoha-Add-EN7581-Cpufreq-SMC-driver/20241017-001217
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20241016161044.28745-1-ansuelsmth%40gmail.com
patch subject: [PATCH] cpufreq: airoha: Add EN7581 Cpufreq SMC driver
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20241017/202410172028.AdkR2sEw-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241017/202410172028.AdkR2sEw-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410172028.AdkR2sEw-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_get':
>> drivers/cpufreq/airoha-cpufreq.c:27:22: error: variable 'args' has initializer but incomplete type
      27 |         const struct arm_smccc_1_2_regs args = {
         |                      ^~~~~~~~~~~~~~~~~~
>> drivers/cpufreq/airoha-cpufreq.c:28:18: error: 'const struct arm_smccc_1_2_regs' has no member named 'a0'
      28 |                 .a0 = AIROHA_SIP_AVS_HANDLE,
         |                  ^~
>> drivers/cpufreq/airoha-cpufreq.c:7:49: warning: excess elements in struct initializer
       7 | #define AIROHA_SIP_AVS_HANDLE                   0x82000301
         |                                                 ^~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:28:23: note: in expansion of macro 'AIROHA_SIP_AVS_HANDLE'
      28 |                 .a0 = AIROHA_SIP_AVS_HANDLE,
         |                       ^~~~~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:7:49: note: (near initialization for 'args')
       7 | #define AIROHA_SIP_AVS_HANDLE                   0x82000301
         |                                                 ^~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:28:23: note: in expansion of macro 'AIROHA_SIP_AVS_HANDLE'
      28 |                 .a0 = AIROHA_SIP_AVS_HANDLE,
         |                       ^~~~~~~~~~~~~~~~~~~~~
>> drivers/cpufreq/airoha-cpufreq.c:29:18: error: 'const struct arm_smccc_1_2_regs' has no member named 'a1'
      29 |                 .a1 = AIROHA_AVS_OP_GET_FREQ,
         |                  ^~
>> drivers/cpufreq/airoha-cpufreq.c:13:50: error: implicit declaration of function 'FIELD_PREP' [-Wimplicit-function-declaration]
      13 |                                                  FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2))
         |                                                  ^~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:29:23: note: in expansion of macro 'AIROHA_AVS_OP_GET_FREQ'
      29 |                 .a1 = AIROHA_AVS_OP_GET_FREQ,
         |                       ^~~~~~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:12:49: warning: excess elements in struct initializer
      12 | #define AIROHA_AVS_OP_GET_FREQ                  (AIROHA_AVS_OP_BASE | \
         |                                                 ^
   drivers/cpufreq/airoha-cpufreq.c:29:23: note: in expansion of macro 'AIROHA_AVS_OP_GET_FREQ'
      29 |                 .a1 = AIROHA_AVS_OP_GET_FREQ,
         |                       ^~~~~~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:12:49: note: (near initialization for 'args')
      12 | #define AIROHA_AVS_OP_GET_FREQ                  (AIROHA_AVS_OP_BASE | \
         |                                                 ^
   drivers/cpufreq/airoha-cpufreq.c:29:23: note: in expansion of macro 'AIROHA_AVS_OP_GET_FREQ'
      29 |                 .a1 = AIROHA_AVS_OP_GET_FREQ,
         |                       ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/cpufreq/airoha-cpufreq.c:27:41: error: storage size of 'args' isn't known
      27 |         const struct arm_smccc_1_2_regs args = {
         |                                         ^~~~
>> drivers/cpufreq/airoha-cpufreq.c:31:35: error: storage size of 'res' isn't known
      31 |         struct arm_smccc_1_2_regs res;
         |                                   ^~~
>> drivers/cpufreq/airoha-cpufreq.c:33:9: error: implicit declaration of function 'arm_smccc_1_2_smc'; did you mean 'arm_smccc_1_1_smc'? [-Wimplicit-function-declaration]
      33 |         arm_smccc_1_2_smc(&args, &res);
         |         ^~~~~~~~~~~~~~~~~
         |         arm_smccc_1_1_smc
>> drivers/cpufreq/airoha-cpufreq.c:31:35: warning: unused variable 'res' [-Wunused-variable]
      31 |         struct arm_smccc_1_2_regs res;
         |                                   ^~~
>> drivers/cpufreq/airoha-cpufreq.c:27:41: warning: unused variable 'args' [-Wunused-variable]
      27 |         const struct arm_smccc_1_2_regs args = {
         |                                         ^~~~
   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_set_target':
   drivers/cpufreq/airoha-cpufreq.c:40:22: error: variable 'args' has initializer but incomplete type
      40 |         const struct arm_smccc_1_2_regs args = {
         |                      ^~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:41:18: error: 'const struct arm_smccc_1_2_regs' has no member named 'a0'
      41 |                 .a0 = AIROHA_SIP_AVS_HANDLE,
         |                  ^~
>> drivers/cpufreq/airoha-cpufreq.c:7:49: warning: excess elements in struct initializer
       7 | #define AIROHA_SIP_AVS_HANDLE                   0x82000301
         |                                                 ^~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:41:23: note: in expansion of macro 'AIROHA_SIP_AVS_HANDLE'
      41 |                 .a0 = AIROHA_SIP_AVS_HANDLE,
         |                       ^~~~~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:7:49: note: (near initialization for 'args')
       7 | #define AIROHA_SIP_AVS_HANDLE                   0x82000301
         |                                                 ^~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:41:23: note: in expansion of macro 'AIROHA_SIP_AVS_HANDLE'
      41 |                 .a0 = AIROHA_SIP_AVS_HANDLE,
         |                       ^~~~~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:42:18: error: 'const struct arm_smccc_1_2_regs' has no member named 'a1'
      42 |                 .a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
         |                  ^~
   drivers/cpufreq/airoha-cpufreq.c:10:49: warning: excess elements in struct initializer
      10 | #define AIROHA_AVS_OP_FREQ_DYN_ADJ              (AIROHA_AVS_OP_BASE | \
         |                                                 ^
   drivers/cpufreq/airoha-cpufreq.c:42:23: note: in expansion of macro 'AIROHA_AVS_OP_FREQ_DYN_ADJ'
      42 |                 .a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
         |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/cpufreq/airoha-cpufreq.c:10:49: note: (near initialization for 'args')
      10 | #define AIROHA_AVS_OP_FREQ_DYN_ADJ              (AIROHA_AVS_OP_BASE | \
         |                                                 ^
   drivers/cpufreq/airoha-cpufreq.c:42:23: note: in expansion of macro 'AIROHA_AVS_OP_FREQ_DYN_ADJ'
      42 |                 .a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
         |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/cpufreq/airoha-cpufreq.c:43:18: error: 'const struct arm_smccc_1_2_regs' has no member named 'a3'
      43 |                 .a3 = index,
         |                  ^~
   drivers/cpufreq/airoha-cpufreq.c:43:23: warning: excess elements in struct initializer
      43 |                 .a3 = index,
         |                       ^~~~~
   drivers/cpufreq/airoha-cpufreq.c:43:23: note: (near initialization for 'args')
   drivers/cpufreq/airoha-cpufreq.c:40:41: error: storage size of 'args' isn't known
      40 |         const struct arm_smccc_1_2_regs args = {
         |                                         ^~~~
   drivers/cpufreq/airoha-cpufreq.c:45:35: error: storage size of 'res' isn't known
      45 |         struct arm_smccc_1_2_regs res;
         |                                   ^~~
   drivers/cpufreq/airoha-cpufreq.c:45:35: warning: unused variable 'res' [-Wunused-variable]
   drivers/cpufreq/airoha-cpufreq.c:40:41: warning: unused variable 'args' [-Wunused-variable]
      40 |         const struct arm_smccc_1_2_regs args = {
         |                                         ^~~~
   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_init':
>> drivers/cpufreq/airoha-cpufreq.c:68:24: warning: variable 'cpu_dev' set but not used [-Wunused-but-set-variable]
      68 |         struct device *cpu_dev;
         |                        ^~~~~~~
   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_driver_init_cpu':
>> drivers/cpufreq/airoha-cpufreq.c:103:16: error: implicit declaration of function 'kzalloc' [-Wimplicit-function-declaration]
     103 |         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
         |                ^~~~~~~
>> drivers/cpufreq/airoha-cpufreq.c:103:14: error: assignment to 'struct airoha_cpufreq_priv *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     103 |         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
         |              ^
   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_release':
>> drivers/cpufreq/airoha-cpufreq.c:145:17: error: implicit declaration of function 'kfree'; did you mean 'kvfree'? [-Wimplicit-function-declaration]
     145 |                 kfree(priv);
         |                 ^~~~~
         |                 kvfree
   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_get':
>> drivers/cpufreq/airoha-cpufreq.c:36:1: warning: control reaches end of non-void function [-Wreturn-type]
      36 | }
         | ^
   drivers/cpufreq/airoha-cpufreq.c: In function 'airoha_cpufreq_set_target':
   drivers/cpufreq/airoha-cpufreq.c:51:1: warning: control reaches end of non-void function [-Wreturn-type]
      51 | }
         | ^


vim +/args +27 drivers/cpufreq/airoha-cpufreq.c

     6	
   > 7	#define AIROHA_SIP_AVS_HANDLE			0x82000301
     8	#define AIROHA_AVS_OP_BASE			0xddddddd0
     9	#define AIROHA_AVS_OP_MASK			GENMASK(1, 0)
    10	#define AIROHA_AVS_OP_FREQ_DYN_ADJ		(AIROHA_AVS_OP_BASE | \
    11							 FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1))
    12	#define AIROHA_AVS_OP_GET_FREQ			(AIROHA_AVS_OP_BASE | \
  > 13							 FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2))
    14	
    15	struct airoha_cpufreq_priv {
    16		struct list_head list;
    17	
    18		cpumask_var_t cpus;
    19		struct device *cpu_dev;
    20		struct cpufreq_frequency_table *freq_table;
    21	};
    22	
    23	static LIST_HEAD(priv_list);
    24	
    25	static unsigned int airoha_cpufreq_get(unsigned int cpu)
    26	{
  > 27		const struct arm_smccc_1_2_regs args = {
  > 28			.a0 = AIROHA_SIP_AVS_HANDLE,
  > 29			.a1 = AIROHA_AVS_OP_GET_FREQ,
    30		};
  > 31		struct arm_smccc_1_2_regs res;
    32	
  > 33		arm_smccc_1_2_smc(&args, &res);
    34	
    35		return (int)(res.a0 * 1000);
  > 36	}
    37	
    38	static int airoha_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
    39	{
    40		const struct arm_smccc_1_2_regs args = {
    41			.a0 = AIROHA_SIP_AVS_HANDLE,
    42			.a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
  > 43			.a3 = index,
    44		};
    45		struct arm_smccc_1_2_regs res;
    46	
    47		arm_smccc_1_2_smc(&args, &res);
    48	
    49		/* SMC signal correct apply by unsetting BIT 0 */
    50		return res.a0 & BIT(0) ? -EINVAL : 0;
    51	}
    52	
    53	static struct airoha_cpufreq_priv *airoha_cpufreq_find_data(int cpu)
    54	{
    55		struct airoha_cpufreq_priv *priv;
    56	
    57		list_for_each_entry(priv, &priv_list, list) {
    58			if (cpumask_test_cpu(cpu, priv->cpus))
    59				return priv;
    60		}
    61	
    62		return NULL;
    63	}
    64	
    65	static int airoha_cpufreq_init(struct cpufreq_policy *policy)
    66	{
    67		struct airoha_cpufreq_priv *priv;
  > 68		struct device *cpu_dev;
    69	
    70		priv = airoha_cpufreq_find_data(policy->cpu);
    71		if (!priv)
    72			return -ENODEV;
    73	
    74		cpu_dev = priv->cpu_dev;
    75		cpumask_copy(policy->cpus, priv->cpus);
    76		policy->driver_data = priv;
    77		policy->freq_table = priv->freq_table;
    78	
    79		return 0;
    80	}
    81	
    82	static struct cpufreq_driver airoha_cpufreq_driver = {
    83		.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK |
    84				  CPUFREQ_IS_COOLING_DEV,
    85		.verify		= cpufreq_generic_frequency_table_verify,
    86		.target_index	= airoha_cpufreq_set_target,
    87		.get		= airoha_cpufreq_get,
    88		.init		= airoha_cpufreq_init,
    89		.attr		= cpufreq_generic_attr,
    90		.name		= "airoha-cpufreq",
    91	};
    92	
    93	static int airoha_cpufreq_driver_init_cpu(int cpu)
    94	{
    95		struct airoha_cpufreq_priv *priv;
    96		struct device *cpu_dev;
    97		int ret;
    98	
    99		cpu_dev = get_cpu_device(cpu);
   100		if (!cpu_dev)
   101			return -EPROBE_DEFER;
   102	
 > 103		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
   104		if (!priv)
   105			return -ENOMEM;
   106	
   107		if (!zalloc_cpumask_var(&priv->cpus, GFP_KERNEL))
   108			return -ENOMEM;
   109	
   110		cpumask_set_cpu(cpu, priv->cpus);
   111		priv->cpu_dev = cpu_dev;
   112	
   113		ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
   114		if (ret)
   115			goto err;
   116	
   117		ret = dev_pm_opp_of_cpumask_add_table(priv->cpus);
   118		if (ret)
   119			goto err;
   120	
   121		ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
   122		if (ret)
   123			goto err;
   124	
   125		list_add(&priv->list, &priv_list);
   126	
   127		return 0;
   128	
   129	err:
   130		dev_pm_opp_of_cpumask_remove_table(priv->cpus);
   131		free_cpumask_var(priv->cpus);
   132	
   133		return ret;
   134	}
   135	
   136	static void airoha_cpufreq_release(void)
   137	{
   138		struct airoha_cpufreq_priv *priv, *tmp;
   139	
   140		list_for_each_entry_safe(priv, tmp, &priv_list, list) {
   141			dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
   142			dev_pm_opp_of_cpumask_remove_table(priv->cpus);
   143			free_cpumask_var(priv->cpus);
   144			list_del(&priv->list);
 > 145			kfree(priv);
   146		}
   147	}
   148
kernel test robot Oct. 17, 2024, 4:56 p.m. UTC | #2
Hi Christian,

kernel test robot noticed the following build errors:

[auto build test ERROR on rafael-pm/linux-next]
[also build test ERROR on rafael-pm/bleeding-edge linus/master v6.12-rc3 next-20241017]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Christian-Marangi/cpufreq-airoha-Add-EN7581-Cpufreq-SMC-driver/20241017-001217
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20241016161044.28745-1-ansuelsmth%40gmail.com
patch subject: [PATCH] cpufreq: airoha: Add EN7581 Cpufreq SMC driver
config: arm-defconfig (https://download.01.org/0day-ci/archive/20241018/202410180024.2NpHo5Z6-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241018/202410180024.2NpHo5Z6-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410180024.2NpHo5Z6-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   drivers/cpufreq/airoha-cpufreq.c:27:34: error: variable has incomplete type 'const struct arm_smccc_1_2_regs'
           const struct arm_smccc_1_2_regs args = {
                                           ^
   drivers/cpufreq/airoha-cpufreq.c:27:15: note: forward declaration of 'struct arm_smccc_1_2_regs'
           const struct arm_smccc_1_2_regs args = {
                        ^
   drivers/cpufreq/airoha-cpufreq.c:31:28: error: variable has incomplete type 'struct arm_smccc_1_2_regs'
           struct arm_smccc_1_2_regs res;
                                     ^
   drivers/cpufreq/airoha-cpufreq.c:27:15: note: forward declaration of 'struct arm_smccc_1_2_regs'
           const struct arm_smccc_1_2_regs args = {
                        ^
>> drivers/cpufreq/airoha-cpufreq.c:33:2: error: implicit declaration of function 'arm_smccc_1_2_smc' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
           arm_smccc_1_2_smc(&args, &res);
           ^
   drivers/cpufreq/airoha-cpufreq.c:40:34: error: variable has incomplete type 'const struct arm_smccc_1_2_regs'
           const struct arm_smccc_1_2_regs args = {
                                           ^
   drivers/cpufreq/airoha-cpufreq.c:40:15: note: forward declaration of 'struct arm_smccc_1_2_regs'
           const struct arm_smccc_1_2_regs args = {
                        ^
   drivers/cpufreq/airoha-cpufreq.c:45:28: error: variable has incomplete type 'struct arm_smccc_1_2_regs'
           struct arm_smccc_1_2_regs res;
                                     ^
   drivers/cpufreq/airoha-cpufreq.c:40:15: note: forward declaration of 'struct arm_smccc_1_2_regs'
           const struct arm_smccc_1_2_regs args = {
                        ^
   drivers/cpufreq/airoha-cpufreq.c:47:2: error: implicit declaration of function 'arm_smccc_1_2_smc' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
           arm_smccc_1_2_smc(&args, &res);
           ^
   drivers/cpufreq/airoha-cpufreq.c:68:17: warning: variable 'cpu_dev' set but not used [-Wunused-but-set-variable]
           struct device *cpu_dev;
                          ^
>> drivers/cpufreq/airoha-cpufreq.c:103:9: error: implicit declaration of function 'kzalloc' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
           priv = kzalloc(sizeof(*priv), GFP_KERNEL);
                  ^
>> drivers/cpufreq/airoha-cpufreq.c:103:7: warning: incompatible integer to pointer conversion assigning to 'struct airoha_cpufreq_priv *' from 'int' [-Wint-conversion]
           priv = kzalloc(sizeof(*priv), GFP_KERNEL);
                ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/cpufreq/airoha-cpufreq.c:145:3: error: implicit declaration of function 'kfree' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
                   kfree(priv);
                   ^
   2 warnings and 8 errors generated.


vim +/arm_smccc_1_2_smc +33 drivers/cpufreq/airoha-cpufreq.c

    24	
    25	static unsigned int airoha_cpufreq_get(unsigned int cpu)
    26	{
    27		const struct arm_smccc_1_2_regs args = {
    28			.a0 = AIROHA_SIP_AVS_HANDLE,
    29			.a1 = AIROHA_AVS_OP_GET_FREQ,
    30		};
    31		struct arm_smccc_1_2_regs res;
    32	
  > 33		arm_smccc_1_2_smc(&args, &res);
    34	
    35		return (int)(res.a0 * 1000);
    36	}
    37	
    38	static int airoha_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
    39	{
    40		const struct arm_smccc_1_2_regs args = {
    41			.a0 = AIROHA_SIP_AVS_HANDLE,
    42			.a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
    43			.a3 = index,
    44		};
    45		struct arm_smccc_1_2_regs res;
    46	
    47		arm_smccc_1_2_smc(&args, &res);
    48	
    49		/* SMC signal correct apply by unsetting BIT 0 */
    50		return res.a0 & BIT(0) ? -EINVAL : 0;
    51	}
    52	
    53	static struct airoha_cpufreq_priv *airoha_cpufreq_find_data(int cpu)
    54	{
    55		struct airoha_cpufreq_priv *priv;
    56	
    57		list_for_each_entry(priv, &priv_list, list) {
    58			if (cpumask_test_cpu(cpu, priv->cpus))
    59				return priv;
    60		}
    61	
    62		return NULL;
    63	}
    64	
    65	static int airoha_cpufreq_init(struct cpufreq_policy *policy)
    66	{
    67		struct airoha_cpufreq_priv *priv;
    68		struct device *cpu_dev;
    69	
    70		priv = airoha_cpufreq_find_data(policy->cpu);
    71		if (!priv)
    72			return -ENODEV;
    73	
    74		cpu_dev = priv->cpu_dev;
    75		cpumask_copy(policy->cpus, priv->cpus);
    76		policy->driver_data = priv;
    77		policy->freq_table = priv->freq_table;
    78	
    79		return 0;
    80	}
    81	
    82	static struct cpufreq_driver airoha_cpufreq_driver = {
    83		.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK |
    84				  CPUFREQ_IS_COOLING_DEV,
    85		.verify		= cpufreq_generic_frequency_table_verify,
    86		.target_index	= airoha_cpufreq_set_target,
    87		.get		= airoha_cpufreq_get,
    88		.init		= airoha_cpufreq_init,
    89		.attr		= cpufreq_generic_attr,
    90		.name		= "airoha-cpufreq",
    91	};
    92	
    93	static int airoha_cpufreq_driver_init_cpu(int cpu)
    94	{
    95		struct airoha_cpufreq_priv *priv;
    96		struct device *cpu_dev;
    97		int ret;
    98	
    99		cpu_dev = get_cpu_device(cpu);
   100		if (!cpu_dev)
   101			return -EPROBE_DEFER;
   102	
 > 103		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
   104		if (!priv)
   105			return -ENOMEM;
   106	
   107		if (!zalloc_cpumask_var(&priv->cpus, GFP_KERNEL))
   108			return -ENOMEM;
   109	
   110		cpumask_set_cpu(cpu, priv->cpus);
   111		priv->cpu_dev = cpu_dev;
   112	
   113		ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
   114		if (ret)
   115			goto err;
   116	
   117		ret = dev_pm_opp_of_cpumask_add_table(priv->cpus);
   118		if (ret)
   119			goto err;
   120	
   121		ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
   122		if (ret)
   123			goto err;
   124	
   125		list_add(&priv->list, &priv_list);
   126	
   127		return 0;
   128	
   129	err:
   130		dev_pm_opp_of_cpumask_remove_table(priv->cpus);
   131		free_cpumask_var(priv->cpus);
   132	
   133		return ret;
   134	}
   135	
   136	static void airoha_cpufreq_release(void)
   137	{
   138		struct airoha_cpufreq_priv *priv, *tmp;
   139	
   140		list_for_each_entry_safe(priv, tmp, &priv_list, list) {
   141			dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
   142			dev_pm_opp_of_cpumask_remove_table(priv->cpus);
   143			free_cpumask_var(priv->cpus);
   144			list_del(&priv->list);
 > 145			kfree(priv);
   146		}
   147	}
   148
diff mbox series

Patch

diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 5f7e13e60c80..704e84d00639 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -15,6 +15,14 @@  config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM
 	  To compile this driver as a module, choose M here: the
 	  module will be called sun50i-cpufreq-nvmem.
 
+config ARM_AIROHA_SOC_CPUFREQ
+	tristate "Airoha EN7581 SoC CPUFreq support"
+	depends on ARCH_AIROHA || COMPILE_TEST
+	select PM_OPP
+	default ARCH_AIROHA
+	help
+	  This adds the CPUFreq driver for Airoha EN7581 SoCs.
+
 config ARM_APPLE_SOC_CPUFREQ
 	tristate "Apple Silicon SoC CPUFreq support"
 	depends on ARCH_APPLE || (COMPILE_TEST && 64BIT)
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 0f184031dd12..8e5a37a95d36 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -52,6 +52,7 @@  obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY)	+= amd_freq_sensitivity.o
 
 ##################################################################################
 # ARM SoC drivers
+obj-$(CONFIG_ARM_AIROHA_SOC_CPUFREQ)	+= airoha-cpufreq.o
 obj-$(CONFIG_ARM_APPLE_SOC_CPUFREQ)	+= apple-soc-cpufreq.o
 obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ)	+= armada-37xx-cpufreq.o
 obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ)	+= armada-8k-cpufreq.o
diff --git a/drivers/cpufreq/airoha-cpufreq.c b/drivers/cpufreq/airoha-cpufreq.c
new file mode 100644
index 000000000000..34006dffd81b
--- /dev/null
+++ b/drivers/cpufreq/airoha-cpufreq.c
@@ -0,0 +1,183 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/arm-smccc.h>
+
+#define AIROHA_SIP_AVS_HANDLE			0x82000301
+#define AIROHA_AVS_OP_BASE			0xddddddd0
+#define AIROHA_AVS_OP_MASK			GENMASK(1, 0)
+#define AIROHA_AVS_OP_FREQ_DYN_ADJ		(AIROHA_AVS_OP_BASE | \
+						 FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1))
+#define AIROHA_AVS_OP_GET_FREQ			(AIROHA_AVS_OP_BASE | \
+						 FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2))
+
+struct airoha_cpufreq_priv {
+	struct list_head list;
+
+	cpumask_var_t cpus;
+	struct device *cpu_dev;
+	struct cpufreq_frequency_table *freq_table;
+};
+
+static LIST_HEAD(priv_list);
+
+static unsigned int airoha_cpufreq_get(unsigned int cpu)
+{
+	const struct arm_smccc_1_2_regs args = {
+		.a0 = AIROHA_SIP_AVS_HANDLE,
+		.a1 = AIROHA_AVS_OP_GET_FREQ,
+	};
+	struct arm_smccc_1_2_regs res;
+
+	arm_smccc_1_2_smc(&args, &res);
+
+	return (int)(res.a0 * 1000);
+}
+
+static int airoha_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+	const struct arm_smccc_1_2_regs args = {
+		.a0 = AIROHA_SIP_AVS_HANDLE,
+		.a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
+		.a3 = index,
+	};
+	struct arm_smccc_1_2_regs res;
+
+	arm_smccc_1_2_smc(&args, &res);
+
+	/* SMC signal correct apply by unsetting BIT 0 */
+	return res.a0 & BIT(0) ? -EINVAL : 0;
+}
+
+static struct airoha_cpufreq_priv *airoha_cpufreq_find_data(int cpu)
+{
+	struct airoha_cpufreq_priv *priv;
+
+	list_for_each_entry(priv, &priv_list, list) {
+		if (cpumask_test_cpu(cpu, priv->cpus))
+			return priv;
+	}
+
+	return NULL;
+}
+
+static int airoha_cpufreq_init(struct cpufreq_policy *policy)
+{
+	struct airoha_cpufreq_priv *priv;
+	struct device *cpu_dev;
+
+	priv = airoha_cpufreq_find_data(policy->cpu);
+	if (!priv)
+		return -ENODEV;
+
+	cpu_dev = priv->cpu_dev;
+	cpumask_copy(policy->cpus, priv->cpus);
+	policy->driver_data = priv;
+	policy->freq_table = priv->freq_table;
+
+	return 0;
+}
+
+static struct cpufreq_driver airoha_cpufreq_driver = {
+	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK |
+			  CPUFREQ_IS_COOLING_DEV,
+	.verify		= cpufreq_generic_frequency_table_verify,
+	.target_index	= airoha_cpufreq_set_target,
+	.get		= airoha_cpufreq_get,
+	.init		= airoha_cpufreq_init,
+	.attr		= cpufreq_generic_attr,
+	.name		= "airoha-cpufreq",
+};
+
+static int airoha_cpufreq_driver_init_cpu(int cpu)
+{
+	struct airoha_cpufreq_priv *priv;
+	struct device *cpu_dev;
+	int ret;
+
+	cpu_dev = get_cpu_device(cpu);
+	if (!cpu_dev)
+		return -EPROBE_DEFER;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	if (!zalloc_cpumask_var(&priv->cpus, GFP_KERNEL))
+		return -ENOMEM;
+
+	cpumask_set_cpu(cpu, priv->cpus);
+	priv->cpu_dev = cpu_dev;
+
+	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
+	if (ret)
+		goto err;
+
+	ret = dev_pm_opp_of_cpumask_add_table(priv->cpus);
+	if (ret)
+		goto err;
+
+	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
+	if (ret)
+		goto err;
+
+	list_add(&priv->list, &priv_list);
+
+	return 0;
+
+err:
+	dev_pm_opp_of_cpumask_remove_table(priv->cpus);
+	free_cpumask_var(priv->cpus);
+
+	return ret;
+}
+
+static void airoha_cpufreq_release(void)
+{
+	struct airoha_cpufreq_priv *priv, *tmp;
+
+	list_for_each_entry_safe(priv, tmp, &priv_list, list) {
+		dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
+		dev_pm_opp_of_cpumask_remove_table(priv->cpus);
+		free_cpumask_var(priv->cpus);
+		list_del(&priv->list);
+		kfree(priv);
+	}
+}
+
+static int __init airoha_cpufreq_driver_probe(void)
+{
+	int cpu, ret;
+
+	if (!of_machine_is_compatible("airoha,en7581"))
+		return -ENODEV;
+
+	for_each_possible_cpu(cpu) {
+		ret = airoha_cpufreq_driver_init_cpu(cpu);
+		if (ret)
+			goto err;
+	}
+
+	ret = cpufreq_register_driver(&airoha_cpufreq_driver);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	airoha_cpufreq_release();
+	return ret;
+}
+module_init(airoha_cpufreq_driver_probe);
+
+static void __exit airoha_cpufreq_driver_remove(void)
+{
+	cpufreq_unregister_driver(&airoha_cpufreq_driver);
+	airoha_cpufreq_release();
+}
+module_exit(airoha_cpufreq_driver_remove);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 18942bfe9c95..5ecd8234bfac 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -103,6 +103,8 @@  static const struct of_device_id allowlist[] __initconst = {
  * platforms using "operating-points-v2" property.
  */
 static const struct of_device_id blocklist[] __initconst = {
+	{ .compatible = "airoha,en7581", },
+
 	{ .compatible = "allwinner,sun50i-h6", },
 	{ .compatible = "allwinner,sun50i-h616", },
 	{ .compatible = "allwinner,sun50i-h618", },