| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * mediatek-mcupm-cpufreq.c - MCUPM based CPUFreq Driver | 
 |  * | 
 |  * Copyright (c) 2020 MediaTek Inc. | 
 |  * Wei-Chia Su <Wei-Chia.Su@mediatek.com> | 
 |  */ | 
 |  | 
 | #include <linux/clk.h> | 
 | #include <linux/cpu.h> | 
 | #include <linux/cpufreq.h> | 
 | #include <linux/cpumask.h> | 
 | #include <linux/energy_model.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/module.h> | 
 | #include <linux/of.h> | 
 | #include <linux/of_address.h> | 
 | #include <linux/of_device.h> | 
 | #include <linux/of_platform.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/pm_opp.h> | 
 | #include <linux/proc_fs.h> | 
 | #include <linux/seq_file.h> | 
 | #include <linux/regulator/consumer.h> | 
 | #include <linux/soc/mediatek/mtk_tinysys_ipi.h> | 
 | #include <linux/uaccess.h> | 
 | #include <mcupm_ipi_id.h> | 
 | #include <mcupm_driver.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/nvmem-consumer.h> | 
 |  | 
 | #define OFFS_WFI_S 0x037c | 
 | #define DVFS_D_LEN (4) | 
 | #define CSRAM_SIZE 0x1400 | 
 | #define OFFS_LOG_S 0x03d0 | 
 | #define DBG_REPO_NUM (CSRAM_SIZE / sizeof(u32)) | 
 | #define REPO_I_LOG_S (OFFS_LOG_S / sizeof(u32)) | 
 | #define ENTRY_EACH_LOG	5 | 
 | #define VMIN_PREDICT_ENABLE (0) | 
 | #define NR_PI_VF 6 | 
 | #define MAX_NR_FREQ 32 | 
 | #define MIN_SIZE_SN_DUMP_CPE (7) | 
 | #define NUM_SN_CPU (8) | 
 | #define SIZE_SN_MCUSYS_REG (10) | 
 | #define SIZE_SN_DUMP_SENSOR (64) | 
 | #define IDX_HW_RES_SN (18) | 
 | #define DEVINFO_IDX_0 0xC8 | 
 | #define DEVINFO_HRID_0 0x30 | 
 | #define EEMSN_CSRAM_BASE 0x0011BC00 | 
 | #define LOW_TEMP_OFF (8) | 
 | #define HIGH_TEMP_OFF (3) | 
 | #define AGING_VAL_CPU (0x6) | 
 | #define EEM_TAG  "[CPU][EEM]" | 
 | #define SUPPORT_PICACHU (1) | 
 | #if SUPPORT_PICACHU | 
 | #define PICACHU_SIG (0xA5) | 
 | #define PICACHU_SIGNATURE_SHIFT_BIT (24) | 
 | #define EEM_PHY_SPARE0 0x11278F20 | 
 | #endif | 
 | #undef  BIT | 
 | #define BIT(bit) (1U << (bit)) | 
 |  | 
 | #define eem_read(addr) __raw_readl((void __iomem *)(addr)) | 
 |  | 
 | #define PROC_FOPS_RW(name)						\ | 
 | 	static int name ## _proc_open(struct inode *inode, struct file *file)	\ | 
 | {									\ | 
 | 	return single_open(file, name ## _proc_show, PDE_DATA(inode));	\ | 
 | }									\ | 
 | static const struct file_operations name ## _proc_fops = {		\ | 
 | 	.owner          = THIS_MODULE,					\ | 
 | 	.open           = name ## _proc_open,				\ | 
 | 	.read           = seq_read,					\ | 
 | 	.llseek         = seq_lseek,					\ | 
 | 	.release        = single_release,				\ | 
 | 	.write          = name ## _proc_write,				\ | 
 | } | 
 |  | 
 | #define PROC_FOPS_RO(name)						\ | 
 | static int name##_proc_open(struct inode *inode, struct file *file)	\ | 
 | {									\ | 
 | 	return single_open(file, name##_proc_show, PDE_DATA(inode));	\ | 
 | }									\ | 
 | static const struct file_operations name##_proc_fops = {		\ | 
 | 	.owner = THIS_MODULE,						\ | 
 | 	.open = name##_proc_open,					\ | 
 | 	.read = seq_read,						\ | 
 | 	.llseek = seq_lseek,						\ | 
 | 	.release = single_release,					\ | 
 | } | 
 |  | 
 | #define PROC_ENTRY(name)	{__stringify(name), &name ## _proc_fops} | 
 | #define PROC_ENTRY_DATA(name)	{__stringify(name), &name ## _proc_fops, g_ ## name} | 
 |  | 
 | enum mt_cpu_dvfs_id { | 
 | 	MT_CPU_DVFS_LL, | 
 | 	MT_CPU_DVFS_CCI, | 
 |  | 
 | 	NR_MT_CPU_DVFS, | 
 | }; | 
 |  | 
 | enum eem_phase { | 
 | 	EEM_PHASE_INIT01, | 
 | 	EEM_PHASE_INIT02, | 
 | 	EEM_PHASE_MON, | 
 | 	EEM_PHASE_SEN, | 
 |  | 
 | 	NR_EEM_PHASE, | 
 | }; | 
 |  | 
 | enum eem_features { | 
 | 	FEA_INIT01	= BIT(EEM_PHASE_INIT01), | 
 | 	FEA_INIT02	= BIT(EEM_PHASE_INIT02), | 
 | 	FEA_MON		= BIT(EEM_PHASE_MON), | 
 | 	FEA_SEN	= BIT(EEM_PHASE_SEN), | 
 | }; | 
 |  | 
 | enum { | 
 | 	IPI_EEMSN_SHARERAM_INIT, | 
 | 	IPI_EEMSN_INIT, | 
 | 	IPI_EEMSN_PROBE, | 
 | 	IPI_EEMSN_INIT01, | 
 | 	IPI_EEMSN_GET_EEM_VOLT, | 
 | 	IPI_EEMSN_INIT02, | 
 | 	IPI_EEMSN_DEBUG_PROC_WRITE, | 
 | 	IPI_EEMSN_SEND_UPOWER_TBL_REF, | 
 | 	IPI_EEMSN_CUR_VOLT_PROC_SHOW, | 
 | 	IPI_EEMSN_DUMP_PROC_SHOW, | 
 | 	IPI_EEMSN_AGING_DUMP_PROC_SHOW, | 
 | 	IPI_EEMSN_OFFSET_PROC_WRITE, | 
 | 	IPI_EEMSN_SNAGING_PROC_WRITE, | 
 | 	IPI_EEMSN_LOGEN_PROC_SHOW, | 
 | 	IPI_EEMSN_LOGEN_PROC_WRITE, | 
 | 	IPI_EEMSN_EN_PROC_SHOW, | 
 | 	IPI_EEMSN_EN_PROC_WRITE, | 
 | 	IPI_EEMSN_SNEN_PROC_SHOW, | 
 | 	IPI_EEMSN_SNEN_PROC_WRITE, | 
 | 	IPI_EEMSN_FAKE_SN_INIT_ISR, | 
 | 	IPI_EEMSN_FORCE_SN_SENSING, | 
 | 	IPI_EEMSN_PULL_DATA, | 
 | 	IPI_EEMSN_FAKE_SN_SENSING_ISR, | 
 | 	NR_EEMSN_IPI, | 
 | }; | 
 |  | 
 | enum eemsn_det_id { | 
 | 	EEMSN_DET_L, | 
 | 	EEMSN_DET_CCI, | 
 |  | 
 | 	NR_EEMSN_DET, | 
 | }; | 
 |  | 
 | enum sn_det_id { | 
 | 	SN_DET_L = 0, | 
 |  | 
 | 	NR_SN_DET, | 
 | }; | 
 |  | 
 | enum cpu_dvfs_ipi_type { | 
 | 	IPI_DVFS_INIT, | 
 | 	NR_DVFS_IPI, | 
 | }; | 
 |  | 
 | static void __iomem *csram_base; | 
 | u32 *g_dbg_repo; | 
 | int ipi_ackdata; | 
 | unsigned char ctrl_agingload_enable; | 
 | static struct eemsn_dbg_log *eemsn_log; | 
 | unsigned int seq; | 
 | static LIST_HEAD(dvfs_info_list); | 
 | struct nvmem_device *nvmem_dev; | 
 | struct cdvfs_data { | 
 | 	unsigned int cmd; | 
 | 	union { | 
 | 		struct { | 
 | 			unsigned int arg[3]; | 
 | 		} set_fv; | 
 | 	} u; | 
 | }; | 
 |  | 
 | struct mtk_cpu_dvfs_info { | 
 | 	struct cpumask cpus; | 
 | 	struct device *cpu_dev; | 
 | 	struct list_head list_head; | 
 | 	struct mutex lock; | 
 | 	void __iomem *csram_base; | 
 | }; | 
 |  | 
 | static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu) | 
 | { | 
 | 	struct mtk_cpu_dvfs_info *info; | 
 | 	struct list_head *list; | 
 |  | 
 | 	list_for_each(list, &dvfs_info_list) { | 
 | 		info = list_entry(list, struct mtk_cpu_dvfs_info, list_head); | 
 | 		if (cpumask_test_cpu(cpu, &info->cpus)) | 
 | 			return info; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | 
 | 				  unsigned int index) | 
 | { | 
 | 	struct mtk_cpu_dvfs_info *info = policy->driver_data; | 
 | 	unsigned int cluster_id = policy->cpu / 4; | 
 |  | 
 | 	writel_relaxed(index, info->csram_base + (OFFS_WFI_S + (cluster_id * 4)) | 
 | 		       ); | 
 | 	arch_set_freq_scale(policy->related_cpus, | 
 | 			    policy->freq_table[index].frequency, | 
 | 			    policy->cpuinfo.max_freq); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) | 
 | { | 
 | 	struct device *cpu_dev; | 
 | 	int ret; | 
 |  | 
 | 	cpu_dev = get_cpu_device(cpu); | 
 | 	if (!cpu_dev) | 
 | 		return -ENODEV; | 
 |  | 
 | 	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, &info->cpus); | 
 | 	if (ret) | 
 | 		goto out_free_resources; | 
 |  | 
 | 	ret = dev_pm_opp_of_cpumask_add_table(&info->cpus); | 
 | 	if (ret) | 
 | 		goto out_free_resources; | 
 |  | 
 | 	info->cpu_dev = cpu_dev; | 
 | 	mutex_init(&info->lock); | 
 |  | 
 | 	return 0; | 
 |  | 
 | out_free_resources: | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) | 
 | { | 
 | 	dev_pm_opp_of_cpumask_remove_table(&info->cpus); | 
 | } | 
 |  | 
 | static int mtk_cpufreq_init(struct cpufreq_policy *policy) | 
 | { | 
 | 	struct mtk_cpu_dvfs_info *info; | 
 | 	struct cpufreq_frequency_table *freq_table; | 
 | 	struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); | 
 | 	int ret; | 
 |  | 
 | 	info = mtk_cpu_dvfs_info_lookup(policy->cpu); | 
 | 	if (!info) | 
 | 		return -EINVAL; | 
 |  | 
 | 	ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table); | 
 | 	if (ret) | 
 | 		return -EINVAL; | 
 |  | 
 | 	ret = dev_pm_opp_get_opp_count(info->cpu_dev); | 
 | 	if (ret <= 0) { | 
 | 		ret = -EINVAL; | 
 | 		goto out_free_cpufreq_table; | 
 | 	} | 
 |  | 
 | 	cpumask_copy(policy->cpus, &info->cpus); | 
 | 	em_register_perf_domain(policy->cpus, ret, &em_cb); | 
 | 	policy->driver_data = info; | 
 | 	policy->freq_table = freq_table; | 
 | 	policy->transition_delay_us = 1000; /* us */ | 
 |  | 
 | 	return 0; | 
 |  | 
 | out_free_cpufreq_table:	 | 
 | 	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int mtk_cpufreq_exit(struct cpufreq_policy *policy) | 
 | { | 
 | 	struct mtk_cpu_dvfs_info *info = policy->driver_data; | 
 |  | 
 | 	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct cpufreq_driver mtk_cpufreq_driver = { | 
 | 	.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | | 
 | 		 CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_IS_COOLING_DEV, | 
 | 	.verify = cpufreq_generic_frequency_table_verify, | 
 | 	.target_index = mtk_cpufreq_set_target, | 
 | 	.init = mtk_cpufreq_init, | 
 | 	.exit = mtk_cpufreq_exit, | 
 | 	.name = "mtk-cpufreq", | 
 | 	.attr = cpufreq_generic_attr, | 
 | }; | 
 |  | 
 | struct A_Tused_VT { | 
 | 	unsigned int A_Tused_SVT:8; | 
 | 	unsigned int A_Tused_LVT:8; | 
 | 	unsigned int A_Tused_ULVT:8; | 
 | 	unsigned int A_Tused_RSV0:8; | 
 | }; | 
 |  | 
 | struct dvfs_vf_tbl { | 
 | 	unsigned short pi_freq_tbl[NR_PI_VF]; | 
 | 	unsigned char pi_volt_tbl[NR_PI_VF]; | 
 | 	unsigned char pi_vf_num; | 
 | }; | 
 |  | 
 | struct sensing_stru { | 
 | #if defined(VMIN_PREDICT_ENABLE) | 
 | 	uint64_t CPE_Vmin_HW; | 
 | #endif | 
 | 	unsigned int SN_Vmin; | 
 | 	int CPE_Vmin; | 
 | 	unsigned int cur_volt; | 
 | #if !VMIN_PREDICT_ENABLE | 
 | 	/* unsigned int count_cur_volt_HT; */ | 
 | 	int Sensor_Volt_HT; | 
 | 	int Sensor_Volt_RT; | 
 | 	int8_t CPE_temp; | 
 | 	int8_t SN_temp; | 
 | 	unsigned char T_SVT_current; | 
 | #endif | 
 | 	unsigned short cur_temp; | 
 | 	unsigned char cur_oppidx; | 
 | 	unsigned char dst_volt_pmic; | 
 | }; | 
 |  | 
 | struct eemsn_log_det { | 
 | 	unsigned char mc50flag; | 
 | 	unsigned char features; | 
 | 	int8_t volt_clamp; | 
 | 	int8_t volt_offset; | 
 | }; | 
 |  | 
 | struct eemsn_vlog_det { | 
 | 	unsigned int temp; | 
 | 	unsigned short freq_tbl[MAX_NR_FREQ]; | 
 | 	unsigned short volt_tbl_pmic[MAX_NR_FREQ]; | 
 | 	unsigned char volt_tbl_orig[MAX_NR_FREQ]; | 
 | 	unsigned char volt_tbl_init2[MAX_NR_FREQ]; | 
 | 	/* unsigned char volt_tbl[NR_FREQ]; */ | 
 | 	unsigned char num_freq_tbl; | 
 | 	unsigned char lock; | 
 | 	unsigned char turn_pt; | 
 | 	enum eemsn_det_id det_id; | 
 | }; | 
 |  | 
 | struct eemsn_devinfo { | 
 | 	/* M_HW_RES0 0x11c1_0580 */ | 
 | 	unsigned int FT_PGM:8; | 
 | 	unsigned int FT_BIN:4; | 
 | 	unsigned int RSV0_1:20; | 
 |  | 
 | 	/* M_HW_RES1 */ | 
 | 	unsigned int CPU_B_MTDES:8; | 
 | 	unsigned int CPU_B_INITEN:1; | 
 | 	unsigned int CPU_B_MONEN:1; | 
 | 	unsigned int CPU_B_DVFS_LOW:3; | 
 | 	unsigned int CPU_B_SPEC:3; | 
 | 	unsigned int CPU_B_BDES:8; | 
 | 	unsigned int CPU_B_MDES:8; | 
 |  | 
 | 	/* M_HW_RES2 */ | 
 | 	unsigned int CPU_B_HI_MTDES:8; | 
 | 	unsigned int CPU_B_HI_INITEN:1; | 
 | 	unsigned int CPU_B_HI_MONEN:1; | 
 | 	unsigned int CPU_B_HI_DVFS_LOW:3; | 
 | 	unsigned int CPU_B_HI_SPEC:3; | 
 | 	unsigned int CPU_B_HI_BDES:8; | 
 | 	unsigned int CPU_B_HI_MDES:8; | 
 |  | 
 | 	/* M_HW_RES3 */ | 
 | 	unsigned int CPU_B_LO_MTDES:8; | 
 | 	unsigned int CPU_B_LO_INITEN:1; | 
 | 	unsigned int CPU_B_LO_MONEN:1; | 
 | 	unsigned int CPU_B_LO_DVFS_LOW:3; | 
 | 	unsigned int CPU_B_LO_SPEC:3; | 
 | 	unsigned int CPU_B_LO_BDES:8; | 
 | 	unsigned int CPU_B_LO_MDES:8; | 
 |  | 
 | 	/* M_HW_RES4 */ | 
 | 	unsigned int CCI_LO_MTDES:8; | 
 | 	unsigned int CCI_LO_INITEN:1; | 
 | 	unsigned int CCI_LO_MONEN:1; | 
 | 	unsigned int CCI_LO_DVFS_LOW:3; | 
 | 	unsigned int CCI_LO_SPEC:3; | 
 | 	unsigned int CCI_LO_BDES:8; | 
 | 	unsigned int CCI_LO_MDES:8; | 
 |  | 
 | 	/* M_HW_RES5 */ | 
 | 	unsigned int CPU_L_MTDES:8; | 
 | 	unsigned int CPU_L_INITEN:1; | 
 | 	unsigned int CPU_L_MONEN:1; | 
 | 	unsigned int CPU_L_DVFS_LOW:3; | 
 | 	unsigned int CPU_L_SPEC:3; | 
 | 	unsigned int CPU_L_BDES:8; | 
 | 	unsigned int CPU_L_MDES:8; | 
 |  | 
 | 	/* M_HW_RES6 */ | 
 | 	unsigned int CPU_L_HI_MTDES:8; | 
 | 	unsigned int CPU_L_HI_INITEN:1; | 
 | 	unsigned int CPU_L_HI__MONEN:1; | 
 | 	unsigned int CPU_L_HI_DVFS_LOW:3; | 
 | 	unsigned int CPU_L_HI_SPEC:3; | 
 | 	unsigned int CPU_L_HI_BDES:8; | 
 | 	unsigned int CPU_L_HI_MDES:8; | 
 |  | 
 | 	/* M_HW_RES7 */ | 
 | 	unsigned int CPU_B_HI_DCBDET:8; | 
 | 	unsigned int CPU_B_HI_DCMDET:8; | 
 | 	unsigned int CPU_B_DCBDET:8; | 
 | 	unsigned int CPU_B_DCMDET:8; | 
 |  | 
 | 	/* M_HW_RES8 */ | 
 | 	unsigned int CCI_LO_DCBDET:8; | 
 | 	unsigned int CCI_LO_DCMDET:8; | 
 | 	unsigned int CCI_HI_DCBDET:8; | 
 | 	unsigned int CCI_HI_DCMDET:8; | 
 |  | 
 | 	/* M_HW_RES */ | 
 | 	unsigned int CPU_L_LO_MTDES:8; | 
 | 	unsigned int CPU_L_LO_INITEN:1; | 
 | 	unsigned int CPU_L_LO_MONEN:1; | 
 | 	unsigned int CPU_L_LO_DVFS_LOW:3; | 
 | 	unsigned int CPU_L_LO_SPEC:3; | 
 | 	unsigned int CPU_L_LO_BDES:8; | 
 | 	unsigned int CPU_L_LO_MDES:8; | 
 |  | 
 | 	/* M_HW_RES */ | 
 | 	unsigned int CCI_HI_MTDES:8; | 
 | 	unsigned int CCI_HI_INITEN:1; | 
 | 	unsigned int CCI_HI_MONEN:1; | 
 | 	unsigned int CCI_HI_DVFS_LOW:3; | 
 | 	unsigned int CCI_HI_SPEC:3; | 
 | 	unsigned int CCI_HI_BDES:8; | 
 | 	unsigned int CCI_HI_MDES:8; | 
 |  | 
 | 	/* M_HW_RES9 */ | 
 | 	unsigned int GPU_HI_MTDES:8; | 
 | 	unsigned int GPU_HI_INITEN:1; | 
 | 	unsigned int GPU_HI_MONEN:1; | 
 | 	unsigned int GPU_HI_DVFS_LOW:3; | 
 | 	unsigned int GPU_HI_SPEC:3; | 
 | 	unsigned int GPU_HI_BDES:8; | 
 | 	unsigned int GPU_HI_MDES:8; | 
 |  | 
 | 	/* M_HW_RES10 */ | 
 | 	unsigned int GPU_LO_MTDES:8; | 
 | 	unsigned int GPU_LO_INITEN:1; | 
 | 	unsigned int GPU_LO_MONEN:1; | 
 | 	unsigned int GPU_LO_DVFS_LOW:3; | 
 | 	unsigned int GPU_LO_SPEC:3; | 
 | 	unsigned int GPU_LO_BDES:8; | 
 | 	unsigned int GPU_LO_MDES:8; | 
 |  | 
 | 	/* M_HW_RES11 */ | 
 | 	unsigned int MD_VMODEM:32; | 
 |  | 
 | 	/* M_HW_RES12 */ | 
 | 	unsigned int MD_VNR:32; | 
 |  | 
 | 	/* M_HW_RES14 */ | 
 | 	unsigned int CPU_L_DCBDET:8; | 
 | 	unsigned int CPU_L_DCMDET:8; | 
 | 	unsigned int CPU_B_LO_DCBDET:8; | 
 | 	unsigned int CPU_B_LO_DCMDET:8; | 
 |  | 
 | 	/* M_HW_RES15 */ | 
 | 	unsigned int CPU_L_LO_DCBDET:8; | 
 | 	unsigned int CPU_L_LO_DCMDET:8; | 
 | 	unsigned int CPU_L_HI_DCBDET:8; | 
 | 	unsigned int CPU_L_HI_DCMDET:8; | 
 |  | 
 | 	/* M_HW_RES17 */ | 
 | 	unsigned int GPU_LO_DCBDET:8; | 
 | 	unsigned int GPU_LO_DCMDET:8; | 
 | 	unsigned int GPU_HI_DCBDET:8; | 
 | 	unsigned int GPU_HI_DCMDET:8; | 
 |  | 
 | 	/* M_HW_RES21 */ | 
 | 	unsigned int BCPU_A_T0_SVT:8; | 
 | 	unsigned int BCPU_A_T0_LVT:8; | 
 | 	unsigned int BCPU_A_T0_ULVT:8; | 
 | 	unsigned int LCPU_A_T0_SVT:8; | 
 |  | 
 | 	/* M_HW_RES22 */ | 
 | 	unsigned int LCPU_A_T0_LVT:8; | 
 | 	unsigned int LCPU_A_T0_ULVT:8; | 
 | 	unsigned int RES22_RSV:16; | 
 |  | 
 | 	/* M_HW_RES23 */ | 
 | 	unsigned int FINAL_VMIN_BCPU:8; | 
 | 	unsigned int FINAL_VMIN_LCPU:8; | 
 | 	unsigned int ATE_TEMP:8; | 
 | 	unsigned int SN_PATTERN:4; | 
 | 	unsigned int SN_VERSION:4; | 
 |  | 
 | 	/* M_HW_RES24 */ | 
 | 	unsigned int T_SVT_HV_BCPU:8; | 
 | 	unsigned int T_SVT_LV_BCPU:8; | 
 | 	unsigned int T_SVT_HV_LCPU:8; | 
 | 	unsigned int T_SVT_LV_LCPU:8; | 
 |  | 
 | 	/* M_HW_RES25 */ | 
 | 	unsigned int T_SVT_HV_BCPU_RT:8; | 
 | 	unsigned int T_SVT_LV_BCPU_RT:8; | 
 | 	unsigned int T_SVT_HV_LCPU_RT:8; | 
 | 	unsigned int T_SVT_LV_LCPU_RT:8; | 
 |  | 
 | 	/* M_HW_RES26 */ | 
 | 	unsigned int FPC_RECORVERY_BCPU:8; | 
 | 	unsigned int CPE_VMIN_BCPU:8; | 
 | 	unsigned int FPC_RECORVERY_LCPU:8; | 
 | 	unsigned int CPE_VMIN_LCPU:8; | 
 | }; | 
 |  | 
 | struct sn_log_data { | 
 | 	unsigned long long timestamp; | 
 | 	unsigned int reg_dump_cpe[MIN_SIZE_SN_DUMP_CPE]; | 
 | 	unsigned int reg_dump_sndata[SIZE_SN_DUMP_SENSOR]; | 
 | 	unsigned int reg_dump_sn_cpu[NUM_SN_CPU][SIZE_SN_MCUSYS_REG]; | 
 | 	struct sensing_stru sd[NR_SN_DET]; | 
 | 	unsigned int footprint[NR_SN_DET]; | 
 | 	unsigned int allfp; | 
 | #if VMIN_PREDICT_ENABLE | 
 | 	unsigned int sn_cpe_vop; | 
 | #endif | 
 | }; | 
 |  | 
 | struct sn_log_cal_data { | 
 | 	int64_t cpe_init_aging; | 
 | 	struct A_Tused_VT atvt; | 
 | 	int TEMP_CAL; | 
 | 	int volt_cross; | 
 | 	short CPE_Aging; | 
 | 	int8_t sn_aging; | 
 | 	int8_t delta_vc; | 
 | 	uint8_t T_SVT_HV_RT; | 
 | 	uint8_t T_SVT_LV_RT; | 
 | }; | 
 |  | 
 | struct sn_param { | 
 | 	int Param_A_Tused_SVT; | 
 | 	int Param_A_Tused_LVT; | 
 | 	int Param_A_Tused_ULVT; | 
 | 	int Param_A_T0_SVT; | 
 | 	int Param_A_T0_LVT; | 
 | 	int Param_A_T0_ULVT; | 
 | 	int Param_ATE_temp; | 
 | 	int Param_temp; | 
 | 	int Param_INTERCEPTION; | 
 | 	int8_t A_GB; | 
 | 	int8_t sn_temp_threshold; | 
 | 	int8_t Default_Aging; | 
 | 	int8_t threshold_H; | 
 | 	int8_t threshold_L; | 
 |  | 
 | 	unsigned char T_GB; | 
 |  | 
 | 	/* Formula for CPE_Vmin (Vmin prediction) */ | 
 | 	unsigned char CPE_GB; | 
 | 	unsigned char MSSV_GB; | 
 |  | 
 | }; | 
 |  | 
 | struct eemsn_log { | 
 | 	struct dvfs_vf_tbl vf_tbl_det[NR_EEMSN_DET]; | 
 | 	unsigned char eemsn_enable; | 
 | 	unsigned char sn_enable; | 
 | 	unsigned char ctrl_aging_Enable; | 
 | 	struct eemsn_log_det det_log[NR_EEMSN_DET]; | 
 | }; | 
 |  | 
 | struct eemsn_det { | 
 | 	int64_t		cpe_init_aging; | 
 | 	int temp; /* det temperature */ | 
 |  | 
 | 	/* dvfs */ | 
 | 	unsigned int cur_volt; | 
 | 	unsigned int *p_sn_cpu_coef; | 
 | 	struct sn_param *p_sn_cpu_param; | 
 | 	enum eemsn_det_id det_id; | 
 |  | 
 | 	unsigned int volt_tbl_pmic[MAX_NR_FREQ]; /* pmic value */ | 
 |  | 
 | 	/* for PMIC */ | 
 | 	unsigned short eemsn_v_base; | 
 | 	unsigned short eemsn_step; | 
 | 	unsigned short pmic_base; | 
 | 	unsigned short pmic_step; | 
 | 	short cpe_volt_total_mar; | 
 |  | 
 | 	/* dvfs */ | 
 | 	unsigned short freq_tbl[MAX_NR_FREQ]; | 
 | 	unsigned char volt_tbl_init2[MAX_NR_FREQ]; /* eem value */ | 
 | 	unsigned char volt_tbl_orig[MAX_NR_FREQ]; /* pmic value */ | 
 | 	unsigned char dst_volt_pmic; | 
 | 	unsigned char volt_tbl0_min; /* pmic value */ | 
 |  | 
 | 	unsigned char features; /* enum eemsn_features */ | 
 | 	unsigned char cur_phase; | 
 | 	unsigned char cur_oppidx; | 
 |  | 
 | 	const char *name; | 
 |  | 
 | 	unsigned char disabled; /* Disabled by error or sysfs */ | 
 | 	unsigned char num_freq_tbl; | 
 |  | 
 | 	unsigned char turn_pt; | 
 | 	unsigned char vmin_high; | 
 | 	unsigned char vmin_mid; | 
 | 	int8_t delta_vc; | 
 | 	int8_t sn_aging; | 
 | 	int8_t volt_offset; | 
 | 	int8_t volt_clamp; | 
 | 	/* int volt_offset:8; */ | 
 | 	unsigned int delta_ir:4; | 
 | 	unsigned int delta_vdppm:5; | 
 | }; | 
 |  | 
 | struct eemsn_dbg_log { | 
 | 	struct dvfs_vf_tbl vf_tbl_det[NR_EEMSN_DET]; | 
 | 	unsigned char eemsn_enable; | 
 | 	unsigned char sn_enable; | 
 | 	unsigned char ctrl_aging_Enable; | 
 | 	struct eemsn_log_det det_log[NR_EEMSN_DET]; | 
 | 	struct eemsn_vlog_det det_vlog[NR_EEMSN_DET]; | 
 | 	struct sn_log_data sn_log; | 
 | 	struct sn_log_cal_data sn_cal_data[NR_SN_DET]; | 
 | 	struct sn_param sn_cpu_param[NR_SN_DET]; | 
 | 	struct eemsn_devinfo efuse_devinfo; | 
 | 	unsigned int efuse_sv; | 
 | 	unsigned int efuse_sv2; | 
 | 	unsigned int picachu_sn_mem_base_phys; | 
 | 	unsigned char init2_v_ready; | 
 | 	unsigned char init_vboot_done; | 
 | 	unsigned char segCode; | 
 | 	unsigned char lock; | 
 | }; | 
 |  | 
 |  | 
 | struct eem_ipi_data { | 
 | 	unsigned int cmd; | 
 | 	union { | 
 | 		struct { | 
 | 			unsigned int arg[3]; | 
 | 		} data; | 
 | 	} u; | 
 | }; | 
 |  | 
 | #define det_to_id(det)	((det) - &eemsn_detectors[0]) | 
 |  | 
 | struct eemsn_det eemsn_detectors[NR_EEMSN_DET] = { | 
 | 	[EEMSN_DET_L] = { | 
 | 		.name			= "EEM_DET_L", | 
 | 		.det_id    = EEMSN_DET_L, | 
 | 		.features	= FEA_INIT01 | FEA_INIT02 | FEA_MON | FEA_SEN, | 
 | 		.volt_offset = 0, | 
 | 	}, | 
 | 	[EEMSN_DET_CCI] = { | 
 | 		.name			= "EEM_DET_CCI", | 
 | 		.det_id    = EEMSN_DET_CCI, | 
 | 		.features	= FEA_INIT02 | FEA_MON, | 
 | 		.volt_offset = 0, | 
 | 	}, | 
 | }; | 
 |  | 
 | #define for_each_det(det) \ | 
 | 	for (det = eemsn_detectors; \ | 
 | 	     det < (eemsn_detectors + ARRAY_SIZE(eemsn_detectors)); \ | 
 | 	     det++) | 
 |  | 
 | static unsigned int eem_to_cpueb(unsigned int cmd, | 
 | 					struct eem_ipi_data *eem_data) | 
 | { | 
 | 	unsigned int ret; | 
 |  | 
 | 	pr_debug("to_cpueb, cmd:%d\n", cmd); | 
 | 	eem_data->cmd = cmd; | 
 |  | 
 | 	ret = mtk_ipi_send_compl(&mcupm_ipidev, CH_S_EEMSN, | 
 | 		/*IPI_SEND_WAIT*/IPI_SEND_POLLING, eem_data, | 
 | 		sizeof(struct eem_ipi_data)/MBOX_SLOT_SIZE, 2000); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | unsigned int detid_to_dvfsid(struct eemsn_det *det) | 
 | { | 
 | 	unsigned int cpudvfsindex; | 
 | 	enum eemsn_det_id detid = det_to_id(det); | 
 |  | 
 | 	if (detid == EEMSN_DET_L) | 
 | 		cpudvfsindex = MT_CPU_DVFS_LL; | 
 | 	else | 
 | 		cpudvfsindex = MT_CPU_DVFS_CCI; | 
 |  | 
 | 	pr_debug("[%s] id:%d, cpudvfsindex:%d\n", __func__, | 
 | 		detid, cpudvfsindex); | 
 |  | 
 | 	return cpudvfsindex; | 
 | } | 
 |  | 
 | static int dbg_repo_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	int i; | 
 | 	u32 *repo = m->private; | 
 | 	char ch; | 
 |  | 
 | 	for (i = 0; i < DBG_REPO_NUM; i++) { | 
 | 		if (i >= REPO_I_LOG_S && (i - REPO_I_LOG_S) % | 
 | 		    ENTRY_EACH_LOG == 0) | 
 | 			ch = ':';	/* timestamp */ | 
 | 		else | 
 | 			ch = '.'; | 
 |  | 
 | 		seq_printf(m, "%4d%c%08x%c", | 
 | 			   i, ch, repo[i], i % 4 == 3 ? '\n' : ' '); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 | /* | 
 |  * =============================================== | 
 |  * PROCFS interface for debugging | 
 |  * =============================================== | 
 |  */ | 
 |  | 
 | /* | 
 |  * show current EEM stauts | 
 |  */ | 
 | static int eem_debug_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	struct eemsn_det *det = (struct eemsn_det *)m->private; | 
 |  | 
 | 	/* FIXME: EEMEN sometimes is disabled temp */ | 
 | 	seq_printf(m, "[%s] %s\n", | 
 | 		   ((char *)(det->name) + 8), | 
 | 		   det->disabled ? "disabled" : "enable" | 
 | 		  ); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * set EEM status by procfs interface | 
 |  */ | 
 | static ssize_t eem_debug_proc_write(struct file *file, | 
 | 				    const char __user *buffer, size_t count, loff_t *pos) | 
 | { | 
 | 	int ret; | 
 | 	int enabled = 0; | 
 | 	char *buf = (char *) __get_free_page(GFP_USER); | 
 | 	struct eemsn_det *det = (struct eemsn_det *)PDE_DATA(file_inode(file)); | 
 | 	struct eem_ipi_data eem_data; | 
 | 	int ipi_ret = 0; | 
 |  | 
 | 	if (!buf) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ret = -EINVAL; | 
 |  | 
 | 	if (count >= PAGE_SIZE) | 
 | 		goto out; | 
 |  | 
 | 	ret = -EFAULT; | 
 |  | 
 | 	if (copy_from_user(buf, buffer, count)) | 
 | 		goto out; | 
 |  | 
 | 	buf[count] = '\0'; | 
 |  | 
 | 	if (!kstrtoint(buf, 10, &enabled)) { | 
 | 		ret = 0; | 
 | 		memset(&eem_data, 0, sizeof(struct eem_ipi_data)); | 
 | 		eem_data.u.data.arg[0] = det_to_id(det); | 
 | 		eem_data.u.data.arg[1] = enabled; | 
 | 		ipi_ret = eem_to_cpueb(IPI_EEMSN_DEBUG_PROC_WRITE, &eem_data); | 
 | 		det->disabled = enabled; | 
 |  | 
 | 	} else | 
 | 		ret = -EINVAL; | 
 |  | 
 | out: | 
 | 	free_page((unsigned long)buf); | 
 |  | 
 | 	return (ret < 0) ? ret : count; | 
 | } | 
 |  | 
 | static int eem_aging_dump_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	struct eem_ipi_data eem_data; | 
 | 	int ipi_ret = 0; | 
 |  | 
 | 	memset(&eem_data, 0, sizeof(struct eem_ipi_data)); | 
 | 	ipi_ret = eem_to_cpueb(IPI_EEMSN_AGING_DUMP_PROC_SHOW, &eem_data); | 
 | 	seq_printf(m, "ipi_ret:%d\n", ipi_ret); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int eem_dump_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	struct eem_ipi_data eem_data; | 
 | 	unsigned int ipi_ret = 0; | 
 |  | 
 | 	memset(&eem_data, 0, sizeof(struct eem_ipi_data)); | 
 | 	ipi_ret = eem_to_cpueb(IPI_EEMSN_DUMP_PROC_SHOW, &eem_data); | 
 | 	seq_printf(m, "ipi_ret:%d\n", ipi_ret); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int eem_hrid_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	unsigned int i; | 
 | 	int devinfo = 0; | 
 |  | 
 | 	for (i = 0; i < 4; i++) { | 
 | 		nvmem_device_read(nvmem_dev, DEVINFO_HRID_0 + 0x4 * i, sizeof(__u32), &devinfo); | 
 | 		seq_printf(m, "%s[HRID][%d]: 0x%08X\n", EEM_TAG, i, devinfo); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int eem_efuse_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	unsigned int i; | 
 | 	int devinfo = 0; | 
 | 	for (i = 0; i < 25; i++) { | 
 | 		nvmem_device_read(nvmem_dev, DEVINFO_IDX_0 + 0x4 * i, sizeof(__u32), &devinfo); | 
 | 		seq_printf(m, "%s[PTP_DUMP] ORIG_RES%d: 0x%08X\n", EEM_TAG, i, devinfo); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int eem_mar_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	seq_printf(m, "%s[CPU_L][HIGH] 1:%d, 2:%d, 3:%d, 5:%d\n", | 
 | 		   EEM_TAG, LOW_TEMP_OFF, 0, | 
 | 		   HIGH_TEMP_OFF, AGING_VAL_CPU); | 
 |  | 
 | 	seq_printf(m, "%s[CPU_CCI][HIGH] 1:%d, 2:%d, 3:%d, 5:%d\n", | 
 | 		   EEM_TAG, LOW_TEMP_OFF, 0, | 
 | 		   HIGH_TEMP_OFF, AGING_VAL_CPU); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int eem_force_sensing_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	struct eem_ipi_data eem_data; | 
 | 	unsigned int ipi_ret = 0; | 
 |  | 
 | 	memset(&eem_data, 0, sizeof(struct eem_ipi_data)); | 
 | 	ipi_ret = eem_to_cpueb(IPI_EEMSN_FORCE_SN_SENSING, &eem_data); | 
 | 	seq_printf(m, "ret:%d\n", ipi_ret); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int eem_pull_data_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	struct eem_ipi_data eem_data; | 
 | 	unsigned int ipi_ret = 0; | 
 |  | 
 | 	memset(&eem_data, 0, sizeof(struct eem_ipi_data)); | 
 | 	ipi_ret = eem_to_cpueb(IPI_EEMSN_PULL_DATA, &eem_data); | 
 | 	seq_printf(m, "ret:%d\n", ipi_ret); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * show EEM offset | 
 |  */ | 
 | static int eem_offset_proc_show(struct seq_file *m, void *v) | 
 | { | 
 | 	struct eemsn_det *det = (struct eemsn_det *)m->private; | 
 |  | 
 | 	seq_printf(m, "%d\n", det->volt_offset); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * set EEM offset by procfs | 
 |  */ | 
 | static ssize_t eem_offset_proc_write(struct file *file, | 
 | 				     const char __user *buffer, size_t count, loff_t *pos) | 
 | { | 
 | 	int ret; | 
 | 	char *buf = (char *) __get_free_page(GFP_USER); | 
 | 	int offset = 0; | 
 | 	struct eemsn_det *det = (struct eemsn_det *)PDE_DATA(file_inode(file)); | 
 | 	unsigned int ipi_ret = 0; | 
 | 	struct eem_ipi_data eem_data; | 
 |  | 
 | 	if (!buf) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ret = -EINVAL; | 
 |  | 
 | 	if (count >= PAGE_SIZE) | 
 | 		goto out; | 
 |  | 
 | 	ret = -EFAULT; | 
 |  | 
 | 	if (copy_from_user(buf, buffer, count)) | 
 | 		goto out; | 
 |  | 
 | 	buf[count] = '\0'; | 
 |  | 
 | 	if (!kstrtoint(buf, 10, &offset)) { | 
 | 		ret = 0; | 
 | 		memset(&eem_data, 0, sizeof(struct eem_ipi_data)); | 
 | 		eem_data.u.data.arg[0] = det_to_id(det); | 
 | 		eem_data.u.data.arg[1] = offset; | 
 | 		ipi_ret = eem_to_cpueb(IPI_EEMSN_OFFSET_PROC_WRITE, &eem_data); | 
 | 		/* to show in eem_offset_proc_show */ | 
 | 		det->volt_offset = (signed char)offset; | 
 | 		pr_debug("set volt_offset %d(%d)\n", offset, det->volt_offset); | 
 | 	} else { | 
 | 		ret = -EINVAL; | 
 | 		pr_debug("bad argument_1!! argument should be \"0\"\n"); | 
 | 	} | 
 |  | 
 | out: | 
 | 	free_page((unsigned long)buf); | 
 |  | 
 | 	return (ret < 0) ? ret : count; | 
 | } | 
 |  | 
 | #if SUPPORT_PICACHU | 
 | static void get_picachu_efuse(void) | 
 | { | 
 | 	/* int *val; */ | 
 | 	phys_addr_t picachu_mem_base_phys; | 
 | 	phys_addr_t picachu_mem_size; | 
 | 	phys_addr_t picachu_mem_base_virt = 0; | 
 | 	unsigned int sig; | 
 | 	void __iomem *addr_ptr; | 
 | 	void __iomem *spare1phys; | 
 |  | 
 | 	/* val = (int *)&eem_devinfo; */ | 
 |  | 
 | 	picachu_mem_size = 0x80000; | 
 | 	spare1phys = ioremap(EEM_PHY_SPARE0, 0); | 
 | 	picachu_mem_base_phys = eem_read(spare1phys); | 
 | 	if ((void __iomem *)picachu_mem_base_phys != NULL) | 
 | 		picachu_mem_base_virt = | 
 | 			(phys_addr_t)(uintptr_t)ioremap_wc( | 
 | 			picachu_mem_base_phys, | 
 | 			picachu_mem_size); | 
 |  | 
 | #if 0 | 
 | 	eem_error("phys:0x%llx, size:0x%llx, virt:0x%llx\n", | 
 | 		(unsigned long long)picachu_mem_base_phys, | 
 | 		(unsigned long long)picachu_mem_size, | 
 | 		(unsigned long long)picachu_mem_base_virt); | 
 | #endif | 
 | 	if ((void __iomem *)(picachu_mem_base_virt) != NULL) { | 
 | 		/* 0x60000 was reserved for eem efuse using */ | 
 | 		addr_ptr = (void __iomem *)(picachu_mem_base_virt | 
 | 			+ 0x60000); | 
 |  | 
 | 		/* check signature */ | 
 | 		sig = (eem_read(addr_ptr) >> | 
 | 			PICACHU_SIGNATURE_SHIFT_BIT) & 0xff; | 
 |  | 
 | 		if (sig == PICACHU_SIG) { | 
 | 			ctrl_agingload_enable = eem_read(addr_ptr) & 0x1; | 
 | 			addr_ptr += 4; | 
 | 			memcpy(eemsn_log->vf_tbl_det, | 
 | 				addr_ptr, sizeof(eemsn_log->vf_tbl_det)); | 
 |  | 
 | #if 0 | 
 | 			/* check efuse data */ | 
 | 			for (i = 1; i < cnt; i++) { | 
 | 				if (((i == 3) || (i == 4) || (i == 7)) && | 
 | 				(eem_read(addr_ptr + i * 4) == 0)) { | 
 | 					eem_error("Wrong PI-OD%d: 0x%x\n", | 
 | 						i, eem_read(addr_ptr + i * 4)); | 
 | 					return; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			for (i = 1; i < cnt; i++) | 
 | 				val[i] = eem_read(addr_ptr + i * 4); | 
 | #endif | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | /* | 
 |  * All | 
 |  */ | 
 | PROC_FOPS_RO(dbg_repo); | 
 | PROC_FOPS_RW(eem_debug); | 
 | PROC_FOPS_RW(eem_offset); | 
 | PROC_FOPS_RO(eem_dump); | 
 | PROC_FOPS_RO(eem_aging_dump); | 
 | PROC_FOPS_RO(eem_hrid); | 
 | PROC_FOPS_RO(eem_efuse); | 
 | PROC_FOPS_RO(eem_mar); | 
 | PROC_FOPS_RO(eem_force_sensing); | 
 | PROC_FOPS_RO(eem_pull_data); | 
 |  | 
 | static int __init mtk_cpufreq_preinit(void) | 
 | { | 
 | 	struct cdvfs_data eem_data; | 
 | 	int err = 0; | 
 | 	phys_addr_t eem_log_phy_addr, eem_log_virt_addr; | 
 |  | 
 | 	err = mtk_ipi_register(&mcupm_ipidev, CH_S_EEMSN, NULL, NULL, | 
 | 			                               (void *) &ipi_ackdata); | 
 | 	if (err != 0) { | 
 | 		pr_info("%s error ret:%d\n", __func__, err); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	eem_log_phy_addr = | 
 | 		mcupm_reserve_mem_get_phys(MCUPM_EEMSN_MEM_ID); | 
 | 	eem_log_virt_addr = | 
 | 		mcupm_reserve_mem_get_virt(MCUPM_EEMSN_MEM_ID); | 
 |  | 
 | 	if (eem_log_virt_addr != 0) | 
 | 		eemsn_log = (struct eemsn_dbg_log *)eem_log_virt_addr; | 
 | 	else | 
 | 		pr_info("mcupm_reserve_mem_get_virt fail\n"); | 
 |  | 
 | 	memset(&eem_data, 0, sizeof(struct cdvfs_data)); | 
 | 	eem_data.u.set_fv.arg[0] = eem_log_phy_addr; | 
 | 	eem_data.u.set_fv.arg[1] = sizeof(struct eemsn_log); | 
 |  | 
 | 	/* eemsn_log->efuse_sv = eem_read(EEM_TEMPSPARE1); */ | 
 |  | 
 | #if SUPPORT_PICACHU | 
 | 	get_picachu_efuse(); | 
 | #endif | 
 |  | 
 | #if defined(MC50_LOAD) | 
 | 	/* force set freq table */ | 
 | 	memcpy(eemsn_log->vf_tbl_det, | 
 | 		mc50_tbl, sizeof(eemsn_log->vf_tbl_det)); | 
 | #endif | 
 |  | 
 | 	eem_data.cmd = IPI_EEMSN_SHARERAM_INIT; | 
 |  | 
 | #if 0 /* TODO: wait mtk efsue ready */ | 
 | 	mtk_ipi_send_compl(&mcupm_ipidev, CH_S_EEMSN, | 
 | 			   /*IPI_SEND_WAIT*/IPI_SEND_POLLING, &eem_data, | 
 | 			   sizeof(struct cdvfs_data)/MBOX_SLOT_SIZE, 2000); | 
 | #endif | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int create_cpufreq_debug_fs(void) | 
 | { | 
 | 	int i; | 
 | 	struct proc_dir_entry *dir = NULL; | 
 | 	struct proc_dir_entry *eem_dir = NULL; | 
 | 	struct proc_dir_entry *det_dir = NULL; | 
 | 	struct eemsn_det *det; | 
 |  | 
 | 	struct pentry { | 
 | 		const char *name; | 
 | 		const struct file_operations *fops; | 
 | 		void *data; | 
 | 	}; | 
 |  | 
 | 	struct pentry det_entries[] = { | 
 | 		PROC_ENTRY(eem_debug), | 
 | 		PROC_ENTRY(eem_offset), | 
 | 	}; | 
 | 	struct pentry eem_entries[] = { | 
 | 		PROC_ENTRY(eem_dump), | 
 | 		PROC_ENTRY(eem_aging_dump), | 
 | 		PROC_ENTRY(eem_hrid), | 
 | 		PROC_ENTRY(eem_efuse), | 
 | 		PROC_ENTRY(eem_mar), | 
 | 		PROC_ENTRY(eem_force_sensing), | 
 | 		PROC_ENTRY(eem_pull_data), | 
 | 	}; | 
 |  | 
 | 	dir = proc_mkdir("cpuhvfs", NULL); | 
 | 	if (!dir) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	if (!proc_create_data("dbg_repo", 0664, dir, &dbg_repo_proc_fops, csram_base)) | 
 | 		return -EIO; | 
 |  | 
 | 	eem_dir = proc_mkdir("eem", NULL); | 
 | 	for (i = 0; i < ARRAY_SIZE(eem_entries); i++) | 
 | 		if(!proc_create(eem_entries[i].name, 0664, eem_dir, eem_entries[i].fops)) | 
 | 			return -3; | 
 |  | 
 | 	for_each_det(det) { | 
 | 		if(det->features == 0) | 
 | 			continue; | 
 |  | 
 | 		det_dir = proc_mkdir(det->name, eem_dir); | 
 | 		if (!det_dir) | 
 | 			return -2; | 
 |  | 
 | 		for(i = 0 ; i<ARRAY_SIZE(det_entries); i++) | 
 | 			if(!proc_create_data(det_entries[i].name, 0644, det_dir, det_entries[i].fops, det)) | 
 | 				return -3; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int get_devinfo(struct platform_device *pdev) | 
 | { | 
 | #if 0 /* TODO: wait mtk efsue ready */ | 
 | 	int *val, ret = 1, i = 0, devinfo = 0; | 
 |  | 
 | 	val = (int *)&eemsn_log->efuse_devinfo; | 
 |  | 
 | 	nvmem_dev = nvmem_device_get(&pdev->dev, "mtk_efuse"); | 
 | 	if (IS_ERR(nvmem_dev)) | 
 | 		return PTR_ERR(nvmem_dev); | 
 |  | 
 | 	for (i = 0; i < 12; i++) { | 
 | 		ret = nvmem_device_read(nvmem_dev, DEVINFO_IDX_0 + 0x4 * i, sizeof(__u32), &devinfo); | 
 | 		if (ret != sizeof(__u32)) { | 
 | 			ret = -EINVAL; | 
 | 			goto release_nvmem; | 
 | 		} | 
 | 		val[i]= devinfo; | 
 | 	} | 
 |  | 
 | 	ret = 1; | 
 |  | 
 | release_nvmem: | 
 | 	nvmem_device_put(nvmem_dev); | 
 |  | 
 | 	return ret; | 
 | #else | 
 | 	return 1; | 
 | #endif | 
 | } | 
 |  | 
 | static int mtk_cpufreq_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct mtk_cpu_dvfs_info *info; | 
 | 	struct list_head *list, *tmp; | 
 | 	int cpu, ret; | 
 | 	struct cdvfs_data cdvfs_d; | 
 | 	uint32_t cpufreq_buf[4]; | 
 |  | 
 | 	ret = mtk_ipi_register(&mcupm_ipidev, CH_S_CPU_DVFS, NULL, NULL, | 
 | 			 (void *) &cpufreq_buf); | 
 | 	if (ret) | 
 | 		return -EINVAL; | 
 |  | 
 | 	cdvfs_d.cmd = IPI_DVFS_INIT; | 
 | 	cdvfs_d.u.set_fv.arg[0] = 0; | 
 | 	ret = mtk_ipi_send_compl(&mcupm_ipidev, CH_S_CPU_DVFS, | 
 | 				 IPI_SEND_POLLING, &cdvfs_d, | 
 | 				 sizeof(struct cdvfs_data)/MBOX_SLOT_SIZE, | 
 | 				 2000); | 
 | 	if (ret) | 
 | 		return 0; | 
 |  | 
 | 	mtk_cpufreq_preinit(); | 
 |  | 
 | 	ret = get_devinfo(pdev); | 
 | 	if(ret <= 0) | 
 | 		return ret; | 
 |  | 
 | 	for_each_possible_cpu(cpu) { | 
 | 		info = mtk_cpu_dvfs_info_lookup(cpu); | 
 | 		if (info) | 
 | 			continue; | 
 |  | 
 | 		info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 
 | 		if (!info) { | 
 | 			ret = -ENOMEM; | 
 | 			goto release_dvfs_info_list; | 
 | 		} | 
 |  | 
 | 		info->csram_base = of_iomap(pdev->dev.of_node, 0); | 
 | 		if (!info->csram_base) { | 
 | 			ret = -ENOMEM; | 
 | 			goto release_dvfs_info_list; | 
 | 		} | 
 |  | 
 | 		ret = mtk_cpu_dvfs_info_init(info, cpu); | 
 | 		if (ret) | 
 | 			goto release_dvfs_info_list; | 
 |  | 
 | 		list_add(&info->list_head, &dvfs_info_list); | 
 | 	} | 
 |  | 
 | 	ret = cpufreq_register_driver(&mtk_cpufreq_driver); | 
 | 	if (ret) | 
 | 		goto release_dvfs_info_list; | 
 |  | 
 | 	csram_base = of_iomap(pdev->dev.of_node, 0); | 
 | 	create_cpufreq_debug_fs(); | 
 |  | 
 | 	return 0; | 
 |  | 
 | release_dvfs_info_list: | 
 | 	list_for_each_safe(list, tmp, &dvfs_info_list) { | 
 | 		info = list_entry(list, struct mtk_cpu_dvfs_info, list_head); | 
 | 		mtk_cpu_dvfs_info_release(info); | 
 | 		list_del(list); | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static const struct of_device_id mtk_cpufreq_machines[] = { | 
 | 	{ .compatible = "mediatek,mcupm-dvfsp", }, | 
 | 	{ } | 
 | }; | 
 |  | 
 | MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines); | 
 |  | 
 | static struct platform_driver mtk_cpufreq_platdrv = { | 
 | 	.probe		= mtk_cpufreq_probe, | 
 | 	.driver = { | 
 | 		.name	= "dvfsp", | 
 | 		.of_match_table = of_match_ptr(mtk_cpufreq_machines), | 
 | 	}, | 
 | }; | 
 | module_platform_driver(mtk_cpufreq_platdrv); | 
 |  | 
 | MODULE_AUTHOR("Wei-Chia Su <Wei-Chia.Su@mediatek.com>"); | 
 | MODULE_DESCRIPTION("Medaitek MCUPM CPUFreq Platform driver"); | 
 | MODULE_LICENSE("GPL v2"); |