[PATCH v16 3/7] soc: mediatek: SVS: introduce MTK SVS engine

Roger Lu roger.lu at mediatek.com
Fri Dec 24 01:27:21 PST 2021


Hi AngeloGioacchino,

Sorry for the late reply and thanks for all the advices.

On Thu, 2021-10-21 at 10:46 +0200, AngeloGioacchino Del Regno wrote:
> Il 28/04/21 08:54, Roger Lu ha scritto:
> > The Smart Voltage Scaling(SVS) engine is a piece of hardware
> > which calculates suitable SVS bank voltages to OPP voltage table.
> > Then, DVFS driver could apply those SVS bank voltages to PMIC/Buck
> > when receiving OPP_EVENT_ADJUST_VOLTAGE.
> > 
> > Signed-off-by: Roger Lu <roger.lu at mediatek.com>
> > ---
> >   drivers/soc/mediatek/Kconfig   |   10 +
> >   drivers/soc/mediatek/Makefile  |    1 +
> >   drivers/soc/mediatek/mtk-svs.c | 1723 ++++++++++++++++++++++++++++++++
> >   3 files changed, 1734 insertions(+)
> >   create mode 100644 drivers/soc/mediatek/mtk-svs.c

[snip]

> > +/* svs bank common setting */
> > +#define SVSB_DET_CLK_EN			BIT(31)
> > +#define SVSB_TZONE_HIGH_TEMP_MAX	U32_MAX
> > +#define SVSB_RUNCONFIG_DEFAULT		0x80000000
> > +#define SVSB_DC_SIGNED_BIT		0x8000
> > +#define SVSB_INTEN_INIT0x		0x00005f01
> > +#define SVSB_INTEN_MONVOPEN		0x00ff0000
> > +#define SVSB_EN_OFF			0x0
> > +#define SVSB_EN_MASK			0x7
> > +#define SVSB_EN_INIT01			0x1
> > +#define SVSB_EN_INIT02			0x5
> > +#define SVSB_EN_MON			0x2
> > +#define SVSB_INTSTS_MONVOP		0x00ff0000
> > +#define SVSB_INTSTS_COMPLETE		0x1
> > +#define SVSB_INTSTS_CLEAN		0x00ffffff
> > +
> > +static DEFINE_SPINLOCK(mtk_svs_lock);
> > +
> > +/*
> 
> Thanks for using kernel-doc!! However, to be proper, this has to be
> /**
>   * ..........

Oh okay. Thanks. I will use /** to add multi-line comments. However, I checked
kernel doc-guide and it uses indent as below. So, I'll follow it. If I'm doing
it wrong, please correct me. Thanks a lot.
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html

/**
 * .........

> 
> 
> > + * enum svsb_phase - svs bank phase enumeration
> > + * @SVSB_PHASE_INIT01: basic init for svs bank
> > + * @SVSB_PHASE_INIT02: svs bank can provide voltages
> > + * @SVSB_PHASE_MON: svs bank can provide voltages with thermal effect
> > + * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition
> 
> Please move @SVSB_PHASE_ERROR before @SVSB_PHASE_INIT01: the order
> is important here, and has to be the same as the actual enumeration.

Sure, I'll improve it in the next version. Thanks.

> 
> > + *
> > + * Each svs bank has its own independent phase. We enable each svs bank by
> > + * running their phase orderly. However, When svs bank encounters
> > unexpected
> > + * condition, it will fire an irq (PHASE_ERROR) to inform svs software.
> > + *
> > + * svs bank general phase-enabled order:
> > + * SVSB_PHASE_INIT01 -> SVSB_PHASE_INIT02 -> SVSB_PHASE_MON
> > + */
> > +enum svsb_phase {
> > +	SVSB_PHASE_ERROR = 0,
> > +	SVSB_PHASE_INIT01,
> > +	SVSB_PHASE_INIT02,
> > +	SVSB_PHASE_MON,
> > +};
> > 
> > 

[snip]

> > +/*
> 
> Same here, use /**

Sure, I'll improve it in the next version. Thanks.

> 
> > + * struct svs_platform - svs platform data
> > + * @dev: svs platform device
> > + * @base: svs platform register address base
> > + * @main_clk: main clock for svs bank
> > + * @pbank: phandle of svs bank and needs to be protected by spin_lock
> > + * @banks: phandle of the banks that support
> > + * @efuse_parsing: phandle of efuse parsing function
> > + * @irqflags: irq settings flags
> > + * @rst: svs reset control
> > + * @regs: phandle to the registers map
> > + * @efuse_num: the total number of svs platform efuse
> > + * @tefuse_num: the total number of thermal efuse
> > + * @bank_num: the total number of banks
> > + * @efuse_check: the svs efuse check index
> > + * @efuse: svs platform efuse data received from NVMEM framework
> > + * @tefuse: thermal efuse data received from NVMEM framework
> > + * @name: svs platform name
> > + */
> > +struct svs_platform {
> > +	struct device *dev;
> > +	void __iomem *base;
> > +	struct clk *main_clk;
> > +	struct svs_bank *pbank;
> > +	struct svs_bank *banks;
> > +	bool (*efuse_parsing)(struct svs_platform *svsp);
> > +	unsigned long irqflags;
> > +	struct reset_control *rst;
> > +	const u32 *regs;
> > +	char *name;
> > +	size_t efuse_num;
> > +	size_t tefuse_num;
> > +	u32 bank_num;
> > +	u32 efuse_check;
> > +	u32 *efuse;
> > +	u32 *tefuse;
> > +};
> > +
> > +/*
> 
> ditto.

Sure, I'll improve it in the next version. Thanks.

> 
> > + * struct svs_bank - svs bank representation
> > + * @dev: svs bank device
> > + * @opp_dev: device for opp table/buck control
> > + * @pd_dev: power domain device for SoC mtcmos control
> > + * @init_completion: the timeout completion for bank init
> > + * @buck: phandle of the regulator

[snip]

> > +static int svs_adjust_pm_opp_volts(struct svs_bank *svsb, bool
> > force_update)
> > +{
> > +	int tzone_temp = 0, ret = -EPERM;
> > +	u32 i, svsb_volt, opp_volt, temp_offset = 0;
> > +
> > +	mutex_lock(&svsb->lock);
> > +
> > +	/*
> > +	 * If svs bank is suspended, it means signed-off voltages are applied.
> > +	 * Don't need to update opp voltage anymore.
> > +	 */
> > +	if (svsb->suspended && !force_update) {
> > +		dev_notice(svsb->dev, "bank is suspended\n");
> > +		ret = -EPERM;
> > +		goto unlock_mutex;
> > +	}
> > +
> > +	/* Get thermal effect */
> > +	if (svsb->phase == SVSB_PHASE_MON) {
> > +		if (svsb->temp > svsb->temp_upper_bound &&
> > +		    svsb->temp < svsb->temp_lower_bound) {
> > +			dev_warn(svsb->dev, "svsb temp = 0x%x?\n", svsb->temp);
> > +			ret = -EINVAL;
> > +			goto unlock_mutex;
> > +		}
> > +
> > +		ret = svs_get_bank_zone_temperature(svsb->tzone_name,
> > +						    &tzone_temp);
> > +		if (ret) {
> > +			dev_err(svsb->dev, "no %s? (%d), run default volts\n",
> > +				svsb->tzone_name, ret);
> > +			svsb->phase = SVSB_PHASE_ERROR;
> > +		}
> > +
> > +		if (tzone_temp >= svsb->tzone_high_temp)
> > +			temp_offset += svsb->tzone_high_temp_offset;
> > +		else if (tzone_temp <= svsb->tzone_low_temp)
> > +			temp_offset += svsb->tzone_low_temp_offset;
> > +	}
> > +
> > +	/* vmin <= svsb_volt (opp_volt) <= signed-off (default) voltage */
> > +	for (i = 0; i < svsb->opp_count; i++) {
> 
> What about using switch here?
> 
> 		switch (svsb->phase) {
> 		case SVSB_PHASE_MON:
> 			......
> 			break;
> 		case .......:
> 			.........
> 			break;
> 		default:
> 			dev_err(......);
> 			ret = -EINVAL;
> 			goto unlock_mutex;
> 		}
> 

Okay. I'll use switch here in the next version. Thanks for the advice.

> > +		if (svsb->phase == SVSB_PHASE_MON) {
> > +			svsb_volt = max(svsb->volts[i] + svsb->volt_offset +
> > +					temp_offset, svsb->vmin);
> > +			opp_volt = svs_bank_volt_to_opp_volt(svsb_volt,
> > +							     svsb->volt_step,
> > +							     svsb->volt_base);
> > +		} else if (svsb->phase == SVSB_PHASE_INIT02) {
> > +			svsb_volt = max(svsb->volts[i] + svsb->volt_offset,
> > +					svsb->vmin);
> > +			opp_volt = svs_bank_volt_to_opp_volt(svsb_volt,
> > +							     svsb->volt_step,
> > +							     svsb->volt_base);
> > +		} else if (svsb->phase == SVSB_PHASE_ERROR) {
> > +			opp_volt = svsb->opp_volts[i];
> > +		} else {
> > +			dev_err(svsb->dev, "unknown phase: %u?\n", svsb->phase);
> > +			ret = -EINVAL;
> > +			goto unlock_mutex;
> > +		}
> > +
> > +		opp_volt = min(opp_volt, svsb->opp_volts[i]);
> > +		ret = dev_pm_opp_adjust_voltage(svsb->opp_dev,
> > +						svsb->opp_freqs[i],
> > +						opp_volt, opp_volt,
> > +						svsb->opp_volts[i]);
> > +		if (ret) {
> > +			dev_err(svsb->dev, "set voltage fail: %d\n", ret);
> > +			goto unlock_mutex;
> > +		}
> > +	}
> > +
> > +unlock_mutex:
> > +	mutex_unlock(&svsb->lock);
> > +
> > +	return ret;
> > +}

[snip]

> > +static void svs_set_bank_phase(struct svs_platform *svsp,
> > +			       enum svsb_phase target_phase)
> > +{
> > +	struct svs_bank *svsb = svsp->pbank;
> > +	u32 des_char, temp_char, det_char, limit_vals;
> > +	u32 init2vals, ts_calcs, val, filter, i;
> > +
> > +	svs_switch_bank(svsp);
> > +
> > +	des_char = (svsb->bdes << 8) | svsb->mdes;
> > +	svs_writel(svsp, des_char, DESCHAR);
> > +
> > +	temp_char = (svsb->vco << 16) | (svsb->mtdes << 8) | svsb->dvt_fixed;
> > +	svs_writel(svsp, temp_char, TEMPCHAR);
> > +
> > +	det_char = (svsb->dcbdet << 8) | svsb->dcmdet;
> > +	svs_writel(svsp, det_char, DETCHAR);
> > +
> > +	svs_writel(svsp, svsb->dc_config, DCCONFIG);
> > +	svs_writel(svsp, svsb->age_config, AGECONFIG);
> > +
> > +	if (!svsb->agem) {
> > +		svs_writel(svsp, SVSB_RUNCONFIG_DEFAULT, RUNCONFIG);
> > +	} else {
> > +		val = 0x0;
> 
> 		val = 0;
> 
> > +
> > +		for (i = 0; i < 24; i += 2) {
> > +			filter = 0x3 << i;
> > +
> > +			if (!(svsb->age_config & filter))
> > +				val |= (0x1 << i);
> 
> 				val |= BIT(i);

I'll remove these if / else for "agem" in this patch as Matthias recommended in
v20 review. Thanks for the advice.

> > +			else
> > +				val |= (svsb->age_config & filter);
> > +		}
> > +		svs_writel(svsp, val, RUNCONFIG);
> > +	}
> > +
> > +	svsb->set_freqs_pct(svsp);
> > +
> > +	limit_vals = (svsb->vmax << 24) | (svsb->vmin << 16) |
> > +		     (svsb->dthi << 8) | svsb->dtlo;
> > +	svs_writel(svsp, limit_vals, LIMITVALS);
> > +	svs_writel(svsp, svsb->vboot, VBOOT);
> > +	svs_writel(svsp, svsb->det_window, DETWINDOW);
> > +	svs_writel(svsp, svsb->det_max, CONFIG);
> > +
> > +	if (svsb->chk_shift)
> > +		svs_writel(svsp, svsb->chk_shift, CHKSHIFT);
> > +
> > +	if (svsb->ctl0)
> > +		svs_writel(svsp, svsb->ctl0, CTL0);
> > +
> > +	svs_writel(svsp, SVSB_INTSTS_CLEAN, INTSTS);
> > +
> > +	switch (target_phase) {
> > +	case SVSB_PHASE_INIT01:
> > +		svs_writel(svsp, SVSB_INTEN_INIT0x, INTEN);
> > +		svs_writel(svsp, SVSB_EN_INIT01, SVSEN);
> > +		break;
> > +	case SVSB_PHASE_INIT02:
> > +		svs_writel(svsp, SVSB_INTEN_INIT0x, INTEN);
> > +		init2vals = (svsb->age_voffset_in << 16) | svsb->dc_voffset_in;
> > +		svs_writel(svsp, init2vals, INIT2VALS);
> > +		svs_writel(svsp, SVSB_EN_INIT02, SVSEN);
> > +		break;
> > +	case SVSB_PHASE_MON:
> > +		ts_calcs = (svsb->bts << 12) | svsb->mts;
> > +		svs_writel(svsp, ts_calcs, TSCALCS);
> > +		svs_writel(svsp, SVSB_INTEN_MONVOPEN, INTEN);
> > +		svs_writel(svsp, SVSB_EN_MON, SVSEN);
> > +		break;
> > +	default:
> > +		WARN_ON(1);
> 
> I agree about printing a big warning in kmsg here, but you can do that in a
> slightly more descriptive way:
> 
> 		WARN(1, "Requested unknown target phase %u", target_phase);
> 

Okay. I'll add description in these kinds of warning kmsg. Thanks for the
advice.

> > +		break;
> > +	}
> > +}

[snip]

> > +static int svs_init02(struct svs_platform *svsp)
> > +{
> > +	struct svs_bank *svsb;
> > +	unsigned long flags, time_left;
> > +	u32 idx;
> > +
> > +	for (idx = 0; idx < svsp->bank_num; idx++) {
> > +		svsb = &svsp->banks[idx];
> > +
> > +		if (!(svsb->mode_support & SVSB_MODE_INIT02))
> > +			continue;
> > +
> > +		reinit_completion(&svsb->init_completion);
> > +		spin_lock_irqsave(&mtk_svs_lock, flags);
> > +		svsp->pbank = svsb;
> > +		svs_set_bank_phase(svsp, SVSB_PHASE_INIT02);
> > +		spin_unlock_irqrestore(&mtk_svs_lock, flags);
> > +
> > +		time_left =
> > +			wait_for_completion_timeout(&svsb->init_completion,
> > +						    msecs_to_jiffies(5000));
> 
> There's no need to break the line... that's going to be fine:
> 
> 		time_left = wait_for_completion_timeout(&svsb->init_completion,
> 
> 							msecs_to_jiffies(5000));
> 

Okay. I'll keep it one line. Thanks.

> > +		if (!time_left) {
> > +			dev_err(svsb->dev, "init02 completion timeout\n");
> > +			return -EBUSY;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int svs_init01(struct svs_platform *svsp)
> > +{
> > +	struct svs_bank *svsb;
> > +	struct pm_qos_request *qos_request;
> > +	unsigned long flags, time_left;
> > +	bool search_done;
> > +	int ret = 0;
> > +	u32 opp_freqs, opp_vboot, buck_volt, idx, i;
> > +
> > +	qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL);
> > +	if (!qos_request)
> > +		return -ENOMEM;
> > +
> > +	/* Let CPUs leave idle-off state for initializing svs_init01. */
> > +	cpu_latency_qos_add_request(qos_request, 0);
> > +
> > +	/*
> > +	 * Sometimes two svs banks use the same buck.
> > +	 * Therefore, we set each svs bank to vboot voltage first.
> > +	 */
> > +	for (idx = 0; idx < svsp->bank_num; idx++) {
> > +		svsb = &svsp->banks[idx];
> > +
> > +		if (!(svsb->mode_support & SVSB_MODE_INIT01))
> > +			continue;
> > +
> > +		search_done = false;
> > +
> > +		if (svsb->pd_req) {
> > +			ret = regulator_enable(svsb->buck);
> > +			if (ret) {
> > +				dev_err(svsb->dev, "%s enable fail: %d\n",
> > +					svsb->buck_name, ret);
> > +				goto init01_finish;
> > +			}
> > +
> > +			if (!pm_runtime_enabled(svsb->pd_dev)) {
> > +				pm_runtime_enable(svsb->pd_dev);
> > +				svsb->enable_pm_runtime_ever = true;
> > +			}
> > +
> > +			ret = pm_runtime_get_sync(svsb->pd_dev);
> > +			if (ret < 0) {
> > +				dev_err(svsb->dev, "mtcmos on fail: %d\n", ret);
> > +				goto init01_finish;
> > +			}
> > +		}
> > +
> > +		if (regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST))
> > +			dev_notice(svsb->dev, "set fast mode fail\n");
> > +
> > +		/*
> > +		 * Find the fastest freq that can be run at vboot and
> > +		 * fix to that freq until svs_init01 is done.
> > +		 */
> > +		opp_vboot = svs_bank_volt_to_opp_volt(svsb->vboot,
> > +						      svsb->volt_step,
> > +						      svsb->volt_base);
> > +
> > +		for (i = 0; i < svsb->opp_count; i++) {
> > +			opp_freqs = svsb->opp_freqs[i];
> > +			if (!search_done && svsb->opp_volts[i] <= opp_vboot) {
> > +				ret = dev_pm_opp_adjust_voltage(svsb->opp_dev,
> > +								opp_freqs,
> > +								opp_vboot,
> > +								opp_vboot,
> > +								opp_vboot);
> > +				if (ret) {
> > +					dev_err(svsb->dev,
> > +						"set voltage fail: %d\n", ret);
> > +					goto init01_finish;
> > +				}
> > +
> > +				search_done = true;
> > +			} else {
> > +				dev_pm_opp_disable(svsb->opp_dev,
> > +						   svsb->opp_freqs[i]);
> > +			}
> > +		}
> > +	}
> > +
> > +	/* svs bank init01 begins */
> > +	for (idx = 0; idx < svsp->bank_num; idx++) {
> > +		svsb = &svsp->banks[idx];
> > +
> > +		if (!(svsb->mode_support & SVSB_MODE_INIT01))
> > +			continue;
> > +
> > +		opp_vboot = svs_bank_volt_to_opp_volt(svsb->vboot,
> > +						      svsb->volt_step,
> > +						      svsb->volt_base);
> > +
> > +		buck_volt = regulator_get_voltage(svsb->buck);
> > +		if (buck_volt != opp_vboot) {
> > +			dev_err(svsb->dev,
> > +				"buck voltage: %u, expected vboot: %u\n",
> > +				buck_volt, opp_vboot);
> > +			ret = -EPERM;
> > +			goto init01_finish;
> > +		}
> > +
> > +		spin_lock_irqsave(&mtk_svs_lock, flags);
> > +		svsp->pbank = svsb;
> > +		svs_set_bank_phase(svsp, SVSB_PHASE_INIT01);
> > +		spin_unlock_irqrestore(&mtk_svs_lock, flags);
> > +
> > +		time_left =
> > +			wait_for_completion_timeout(&svsb->init_completion,
> 
> 81 columns is ok to have.
> 

Okay. Thanks. I'll keep it one line.

> > +						    msecs_to_jiffies(5000));
> > +		if (!time_left) {
> > +			dev_err(svsb->dev, "init01 completion timeout\n");
> > +			ret = -EBUSY;
> > +			goto init01_finish;
> > +		}
> > +	}
> > +
> > +init01_finish:
> > +	for (idx = 0; idx < svsp->bank_num; idx++) {
> > +		svsb = &svsp->banks[idx];
> > +
> > +		if (!(svsb->mode_support & SVSB_MODE_INIT01))
> > +			continue;
> > +
> > +		for (i = 0; i < svsb->opp_count; i++)
> > +			dev_pm_opp_enable(svsb->opp_dev, svsb->opp_freqs[i]);
> > +
> > +		if (regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL))
> > +			dev_notice(svsb->dev, "fail to set normal mode\n");
> > +
> > +		if (svsb->pd_req) {
> > +			if (pm_runtime_put_sync(svsb->pd_dev))
> > +				dev_err(svsb->dev, "mtcmos off fail\n");
> > +
> > +			if (svsb->enable_pm_runtime_ever) {
> > +				pm_runtime_disable(svsb->pd_dev);
> > +				svsb->enable_pm_runtime_ever = false;
> > +			}
> > +
> > +			if (regulator_disable(svsb->buck))
> > +				dev_err(svsb->dev, "%s disable fail: %d\n",
> > +					svsb->buck_name, ret);
> > +		}
> > +	}
> > +
> > +	cpu_latency_qos_remove_request(qos_request);
> > +	kfree(qos_request);
> > +
> > +	return ret;
> > +}

[snip]

> > +static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp)
> > +{
> > +	struct thermal_parameter tp;
> > +	struct svs_bank *svsb;
> > +	bool mon_mode_support = true;
> > +	int format[6], x_roomt[6], tb_roomt = 0;
> > +	struct nvmem_cell *cell;
> > +	u32 idx, i, ft_pgm, mts, temp0, temp1, temp2;
> > +
> > +	for (i = 0; i < svsp->efuse_num; i++)
> > +		if (svsp->efuse[i])
> > +			dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n",
> > +				 i, svsp->efuse[i]);
> > +
> > +	/* Svs efuse parsing */
> > +	ft_pgm = (svsp->efuse[0] >> 4) & GENMASK(3, 0);
> > +
> > +	for (idx = 0; idx < svsp->bank_num; idx++) {
> > +		svsb = &svsp->banks[idx];
> > +
> > +		if (ft_pgm <= 1)
> > +			svsb->volt_flags |= SVSB_INIT01_VOLT_IGNORE;
> > +
> > +		switch (svsb->sw_id) {
> > +		case SVSB_CPU_LITTLE:
> > +			svsb->bdes = svsp->efuse[16] & GENMASK(7, 0);
> > +			svsb->mdes = (svsp->efuse[16] >> 8) & GENMASK(7, 0);
> > +			svsb->dcbdet = (svsp->efuse[16] >> 16) & GENMASK(7, 0);
> > +			svsb->dcmdet = (svsp->efuse[16] >> 24) & GENMASK(7, 0);
> > +			svsb->mtdes  = (svsp->efuse[17] >> 16) & GENMASK(7, 0);
> > +
> > +			if (ft_pgm <= 3)
> > +				svsb->volt_offset += 10;
> > +			else
> > +				svsb->volt_offset += 2;
> > +			break;
> > +		case SVSB_CPU_BIG:
> > +			svsb->bdes = svsp->efuse[18] & GENMASK(7, 0);
> > +			svsb->mdes = (svsp->efuse[18] >> 8) & GENMASK(7, 0);
> > +			svsb->dcbdet = (svsp->efuse[18] >> 16) & GENMASK(7, 0);
> > +			svsb->dcmdet = (svsp->efuse[18] >> 24) & GENMASK(7, 0);
> > +			svsb->mtdes  = svsp->efuse[17] & GENMASK(7, 0);
> > +
> > +			if (ft_pgm <= 3)
> > +				svsb->volt_offset += 15;
> > +			else
> > +				svsb->volt_offset += 12;
> > +			break;
> > +		case SVSB_CCI:
> > +			svsb->bdes = svsp->efuse[4] & GENMASK(7, 0);
> > +			svsb->mdes = (svsp->efuse[4] >> 8) & GENMASK(7, 0);
> > +			svsb->dcbdet = (svsp->efuse[4] >> 16) & GENMASK(7, 0);
> > +			svsb->dcmdet = (svsp->efuse[4] >> 24) & GENMASK(7, 0);
> > +			svsb->mtdes  = (svsp->efuse[5] >> 16) & GENMASK(7, 0);
> > +
> > +			if (ft_pgm <= 3)
> > +				svsb->volt_offset += 10;
> > +			else
> > +				svsb->volt_offset += 2;
> > +			break;
> > +		case SVSB_GPU:
> > +			svsb->bdes = svsp->efuse[6] & GENMASK(7, 0);
> > +			svsb->mdes = (svsp->efuse[6] >> 8) & GENMASK(7, 0);
> > +			svsb->dcbdet = (svsp->efuse[6] >> 16) & GENMASK(7, 0);
> > +			svsb->dcmdet = (svsp->efuse[6] >> 24) & GENMASK(7, 0);
> > +			svsb->mtdes  = svsp->efuse[5] & GENMASK(7, 0);
> > +
> > +			if (ft_pgm >= 2) {
> > +				svsb->freq_base = 800000000; /* 800MHz */
> > +				svsb->dvt_fixed = 2;
> > +			}
> > +			break;
> > +		default:
> > +			break;
> > +		}
> > +	}
> > +
> > +	/* Get thermal efuse by nvmem */
> > +	cell = nvmem_cell_get(svsp->dev, "t-calibration-data");
> > +	if (IS_ERR_OR_NULL(cell)) {
> > +		dev_err(svsp->dev, "no thermal cell, no mon mode\n");
> > +		for (idx = 0; idx < svsp->bank_num; idx++) {
> > +			svsb = &svsp->banks[idx];
> > +			svsb->mode_support &= ~SVSB_MODE_MON;
> > +		}
> > +
> > +		return true;
> > +	}
> > +
> > +	svsp->tefuse = nvmem_cell_read(cell, &svsp->tefuse_num);
> 
> nvmem_cell_read may return an error pointer: you have to check that.
> 
> 	if (IS_ERR(svsp->tefuse))
> 		.........
> 
> Failing to perform this check will produce unpredictable behavior
> during the parsing stage.
> 

Sure. I'll add the error handling when using nvmem_cell_read(). Thanks for the
advice.

> > +	svsp->tefuse_num /= sizeof(u32);
> > +	nvmem_cell_put(cell);
> > +
> > +	/* Thermal efuse parsing */
> > +	tp.adc_ge_t = (svsp->tefuse[1] >> 22) & GENMASK(9, 0);
> > +	tp.adc_oe_t = (svsp->tefuse[1] >> 12) & GENMASK(9, 0);
> > +
> > +	tp.o_vtsmcu1 = (svsp->tefuse[0] >> 17) & GENMASK(8, 0);
> > +	tp.o_vtsmcu2 = (svsp->tefuse[0] >> 8) & GENMASK(8, 0);
> > +	tp.o_vtsmcu3 = svsp->tefuse[1] & GENMASK(8, 0);
> > +	tp.o_vtsmcu4 = (svsp->tefuse[2] >> 23) & GENMASK(8, 0);
> > +	tp.o_vtsmcu5 = (svsp->tefuse[2] >> 5) & GENMASK(8, 0);
> > +	tp.o_vtsabb = (svsp->tefuse[2] >> 14) & GENMASK(8, 0);
> > +
> > +	tp.degc_cali = (svsp->tefuse[0] >> 1) & GENMASK(5, 0);
> > +	tp.adc_cali_en_t = svsp->tefuse[0] & BIT(0);
> > +	tp.o_slope_sign = (svsp->tefuse[0] >> 7) & BIT(0);
> > +
> > +	tp.ts_id = (svsp->tefuse[1] >> 9) & BIT(0);
> > +	tp.o_slope = (svsp->tefuse[0] >> 26) & GENMASK(5, 0);
> > +
> 
> Regards,
> - Angelo
> 
> 




More information about the linux-arm-kernel mailing list