brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · 7ad5225 Raw
444 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2022 MediaTek Inc.4 */5 6#include <linux/clk.h>7#include <linux/devfreq.h>8#include <linux/minmax.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/platform_device.h>12#include <linux/pm_opp.h>13#include <linux/regulator/consumer.h>14 15struct mtk_ccifreq_platform_data {16	int min_volt_shift;17	int max_volt_shift;18	int proc_max_volt;19	int sram_min_volt;20	int sram_max_volt;21};22 23struct mtk_ccifreq_drv {24	struct device *dev;25	struct devfreq *devfreq;26	struct regulator *proc_reg;27	struct regulator *sram_reg;28	struct clk *cci_clk;29	struct clk *inter_clk;30	int inter_voltage;31	unsigned long pre_freq;32	/* Avoid race condition for regulators between notify and policy */33	struct mutex reg_lock;34	struct notifier_block opp_nb;35	const struct mtk_ccifreq_platform_data *soc_data;36	int vtrack_max;37};38 39static int mtk_ccifreq_set_voltage(struct mtk_ccifreq_drv *drv, int new_voltage)40{41	const struct mtk_ccifreq_platform_data *soc_data = drv->soc_data;42	struct device *dev = drv->dev;43	int pre_voltage, pre_vsram, new_vsram, vsram, voltage, ret;44	int retry_max = drv->vtrack_max;45 46	if (!drv->sram_reg) {47		ret = regulator_set_voltage(drv->proc_reg, new_voltage,48					    drv->soc_data->proc_max_volt);49		return ret;50	}51 52	pre_voltage = regulator_get_voltage(drv->proc_reg);53	if (pre_voltage < 0) {54		dev_err(dev, "invalid vproc value: %d\n", pre_voltage);55		return pre_voltage;56	}57 58	pre_vsram = regulator_get_voltage(drv->sram_reg);59	if (pre_vsram < 0) {60		dev_err(dev, "invalid vsram value: %d\n", pre_vsram);61		return pre_vsram;62	}63 64	new_vsram = clamp(new_voltage + soc_data->min_volt_shift,65			  soc_data->sram_min_volt, soc_data->sram_max_volt);66 67	do {68		if (pre_voltage <= new_voltage) {69			vsram = clamp(pre_voltage + soc_data->max_volt_shift,70				      soc_data->sram_min_volt, new_vsram);71			ret = regulator_set_voltage(drv->sram_reg, vsram,72						    soc_data->sram_max_volt);73			if (ret)74				return ret;75 76			if (vsram == soc_data->sram_max_volt ||77			    new_vsram == soc_data->sram_min_volt)78				voltage = new_voltage;79			else80				voltage = vsram - soc_data->min_volt_shift;81 82			ret = regulator_set_voltage(drv->proc_reg, voltage,83						    soc_data->proc_max_volt);84			if (ret) {85				regulator_set_voltage(drv->sram_reg, pre_vsram,86						      soc_data->sram_max_volt);87				return ret;88			}89		} else if (pre_voltage > new_voltage) {90			voltage = max(new_voltage,91				      pre_vsram - soc_data->max_volt_shift);92			ret = regulator_set_voltage(drv->proc_reg, voltage,93						    soc_data->proc_max_volt);94			if (ret)95				return ret;96 97			if (voltage == new_voltage)98				vsram = new_vsram;99			else100				vsram = max(new_vsram,101					    voltage + soc_data->min_volt_shift);102 103			ret = regulator_set_voltage(drv->sram_reg, vsram,104						    soc_data->sram_max_volt);105			if (ret) {106				regulator_set_voltage(drv->proc_reg, pre_voltage,107						      soc_data->proc_max_volt);108				return ret;109			}110		}111 112		pre_voltage = voltage;113		pre_vsram = vsram;114 115		if (--retry_max < 0) {116			dev_err(dev,117				"over loop count, failed to set voltage\n");118			return -EINVAL;119		}120	} while (voltage != new_voltage || vsram != new_vsram);121 122	return 0;123}124 125static int mtk_ccifreq_target(struct device *dev, unsigned long *freq,126			      u32 flags)127{128	struct mtk_ccifreq_drv *drv = dev_get_drvdata(dev);129	struct clk *cci_pll;130	struct dev_pm_opp *opp;131	unsigned long opp_rate;132	int voltage, pre_voltage, inter_voltage, target_voltage, ret;133 134	if (!drv)135		return -EINVAL;136 137	if (drv->pre_freq == *freq)138		return 0;139 140	mutex_lock(&drv->reg_lock);141 142	inter_voltage = drv->inter_voltage;143	cci_pll = clk_get_parent(drv->cci_clk);144 145	opp_rate = *freq;146	opp = devfreq_recommended_opp(dev, &opp_rate, 1);147	if (IS_ERR(opp)) {148		dev_err(dev, "failed to find opp for freq: %ld\n", opp_rate);149		ret = PTR_ERR(opp);150		goto out_unlock;151	}152 153	voltage = dev_pm_opp_get_voltage(opp);154	dev_pm_opp_put(opp);155 156	pre_voltage = regulator_get_voltage(drv->proc_reg);157	if (pre_voltage < 0) {158		dev_err(dev, "invalid vproc value: %d\n", pre_voltage);159		ret = pre_voltage;160		goto out_unlock;161	}162 163	/* scale up: set voltage first then freq. */164	target_voltage = max(inter_voltage, voltage);165	if (pre_voltage <= target_voltage) {166		ret = mtk_ccifreq_set_voltage(drv, target_voltage);167		if (ret) {168			dev_err(dev, "failed to scale up voltage\n");169			goto out_restore_voltage;170		}171	}172 173	/* switch the cci clock to intermediate clock source. */174	ret = clk_set_parent(drv->cci_clk, drv->inter_clk);175	if (ret) {176		dev_err(dev, "failed to re-parent cci clock\n");177		goto out_restore_voltage;178	}179 180	/* set the original clock to target rate. */181	ret = clk_set_rate(cci_pll, *freq);182	if (ret) {183		dev_err(dev, "failed to set cci pll rate: %d\n", ret);184		clk_set_parent(drv->cci_clk, cci_pll);185		goto out_restore_voltage;186	}187 188	/* switch the cci clock back to the original clock source. */189	ret = clk_set_parent(drv->cci_clk, cci_pll);190	if (ret) {191		dev_err(dev, "failed to re-parent cci clock\n");192		mtk_ccifreq_set_voltage(drv, inter_voltage);193		goto out_unlock;194	}195 196	/*197	 * If the new voltage is lower than the intermediate voltage or the198	 * original voltage, scale down to the new voltage.199	 */200	if (voltage < inter_voltage || voltage < pre_voltage) {201		ret = mtk_ccifreq_set_voltage(drv, voltage);202		if (ret) {203			dev_err(dev, "failed to scale down voltage\n");204			goto out_unlock;205		}206	}207 208	drv->pre_freq = *freq;209	mutex_unlock(&drv->reg_lock);210 211	return 0;212 213out_restore_voltage:214	mtk_ccifreq_set_voltage(drv, pre_voltage);215 216out_unlock:217	mutex_unlock(&drv->reg_lock);218	return ret;219}220 221static int mtk_ccifreq_opp_notifier(struct notifier_block *nb,222				    unsigned long event, void *data)223{224	struct dev_pm_opp *opp = data;225	struct mtk_ccifreq_drv *drv;226	unsigned long freq, volt;227 228	drv = container_of(nb, struct mtk_ccifreq_drv, opp_nb);229 230	if (event == OPP_EVENT_ADJUST_VOLTAGE) {231		mutex_lock(&drv->reg_lock);232		freq = dev_pm_opp_get_freq(opp);233 234		/* current opp item is changed */235		if (freq == drv->pre_freq) {236			volt = dev_pm_opp_get_voltage(opp);237			mtk_ccifreq_set_voltage(drv, volt);238		}239		mutex_unlock(&drv->reg_lock);240	}241 242	return 0;243}244 245static struct devfreq_dev_profile mtk_ccifreq_profile = {246	.target = mtk_ccifreq_target,247};248 249static int mtk_ccifreq_probe(struct platform_device *pdev)250{251	struct device *dev = &pdev->dev;252	struct mtk_ccifreq_drv *drv;253	struct devfreq_passive_data *passive_data;254	struct dev_pm_opp *opp;255	unsigned long rate, opp_volt;256	int ret;257 258	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);259	if (!drv)260		return -ENOMEM;261 262	drv->dev = dev;263	drv->soc_data = (const struct mtk_ccifreq_platform_data *)264				of_device_get_match_data(&pdev->dev);265	mutex_init(&drv->reg_lock);266	platform_set_drvdata(pdev, drv);267 268	drv->cci_clk = devm_clk_get(dev, "cci");269	if (IS_ERR(drv->cci_clk)) {270		ret = PTR_ERR(drv->cci_clk);271		return dev_err_probe(dev, ret, "failed to get cci clk\n");272	}273 274	drv->inter_clk = devm_clk_get(dev, "intermediate");275	if (IS_ERR(drv->inter_clk)) {276		ret = PTR_ERR(drv->inter_clk);277		return dev_err_probe(dev, ret,278				     "failed to get intermediate clk\n");279	}280 281	drv->proc_reg = devm_regulator_get_optional(dev, "proc");282	if (IS_ERR(drv->proc_reg)) {283		ret = PTR_ERR(drv->proc_reg);284		return dev_err_probe(dev, ret,285				     "failed to get proc regulator\n");286	}287 288	ret = regulator_enable(drv->proc_reg);289	if (ret) {290		dev_err(dev, "failed to enable proc regulator\n");291		return ret;292	}293 294	drv->sram_reg = devm_regulator_get_optional(dev, "sram");295	if (IS_ERR(drv->sram_reg)) {296		ret = PTR_ERR(drv->sram_reg);297		if (ret == -EPROBE_DEFER)298			goto out_free_resources;299 300		drv->sram_reg = NULL;301	} else {302		ret = regulator_enable(drv->sram_reg);303		if (ret) {304			dev_err(dev, "failed to enable sram regulator\n");305			goto out_free_resources;306		}307	}308 309	/*310	 * We assume min voltage is 0 and tracking target voltage using311	 * min_volt_shift for each iteration.312	 * The retry_max is 3 times of expected iteration count.313	 */314	drv->vtrack_max = 3 * DIV_ROUND_UP(max(drv->soc_data->sram_max_volt,315					       drv->soc_data->proc_max_volt),316					   drv->soc_data->min_volt_shift);317 318	ret = clk_prepare_enable(drv->cci_clk);319	if (ret)320		goto out_free_resources;321 322	ret = dev_pm_opp_of_add_table(dev);323	if (ret) {324		dev_err(dev, "failed to add opp table: %d\n", ret);325		goto out_disable_cci_clk;326	}327 328	rate = clk_get_rate(drv->inter_clk);329	opp = dev_pm_opp_find_freq_ceil(dev, &rate);330	if (IS_ERR(opp)) {331		ret = PTR_ERR(opp);332		dev_err(dev, "failed to get intermediate opp: %d\n", ret);333		goto out_remove_opp_table;334	}335	drv->inter_voltage = dev_pm_opp_get_voltage(opp);336	dev_pm_opp_put(opp);337 338	rate = U32_MAX;339	opp = dev_pm_opp_find_freq_floor(drv->dev, &rate);340	if (IS_ERR(opp)) {341		dev_err(dev, "failed to get opp\n");342		ret = PTR_ERR(opp);343		goto out_remove_opp_table;344	}345 346	opp_volt = dev_pm_opp_get_voltage(opp);347	dev_pm_opp_put(opp);348	ret = mtk_ccifreq_set_voltage(drv, opp_volt);349	if (ret) {350		dev_err(dev, "failed to scale to highest voltage %lu in proc_reg\n",351			opp_volt);352		goto out_remove_opp_table;353	}354 355	passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);356	if (!passive_data) {357		ret = -ENOMEM;358		goto out_remove_opp_table;359	}360 361	passive_data->parent_type = CPUFREQ_PARENT_DEV;362	drv->devfreq = devm_devfreq_add_device(dev, &mtk_ccifreq_profile,363					       DEVFREQ_GOV_PASSIVE,364					       passive_data);365	if (IS_ERR(drv->devfreq)) {366		ret = -EPROBE_DEFER;367		dev_err(dev, "failed to add devfreq device: %ld\n",368			PTR_ERR(drv->devfreq));369		goto out_remove_opp_table;370	}371 372	drv->opp_nb.notifier_call = mtk_ccifreq_opp_notifier;373	ret = dev_pm_opp_register_notifier(dev, &drv->opp_nb);374	if (ret) {375		dev_err(dev, "failed to register opp notifier: %d\n", ret);376		goto out_remove_opp_table;377	}378	return 0;379 380out_remove_opp_table:381	dev_pm_opp_of_remove_table(dev);382 383out_disable_cci_clk:384	clk_disable_unprepare(drv->cci_clk);385 386out_free_resources:387	if (regulator_is_enabled(drv->proc_reg))388		regulator_disable(drv->proc_reg);389	if (drv->sram_reg && regulator_is_enabled(drv->sram_reg))390		regulator_disable(drv->sram_reg);391 392	return ret;393}394 395static void mtk_ccifreq_remove(struct platform_device *pdev)396{397	struct device *dev = &pdev->dev;398	struct mtk_ccifreq_drv *drv;399 400	drv = platform_get_drvdata(pdev);401 402	dev_pm_opp_unregister_notifier(dev, &drv->opp_nb);403	dev_pm_opp_of_remove_table(dev);404	clk_disable_unprepare(drv->cci_clk);405	regulator_disable(drv->proc_reg);406	if (drv->sram_reg)407		regulator_disable(drv->sram_reg);408}409 410static const struct mtk_ccifreq_platform_data mt8183_platform_data = {411	.min_volt_shift = 100000,412	.max_volt_shift = 200000,413	.proc_max_volt = 1150000,414};415 416static const struct mtk_ccifreq_platform_data mt8186_platform_data = {417	.min_volt_shift = 100000,418	.max_volt_shift = 250000,419	.proc_max_volt = 1118750,420	.sram_min_volt = 850000,421	.sram_max_volt = 1118750,422};423 424static const struct of_device_id mtk_ccifreq_machines[] = {425	{ .compatible = "mediatek,mt8183-cci", .data = &mt8183_platform_data },426	{ .compatible = "mediatek,mt8186-cci", .data = &mt8186_platform_data },427	{ },428};429MODULE_DEVICE_TABLE(of, mtk_ccifreq_machines);430 431static struct platform_driver mtk_ccifreq_platdrv = {432	.probe	= mtk_ccifreq_probe,433	.remove_new = mtk_ccifreq_remove,434	.driver = {435		.name = "mtk-ccifreq",436		.of_match_table = mtk_ccifreq_machines,437	},438};439module_platform_driver(mtk_ccifreq_platdrv);440 441MODULE_DESCRIPTION("MediaTek CCI devfreq driver");442MODULE_AUTHOR("Jia-Wei Chang <jia-wei.chang@mediatek.com>");443MODULE_LICENSE("GPL v2");444