Skip to content

Commit

Permalink
cpufreq: mediatek: Link CCI device to CPU
Browse files Browse the repository at this point in the history
In some MediaTek SoCs, like MT8183, CPU and CCI share the same power
supplies. Cpufreq needs to check if CCI devfreq exists and wait until
CCI devfreq ready before scaling frequency.

Before CCI devfreq is ready, we record the voltage when booting to
kernel and use the max(cpu target voltage, booting voltage) to
prevent cpufreq adjust to the lower voltage which will cause the CCI
crash because of high frequency and low voltage.

- Add is_ccifreq_ready() to link CCI device to CPI, and CPU will start
  DVFS when CCI is ready.
- Add platform data for MT8183.

Signed-off-by: Jia-Wei Chang <[email protected]>
Signed-off-by: Rex-BC Chen <[email protected]>
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Reviewed-by: Kevin Hilman <[email protected]>
Signed-off-by: Viresh Kumar <[email protected]>
  • Loading branch information
mtk-rex-bc-chen authored and vireshk committed May 12, 2022
1 parent 818c832 commit 0daa473
Showing 1 changed file with 81 additions and 1 deletion.
82 changes: 81 additions & 1 deletion drivers/cpufreq/mediatek-cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct mtk_cpufreq_platform_data {
int proc_max_volt;
int sram_min_volt;
int sram_max_volt;
bool ccifreq_supported;
};

/*
Expand All @@ -38,13 +39,15 @@ struct mtk_cpufreq_platform_data {
struct mtk_cpu_dvfs_info {
struct cpumask cpus;
struct device *cpu_dev;
struct device *cci_dev;
struct regulator *proc_reg;
struct regulator *sram_reg;
struct clk *cpu_clk;
struct clk *inter_clk;
struct list_head list_head;
int intermediate_voltage;
bool need_voltage_tracking;
int vproc_on_boot;
int pre_vproc;
/* Avoid race condition for regulators between notify and policy */
struct mutex reg_lock;
Expand All @@ -53,6 +56,7 @@ struct mtk_cpu_dvfs_info {
unsigned long current_freq;
const struct mtk_cpufreq_platform_data *soc_data;
int vtrack_max;
bool ccifreq_bound;
};

static struct platform_device *cpufreq_pdev;
Expand Down Expand Up @@ -171,6 +175,28 @@ static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
return ret;
}

static bool is_ccifreq_ready(struct mtk_cpu_dvfs_info *info)
{
struct device_link *sup_link;

if (info->ccifreq_bound)
return true;

sup_link = device_link_add(info->cpu_dev, info->cci_dev,
DL_FLAG_AUTOREMOVE_CONSUMER);
if (!sup_link) {
dev_err(info->cpu_dev, "cpu%d: sup_link is NULL\n", info->opp_cpu);
return false;
}

if (sup_link->supplier->links.status != DL_DEV_DRIVER_BOUND)
return false;

info->ccifreq_bound = true;

return true;
}

static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index)
{
Expand Down Expand Up @@ -212,6 +238,14 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
vproc = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);

/*
* If MediaTek cci is supported but is not ready, we will use the value
* of max(target cpu voltage, booting voltage) to prevent high freqeuncy
* low voltage crash.
*/
if (info->soc_data->ccifreq_supported && !is_ccifreq_ready(info))
vproc = max(vproc, info->vproc_on_boot);

/*
* If the new voltage or the intermediate voltage is higher than the
* current voltage, scale up voltage first.
Expand Down Expand Up @@ -333,6 +367,23 @@ static int mtk_cpufreq_opp_notifier(struct notifier_block *nb,
return notifier_from_errno(ret);
}

static struct device *of_get_cci(struct device *cpu_dev)
{
struct device_node *np;
struct platform_device *pdev;

np = of_parse_phandle(cpu_dev->of_node, "mediatek,cci", 0);
if (IS_ERR_OR_NULL(np))
return NULL;

pdev = of_find_device_by_node(np);
of_node_put(np);
if (IS_ERR_OR_NULL(pdev))
return NULL;

return &pdev->dev;
}

static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
{
struct device *cpu_dev;
Expand All @@ -347,6 +398,16 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
}
info->cpu_dev = cpu_dev;

info->ccifreq_bound = false;
if (info->soc_data->ccifreq_supported) {
info->cci_dev = of_get_cci(info->cpu_dev);
if (IS_ERR_OR_NULL(info->cci_dev)) {
ret = PTR_ERR(info->cci_dev);
dev_err(cpu_dev, "cpu%d: failed to get cci device\n", cpu);
return -ENODEV;
}
}

info->cpu_clk = clk_get(cpu_dev, "cpu");
if (IS_ERR(info->cpu_clk)) {
ret = PTR_ERR(info->cpu_clk);
Expand Down Expand Up @@ -410,6 +471,15 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
if (ret)
goto out_disable_mux_clock;

if (info->soc_data->ccifreq_supported) {
info->vproc_on_boot = regulator_get_voltage(info->proc_reg);
if (info->vproc_on_boot < 0) {
dev_err(info->cpu_dev,
"invalid Vproc value: %d\n", info->vproc_on_boot);
goto out_disable_inter_clock;
}
}

/* Search a safe voltage for intermediate frequency. */
rate = clk_get_rate(info->inter_clk);
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
Expand Down Expand Up @@ -617,6 +687,16 @@ static const struct mtk_cpufreq_platform_data mt2701_platform_data = {
.proc_max_volt = 1150000,
.sram_min_volt = 0,
.sram_max_volt = 1150000,
.ccifreq_supported = false,
};

static const struct mtk_cpufreq_platform_data mt8183_platform_data = {
.min_volt_shift = 100000,
.max_volt_shift = 200000,
.proc_max_volt = 1150000,
.sram_min_volt = 0,
.sram_max_volt = 1150000,
.ccifreq_supported = true,
};

/* List of machines supported by this driver */
Expand All @@ -629,7 +709,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
{ .compatible = "mediatek,mt817x", .data = &mt2701_platform_data },
{ .compatible = "mediatek,mt8173", .data = &mt2701_platform_data },
{ .compatible = "mediatek,mt8176", .data = &mt2701_platform_data },
{ .compatible = "mediatek,mt8183", .data = &mt2701_platform_data },
{ .compatible = "mediatek,mt8183", .data = &mt8183_platform_data },
{ .compatible = "mediatek,mt8365", .data = &mt2701_platform_data },
{ .compatible = "mediatek,mt8516", .data = &mt2701_platform_data },
{ }
Expand Down

0 comments on commit 0daa473

Please sign in to comment.