Re: [PATCH 4/6] cpufreq: sun50i: Add H616 support

Martin Botka
2023-09-04T20:45:24Z

On Mon, Sep 4 2023 at 09:41:08 PM +01:00:00, Andre Przywara
<andre.przywara@arm.com> wrote:
> On Mon, 04 Sep 2023 17:57:04 +0200
> Martin Botka <martin.botka@somainline.org> wrote:
>
> Hi,
>
>> AllWinner H616 SoC has few revisions that support different list
>> of uV and frequencies.
>>
>> Some revisions have the same NVMEM value and thus we have to check
>> the SoC revision from SMCCC to differentiate between them.
>
> So this patch is a bit hard to read, as it combines two things: the
> refactoring and the actual H616 bits. Can you please split this up,
> with a first patch just introducing struct sunxi_cpufreq_soc_data and
> moving the existing code into the separate xlate function, and all the
> other required changes? Then having a second patch adding the H616
> bits on top? This makes review easier, as the first patch should not
> change any behaviour, and the second patch just focuses on the new
> H616
> bits.
Yea it is. I will split them up in V2.
>
> Cheers,
> Andre
>
>>
>> Signed-off-by: Martin Botka <martin.botka@somainline.org>
>> ---
>> drivers/cpufreq/sun50i-cpufreq-nvmem.c | 149
>> ++++++++++++++++++++++++++++-----
>> 1 file changed, 126 insertions(+), 23 deletions(-)
>>
>> diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
>> b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
>> index 4321d7bbe769..19c126fb081e 100644
>> --- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
>> +++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
>> @@ -10,6 +10,7 @@
>>
>> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>
>> +#include <linux/arm-smccc.h>
>> #include <linux/cpu.h>
>> #include <linux/module.h>
>> #include <linux/nvmem-consumer.h>
>> @@ -23,20 +24,94 @@
>> #define NVMEM_MASK 0x7
>> #define NVMEM_SHIFT 5
>>
>> +struct sunxi_cpufreq_soc_data {
>> + int (*efuse_xlate)(u32 *versions, u32 *efuse, char *name, size_t
>> len);
>> + u8 ver_freq_limit;
>> +};
>> +
>> static struct platform_device *cpufreq_dt_pdev,
>> *sun50i_cpufreq_pdev;
>>
>> +static int sun50i_h616_efuse_xlate(u32 *versions, u32 *efuse, char
>> *name, size_t len)
>> +{
>> + int value = 0;
>> + u32 speedgrade = 0;
>> + u32 i;
>> + int ver_bits = arm_smccc_get_soc_id_revision();
>> +
>> + if (len > 4) {
>> + pr_err("Invalid nvmem cell length\n");
>> + return -EINVAL;
>> + }
>> +
>> + for (i = 0; i < len; i++)
>> + speedgrade |= (efuse[i] << (i * 8));
>> +
>> + switch (speedgrade) {
>> + case 0x2000:
>> + value = 0;
>> + break;
>> + case 0x2400:
>> + case 0x7400:
>> + case 0x2c00:
>> + case 0x7c00:
>> + if (ver_bits <= 1) {
>> + /* ic version A/B */
>> + value = 1;
>> + } else {
>> + /* ic version C and later version */
>> + value = 2;
>> + }
>> + break;
>> + case 0x5000:
>> + case 0x5400:
>> + case 0x6000:
>> + value = 3;
>> + break;
>> + case 0x5c00:
>> + value = 4;
>> + break;
>> + case 0x5d00:
>> + default:
>> + value = 0;
>> + }
>> + *versions = (1 << value);
>> + snprintf(name, MAX_NAME_LEN, "speed%d", value);
>> + return 0;
>> +}
>> +
>> +static int sun50i_h6_efuse_xlate(u32 *versions, u32 *efuse, char
>> *name, size_t len)
>> +{
>> + int efuse_value = (*efuse >> NVMEM_SHIFT) & NVMEM_MASK;
>> +
>> + /*
>> + * We treat unexpected efuse values as if the SoC was from
>> + * the slowest bin. Expected efuse values are 1-3, slowest
>> + * to fastest.
>> + */
>> + if (efuse_value >= 1 && efuse_value <= 3)
>> + *versions = efuse_value - 1;
>> + else
>> + *versions = 0;
>> +
>> + snprintf(name, MAX_NAME_LEN, "speed%d", *versions);
>> + return 0;
>> +}
>> +
>> /**
>> * sun50i_cpufreq_get_efuse() - Determine speed grade from efuse
>> value
>> + * @soc_data: Struct containing soc specific data & functions
>> * @versions: Set to the value parsed from efuse
>> + * @name: Set to the name of speed
>> *
>> * Returns 0 if success.
>> */
>> -static int sun50i_cpufreq_get_efuse(u32 *versions)
>> +static int sun50i_cpufreq_get_efuse(const struct
>> sunxi_cpufreq_soc_data *soc_data,
>> + u32 *versions, char *name)
>> {
>> struct nvmem_cell *speedbin_nvmem;
>> struct device_node *np;
>> struct device *cpu_dev;
>> - u32 *speedbin, efuse_value;
>> + u32 *speedbin;
>> size_t len;
>> int ret;
>>
>> @@ -48,9 +123,9 @@ static int sun50i_cpufreq_get_efuse(u32
>> *versions)
>> if (!np)
>> return -ENOENT;
>>
>> - ret = of_device_is_compatible(np,
>> - "allwinner,sun50i-h6-operating-points");
>> - if (!ret) {
>> + if (of_device_is_compatible(np,
>> "allwinner,sun50i-h6-operating-points")) {
>> + } else if (of_device_is_compatible(np,
>> "allwinner,sun50i-h616-operating-points")) {
>> + } else {
>> of_node_put(np);
>> return -ENOENT;
>> }
>> @@ -66,17 +141,9 @@ static int sun50i_cpufreq_get_efuse(u32
>> *versions)
>> if (IS_ERR(speedbin))
>> return PTR_ERR(speedbin);
>>
>> - efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK;
>> -
>> - /*
>> - * We treat unexpected efuse values as if the SoC was from
>> - * the slowest bin. Expected efuse values are 1-3, slowest
>> - * to fastest.
>> - */
>> - if (efuse_value >= 1 && efuse_value <= 3)
>> - *versions = efuse_value - 1;
>> - else
>> - *versions = 0;
>> + ret = soc_data->efuse_xlate(versions, speedbin, name, len);
>> + if (ret)
>> + return ret;
>>
>> kfree(speedbin);
>> return 0;
>> @@ -84,25 +151,30 @@ static int sun50i_cpufreq_get_efuse(u32
>> *versions)
>>
>> static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
>> {
>> + const struct of_device_id *match;
>> + const struct sunxi_cpufreq_soc_data *soc_data;
>> int *opp_tokens;
>> char name[MAX_NAME_LEN];
>> unsigned int cpu;
>> - u32 speed = 0;
>> + u32 version = 0;
>> int ret;
>>
>> + match = dev_get_platdata(&pdev->dev);
>> + if (!match)
>> + return -EINVAL;
>> + soc_data = match->data;
>> +
>> opp_tokens = kcalloc(num_possible_cpus(), sizeof(*opp_tokens),
>> GFP_KERNEL);
>> if (!opp_tokens)
>> return -ENOMEM;
>>
>> - ret = sun50i_cpufreq_get_efuse(&speed);
>> + ret = sun50i_cpufreq_get_efuse(match->data, &version, name);
>> if (ret) {
>> kfree(opp_tokens);
>> return ret;
>> }
>>
>> - snprintf(name, MAX_NAME_LEN, "speed%d", speed);
>> -
>> for_each_possible_cpu(cpu) {
>> struct device *cpu_dev = get_cpu_device(cpu);
>>
>> @@ -117,6 +189,16 @@ static int sun50i_cpufreq_nvmem_probe(struct
>> platform_device *pdev)
>> pr_err("Failed to set prop name\n");
>> goto free_opp;
>> }
>> +
>> + if (soc_data->ver_freq_limit) {
>> + opp_tokens[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
>> + &version, 1);
>> + if (opp_tokens[cpu] < 0) {
>> + ret = opp_tokens[cpu];
>> + pr_err("Failed to set hw\n");
>> + goto free_opp;
>> + }
>> + }
>> }
>>
>> cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt",
>> -1,
>> @@ -132,6 +214,8 @@ static int sun50i_cpufreq_nvmem_probe(struct
>> platform_device *pdev)
>> free_opp:
>> for_each_possible_cpu(cpu)
>> dev_pm_opp_put_prop_name(opp_tokens[cpu]);
>> + if (soc_data->ver_freq_limit)
>> + dev_pm_opp_put_supported_hw(opp_tokens[cpu]);
>> kfree(opp_tokens);
>>
>> return ret;
>> @@ -140,12 +224,21 @@ static int sun50i_cpufreq_nvmem_probe(struct
>> platform_device *pdev)
>> static int sun50i_cpufreq_nvmem_remove(struct platform_device
>> *pdev)
>> {
>> int *opp_tokens = platform_get_drvdata(pdev);
>> + const struct of_device_id *match;
>> + const struct sunxi_cpufreq_soc_data *soc_data;
>> unsigned int cpu;
>>
>> + match = dev_get_platdata(&pdev->dev);
>> + if (!match)
>> + return -EINVAL;
>> + soc_data = match->data;
>> +
>> platform_device_unregister(cpufreq_dt_pdev);
>>
>> for_each_possible_cpu(cpu)
>> dev_pm_opp_put_prop_name(opp_tokens[cpu]);
>> + if (soc_data->ver_freq_limit)
>> + dev_pm_opp_put_supported_hw(opp_tokens[cpu]);
>>
>> kfree(opp_tokens);
>>
>> @@ -160,8 +253,18 @@ static struct platform_driver
>> sun50i_cpufreq_driver = {
>> },
>> };
>>
>> +static const struct sunxi_cpufreq_soc_data sun50i_h616_data = {
>> + .efuse_xlate = sun50i_h616_efuse_xlate,
>> + .ver_freq_limit = true,
>> +};
>> +
>> +static const struct sunxi_cpufreq_soc_data sun50i_h6_data = {
>> + .efuse_xlate = sun50i_h6_efuse_xlate,
>> +};
>> +
>> static const struct of_device_id sun50i_cpufreq_match_list[] = {
>> - { .compatible = "allwinner,sun50i-h6" },
>> + { .compatible = "allwinner,sun50i-h6", .data = &sun50i_h6_data },
>> + { .compatible = "allwinner,sun50i-h616", .data =
>> &sun50i_h616_data },
>> {}
>> };
>> MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
>> @@ -197,8 +300,8 @@ static int __init sun50i_cpufreq_init(void)
>> return ret;
>>
>> sun50i_cpufreq_pdev =
>> - platform_device_register_simple("sun50i-cpufreq-nvmem",
>> - -1, NULL, 0);
>> + platform_device_register_data(NULL, "sun50i-cpufreq-nvmem",
>> + -1, match, sizeof(*match));
>> ret = PTR_ERR_OR_ZERO(sun50i_cpufreq_pdev);
>> if (ret == 0)
>> return 0;
>>