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);
129a83bfdcaSSukrut 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
140d2805601SMark Tseng mutex_lock(&drv->reg_lock);
141d2805601SMark Tseng
14286d231b1SJohnson Wang inter_voltage = drv->inter_voltage;
143a83bfdcaSSukrut Bellary cci_pll = clk_get_parent(drv->cci_clk);
14486d231b1SJohnson Wang
14586d231b1SJohnson Wang opp_rate = *freq;
14686d231b1SJohnson Wang opp = devfreq_recommended_opp(dev, &opp_rate, 1);
14786d231b1SJohnson Wang if (IS_ERR(opp)) {
14886d231b1SJohnson Wang dev_err(dev, "failed to find opp for freq: %ld\n", opp_rate);
1496c29e29eSDan Carpenter ret = PTR_ERR(opp);
1506c29e29eSDan Carpenter goto out_unlock;
15186d231b1SJohnson Wang }
15286d231b1SJohnson Wang
15386d231b1SJohnson Wang voltage = dev_pm_opp_get_voltage(opp);
15486d231b1SJohnson Wang dev_pm_opp_put(opp);
15586d231b1SJohnson Wang
15686d231b1SJohnson Wang pre_voltage = regulator_get_voltage(drv->proc_reg);
15786d231b1SJohnson Wang if (pre_voltage < 0) {
15886d231b1SJohnson Wang dev_err(dev, "invalid vproc value: %d\n", pre_voltage);
15986d231b1SJohnson Wang ret = pre_voltage;
16086d231b1SJohnson Wang goto out_unlock;
16186d231b1SJohnson Wang }
16286d231b1SJohnson Wang
16386d231b1SJohnson Wang /* scale up: set voltage first then freq. */
16486d231b1SJohnson Wang target_voltage = max(inter_voltage, voltage);
16586d231b1SJohnson Wang if (pre_voltage <= target_voltage) {
16686d231b1SJohnson Wang ret = mtk_ccifreq_set_voltage(drv, target_voltage);
16786d231b1SJohnson Wang if (ret) {
16886d231b1SJohnson Wang dev_err(dev, "failed to scale up voltage\n");
16986d231b1SJohnson Wang goto out_restore_voltage;
17086d231b1SJohnson Wang }
17186d231b1SJohnson Wang }
17286d231b1SJohnson Wang
17386d231b1SJohnson Wang /* switch the cci clock to intermediate clock source. */
17486d231b1SJohnson Wang ret = clk_set_parent(drv->cci_clk, drv->inter_clk);
17586d231b1SJohnson Wang if (ret) {
17686d231b1SJohnson Wang dev_err(dev, "failed to re-parent cci clock\n");
17786d231b1SJohnson Wang goto out_restore_voltage;
17886d231b1SJohnson Wang }
17986d231b1SJohnson Wang
18086d231b1SJohnson Wang /* set the original clock to target rate. */
18186d231b1SJohnson Wang ret = clk_set_rate(cci_pll, *freq);
18286d231b1SJohnson Wang if (ret) {
18386d231b1SJohnson Wang dev_err(dev, "failed to set cci pll rate: %d\n", ret);
18486d231b1SJohnson Wang clk_set_parent(drv->cci_clk, cci_pll);
18586d231b1SJohnson Wang goto out_restore_voltage;
18686d231b1SJohnson Wang }
18786d231b1SJohnson Wang
18886d231b1SJohnson Wang /* switch the cci clock back to the original clock source. */
18986d231b1SJohnson Wang ret = clk_set_parent(drv->cci_clk, cci_pll);
19086d231b1SJohnson Wang if (ret) {
19186d231b1SJohnson Wang dev_err(dev, "failed to re-parent cci clock\n");
19286d231b1SJohnson Wang mtk_ccifreq_set_voltage(drv, inter_voltage);
19386d231b1SJohnson Wang goto out_unlock;
19486d231b1SJohnson Wang }
19586d231b1SJohnson Wang
19686d231b1SJohnson Wang /*
19786d231b1SJohnson Wang * If the new voltage is lower than the intermediate voltage or the
19886d231b1SJohnson Wang * original voltage, scale down to the new voltage.
19986d231b1SJohnson Wang */
20086d231b1SJohnson Wang if (voltage < inter_voltage || voltage < pre_voltage) {
20186d231b1SJohnson Wang ret = mtk_ccifreq_set_voltage(drv, voltage);
20286d231b1SJohnson Wang if (ret) {
20386d231b1SJohnson Wang dev_err(dev, "failed to scale down voltage\n");
20486d231b1SJohnson Wang goto out_unlock;
20586d231b1SJohnson Wang }
20686d231b1SJohnson Wang }
20786d231b1SJohnson Wang
20886d231b1SJohnson Wang drv->pre_freq = *freq;
20986d231b1SJohnson Wang mutex_unlock(&drv->reg_lock);
21086d231b1SJohnson Wang
21186d231b1SJohnson Wang return 0;
21286d231b1SJohnson Wang
21386d231b1SJohnson Wang out_restore_voltage:
21486d231b1SJohnson Wang mtk_ccifreq_set_voltage(drv, pre_voltage);
21586d231b1SJohnson Wang
21686d231b1SJohnson Wang out_unlock:
21786d231b1SJohnson Wang mutex_unlock(&drv->reg_lock);
21886d231b1SJohnson Wang return ret;
21986d231b1SJohnson Wang }
22086d231b1SJohnson Wang
mtk_ccifreq_opp_notifier(struct notifier_block * nb,unsigned long event,void * data)22186d231b1SJohnson Wang static int mtk_ccifreq_opp_notifier(struct notifier_block *nb,
22286d231b1SJohnson Wang unsigned long event, void *data)
22386d231b1SJohnson Wang {
22486d231b1SJohnson Wang struct dev_pm_opp *opp = data;
22586d231b1SJohnson Wang struct mtk_ccifreq_drv *drv;
22686d231b1SJohnson Wang unsigned long freq, volt;
22786d231b1SJohnson Wang
22886d231b1SJohnson Wang drv = container_of(nb, struct mtk_ccifreq_drv, opp_nb);
22986d231b1SJohnson Wang
23086d231b1SJohnson Wang if (event == OPP_EVENT_ADJUST_VOLTAGE) {
231d2805601SMark Tseng mutex_lock(&drv->reg_lock);
23286d231b1SJohnson Wang freq = dev_pm_opp_get_freq(opp);
23386d231b1SJohnson Wang
23486d231b1SJohnson Wang /* current opp item is changed */
23586d231b1SJohnson Wang if (freq == drv->pre_freq) {
23686d231b1SJohnson Wang volt = dev_pm_opp_get_voltage(opp);
23786d231b1SJohnson Wang mtk_ccifreq_set_voltage(drv, volt);
23886d231b1SJohnson Wang }
23986d231b1SJohnson Wang mutex_unlock(&drv->reg_lock);
24086d231b1SJohnson Wang }
24186d231b1SJohnson Wang
24286d231b1SJohnson Wang return 0;
24386d231b1SJohnson Wang }
24486d231b1SJohnson Wang
24586d231b1SJohnson Wang static struct devfreq_dev_profile mtk_ccifreq_profile = {
24686d231b1SJohnson Wang .target = mtk_ccifreq_target,
24786d231b1SJohnson Wang };
24886d231b1SJohnson Wang
mtk_ccifreq_probe(struct platform_device * pdev)24986d231b1SJohnson Wang static int mtk_ccifreq_probe(struct platform_device *pdev)
25086d231b1SJohnson Wang {
25186d231b1SJohnson Wang struct device *dev = &pdev->dev;
25286d231b1SJohnson Wang struct mtk_ccifreq_drv *drv;
25386d231b1SJohnson Wang struct devfreq_passive_data *passive_data;
25486d231b1SJohnson Wang struct dev_pm_opp *opp;
25586d231b1SJohnson Wang unsigned long rate, opp_volt;
25686d231b1SJohnson Wang int ret;
25786d231b1SJohnson Wang
25886d231b1SJohnson Wang drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
25986d231b1SJohnson Wang if (!drv)
26086d231b1SJohnson Wang return -ENOMEM;
26186d231b1SJohnson Wang
26286d231b1SJohnson Wang drv->dev = dev;
26386d231b1SJohnson Wang drv->soc_data = (const struct mtk_ccifreq_platform_data *)
26486d231b1SJohnson Wang of_device_get_match_data(&pdev->dev);
26586d231b1SJohnson Wang mutex_init(&drv->reg_lock);
26686d231b1SJohnson Wang platform_set_drvdata(pdev, drv);
26786d231b1SJohnson Wang
26886d231b1SJohnson Wang drv->cci_clk = devm_clk_get(dev, "cci");
26986d231b1SJohnson Wang if (IS_ERR(drv->cci_clk)) {
27086d231b1SJohnson Wang ret = PTR_ERR(drv->cci_clk);
27186d231b1SJohnson Wang return dev_err_probe(dev, ret, "failed to get cci clk\n");
27286d231b1SJohnson Wang }
27386d231b1SJohnson Wang
27486d231b1SJohnson Wang drv->inter_clk = devm_clk_get(dev, "intermediate");
27586d231b1SJohnson Wang if (IS_ERR(drv->inter_clk)) {
27686d231b1SJohnson Wang ret = PTR_ERR(drv->inter_clk);
27786d231b1SJohnson Wang return dev_err_probe(dev, ret,
27886d231b1SJohnson Wang "failed to get intermediate clk\n");
27986d231b1SJohnson Wang }
28086d231b1SJohnson Wang
28186d231b1SJohnson Wang drv->proc_reg = devm_regulator_get_optional(dev, "proc");
28286d231b1SJohnson Wang if (IS_ERR(drv->proc_reg)) {
28386d231b1SJohnson Wang ret = PTR_ERR(drv->proc_reg);
28486d231b1SJohnson Wang return dev_err_probe(dev, ret,
28586d231b1SJohnson Wang "failed to get proc regulator\n");
28686d231b1SJohnson Wang }
28786d231b1SJohnson Wang
28886d231b1SJohnson Wang ret = regulator_enable(drv->proc_reg);
28986d231b1SJohnson Wang if (ret) {
29086d231b1SJohnson Wang dev_err(dev, "failed to enable proc regulator\n");
29186d231b1SJohnson Wang return ret;
29286d231b1SJohnson Wang }
29386d231b1SJohnson Wang
29486d231b1SJohnson Wang drv->sram_reg = devm_regulator_get_optional(dev, "sram");
295e09bd575SAngeloGioacchino Del Regno if (IS_ERR(drv->sram_reg)) {
296e09bd575SAngeloGioacchino Del Regno ret = PTR_ERR(drv->sram_reg);
297e09bd575SAngeloGioacchino Del Regno if (ret == -EPROBE_DEFER)
298e09bd575SAngeloGioacchino Del Regno goto out_free_resources;
299e09bd575SAngeloGioacchino Del Regno
30086d231b1SJohnson Wang drv->sram_reg = NULL;
301e09bd575SAngeloGioacchino Del Regno } else {
30286d231b1SJohnson Wang ret = regulator_enable(drv->sram_reg);
30386d231b1SJohnson Wang if (ret) {
30486d231b1SJohnson Wang dev_err(dev, "failed to enable sram regulator\n");
30586d231b1SJohnson Wang goto out_free_resources;
30686d231b1SJohnson Wang }
30786d231b1SJohnson Wang }
30886d231b1SJohnson Wang
30986d231b1SJohnson Wang /*
31086d231b1SJohnson Wang * We assume min voltage is 0 and tracking target voltage using
31186d231b1SJohnson Wang * min_volt_shift for each iteration.
31286d231b1SJohnson Wang * The retry_max is 3 times of expected iteration count.
31386d231b1SJohnson Wang */
31486d231b1SJohnson Wang drv->vtrack_max = 3 * DIV_ROUND_UP(max(drv->soc_data->sram_max_volt,
31586d231b1SJohnson Wang drv->soc_data->proc_max_volt),
31686d231b1SJohnson Wang drv->soc_data->min_volt_shift);
31786d231b1SJohnson Wang
31886d231b1SJohnson Wang ret = clk_prepare_enable(drv->cci_clk);
31986d231b1SJohnson Wang if (ret)
32086d231b1SJohnson Wang goto out_free_resources;
32186d231b1SJohnson Wang
32286d231b1SJohnson Wang ret = dev_pm_opp_of_add_table(dev);
32386d231b1SJohnson Wang if (ret) {
32486d231b1SJohnson Wang dev_err(dev, "failed to add opp table: %d\n", ret);
32586d231b1SJohnson Wang goto out_disable_cci_clk;
32686d231b1SJohnson Wang }
32786d231b1SJohnson Wang
32886d231b1SJohnson Wang rate = clk_get_rate(drv->inter_clk);
32986d231b1SJohnson Wang opp = dev_pm_opp_find_freq_ceil(dev, &rate);
33086d231b1SJohnson Wang if (IS_ERR(opp)) {
33186d231b1SJohnson Wang ret = PTR_ERR(opp);
33286d231b1SJohnson Wang dev_err(dev, "failed to get intermediate opp: %d\n", ret);
33386d231b1SJohnson Wang goto out_remove_opp_table;
33486d231b1SJohnson Wang }
33586d231b1SJohnson Wang drv->inter_voltage = dev_pm_opp_get_voltage(opp);
33686d231b1SJohnson Wang dev_pm_opp_put(opp);
33786d231b1SJohnson Wang
33886d231b1SJohnson Wang rate = U32_MAX;
33986d231b1SJohnson Wang opp = dev_pm_opp_find_freq_floor(drv->dev, &rate);
34086d231b1SJohnson Wang if (IS_ERR(opp)) {
34186d231b1SJohnson Wang dev_err(dev, "failed to get opp\n");
34286d231b1SJohnson Wang ret = PTR_ERR(opp);
34386d231b1SJohnson Wang goto out_remove_opp_table;
34486d231b1SJohnson Wang }
34586d231b1SJohnson Wang
34686d231b1SJohnson Wang opp_volt = dev_pm_opp_get_voltage(opp);
34786d231b1SJohnson Wang dev_pm_opp_put(opp);
34886d231b1SJohnson Wang ret = mtk_ccifreq_set_voltage(drv, opp_volt);
34986d231b1SJohnson Wang if (ret) {
35086d231b1SJohnson Wang dev_err(dev, "failed to scale to highest voltage %lu in proc_reg\n",
35186d231b1SJohnson Wang opp_volt);
35286d231b1SJohnson Wang goto out_remove_opp_table;
35386d231b1SJohnson Wang }
35486d231b1SJohnson Wang
35586d231b1SJohnson Wang passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
35686d231b1SJohnson Wang if (!passive_data) {
35786d231b1SJohnson Wang ret = -ENOMEM;
35886d231b1SJohnson Wang goto out_remove_opp_table;
35986d231b1SJohnson Wang }
36086d231b1SJohnson Wang
36186d231b1SJohnson Wang passive_data->parent_type = CPUFREQ_PARENT_DEV;
36286d231b1SJohnson Wang drv->devfreq = devm_devfreq_add_device(dev, &mtk_ccifreq_profile,
36386d231b1SJohnson Wang DEVFREQ_GOV_PASSIVE,
36486d231b1SJohnson Wang passive_data);
36586d231b1SJohnson Wang if (IS_ERR(drv->devfreq)) {
36686d231b1SJohnson Wang ret = -EPROBE_DEFER;
36786d231b1SJohnson Wang dev_err(dev, "failed to add devfreq device: %ld\n",
36886d231b1SJohnson Wang PTR_ERR(drv->devfreq));
36986d231b1SJohnson Wang goto out_remove_opp_table;
37086d231b1SJohnson Wang }
37186d231b1SJohnson Wang
37286d231b1SJohnson Wang drv->opp_nb.notifier_call = mtk_ccifreq_opp_notifier;
37386d231b1SJohnson Wang ret = dev_pm_opp_register_notifier(dev, &drv->opp_nb);
37486d231b1SJohnson Wang if (ret) {
37586d231b1SJohnson Wang dev_err(dev, "failed to register opp notifier: %d\n", ret);
37686d231b1SJohnson Wang goto out_remove_opp_table;
37786d231b1SJohnson Wang }
37886d231b1SJohnson Wang return 0;
37986d231b1SJohnson Wang
38086d231b1SJohnson Wang out_remove_opp_table:
38186d231b1SJohnson Wang dev_pm_opp_of_remove_table(dev);
38286d231b1SJohnson Wang
38386d231b1SJohnson Wang out_disable_cci_clk:
38486d231b1SJohnson Wang clk_disable_unprepare(drv->cci_clk);
38586d231b1SJohnson Wang
38686d231b1SJohnson Wang out_free_resources:
38786d231b1SJohnson Wang if (regulator_is_enabled(drv->proc_reg))
38886d231b1SJohnson Wang regulator_disable(drv->proc_reg);
38986d231b1SJohnson Wang if (drv->sram_reg && regulator_is_enabled(drv->sram_reg))
39086d231b1SJohnson Wang regulator_disable(drv->sram_reg);
39186d231b1SJohnson Wang
39286d231b1SJohnson Wang return ret;
39386d231b1SJohnson Wang }
39486d231b1SJohnson Wang
mtk_ccifreq_remove(struct platform_device * pdev)395*0df02586SUwe Kleine-König static void mtk_ccifreq_remove(struct platform_device *pdev)
39686d231b1SJohnson Wang {
39786d231b1SJohnson Wang struct device *dev = &pdev->dev;
39886d231b1SJohnson Wang struct mtk_ccifreq_drv *drv;
39986d231b1SJohnson Wang
40086d231b1SJohnson Wang drv = platform_get_drvdata(pdev);
40186d231b1SJohnson Wang
40286d231b1SJohnson Wang dev_pm_opp_unregister_notifier(dev, &drv->opp_nb);
40386d231b1SJohnson Wang dev_pm_opp_of_remove_table(dev);
40486d231b1SJohnson Wang clk_disable_unprepare(drv->cci_clk);
40586d231b1SJohnson Wang regulator_disable(drv->proc_reg);
40686d231b1SJohnson Wang if (drv->sram_reg)
40786d231b1SJohnson Wang regulator_disable(drv->sram_reg);
40886d231b1SJohnson Wang }
40986d231b1SJohnson Wang
41086d231b1SJohnson Wang static const struct mtk_ccifreq_platform_data mt8183_platform_data = {
41186d231b1SJohnson Wang .min_volt_shift = 100000,
41286d231b1SJohnson Wang .max_volt_shift = 200000,
41386d231b1SJohnson Wang .proc_max_volt = 1150000,
41486d231b1SJohnson Wang };
41586d231b1SJohnson Wang
41686d231b1SJohnson Wang static const struct mtk_ccifreq_platform_data mt8186_platform_data = {
41786d231b1SJohnson Wang .min_volt_shift = 100000,
41886d231b1SJohnson Wang .max_volt_shift = 250000,
41986d231b1SJohnson Wang .proc_max_volt = 1118750,
42086d231b1SJohnson Wang .sram_min_volt = 850000,
42186d231b1SJohnson Wang .sram_max_volt = 1118750,
42286d231b1SJohnson Wang };
42386d231b1SJohnson Wang
42486d231b1SJohnson Wang static const struct of_device_id mtk_ccifreq_machines[] = {
42586d231b1SJohnson Wang { .compatible = "mediatek,mt8183-cci", .data = &mt8183_platform_data },
42686d231b1SJohnson Wang { .compatible = "mediatek,mt8186-cci", .data = &mt8186_platform_data },
42786d231b1SJohnson Wang { },
42886d231b1SJohnson Wang };
42986d231b1SJohnson Wang MODULE_DEVICE_TABLE(of, mtk_ccifreq_machines);
43086d231b1SJohnson Wang
43186d231b1SJohnson Wang static struct platform_driver mtk_ccifreq_platdrv = {
43286d231b1SJohnson Wang .probe = mtk_ccifreq_probe,
433*0df02586SUwe Kleine-König .remove_new = mtk_ccifreq_remove,
43486d231b1SJohnson Wang .driver = {
43586d231b1SJohnson Wang .name = "mtk-ccifreq",
43686d231b1SJohnson Wang .of_match_table = mtk_ccifreq_machines,
43786d231b1SJohnson Wang },
43886d231b1SJohnson Wang };
43986d231b1SJohnson Wang module_platform_driver(mtk_ccifreq_platdrv);
44086d231b1SJohnson Wang
44186d231b1SJohnson Wang MODULE_DESCRIPTION("MediaTek CCI devfreq driver");
44286d231b1SJohnson Wang MODULE_AUTHOR("Jia-Wei Chang <jia-wei.chang@mediatek.com>");
44386d231b1SJohnson Wang MODULE_LICENSE("GPL v2");
444