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