// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OF #include #include #include #include #include #endif #include "mtk_eem.h" #if ENABLE_LOO #define NR_PTP1_EFUSE_SPARE_REG 5 #else #define NR_PTP1_EFUSE_SPARE_REG 1 #endif #define PICACHU_SIGNATURE (0xA5) #define PICACHU_PTP1_EFUSE_MASK (0xFFFFFF) #define PICACHU_SIGNATURE_SHIFT_BIT (24) #define EEM_BASEADDR (0x1100B000) #define EEM_SIZE (0x1000) #define TEMPSPARE0 (0xF0) #define TEMPSPARE1 (0xF4) #define TEMPSPARE2 (0xF8) #define EEMSPARE0 (0xF20) #define EEMSPARE1 (0xF24) #define EEMSPARE2 (0xF28) #define EEMSPARE3 (0xF2C) #define TEMPSPARE0_OFFSET (0x0F0) #define EEMSPARE0_OFFSET (0xF20) #undef TAG #define TAG "[Picachu] " #define picachu_read(addr) __raw_readl((void __iomem *)(addr)) #define picachu_write(addr, val) mt_reg_sync_writel(val, 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 PICACHU_PROC_ENTRY_ATTR (0440) enum { OPPH_VMIN_SEARCH, /* OPP High -> OPP0 */ #if ENABLE_LOO OPPM_VMIN_SEARCH, /* OPP Medium */ #endif NR_OPP_VMIN_SEARCH }; struct picachu_info { /* * Bit[7:0]: vmin@opp0 * Bit[15:8]: reserved * Bit[23:16]: pi_offset@opp0 * Bit[31:24]: reserved */ unsigned int pi_offset_n_vmin; /* * L-H, 2L-H, CCI, L-M, 2L-M: * Bit[7:0]: MTDES * Bit[15:8]: BDES * Bit[23:16]: MDES */ unsigned int ptp1_efuse[NR_PTP1_EFUSE_SPARE_REG]; }; enum mt_picachu_vproc_id { MT_PICACHU_LITTLE_VPROC, /* Little-little, Little and CCI */ NR_PICACHU_VPROC, }; static struct picachu_info picachu_data[NR_PICACHU_VPROC]; static void __iomem *eem_base_addr; static unsigned int efuse_spare_reg[NR_PTP1_EFUSE_SPARE_REG] = {EEMSPARE0}; struct picachu_proc { char *name; int vproc_id; unsigned int spare_reg_offset; umode_t mode; }; struct pentry { const char *name; const struct file_operations *fops; }; static struct picachu_proc picachu_proc_list[] = { {"little", MT_PICACHU_LITTLE_VPROC, EEMSPARE0_OFFSET, PICACHU_PROC_ENTRY_ATTR}, {0}, }; static void dump_picachu_info(struct seq_file *m, struct picachu_info *info) { unsigned int i; seq_printf(m, "0x%X\n", info->pi_offset_n_vmin); for (i = 0; i < NR_PTP1_EFUSE_SPARE_REG; i++) seq_printf(m, "0x%X\n", info->ptp1_efuse[i]); } static int picachu_dump_proc_show(struct seq_file *m, void *v) { dump_picachu_info(m, (struct picachu_info *) m->private); return 0; } PROC_FOPS_RO(picachu_dump); static int create_procfs_entries(struct proc_dir_entry *dir, struct picachu_proc *proc) { int i, num; struct pentry entries[] = { PROC_ENTRY(picachu_dump), }; num = ARRAY_SIZE(entries); for (i = 0; i < num; i++) { if (!proc_create_data(entries[i].name, proc->mode, dir, entries[i].fops, (void *) &picachu_data[proc->vproc_id])) { pr_notice("[%s]: create /proc/picachu/%s failed\n", __func__, entries[i].name); return -ENOMEM; } } return 0; } static int create_procfs(void) { struct proc_dir_entry *root, *dir; struct picachu_proc *proc; int ret; root = proc_mkdir("picachu", NULL); if (!root) { pr_notice("[%s]: mkdir /proc/picachu failed\n", __func__); return -ENOMEM; } for (proc = picachu_proc_list; proc->name; proc++) { dir = proc_mkdir(proc->name, root); if (!dir) { pr_notice("[%s]: mkdir /proc/picachu/%s failed\n", __func__, proc->name); return -ENOMEM; } ret = create_procfs_entries(dir, proc); if (ret) return ret; } return 0; } static void picachu_get_data(enum mt_picachu_vproc_id vproc_id) { struct picachu_proc *proc; unsigned int i, val, tmp; struct picachu_info *p; if (vproc_id >= NR_PICACHU_VPROC) return; p = &picachu_data[vproc_id]; for (proc = picachu_proc_list; proc->name; proc++) { if (proc->vproc_id == vproc_id) break; } for (i = 0; i < NR_PTP1_EFUSE_SPARE_REG; i++) { val = picachu_read(eem_base_addr + efuse_spare_reg[i]); tmp = (val >> PICACHU_SIGNATURE_SHIFT_BIT) & 0xff; if (tmp != PICACHU_SIGNATURE) continue; p->ptp1_efuse[i] = val & PICACHU_PTP1_EFUSE_MASK; } p->pi_offset_n_vmin = picachu_read(eem_base_addr + EEMSPARE1); } static int eem_ctrl_id[NR_PICACHU_VPROC][NR_PTP1_EFUSE_SPARE_REG] = { [MT_PICACHU_LITTLE_VPROC] = {EEM_CTRL_2L}, }; #if !EEM_FAKE_EFUSE static void picachu_apply_efuse_to_eem(enum mt_picachu_vproc_id id, struct picachu_info *p) { int i; for (i = 0; i < NR_PTP1_EFUSE_SPARE_REG; i++) { if (!p->ptp1_efuse[i] || eem_ctrl_id[id][i] == -1) continue; eem_set_pi_efuse(eem_ctrl_id[id][i], p->ptp1_efuse[i]); } } #else static void picachu_apply_efuse_to_eem(enum mt_picachu_vproc_id id, struct picachu_info *p) { } #endif static int __init picachu_init(void) { unsigned int i; eem_base_addr = ioremap(EEM_BASEADDR, EEM_SIZE); if (!eem_base_addr) { pr_notice("ioremap failed!\n"); return -ENOMEM; } /* Update Picachu calibration data if the data is valid. */ for (i = 0; i < NR_PICACHU_VPROC; i++) { picachu_get_data(i); picachu_apply_efuse_to_eem(i, &picachu_data[i]); } create_procfs(); return 0; } static void __exit picachu_exit(void) { if (eem_base_addr) iounmap(eem_base_addr); } subsys_initcall(picachu_init); MODULE_DESCRIPTION("MediaTek Picachu Driver v0.1"); MODULE_LICENSE("GPL");