18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/init.h>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include "common.h"
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "voltage.h"
88c2ecf20Sopenharmony_ci#include "vp.h"
98c2ecf20Sopenharmony_ci#include "prm-regbits-34xx.h"
108c2ecf20Sopenharmony_ci#include "prm-regbits-44xx.h"
118c2ecf20Sopenharmony_ci#include "prm44xx.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct omap_vp_instance *vp = voltdm->vp;
168c2ecf20Sopenharmony_ci	u32 vpconfig;
178c2ecf20Sopenharmony_ci	char vsel;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	vsel = voltdm->pmic->uv_to_vsel(volt);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	vpconfig = voltdm->read(vp->vpconfig);
228c2ecf20Sopenharmony_ci	vpconfig &= ~(vp->common->vpconfig_initvoltage_mask |
238c2ecf20Sopenharmony_ci		      vp->common->vpconfig_forceupdate |
248c2ecf20Sopenharmony_ci		      vp->common->vpconfig_initvdd);
258c2ecf20Sopenharmony_ci	vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask);
268c2ecf20Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	/* Trigger initVDD value copy to voltage processor */
298c2ecf20Sopenharmony_ci	voltdm->write((vpconfig | vp->common->vpconfig_initvdd),
308c2ecf20Sopenharmony_ci		       vp->vpconfig);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	/* Clear initVDD copy trigger bit */
338c2ecf20Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return vpconfig;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Generic voltage init functions */
398c2ecf20Sopenharmony_civoid __init omap_vp_init(struct voltagedomain *voltdm)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct omap_vp_instance *vp = voltdm->vp;
428c2ecf20Sopenharmony_ci	u32 val, sys_clk_rate, timeout, waittime;
438c2ecf20Sopenharmony_ci	u32 vddmin, vddmax, vstepmin, vstepmax;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
468c2ecf20Sopenharmony_ci		pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name);
478c2ecf20Sopenharmony_ci		return;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (!voltdm->read || !voltdm->write) {
518c2ecf20Sopenharmony_ci		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
528c2ecf20Sopenharmony_ci			__func__, voltdm->name);
538c2ecf20Sopenharmony_ci		return;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	vp->enabled = false;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* Divide to avoid overflow */
598c2ecf20Sopenharmony_ci	sys_clk_rate = voltdm->sys_clk.rate / 1000;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000;
628c2ecf20Sopenharmony_ci	vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin);
638c2ecf20Sopenharmony_ci	vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax);
648c2ecf20Sopenharmony_ci	vddmin = voltdm->pmic->uv_to_vsel(vddmin);
658c2ecf20Sopenharmony_ci	vddmax = voltdm->pmic->uv_to_vsel(vddmax);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate,
688c2ecf20Sopenharmony_ci				1000 * voltdm->pmic->slew_rate);
698c2ecf20Sopenharmony_ci	vstepmin = voltdm->pmic->vp_vstepmin;
708c2ecf20Sopenharmony_ci	vstepmax = voltdm->pmic->vp_vstepmax;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/*
738c2ecf20Sopenharmony_ci	 * VP_CONFIG: error gain is not set here, it will be updated
748c2ecf20Sopenharmony_ci	 * on each scale, based on OPP.
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	val = (voltdm->pmic->vp_erroroffset <<
778c2ecf20Sopenharmony_ci	       __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) |
788c2ecf20Sopenharmony_ci		vp->common->vpconfig_timeouten;
798c2ecf20Sopenharmony_ci	voltdm->write(val, vp->vpconfig);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* VSTEPMIN */
828c2ecf20Sopenharmony_ci	val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) |
838c2ecf20Sopenharmony_ci		(vstepmin <<  vp->common->vstepmin_stepmin_shift);
848c2ecf20Sopenharmony_ci	voltdm->write(val, vp->vstepmin);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* VSTEPMAX */
878c2ecf20Sopenharmony_ci	val = (vstepmax << vp->common->vstepmax_stepmax_shift) |
888c2ecf20Sopenharmony_ci		(waittime << vp->common->vstepmax_smpswaittimemax_shift);
898c2ecf20Sopenharmony_ci	voltdm->write(val, vp->vstepmax);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* VLIMITTO */
928c2ecf20Sopenharmony_ci	val = (vddmax << vp->common->vlimitto_vddmax_shift) |
938c2ecf20Sopenharmony_ci		(vddmin << vp->common->vlimitto_vddmin_shift) |
948c2ecf20Sopenharmony_ci		(timeout <<  vp->common->vlimitto_timeout_shift);
958c2ecf20Sopenharmony_ci	voltdm->write(val, vp->vlimitto);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciint omap_vp_update_errorgain(struct voltagedomain *voltdm,
998c2ecf20Sopenharmony_ci			     unsigned long target_volt)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct omap_volt_data *volt_data;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!voltdm->vp)
1048c2ecf20Sopenharmony_ci		return -EINVAL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Get volt_data corresponding to target_volt */
1078c2ecf20Sopenharmony_ci	volt_data = omap_voltage_get_voltdata(voltdm, target_volt);
1088c2ecf20Sopenharmony_ci	if (IS_ERR(volt_data))
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Setting vp errorgain based on the voltage */
1128c2ecf20Sopenharmony_ci	voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask,
1138c2ecf20Sopenharmony_ci		    volt_data->vp_errgain <<
1148c2ecf20Sopenharmony_ci		    __ffs(voltdm->vp->common->vpconfig_errorgain_mask),
1158c2ecf20Sopenharmony_ci		    voltdm->vp->vpconfig);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* VP force update method of voltage scaling */
1218c2ecf20Sopenharmony_ciint omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
1228c2ecf20Sopenharmony_ci			      unsigned long target_volt)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct omap_vp_instance *vp = voltdm->vp;
1258c2ecf20Sopenharmony_ci	u32 vpconfig;
1268c2ecf20Sopenharmony_ci	u8 target_vsel, current_vsel;
1278c2ecf20Sopenharmony_ci	int ret, timeout = 0;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
1308c2ecf20Sopenharmony_ci	if (ret)
1318c2ecf20Sopenharmony_ci		return ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/*
1348c2ecf20Sopenharmony_ci	 * Clear all pending TransactionDone interrupt/status. Typical latency
1358c2ecf20Sopenharmony_ci	 * is <3us
1368c2ecf20Sopenharmony_ci	 */
1378c2ecf20Sopenharmony_ci	while (timeout++ < VP_TRANXDONE_TIMEOUT) {
1388c2ecf20Sopenharmony_ci		vp->common->ops->clear_txdone(vp->id);
1398c2ecf20Sopenharmony_ci		if (!vp->common->ops->check_txdone(vp->id))
1408c2ecf20Sopenharmony_ci			break;
1418c2ecf20Sopenharmony_ci		udelay(1);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	if (timeout >= VP_TRANXDONE_TIMEOUT) {
1448c2ecf20Sopenharmony_ci		pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n",
1458c2ecf20Sopenharmony_ci			__func__, voltdm->name);
1468c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	vpconfig = _vp_set_init_voltage(voltdm, target_volt);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* Force update of voltage */
1528c2ecf20Sopenharmony_ci	voltdm->write(vpconfig | vp->common->vpconfig_forceupdate,
1538c2ecf20Sopenharmony_ci		      voltdm->vp->vpconfig);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/*
1568c2ecf20Sopenharmony_ci	 * Wait for TransactionDone. Typical latency is <200us.
1578c2ecf20Sopenharmony_ci	 * Depends on SMPSWAITTIMEMIN/MAX and voltage change
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	timeout = 0;
1608c2ecf20Sopenharmony_ci	omap_test_timeout(vp->common->ops->check_txdone(vp->id),
1618c2ecf20Sopenharmony_ci			  VP_TRANXDONE_TIMEOUT, timeout);
1628c2ecf20Sopenharmony_ci	if (timeout >= VP_TRANXDONE_TIMEOUT)
1638c2ecf20Sopenharmony_ci		pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n",
1648c2ecf20Sopenharmony_ci		       __func__, voltdm->name);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/*
1698c2ecf20Sopenharmony_ci	 * Disable TransactionDone interrupt , clear all status, clear
1708c2ecf20Sopenharmony_ci	 * control registers
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	timeout = 0;
1738c2ecf20Sopenharmony_ci	while (timeout++ < VP_TRANXDONE_TIMEOUT) {
1748c2ecf20Sopenharmony_ci		vp->common->ops->clear_txdone(vp->id);
1758c2ecf20Sopenharmony_ci		if (!vp->common->ops->check_txdone(vp->id))
1768c2ecf20Sopenharmony_ci			break;
1778c2ecf20Sopenharmony_ci		udelay(1);
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (timeout >= VP_TRANXDONE_TIMEOUT)
1818c2ecf20Sopenharmony_ci		pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n",
1828c2ecf20Sopenharmony_ci			__func__, voltdm->name);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Clear force bit */
1858c2ecf20Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/**
1918c2ecf20Sopenharmony_ci * omap_vp_enable() - API to enable a particular VP
1928c2ecf20Sopenharmony_ci * @voltdm:	pointer to the VDD whose VP is to be enabled.
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci * This API enables a particular voltage processor. Needed by the smartreflex
1958c2ecf20Sopenharmony_ci * class drivers.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_civoid omap_vp_enable(struct voltagedomain *voltdm)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct omap_vp_instance *vp;
2008c2ecf20Sopenharmony_ci	u32 vpconfig, volt;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (!voltdm || IS_ERR(voltdm)) {
2038c2ecf20Sopenharmony_ci		pr_warn("%s: VDD specified does not exist!\n", __func__);
2048c2ecf20Sopenharmony_ci		return;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	vp = voltdm->vp;
2088c2ecf20Sopenharmony_ci	if (!voltdm->read || !voltdm->write) {
2098c2ecf20Sopenharmony_ci		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
2108c2ecf20Sopenharmony_ci			__func__, voltdm->name);
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* If VP is already enabled, do nothing. Return */
2158c2ecf20Sopenharmony_ci	if (vp->enabled)
2168c2ecf20Sopenharmony_ci		return;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	volt = voltdm_get_voltage(voltdm);
2198c2ecf20Sopenharmony_ci	if (!volt) {
2208c2ecf20Sopenharmony_ci		pr_warn("%s: unable to find current voltage for %s\n",
2218c2ecf20Sopenharmony_ci			__func__, voltdm->name);
2228c2ecf20Sopenharmony_ci		return;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	vpconfig = _vp_set_init_voltage(voltdm, volt);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* Enable VP */
2288c2ecf20Sopenharmony_ci	vpconfig |= vp->common->vpconfig_vpenable;
2298c2ecf20Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	vp->enabled = true;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci/**
2358c2ecf20Sopenharmony_ci * omap_vp_disable() - API to disable a particular VP
2368c2ecf20Sopenharmony_ci * @voltdm:	pointer to the VDD whose VP is to be disabled.
2378c2ecf20Sopenharmony_ci *
2388c2ecf20Sopenharmony_ci * This API disables a particular voltage processor. Needed by the smartreflex
2398c2ecf20Sopenharmony_ci * class drivers.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_civoid omap_vp_disable(struct voltagedomain *voltdm)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct omap_vp_instance *vp;
2448c2ecf20Sopenharmony_ci	u32 vpconfig;
2458c2ecf20Sopenharmony_ci	int timeout;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (!voltdm || IS_ERR(voltdm)) {
2488c2ecf20Sopenharmony_ci		pr_warn("%s: VDD specified does not exist!\n", __func__);
2498c2ecf20Sopenharmony_ci		return;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	vp = voltdm->vp;
2538c2ecf20Sopenharmony_ci	if (!voltdm->read || !voltdm->write) {
2548c2ecf20Sopenharmony_ci		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
2558c2ecf20Sopenharmony_ci			__func__, voltdm->name);
2568c2ecf20Sopenharmony_ci		return;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* If VP is already disabled, do nothing. Return */
2608c2ecf20Sopenharmony_ci	if (!vp->enabled) {
2618c2ecf20Sopenharmony_ci		pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n",
2628c2ecf20Sopenharmony_ci			__func__, voltdm->name);
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Disable VP */
2678c2ecf20Sopenharmony_ci	vpconfig = voltdm->read(vp->vpconfig);
2688c2ecf20Sopenharmony_ci	vpconfig &= ~vp->common->vpconfig_vpenable;
2698c2ecf20Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/*
2728c2ecf20Sopenharmony_ci	 * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	omap_test_timeout((voltdm->read(vp->vstatus)),
2758c2ecf20Sopenharmony_ci			  VP_IDLE_TIMEOUT, timeout);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (timeout >= VP_IDLE_TIMEOUT)
2788c2ecf20Sopenharmony_ci		pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	vp->enabled = false;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return;
2838c2ecf20Sopenharmony_ci}
284