/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 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" #define PICACHU_SIGNATURE (0xA5) #define PICACHU_PTP1_EFUSE_MASK (0xFFFFFF) #define PICACHU_VMIN_SHIFT_BIT (0) #define PICACHU_OFFSET_SHIFT_BIT (8) #define PICACHU_WFE_STATUS_SHIFT_BIT (16) #define PICACHU_SIGNATURE_SHIFT_BIT (24) #define PICACHU_SUPPORT_CLUSTERS 1 #define EEM_BASEADDR (0x1100B000) #define EEM_SIZE (0x1000) #define EEMSPARE0 (eem_base_addr + 0xF20) #define EEMSPARE1 (eem_base_addr + 0xF24) #define EEMSPARE2 (eem_base_addr + 0xF28) #define EEMSPARE3 (eem_base_addr + 0xF2C) #undef TAG #define TAG "[Picachu] " #define PICACHU_PR_ERR(fmt, args...) \ pr_err(TAG"[ERROR]"fmt, ##args) #define PICACHU_INFO(fmt, args...) \ pr_info(TAG""fmt, ##args) #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} struct picachu_info { int vmin; int offset; unsigned int wfe_status; /* * Bit[7:0]: MTDES * Bit[15:8]: BDES * Bit[23:16]: MDES */ unsigned int ptp1_efuse[PICACHU_SUPPORT_CLUSTERS]; /* * Bit[7:0]: MTDES * Bit[15:8]: BDES * Bit[23:16]: MDES */ unsigned int orig_ptp1_efuse; }; struct pentry { const char *name; const struct file_operations *fops; }; static struct picachu_info picachu_data; static void __iomem *eem_base_addr; static void dump_picachu_info(struct seq_file *m, struct picachu_info *info) { unsigned int i; seq_printf(m, "0x%X\n", info->vmin); seq_printf(m, "0x%X\n", info->offset); seq_printf(m, "0x%X\n", info->wfe_status); for (i = 0; i < PICACHU_SUPPORT_CLUSTERS; i++) seq_printf(m, "0x%X\n", info->ptp1_efuse[i]); seq_printf(m, "0x%X\n", info->orig_ptp1_efuse); } static int picachu_dump_proc_show(struct seq_file *m, void *v) { dump_picachu_info(m, &picachu_data); return 0; } PROC_FOPS_RO(picachu_dump); static int create_procfs(void) { int i; struct proc_dir_entry *dir = NULL; struct pentry entries[] = { PROC_ENTRY(picachu_dump), }; dir = proc_mkdir("picachu", NULL); if (!dir) { PICACHU_PR_ERR("[%s]: mkdir /proc/picachu failed\n", __func__); return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(entries); i++) { if (!proc_create(entries[i].name, S_IRUGO | S_IWUSR | S_IWGRP, dir, entries[i].fops)) { PICACHU_PR_ERR("[%s]: create /proc/picachu/%s failed\n", __func__, entries[i].name); return -ENOMEM; } } return 0; } static void picachu_get_data(void) { unsigned int i, val, tmp; for (i = 0; i < PICACHU_SUPPORT_CLUSTERS; i++) { val = picachu_read(EEMSPARE0 + (i << 2)); tmp = (val >> PICACHU_SIGNATURE_SHIFT_BIT) & 0xff; if (tmp != PICACHU_SIGNATURE) continue; picachu_data.ptp1_efuse[i] = val & PICACHU_PTP1_EFUSE_MASK; } /* Get original ptp1 efuse for MDES/BDES/MTDES. */ val = picachu_read(EEMSPARE0 + (i << 2)); tmp = (val >> PICACHU_SIGNATURE_SHIFT_BIT) & 0xff; if (tmp == PICACHU_SIGNATURE) picachu_data.orig_ptp1_efuse = val & PICACHU_PTP1_EFUSE_MASK; val = picachu_read(EEMSPARE3); tmp = (val >> PICACHU_SIGNATURE_SHIFT_BIT) & 0xff; if (tmp != PICACHU_SIGNATURE) return; picachu_data.vmin = (val >> PICACHU_VMIN_SHIFT_BIT) & 0xff; picachu_data.offset = (val >> PICACHU_OFFSET_SHIFT_BIT) & 0xff; picachu_data.wfe_status = (val >> PICACHU_WFE_STATUS_SHIFT_BIT) & 0xff; } static int __init picachu_init(void) { unsigned int i; eem_base_addr = ioremap(EEM_BASEADDR, EEM_SIZE); if (!eem_base_addr) { PICACHU_PR_ERR("ioremap failed!\n"); return -ENOMEM; } picachu_get_data(); create_procfs(); /* Update Picachu calibration data if the data is valid. */ for (i = 0; i < PICACHU_SUPPORT_CLUSTERS; i++) { if (!picachu_data.ptp1_efuse[i]) continue; eem_set_pi_efuse(i, picachu_data.ptp1_efuse[i]); } 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");