[Feature] add GA346 baseline version
Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/cpufreq/mediatek-mcupm-cpufreq.c b/src/kernel/linux/v4.19/drivers/cpufreq/mediatek-mcupm-cpufreq.c
new file mode 100644
index 0000000..dec8880
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/cpufreq/mediatek-mcupm-cpufreq.c
@@ -0,0 +1,1245 @@
+// 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");