xref: /linux/drivers/devfreq/mtk-cci-devfreq.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
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