[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