186d231b1SJohnson Wang // SPDX-License-Identifier: GPL-2.0-only
286d231b1SJohnson Wang /*
386d231b1SJohnson Wang * Copyright (C) 2022 MediaTek Inc.
486d231b1SJohnson Wang */
586d231b1SJohnson Wang
686d231b1SJohnson Wang #include <linux/clk.h>
786d231b1SJohnson Wang #include <linux/devfreq.h>
886d231b1SJohnson Wang #include <linux/minmax.h>
986d231b1SJohnson Wang #include <linux/module.h>
1086d231b1SJohnson Wang #include <linux/of.h>
1186d231b1SJohnson Wang #include <linux/platform_device.h>
1286d231b1SJohnson Wang #include <linux/pm_opp.h>
1386d231b1SJohnson Wang #include <linux/regulator/consumer.h>
1486d231b1SJohnson Wang
1586d231b1SJohnson Wang struct mtk_ccifreq_platform_data {
1686d231b1SJohnson Wang int min_volt_shift;
1786d231b1SJohnson Wang int max_volt_shift;
1886d231b1SJohnson Wang int proc_max_volt;
1986d231b1SJohnson Wang int sram_min_volt;
2086d231b1SJohnson Wang int sram_max_volt;
2186d231b1SJohnson Wang };
2286d231b1SJohnson Wang
2386d231b1SJohnson Wang struct mtk_ccifreq_drv {
2486d231b1SJohnson Wang struct device *dev;
2586d231b1SJohnson Wang struct devfreq *devfreq;
2686d231b1SJohnson Wang struct regulator *proc_reg;
2786d231b1SJohnson Wang struct regulator *sram_reg;
2886d231b1SJohnson Wang struct clk *cci_clk;
2986d231b1SJohnson Wang struct clk *inter_clk;
3086d231b1SJohnson Wang int inter_voltage;
3186d231b1SJohnson Wang unsigned long pre_freq;
3286d231b1SJohnson Wang /* Avoid race condition for regulators between notify and policy */
3386d231b1SJohnson Wang struct mutex reg_lock;
3486d231b1SJohnson Wang struct notifier_block opp_nb;
3586d231b1SJohnson Wang const struct mtk_ccifreq_platform_data *soc_data;
3686d231b1SJohnson Wang int vtrack_max;
3786d231b1SJohnson Wang };
3886d231b1SJohnson Wang
mtk_ccifreq_set_voltage(struct mtk_ccifreq_drv * drv,int new_voltage)3986d231b1SJohnson Wang static int mtk_ccifreq_set_voltage(struct mtk_ccifreq_drv *drv, int new_voltage)
4086d231b1SJohnson Wang {
4186d231b1SJohnson Wang const struct mtk_ccifreq_platform_data *soc_data = drv->soc_data;
4286d231b1SJohnson Wang struct device *dev = drv->dev;
4386d231b1SJohnson Wang int pre_voltage, pre_vsram, new_vsram, vsram, voltage, ret;
4486d231b1SJohnson Wang int retry_max = drv->vtrack_max;
4586d231b1SJohnson Wang
4686d231b1SJohnson Wang if (!drv->sram_reg) {
4786d231b1SJohnson Wang ret = regulator_set_voltage(drv->proc_reg, new_voltage,
4886d231b1SJohnson Wang drv->soc_data->proc_max_volt);
4986d231b1SJohnson Wang return ret;
5086d231b1SJohnson Wang }
5186d231b1SJohnson Wang
5286d231b1SJohnson Wang pre_voltage = regulator_get_voltage(drv->proc_reg);
5386d231b1SJohnson Wang if (pre_voltage < 0) {
5486d231b1SJohnson Wang dev_err(dev, "invalid vproc value: %d\n", pre_voltage);
5586d231b1SJohnson Wang return pre_voltage;
5686d231b1SJohnson Wang }
5786d231b1SJohnson Wang
5886d231b1SJohnson Wang pre_vsram = regulator_get_voltage(drv->sram_reg);
5986d231b1SJohnson Wang if (pre_vsram < 0) {
6086d231b1SJohnson Wang dev_err(dev, "invalid vsram value: %d\n", pre_vsram);
6186d231b1SJohnson Wang return pre_vsram;
6286d231b1SJohnson Wang }
6386d231b1SJohnson Wang
6486d231b1SJohnson Wang new_vsram = clamp(new_voltage + soc_data->min_volt_shift,
6586d231b1SJohnson Wang soc_data->sram_min_volt, soc_data->sram_max_volt);
6686d231b1SJohnson Wang
6786d231b1SJohnson Wang do {
6886d231b1SJohnson Wang if (pre_voltage <= new_voltage) {
6986d231b1SJohnson Wang vsram = clamp(pre_voltage + soc_data->max_volt_shift,
7086d231b1SJohnson Wang soc_data->sram_min_volt, new_vsram);
7186d231b1SJohnson Wang ret = regulator_set_voltage(drv->sram_reg, vsram,
7286d231b1SJohnson Wang soc_data->sram_max_volt);
7386d231b1SJohnson Wang if (ret)
7486d231b1SJohnson Wang return ret;
7586d231b1SJohnson Wang
7686d231b1SJohnson Wang if (vsram == soc_data->sram_max_volt ||
7786d231b1SJohnson Wang new_vsram == soc_data->sram_min_volt)
7886d231b1SJohnson Wang voltage = new_voltage;
7986d231b1SJohnson Wang else
8086d231b1SJohnson Wang voltage = vsram - soc_data->min_volt_shift;
8186d231b1SJohnson Wang
8286d231b1SJohnson Wang ret = regulator_set_voltage(drv->proc_reg, voltage,
8386d231b1SJohnson Wang soc_data->proc_max_volt);
8486d231b1SJohnson Wang if (ret) {
8586d231b1SJohnson Wang regulator_set_voltage(drv->sram_reg, pre_vsram,
8686d231b1SJohnson Wang soc_data->sram_max_volt);
8786d231b1SJohnson Wang return ret;
8886d231b1SJohnson Wang }
8986d231b1SJohnson Wang } else if (pre_voltage > new_voltage) {
9086d231b1SJohnson Wang voltage = max(new_voltage,
9186d231b1SJohnson Wang pre_vsram - soc_data->max_volt_shift);
9286d231b1SJohnson Wang ret = regulator_set_voltage(drv->proc_reg, voltage,
9386d231b1SJohnson Wang soc_data->proc_max_volt);
9486d231b1SJohnson Wang if (ret)
9586d231b1SJohnson Wang return ret;
9686d231b1SJohnson Wang
9786d231b1SJohnson Wang if (voltage == new_voltage)
9886d231b1SJohnson Wang vsram = new_vsram;
9986d231b1SJohnson Wang else
10086d231b1SJohnson Wang vsram = max(new_vsram,
10186d231b1SJohnson Wang voltage + soc_data->min_volt_shift);
10286d231b1SJohnson Wang
10386d231b1SJohnson Wang ret = regulator_set_voltage(drv->sram_reg, vsram,
10486d231b1SJohnson Wang soc_data->sram_max_volt);
10586d231b1SJohnson Wang if (ret) {
10686d231b1SJohnson Wang regulator_set_voltage(drv->proc_reg, pre_voltage,
10786d231b1SJohnson Wang soc_data->proc_max_volt);
10886d231b1SJohnson Wang return ret;
10986d231b1SJohnson Wang }
11086d231b1SJohnson Wang }
11186d231b1SJohnson Wang
11286d231b1SJohnson Wang pre_voltage = voltage;
11386d231b1SJohnson Wang pre_vsram = vsram;
11486d231b1SJohnson Wang
11586d231b1SJohnson Wang if (--retry_max < 0) {
11686d231b1SJohnson Wang dev_err(dev,
11786d231b1SJohnson Wang "over loop count, failed to set voltage\n");
11886d231b1SJohnson Wang return -EINVAL;
11986d231b1SJohnson Wang }
12086d231b1SJohnson Wang } while (voltage != new_voltage || vsram != new_vsram);
12186d231b1SJohnson Wang
12286d231b1SJohnson Wang return 0;
12386d231b1SJohnson Wang }
12486d231b1SJohnson Wang
mtk_ccifreq_target(struct device * dev,unsigned long * freq,u32 flags)12586d231b1SJohnson Wang static int mtk_ccifreq_target(struct device *dev, unsigned long *freq,
12686d231b1SJohnson Wang u32 flags)
12786d231b1SJohnson Wang {
12886d231b1SJohnson Wang struct mtk_ccifreq_drv *drv = dev_get_drvdata(dev);
129*a83bfdcaSSukrut Bellary struct clk *cci_pll;
13086d231b1SJohnson Wang struct dev_pm_opp *opp;
13186d231b1SJohnson Wang unsigned long opp_rate;
13286d231b1SJohnson Wang int voltage, pre_voltage, inter_voltage, target_voltage, ret;
13386d231b1SJohnson Wang
13486d231b1SJohnson Wang if (!drv)
13586d231b1SJohnson Wang return -EINVAL;
13686d231b1SJohnson Wang
13786d231b1SJohnson Wang if (drv->pre_freq == *freq)
13886d231b1SJohnson Wang return 0;
13986d231b1SJohnson Wang
14086d231b1SJohnson Wang inter_voltage = drv->inter_voltage;
141*a83bfdcaSSukrut Bellary cci_pll = clk_get_parent(drv->cci_clk);
14286d231b1SJohnson Wang
14386d231b1SJohnson Wang opp_rate = *freq;
14486d231b1SJohnson Wang opp = devfreq_recommended_opp(dev, &opp_rate, 1);
14586d231b1SJohnson Wang if (IS_ERR(opp)) {
14686d231b1SJohnson Wang dev_err(dev, "failed to find opp for freq: %ld\n", opp_rate);
14786d231b1SJohnson Wang return PTR_ERR(opp);
14886d231b1SJohnson Wang }
14986d231b1SJohnson Wang
15086d231b1SJohnson Wang mutex_lock(&drv->reg_lock);
15186d231b1SJohnson Wang
15286d231b1SJohnson Wang voltage = dev_pm_opp_get_voltage(opp);
15386d231b1SJohnson Wang dev_pm_opp_put(opp);
15486d231b1SJohnson Wang
15586d231b1SJohnson Wang pre_voltage = regulator_get_voltage(drv->proc_reg);
15686d231b1SJohnson Wang if (pre_voltage < 0) {
15786d231b1SJohnson Wang dev_err(dev, "invalid vproc value: %d\n", pre_voltage);
15886d231b1SJohnson Wang ret = pre_voltage;
15986d231b1SJohnson Wang goto out_unlock;
16086d231b1SJohnson Wang }
16186d231b1SJohnson Wang
16286d231b1SJohnson Wang /* scale up: set voltage first then freq. */
16386d231b1SJohnson Wang target_voltage = max(inter_voltage, voltage);
16486d231b1SJohnson Wang if (pre_voltage <= target_voltage) {
16586d231b1SJohnson Wang ret = mtk_ccifreq_set_voltage(drv, target_voltage);
16686d231b1SJohnson Wang if (ret) {
16786d231b1SJohnson Wang dev_err(dev, "failed to scale up voltage\n");
16886d231b1SJohnson Wang goto out_restore_voltage;
16986d231b1SJohnson Wang }
17086d231b1SJohnson Wang }
17186d231b1SJohnson Wang
17286d231b1SJohnson Wang /* switch the cci clock to intermediate clock source. */
17386d231b1SJohnson Wang ret = clk_set_parent(drv->cci_clk, drv->inter_clk);
17486d231b1SJohnson Wang if (ret) {
17586d231b1SJohnson Wang dev_err(dev, "failed to re-parent cci clock\n");
17686d231b1SJohnson Wang goto out_restore_voltage;
17786d231b1SJohnson Wang }
17886d231b1SJohnson Wang
17986d231b1SJohnson Wang /* set the original clock to target rate. */
18086d231b1SJohnson Wang ret = clk_set_rate(cci_pll, *freq);
18186d231b1SJohnson Wang if (ret) {
18286d231b1SJohnson Wang dev_err(dev, "failed to set cci pll rate: %d\n", ret);
18386d231b1SJohnson Wang clk_set_parent(drv->cci_clk, cci_pll);
18486d231b1SJohnson Wang goto out_restore_voltage;
18586d231b1SJohnson Wang }
18686d231b1SJohnson Wang
18786d231b1SJohnson Wang /* switch the cci clock back to the original clock source. */
18886d231b1SJohnson Wang ret = clk_set_parent(drv->cci_clk, cci_pll);
18986d231b1SJohnson Wang if (ret) {
19086d231b1SJohnson Wang dev_err(dev, "failed to re-parent cci clock\n");
19186d231b1SJohnson Wang mtk_ccifreq_set_voltage(drv, inter_voltage);
19286d231b1SJohnson Wang goto out_unlock;
19386d231b1SJohnson Wang }
19486d231b1SJohnson Wang
19586d231b1SJohnson Wang /*
19686d231b1SJohnson Wang * If the new voltage is lower than the intermediate voltage or the
19786d231b1SJohnson Wang * original voltage, scale down to the new voltage.
19886d231b1SJohnson Wang */
19986d231b1SJohnson Wang if (voltage < inter_voltage || voltage < pre_voltage) {
20086d231b1SJohnson Wang ret = mtk_ccifreq_set_voltage(drv, voltage);
20186d231b1SJohnson Wang if (ret) {
20286d231b1SJohnson Wang dev_err(dev, "failed to scale down voltage\n");
20386d231b1SJohnson Wang goto out_unlock;
20486d231b1SJohnson Wang }
20586d231b1SJohnson Wang }
20686d231b1SJohnson Wang
20786d231b1SJohnson Wang drv->pre_freq = *freq;
20886d231b1SJohnson Wang mutex_unlock(&drv->reg_lock);
20986d231b1SJohnson Wang
21086d231b1SJohnson Wang return 0;
21186d231b1SJohnson Wang
21286d231b1SJohnson Wang out_restore_voltage:
21386d231b1SJohnson Wang mtk_ccifreq_set_voltage(drv, pre_voltage);
21486d231b1SJohnson Wang
21586d231b1SJohnson Wang out_unlock:
21686d231b1SJohnson Wang mutex_unlock(&drv->reg_lock);
21786d231b1SJohnson Wang return ret;
21886d231b1SJohnson Wang }
21986d231b1SJohnson Wang
mtk_ccifreq_opp_notifier(struct notifier_block * nb,unsigned long event,void * data)22086d231b1SJohnson Wang static int mtk_ccifreq_opp_notifier(struct notifier_block *nb,
22186d231b1SJohnson Wang unsigned long event, void *data)
22286d231b1SJohnson Wang {
22386d231b1SJohnson Wang struct dev_pm_opp *opp = data;
22486d231b1SJohnson Wang struct mtk_ccifreq_drv *drv;
22586d231b1SJohnson Wang unsigned long freq, volt;
22686d231b1SJohnson Wang
22786d231b1SJohnson Wang drv = container_of(nb, struct mtk_ccifreq_drv, opp_nb);
22886d231b1SJohnson Wang
22986d231b1SJohnson Wang if (event == OPP_EVENT_ADJUST_VOLTAGE) {
23086d231b1SJohnson Wang freq = dev_pm_opp_get_freq(opp);
23186d231b1SJohnson Wang
23286d231b1SJohnson Wang mutex_lock(&drv->reg_lock);
23386d231b1SJohnson Wang /* current opp item is changed */
23486d231b1SJohnson Wang if (freq == drv->pre_freq) {
23586d231b1SJohnson Wang volt = dev_pm_opp_get_voltage(opp);
23686d231b1SJohnson Wang mtk_ccifreq_set_voltage(drv, volt);
23786d231b1SJohnson Wang }
23886d231b1SJohnson Wang mutex_unlock(&drv->reg_lock);
23986d231b1SJohnson Wang }
24086d231b1SJohnson Wang
24186d231b1SJohnson Wang return 0;
24286d231b1SJohnson Wang }
24386d231b1SJohnson Wang
24486d231b1SJohnson Wang static struct devfreq_dev_profile mtk_ccifreq_profile = {
24586d231b1SJohnson Wang .target = mtk_ccifreq_target,
24686d231b1SJohnson Wang };
24786d231b1SJohnson Wang
mtk_ccifreq_probe(struct platform_device * pdev)24886d231b1SJohnson Wang static int mtk_ccifreq_probe(struct platform_device *pdev)
24986d231b1SJohnson Wang {
25086d231b1SJohnson Wang struct device *dev = &pdev->dev;
25186d231b1SJohnson Wang struct mtk_ccifreq_drv *drv;
25286d231b1SJohnson Wang struct devfreq_passive_data *passive_data;
25386d231b1SJohnson Wang struct dev_pm_opp *opp;
25486d231b1SJohnson Wang unsigned long rate, opp_volt;
25586d231b1SJohnson Wang int ret;
25686d231b1SJohnson Wang
25786d231b1SJohnson Wang drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
25886d231b1SJohnson Wang if (!drv)
25986d231b1SJohnson Wang return -ENOMEM;
26086d231b1SJohnson Wang
26186d231b1SJohnson Wang drv->dev = dev;
26286d231b1SJohnson Wang drv->soc_data = (const struct mtk_ccifreq_platform_data *)
26386d231b1SJohnson Wang of_device_get_match_data(&pdev->dev);
26486d231b1SJohnson Wang mutex_init(&drv->reg_lock);
26586d231b1SJohnson Wang platform_set_drvdata(pdev, drv);
26686d231b1SJohnson Wang
26786d231b1SJohnson Wang drv->cci_clk = devm_clk_get(dev, "cci");
26886d231b1SJohnson Wang if (IS_ERR(drv->cci_clk)) {
26986d231b1SJohnson Wang ret = PTR_ERR(drv->cci_clk);
27086d231b1SJohnson Wang return dev_err_probe(dev, ret, "failed to get cci clk\n");
27186d231b1SJohnson Wang }
27286d231b1SJohnson Wang
27386d231b1SJohnson Wang drv->inter_clk = devm_clk_get(dev, "intermediate");
27486d231b1SJohnson Wang if (IS_ERR(drv->inter_clk)) {
27586d231b1SJohnson Wang ret = PTR_ERR(drv->inter_clk);
27686d231b1SJohnson Wang return dev_err_probe(dev, ret,
27786d231b1SJohnson Wang "failed to get intermediate clk\n");
27886d231b1SJohnson Wang }
27986d231b1SJohnson Wang
28086d231b1SJohnson Wang drv->proc_reg = devm_regulator_get_optional(dev, "proc");
28186d231b1SJohnson Wang if (IS_ERR(drv->proc_reg)) {
28286d231b1SJohnson Wang ret = PTR_ERR(drv->proc_reg);
28386d231b1SJohnson Wang return dev_err_probe(dev, ret,
28486d231b1SJohnson Wang "failed to get proc regulator\n");
28586d231b1SJohnson Wang }
28686d231b1SJohnson Wang
28786d231b1SJohnson Wang ret = regulator_enable(drv->proc_reg);
28886d231b1SJohnson Wang if (ret) {
28986d231b1SJohnson Wang dev_err(dev, "failed to enable proc regulator\n");
29086d231b1SJohnson Wang return ret;
29186d231b1SJohnson Wang }
29286d231b1SJohnson Wang
29386d231b1SJohnson Wang drv->sram_reg = devm_regulator_get_optional(dev, "sram");
294e09bd575SAngeloGioacchino Del Regno if (IS_ERR(drv->sram_reg)) {
295e09bd575SAngeloGioacchino Del Regno ret = PTR_ERR(drv->sram_reg);
296e09bd575SAngeloGioacchino Del Regno if (ret == -EPROBE_DEFER)
297e09bd575SAngeloGioacchino Del Regno goto out_free_resources;
298e09bd575SAngeloGioacchino Del Regno
29986d231b1SJohnson Wang drv->sram_reg = NULL;
300e09bd575SAngeloGioacchino Del Regno } else {
30186d231b1SJohnson Wang ret = regulator_enable(drv->sram_reg);
30286d231b1SJohnson Wang if (ret) {
30386d231b1SJohnson Wang dev_err(dev, "failed to enable sram regulator\n");
30486d231b1SJohnson Wang goto out_free_resources;
30586d231b1SJohnson Wang }
30686d231b1SJohnson Wang }
30786d231b1SJohnson Wang
30886d231b1SJohnson Wang /*
30986d231b1SJohnson Wang * We assume min voltage is 0 and tracking target voltage using
31086d231b1SJohnson Wang * min_volt_shift for each iteration.
31186d231b1SJohnson Wang * The retry_max is 3 times of expected iteration count.
31286d231b1SJohnson Wang */
31386d231b1SJohnson Wang drv->vtrack_max = 3 * DIV_ROUND_UP(max(drv->soc_data->sram_max_volt,
31486d231b1SJohnson Wang drv->soc_data->proc_max_volt),
31586d231b1SJohnson Wang drv->soc_data->min_volt_shift);
31686d231b1SJohnson Wang
31786d231b1SJohnson Wang ret = clk_prepare_enable(drv->cci_clk);
31886d231b1SJohnson Wang if (ret)
31986d231b1SJohnson Wang goto out_free_resources;
32086d231b1SJohnson Wang
32186d231b1SJohnson Wang ret = dev_pm_opp_of_add_table(dev);
32286d231b1SJohnson Wang if (ret) {
32386d231b1SJohnson Wang dev_err(dev, "failed to add opp table: %d\n", ret);
32486d231b1SJohnson Wang goto out_disable_cci_clk;
32586d231b1SJohnson Wang }
32686d231b1SJohnson Wang
32786d231b1SJohnson Wang rate = clk_get_rate(drv->inter_clk);
32886d231b1SJohnson Wang opp = dev_pm_opp_find_freq_ceil(dev, &rate);
32986d231b1SJohnson Wang if (IS_ERR(opp)) {
33086d231b1SJohnson Wang ret = PTR_ERR(opp);
33186d231b1SJohnson Wang dev_err(dev, "failed to get intermediate opp: %d\n", ret);
33286d231b1SJohnson Wang goto out_remove_opp_table;
33386d231b1SJohnson Wang }
33486d231b1SJohnson Wang drv->inter_voltage = dev_pm_opp_get_voltage(opp);
33586d231b1SJohnson Wang dev_pm_opp_put(opp);
33686d231b1SJohnson Wang
33786d231b1SJohnson Wang rate = U32_MAX;
33886d231b1SJohnson Wang opp = dev_pm_opp_find_freq_floor(drv->dev, &rate);
33986d231b1SJohnson Wang if (IS_ERR(opp)) {
34086d231b1SJohnson Wang dev_err(dev, "failed to get opp\n");
34186d231b1SJohnson Wang ret = PTR_ERR(opp);
34286d231b1SJohnson Wang goto out_remove_opp_table;
34386d231b1SJohnson Wang }
34486d231b1SJohnson Wang
34586d231b1SJohnson Wang opp_volt = dev_pm_opp_get_voltage(opp);
34686d231b1SJohnson Wang dev_pm_opp_put(opp);
34786d231b1SJohnson Wang ret = mtk_ccifreq_set_voltage(drv, opp_volt);
34886d231b1SJohnson Wang if (ret) {
34986d231b1SJohnson Wang dev_err(dev, "failed to scale to highest voltage %lu in proc_reg\n",
35086d231b1SJohnson Wang opp_volt);
35186d231b1SJohnson Wang goto out_remove_opp_table;
35286d231b1SJohnson Wang }
35386d231b1SJohnson Wang
35486d231b1SJohnson Wang passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
35586d231b1SJohnson Wang if (!passive_data) {
35686d231b1SJohnson Wang ret = -ENOMEM;
35786d231b1SJohnson Wang goto out_remove_opp_table;
35886d231b1SJohnson Wang }
35986d231b1SJohnson Wang
36086d231b1SJohnson Wang passive_data->parent_type = CPUFREQ_PARENT_DEV;
36186d231b1SJohnson Wang drv->devfreq = devm_devfreq_add_device(dev, &mtk_ccifreq_profile,
36286d231b1SJohnson Wang DEVFREQ_GOV_PASSIVE,
36386d231b1SJohnson Wang passive_data);
36486d231b1SJohnson Wang if (IS_ERR(drv->devfreq)) {
36586d231b1SJohnson Wang ret = -EPROBE_DEFER;
36686d231b1SJohnson Wang dev_err(dev, "failed to add devfreq device: %ld\n",
36786d231b1SJohnson Wang PTR_ERR(drv->devfreq));
36886d231b1SJohnson Wang goto out_remove_opp_table;
36986d231b1SJohnson Wang }
37086d231b1SJohnson Wang
37186d231b1SJohnson Wang drv->opp_nb.notifier_call = mtk_ccifreq_opp_notifier;
37286d231b1SJohnson Wang ret = dev_pm_opp_register_notifier(dev, &drv->opp_nb);
37386d231b1SJohnson Wang if (ret) {
37486d231b1SJohnson Wang dev_err(dev, "failed to register opp notifier: %d\n", ret);
37586d231b1SJohnson Wang goto out_remove_opp_table;
37686d231b1SJohnson Wang }
37786d231b1SJohnson Wang return 0;
37886d231b1SJohnson Wang
37986d231b1SJohnson Wang out_remove_opp_table:
38086d231b1SJohnson Wang dev_pm_opp_of_remove_table(dev);
38186d231b1SJohnson Wang
38286d231b1SJohnson Wang out_disable_cci_clk:
38386d231b1SJohnson Wang clk_disable_unprepare(drv->cci_clk);
38486d231b1SJohnson Wang
38586d231b1SJohnson Wang out_free_resources:
38686d231b1SJohnson Wang if (regulator_is_enabled(drv->proc_reg))
38786d231b1SJohnson Wang regulator_disable(drv->proc_reg);
38886d231b1SJohnson Wang if (drv->sram_reg && regulator_is_enabled(drv->sram_reg))
38986d231b1SJohnson Wang regulator_disable(drv->sram_reg);
39086d231b1SJohnson Wang
39186d231b1SJohnson Wang return ret;
39286d231b1SJohnson Wang }
39386d231b1SJohnson Wang
mtk_ccifreq_remove(struct platform_device * pdev)39486d231b1SJohnson Wang static int mtk_ccifreq_remove(struct platform_device *pdev)
39586d231b1SJohnson Wang {
39686d231b1SJohnson Wang struct device *dev = &pdev->dev;
39786d231b1SJohnson Wang struct mtk_ccifreq_drv *drv;
39886d231b1SJohnson Wang
39986d231b1SJohnson Wang drv = platform_get_drvdata(pdev);
40086d231b1SJohnson Wang
40186d231b1SJohnson Wang dev_pm_opp_unregister_notifier(dev, &drv->opp_nb);
40286d231b1SJohnson Wang dev_pm_opp_of_remove_table(dev);
40386d231b1SJohnson Wang clk_disable_unprepare(drv->cci_clk);
40486d231b1SJohnson Wang regulator_disable(drv->proc_reg);
40586d231b1SJohnson Wang if (drv->sram_reg)
40686d231b1SJohnson Wang regulator_disable(drv->sram_reg);
40786d231b1SJohnson Wang
40886d231b1SJohnson Wang return 0;
40986d231b1SJohnson Wang }
41086d231b1SJohnson Wang
41186d231b1SJohnson Wang static const struct mtk_ccifreq_platform_data mt8183_platform_data = {
41286d231b1SJohnson Wang .min_volt_shift = 100000,
41386d231b1SJohnson Wang .max_volt_shift = 200000,
41486d231b1SJohnson Wang .proc_max_volt = 1150000,
41586d231b1SJohnson Wang };
41686d231b1SJohnson Wang
41786d231b1SJohnson Wang static const struct mtk_ccifreq_platform_data mt8186_platform_data = {
41886d231b1SJohnson Wang .min_volt_shift = 100000,
41986d231b1SJohnson Wang .max_volt_shift = 250000,
42086d231b1SJohnson Wang .proc_max_volt = 1118750,
42186d231b1SJohnson Wang .sram_min_volt = 850000,
42286d231b1SJohnson Wang .sram_max_volt = 1118750,
42386d231b1SJohnson Wang };
42486d231b1SJohnson Wang
42586d231b1SJohnson Wang static const struct of_device_id mtk_ccifreq_machines[] = {
42686d231b1SJohnson Wang { .compatible = "mediatek,mt8183-cci", .data = &mt8183_platform_data },
42786d231b1SJohnson Wang { .compatible = "mediatek,mt8186-cci", .data = &mt8186_platform_data },
42886d231b1SJohnson Wang { },
42986d231b1SJohnson Wang };
43086d231b1SJohnson Wang MODULE_DEVICE_TABLE(of, mtk_ccifreq_machines);
43186d231b1SJohnson Wang
43286d231b1SJohnson Wang static struct platform_driver mtk_ccifreq_platdrv = {
43386d231b1SJohnson Wang .probe = mtk_ccifreq_probe,
43486d231b1SJohnson Wang .remove = mtk_ccifreq_remove,
43586d231b1SJohnson Wang .driver = {
43686d231b1SJohnson Wang .name = "mtk-ccifreq",
43786d231b1SJohnson Wang .of_match_table = mtk_ccifreq_machines,
43886d231b1SJohnson Wang },
43986d231b1SJohnson Wang };
44086d231b1SJohnson Wang module_platform_driver(mtk_ccifreq_platdrv);
44186d231b1SJohnson Wang
44286d231b1SJohnson Wang MODULE_DESCRIPTION("MediaTek CCI devfreq driver");
44386d231b1SJohnson Wang MODULE_AUTHOR("Jia-Wei Chang <jia-wei.chang@mediatek.com>");
44486d231b1SJohnson Wang MODULE_LICENSE("GPL v2");
445