xref: /linux/drivers/devfreq/mtk-cci-devfreq.c (revision e09bd5757b5227d6804b30c58d4587f7f87d1afa)
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/of_device.h>
1286d231b1SJohnson Wang #include <linux/platform_device.h>
1386d231b1SJohnson Wang #include <linux/pm_opp.h>
1486d231b1SJohnson Wang #include <linux/regulator/consumer.h>
1586d231b1SJohnson Wang 
1686d231b1SJohnson Wang struct mtk_ccifreq_platform_data {
1786d231b1SJohnson Wang 	int min_volt_shift;
1886d231b1SJohnson Wang 	int max_volt_shift;
1986d231b1SJohnson Wang 	int proc_max_volt;
2086d231b1SJohnson Wang 	int sram_min_volt;
2186d231b1SJohnson Wang 	int sram_max_volt;
2286d231b1SJohnson Wang };
2386d231b1SJohnson Wang 
2486d231b1SJohnson Wang struct mtk_ccifreq_drv {
2586d231b1SJohnson Wang 	struct device *dev;
2686d231b1SJohnson Wang 	struct devfreq *devfreq;
2786d231b1SJohnson Wang 	struct regulator *proc_reg;
2886d231b1SJohnson Wang 	struct regulator *sram_reg;
2986d231b1SJohnson Wang 	struct clk *cci_clk;
3086d231b1SJohnson Wang 	struct clk *inter_clk;
3186d231b1SJohnson Wang 	int inter_voltage;
3286d231b1SJohnson Wang 	unsigned long pre_freq;
3386d231b1SJohnson Wang 	/* Avoid race condition for regulators between notify and policy */
3486d231b1SJohnson Wang 	struct mutex reg_lock;
3586d231b1SJohnson Wang 	struct notifier_block opp_nb;
3686d231b1SJohnson Wang 	const struct mtk_ccifreq_platform_data *soc_data;
3786d231b1SJohnson Wang 	int vtrack_max;
3886d231b1SJohnson Wang };
3986d231b1SJohnson Wang 
4086d231b1SJohnson Wang static int mtk_ccifreq_set_voltage(struct mtk_ccifreq_drv *drv, int new_voltage)
4186d231b1SJohnson Wang {
4286d231b1SJohnson Wang 	const struct mtk_ccifreq_platform_data *soc_data = drv->soc_data;
4386d231b1SJohnson Wang 	struct device *dev = drv->dev;
4486d231b1SJohnson Wang 	int pre_voltage, pre_vsram, new_vsram, vsram, voltage, ret;
4586d231b1SJohnson Wang 	int retry_max = drv->vtrack_max;
4686d231b1SJohnson Wang 
4786d231b1SJohnson Wang 	if (!drv->sram_reg) {
4886d231b1SJohnson Wang 		ret = regulator_set_voltage(drv->proc_reg, new_voltage,
4986d231b1SJohnson Wang 					    drv->soc_data->proc_max_volt);
5086d231b1SJohnson Wang 		return ret;
5186d231b1SJohnson Wang 	}
5286d231b1SJohnson Wang 
5386d231b1SJohnson Wang 	pre_voltage = regulator_get_voltage(drv->proc_reg);
5486d231b1SJohnson Wang 	if (pre_voltage < 0) {
5586d231b1SJohnson Wang 		dev_err(dev, "invalid vproc value: %d\n", pre_voltage);
5686d231b1SJohnson Wang 		return pre_voltage;
5786d231b1SJohnson Wang 	}
5886d231b1SJohnson Wang 
5986d231b1SJohnson Wang 	pre_vsram = regulator_get_voltage(drv->sram_reg);
6086d231b1SJohnson Wang 	if (pre_vsram < 0) {
6186d231b1SJohnson Wang 		dev_err(dev, "invalid vsram value: %d\n", pre_vsram);
6286d231b1SJohnson Wang 		return pre_vsram;
6386d231b1SJohnson Wang 	}
6486d231b1SJohnson Wang 
6586d231b1SJohnson Wang 	new_vsram = clamp(new_voltage + soc_data->min_volt_shift,
6686d231b1SJohnson Wang 			  soc_data->sram_min_volt, soc_data->sram_max_volt);
6786d231b1SJohnson Wang 
6886d231b1SJohnson Wang 	do {
6986d231b1SJohnson Wang 		if (pre_voltage <= new_voltage) {
7086d231b1SJohnson Wang 			vsram = clamp(pre_voltage + soc_data->max_volt_shift,
7186d231b1SJohnson Wang 				      soc_data->sram_min_volt, new_vsram);
7286d231b1SJohnson Wang 			ret = regulator_set_voltage(drv->sram_reg, vsram,
7386d231b1SJohnson Wang 						    soc_data->sram_max_volt);
7486d231b1SJohnson Wang 			if (ret)
7586d231b1SJohnson Wang 				return ret;
7686d231b1SJohnson Wang 
7786d231b1SJohnson Wang 			if (vsram == soc_data->sram_max_volt ||
7886d231b1SJohnson Wang 			    new_vsram == soc_data->sram_min_volt)
7986d231b1SJohnson Wang 				voltage = new_voltage;
8086d231b1SJohnson Wang 			else
8186d231b1SJohnson Wang 				voltage = vsram - soc_data->min_volt_shift;
8286d231b1SJohnson Wang 
8386d231b1SJohnson Wang 			ret = regulator_set_voltage(drv->proc_reg, voltage,
8486d231b1SJohnson Wang 						    soc_data->proc_max_volt);
8586d231b1SJohnson Wang 			if (ret) {
8686d231b1SJohnson Wang 				regulator_set_voltage(drv->sram_reg, pre_vsram,
8786d231b1SJohnson Wang 						      soc_data->sram_max_volt);
8886d231b1SJohnson Wang 				return ret;
8986d231b1SJohnson Wang 			}
9086d231b1SJohnson Wang 		} else if (pre_voltage > new_voltage) {
9186d231b1SJohnson Wang 			voltage = max(new_voltage,
9286d231b1SJohnson Wang 				      pre_vsram - soc_data->max_volt_shift);
9386d231b1SJohnson Wang 			ret = regulator_set_voltage(drv->proc_reg, voltage,
9486d231b1SJohnson Wang 						    soc_data->proc_max_volt);
9586d231b1SJohnson Wang 			if (ret)
9686d231b1SJohnson Wang 				return ret;
9786d231b1SJohnson Wang 
9886d231b1SJohnson Wang 			if (voltage == new_voltage)
9986d231b1SJohnson Wang 				vsram = new_vsram;
10086d231b1SJohnson Wang 			else
10186d231b1SJohnson Wang 				vsram = max(new_vsram,
10286d231b1SJohnson Wang 					    voltage + soc_data->min_volt_shift);
10386d231b1SJohnson Wang 
10486d231b1SJohnson Wang 			ret = regulator_set_voltage(drv->sram_reg, vsram,
10586d231b1SJohnson Wang 						    soc_data->sram_max_volt);
10686d231b1SJohnson Wang 			if (ret) {
10786d231b1SJohnson Wang 				regulator_set_voltage(drv->proc_reg, pre_voltage,
10886d231b1SJohnson Wang 						      soc_data->proc_max_volt);
10986d231b1SJohnson Wang 				return ret;
11086d231b1SJohnson Wang 			}
11186d231b1SJohnson Wang 		}
11286d231b1SJohnson Wang 
11386d231b1SJohnson Wang 		pre_voltage = voltage;
11486d231b1SJohnson Wang 		pre_vsram = vsram;
11586d231b1SJohnson Wang 
11686d231b1SJohnson Wang 		if (--retry_max < 0) {
11786d231b1SJohnson Wang 			dev_err(dev,
11886d231b1SJohnson Wang 				"over loop count, failed to set voltage\n");
11986d231b1SJohnson Wang 			return -EINVAL;
12086d231b1SJohnson Wang 		}
12186d231b1SJohnson Wang 	} while (voltage != new_voltage || vsram != new_vsram);
12286d231b1SJohnson Wang 
12386d231b1SJohnson Wang 	return 0;
12486d231b1SJohnson Wang }
12586d231b1SJohnson Wang 
12686d231b1SJohnson Wang static int mtk_ccifreq_target(struct device *dev, unsigned long *freq,
12786d231b1SJohnson Wang 			      u32 flags)
12886d231b1SJohnson Wang {
12986d231b1SJohnson Wang 	struct mtk_ccifreq_drv *drv = dev_get_drvdata(dev);
13086d231b1SJohnson Wang 	struct clk *cci_pll = clk_get_parent(drv->cci_clk);
13186d231b1SJohnson Wang 	struct dev_pm_opp *opp;
13286d231b1SJohnson Wang 	unsigned long opp_rate;
13386d231b1SJohnson Wang 	int voltage, pre_voltage, inter_voltage, target_voltage, ret;
13486d231b1SJohnson Wang 
13586d231b1SJohnson Wang 	if (!drv)
13686d231b1SJohnson Wang 		return -EINVAL;
13786d231b1SJohnson Wang 
13886d231b1SJohnson Wang 	if (drv->pre_freq == *freq)
13986d231b1SJohnson Wang 		return 0;
14086d231b1SJohnson Wang 
14186d231b1SJohnson Wang 	inter_voltage = drv->inter_voltage;
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 
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 
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");
294*e09bd575SAngeloGioacchino Del Regno 	if (IS_ERR(drv->sram_reg)) {
295*e09bd575SAngeloGioacchino Del Regno 		ret = PTR_ERR(drv->sram_reg);
296*e09bd575SAngeloGioacchino Del Regno 		if (ret == -EPROBE_DEFER)
297*e09bd575SAngeloGioacchino Del Regno 			goto out_free_resources;
298*e09bd575SAngeloGioacchino Del Regno 
29986d231b1SJohnson Wang 		drv->sram_reg = NULL;
300*e09bd575SAngeloGioacchino 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 
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