162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/init.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include "common.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "voltage.h"
862306a36Sopenharmony_ci#include "vp.h"
962306a36Sopenharmony_ci#include "prm-regbits-34xx.h"
1062306a36Sopenharmony_ci#include "prm-regbits-44xx.h"
1162306a36Sopenharmony_ci#include "prm44xx.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	struct omap_vp_instance *vp = voltdm->vp;
1662306a36Sopenharmony_ci	u32 vpconfig;
1762306a36Sopenharmony_ci	char vsel;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	vsel = voltdm->pmic->uv_to_vsel(volt);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	vpconfig = voltdm->read(vp->vpconfig);
2262306a36Sopenharmony_ci	vpconfig &= ~(vp->common->vpconfig_initvoltage_mask |
2362306a36Sopenharmony_ci		      vp->common->vpconfig_forceupdate |
2462306a36Sopenharmony_ci		      vp->common->vpconfig_initvdd);
2562306a36Sopenharmony_ci	vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask);
2662306a36Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	/* Trigger initVDD value copy to voltage processor */
2962306a36Sopenharmony_ci	voltdm->write((vpconfig | vp->common->vpconfig_initvdd),
3062306a36Sopenharmony_ci		       vp->vpconfig);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Clear initVDD copy trigger bit */
3362306a36Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return vpconfig;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Generic voltage init functions */
3962306a36Sopenharmony_civoid __init omap_vp_init(struct voltagedomain *voltdm)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct omap_vp_instance *vp = voltdm->vp;
4262306a36Sopenharmony_ci	u32 val, sys_clk_rate, timeout, waittime;
4362306a36Sopenharmony_ci	u32 vddmin, vddmax, vstepmin, vstepmax;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
4662306a36Sopenharmony_ci		pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name);
4762306a36Sopenharmony_ci		return;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (!voltdm->read || !voltdm->write) {
5162306a36Sopenharmony_ci		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
5262306a36Sopenharmony_ci			__func__, voltdm->name);
5362306a36Sopenharmony_ci		return;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	vp->enabled = false;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Divide to avoid overflow */
5962306a36Sopenharmony_ci	sys_clk_rate = voltdm->sys_clk.rate / 1000;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000;
6262306a36Sopenharmony_ci	vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin);
6362306a36Sopenharmony_ci	vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax);
6462306a36Sopenharmony_ci	vddmin = voltdm->pmic->uv_to_vsel(vddmin);
6562306a36Sopenharmony_ci	vddmax = voltdm->pmic->uv_to_vsel(vddmax);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate,
6862306a36Sopenharmony_ci				1000 * voltdm->pmic->slew_rate);
6962306a36Sopenharmony_ci	vstepmin = voltdm->pmic->vp_vstepmin;
7062306a36Sopenharmony_ci	vstepmax = voltdm->pmic->vp_vstepmax;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/*
7362306a36Sopenharmony_ci	 * VP_CONFIG: error gain is not set here, it will be updated
7462306a36Sopenharmony_ci	 * on each scale, based on OPP.
7562306a36Sopenharmony_ci	 */
7662306a36Sopenharmony_ci	val = (voltdm->pmic->vp_erroroffset <<
7762306a36Sopenharmony_ci	       __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) |
7862306a36Sopenharmony_ci		vp->common->vpconfig_timeouten;
7962306a36Sopenharmony_ci	voltdm->write(val, vp->vpconfig);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* VSTEPMIN */
8262306a36Sopenharmony_ci	val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) |
8362306a36Sopenharmony_ci		(vstepmin <<  vp->common->vstepmin_stepmin_shift);
8462306a36Sopenharmony_ci	voltdm->write(val, vp->vstepmin);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* VSTEPMAX */
8762306a36Sopenharmony_ci	val = (vstepmax << vp->common->vstepmax_stepmax_shift) |
8862306a36Sopenharmony_ci		(waittime << vp->common->vstepmax_smpswaittimemax_shift);
8962306a36Sopenharmony_ci	voltdm->write(val, vp->vstepmax);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* VLIMITTO */
9262306a36Sopenharmony_ci	val = (vddmax << vp->common->vlimitto_vddmax_shift) |
9362306a36Sopenharmony_ci		(vddmin << vp->common->vlimitto_vddmin_shift) |
9462306a36Sopenharmony_ci		(timeout <<  vp->common->vlimitto_timeout_shift);
9562306a36Sopenharmony_ci	voltdm->write(val, vp->vlimitto);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciint omap_vp_update_errorgain(struct voltagedomain *voltdm,
9962306a36Sopenharmony_ci			     unsigned long target_volt)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct omap_volt_data *volt_data;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (!voltdm->vp)
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Get volt_data corresponding to target_volt */
10762306a36Sopenharmony_ci	volt_data = omap_voltage_get_voltdata(voltdm, target_volt);
10862306a36Sopenharmony_ci	if (IS_ERR(volt_data))
10962306a36Sopenharmony_ci		return -EINVAL;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Setting vp errorgain based on the voltage */
11262306a36Sopenharmony_ci	voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask,
11362306a36Sopenharmony_ci		    volt_data->vp_errgain <<
11462306a36Sopenharmony_ci		    __ffs(voltdm->vp->common->vpconfig_errorgain_mask),
11562306a36Sopenharmony_ci		    voltdm->vp->vpconfig);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/* VP force update method of voltage scaling */
12162306a36Sopenharmony_ciint omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
12262306a36Sopenharmony_ci			      unsigned long target_volt)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct omap_vp_instance *vp = voltdm->vp;
12562306a36Sopenharmony_ci	u32 vpconfig;
12662306a36Sopenharmony_ci	u8 target_vsel, current_vsel;
12762306a36Sopenharmony_ci	int ret, timeout = 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
13062306a36Sopenharmony_ci	if (ret)
13162306a36Sopenharmony_ci		return ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * Clear all pending TransactionDone interrupt/status. Typical latency
13562306a36Sopenharmony_ci	 * is <3us
13662306a36Sopenharmony_ci	 */
13762306a36Sopenharmony_ci	while (timeout++ < VP_TRANXDONE_TIMEOUT) {
13862306a36Sopenharmony_ci		vp->common->ops->clear_txdone(vp->id);
13962306a36Sopenharmony_ci		if (!vp->common->ops->check_txdone(vp->id))
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci		udelay(1);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	if (timeout >= VP_TRANXDONE_TIMEOUT) {
14462306a36Sopenharmony_ci		pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n",
14562306a36Sopenharmony_ci			__func__, voltdm->name);
14662306a36Sopenharmony_ci		return -ETIMEDOUT;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	vpconfig = _vp_set_init_voltage(voltdm, target_volt);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Force update of voltage */
15262306a36Sopenharmony_ci	voltdm->write(vpconfig | vp->common->vpconfig_forceupdate,
15362306a36Sopenharmony_ci		      voltdm->vp->vpconfig);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/*
15662306a36Sopenharmony_ci	 * Wait for TransactionDone. Typical latency is <200us.
15762306a36Sopenharmony_ci	 * Depends on SMPSWAITTIMEMIN/MAX and voltage change
15862306a36Sopenharmony_ci	 */
15962306a36Sopenharmony_ci	timeout = 0;
16062306a36Sopenharmony_ci	omap_test_timeout(vp->common->ops->check_txdone(vp->id),
16162306a36Sopenharmony_ci			  VP_TRANXDONE_TIMEOUT, timeout);
16262306a36Sopenharmony_ci	if (timeout >= VP_TRANXDONE_TIMEOUT)
16362306a36Sopenharmony_ci		pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n",
16462306a36Sopenharmony_ci		       __func__, voltdm->name);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Disable TransactionDone interrupt , clear all status, clear
17062306a36Sopenharmony_ci	 * control registers
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	timeout = 0;
17362306a36Sopenharmony_ci	while (timeout++ < VP_TRANXDONE_TIMEOUT) {
17462306a36Sopenharmony_ci		vp->common->ops->clear_txdone(vp->id);
17562306a36Sopenharmony_ci		if (!vp->common->ops->check_txdone(vp->id))
17662306a36Sopenharmony_ci			break;
17762306a36Sopenharmony_ci		udelay(1);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (timeout >= VP_TRANXDONE_TIMEOUT)
18162306a36Sopenharmony_ci		pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n",
18262306a36Sopenharmony_ci			__func__, voltdm->name);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Clear force bit */
18562306a36Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/**
19162306a36Sopenharmony_ci * omap_vp_enable() - API to enable a particular VP
19262306a36Sopenharmony_ci * @voltdm:	pointer to the VDD whose VP is to be enabled.
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * This API enables a particular voltage processor. Needed by the smartreflex
19562306a36Sopenharmony_ci * class drivers.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_civoid omap_vp_enable(struct voltagedomain *voltdm)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct omap_vp_instance *vp;
20062306a36Sopenharmony_ci	u32 vpconfig, volt;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!voltdm || IS_ERR(voltdm)) {
20362306a36Sopenharmony_ci		pr_warn("%s: VDD specified does not exist!\n", __func__);
20462306a36Sopenharmony_ci		return;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	vp = voltdm->vp;
20862306a36Sopenharmony_ci	if (!voltdm->read || !voltdm->write) {
20962306a36Sopenharmony_ci		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
21062306a36Sopenharmony_ci			__func__, voltdm->name);
21162306a36Sopenharmony_ci		return;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* If VP is already enabled, do nothing. Return */
21562306a36Sopenharmony_ci	if (vp->enabled)
21662306a36Sopenharmony_ci		return;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	volt = voltdm_get_voltage(voltdm);
21962306a36Sopenharmony_ci	if (!volt) {
22062306a36Sopenharmony_ci		pr_warn("%s: unable to find current voltage for %s\n",
22162306a36Sopenharmony_ci			__func__, voltdm->name);
22262306a36Sopenharmony_ci		return;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	vpconfig = _vp_set_init_voltage(voltdm, volt);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Enable VP */
22862306a36Sopenharmony_ci	vpconfig |= vp->common->vpconfig_vpenable;
22962306a36Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	vp->enabled = true;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * omap_vp_disable() - API to disable a particular VP
23662306a36Sopenharmony_ci * @voltdm:	pointer to the VDD whose VP is to be disabled.
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * This API disables a particular voltage processor. Needed by the smartreflex
23962306a36Sopenharmony_ci * class drivers.
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_civoid omap_vp_disable(struct voltagedomain *voltdm)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct omap_vp_instance *vp;
24462306a36Sopenharmony_ci	u32 vpconfig;
24562306a36Sopenharmony_ci	int timeout;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (!voltdm || IS_ERR(voltdm)) {
24862306a36Sopenharmony_ci		pr_warn("%s: VDD specified does not exist!\n", __func__);
24962306a36Sopenharmony_ci		return;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	vp = voltdm->vp;
25362306a36Sopenharmony_ci	if (!voltdm->read || !voltdm->write) {
25462306a36Sopenharmony_ci		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
25562306a36Sopenharmony_ci			__func__, voltdm->name);
25662306a36Sopenharmony_ci		return;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* If VP is already disabled, do nothing. Return */
26062306a36Sopenharmony_ci	if (!vp->enabled) {
26162306a36Sopenharmony_ci		pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n",
26262306a36Sopenharmony_ci			__func__, voltdm->name);
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Disable VP */
26762306a36Sopenharmony_ci	vpconfig = voltdm->read(vp->vpconfig);
26862306a36Sopenharmony_ci	vpconfig &= ~vp->common->vpconfig_vpenable;
26962306a36Sopenharmony_ci	voltdm->write(vpconfig, vp->vpconfig);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/*
27262306a36Sopenharmony_ci	 * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
27362306a36Sopenharmony_ci	 */
27462306a36Sopenharmony_ci	omap_test_timeout((voltdm->read(vp->vstatus)),
27562306a36Sopenharmony_ci			  VP_IDLE_TIMEOUT, timeout);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (timeout >= VP_IDLE_TIMEOUT)
27862306a36Sopenharmony_ci		pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	vp->enabled = false;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return;
28362306a36Sopenharmony_ci}
284