| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Copyright (C) 2022 MediaTek Inc. | 
 |  * Copyright (C) 2022 Collabora Ltd. | 
 |  *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> | 
 |  */ | 
 |  | 
 | #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 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 Thermal Coefficients */ | 
 | #define SVSB_TS_COEFF_MT8195		250460 | 
 | #define SVSB_TS_COEFF_MT8186		204650 | 
 |  | 
 | /* Algo helpers */ | 
 | #define FUSE_DATA_NOT_VALID		U32_MAX | 
 |  | 
 | /* 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_sw_id - SVS Bank Software ID | 
 |  * @SVSB_SWID_CPU_LITTLE: CPU little cluster Bank | 
 |  * @SVSB_SWID_CPU_BIG:    CPU big cluster Bank | 
 |  * @SVSB_SWID_CCI:        Cache Coherent Interconnect Bank | 
 |  * @SVSB_SWID_GPU:        GPU Bank | 
 |  * @SVSB_SWID_MAX:        Total number of Banks | 
 |  */ | 
 | enum svsb_sw_id { | 
 | 	SVSB_SWID_CPU_LITTLE, | 
 | 	SVSB_SWID_CPU_BIG, | 
 | 	SVSB_SWID_CCI, | 
 | 	SVSB_SWID_GPU, | 
 | 	SVSB_SWID_MAX | 
 | }; | 
 |  | 
 | /** | 
 |  * enum svsb_type - SVS Bank 2-line: Type and Role | 
 |  * @SVSB_TYPE_NONE: One-line type Bank - Global role | 
 |  * @SVSB_TYPE_LOW:  Two-line type Bank - Low bank role | 
 |  * @SVSB_TYPE_HIGH: Two-line type Bank - High bank role | 
 |  * @SVSB_TYPE_MAX:  Total number of bank types | 
 |  */ | 
 | enum svsb_type { | 
 | 	SVSB_TYPE_NONE, | 
 | 	SVSB_TYPE_LOW, | 
 | 	SVSB_TYPE_HIGH, | 
 | 	SVSB_TYPE_MAX | 
 | }; | 
 |  | 
 | /** | 
 |  * 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]		= 0x00, | 
 | 	[TEMPCHAR]		= 0x04, | 
 | 	[DETCHAR]		= 0x08, | 
 | 	[AGECHAR]		= 0x0c, | 
 | 	[DCCONFIG]		= 0x10, | 
 | 	[AGECONFIG]		= 0x14, | 
 | 	[FREQPCT30]		= 0x18, | 
 | 	[FREQPCT74]		= 0x1c, | 
 | 	[LIMITVALS]		= 0x20, | 
 | 	[VBOOT]			= 0x24, | 
 | 	[DETWINDOW]		= 0x28, | 
 | 	[CONFIG]		= 0x2c, | 
 | 	[TSCALCS]		= 0x30, | 
 | 	[RUNCONFIG]		= 0x34, | 
 | 	[SVSEN]			= 0x38, | 
 | 	[INIT2VALS]		= 0x3c, | 
 | 	[DCVALUES]		= 0x40, | 
 | 	[AGEVALUES]		= 0x44, | 
 | 	[VOP30]			= 0x48, | 
 | 	[VOP74]			= 0x4c, | 
 | 	[TEMP]			= 0x50, | 
 | 	[INTSTS]		= 0x54, | 
 | 	[INTSTSRAW]		= 0x58, | 
 | 	[INTEN]			= 0x5c, | 
 | 	[CHKINT]		= 0x60, | 
 | 	[CHKSHIFT]		= 0x64, | 
 | 	[STATUS]		= 0x68, | 
 | 	[VDESIGN30]		= 0x6c, | 
 | 	[VDESIGN74]		= 0x70, | 
 | 	[DVT30]			= 0x74, | 
 | 	[DVT74]			= 0x78, | 
 | 	[AGECOUNT]		= 0x7c, | 
 | 	[SMSTATE0]		= 0x80, | 
 | 	[SMSTATE1]		= 0x84, | 
 | 	[CTL0]			= 0x88, | 
 | 	[DESDETSEC]		= 0xe0, | 
 | 	[TEMPAGESEC]		= 0xe4, | 
 | 	[CTRLSPARE0]		= 0xf0, | 
 | 	[CTRLSPARE1]		= 0xf4, | 
 | 	[CTRLSPARE2]		= 0xf8, | 
 | 	[CTRLSPARE3]		= 0xfc, | 
 | 	[CORESEL]		= 0x300, | 
 | 	[THERMINTST]		= 0x304, | 
 | 	[INTST]			= 0x308, | 
 | 	[THSTAGE0ST]		= 0x30c, | 
 | 	[THSTAGE1ST]		= 0x310, | 
 | 	[THSTAGE2ST]		= 0x314, | 
 | 	[THAHBST0]		= 0x318, | 
 | 	[THAHBST1]		= 0x31c, | 
 | 	[SPARE0]		= 0x320, | 
 | 	[SPARE1]		= 0x324, | 
 | 	[SPARE2]		= 0x328, | 
 | 	[SPARE3]		= 0x32c, | 
 | 	[THSLPEVEB]		= 0x330, | 
 | }; | 
 |  | 
 | static const char * const svs_swid_names[SVSB_SWID_MAX] = { | 
 | 	"SVSB_CPU_LITTLE", "SVSB_CPU_BIG", "SVSB_CCI", "SVSB_GPU" | 
 | }; | 
 |  | 
 | static const char * const svs_type_names[SVSB_TYPE_MAX] = { | 
 | 	"", "_LOW", "_HIGH" | 
 | }; | 
 |  | 
 | enum svs_fusemap_dev { | 
 | 	BDEV_BDES, | 
 | 	BDEV_MDES, | 
 | 	BDEV_MTDES, | 
 | 	BDEV_DCBDET, | 
 | 	BDEV_DCMDET, | 
 | 	BDEV_MAX | 
 | }; | 
 |  | 
 | enum svs_fusemap_glb { | 
 | 	GLB_FT_PGM, | 
 | 	GLB_VMIN, | 
 | 	GLB_MAX | 
 | }; | 
 |  | 
 | struct svs_fusemap { | 
 | 	s8 index; | 
 | 	u8 ofst; | 
 | }; | 
 |  | 
 | /** | 
 |  * struct svs_platform - svs platform control | 
 |  * @base: svs platform register base | 
 |  * @dev: svs platform device | 
 |  * @main_clk: main clock for svs bank | 
 |  * @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 | 
 |  * @efuse: svs efuse data received from NVMEM framework | 
 |  * @tefuse: thermal efuse data received from NVMEM framework | 
 |  * @ts_coeff: thermal sensors coefficient | 
 |  * @bank_max: total number of svs banks | 
 |  */ | 
 | struct svs_platform { | 
 | 	void __iomem *base; | 
 | 	struct device *dev; | 
 | 	struct clk *main_clk; | 
 | 	struct svs_bank *banks; | 
 | 	struct reset_control *rst; | 
 | 	size_t efuse_max; | 
 | 	size_t tefuse_max; | 
 | 	const u32 *regs; | 
 | 	u32 *efuse; | 
 | 	u32 *tefuse; | 
 | 	u32 ts_coeff; | 
 | 	u16 bank_max; | 
 | }; | 
 |  | 
 | struct svs_platform_data { | 
 | 	char *name; | 
 | 	struct svs_bank *banks; | 
 | 	bool (*efuse_parsing)(struct svs_platform *svsp, const struct svs_platform_data *pdata); | 
 | 	int (*probe)(struct svs_platform *svsp); | 
 | 	const struct svs_fusemap *glb_fuse_map; | 
 | 	const u32 *regs; | 
 | 	u32 ts_coeff; | 
 | 	u16 bank_max; | 
 | }; | 
 |  | 
 | /** | 
 |  * struct svs_bank_pdata - SVS Bank immutable config parameters | 
 |  * @dev_fuse_map: Bank fuse map data | 
 |  * @buck_name: Regulator name | 
 |  * @tzone_name: Thermal zone name | 
 |  * @age_config: Bank age configuration | 
 |  * @ctl0: TS-x selection | 
 |  * @dc_config: Bank dc configuration | 
 |  * @int_st: Bank interrupt identification | 
 |  * @turn_freq_base: Reference frequency for 2-line turn point | 
 |  * @tzone_htemp: Thermal zone high temperature threshold | 
 |  * @tzone_ltemp: Thermal zone low temperature threshold | 
 |  * @volt_step: Bank voltage step | 
 |  * @volt_base: Bank voltage base | 
 |  * @tzone_htemp_voffset: Thermal zone high temperature voltage offset | 
 |  * @tzone_ltemp_voffset: Thermal zone low temperature voltage offset | 
 |  * @chk_shift: Bank chicken shift | 
 |  * @cpu_id: CPU core ID for SVS CPU bank use only | 
 |  * @opp_count: Bank opp count | 
 |  * @vboot: Voltage request for bank init01 only | 
 |  * @vco: Bank VCO value | 
 |  * @sw_id: Bank software identification | 
 |  * @type: SVS Bank Type (1 or 2-line) and Role (high/low) | 
 |  * @set_freq_pct: function pointer to set bank frequency percent table | 
 |  * @get_volts: function pointer to get bank voltages | 
 |  */ | 
 | struct svs_bank_pdata { | 
 | 	const struct svs_fusemap *dev_fuse_map; | 
 | 	char *buck_name; | 
 | 	char *tzone_name; | 
 | 	u32 age_config; | 
 | 	u32 ctl0; | 
 | 	u32 dc_config; | 
 | 	u32 int_st; | 
 | 	u32 turn_freq_base; | 
 | 	u32 tzone_htemp; | 
 | 	u32 tzone_ltemp; | 
 | 	u32 volt_step; | 
 | 	u32 volt_base; | 
 | 	u16 tzone_htemp_voffset; | 
 | 	u16 tzone_ltemp_voffset; | 
 | 	u8 chk_shift; | 
 | 	u8 cpu_id; | 
 | 	u8 opp_count; | 
 | 	u8 vboot; | 
 | 	u8 vco; | 
 | 	u8 sw_id; | 
 | 	u8 type; | 
 |  | 
 | 	/* Callbacks */ | 
 | 	void (*set_freq_pct)(struct svs_platform *svsp, struct svs_bank *svsb); | 
 | 	void (*get_volts)(struct svs_platform *svsp, struct svs_bank *svsb); | 
 | }; | 
 |  | 
 | /** | 
 |  * struct svs_bank - svs bank representation | 
 |  * @pdata: SVS Bank immutable config parameters | 
 |  * @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 | 
 |  * @name: bank 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 | 
 |  * @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_flags: bank voltage flags | 
 |  * @vmax: bank voltage maximum | 
 |  * @vmin: bank voltage minimum | 
 |  * @age_voffset_in: bank age voltage offset | 
 |  * @dc_voffset_in: bank dc voltage offset | 
 |  * @dvt_fixed: bank dvt fixed value | 
 |  * @core_sel: bank selection | 
 |  * @temp: bank temperature | 
 |  * @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 | 
 |  * @vbin_turn_pt: voltage bin turn point helps know which svsb_volt should be overridden | 
 |  * | 
 |  * Svs bank will generate suitable 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 { | 
 | 	const struct svs_bank_pdata pdata; | 
 | 	struct device *dev; | 
 | 	struct device *opp_dev; | 
 | 	struct completion init_completion; | 
 | 	struct regulator *buck; | 
 | 	struct thermal_zone_device *tzd; | 
 | 	struct mutex lock; | 
 | 	int pm_runtime_enabled_count; | 
 | 	short int volt_od; | 
 | 	char *name; | 
 | 	enum svsb_phase phase; | 
 | 	u32 reg_data[SVSB_PHASE_MAX][SVS_REG_MAX]; | 
 | 	u8 mode_support; | 
 | 	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_flags; | 
 | 	u32 freq_base; | 
 | 	u32 turn_pt; | 
 | 	u32 vbin_turn_pt; | 
 | 	u32 core_sel; | 
 | 	u32 temp; | 
 | 	u16 age_voffset_in; | 
 | 	u16 dc_voffset_in; | 
 | 	u8 dvt_fixed; | 
 | 	u8 vmax; | 
 | 	u8 vmin; | 
 | 	u16 bts; | 
 | 	u16 mts; | 
 | 	u16 bdes; | 
 | 	u16 mdes; | 
 | 	u8 mtdes; | 
 | 	u8 dcbdet; | 
 | 	u8 dcmdet; | 
 | }; | 
 |  | 
 | 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) | 
 | { | 
 | 	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) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 	struct dev_pm_opp *opp; | 
 | 	u32 i, opp_u_volt; | 
 |  | 
 | 	for (i = 0; i < bdata->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, | 
 | 							  bdata->volt_step, | 
 | 							  bdata->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; | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 	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 (bdata->type == SVSB_TYPE_HIGH) { | 
 | 		opp_start = 0; | 
 | 		opp_stop = svsb->turn_pt; | 
 | 	} else if (bdata->type == SVSB_TYPE_LOW) { | 
 | 		opp_start = svsb->turn_pt; | 
 | 		opp_stop = bdata->opp_count; | 
 | 	} else { | 
 | 		opp_start = 0; | 
 | 		opp_stop = bdata->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", | 
 | 				bdata->tzone_name, ret, svsb->temp); | 
 | 			svsb->phase = SVSB_PHASE_ERROR; | 
 | 		} | 
 |  | 
 | 		if (tzone_temp >= bdata->tzone_htemp) | 
 | 			temp_voffset += bdata->tzone_htemp_voffset; | 
 | 		else if (tzone_temp <= bdata->tzone_ltemp) | 
 | 			temp_voffset += bdata->tzone_ltemp_voffset; | 
 |  | 
 | 		/* 2-line bank update all opp volts when running mon mode */ | 
 | 		if (svsb->phase == SVSB_PHASE_MON && (bdata->type == SVSB_TYPE_HIGH || | 
 | 						      bdata->type == SVSB_TYPE_LOW)) { | 
 | 			opp_start = 0; | 
 | 			opp_stop = bdata->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, | 
 | 							     bdata->volt_step, | 
 | 							     bdata->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); | 
 | 	svs_switch_bank(svsp, svsb); | 
 | 	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, vbin_turn_pt = %u, turn_pt = %u\n", | 
 | 			   svsb->name, svsb->vbin_turn_pt, svsb->turn_pt); | 
 | 	else | 
 | 		seq_printf(m, "%s: temperature = %d, vbin_turn_pt = %u, turn_pt = %u\n", | 
 | 			   svsb->name, tzone_temp, svsb->vbin_turn_pt, | 
 | 			   svsb->turn_pt); | 
 |  | 
 | 	for (i = 0; i < svsb->pdata.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) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 	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 = (bdata->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 (bdata->type == SVSB_TYPE_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 (bdata->type == SVSB_TYPE_LOW) { | 
 | 			/* volt[turn_pt] + volt[j] ~ volt[opp_count - 1] */ | 
 | 			j = bdata->opp_count - 7; | 
 | 			svsb->volt[turn_pt] = FIELD_GET(SVSB_VOPS_FLD_VOP0_4, vop30); | 
 | 			shift_byte++; | 
 | 			for (i = j; i < bdata->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 (bdata->type == SVSB_TYPE_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 (bdata->type == SVSB_TYPE_LOW) { | 
 | 			/* volt[turn_pt] ~ volt[opp_count - 1] */ | 
 | 			for (i = turn_pt; i < bdata->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 (bdata->type == SVSB_TYPE_HIGH) { | 
 | 		opp_start = 0; | 
 | 		opp_stop = svsb->turn_pt; | 
 | 	} else if (bdata->type == SVSB_TYPE_LOW) { | 
 | 		opp_start = svsb->turn_pt; | 
 | 		opp_stop = bdata->opp_count; | 
 | 	} | 
 |  | 
 | 	for (i = opp_start; i < opp_stop; i++) | 
 | 		if (svsb->volt_flags & SVSB_REMOVE_DVTFIXED_VOLT) | 
 | 			svsb->volt[i] -= svsb->dvt_fixed; | 
 |  | 
 | 	/* For voltage bin support */ | 
 | 	if (svsb->opp_dfreq[0] > svsb->freq_base) { | 
 | 		svsb->volt[0] = svs_opp_volt_to_bank_volt(svsb->opp_dvolt[0], | 
 | 							  bdata->volt_step, | 
 | 							  bdata->volt_base); | 
 |  | 
 | 		/* Find voltage bin turn point */ | 
 | 		for (i = 0; i < bdata->opp_count; i++) { | 
 | 			if (svsb->opp_dfreq[i] <= svsb->freq_base) { | 
 | 				svsb->vbin_turn_pt = i; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* Override svs bank voltages */ | 
 | 		for (i = 1; i < svsb->vbin_turn_pt; i++) | 
 | 			svsb->volt[i] = interpolate(svsb->freq_pct[0], | 
 | 						    svsb->freq_pct[svsb->vbin_turn_pt], | 
 | 						    svsb->volt[0], | 
 | 						    svsb->volt[svsb->vbin_turn_pt], | 
 | 						    svsb->freq_pct[i]); | 
 | 	} | 
 | } | 
 |  | 
 | static void svs_set_bank_freq_pct_v3(struct svs_platform *svsp, struct svs_bank *svsb) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 	u32 i, j, *freq_pct, freq_pct74 = 0, freq_pct30 = 0; | 
 | 	u32 b_sft, shift_byte = 0, turn_pt; | 
 | 	u32 middle_index = (bdata->opp_count / 2); | 
 |  | 
 | 	for (i = 0; i < bdata->opp_count; i++) { | 
 | 		if (svsb->opp_dfreq[i] <= bdata->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 (bdata->type == SVSB_TYPE_HIGH) { | 
 | 			/* | 
 | 			 * If we don't handle this situation, | 
 | 			 * SVSB_TYPE_HIGH's FREQPCT74 / FREQPCT30 would keep "0" | 
 | 			 * and this leads SVSB_TYPE_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 (bdata->type == SVSB_TYPE_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 = bdata->opp_count - 7; | 
 | 			for (i = j; i < bdata->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 (bdata->type == SVSB_TYPE_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 (bdata->type == SVSB_TYPE_LOW) { | 
 | 			/* freq_pct[turn_pt] ~ freq_pct[opp_count - 1] */ | 
 | 			for (i = turn_pt; i < bdata->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) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 	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 < bdata->opp_count; i++) | 
 | 		svsb->volt[i] += svsb->volt_od; | 
 |  | 
 | 	/* For voltage bin support */ | 
 | 	if (svsb->opp_dfreq[0] > svsb->freq_base) { | 
 | 		svsb->volt[0] = svs_opp_volt_to_bank_volt(svsb->opp_dvolt[0], | 
 | 							  bdata->volt_step, | 
 | 							  bdata->volt_base); | 
 |  | 
 | 		/* Find voltage bin turn point */ | 
 | 		for (i = 0; i < bdata->opp_count; i++) { | 
 | 			if (svsb->opp_dfreq[i] <= svsb->freq_base) { | 
 | 				svsb->vbin_turn_pt = i; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* Override svs bank voltages */ | 
 | 		for (i = 1; i < svsb->vbin_turn_pt; i++) | 
 | 			svsb->volt[i] = interpolate(svsb->freq_pct[0], | 
 | 						    svsb->freq_pct[svsb->vbin_turn_pt], | 
 | 						    svsb->volt[0], | 
 | 						    svsb->volt[svsb->vbin_turn_pt], | 
 | 						    svsb->freq_pct[i]); | 
 | 	} | 
 | } | 
 |  | 
 | static void svs_set_bank_freq_pct_v2(struct svs_platform *svsp, struct svs_bank *svsb) | 
 | { | 
 | 	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, | 
 | 			       unsigned int bank_idx, | 
 | 			       enum svsb_phase target_phase) | 
 | { | 
 | 	struct svs_bank *svsb = &svsp->banks[bank_idx]; | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 	u32 des_char, temp_char, det_char, limit_vals, init2vals, ts_calcs; | 
 |  | 
 | 	svs_switch_bank(svsp, svsb); | 
 |  | 
 | 	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, bdata->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, bdata->dc_config, DCCONFIG); | 
 | 	svs_writel_relaxed(svsp, bdata->age_config, AGECONFIG); | 
 | 	svs_writel_relaxed(svsp, SVSB_RUNCONFIG_DEFAULT, RUNCONFIG); | 
 |  | 
 | 	bdata->set_freq_pct(svsp, svsb); | 
 |  | 
 | 	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, bdata->chk_shift, CHKSHIFT); | 
 | 	svs_writel_relaxed(svsp, bdata->ctl0, CTL0); | 
 | 	svs_writel_relaxed(svsp, SVSB_INTSTS_VAL_CLEAN, INTSTS); | 
 |  | 
 | 	switch (target_phase) { | 
 | 	case SVSB_PHASE_INIT01: | 
 | 		svs_writel_relaxed(svsp, bdata->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, | 
 | 					       unsigned short bank_idx, | 
 | 					       enum svsb_phase phase) | 
 | { | 
 | 	struct svs_bank *svsb = &svsp->banks[bank_idx]; | 
 | 	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, | 
 | 					 unsigned short bank_idx) | 
 | { | 
 | 	struct svs_bank *svsb = &svsp->banks[bank_idx]; | 
 |  | 
 | 	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, bank_idx, 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, | 
 | 					  unsigned short bank_idx) | 
 | { | 
 | 	struct svs_bank *svsb = &svsp->banks[bank_idx]; | 
 | 	u32 val; | 
 |  | 
 | 	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, bank_idx, SVSB_PHASE_INIT01); | 
 |  | 
 | 	svsb->phase = SVSB_PHASE_INIT01; | 
 | 	val = ~(svs_readl_relaxed(svsp, DCVALUES) & GENMASK(15, 0)) + 1; | 
 | 	svsb->dc_voffset_in = val & GENMASK(15, 0); | 
 | 	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, | 
 | 					  unsigned short bank_idx) | 
 | { | 
 | 	struct svs_bank *svsb = &svsp->banks[bank_idx]; | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 |  | 
 | 	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, bank_idx, SVSB_PHASE_INIT02); | 
 |  | 
 | 	svsb->phase = SVSB_PHASE_INIT02; | 
 | 	bdata->get_volts(svsp, svsb); | 
 |  | 
 | 	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, | 
 | 					    unsigned short bank_idx) | 
 | { | 
 | 	struct svs_bank *svsb = &svsp->banks[bank_idx]; | 
 | 	const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 |  | 
 | 	svs_save_bank_register_data(svsp, bank_idx, SVSB_PHASE_MON); | 
 |  | 
 | 	svsb->phase = SVSB_PHASE_MON; | 
 | 	bdata->get_volts(svsp, svsb); | 
 |  | 
 | 	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; | 
 | 	const struct svs_bank_pdata *bdata; | 
 | 	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]; | 
 | 		bdata = &svsb->pdata; | 
 | 		WARN(!svsb, "%s: svsb(%s) is null", __func__, svsb->name); | 
 |  | 
 | 		spin_lock_irqsave(&svs_lock, flags); | 
 |  | 
 | 		/* Find out which svs bank fires interrupt */ | 
 | 		if (bdata->int_st & svs_readl_relaxed(svsp, INTST)) { | 
 | 			spin_unlock_irqrestore(&svs_lock, flags); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		svs_switch_bank(svsp, svsb); | 
 | 		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, idx); | 
 | 		else if (int_sts == SVSB_INTSTS_F0_COMPLETE && | 
 | 			 svs_en == SVSB_PTPEN_INIT02) | 
 | 			svs_init02_isr_handler(svsp, idx); | 
 | 		else if (int_sts & SVSB_INTSTS_FLD_MONVOP) | 
 | 			svs_mon_mode_isr_handler(svsp, idx); | 
 | 		else | 
 | 			svs_error_isr_handler(svsp, idx); | 
 |  | 
 | 		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 bool svs_mode_available(struct svs_platform *svsp, u8 mode) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < svsp->bank_max; i++) | 
 | 		if (svsp->banks[i].mode_support & mode) | 
 | 			return true; | 
 | 	return false; | 
 | } | 
 |  | 
 | static int svs_init01(struct svs_platform *svsp) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata; | 
 | 	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; | 
 |  | 
 | 	if (!svs_mode_available(svsp, SVSB_MODE_INIT01)) | 
 | 		return 0; | 
 |  | 
 | 	/* 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]; | 
 | 		bdata = &svsb->pdata; | 
 |  | 
 | 		if (!(svsb->mode_support & SVSB_MODE_INIT01)) | 
 | 			continue; | 
 |  | 
 | 		ret = regulator_enable(svsb->buck); | 
 | 		if (ret) { | 
 | 			dev_err(svsb->dev, "%s enable fail: %d\n", | 
 | 				bdata->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]; | 
 | 		bdata = &svsb->pdata; | 
 |  | 
 | 		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(bdata->vboot, | 
 | 						      bdata->volt_step, | 
 | 						      bdata->volt_base); | 
 |  | 
 | 		for (i = 0; i < bdata->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]; | 
 | 		bdata = &svsb->pdata; | 
 |  | 
 | 		if (!(svsb->mode_support & SVSB_MODE_INIT01)) | 
 | 			continue; | 
 |  | 
 | 		opp_vboot = svs_bank_volt_to_opp_volt(bdata->vboot, | 
 | 						      bdata->volt_step, | 
 | 						      bdata->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); | 
 | 		svs_set_bank_phase(svsp, idx, 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]; | 
 | 		bdata = &svsb->pdata; | 
 |  | 
 | 		if (!(svsb->mode_support & SVSB_MODE_INIT01)) | 
 | 			continue; | 
 |  | 
 | 		for (i = 0; i < bdata->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", | 
 | 				bdata->buck_name, r); | 
 | 	} | 
 |  | 
 | svs_init01_resume_cpuidle: | 
 | 	cpuidle_resume_and_unlock(); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int svs_init02(struct svs_platform *svsp) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata; | 
 | 	struct svs_bank *svsb; | 
 | 	unsigned long flags, time_left; | 
 | 	int ret; | 
 | 	u32 idx; | 
 |  | 
 | 	if (!svs_mode_available(svsp, SVSB_MODE_INIT02)) | 
 | 		return 0; | 
 |  | 
 | 	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); | 
 | 		svs_set_bank_phase(svsp, idx, 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]; | 
 | 		bdata = &svsb->pdata; | 
 |  | 
 | 		if (!(svsb->mode_support & SVSB_MODE_INIT02)) | 
 | 			continue; | 
 |  | 
 | 		if (bdata->type == SVSB_TYPE_HIGH || bdata->type == SVSB_TYPE_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); | 
 | 		svs_set_bank_phase(svsp, idx, 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); | 
 | 	int ret; | 
 | 	u32 idx; | 
 |  | 
 | 	for (idx = 0; idx < svsp->bank_max; idx++) { | 
 | 		struct svs_bank *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) | 
 | { | 
 | 	const struct svs_bank_pdata *bdata; | 
 | 	struct svs_bank *svsb; | 
 | 	struct dev_pm_opp *opp; | 
 | 	char tz_name_buf[20]; | 
 | 	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]; | 
 | 		bdata = &svsb->pdata; | 
 |  | 
 | 		if (bdata->sw_id >= SVSB_SWID_MAX || bdata->type >= SVSB_TYPE_MAX) { | 
 | 			dev_err(svsb->dev, "unknown bank sw_id or type\n"); | 
 | 			return -EINVAL; | 
 | 		} | 
 |  | 
 | 		svsb->dev = devm_kzalloc(svsp->dev, sizeof(*svsb->dev), GFP_KERNEL); | 
 | 		if (!svsb->dev) | 
 | 			return -ENOMEM; | 
 |  | 
 | 		svsb->name = devm_kasprintf(svsp->dev, GFP_KERNEL, "%s%s", | 
 | 					    svs_swid_names[bdata->sw_id], | 
 | 					    svs_type_names[bdata->type]); | 
 | 		if (!svsb->name) | 
 | 			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, | 
 | 								 bdata->buck_name); | 
 | 			if (IS_ERR(svsb->buck)) { | 
 | 				dev_err(svsb->dev, "cannot get \"%s-supply\"\n", | 
 | 					bdata->buck_name); | 
 | 				return PTR_ERR(svsb->buck); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (!IS_ERR_OR_NULL(bdata->tzone_name)) { | 
 | 			snprintf(tz_name_buf, ARRAY_SIZE(tz_name_buf), | 
 | 				 "%s-thermal", bdata->tzone_name); | 
 | 			svsb->tzd = thermal_zone_get_zone_by_name(tz_name_buf); | 
 | 			if (IS_ERR(svsb->tzd)) { | 
 | 				dev_err(svsb->dev, "cannot get \"%s\" thermal zone\n", | 
 | 					tz_name_buf); | 
 | 				return PTR_ERR(svsb->tzd); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		count = dev_pm_opp_get_opp_count(svsb->opp_dev); | 
 | 		if (bdata->opp_count != count) { | 
 | 			dev_err(svsb->dev, | 
 | 				"opp_count not \"%u\" but get \"%d\"?\n", | 
 | 				bdata->opp_count, count); | 
 | 			return count; | 
 | 		} | 
 |  | 
 | 		for (i = 0, freq = ULONG_MAX; i < bdata->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)) { | 
 | 		nvmem_cell_put(cell); | 
 | 		return PTR_ERR(*svsp_efuse); | 
 | 	} | 
 |  | 
 | 	*svsp_efuse_max /= sizeof(u32); | 
 | 	nvmem_cell_put(cell); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static u32 svs_get_fuse_val(u32 *fuse_array, const struct svs_fusemap *fmap, u8 nbits) | 
 | { | 
 | 	u32 val; | 
 |  | 
 | 	if (fmap->index < 0) | 
 | 		return FUSE_DATA_NOT_VALID; | 
 |  | 
 | 	val = fuse_array[fmap->index] >> fmap->ofst; | 
 | 	val &= GENMASK(nbits - 1, 0); | 
 |  | 
 | 	return val; | 
 | } | 
 |  | 
 | static bool svs_is_available(struct svs_platform *svsp) | 
 | { | 
 | 	int i, num_populated = 0; | 
 |  | 
 | 	/* If at least two fuse arrays are populated, SVS is calibrated */ | 
 | 	for (i = 0; i < svsp->efuse_max; i++) { | 
 | 		if (svsp->efuse[i]) | 
 | 			num_populated++; | 
 |  | 
 | 		if (num_populated > 1) | 
 | 			return true; | 
 | 	} | 
 |  | 
 | 	return false; | 
 | } | 
 |  | 
 | static bool svs_common_parse_efuse(struct svs_platform *svsp, | 
 | 				   const struct svs_platform_data *pdata) | 
 | { | 
 | 	const struct svs_fusemap *gfmap = pdata->glb_fuse_map; | 
 | 	struct svs_fusemap tfm = { 0, 24 }; | 
 | 	u32 golden_temp, val; | 
 | 	u8 ft_pgm, vmin; | 
 | 	int i; | 
 |  | 
 | 	if (!svs_is_available(svsp)) | 
 | 		return false; | 
 |  | 
 | 	/* Get golden temperature from SVS-Thermal calibration */ | 
 | 	val = svs_get_fuse_val(svsp->tefuse, &tfm, 8); | 
 |  | 
 | 	/* If golden temp is not programmed, use the default of 50 */ | 
 | 	golden_temp = val ? val : 50; | 
 |  | 
 | 	/* Parse fused SVS calibration */ | 
 | 	ft_pgm = svs_get_fuse_val(svsp->efuse, &gfmap[GLB_FT_PGM], 8); | 
 | 	vmin = svs_get_fuse_val(svsp->efuse, &gfmap[GLB_VMIN], 2); | 
 |  | 
 | 	for (i = 0; i < svsp->bank_max; i++) { | 
 | 		struct svs_bank *svsb = &svsp->banks[i]; | 
 | 		const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 | 		const struct svs_fusemap *dfmap = bdata->dev_fuse_map; | 
 |  | 
 | 		if (vmin == 1) | 
 | 			svsb->vmin = 0x1e; | 
 |  | 
 | 		if (ft_pgm == 0) | 
 | 			svsb->volt_flags |= SVSB_INIT01_VOLT_IGNORE; | 
 |  | 
 | 		svsb->mtdes = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_MTDES], 8); | 
 | 		svsb->bdes = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_BDES], 8); | 
 | 		svsb->mdes = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_MDES], 8); | 
 | 		svsb->dcbdet = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_DCBDET], 8); | 
 | 		svsb->dcmdet = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_DCMDET], 8); | 
 | 		svsb->vmax += svsb->dvt_fixed; | 
 |  | 
 | 		svsb->mts = (svsp->ts_coeff * 2) / 1000; | 
 | 		svsb->bts = (((500 * golden_temp + svsp->ts_coeff) / 1000) - 25) * 4; | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp, | 
 | 				     const struct svs_platform_data *pdata) | 
 | { | 
 | 	struct svs_bank *svsb; | 
 | 	const struct svs_bank_pdata *bdata; | 
 | 	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; | 
 |  | 
 | 	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 = svs_get_fuse_val(svsp->efuse, &pdata->glb_fuse_map[GLB_FT_PGM], 4); | 
 |  | 
 | 	for (idx = 0; idx < svsp->bank_max; idx++) { | 
 | 		svsb = &svsp->banks[idx]; | 
 | 		bdata = &svsb->pdata; | 
 | 		const struct svs_fusemap *dfmap = bdata->dev_fuse_map; | 
 |  | 
 | 		if (ft_pgm <= 1) | 
 | 			svsb->volt_flags |= SVSB_INIT01_VOLT_IGNORE; | 
 |  | 
 | 		svsb->mtdes = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_MTDES], 8); | 
 | 		svsb->bdes = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_BDES], 8); | 
 | 		svsb->mdes = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_MDES], 8); | 
 | 		svsb->dcbdet = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_DCBDET], 8); | 
 | 		svsb->dcmdet = svs_get_fuse_val(svsp->efuse, &dfmap[BDEV_DCMDET], 8); | 
 |  | 
 | 		switch (bdata->sw_id) { | 
 | 		case SVSB_SWID_CPU_LITTLE: | 
 | 		case SVSB_SWID_CCI: | 
 | 			if (ft_pgm <= 3) | 
 | 				svsb->volt_od += 10; | 
 | 			else | 
 | 				svsb->volt_od += 2; | 
 | 			break; | 
 | 		case SVSB_SWID_CPU_BIG: | 
 | 			if (ft_pgm <= 3) | 
 | 				svsb->volt_od += 15; | 
 | 			else | 
 | 				svsb->volt_od += 12; | 
 | 			break; | 
 | 		case SVSB_SWID_GPU: | 
 | 			if (ft_pgm != FUSE_DATA_NOT_VALID && ft_pgm >= 2) { | 
 | 				svsb->freq_base = 800000000; /* 800MHz */ | 
 | 				svsb->dvt_fixed = 2; | 
 | 			} | 
 | 			break; | 
 | 		default: | 
 | 			dev_err(svsb->dev, "unknown sw_id: %u\n", bdata->sw_id); | 
 | 			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]; | 
 | 		bdata = &svsb->pdata; | 
 | 		svsb->mts = mts; | 
 |  | 
 | 		switch (bdata->sw_id) { | 
 | 		case SVSB_SWID_CPU_LITTLE: | 
 | 			tb_roomt = x_roomt[3]; | 
 | 			break; | 
 | 		case SVSB_SWID_CPU_BIG: | 
 | 			tb_roomt = x_roomt[4]; | 
 | 			break; | 
 | 		case SVSB_SWID_CCI: | 
 | 			tb_roomt = x_roomt[3]; | 
 | 			break; | 
 | 		case SVSB_SWID_GPU: | 
 | 			tb_roomt = x_roomt[1]; | 
 | 			break; | 
 | 		default: | 
 | 			dev_err(svsb->dev, "unknown sw_id: %u\n", bdata->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); | 
 | 	of_node_put(np); | 
 | 	if (!pdev) { | 
 | 		dev_err(svsp->dev, "cannot find pdev by %s\n", node_name); | 
 | 		return ERR_PTR(-ENXIO); | 
 | 	} | 
 |  | 
 | 	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 void svs_put_device(void *_dev) | 
 | { | 
 | 	struct device *dev = _dev; | 
 |  | 
 | 	put_device(dev); | 
 | } | 
 |  | 
 | static int svs_mt8192_platform_probe(struct svs_platform *svsp) | 
 | { | 
 | 	struct device *dev; | 
 | 	u32 idx; | 
 | 	int ret; | 
 |  | 
 | 	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, "thermal-sensor"); | 
 | 	if (IS_ERR(dev)) | 
 | 		return dev_err_probe(svsp->dev, PTR_ERR(dev), | 
 | 				     "failed to get lvts device\n"); | 
 | 	put_device(dev); | 
 |  | 
 | 	for (idx = 0; idx < svsp->bank_max; idx++) { | 
 | 		struct svs_bank *svsb = &svsp->banks[idx]; | 
 | 		const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 |  | 
 | 		switch (bdata->sw_id) { | 
 | 		case SVSB_SWID_CPU_LITTLE: | 
 | 		case SVSB_SWID_CPU_BIG: | 
 | 			svsb->opp_dev = get_cpu_device(bdata->cpu_id); | 
 | 			get_device(svsb->opp_dev); | 
 | 			break; | 
 | 		case SVSB_SWID_CCI: | 
 | 			svsb->opp_dev = svs_add_device_link(svsp, "cci"); | 
 | 			break; | 
 | 		case SVSB_SWID_GPU: | 
 | 			if (bdata->type == SVSB_TYPE_LOW) | 
 | 				svsb->opp_dev = svs_get_subsys_device(svsp, "gpu"); | 
 | 			else | 
 | 				svsb->opp_dev = svs_add_device_link(svsp, "gpu"); | 
 | 			break; | 
 | 		default: | 
 | 			dev_err(svsb->dev, "unknown sw_id: %u\n", bdata->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); | 
 |  | 
 | 		ret = devm_add_action_or_reset(svsp->dev, svs_put_device, | 
 | 					       svsb->opp_dev); | 
 | 		if (ret) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int svs_mt8183_platform_probe(struct svs_platform *svsp) | 
 | { | 
 | 	struct device *dev; | 
 | 	u32 idx; | 
 | 	int ret; | 
 |  | 
 | 	dev = svs_add_device_link(svsp, "thermal-sensor"); | 
 | 	if (IS_ERR(dev)) | 
 | 		return dev_err_probe(svsp->dev, PTR_ERR(dev), | 
 | 				     "failed to get thermal device\n"); | 
 | 	put_device(dev); | 
 |  | 
 | 	for (idx = 0; idx < svsp->bank_max; idx++) { | 
 | 		struct svs_bank *svsb = &svsp->banks[idx]; | 
 | 		const struct svs_bank_pdata *bdata = &svsb->pdata; | 
 |  | 
 | 		switch (bdata->sw_id) { | 
 | 		case SVSB_SWID_CPU_LITTLE: | 
 | 		case SVSB_SWID_CPU_BIG: | 
 | 			svsb->opp_dev = get_cpu_device(bdata->cpu_id); | 
 | 			get_device(svsb->opp_dev); | 
 | 			break; | 
 | 		case SVSB_SWID_CCI: | 
 | 			svsb->opp_dev = svs_add_device_link(svsp, "cci"); | 
 | 			break; | 
 | 		case SVSB_SWID_GPU: | 
 | 			svsb->opp_dev = svs_add_device_link(svsp, "gpu"); | 
 | 			break; | 
 | 		default: | 
 | 			dev_err(svsb->dev, "unknown sw_id: %u\n", bdata->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); | 
 |  | 
 | 		ret = devm_add_action_or_reset(svsp->dev, svs_put_device, | 
 | 					       svsb->opp_dev); | 
 | 		if (ret) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct svs_bank svs_mt8195_banks[] = { | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.type			= SVSB_TYPE_LOW, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 640000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x18, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(0), | 
 | 			.ctl0			= 0x00540003, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 10, 16 }, { 10, 24 }, { 10, 0 }, { 8, 0 }, { 8, 8 } | 
 | 			} | 
 | 		}, | 
 | 		.mode_support	= SVSB_MODE_INIT02, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT, | 
 | 		.freq_base	= 640000000, | 
 | 		.core_sel	= 0x0fff0100, | 
 | 		.dvt_fixed	= 0x1, | 
 | 		.vmax		= 0x38, | 
 | 		.vmin		= 0x14, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.type			= SVSB_TYPE_HIGH, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.tzone_name		= "gpu", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 640000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x18, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(1), | 
 | 			.ctl0			= 0x00540003, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 0, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 7, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 9, 16 }, { 9, 24 }, { 9, 0 }, { 8, 0 }, { 8, 8 } | 
 | 			}, | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 880000000, | 
 | 		.core_sel	= 0x0fff0101, | 
 | 		.dvt_fixed	= 0x6, | 
 | 		.vmax		= 0x38, | 
 | 		.vmin		= 0x14, | 
 | 	}, | 
 | }; | 
 |  | 
 | static struct svs_bank svs_mt8192_banks[] = { | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.type			= SVSB_TYPE_LOW, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.tzone_name		= "gpu", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 688000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x18, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(0), | 
 | 			.ctl0			= 0x00540003, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 0, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 7, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 10, 16 }, { 10, 24 }, { 10, 0 }, { 17, 0 }, { 17, 8 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT, | 
 | 		.mode_support	= SVSB_MODE_INIT02, | 
 | 		.freq_base	= 688000000, | 
 | 		.core_sel	= 0x0fff0100, | 
 | 		.dvt_fixed	= 0x1, | 
 | 		.vmax		= 0x60, | 
 | 		.vmin		= 0x1a, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.type			= SVSB_TYPE_HIGH, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.tzone_name		= "gpu", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 688000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x18, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(1), | 
 | 			.ctl0			= 0x00540003, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 0, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 7, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 9, 16 }, { 9, 24 }, { 17, 0 }, { 17, 16 }, { 17, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 902000000, | 
 | 		.core_sel	= 0x0fff0101, | 
 | 		.dvt_fixed	= 0x6, | 
 | 		.vmax		= 0x60, | 
 | 		.vmin		= 0x1a, | 
 | 	}, | 
 | }; | 
 |  | 
 | static struct svs_bank svs_mt8188_banks[] = { | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.type			= SVSB_TYPE_LOW, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 640000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x555555, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(0), | 
 | 			.ctl0			= 0x00100003, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 5, 16 }, { 5, 24 }, { 5, 0 }, { 15, 16 }, { 15, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT, | 
 | 		.mode_support	= SVSB_MODE_INIT02, | 
 | 		.freq_base	= 640000000, | 
 | 		.core_sel	= 0x0fff0000, | 
 | 		.dvt_fixed	= 0x1, | 
 | 		.vmax		= 0x38, | 
 | 		.vmin		= 0x1c, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.type			= SVSB_TYPE_HIGH, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.tzone_name		= "gpu", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 640000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x555555, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(1), | 
 | 			.ctl0			= 0x00100003, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 0, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 7, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 4, 16 }, { 4, 24 }, { 4, 0 }, { 14, 0 }, { 14, 8 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 880000000, | 
 | 		.core_sel	= 0x0fff0001, | 
 | 		.dvt_fixed	= 0x4, | 
 | 		.vmax		= 0x38, | 
 | 		.vmin		= 0x1c, | 
 | 	}, | 
 | }; | 
 |  | 
 | static struct svs_bank svs_mt8186_banks[] = { | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CPU_BIG, | 
 | 			.type			= SVSB_TYPE_LOW, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.cpu_id			= 6, | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 1670000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x1, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(0), | 
 | 			.ctl0			= 0x00540003, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 3, 16 }, { 3, 24 }, { 3, 0 }, { 14, 16 }, { 14, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT, | 
 | 		.volt_od	= 4, | 
 | 		.mode_support	= SVSB_MODE_INIT02, | 
 | 		.freq_base	= 1670000000, | 
 | 		.core_sel	= 0x0fff0100, | 
 | 		.dvt_fixed	= 0x3, | 
 | 		.vmax		= 0x59, | 
 | 		.vmin		= 0x20, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CPU_BIG, | 
 | 			.type			= SVSB_TYPE_HIGH, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v3, | 
 | 			.get_volts		= svs_get_bank_volts_v3, | 
 | 			.cpu_id			= 6, | 
 | 			.tzone_name		= "cpu-big", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.turn_freq_base		= 1670000000, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x1, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(1), | 
 | 			.ctl0			= 0x00540003, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 8, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 8, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 2, 16 }, { 2, 24 }, { 2, 0 }, { 13, 0 }, { 13, 8 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.volt_od	= 4, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 2050000000, | 
 | 		.core_sel	= 0x0fff0101, | 
 | 		.dvt_fixed	= 0x6, | 
 | 		.vmax		= 0x73, | 
 | 		.vmin		= 0x20, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CPU_LITTLE, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.cpu_id			= 0, | 
 | 			.tzone_name		= "cpu-little", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x1, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(2), | 
 | 			.ctl0			= 0x3210000f, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 8, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 8, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 4, 16 }, { 4, 24 }, { 4, 0 }, { 14, 0 }, { 14, 8 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.volt_od	= 3, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 2000000000, | 
 | 		.core_sel	= 0x0fff0102, | 
 | 		.dvt_fixed	= 0x6, | 
 | 		.vmax		= 0x65, | 
 | 		.vmin		= 0x20, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CCI, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.tzone_name		= "cci", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x1, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(3), | 
 | 			.ctl0			= 0x3210000f, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 8, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 8, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 5, 16 }, { 5, 24 }, { 5, 0 }, { 15, 16 }, { 15, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.volt_od	= 3, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 1400000000, | 
 | 		.core_sel	= 0x0fff0103, | 
 | 		.dvt_fixed	= 0x6, | 
 | 		.vmax		= 0x65, | 
 | 		.vmin		= 0x20, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.tzone_name		= "gpu", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 400000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x1, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x87, | 
 | 			.int_st			= BIT(4), | 
 | 			.ctl0			= 0x00100003, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 8, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 7, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 6, 16 }, { 6, 24 }, { 6, 0 }, { 15, 8 }, { 15, 0 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_REMOVE_DVTFIXED_VOLT | SVSB_MON_VOLT_IGNORE, | 
 | 		.mode_support	= SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 850000000, | 
 | 		.core_sel	= 0x0fff0104, | 
 | 		.dvt_fixed	= 0x4, | 
 | 		.vmax		= 0x58, | 
 | 		.vmin		= 0x20, | 
 | 	}, | 
 | }; | 
 |  | 
 | static struct svs_bank svs_mt8183_banks[] = { | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CPU_LITTLE, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.cpu_id			= 0, | 
 | 			.buck_name		= "proc", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.vboot			= 0x30, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 500000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x555555, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x77, | 
 | 			.int_st			= BIT(0), | 
 | 			.ctl0			= 0x00010001, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 16, 0 }, { 16, 8 }, { 17, 16 }, { 16, 16 }, { 16, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_INIT01_VOLT_INC_ONLY, | 
 | 		.mode_support	= SVSB_MODE_INIT01 | SVSB_MODE_INIT02, | 
 | 		.freq_base	= 1989000000, | 
 | 		.core_sel	= 0x8fff0000, | 
 | 		.dvt_fixed	= 0x7, | 
 | 		.vmax		= 0x64, | 
 | 		.vmin		= 0x18, | 
 |  | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CPU_BIG, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.cpu_id			= 4, | 
 | 			.buck_name		= "proc", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.vboot			= 0x30, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 500000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x555555, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x77, | 
 | 			.int_st			= BIT(1), | 
 | 			.ctl0			= 0x00000001, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 18, 0 }, { 18, 8 }, { 17, 0 }, { 18, 16 }, { 18, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_INIT01_VOLT_INC_ONLY, | 
 | 		.mode_support	= SVSB_MODE_INIT01 | SVSB_MODE_INIT02, | 
 | 		.freq_base	= 1989000000, | 
 | 		.core_sel	= 0x8fff0001, | 
 | 		.dvt_fixed	= 0x7, | 
 | 		.vmax		= 0x58, | 
 | 		.vmin		= 0x10, | 
 |  | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_CCI, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.buck_name		= "proc", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.vboot			= 0x30, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 500000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x555555, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x77, | 
 | 			.int_st			= BIT(2), | 
 | 			.ctl0			= 0x00100003, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 4, 0 }, { 4, 8 }, { 5, 16 }, { 4, 16 }, { 4, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_INIT01_VOLT_INC_ONLY, | 
 | 		.mode_support	= SVSB_MODE_INIT01 | SVSB_MODE_INIT02, | 
 | 		.freq_base	= 1196000000, | 
 | 		.core_sel	= 0x8fff0002, | 
 | 		.dvt_fixed	= 0x7, | 
 | 		.vmax		= 0x64, | 
 | 		.vmin		= 0x18, | 
 | 	}, | 
 | 	{ | 
 | 		.pdata = (const struct svs_bank_pdata) { | 
 | 			.sw_id			= SVSB_SWID_GPU, | 
 | 			.set_freq_pct		= svs_set_bank_freq_pct_v2, | 
 | 			.get_volts		= svs_get_bank_volts_v2, | 
 | 			.buck_name		= "mali", | 
 | 			.tzone_name		= "gpu", | 
 | 			.opp_count		= MAX_OPP_ENTRIES, | 
 | 			.vboot			= 0x30, | 
 | 			.volt_step		= 6250, | 
 | 			.volt_base		= 500000, | 
 | 			.age_config		= 0x555555, | 
 | 			.dc_config		= 0x555555, | 
 | 			.vco			= 0x10, | 
 | 			.chk_shift		= 0x77, | 
 | 			.int_st			= BIT(3), | 
 | 			.ctl0			= 0x00050001, | 
 | 			.tzone_htemp		= 85000, | 
 | 			.tzone_htemp_voffset	= 0, | 
 | 			.tzone_ltemp		= 25000, | 
 | 			.tzone_ltemp_voffset	= 3, | 
 | 			.dev_fuse_map		= (const struct svs_fusemap[BDEV_MAX]) { | 
 | 				{ 6, 0 }, { 6, 8 }, { 5, 0 }, { 6, 16 }, { 6, 24 } | 
 | 			} | 
 | 		}, | 
 | 		.volt_flags	= SVSB_INIT01_PD_REQ | SVSB_INIT01_VOLT_INC_ONLY, | 
 | 		.mode_support	= SVSB_MODE_INIT01 | SVSB_MODE_INIT02 | SVSB_MODE_MON, | 
 | 		.freq_base	= 900000000, | 
 | 		.core_sel	= 0x8fff0003, | 
 | 		.dvt_fixed	= 0x3, | 
 | 		.vmax		= 0x40, | 
 | 		.vmin		= 0x14, | 
 | 	}, | 
 | }; | 
 |  | 
 | static const struct svs_platform_data svs_mt8195_platform_data = { | 
 | 	.name = "mt8195-svs", | 
 | 	.banks = svs_mt8195_banks, | 
 | 	.efuse_parsing = svs_common_parse_efuse, | 
 | 	.probe = svs_mt8192_platform_probe, | 
 | 	.regs = svs_regs_v2, | 
 | 	.bank_max = ARRAY_SIZE(svs_mt8195_banks), | 
 | 	.ts_coeff = SVSB_TS_COEFF_MT8195, | 
 | 	.glb_fuse_map = (const struct svs_fusemap[GLB_MAX]) { | 
 | 		{ 0, 0 }, { 19, 4 } | 
 | 	} | 
 | }; | 
 |  | 
 | static const struct svs_platform_data svs_mt8192_platform_data = { | 
 | 	.name = "mt8192-svs", | 
 | 	.banks = svs_mt8192_banks, | 
 | 	.efuse_parsing = svs_common_parse_efuse, | 
 | 	.probe = svs_mt8192_platform_probe, | 
 | 	.regs = svs_regs_v2, | 
 | 	.bank_max = ARRAY_SIZE(svs_mt8192_banks), | 
 | 	.ts_coeff = SVSB_TS_COEFF_MT8195, | 
 | 	.glb_fuse_map = (const struct svs_fusemap[GLB_MAX]) { | 
 | 		/* FT_PGM not present */ | 
 | 		{ -1, 0 }, { 19, 4 } | 
 | 	} | 
 | }; | 
 |  | 
 | static const struct svs_platform_data svs_mt8188_platform_data = { | 
 | 	.name = "mt8188-svs", | 
 | 	.banks = svs_mt8188_banks, | 
 | 	.efuse_parsing = svs_common_parse_efuse, | 
 | 	.probe = svs_mt8192_platform_probe, | 
 | 	.regs = svs_regs_v2, | 
 | 	.bank_max = ARRAY_SIZE(svs_mt8188_banks), | 
 | 	.ts_coeff = SVSB_TS_COEFF_MT8195, | 
 | 	.glb_fuse_map = (const struct svs_fusemap[GLB_MAX]) { | 
 | 		/* FT_PGM and VMIN not present */ | 
 | 		{ -1, 0 }, { -1, 0 } | 
 | 	} | 
 | }; | 
 |  | 
 | static const struct svs_platform_data svs_mt8186_platform_data = { | 
 | 	.name = "mt8186-svs", | 
 | 	.banks = svs_mt8186_banks, | 
 | 	.efuse_parsing = svs_common_parse_efuse, | 
 | 	.probe = svs_mt8192_platform_probe, | 
 | 	.regs = svs_regs_v2, | 
 | 	.bank_max = ARRAY_SIZE(svs_mt8186_banks), | 
 | 	.ts_coeff = SVSB_TS_COEFF_MT8186, | 
 | 	.glb_fuse_map = (const struct svs_fusemap[GLB_MAX]) { | 
 | 		/* FT_PGM and VMIN not present */ | 
 | 		{ -1, 0 }, { -1, 0 } | 
 | 	} | 
 | }; | 
 |  | 
 | 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), | 
 | 	.glb_fuse_map = (const struct svs_fusemap[GLB_MAX]) { | 
 | 		/* VMIN not present */ | 
 | 		{ 0, 4 }, { -1, 0 } | 
 | 	} | 
 | }; | 
 |  | 
 | static const struct of_device_id svs_of_match[] = { | 
 | 	{ .compatible = "mediatek,mt8195-svs", .data = &svs_mt8195_platform_data }, | 
 | 	{ .compatible = "mediatek,mt8192-svs", .data = &svs_mt8192_platform_data }, | 
 | 	{ .compatible = "mediatek,mt8188-svs", .data = &svs_mt8188_platform_data }, | 
 | 	{ .compatible = "mediatek,mt8186-svs", .data = &svs_mt8186_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; | 
 | 	svsp->ts_coeff = svsp_data->ts_coeff; | 
 |  | 
 | 	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) | 
 | 		return dev_err_probe(&pdev->dev, ret, "Cannot read SVS calibration\n"); | 
 |  | 
 | 	ret = svs_get_efuse_data(svsp, "t-calibration-data", | 
 | 				 &svsp->tefuse, &svsp->tefuse_max); | 
 | 	if (ret) { | 
 | 		dev_err_probe(&pdev->dev, ret, "Cannot read SVS-Thermal calibration\n"); | 
 | 		goto svs_probe_free_efuse; | 
 | 	} | 
 |  | 
 | 	if (!svsp_data->efuse_parsing(svsp, svsp_data)) { | 
 | 		ret = dev_err_probe(svsp->dev, -EINVAL, "efuse data parsing failed\n"); | 
 | 		goto svs_probe_free_tefuse; | 
 | 	} | 
 |  | 
 | 	ret = svs_bank_resource_setup(svsp); | 
 | 	if (ret) { | 
 | 		dev_err_probe(svsp->dev, ret, "svs bank resource setup fail\n"); | 
 | 		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)) { | 
 | 		ret = dev_err_probe(svsp->dev, PTR_ERR(svsp->main_clk), | 
 | 				    "failed to get clock\n"); | 
 | 		goto svs_probe_free_tefuse; | 
 | 	} | 
 |  | 
 | 	ret = clk_prepare_enable(svsp->main_clk); | 
 | 	if (ret) { | 
 | 		dev_err_probe(svsp->dev, ret, "cannot enable main clk\n"); | 
 | 		goto svs_probe_free_tefuse; | 
 | 	} | 
 |  | 
 | 	svsp->base = of_iomap(svsp->dev->of_node, 0); | 
 | 	if (IS_ERR_OR_NULL(svsp->base)) { | 
 | 		ret = dev_err_probe(svsp->dev, -EINVAL, "cannot find svs register base\n"); | 
 | 		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_probe(svsp->dev, ret, "register irq(%d) failed\n", svsp_irq); | 
 | 		goto svs_probe_iounmap; | 
 | 	} | 
 |  | 
 | 	ret = svs_start(svsp); | 
 | 	if (ret) { | 
 | 		dev_err_probe(svsp->dev, ret, "svs start fail\n"); | 
 | 		goto svs_probe_iounmap; | 
 | 	} | 
 |  | 
 | #ifdef CONFIG_DEBUG_FS | 
 | 	ret = svs_create_debug_cmds(svsp); | 
 | 	if (ret) { | 
 | 		dev_err_probe(svsp->dev, ret, "svs create debug cmds fail\n"); | 
 | 		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: | 
 | 	kfree(svsp->tefuse); | 
 | svs_probe_free_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_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); | 
 | MODULE_DESCRIPTION("MediaTek SVS driver"); | 
 | MODULE_LICENSE("GPL"); |