Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Roger Lu | 11708 | 95.91% | 12 | 44.44% |
Angelo G. Del Regno | 443 | 3.63% | 4 | 14.81% |
Matthias Brugger | 25 | 0.20% | 3 | 11.11% |
Peng Wu | 9 | 0.07% | 1 | 3.70% |
Zeng Heng | 7 | 0.06% | 1 | 3.70% |
Ricardo Ribalda Delgado | 5 | 0.04% | 1 | 3.70% |
Nícolas F. R. A. Prado | 3 | 0.02% | 1 | 3.70% |
Rob Herring | 3 | 0.02% | 1 | 3.70% |
Chen-Yu Tsai | 2 | 0.02% | 1 | 3.70% |
Shang XiaoJing | 1 | 0.01% | 1 | 3.70% |
Nathan Chancellor | 1 | 0.01% | 1 | 3.70% |
Total | 12207 | 27 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2022 MediaTek Inc. */ #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/clk.h> #include <linux/completion.h> #include <linux/cpu.h> #include <linux/cpuidle.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/nvmem-consumer.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/thermal.h> /* svs bank 1-line software id */ #define SVSB_CPU_LITTLE BIT(0) #define SVSB_CPU_BIG BIT(1) #define SVSB_CCI BIT(2) #define SVSB_GPU BIT(3) /* svs bank 2-line type */ #define SVSB_LOW BIT(8) #define SVSB_HIGH BIT(9) /* svs bank mode support */ #define SVSB_MODE_ALL_DISABLE 0 #define SVSB_MODE_INIT01 BIT(1) #define SVSB_MODE_INIT02 BIT(2) #define SVSB_MODE_MON BIT(3) /* svs bank volt flags */ #define SVSB_INIT01_PD_REQ BIT(0) #define SVSB_INIT01_VOLT_IGNORE BIT(1) #define SVSB_INIT01_VOLT_INC_ONLY BIT(2) #define SVSB_MON_VOLT_IGNORE BIT(16) #define SVSB_REMOVE_DVTFIXED_VOLT BIT(24) /* svs bank register fields and common configuration */ #define SVSB_PTPCONFIG_DETMAX GENMASK(15, 0) #define SVSB_DET_MAX FIELD_PREP(SVSB_PTPCONFIG_DETMAX, 0xffff) #define SVSB_DET_WINDOW 0xa28 /* DESCHAR */ #define SVSB_DESCHAR_FLD_MDES GENMASK(7, 0) #define SVSB_DESCHAR_FLD_BDES GENMASK(15, 8) /* TEMPCHAR */ #define SVSB_TEMPCHAR_FLD_DVT_FIXED GENMASK(7, 0) #define SVSB_TEMPCHAR_FLD_MTDES GENMASK(15, 8) #define SVSB_TEMPCHAR_FLD_VCO GENMASK(23, 16) /* DETCHAR */ #define SVSB_DETCHAR_FLD_DCMDET GENMASK(7, 0) #define SVSB_DETCHAR_FLD_DCBDET GENMASK(15, 8) /* SVSEN (PTPEN) */ #define SVSB_PTPEN_INIT01 BIT(0) #define SVSB_PTPEN_MON BIT(1) #define SVSB_PTPEN_INIT02 (SVSB_PTPEN_INIT01 | BIT(2)) #define SVSB_PTPEN_OFF 0x0 /* FREQPCTS */ #define SVSB_FREQPCTS_FLD_PCT0_4 GENMASK(7, 0) #define SVSB_FREQPCTS_FLD_PCT1_5 GENMASK(15, 8) #define SVSB_FREQPCTS_FLD_PCT2_6 GENMASK(23, 16) #define SVSB_FREQPCTS_FLD_PCT3_7 GENMASK(31, 24) /* INTSTS */ #define SVSB_INTSTS_VAL_CLEAN 0x00ffffff #define SVSB_INTSTS_F0_COMPLETE BIT(0) #define SVSB_INTSTS_FLD_MONVOP GENMASK(23, 16) #define SVSB_RUNCONFIG_DEFAULT 0x80000000 /* LIMITVALS */ #define SVSB_LIMITVALS_FLD_DTLO GENMASK(7, 0) #define SVSB_LIMITVALS_FLD_DTHI GENMASK(15, 8) #define SVSB_LIMITVALS_FLD_VMIN GENMASK(23, 16) #define SVSB_LIMITVALS_FLD_VMAX GENMASK(31, 24) #define SVSB_VAL_DTHI 0x1 #define SVSB_VAL_DTLO 0xfe /* INTEN */ #define SVSB_INTEN_F0EN BIT(0) #define SVSB_INTEN_DACK0UPEN BIT(8) #define SVSB_INTEN_DC0EN BIT(9) #define SVSB_INTEN_DC1EN BIT(10) #define SVSB_INTEN_DACK0LOEN BIT(11) #define SVSB_INTEN_INITPROD_OVF_EN BIT(12) #define SVSB_INTEN_INITSUM_OVF_EN BIT(14) #define SVSB_INTEN_MONVOPEN GENMASK(23, 16) #define SVSB_INTEN_INIT0x (SVSB_INTEN_F0EN | SVSB_INTEN_DACK0UPEN | \ SVSB_INTEN_DC0EN | SVSB_INTEN_DC1EN | \ SVSB_INTEN_DACK0LOEN | \ SVSB_INTEN_INITPROD_OVF_EN | \ SVSB_INTEN_INITSUM_OVF_EN) /* TSCALCS */ #define SVSB_TSCALCS_FLD_MTS GENMASK(11, 0) #define SVSB_TSCALCS_FLD_BTS GENMASK(23, 12) /* INIT2VALS */ #define SVSB_INIT2VALS_FLD_DCVOFFSETIN GENMASK(15, 0) #define SVSB_INIT2VALS_FLD_AGEVOFFSETIN GENMASK(31, 16) /* VOPS */ #define SVSB_VOPS_FLD_VOP0_4 GENMASK(7, 0) #define SVSB_VOPS_FLD_VOP1_5 GENMASK(15, 8) #define SVSB_VOPS_FLD_VOP2_6 GENMASK(23, 16) #define SVSB_VOPS_FLD_VOP3_7 GENMASK(31, 24) /* svs bank related setting */ #define BITS8 8 #define MAX_OPP_ENTRIES 16 #define REG_BYTES 4 #define SVSB_DC_SIGNED_BIT BIT(15) #define SVSB_DET_CLK_EN BIT(31) #define SVSB_TEMP_LOWER_BOUND 0xb2 #define SVSB_TEMP_UPPER_BOUND 0x64 static DEFINE_SPINLOCK(svs_lock); #ifdef CONFIG_DEBUG_FS #define debug_fops_ro(name) \ static int svs_##name##_debug_open(struct inode *inode, \ struct file *filp) \ { \ return single_open(filp, svs_##name##_debug_show, \ inode->i_private); \ } \ static const struct file_operations svs_##name##_debug_fops = { \ .owner = THIS_MODULE, \ .open = svs_##name##_debug_open, \ .read = seq_read, \ .llseek = seq_lseek, \ .release = single_release, \ } #define debug_fops_rw(name) \ static int svs_##name##_debug_open(struct inode *inode, \ struct file *filp) \ { \ return single_open(filp, svs_##name##_debug_show, \ inode->i_private); \ } \ static const struct file_operations svs_##name##_debug_fops = { \ .owner = THIS_MODULE, \ .open = svs_##name##_debug_open, \ .read = seq_read, \ .write = svs_##name##_debug_write, \ .llseek = seq_lseek, \ .release = single_release, \ } #define svs_dentry_data(name) {__stringify(name), &svs_##name##_debug_fops} #endif /** * enum svsb_phase - svs bank phase enumeration * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition * @SVSB_PHASE_INIT01: svs bank basic init for data calibration * @SVSB_PHASE_INIT02: svs bank can provide voltages to opp table * @SVSB_PHASE_MON: svs bank can provide voltages with thermal effect * @SVSB_PHASE_MAX: total number of svs bank phase (debug purpose) * * Each svs bank has its own independent phase and 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, SVSB_PHASE_MAX, }; enum svs_reg_index { DESCHAR = 0, TEMPCHAR, DETCHAR, AGECHAR, DCCONFIG, AGECONFIG, FREQPCT30, FREQPCT74, LIMITVALS, VBOOT, DETWINDOW, CONFIG, TSCALCS, RUNCONFIG, SVSEN, INIT2VALS, DCVALUES, AGEVALUES, VOP30, VOP74, TEMP, INTSTS, INTSTSRAW, INTEN, CHKINT, CHKSHIFT, STATUS, VDESIGN30, VDESIGN74, DVT30, DVT74, AGECOUNT, SMSTATE0, SMSTATE1, CTL0, DESDETSEC, TEMPAGESEC, CTRLSPARE0, CTRLSPARE1, CTRLSPARE2, CTRLSPARE3, CORESEL, THERMINTST, INTST, THSTAGE0ST, THSTAGE1ST, THSTAGE2ST, THAHBST0, THAHBST1, SPARE0, SPARE1, SPARE2, SPARE3, THSLPEVEB, SVS_REG_MAX, }; static const u32 svs_regs_v2[] = { [DESCHAR] = 0xc00, [TEMPCHAR] = 0xc04, [DETCHAR] = 0xc08, [AGECHAR] = 0xc0c, [DCCONFIG] = 0xc10, [AGECONFIG] = 0xc14, [FREQPCT30] = 0xc18, [FREQPCT74] = 0xc1c, [LIMITVALS] = 0xc20, [VBOOT] = 0xc24, [DETWINDOW] = 0xc28, [CONFIG] = 0xc2c, [TSCALCS] = 0xc30, [RUNCONFIG] = 0xc34, [SVSEN] = 0xc38, [INIT2VALS] = 0xc3c, [DCVALUES] = 0xc40, [AGEVALUES] = 0xc44, [VOP30] = 0xc48, [VOP74] = 0xc4c, [TEMP] = 0xc50, [INTSTS] = 0xc54, [INTSTSRAW] = 0xc58, [INTEN] = 0xc5c, [CHKINT] = 0xc60, [CHKSHIFT] = 0xc64, [STATUS] = 0xc68, [VDESIGN30] = 0xc6c, [VDESIGN74] = 0xc70, [DVT30] = 0xc74, [DVT74] = 0xc78, [AGECOUNT] = 0xc7c, [SMSTATE0] = 0xc80, [SMSTATE1] = 0xc84, [CTL0] = 0xc88, [DESDETSEC] = 0xce0, [TEMPAGESEC] = 0xce4, [CTRLSPARE0] = 0xcf0, [CTRLSPARE1] = 0xcf4, [CTRLSPARE2] = 0xcf8, [CTRLSPARE3] = 0xcfc, [CORESEL] = 0xf00, [THERMINTST] = 0xf04, [INTST] = 0xf08, [THSTAGE0ST] = 0xf0c, [THSTAGE1ST] = 0xf10, [THSTAGE2ST] = 0xf14, [THAHBST0] = 0xf18, [THAHBST1] = 0xf1c, [SPARE0] = 0xf20, [SPARE1] = 0xf24, [SPARE2] = 0xf28, [SPARE3] = 0xf2c, [THSLPEVEB] = 0xf30, }; /** * struct svs_platform - svs platform control * @base: svs platform register base * @dev: svs platform device * @main_clk: main clock for svs bank * @pbank: svs bank pointer needing to be protected by spin_lock section * @banks: svs banks that svs platform supports * @rst: svs platform reset control * @efuse_max: total number of svs efuse * @tefuse_max: total number of thermal efuse * @regs: svs platform registers map * @bank_max: total number of svs banks * @efuse: svs efuse data received from NVMEM framework * @tefuse: thermal efuse data received from NVMEM framework */ struct svs_platform { void __iomem *base; struct device *dev; struct clk *main_clk; struct svs_bank *pbank; struct svs_bank *banks; struct reset_control *rst; size_t efuse_max; size_t tefuse_max; const u32 *regs; u32 bank_max; u32 *efuse; u32 *tefuse; }; struct svs_platform_data { char *name; struct svs_bank *banks; bool (*efuse_parsing)(struct svs_platform *svsp); int (*probe)(struct svs_platform *svsp); const u32 *regs; u32 bank_max; }; /** * struct svs_bank - svs bank representation * @dev: bank device * @opp_dev: device for opp table/buck control * @init_completion: the timeout completion for bank init * @buck: regulator used by opp_dev * @tzd: thermal zone device for getting temperature * @lock: mutex lock to protect voltage update process * @set_freq_pct: function pointer to set bank frequency percent table * @get_volts: function pointer to get bank voltages * @name: bank name * @buck_name: regulator name * @tzone_name: thermal zone name * @phase: bank current phase * @volt_od: bank voltage overdrive * @reg_data: bank register data in different phase for debug purpose * @pm_runtime_enabled_count: bank pm runtime enabled count * @mode_support: bank mode support. * @freq_base: reference frequency for bank init * @turn_freq_base: refenrece frequency for 2-line turn point * @vboot: voltage request for bank init01 only * @opp_dfreq: default opp frequency table * @opp_dvolt: default opp voltage table * @freq_pct: frequency percent table for bank init * @volt: bank voltage table * @volt_step: bank voltage step * @volt_base: bank voltage base * @volt_flags: bank voltage flags * @vmax: bank voltage maximum * @vmin: bank voltage minimum * @age_config: bank age configuration * @age_voffset_in: bank age voltage offset * @dc_config: bank dc configuration * @dc_voffset_in: bank dc voltage offset * @dvt_fixed: bank dvt fixed value * @vco: bank VCO value * @chk_shift: bank chicken shift * @core_sel: bank selection * @opp_count: bank opp count * @int_st: bank interrupt identification * @sw_id: bank software identification * @cpu_id: cpu core id for SVS CPU bank use only * @ctl0: TS-x selection * @temp: bank temperature * @tzone_htemp: thermal zone high temperature threshold * @tzone_htemp_voffset: thermal zone high temperature voltage offset * @tzone_ltemp: thermal zone low temperature threshold * @tzone_ltemp_voffset: thermal zone low temperature voltage offset * @bts: svs efuse data * @mts: svs efuse data * @bdes: svs efuse data * @mdes: svs efuse data * @mtdes: svs efuse data * @dcbdet: svs efuse data * @dcmdet: svs efuse data * @turn_pt: 2-line turn point tells which opp_volt calculated by high/low bank * @type: bank type to represent it is 2-line (high/low) bank or 1-line bank * * Svs bank will generate suitalbe voltages by below general math equation * and provide these voltages to opp voltage table. * * opp_volt[i] = (volt[i] * volt_step) + volt_base; */ struct svs_bank { struct device *dev; struct device *opp_dev; struct completion init_completion; struct regulator *buck; struct thermal_zone_device *tzd; struct mutex lock; /* lock to protect voltage update process */ void (*set_freq_pct)(struct svs_platform *svsp); void (*get_volts)(struct svs_platform *svsp); char *name; char *buck_name; char *tzone_name; enum svsb_phase phase; s32 volt_od; u32 reg_data[SVSB_PHASE_MAX][SVS_REG_MAX]; u32 pm_runtime_enabled_count; u32 mode_support; u32 freq_base; u32 turn_freq_base; u32 vboot; u32 opp_dfreq[MAX_OPP_ENTRIES]; u32 opp_dvolt[MAX_OPP_ENTRIES]; u32 freq_pct[MAX_OPP_ENTRIES]; u32 volt[MAX_OPP_ENTRIES]; u32 volt_step; u32 volt_base; u32 volt_flags; u32 vmax; u32 vmin; u32 age_config; u32 age_voffset_in; u32 dc_config; u32 dc_voffset_in; u32 dvt_fixed; u32 vco; u32 chk_shift; u32 core_sel; u32 opp_count; u32 int_st; u32 sw_id; u32 cpu_id; u32 ctl0; u32 temp; u32 tzone_htemp; u32 tzone_htemp_voffset; u32 tzone_ltemp; u32 tzone_ltemp_voffset; u32 bts; u32 mts; u32 bdes; u32 mdes; u32 mtdes; u32 dcbdet; u32 dcmdet; u32 turn_pt; u32 type; }; static u32 percent(u32 numerator, u32 denominator) { /* If not divide 1000, "numerator * 100" will have data overflow. */ numerator /= 1000; denominator /= 1000; return DIV_ROUND_UP(numerator * 100, denominator); } static u32 svs_readl_relaxed(struct svs_platform *svsp, enum svs_reg_index rg_i) { return readl_relaxed(svsp->base + svsp->regs[rg_i]); } static void svs_writel_relaxed(struct svs_platform *svsp, u32 val, enum svs_reg_index rg_i) { writel_relaxed(val, svsp->base + svsp->regs[rg_i]); } static void svs_switch_bank(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; svs_writel_relaxed(svsp, svsb->core_sel, CORESEL); } static u32 svs_bank_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step, u32 svsb_volt_base) { return (svsb_volt * svsb_volt_step) + svsb_volt_base; } static u32 svs_opp_volt_to_bank_volt(u32 opp_u_volt, u32 svsb_volt_step, u32 svsb_volt_base) { return (opp_u_volt - svsb_volt_base) / svsb_volt_step; } static int svs_sync_bank_volts_from_opp(struct svs_bank *svsb) { struct dev_pm_opp *opp; u32 i, opp_u_volt; for (i = 0; i < svsb->opp_count; i++) { opp = dev_pm_opp_find_freq_exact(svsb->opp_dev, svsb->opp_dfreq[i], true); if (IS_ERR(opp)) { dev_err(svsb->dev, "cannot find freq = %u (%ld)\n", svsb->opp_dfreq[i], PTR_ERR(opp)); return PTR_ERR(opp); } opp_u_volt = dev_pm_opp_get_voltage(opp); svsb->volt[i] = svs_opp_volt_to_bank_volt(opp_u_volt, svsb->volt_step, svsb->volt_base); dev_pm_opp_put(opp); } return 0; } static int svs_adjust_pm_opp_volts(struct svs_bank *svsb) { int ret = -EPERM, tzone_temp = 0; u32 i, svsb_volt, opp_volt, temp_voffset = 0, opp_start, opp_stop; mutex_lock(&svsb->lock); /* * 2-line bank updates its corresponding opp volts. * 1-line bank updates all opp volts. */ if (svsb->type == SVSB_HIGH) { opp_start = 0; opp_stop = svsb->turn_pt; } else if (svsb->type == SVSB_LOW) { opp_start = svsb->turn_pt; opp_stop = svsb->opp_count; } else { opp_start = 0; opp_stop = svsb->opp_count; } /* Get thermal effect */ if (!IS_ERR_OR_NULL(svsb->tzd)) { ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); if (ret || (svsb->temp > SVSB_TEMP_UPPER_BOUND && svsb->temp < SVSB_TEMP_LOWER_BOUND)) { dev_err(svsb->dev, "%s: %d (0x%x), run default volts\n", svsb->tzone_name, ret, svsb->temp); svsb->phase = SVSB_PHASE_ERROR; } if (tzone_temp >= svsb->tzone_htemp) temp_voffset += svsb->tzone_htemp_voffset; else if (tzone_temp <= svsb->tzone_ltemp) temp_voffset += svsb->tzone_ltemp_voffset; /* 2-line bank update all opp volts when running mon mode */ if (svsb->phase == SVSB_PHASE_MON && (svsb->type == SVSB_HIGH || svsb->type == SVSB_LOW)) { opp_start = 0; opp_stop = svsb->opp_count; } } /* vmin <= svsb_volt (opp_volt) <= default opp voltage */ for (i = opp_start; i < opp_stop; i++) { switch (svsb->phase) { case SVSB_PHASE_ERROR: opp_volt = svsb->opp_dvolt[i]; break; case SVSB_PHASE_INIT01: /* do nothing */ goto unlock_mutex; case SVSB_PHASE_INIT02: case SVSB_PHASE_MON: svsb_volt = max(svsb->volt[i] + temp_voffset, svsb->vmin); opp_volt = svs_bank_volt_to_opp_volt(svsb_volt, svsb->volt_step, svsb->volt_base); break; default: dev_err(svsb->dev, "unknown phase: %u\n", svsb->phase); ret = -EINVAL; goto unlock_mutex; } opp_volt = min(opp_volt, svsb->opp_dvolt[i]); ret = dev_pm_opp_adjust_voltage(svsb->opp_dev, svsb->opp_dfreq[i], opp_volt, opp_volt, svsb->opp_dvolt[i]); if (ret) { dev_err(svsb->dev, "set %uuV fail: %d\n", opp_volt, ret); goto unlock_mutex; } } unlock_mutex: mutex_unlock(&svsb->lock); return ret; } static void svs_bank_disable_and_restore_default_volts(struct svs_platform *svsp, struct svs_bank *svsb) { unsigned long flags; if (svsb->mode_support == SVSB_MODE_ALL_DISABLE) return; spin_lock_irqsave(&svs_lock, flags); svsp->pbank = svsb; svs_switch_bank(svsp); svs_writel_relaxed(svsp, SVSB_PTPEN_OFF, SVSEN); svs_writel_relaxed(svsp, SVSB_INTSTS_VAL_CLEAN, INTSTS); spin_unlock_irqrestore(&svs_lock, flags); svsb->phase = SVSB_PHASE_ERROR; svs_adjust_pm_opp_volts(svsb); } #ifdef CONFIG_DEBUG_FS static int svs_dump_debug_show(struct seq_file *m, void *p) { struct svs_platform *svsp = (struct svs_platform *)m->private; struct svs_bank *svsb; unsigned long svs_reg_addr; u32 idx, i, j, bank_id; for (i = 0; i < svsp->efuse_max; i++) if (svsp->efuse && svsp->efuse[i]) seq_printf(m, "M_HW_RES%d = 0x%08x\n", i, svsp->efuse[i]); for (i = 0; i < svsp->tefuse_max; i++) if (svsp->tefuse) seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n", i, svsp->tefuse[i]); for (bank_id = 0, idx = 0; idx < svsp->bank_max; idx++, bank_id++) { svsb = &svsp->banks[idx]; for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) { seq_printf(m, "Bank_number = %u\n", bank_id); if (i == SVSB_PHASE_INIT01 || i == SVSB_PHASE_INIT02) seq_printf(m, "mode = init%d\n", i); else if (i == SVSB_PHASE_MON) seq_puts(m, "mode = mon\n"); else seq_puts(m, "mode = error\n"); for (j = DESCHAR; j < SVS_REG_MAX; j++) { svs_reg_addr = (unsigned long)(svsp->base + svsp->regs[j]); seq_printf(m, "0x%08lx = 0x%08x\n", svs_reg_addr, svsb->reg_data[i][j]); } } } return 0; } debug_fops_ro(dump); static int svs_enable_debug_show(struct seq_file *m, void *v) { struct svs_bank *svsb = (struct svs_bank *)m->private; switch (svsb->phase) { case SVSB_PHASE_ERROR: seq_puts(m, "disabled\n"); break; case SVSB_PHASE_INIT01: seq_puts(m, "init1\n"); break; case SVSB_PHASE_INIT02: seq_puts(m, "init2\n"); break; case SVSB_PHASE_MON: seq_puts(m, "mon mode\n"); break; default: seq_puts(m, "unknown\n"); break; } return 0; } static ssize_t svs_enable_debug_write(struct file *filp, const char __user *buffer, size_t count, loff_t *pos) { struct svs_bank *svsb = file_inode(filp)->i_private; struct svs_platform *svsp = dev_get_drvdata(svsb->dev); int enabled, ret; char *buf = NULL; if (count >= PAGE_SIZE) return -EINVAL; buf = (char *)memdup_user_nul(buffer, count); if (IS_ERR(buf)) return PTR_ERR(buf); ret = kstrtoint(buf, 10, &enabled); if (ret) return ret; if (!enabled) { svs_bank_disable_and_restore_default_volts(svsp, svsb); svsb->mode_support = SVSB_MODE_ALL_DISABLE; } kfree(buf); return count; } debug_fops_rw(enable); static int svs_status_debug_show(struct seq_file *m, void *v) { struct svs_bank *svsb = (struct svs_bank *)m->private; struct dev_pm_opp *opp; int tzone_temp = 0, ret; u32 i; ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); if (ret) seq_printf(m, "%s: temperature ignore, turn_pt = %u\n", svsb->name, svsb->turn_pt); else seq_printf(m, "%s: temperature = %d, turn_pt = %u\n", svsb->name, tzone_temp, svsb->turn_pt); for (i = 0; i < svsb->opp_count; i++) { opp = dev_pm_opp_find_freq_exact(svsb->opp_dev, svsb->opp_dfreq[i], true); if (IS_ERR(opp)) { seq_printf(m, "%s: cannot find freq = %u (%ld)\n", svsb->name, svsb->opp_dfreq[i], PTR_ERR(opp)); return PTR_ERR(opp); } seq_printf(m, "opp_freq[%02u]: %u, opp_volt[%02u]: %lu, ", i, svsb->opp_dfreq[i], i, dev_pm_opp_get_voltage(opp)); seq_printf(m, "svsb_volt[%02u]: 0x%x, freq_pct[%02u]: %u\n", i, svsb->volt[i], i, svsb->freq_pct[i]); dev_pm_opp_put(opp); } return 0; } debug_fops_ro(status); static int svs_create_debug_cmds(struct svs_platform *svsp) { struct svs_bank *svsb; struct dentry *svs_dir, *svsb_dir, *file_entry; const char *d = "/sys/kernel/debug/svs"; u32 i, idx; struct svs_dentry { const char *name; const struct file_operations *fops; }; struct svs_dentry svs_entries[] = { svs_dentry_data(dump), }; struct svs_dentry svsb_entries[] = { svs_dentry_data(enable), svs_dentry_data(status), }; svs_dir = debugfs_create_dir("svs", NULL); if (IS_ERR(svs_dir)) { dev_err(svsp->dev, "cannot create %s: %ld\n", d, PTR_ERR(svs_dir)); return PTR_ERR(svs_dir); } for (i = 0; i < ARRAY_SIZE(svs_entries); i++) { file_entry = debugfs_create_file(svs_entries[i].name, 0664, svs_dir, svsp, svs_entries[i].fops); if (IS_ERR(file_entry)) { dev_err(svsp->dev, "cannot create %s/%s: %ld\n", d, svs_entries[i].name, PTR_ERR(file_entry)); return PTR_ERR(file_entry); } } for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (svsb->mode_support == SVSB_MODE_ALL_DISABLE) continue; svsb_dir = debugfs_create_dir(svsb->name, svs_dir); if (IS_ERR(svsb_dir)) { dev_err(svsp->dev, "cannot create %s/%s: %ld\n", d, svsb->name, PTR_ERR(svsb_dir)); return PTR_ERR(svsb_dir); } for (i = 0; i < ARRAY_SIZE(svsb_entries); i++) { file_entry = debugfs_create_file(svsb_entries[i].name, 0664, svsb_dir, svsb, svsb_entries[i].fops); if (IS_ERR(file_entry)) { dev_err(svsp->dev, "no %s/%s/%s?: %ld\n", d, svsb->name, svsb_entries[i].name, PTR_ERR(file_entry)); return PTR_ERR(file_entry); } } } return 0; } #endif /* CONFIG_DEBUG_FS */ static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx) { u32 vx; if (v0 == v1 || f0 == f1) return v0; /* *100 to have decimal fraction factor */ vx = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx)); return DIV_ROUND_UP(vx, 100); } static void svs_get_bank_volts_v3(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; u32 i, j, *vop, vop74, vop30, turn_pt = svsb->turn_pt; u32 b_sft, shift_byte = 0, opp_start = 0, opp_stop = 0; u32 middle_index = (svsb->opp_count / 2); if (svsb->phase == SVSB_PHASE_MON && svsb->volt_flags & SVSB_MON_VOLT_IGNORE) return; vop74 = svs_readl_relaxed(svsp, VOP74); vop30 = svs_readl_relaxed(svsp, VOP30); /* Target is to set svsb->volt[] by algorithm */ if (turn_pt < middle_index) { if (svsb->type == SVSB_HIGH) { /* volt[0] ~ volt[turn_pt - 1] */ for (i = 0; i < turn_pt; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); vop = (shift_byte < REG_BYTES) ? &vop30 : &vop74; svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); shift_byte++; } } else if (svsb->type == SVSB_LOW) { /* volt[turn_pt] + volt[j] ~ volt[opp_count - 1] */ j = svsb->opp_count - 7; svsb->volt[turn_pt] = FIELD_GET(SVSB_VOPS_FLD_VOP0_4, vop30); shift_byte++; for (i = j; i < svsb->opp_count; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); vop = (shift_byte < REG_BYTES) ? &vop30 : &vop74; svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); shift_byte++; } /* volt[turn_pt + 1] ~ volt[j - 1] by interpolate */ for (i = turn_pt + 1; i < j; i++) svsb->volt[i] = interpolate(svsb->freq_pct[turn_pt], svsb->freq_pct[j], svsb->volt[turn_pt], svsb->volt[j], svsb->freq_pct[i]); } } else { if (svsb->type == SVSB_HIGH) { /* volt[0] + volt[j] ~ volt[turn_pt - 1] */ j = turn_pt - 7; svsb->volt[0] = FIELD_GET(SVSB_VOPS_FLD_VOP0_4, vop30); shift_byte++; for (i = j; i < turn_pt; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); vop = (shift_byte < REG_BYTES) ? &vop30 : &vop74; svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); shift_byte++; } /* volt[1] ~ volt[j - 1] by interpolate */ for (i = 1; i < j; i++) svsb->volt[i] = interpolate(svsb->freq_pct[0], svsb->freq_pct[j], svsb->volt[0], svsb->volt[j], svsb->freq_pct[i]); } else if (svsb->type == SVSB_LOW) { /* volt[turn_pt] ~ volt[opp_count - 1] */ for (i = turn_pt; i < svsb->opp_count; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); vop = (shift_byte < REG_BYTES) ? &vop30 : &vop74; svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); shift_byte++; } } } if (svsb->type == SVSB_HIGH) { opp_start = 0; opp_stop = svsb->turn_pt; } else if (svsb->type == SVSB_LOW) { opp_start = svsb->turn_pt; opp_stop = svsb->opp_count; } for (i = opp_start; i < opp_stop; i++) if (svsb->volt_flags & SVSB_REMOVE_DVTFIXED_VOLT) svsb->volt[i] -= svsb->dvt_fixed; } static void svs_set_bank_freq_pct_v3(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; u32 i, j, *freq_pct, freq_pct74 = 0, freq_pct30 = 0; u32 b_sft, shift_byte = 0, turn_pt; u32 middle_index = (svsb->opp_count / 2); for (i = 0; i < svsb->opp_count; i++) { if (svsb->opp_dfreq[i] <= svsb->turn_freq_base) { svsb->turn_pt = i; break; } } turn_pt = svsb->turn_pt; /* Target is to fill out freq_pct74 / freq_pct30 by algorithm */ if (turn_pt < middle_index) { if (svsb->type == SVSB_HIGH) { /* * If we don't handle this situation, * SVSB_HIGH's FREQPCT74 / FREQPCT30 would keep "0" * and this leads SVSB_LOW to work abnormally. */ if (turn_pt == 0) freq_pct30 = svsb->freq_pct[0]; /* freq_pct[0] ~ freq_pct[turn_pt - 1] */ for (i = 0; i < turn_pt; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); freq_pct = (shift_byte < REG_BYTES) ? &freq_pct30 : &freq_pct74; *freq_pct |= (svsb->freq_pct[i] << b_sft); shift_byte++; } } else if (svsb->type == SVSB_LOW) { /* * freq_pct[turn_pt] + * freq_pct[opp_count - 7] ~ freq_pct[opp_count -1] */ freq_pct30 = svsb->freq_pct[turn_pt]; shift_byte++; j = svsb->opp_count - 7; for (i = j; i < svsb->opp_count; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); freq_pct = (shift_byte < REG_BYTES) ? &freq_pct30 : &freq_pct74; *freq_pct |= (svsb->freq_pct[i] << b_sft); shift_byte++; } } } else { if (svsb->type == SVSB_HIGH) { /* * freq_pct[0] + * freq_pct[turn_pt - 7] ~ freq_pct[turn_pt - 1] */ freq_pct30 = svsb->freq_pct[0]; shift_byte++; j = turn_pt - 7; for (i = j; i < turn_pt; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); freq_pct = (shift_byte < REG_BYTES) ? &freq_pct30 : &freq_pct74; *freq_pct |= (svsb->freq_pct[i] << b_sft); shift_byte++; } } else if (svsb->type == SVSB_LOW) { /* freq_pct[turn_pt] ~ freq_pct[opp_count - 1] */ for (i = turn_pt; i < svsb->opp_count; i++) { b_sft = BITS8 * (shift_byte % REG_BYTES); freq_pct = (shift_byte < REG_BYTES) ? &freq_pct30 : &freq_pct74; *freq_pct |= (svsb->freq_pct[i] << b_sft); shift_byte++; } } } svs_writel_relaxed(svsp, freq_pct74, FREQPCT74); svs_writel_relaxed(svsp, freq_pct30, FREQPCT30); } static void svs_get_bank_volts_v2(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; u32 temp, i; temp = svs_readl_relaxed(svsp, VOP74); svsb->volt[14] = FIELD_GET(SVSB_VOPS_FLD_VOP3_7, temp); svsb->volt[12] = FIELD_GET(SVSB_VOPS_FLD_VOP2_6, temp); svsb->volt[10] = FIELD_GET(SVSB_VOPS_FLD_VOP1_5, temp); svsb->volt[8] = FIELD_GET(SVSB_VOPS_FLD_VOP0_4, temp); temp = svs_readl_relaxed(svsp, VOP30); svsb->volt[6] = FIELD_GET(SVSB_VOPS_FLD_VOP3_7, temp); svsb->volt[4] = FIELD_GET(SVSB_VOPS_FLD_VOP2_6, temp); svsb->volt[2] = FIELD_GET(SVSB_VOPS_FLD_VOP1_5, temp); svsb->volt[0] = FIELD_GET(SVSB_VOPS_FLD_VOP0_4, temp); for (i = 0; i <= 12; i += 2) svsb->volt[i + 1] = interpolate(svsb->freq_pct[i], svsb->freq_pct[i + 2], svsb->volt[i], svsb->volt[i + 2], svsb->freq_pct[i + 1]); svsb->volt[15] = interpolate(svsb->freq_pct[12], svsb->freq_pct[14], svsb->volt[12], svsb->volt[14], svsb->freq_pct[15]); for (i = 0; i < svsb->opp_count; i++) svsb->volt[i] += svsb->volt_od; } static void svs_set_bank_freq_pct_v2(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; u32 freqpct74_val, freqpct30_val; freqpct74_val = FIELD_PREP(SVSB_FREQPCTS_FLD_PCT0_4, svsb->freq_pct[8]) | FIELD_PREP(SVSB_FREQPCTS_FLD_PCT1_5, svsb->freq_pct[10]) | FIELD_PREP(SVSB_FREQPCTS_FLD_PCT2_6, svsb->freq_pct[12]) | FIELD_PREP(SVSB_FREQPCTS_FLD_PCT3_7, svsb->freq_pct[14]); freqpct30_val = FIELD_PREP(SVSB_FREQPCTS_FLD_PCT0_4, svsb->freq_pct[0]) | FIELD_PREP(SVSB_FREQPCTS_FLD_PCT1_5, svsb->freq_pct[2]) | FIELD_PREP(SVSB_FREQPCTS_FLD_PCT2_6, svsb->freq_pct[4]) | FIELD_PREP(SVSB_FREQPCTS_FLD_PCT3_7, svsb->freq_pct[6]); svs_writel_relaxed(svsp, freqpct74_val, FREQPCT74); svs_writel_relaxed(svsp, freqpct30_val, FREQPCT30); } 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, init2vals, ts_calcs; svs_switch_bank(svsp); des_char = FIELD_PREP(SVSB_DESCHAR_FLD_BDES, svsb->bdes) | FIELD_PREP(SVSB_DESCHAR_FLD_MDES, svsb->mdes); svs_writel_relaxed(svsp, des_char, DESCHAR); temp_char = FIELD_PREP(SVSB_TEMPCHAR_FLD_VCO, svsb->vco) | FIELD_PREP(SVSB_TEMPCHAR_FLD_MTDES, svsb->mtdes) | FIELD_PREP(SVSB_TEMPCHAR_FLD_DVT_FIXED, svsb->dvt_fixed); svs_writel_relaxed(svsp, temp_char, TEMPCHAR); det_char = FIELD_PREP(SVSB_DETCHAR_FLD_DCBDET, svsb->dcbdet) | FIELD_PREP(SVSB_DETCHAR_FLD_DCMDET, svsb->dcmdet); svs_writel_relaxed(svsp, det_char, DETCHAR); svs_writel_relaxed(svsp, svsb->dc_config, DCCONFIG); svs_writel_relaxed(svsp, svsb->age_config, AGECONFIG); svs_writel_relaxed(svsp, SVSB_RUNCONFIG_DEFAULT, RUNCONFIG); svsb->set_freq_pct(svsp); limit_vals = FIELD_PREP(SVSB_LIMITVALS_FLD_DTLO, SVSB_VAL_DTLO) | FIELD_PREP(SVSB_LIMITVALS_FLD_DTHI, SVSB_VAL_DTHI) | FIELD_PREP(SVSB_LIMITVALS_FLD_VMIN, svsb->vmin) | FIELD_PREP(SVSB_LIMITVALS_FLD_VMAX, svsb->vmax); svs_writel_relaxed(svsp, limit_vals, LIMITVALS); svs_writel_relaxed(svsp, SVSB_DET_WINDOW, DETWINDOW); svs_writel_relaxed(svsp, SVSB_DET_MAX, CONFIG); svs_writel_relaxed(svsp, svsb->chk_shift, CHKSHIFT); svs_writel_relaxed(svsp, svsb->ctl0, CTL0); svs_writel_relaxed(svsp, SVSB_INTSTS_VAL_CLEAN, INTSTS); switch (target_phase) { case SVSB_PHASE_INIT01: svs_writel_relaxed(svsp, svsb->vboot, VBOOT); svs_writel_relaxed(svsp, SVSB_INTEN_INIT0x, INTEN); svs_writel_relaxed(svsp, SVSB_PTPEN_INIT01, SVSEN); break; case SVSB_PHASE_INIT02: init2vals = FIELD_PREP(SVSB_INIT2VALS_FLD_AGEVOFFSETIN, svsb->age_voffset_in) | FIELD_PREP(SVSB_INIT2VALS_FLD_DCVOFFSETIN, svsb->dc_voffset_in); svs_writel_relaxed(svsp, SVSB_INTEN_INIT0x, INTEN); svs_writel_relaxed(svsp, init2vals, INIT2VALS); svs_writel_relaxed(svsp, SVSB_PTPEN_INIT02, SVSEN); break; case SVSB_PHASE_MON: ts_calcs = FIELD_PREP(SVSB_TSCALCS_FLD_BTS, svsb->bts) | FIELD_PREP(SVSB_TSCALCS_FLD_MTS, svsb->mts); svs_writel_relaxed(svsp, ts_calcs, TSCALCS); svs_writel_relaxed(svsp, SVSB_INTEN_MONVOPEN, INTEN); svs_writel_relaxed(svsp, SVSB_PTPEN_MON, SVSEN); break; default: dev_err(svsb->dev, "requested unknown target phase: %u\n", target_phase); break; } } static inline void svs_save_bank_register_data(struct svs_platform *svsp, enum svsb_phase phase) { struct svs_bank *svsb = svsp->pbank; enum svs_reg_index rg_i; for (rg_i = DESCHAR; rg_i < SVS_REG_MAX; rg_i++) svsb->reg_data[phase][rg_i] = svs_readl_relaxed(svsp, rg_i); } static inline void svs_error_isr_handler(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; dev_err(svsb->dev, "%s: CORESEL = 0x%08x\n", __func__, svs_readl_relaxed(svsp, CORESEL)); dev_err(svsb->dev, "SVSEN = 0x%08x, INTSTS = 0x%08x\n", svs_readl_relaxed(svsp, SVSEN), svs_readl_relaxed(svsp, INTSTS)); dev_err(svsb->dev, "SMSTATE0 = 0x%08x, SMSTATE1 = 0x%08x\n", svs_readl_relaxed(svsp, SMSTATE0), svs_readl_relaxed(svsp, SMSTATE1)); dev_err(svsb->dev, "TEMP = 0x%08x\n", svs_readl_relaxed(svsp, TEMP)); svs_save_bank_register_data(svsp, SVSB_PHASE_ERROR); svsb->phase = SVSB_PHASE_ERROR; svs_writel_relaxed(svsp, SVSB_PTPEN_OFF, SVSEN); svs_writel_relaxed(svsp, SVSB_INTSTS_VAL_CLEAN, INTSTS); } static inline void svs_init01_isr_handler(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; dev_info(svsb->dev, "%s: VDN74~30:0x%08x~0x%08x, DC:0x%08x\n", __func__, svs_readl_relaxed(svsp, VDESIGN74), svs_readl_relaxed(svsp, VDESIGN30), svs_readl_relaxed(svsp, DCVALUES)); svs_save_bank_register_data(svsp, SVSB_PHASE_INIT01); svsb->phase = SVSB_PHASE_INIT01; svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) & GENMASK(15, 0)) + 1; if (svsb->volt_flags & SVSB_INIT01_VOLT_IGNORE || (svsb->dc_voffset_in & SVSB_DC_SIGNED_BIT && svsb->volt_flags & SVSB_INIT01_VOLT_INC_ONLY)) svsb->dc_voffset_in = 0; svsb->age_voffset_in = svs_readl_relaxed(svsp, AGEVALUES) & GENMASK(15, 0); svs_writel_relaxed(svsp, SVSB_PTPEN_OFF, SVSEN); svs_writel_relaxed(svsp, SVSB_INTSTS_F0_COMPLETE, INTSTS); svsb->core_sel &= ~SVSB_DET_CLK_EN; } static inline void svs_init02_isr_handler(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; dev_info(svsb->dev, "%s: VOP74~30:0x%08x~0x%08x, DC:0x%08x\n", __func__, svs_readl_relaxed(svsp, VOP74), svs_readl_relaxed(svsp, VOP30), svs_readl_relaxed(svsp, DCVALUES)); svs_save_bank_register_data(svsp, SVSB_PHASE_INIT02); svsb->phase = SVSB_PHASE_INIT02; svsb->get_volts(svsp); svs_writel_relaxed(svsp, SVSB_PTPEN_OFF, SVSEN); svs_writel_relaxed(svsp, SVSB_INTSTS_F0_COMPLETE, INTSTS); } static inline void svs_mon_mode_isr_handler(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; svs_save_bank_register_data(svsp, SVSB_PHASE_MON); svsb->phase = SVSB_PHASE_MON; svsb->get_volts(svsp); svsb->temp = svs_readl_relaxed(svsp, TEMP) & GENMASK(7, 0); svs_writel_relaxed(svsp, SVSB_INTSTS_FLD_MONVOP, INTSTS); } static irqreturn_t svs_isr(int irq, void *data) { struct svs_platform *svsp = data; struct svs_bank *svsb = NULL; unsigned long flags; u32 idx, int_sts, svs_en; for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; WARN(!svsb, "%s: svsb(%s) is null", __func__, svsb->name); spin_lock_irqsave(&svs_lock, flags); svsp->pbank = svsb; /* Find out which svs bank fires interrupt */ if (svsb->int_st & svs_readl_relaxed(svsp, INTST)) { spin_unlock_irqrestore(&svs_lock, flags); continue; } svs_switch_bank(svsp); int_sts = svs_readl_relaxed(svsp, INTSTS); svs_en = svs_readl_relaxed(svsp, SVSEN); if (int_sts == SVSB_INTSTS_F0_COMPLETE && svs_en == SVSB_PTPEN_INIT01) svs_init01_isr_handler(svsp); else if (int_sts == SVSB_INTSTS_F0_COMPLETE && svs_en == SVSB_PTPEN_INIT02) svs_init02_isr_handler(svsp); else if (int_sts & SVSB_INTSTS_FLD_MONVOP) svs_mon_mode_isr_handler(svsp); else svs_error_isr_handler(svsp); spin_unlock_irqrestore(&svs_lock, flags); break; } svs_adjust_pm_opp_volts(svsb); if (svsb->phase == SVSB_PHASE_INIT01 || svsb->phase == SVSB_PHASE_INIT02) complete(&svsb->init_completion); return IRQ_HANDLED; } static int svs_init01(struct svs_platform *svsp) { struct svs_bank *svsb; unsigned long flags, time_left; bool search_done; int ret = 0, r; u32 opp_freq, opp_vboot, buck_volt, idx, i; /* Keep CPUs' core power on for svs_init01 initialization */ cpuidle_pause_and_lock(); /* Svs bank init01 preparation - power enable */ for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (!(svsb->mode_support & SVSB_MODE_INIT01)) continue; ret = regulator_enable(svsb->buck); if (ret) { dev_err(svsb->dev, "%s enable fail: %d\n", svsb->buck_name, ret); goto svs_init01_resume_cpuidle; } /* Some buck doesn't support mode change. Show fail msg only */ ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST); if (ret) dev_notice(svsb->dev, "set fast mode fail: %d\n", ret); if (svsb->volt_flags & SVSB_INIT01_PD_REQ) { if (!pm_runtime_enabled(svsb->opp_dev)) { pm_runtime_enable(svsb->opp_dev); svsb->pm_runtime_enabled_count++; } ret = pm_runtime_resume_and_get(svsb->opp_dev); if (ret < 0) { dev_err(svsb->dev, "mtcmos on fail: %d\n", ret); goto svs_init01_resume_cpuidle; } } } /* * Svs bank init01 preparation - vboot voltage adjustment * Sometimes two svs banks use the same buck. Therefore, * we have to set each svs bank to target voltage(vboot) first. */ for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (!(svsb->mode_support & SVSB_MODE_INIT01)) continue; /* * Find the fastest freq that can be run at vboot and * fix to that freq until svs_init01 is done. */ search_done = false; 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_freq = svsb->opp_dfreq[i]; if (!search_done && svsb->opp_dvolt[i] <= opp_vboot) { ret = dev_pm_opp_adjust_voltage(svsb->opp_dev, opp_freq, opp_vboot, opp_vboot, opp_vboot); if (ret) { dev_err(svsb->dev, "set opp %uuV vboot fail: %d\n", opp_vboot, ret); goto svs_init01_finish; } search_done = true; } else { ret = dev_pm_opp_disable(svsb->opp_dev, svsb->opp_dfreq[i]); if (ret) { dev_err(svsb->dev, "opp %uHz disable fail: %d\n", svsb->opp_dfreq[i], ret); goto svs_init01_finish; } } } } /* Svs bank init01 begins */ for (idx = 0; idx < svsp->bank_max; 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: %uuV, expected vboot: %uuV\n", buck_volt, opp_vboot); ret = -EPERM; goto svs_init01_finish; } spin_lock_irqsave(&svs_lock, flags); svsp->pbank = svsb; svs_set_bank_phase(svsp, SVSB_PHASE_INIT01); spin_unlock_irqrestore(&svs_lock, flags); time_left = wait_for_completion_timeout(&svsb->init_completion, msecs_to_jiffies(5000)); if (!time_left) { dev_err(svsb->dev, "init01 completion timeout\n"); ret = -EBUSY; goto svs_init01_finish; } } svs_init01_finish: for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (!(svsb->mode_support & SVSB_MODE_INIT01)) continue; for (i = 0; i < svsb->opp_count; i++) { r = dev_pm_opp_enable(svsb->opp_dev, svsb->opp_dfreq[i]); if (r) dev_err(svsb->dev, "opp %uHz enable fail: %d\n", svsb->opp_dfreq[i], r); } if (svsb->volt_flags & SVSB_INIT01_PD_REQ) { r = pm_runtime_put_sync(svsb->opp_dev); if (r) dev_err(svsb->dev, "mtcmos off fail: %d\n", r); if (svsb->pm_runtime_enabled_count > 0) { pm_runtime_disable(svsb->opp_dev); svsb->pm_runtime_enabled_count--; } } r = regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL); if (r) dev_notice(svsb->dev, "set normal mode fail: %d\n", r); r = regulator_disable(svsb->buck); if (r) dev_err(svsb->dev, "%s disable fail: %d\n", svsb->buck_name, r); } svs_init01_resume_cpuidle: cpuidle_resume_and_unlock(); return ret; } static int svs_init02(struct svs_platform *svsp) { struct svs_bank *svsb; unsigned long flags, time_left; int ret; u32 idx; for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (!(svsb->mode_support & SVSB_MODE_INIT02)) continue; reinit_completion(&svsb->init_completion); spin_lock_irqsave(&svs_lock, flags); svsp->pbank = svsb; svs_set_bank_phase(svsp, SVSB_PHASE_INIT02); spin_unlock_irqrestore(&svs_lock, flags); time_left = wait_for_completion_timeout(&svsb->init_completion, msecs_to_jiffies(5000)); if (!time_left) { dev_err(svsb->dev, "init02 completion timeout\n"); ret = -EBUSY; goto out_of_init02; } } /* * 2-line high/low bank update its corresponding opp voltages only. * Therefore, we sync voltages from opp for high/low bank voltages * consistency. */ for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (!(svsb->mode_support & SVSB_MODE_INIT02)) continue; if (svsb->type == SVSB_HIGH || svsb->type == SVSB_LOW) { if (svs_sync_bank_volts_from_opp(svsb)) { dev_err(svsb->dev, "sync volt fail\n"); ret = -EPERM; goto out_of_init02; } } } return 0; out_of_init02: for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; svs_bank_disable_and_restore_default_volts(svsp, svsb); } return ret; } static void svs_mon_mode(struct svs_platform *svsp) { struct svs_bank *svsb; unsigned long flags; u32 idx; for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (!(svsb->mode_support & SVSB_MODE_MON)) continue; spin_lock_irqsave(&svs_lock, flags); svsp->pbank = svsb; svs_set_bank_phase(svsp, SVSB_PHASE_MON); spin_unlock_irqrestore(&svs_lock, flags); } } static int svs_start(struct svs_platform *svsp) { int ret; ret = svs_init01(svsp); if (ret) return ret; ret = svs_init02(svsp); if (ret) return ret; svs_mon_mode(svsp); return 0; } static int svs_suspend(struct device *dev) { struct svs_platform *svsp = dev_get_drvdata(dev); struct svs_bank *svsb; int ret; u32 idx; for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; svs_bank_disable_and_restore_default_volts(svsp, svsb); } ret = reset_control_assert(svsp->rst); if (ret) { dev_err(svsp->dev, "cannot assert reset %d\n", ret); return ret; } clk_disable_unprepare(svsp->main_clk); return 0; } static int svs_resume(struct device *dev) { struct svs_platform *svsp = dev_get_drvdata(dev); int ret; ret = clk_prepare_enable(svsp->main_clk); if (ret) { dev_err(svsp->dev, "cannot enable main_clk, disable svs\n"); return ret; } ret = reset_control_deassert(svsp->rst); if (ret) { dev_err(svsp->dev, "cannot deassert reset %d\n", ret); goto out_of_resume; } ret = svs_init02(svsp); if (ret) goto svs_resume_reset_assert; svs_mon_mode(svsp); return 0; svs_resume_reset_assert: dev_err(svsp->dev, "assert reset: %d\n", reset_control_assert(svsp->rst)); out_of_resume: clk_disable_unprepare(svsp->main_clk); return ret; } static int svs_bank_resource_setup(struct svs_platform *svsp) { struct svs_bank *svsb; struct dev_pm_opp *opp; unsigned long freq; int count, ret; u32 idx, i; dev_set_drvdata(svsp->dev, svsp); for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; switch (svsb->sw_id) { case SVSB_CPU_LITTLE: svsb->name = "SVSB_CPU_LITTLE"; break; case SVSB_CPU_BIG: svsb->name = "SVSB_CPU_BIG"; break; case SVSB_CCI: svsb->name = "SVSB_CCI"; break; case SVSB_GPU: if (svsb->type == SVSB_HIGH) svsb->name = "SVSB_GPU_HIGH"; else if (svsb->type == SVSB_LOW) svsb->name = "SVSB_GPU_LOW"; else svsb->name = "SVSB_GPU"; break; default: dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); return -EINVAL; } svsb->dev = devm_kzalloc(svsp->dev, sizeof(*svsb->dev), GFP_KERNEL); if (!svsb->dev) return -ENOMEM; ret = dev_set_name(svsb->dev, "%s", svsb->name); if (ret) return ret; dev_set_drvdata(svsb->dev, svsp); ret = devm_pm_opp_of_add_table(svsb->opp_dev); if (ret) { dev_err(svsb->dev, "add opp table fail: %d\n", ret); return ret; } mutex_init(&svsb->lock); init_completion(&svsb->init_completion); if (svsb->mode_support & SVSB_MODE_INIT01) { svsb->buck = devm_regulator_get_optional(svsb->opp_dev, svsb->buck_name); if (IS_ERR(svsb->buck)) { dev_err(svsb->dev, "cannot get \"%s-supply\"\n", svsb->buck_name); return PTR_ERR(svsb->buck); } } if (!IS_ERR_OR_NULL(svsb->tzone_name)) { svsb->tzd = thermal_zone_get_zone_by_name(svsb->tzone_name); if (IS_ERR(svsb->tzd)) { dev_err(svsb->dev, "cannot get \"%s\" thermal zone\n", svsb->tzone_name); return PTR_ERR(svsb->tzd); } } count = dev_pm_opp_get_opp_count(svsb->opp_dev); if (svsb->opp_count != count) { dev_err(svsb->dev, "opp_count not \"%u\" but get \"%d\"?\n", svsb->opp_count, count); return count; } for (i = 0, freq = U32_MAX; i < svsb->opp_count; i++, freq--) { opp = dev_pm_opp_find_freq_floor(svsb->opp_dev, &freq); if (IS_ERR(opp)) { dev_err(svsb->dev, "cannot find freq = %ld\n", PTR_ERR(opp)); return PTR_ERR(opp); } svsb->opp_dfreq[i] = freq; svsb->opp_dvolt[i] = dev_pm_opp_get_voltage(opp); svsb->freq_pct[i] = percent(svsb->opp_dfreq[i], svsb->freq_base); dev_pm_opp_put(opp); } } return 0; } static int svs_get_efuse_data(struct svs_platform *svsp, const char *nvmem_cell_name, u32 **svsp_efuse, size_t *svsp_efuse_max) { struct nvmem_cell *cell; cell = nvmem_cell_get(svsp->dev, nvmem_cell_name); if (IS_ERR(cell)) { dev_err(svsp->dev, "no \"%s\"? %ld\n", nvmem_cell_name, PTR_ERR(cell)); return PTR_ERR(cell); } *svsp_efuse = nvmem_cell_read(cell, svsp_efuse_max); if (IS_ERR(*svsp_efuse)) { dev_err(svsp->dev, "cannot read \"%s\" efuse: %ld\n", nvmem_cell_name, PTR_ERR(*svsp_efuse)); nvmem_cell_put(cell); return PTR_ERR(*svsp_efuse); } *svsp_efuse_max /= sizeof(u32); nvmem_cell_put(cell); return 0; } static bool svs_mt8192_efuse_parsing(struct svs_platform *svsp) { struct svs_bank *svsb; u32 idx, i, vmin, golden_temp; int ret; for (i = 0; i < svsp->efuse_max; i++) if (svsp->efuse[i]) dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n", i, svsp->efuse[i]); if (!svsp->efuse[9]) { dev_notice(svsp->dev, "svs_efuse[9] = 0x0?\n"); return false; } /* Svs efuse parsing */ vmin = (svsp->efuse[19] >> 4) & GENMASK(1, 0); for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (vmin == 0x1) svsb->vmin = 0x1e; if (svsb->type == SVSB_LOW) { svsb->mtdes = svsp->efuse[10] & GENMASK(7, 0); svsb->bdes = (svsp->efuse[10] >> 16) & GENMASK(7, 0); svsb->mdes = (svsp->efuse[10] >> 24) & GENMASK(7, 0); svsb->dcbdet = (svsp->efuse[17]) & GENMASK(7, 0); svsb->dcmdet = (svsp->efuse[17] >> 8) & GENMASK(7, 0); } else if (svsb->type == SVSB_HIGH) { svsb->mtdes = svsp->efuse[9] & GENMASK(7, 0); svsb->bdes = (svsp->efuse[9] >> 16) & GENMASK(7, 0); svsb->mdes = (svsp->efuse[9] >> 24) & GENMASK(7, 0); svsb->dcbdet = (svsp->efuse[17] >> 16) & GENMASK(7, 0); svsb->dcmdet = (svsp->efuse[17] >> 24) & GENMASK(7, 0); } svsb->vmax += svsb->dvt_fixed; } ret = svs_get_efuse_data(svsp, "t-calibration-data", &svsp->tefuse, &svsp->tefuse_max); if (ret) return false; for (i = 0; i < svsp->tefuse_max; i++) if (svsp->tefuse[i] != 0) break; if (i == svsp->tefuse_max) golden_temp = 50; /* All thermal efuse data are 0 */ else golden_temp = (svsp->tefuse[0] >> 24) & GENMASK(7, 0); for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; svsb->mts = 500; svsb->bts = (((500 * golden_temp + 250460) / 1000) - 25) * 4; } return true; } static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp) { struct svs_bank *svsb; int format[6], x_roomt[6], o_vtsmcu[5], o_vtsabb, tb_roomt = 0; int adc_ge_t, adc_oe_t, ge, oe, gain, degc_cali, adc_cali_en_t; int o_slope, o_slope_sign, ts_id; u32 idx, i, ft_pgm, mts, temp0, temp1, temp2; int ret; for (i = 0; i < svsp->efuse_max; i++) if (svsp->efuse[i]) dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n", i, svsp->efuse[i]); if (!svsp->efuse[2]) { dev_notice(svsp->dev, "svs_efuse[2] = 0x0?\n"); return false; } /* Svs efuse parsing */ ft_pgm = (svsp->efuse[0] >> 4) & GENMASK(3, 0); for (idx = 0; idx < svsp->bank_max; 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_od += 10; else svsb->volt_od += 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_od += 15; else svsb->volt_od += 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_od += 10; else svsb->volt_od += 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: dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); return false; } } ret = svs_get_efuse_data(svsp, "t-calibration-data", &svsp->tefuse, &svsp->tefuse_max); if (ret) return false; /* Thermal efuse parsing */ adc_ge_t = (svsp->tefuse[1] >> 22) & GENMASK(9, 0); adc_oe_t = (svsp->tefuse[1] >> 12) & GENMASK(9, 0); o_vtsmcu[0] = (svsp->tefuse[0] >> 17) & GENMASK(8, 0); o_vtsmcu[1] = (svsp->tefuse[0] >> 8) & GENMASK(8, 0); o_vtsmcu[2] = svsp->tefuse[1] & GENMASK(8, 0); o_vtsmcu[3] = (svsp->tefuse[2] >> 23) & GENMASK(8, 0); o_vtsmcu[4] = (svsp->tefuse[2] >> 5) & GENMASK(8, 0); o_vtsabb = (svsp->tefuse[2] >> 14) & GENMASK(8, 0); degc_cali = (svsp->tefuse[0] >> 1) & GENMASK(5, 0); adc_cali_en_t = svsp->tefuse[0] & BIT(0); o_slope_sign = (svsp->tefuse[0] >> 7) & BIT(0); ts_id = (svsp->tefuse[1] >> 9) & BIT(0); if (!ts_id) { o_slope = 1534; } else { o_slope = (svsp->tefuse[0] >> 26) & GENMASK(5, 0); if (!o_slope_sign) o_slope = 1534 + o_slope * 10; else o_slope = 1534 - o_slope * 10; } if (adc_cali_en_t == 0 || adc_ge_t < 265 || adc_ge_t > 758 || adc_oe_t < 265 || adc_oe_t > 758 || o_vtsmcu[0] < -8 || o_vtsmcu[0] > 484 || o_vtsmcu[1] < -8 || o_vtsmcu[1] > 484 || o_vtsmcu[2] < -8 || o_vtsmcu[2] > 484 || o_vtsmcu[3] < -8 || o_vtsmcu[3] > 484 || o_vtsmcu[4] < -8 || o_vtsmcu[4] > 484 || o_vtsabb < -8 || o_vtsabb > 484 || degc_cali < 1 || degc_cali > 63) { dev_err(svsp->dev, "bad thermal efuse, no mon mode\n"); goto remove_mt8183_svsb_mon_mode; } ge = ((adc_ge_t - 512) * 10000) / 4096; oe = (adc_oe_t - 512); gain = (10000 + ge); format[0] = (o_vtsmcu[0] + 3350 - oe); format[1] = (o_vtsmcu[1] + 3350 - oe); format[2] = (o_vtsmcu[2] + 3350 - oe); format[3] = (o_vtsmcu[3] + 3350 - oe); format[4] = (o_vtsmcu[4] + 3350 - oe); format[5] = (o_vtsabb + 3350 - oe); for (i = 0; i < 6; i++) x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / gain; temp0 = (10000 * 100000 / gain) * 15 / 18; mts = (temp0 * 10) / o_slope; for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; svsb->mts = mts; switch (svsb->sw_id) { case SVSB_CPU_LITTLE: tb_roomt = x_roomt[3]; break; case SVSB_CPU_BIG: tb_roomt = x_roomt[4]; break; case SVSB_CCI: tb_roomt = x_roomt[3]; break; case SVSB_GPU: tb_roomt = x_roomt[1]; break; default: dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); goto remove_mt8183_svsb_mon_mode; } temp0 = (degc_cali * 10 / 2); temp1 = ((10000 * 100000 / 4096 / gain) * oe + tb_roomt * 10) * 15 / 18; temp2 = temp1 * 100 / o_slope; svsb->bts = (temp0 + temp2 - 250) * 4 / 10; } return true; remove_mt8183_svsb_mon_mode: for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; svsb->mode_support &= ~SVSB_MODE_MON; } return true; } static struct device *svs_get_subsys_device(struct svs_platform *svsp, const char *node_name) { struct platform_device *pdev; struct device_node *np; np = of_find_node_by_name(NULL, node_name); if (!np) { dev_err(svsp->dev, "cannot find %s node\n", node_name); return ERR_PTR(-ENODEV); } pdev = of_find_device_by_node(np); if (!pdev) { of_node_put(np); dev_err(svsp->dev, "cannot find pdev by %s\n", node_name); return ERR_PTR(-ENXIO); } of_node_put(np); return &pdev->dev; } static struct device *svs_add_device_link(struct svs_platform *svsp, const char *node_name) { struct device *dev; struct device_link *sup_link; dev = svs_get_subsys_device(svsp, node_name); if (IS_ERR(dev)) return dev; sup_link = device_link_add(svsp->dev, dev, DL_FLAG_AUTOREMOVE_CONSUMER); if (!sup_link) { dev_err(svsp->dev, "sup_link is NULL\n"); return ERR_PTR(-EINVAL); } if (sup_link->supplier->links.status != DL_DEV_DRIVER_BOUND) return ERR_PTR(-EPROBE_DEFER); return dev; } static int svs_mt8192_platform_probe(struct svs_platform *svsp) { struct device *dev; struct svs_bank *svsb; u32 idx; svsp->rst = devm_reset_control_get_optional(svsp->dev, "svs_rst"); if (IS_ERR(svsp->rst)) return dev_err_probe(svsp->dev, PTR_ERR(svsp->rst), "cannot get svs reset control\n"); dev = svs_add_device_link(svsp, "lvts"); if (IS_ERR(dev)) return dev_err_probe(svsp->dev, PTR_ERR(dev), "failed to get lvts device\n"); for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; if (svsb->type == SVSB_HIGH) svsb->opp_dev = svs_add_device_link(svsp, "gpu"); else if (svsb->type == SVSB_LOW) svsb->opp_dev = svs_get_subsys_device(svsp, "gpu"); if (IS_ERR(svsb->opp_dev)) return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), "failed to get OPP device for bank %d\n", idx); } return 0; } static int svs_mt8183_platform_probe(struct svs_platform *svsp) { struct device *dev; struct svs_bank *svsb; u32 idx; dev = svs_add_device_link(svsp, "thermal"); if (IS_ERR(dev)) return dev_err_probe(svsp->dev, PTR_ERR(dev), "failed to get thermal device\n"); for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; switch (svsb->sw_id) { case SVSB_CPU_LITTLE: case SVSB_CPU_BIG: svsb->opp_dev = get_cpu_device(svsb->cpu_id); break; case SVSB_CCI: svsb->opp_dev = svs_add_device_link(svsp, "cci"); break; case SVSB_GPU: svsb->opp_dev = svs_add_device_link(svsp, "gpu"); break; default: dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); return -EINVAL; } if (IS_ERR(svsb->opp_dev)) return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), "failed to get OPP device for bank %d\n", idx); } return 0; } static struct svs_bank svs_mt8192_banks[] = { { .sw_id = SVSB_GPU, .type = SVSB_LOW, .set_freq_pct = svs_set_bank_freq_pct_v3, .get_volts = svs_get_bank_volts_v3, .tzone_name = "gpu1", .volt_flags = SVSB_REMOVE_DVTFIXED_VOLT, .mode_support = SVSB_MODE_INIT02, .opp_count = MAX_OPP_ENTRIES, .freq_base = 688000000, .turn_freq_base = 688000000, .volt_step = 6250, .volt_base = 400000, .vmax = 0x60, .vmin = 0x1a, .age_config = 0x555555, .dc_config = 0x1, .dvt_fixed = 0x1, .vco = 0x18, .chk_shift = 0x87, .core_sel = 0x0fff0100, .int_st = BIT(0), .ctl0 = 0x00540003, .tzone_htemp = 85000, .tzone_htemp_voffset = 0, .tzone_ltemp = 25000, .tzone_ltemp_voffset = 7, }, { .sw_id = SVSB_GPU, .type = SVSB_HIGH, .set_freq_pct = svs_set_bank_freq_pct_v3, .get_volts = svs_get_bank_volts_v3, .tzone_name = "gpu1", .volt_flags = SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, .mode_support = SVSB_MODE_INIT02 | SVSB_MODE_MON, .opp_count = MAX_OPP_ENTRIES, .freq_base = 902000000, .turn_freq_base = 688000000, .volt_step = 6250, .volt_base = 400000, .vmax = 0x60, .vmin = 0x1a, .age_config = 0x555555, .dc_config = 0x1, .dvt_fixed = 0x6, .vco = 0x18, .chk_shift = 0x87, .core_sel = 0x0fff0101, .int_st = BIT(1), .ctl0 = 0x00540003, .tzone_htemp = 85000, .tzone_htemp_voffset = 0, .tzone_ltemp = 25000, .tzone_ltemp_voffset = 7, }, }; static struct svs_bank svs_mt8183_banks[] = { { .sw_id = SVSB_CPU_LITTLE, .set_freq_pct = svs_set_bank_freq_pct_v2, .get_volts = svs_get_bank_volts_v2, .cpu_id = 0, .buck_name = "proc", .volt_flags = SVSB_INIT01_VOLT_INC_ONLY, .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, .opp_count = MAX_OPP_ENTRIES, .freq_base = 1989000000, .vboot = 0x30, .volt_step = 6250, .volt_base = 500000, .vmax = 0x64, .vmin = 0x18, .age_config = 0x555555, .dc_config = 0x555555, .dvt_fixed = 0x7, .vco = 0x10, .chk_shift = 0x77, .core_sel = 0x8fff0000, .int_st = BIT(0), .ctl0 = 0x00010001, }, { .sw_id = SVSB_CPU_BIG, .set_freq_pct = svs_set_bank_freq_pct_v2, .get_volts = svs_get_bank_volts_v2, .cpu_id = 4, .buck_name = "proc", .volt_flags = SVSB_INIT01_VOLT_INC_ONLY, .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, .opp_count = MAX_OPP_ENTRIES, .freq_base = 1989000000, .vboot = 0x30, .volt_step = 6250, .volt_base = 500000, .vmax = 0x58, .vmin = 0x10, .age_config = 0x555555, .dc_config = 0x555555, .dvt_fixed = 0x7, .vco = 0x10, .chk_shift = 0x77, .core_sel = 0x8fff0001, .int_st = BIT(1), .ctl0 = 0x00000001, }, { .sw_id = SVSB_CCI, .set_freq_pct = svs_set_bank_freq_pct_v2, .get_volts = svs_get_bank_volts_v2, .buck_name = "proc", .volt_flags = SVSB_INIT01_VOLT_INC_ONLY, .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, .opp_count = MAX_OPP_ENTRIES, .freq_base = 1196000000, .vboot = 0x30, .volt_step = 6250, .volt_base = 500000, .vmax = 0x64, .vmin = 0x18, .age_config = 0x555555, .dc_config = 0x555555, .dvt_fixed = 0x7, .vco = 0x10, .chk_shift = 0x77, .core_sel = 0x8fff0002, .int_st = BIT(2), .ctl0 = 0x00100003, }, { .sw_id = SVSB_GPU, .set_freq_pct = svs_set_bank_freq_pct_v2, .get_volts = svs_get_bank_volts_v2, .buck_name = "mali", .tzone_name = "tzts2", .volt_flags = SVSB_INIT01_PD_REQ | SVSB_INIT01_VOLT_INC_ONLY, .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02 | SVSB_MODE_MON, .opp_count = MAX_OPP_ENTRIES, .freq_base = 900000000, .vboot = 0x30, .volt_step = 6250, .volt_base = 500000, .vmax = 0x40, .vmin = 0x14, .age_config = 0x555555, .dc_config = 0x555555, .dvt_fixed = 0x3, .vco = 0x10, .chk_shift = 0x77, .core_sel = 0x8fff0003, .int_st = BIT(3), .ctl0 = 0x00050001, .tzone_htemp = 85000, .tzone_htemp_voffset = 0, .tzone_ltemp = 25000, .tzone_ltemp_voffset = 3, }, }; static const struct svs_platform_data svs_mt8192_platform_data = { .name = "mt8192-svs", .banks = svs_mt8192_banks, .efuse_parsing = svs_mt8192_efuse_parsing, .probe = svs_mt8192_platform_probe, .regs = svs_regs_v2, .bank_max = ARRAY_SIZE(svs_mt8192_banks), }; static const struct svs_platform_data svs_mt8183_platform_data = { .name = "mt8183-svs", .banks = svs_mt8183_banks, .efuse_parsing = svs_mt8183_efuse_parsing, .probe = svs_mt8183_platform_probe, .regs = svs_regs_v2, .bank_max = ARRAY_SIZE(svs_mt8183_banks), }; static const struct of_device_id svs_of_match[] = { { .compatible = "mediatek,mt8192-svs", .data = &svs_mt8192_platform_data, }, { .compatible = "mediatek,mt8183-svs", .data = &svs_mt8183_platform_data, }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, svs_of_match); static int svs_probe(struct platform_device *pdev) { struct svs_platform *svsp; const struct svs_platform_data *svsp_data; int ret, svsp_irq; svsp_data = of_device_get_match_data(&pdev->dev); svsp = devm_kzalloc(&pdev->dev, sizeof(*svsp), GFP_KERNEL); if (!svsp) return -ENOMEM; svsp->dev = &pdev->dev; svsp->banks = svsp_data->banks; svsp->regs = svsp_data->regs; svsp->bank_max = svsp_data->bank_max; ret = svsp_data->probe(svsp); if (ret) return ret; ret = svs_get_efuse_data(svsp, "svs-calibration-data", &svsp->efuse, &svsp->efuse_max); if (ret) { ret = -EPERM; goto svs_probe_free_efuse; } if (!svsp_data->efuse_parsing(svsp)) { dev_err(svsp->dev, "efuse data parsing failed\n"); ret = -EPERM; goto svs_probe_free_tefuse; } ret = svs_bank_resource_setup(svsp); if (ret) { dev_err(svsp->dev, "svs bank resource setup fail: %d\n", ret); goto svs_probe_free_tefuse; } svsp_irq = platform_get_irq(pdev, 0); if (svsp_irq < 0) { ret = svsp_irq; goto svs_probe_free_tefuse; } svsp->main_clk = devm_clk_get(svsp->dev, "main"); if (IS_ERR(svsp->main_clk)) { dev_err(svsp->dev, "failed to get clock: %ld\n", PTR_ERR(svsp->main_clk)); ret = PTR_ERR(svsp->main_clk); goto svs_probe_free_tefuse; } ret = clk_prepare_enable(svsp->main_clk); if (ret) { dev_err(svsp->dev, "cannot enable main clk: %d\n", ret); goto svs_probe_free_tefuse; } svsp->base = of_iomap(svsp->dev->of_node, 0); if (IS_ERR_OR_NULL(svsp->base)) { dev_err(svsp->dev, "cannot find svs register base\n"); ret = -EINVAL; goto svs_probe_clk_disable; } ret = devm_request_threaded_irq(svsp->dev, svsp_irq, NULL, svs_isr, IRQF_ONESHOT, svsp_data->name, svsp); if (ret) { dev_err(svsp->dev, "register irq(%d) failed: %d\n", svsp_irq, ret); goto svs_probe_iounmap; } ret = svs_start(svsp); if (ret) { dev_err(svsp->dev, "svs start fail: %d\n", ret); goto svs_probe_iounmap; } #ifdef CONFIG_DEBUG_FS ret = svs_create_debug_cmds(svsp); if (ret) { dev_err(svsp->dev, "svs create debug cmds fail: %d\n", ret); goto svs_probe_iounmap; } #endif return 0; svs_probe_iounmap: iounmap(svsp->base); svs_probe_clk_disable: clk_disable_unprepare(svsp->main_clk); svs_probe_free_tefuse: if (!IS_ERR_OR_NULL(svsp->tefuse)) kfree(svsp->tefuse); svs_probe_free_efuse: if (!IS_ERR_OR_NULL(svsp->efuse)) kfree(svsp->efuse); return ret; } static DEFINE_SIMPLE_DEV_PM_OPS(svs_pm_ops, svs_suspend, svs_resume); static struct platform_driver svs_driver = { .probe = svs_probe, .driver = { .name = "mtk-svs", .pm = &svs_pm_ops, .of_match_table = svs_of_match, }, }; module_platform_driver(svs_driver); MODULE_AUTHOR("Roger Lu <roger.lu@mediatek.com>"); MODULE_DESCRIPTION("MediaTek SVS driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1