// 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 /* #define CREATE_TRACE_POINTS */ /* #include */ #define trace_slbc_api(f, id) #define trace_slbc_data(f, data) #include #include #include static struct pm_qos_request slbc_qos_request; #define SLBC_WAY_SIZE 0x80000 #if IS_ENABLED(CONFIG_MTK_L3C_PART) #include #endif /* CONFIG_MTK_L3C_PART */ /* #define SLBC_TRACE */ #define ENABLE_SLBC #define SLBC_WAY_A_BASE 0x0f000000 #define SLBC_WAY_B_BASE 0x680000000 #define SLBC_PADDR_MASK 0x00ffffff static struct mtk_slbc *slbc; static int slb_disable; static int slc_disable; static int slbc_sram_enable; static u32 slbc_force; static int buffer_ref; static u32 acp_ref; static u32 slbc_ref; static u32 slbc_sta; static u32 slbc_ack_c; static u32 slbc_ack_g; static u32 cpuqos_mode; static u32 slbc_sram_con; static u32 slbc_cache_used; static u32 slbc_pmu_0; static u32 slbc_pmu_1; static u32 slbc_pmu_2; static u32 slbc_pmu_3; static u32 slbc_pmu_4; static u32 slbc_pmu_5; static u32 slbc_pmu_6; static int debug_level; static int uid_ref[UID_MAX]; static int slbc_mic_num = 3; static int slbc_inner = 5; static int slbc_outer = 5; static u64 req_val_count; static u64 rel_val_count; static u64 req_val_min; static u64 req_val_max; static u64 req_val_total; static u64 rel_val_min; static u64 rel_val_max; static u64 rel_val_total; static struct slbc_data test_d; static LIST_HEAD(slbc_ops_list); static DEFINE_MUTEX(slbc_ops_lock); /* 1 in bit is from request done to relase done */ static unsigned long slbc_uid_used; /* 1 in bit is for mask */ static unsigned long slbc_sid_mask; /* 1 in bit is under request flow */ static unsigned long slbc_sid_req_q; /* 1 int bit is under release flow */ static unsigned long slbc_sid_rel_q; /* 1 int bit is for slot ussage */ static unsigned long slbc_slot_used; static struct slbc_config p_config[] = { /* SLBC_ENTRY(id, sid, max, fix, p, extra, res, cache) */ SLBC_ENTRY(UID_MM_VENC, 0, 0, 0, 0, 0x0, 0x7c0, 0), SLBC_ENTRY(UID_SH_P2, 1, 0, 0, 0, 0x0, 0x03f, 0), SLBC_ENTRY(UID_SH_APU, 2, 0, 0, 0, 0x0, 0x03f, 0), SLBC_ENTRY(UID_MML, 3, 0, 0, 0, 0x0, 0x030, 0), SLBC_ENTRY(UID_AINR, 4, 0, 0, 0, 0x0, 0x800, 0), SLBC_ENTRY(UID_DISP, 5, 0, 0, 0, 0x0, 0x030, 0), SLBC_ENTRY(UID_AOV_DC, 6, 0, 0, 0, 0x0, 0x007, 0), SLBC_ENTRY(UID_AOV_APU, 7, 0, 0, 0, 0x0, 0x038, 0), SLBC_ENTRY(UID_AISR_APU, 8, 0, 0, 0, 0x0, 0x00c, 0), SLBC_ENTRY(UID_AISR_MML, 9, 0, 0, 0, 0x0, 0x00c, 0), SLBC_ENTRY(UID_SH_P1, 10, 0, 0, 0, 0x0, 0x03f, 0), SLBC_ENTRY(UID_SMT, 11, 0, 0, 0, 0x0, 0x03f, 0), SLBC_ENTRY(UID_APU, 12, 0, 0, 0, 0x0, 0x03f, 0), SLBC_ENTRY(UID_AOD, 13, 0, 0, 0, 0x0, 0x7c0, 0), SLBC_ENTRY(UID_BIF, 14, 0, 0, 0, 0x0, 0x03f, 0), }; u32 slbc_sram_read(u32 offset) { if (!slbc_enable) return 0; if (!slbc_sram_enable) return 0; if (!slbc->sram_vaddr || offset >= slbc->regsize) return 0; /* pr_info("#@# %s(%d) regs 0x%x 0%x\n", __func__, __LINE__, slbc->regs, offset); */ return readl(slbc->sram_vaddr + offset); } void slbc_sram_write(u32 offset, u32 val) { if (!slbc_enable) return; if (!slbc_sram_enable) return; if (!slbc->sram_vaddr || offset >= slbc->regsize) return; /* pr_info("#@# %s(%d) regs 0x%x 0%x\n", __func__, __LINE__, slbc->regs, offset); */ writel(val, slbc->sram_vaddr + offset); } void slbc_sram_init(struct mtk_slbc *slbc) { int i; pr_info("slbc_sram addr:0x%lx len:%d\n", (unsigned long)slbc->regs, slbc->regsize); /* print_hex_dump(KERN_INFO, "SLBC: ", DUMP_PREFIX_OFFSET, */ /* 16, 4, slbc->sram_vaddr, slbc->regsize, 1); */ for (i = 0; i < slbc->regsize; i += 4) writel(0x0, slbc->sram_vaddr + i); } static void slbc_set_sram_data(struct slbc_data *d) { /* pr_info("slbc: set pa:%lx va:%lx\n", */ /* (unsigned long)d->paddr, (unsigned long)d->vaddr); */ } static void slbc_clr_sram_data(struct slbc_data *d) { /* pr_info("slbc: clr pa:%lx va:%lx\n", */ /* (unsigned long)d->paddr, (unsigned long)d->vaddr); */ } static void slbc_debug_log(const char *fmt, ...) { #ifdef SLBC_DEBUG static char buf[1024]; va_list va; int len; va_start(va, fmt); len = vsnprintf(buf, sizeof(buf), fmt, va); va_end(va); if (len) pr_info("#@# %s\n", buf); #endif /* SLBC_DEBUG */ } static int slbc_get_sid_by_uid(enum slbc_uid uid) { int i; for (i = 0; i < ARRAY_SIZE(p_config); i++) { if (p_config[i].uid == uid) return p_config[i].slot_id; } return SID_NOT_FOUND; } static u32 slbc_read_debug_sram(int sid) { if (sid < 0 || sid >= ARRAY_SIZE(p_config) || sid >= 16) return SID_NOT_FOUND; if (sid < 8) return slbc_sram_read(SLBC_DEBUG_0 + sid * 4); else return slbc_sram_read(SLBC_DEBUG_8 + (sid - 8) * 4); } void slbc_force_cmd(unsigned int force) { if ((force & 0xffff) < FR_MAX) { slbc_force = force; slbc_force_scmi_cmd(force); } } static int slbc_request_cache(struct slbc_data *d) { int ret = 0; /* slbc_debug_log("%s: TP_CACHE\n", __func__); */ ret = _slbc_request_cache_scmi(d); return ret; } static int slbc_request_status(struct slbc_data *d) { int ret = 0; int uid = d->uid; int sid; struct slbc_config *config; /* slbc_debug_log("%s: TP_BUFFER\n", __func__); */ if (uid <= UID_ZERO || uid > UID_MAX) d->config = NULL; else { sid = slbc_get_sid_by_uid((enum slbc_uid)uid); if (sid != SID_NOT_FOUND) { d->sid = sid; d->config = &p_config[sid]; config = (struct slbc_config *)d->config; ret = _slbc_buffer_status_scmi(d); } } return ret; } static int slbc_request_buffer(struct slbc_data *d) { int ret = 0; int uid = d->uid; int sid; struct slbc_config *config; /* slbc_debug_log("%s: TP_BUFFER\n", __func__); */ if (uid <= UID_ZERO || uid > UID_MAX) d->config = NULL; else { sid = slbc_get_sid_by_uid((enum slbc_uid)uid); if (sid != SID_NOT_FOUND) { d->sid = sid; d->config = &p_config[sid]; config = (struct slbc_config *)d->config; } } ret = _slbc_request_buffer_scmi(d); if (!ret) { slbc_set_sram_data(d); #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) buffer_ref = slbc_sram_read(SLBC_BUFFER_REF); #else buffer_ref++; #endif /* CONFIG_MTK_SLBC_IPI */ } return ret; } static int slbc_request_acp(void *ptr) { int ret = 0; #if IS_ENABLED(CONFIG_MTK_L3C_PART) struct slbc_data *d = ptr; /* slbc_debug_log("%s: TP_ACP\n", __func__); */ if (!acp_ref) { #if IS_ENABLED(CONFIG_PM_SLEEP) __pm_stay_awake(slbc->ws); #endif /* CONFIG_PM_SLEEP */ cpu_latency_qos_update_request(&slbc_qos_request, slbc->slbc_qos_latency); } if (acp_ref++ == 0) { const unsigned long ratio_bits = d->flag & FG_ACP_BITS; int ratio = find_first_bit(&ratio_bits, 32) - ACP_ONLY_BIT; slbc_debug_log("%s: before %s:0x%x, %s:0x%x\n", __func__, "MTK_L3C_PART_MCU", mtk_l3c_get_part(MTK_L3C_PART_MCU), "MTK_L3C_PART_ACP", mtk_l3c_get_part(MTK_L3C_PART_ACP)); if (d->flag & FG_ACP_ONLY) mtk_l3c_set_mcu_part(4 - ratio); else mtk_l3c_set_mcu_part(4); mtk_l3c_set_acp_part(ratio); slbc_debug_log("%s: after %s:0x%x, %s:0x%x\n", __func__, "MTK_L3C_PART_MCU", mtk_l3c_get_part(MTK_L3C_PART_MCU), "MTK_L3C_PART_ACP", mtk_l3c_get_part(MTK_L3C_PART_ACP)); } #endif /* CONFIG_MTK_L3C_PART */ return ret; } int slbc_status(struct slbc_data *d) { int ret = 0; if ((d->type) == TP_BUFFER) ret = slbc_request_status(d); pr_info("#@# %s(%d) uid 0x%x ret %d\n", __func__, __LINE__, d->uid, ret); return ret; } int slbc_request(struct slbc_data *d) { int ret = 0; u64 begin, val; begin = ktime_get_ns(); if ((d->type) == TP_BUFFER) { ret = slbc_request_buffer(d); d->size = SLBC_WAY_SIZE * popcount(d->slot_used); if (!d->paddr) ret = -1; else d->emi_paddr = (void __iomem *)((((unsigned long)d->paddr) & SLBC_PADDR_MASK) | SLBC_WAY_B_BASE); } else if ((d->type) == TP_CACHE) ret = slbc_request_cache(d); else if ((d->type) == TP_ACP) ret = slbc_request_acp(d); pr_info("#@# %s(%d) uid 0x%x ret %d d->ret %d pa 0x%lx emipa 0x%lx size 0x%lx\n", __func__, __LINE__, d->uid, ret, d->ret, (unsigned long)d->paddr, (unsigned long)d->emi_paddr, d->size); if (!ret) { #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) slbc_ref = slbc_sram_read(SLBC_REF); #else slbc_ref++; #endif /* CONFIG_MTK_SLBC_IPI */ } val = (ktime_get_ns() - begin) / 1000000; req_val_count++; req_val_total += val; req_val_max = max(val, req_val_max); if (!req_val_min) req_val_min = val; else req_val_min = min(val, req_val_min); return ret; } static int slbc_release_cache(struct slbc_data *d) { int ret = 0; /* slbc_debug_log("%s: TP_CACHE\n", __func__); */ ret = _slbc_release_cache_scmi(d); return ret; } static int slbc_release_buffer(struct slbc_data *d) { int ret = 0; /* slbc_debug_log("%s: TP_BUFFER\n", __func__); */ ret = _slbc_release_buffer_scmi(d); if (!ret) { slbc_clr_sram_data(d); #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) buffer_ref = slbc_sram_read(SLBC_BUFFER_REF); #else buffer_ref--; #endif /* CONFIG_MTK_SLBC_IPI */ WARN_ON(buffer_ref < 0); } return ret; } static int slbc_release_acp(void *ptr) { int ret = 0; #if IS_ENABLED(CONFIG_MTK_L3C_PART) struct slbc_data *d = ptr; /* slbc_debug_log("%s: TP_ACP\n", __func__); */ if (--acp_ref == 0) { slbc_debug_log("%s: before %s:0x%x, %s:0x%x\n", __func__, "MTK_L3C_PART_MCU", mtk_l3c_get_part(MTK_L3C_PART_MCU), "MTK_L3C_PART_ACP", mtk_l3c_get_part(MTK_L3C_PART_ACP)); mtk_l3c_set_mcu_part(4); mtk_l3c_set_acp_part(1); slbc_debug_log("%s: after %s:0x%x, %s:0x%x\n", __func__, "MTK_L3C_PART_MCU", mtk_l3c_get_part(MTK_L3C_PART_MCU), "MTK_L3C_PART_ACP", mtk_l3c_get_part(MTK_L3C_PART_ACP)); } WARN_ON(acp_ref < 0); if (!acp_ref) { #if IS_ENABLED(CONFIG_PM_SLEEP) __pm_relax(slbc->ws); #endif /* CONFIG_PM_SLEEP */ cpu_latency_qos_update_request(&slbc_qos_request, PM_QOS_DEFAULT_VALUE); } #endif /* CONFIG_MTK_L3C_PART */ return ret; } int slbc_release(struct slbc_data *d) { int ret = 0; u64 begin, val; begin = ktime_get_ns(); if ((d->type) == TP_BUFFER) { ret = slbc_release_buffer(d); d->size = 0; } else if ((d->type) == TP_CACHE) ret = slbc_release_cache(d); else if ((d->type) == TP_ACP) ret = slbc_release_acp(d); pr_info("#@# %s(%d) uid 0x%x ret %d d->ret %d pa 0x%lx size 0x%lx\n", __func__, __LINE__, d->uid, ret, d->ret, (unsigned long)d->paddr, d->size); if (!ret) { #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) slbc_ref = slbc_sram_read(SLBC_REF); #else slbc_ref--; #endif /* CONFIG_MTK_SLBC_IPI */ } val = (ktime_get_ns() - begin) / 1000000; rel_val_count++; rel_val_total += val; rel_val_max = max(val, rel_val_max); if (!rel_val_min) rel_val_min = val; else rel_val_min = min(val, rel_val_min); return ret; } static void slbc_sync_mb(void *task) { dsb(sy); isb(); /* Pairs with smp_wmb() */ smp_rmb(); } static void slbc_mem_barrier(void) { /* * Ensure all data update before kicking the CPUs. * Pairs with smp_rmb() in slbc_sync_mb(). */ smp_wmb(); dsb(sy); isb(); smp_call_function(slbc_sync_mb, NULL, 1); } int slbc_power_on(struct slbc_data *d) { unsigned int uid; if (slbc_enable == 0) return -EDISABLED; if (!d) return -EINVAL; uid = d->uid; if (uid <= UID_ZERO || uid >= UID_MAX) return -EINVAL; #ifdef SLBC_TRACE trace_slbc_api((void *)__func__, slbc_uid_str[uid]); #endif /* SLBC_TRACE */ /* slbc_debug_log("%s: %s flag %x", __func__, */ /* slbc_uid_str[uid], d->flag); */ return 0; } int slbc_power_off(struct slbc_data *d) { unsigned int uid; if (slbc_enable == 0) return -EDISABLED; if (!d) return -EINVAL; uid = d->uid; if (uid <= UID_ZERO || uid >= UID_MAX) return -EINVAL; #ifdef SLBC_TRACE trace_slbc_api((void *)__func__, slbc_uid_str[uid]); #endif /* SLBC_TRACE */ /* slbc_debug_log("%s: %s flag %x", __func__, */ /* slbc_uid_str[uid], d->flag); */ return 0; } int slbc_secure_on(struct slbc_data *d) { unsigned int uid; if (slbc_enable == 0) return -EDISABLED; if (!d) return -EINVAL; uid = d->uid; if (uid <= UID_ZERO || uid >= UID_MAX) return -EINVAL; #ifdef SLBC_TRACE trace_slbc_api((void *)__func__, slbc_uid_str[uid]); #endif /* SLBC_TRACE */ slbc_debug_log("%s: %s flag %x", __func__, slbc_uid_str[uid], d->flag); return 0; } int slbc_secure_off(struct slbc_data *d) { unsigned int uid; if (slbc_enable == 0) return -EDISABLED; if (!d) return -EINVAL; uid = d->uid; if (uid <= UID_ZERO || uid >= UID_MAX) return -EINVAL; #ifdef SLBC_TRACE trace_slbc_api((void *)__func__, slbc_uid_str[uid]); #endif /* SLBC_TRACE */ slbc_debug_log("%s: %s flag %x", __func__, slbc_uid_str[uid], d->flag); return 0; } void slbc_update_mm_bw(unsigned int bw) { slbc_sram_write(SLBC_MM_EST_BW, bw); } void slbc_update_mic_num(unsigned int num) { int i; slbc_mic_num = num; slbc_mic_num_cmd(num); if (!uid_ref[UID_HIFI3]) { for (i = 0; i < ARRAY_SIZE(p_config); i++) { if (p_config[i].uid == UID_HIFI3) { if (num == 3) p_config[i].res_slot = 0xe00; else p_config[i].res_slot = 0x200; } } } } void slbc_update_inner(unsigned int inner) { slbc_inner = inner; slbc_inner_cmd(inner); } void slbc_update_outer(unsigned int outer) { slbc_outer = outer; slbc_outer_cmd(outer); } static void slbc_dump_data(struct seq_file *m, struct slbc_data *d) { unsigned int uid = d->uid; if (uid >= sizeof(slbc_uid_used)) { pr_info("slbc: uid size % >= slbc_uid_used size %d\n", uid, sizeof(slbc_uid_used)); return; } seq_printf(m, "ID %s\t", slbc_uid_str[uid]); if (test_bit(uid, &slbc_uid_used)) seq_puts(m, " activate\n"); else seq_puts(m, " deactivate\n"); seq_printf(m, "uid: %d\n", uid); seq_printf(m, "type: 0x%x\n", d->type); seq_printf(m, "size: %ld\n", d->size); seq_printf(m, "paddr: %lx\n", (unsigned long)d->paddr); seq_printf(m, "vaddr: %lx\n", (unsigned long)d->vaddr); seq_printf(m, "sid: %d\n", d->sid); seq_printf(m, "slot_used: 0x%x\n", d->slot_used); seq_printf(m, "config: %p\n", d->config); seq_printf(m, "ref: %d\n", d->ref); seq_printf(m, "pwr_ref: %d\n", d->pwr_ref); } static int dbg_slbc_proc_show(struct seq_file *m, void *v) { struct slbc_ops *ops; int i; int sid; #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) slbc_uid_used = slbc_sram_read(SLBC_UID_USED); slbc_sid_mask = slbc_sram_read(SLBC_SID_MASK); slbc_sid_req_q = slbc_sram_read(SLBC_SID_REQ_Q); slbc_sid_rel_q = slbc_sram_read(SLBC_SID_REL_Q); slbc_slot_used = slbc_sram_read(SLBC_SLOT_USED); slbc_force = slbc_sram_read(SLBC_FORCE); buffer_ref = slbc_sram_read(SLBC_BUFFER_REF); slbc_ref = slbc_sram_read(SLBC_REF); slbc_sta = slbc_sram_read(SLBC_STA); slbc_ack_c = slbc_sram_read(SLBC_ACK_C); slbc_ack_g = slbc_sram_read(SLBC_ACK_G); cpuqos_mode = slbc_sram_read(CPUQOS_MODE); slbc_sram_con = slbc_sram_read(SLBC_SRAM_CON); slbc_cache_used = slbc_sram_read(SLBC_CACHE_USED); slbc_pmu_0 = slbc_sram_read(SLBC_PMU_0); slbc_pmu_1 = slbc_sram_read(SLBC_PMU_1); slbc_pmu_2 = slbc_sram_read(SLBC_PMU_2); slbc_pmu_3 = slbc_sram_read(SLBC_PMU_3); slbc_pmu_4 = slbc_sram_read(SLBC_PMU_4); slbc_pmu_5 = slbc_sram_read(SLBC_PMU_5); slbc_pmu_6 = slbc_sram_read(SLBC_PMU_6); for (i = 0; i < UID_MAX; i++) { sid = slbc_get_sid_by_uid(i); if (sid != SID_NOT_FOUND) uid_ref[i] = slbc_read_debug_sram(sid); } #endif /* CONFIG_MTK_SLBC_IPI */ seq_printf(m, "slbc_enable %x\n", slbc_enable); seq_printf(m, "slb_disable %x\n", slb_disable); seq_printf(m, "slc_disable %x\n", slc_disable); seq_printf(m, "slbc_sram_enable %x\n", slbc_sram_enable); seq_printf(m, "slbc_scmi_enable %x\n", slbc_get_scmi_enable()); seq_printf(m, "slbc_uid_used 0x%lx\n", slbc_uid_used); seq_printf(m, "slbc_sid_mask 0x%lx\n", slbc_sid_mask); seq_printf(m, "slbc_sid_req_q 0x%lx\n", slbc_sid_req_q); seq_printf(m, "slbc_sid_rel_q 0x%lx\n", slbc_sid_rel_q); seq_printf(m, "slbc_slot_used 0x%lx\n", slbc_slot_used); seq_printf(m, "slbc_force 0x%x\n", slbc_force); seq_printf(m, "buffer_ref %x\n", buffer_ref); seq_printf(m, "acp_ref %x\n", acp_ref); seq_printf(m, "slbc_ref %x\n", slbc_ref); seq_printf(m, "debug_level %x\n", debug_level); seq_printf(m, "slbc_sta %x\n", slbc_sta); seq_printf(m, "slbc_ack_c %x\n", slbc_ack_c); seq_printf(m, "slbc_ack_g %x\n", slbc_ack_g); seq_printf(m, "cpuqos_mode %x\n", cpuqos_mode); seq_printf(m, "slbc_sram_con %x\n", slbc_sram_con); seq_printf(m, "slbc_cache_used %x\n", slbc_cache_used); seq_printf(m, "slbc_pmu_0 %x\n", slbc_pmu_0); seq_printf(m, "slbc_pmu_1 %x\n", slbc_pmu_1); seq_printf(m, "slbc_pmu_2 %x\n", slbc_pmu_2); seq_printf(m, "slbc_pmu_3 %x\n", slbc_pmu_3); seq_printf(m, "slbc_pmu_4 %x\n", slbc_pmu_4); seq_printf(m, "slbc_pmu_5 %x\n", slbc_pmu_5); seq_printf(m, "slbc_pmu_6 %x\n", slbc_pmu_6); seq_printf(m, "mic_num %x\n", slbc_mic_num); seq_printf(m, "inner %x\n", slbc_inner); seq_printf(m, "outer %x\n", slbc_outer); for (i = 0; i < UID_MAX; i++) { sid = slbc_get_sid_by_uid(i); if (sid != SID_NOT_FOUND) seq_printf(m, "uid_ref %s %x\n", slbc_uid_str[i], uid_ref[i]); } mutex_lock(&slbc_ops_lock); list_for_each_entry(ops, &slbc_ops_list, node) { struct slbc_data *d = ops->data; slbc_dump_data(m, d); } mutex_unlock(&slbc_ops_lock); if (req_val_count) { seq_printf(m, "stat req count:%lld min:%lld avg:%lld max:%lld\n", req_val_count, req_val_min, req_val_total / req_val_count, req_val_max); seq_printf(m, "stat rel count:%lld min:%lld avg:%lld max:%lld\n", rel_val_count, rel_val_min, rel_val_total / rel_val_count, rel_val_max); } seq_puts(m, "\n"); return 0; } static ssize_t dbg_slbc_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { int ret = 0; char *buf = (char *) __get_free_page(GFP_USER); char cmd[64]; unsigned long val_1; unsigned long val_2; 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'; ret = sscanf(buf, "%63s %lx %lx", cmd, &val_1, &val_2); if (ret < 1) { ret = -EPERM; goto out; } if (!strcmp(cmd, "slbc_enable")) { slbc_enable = !!val_1; if (slbc_enable == 0) { struct slbc_ops *ops; mutex_lock(&slbc_ops_lock); list_for_each_entry(ops, &slbc_ops_list, node) { struct slbc_data *d = ops->data; unsigned int uid = d->uid; if (uid >= sizeof(slbc_uid_used)) { pr_info("slbc: uid size % >= slbc_uid_used size %d\n", uid, sizeof(slbc_uid_used)); return -EINVAL; } if (test_bit(uid, &slbc_uid_used)) ops->deactivate(d); } mutex_unlock(&slbc_ops_lock); } } else if (!strcmp(cmd, "slb_disable")) { pr_info("slb disable %ld\n", val_1); slb_disable = val_1; slbc_sspm_slb_disable((int)!!val_1); } else if (!strcmp(cmd, "slc_disable")) { pr_info("slc disable %ld\n", val_1); slc_disable = val_1; slbc_sspm_slc_disable((int)!!val_1); } else if (!strcmp(cmd, "slbc_scmi_enable")) { pr_info("slbc scmi enable %ld\n", val_1); slbc_sspm_enable((int)!!val_1); } else if (!strcmp(cmd, "slbc_uid_used")) { slbc_uid_used = val_1; slbc_sram_write(SLBC_UID_USED, slbc_uid_used); } else if (!strcmp(cmd, "slbc_sid_mask")) { slbc_sid_mask = val_1; slbc_sram_write(SLBC_SID_MASK, slbc_sid_mask); } else if (!strcmp(cmd, "slbc_sid_req_q")) { slbc_sid_req_q = val_1; slbc_sram_write(SLBC_SID_REQ_Q, slbc_sid_req_q); } else if (!strcmp(cmd, "slbc_sid_rel_q")) { slbc_sid_rel_q = val_1; slbc_sram_write(SLBC_SID_REL_Q, slbc_sid_rel_q); } else if (!strcmp(cmd, "slbc_slot_used")) { slbc_slot_used = val_1; slbc_sram_write(SLBC_SLOT_USED, slbc_slot_used); } else if (!strcmp(cmd, "test_acp_request")) { test_d.uid = UID_TEST_ACP; test_d.type = TP_ACP; test_d.flag = val_1; ret = slbc_request(&test_d); } else if (!strcmp(cmd, "test_acp_release")) { test_d.uid = UID_TEST_ACP; test_d.type = TP_ACP; test_d.flag = val_1; ret = slbc_release(&test_d); } else if (!strcmp(cmd, "test_slb_request")) { test_d.uid = val_1; test_d.type = TP_BUFFER; ret = slbc_request(&test_d); } else if (!strcmp(cmd, "test_slb_release")) { test_d.uid = val_1; test_d.type = TP_BUFFER; ret = slbc_release(&test_d); } else if (!strcmp(cmd, "slbc_force")) { slbc_force = val_1; slbc_force_cmd(slbc_force); } else if (!strcmp(cmd, "mic_num")) { slbc_update_mic_num(val_1); } else if (!strcmp(cmd, "inner")) { slbc_update_inner(val_1); } else if (!strcmp(cmd, "outer")) { slbc_update_outer(val_1); } else if (!strcmp(cmd, "debug_level")) { debug_level = val_1; } else if (!strcmp(cmd, "sram")) { pr_info("#@# %s(%d) slbc->regs 0x%lx slbc->regsize 0x%x\n", __func__, __LINE__, (unsigned long)slbc->regs, slbc->regsize); print_hex_dump(KERN_INFO, "SLBC: ", DUMP_PREFIX_OFFSET, 16, 4, slbc->sram_vaddr, slbc->regsize, 1); } else { pr_info("#@# %s(%d) wrong cmd %s val %ld\n", __func__, __LINE__, cmd, val_1); } out: free_page((unsigned long)buf); if (ret < 0) return ret; return count; } PROC_FOPS_RW(dbg_slbc); static int slbc_create_debug_fs(void) { int i; struct proc_dir_entry *dir = NULL; struct pentry { const char *name; const struct proc_ops *fops; void *data; }; const struct pentry entries[] = { PROC_ENTRY(dbg_slbc), }; /* create /proc/slbc */ dir = proc_mkdir("slbc", NULL); if (!dir) { pr_info("fail to create /proc/slbc @ %s()\n", __func__); return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(entries); i++) { if (!proc_create_data(entries[i].name, 0660, dir, entries[i].fops, entries[i].data)) pr_info("%s(), create /proc/slbc/%s failed\n", __func__, entries[i].name); } return 0; } static struct slbc_common_ops common_ops = { .slbc_status = slbc_status, .slbc_request = slbc_request, .slbc_release = slbc_release, .slbc_power_on = slbc_power_on, .slbc_power_off = slbc_power_off, .slbc_secure_on = slbc_secure_on, .slbc_secure_off = slbc_secure_off, .slbc_sram_read = slbc_sram_read, .slbc_sram_write = slbc_sram_write, .slbc_update_mm_bw = slbc_update_mm_bw, .slbc_update_mic_num = slbc_update_mic_num, }; static struct slbc_ipi_ops ipi_ops = { .slbc_request_acp = slbc_request_acp, .slbc_release_acp = slbc_release_acp, .slbc_mem_barrier = slbc_mem_barrier, }; static int slbc_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret = 0; struct cpuidle_driver *drv = cpuidle_get_driver(); /* struct resource *res; */ uint32_t reg[4] = {0, 0, 0, 0}; #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) ret = slbc_scmi_init(); if (ret < 0) return ret; #endif /* CONFIG_MTK_SLBC_IPI */ slbc = devm_kzalloc(dev, sizeof(struct mtk_slbc), GFP_KERNEL); if (!slbc) return -ENOMEM; slbc->dev = dev; #ifdef ENABLE_SLBC if (node) { ret = of_property_read_u32(node, "slbc-enable", &slbc_enable); if (ret) pr_info("failed to get slbc_enable from dts\n"); else pr_info("#@# %s(%d) slbc_enable %d\n", __func__, __LINE__, slbc_enable); } else pr_info("find slbc node failed\n"); #else slbc_enable = 0; pr_info("#@# %s(%d) slbc_enable %d\n", __func__, __LINE__, slbc_enable); #endif /* ENABLE_SLBC */ ret = of_property_read_u32_array(node, "reg", reg, 4); if (ret < 0) { slbc_sram_enable = 0; pr_info("slbc of_property_read_u32_array ERR : %d\n", ret); } else { slbc->regs = (void *)(long)reg[1]; slbc->regsize = reg[3]; slbc->sram_vaddr = (void __iomem *) devm_memremap(dev, (resource_size_t)reg[1], slbc->regsize, MEMREMAP_WT); if (IS_ERR(slbc->sram_vaddr)) { slbc_sram_enable = 0; dev_notice(dev, "slbc could not ioremap resource for memory\n"); } else { slbc_sram_enable = 1; pr_info("#@# %s(%d) slbc->regs 0x%lx slbc->regsize 0x%x\n", __func__, __LINE__, (unsigned long)slbc->regs, slbc->regsize); slbc_sram_init(slbc); } } #if IS_ENABLED(CONFIG_PM_SLEEP) slbc->ws = wakeup_source_register(NULL, "slbc"); if (!slbc->ws) pr_debug("slbc wakelock register fail!\n"); #endif /* CONFIG_PM_SLEEP */ ret = slbc_create_debug_fs(); if (ret) { pr_info("SLBC FAILED TO CREATE DEBUG FILESYSTEM (%d)\n", ret); return ret; } if (drv) slbc->slbc_qos_latency = drv->states[2].exit_latency - 10; else slbc->slbc_qos_latency = 300; pr_info("slbc_qos_latency %dus\n", slbc->slbc_qos_latency); cpu_latency_qos_add_request(&slbc_qos_request, PM_QOS_DEFAULT_VALUE); slbc_register_ipi_ops(&ipi_ops); slbc_register_common_ops(&common_ops); if (slbc_enable) slbc_sspm_enable(slbc_enable); return 0; } static int slbc_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; slbc_unregister_ipi_ops(&ipi_ops); slbc_unregister_common_ops(&common_ops); devm_kfree(dev, slbc); return 0; } static int slbc_suspend(struct device *dev) { #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) slbc_suspend_resume_notify(1); #endif /* CONFIG_MTK_SLBC_IPI */ return 0; } static int slbc_resume(struct device *dev) { #if IS_ENABLED(CONFIG_MTK_SLBC_IPI) slbc_suspend_resume_notify(0); #endif /* CONFIG_MTK_SLBC_IPI */ return 0; } static int slbc_suspend_cb(struct device *dev) { int i; int sid; for (i = 0; i < UID_MAX; i++) { sid = slbc_get_sid_by_uid(i); if (sid != SID_NOT_FOUND && uid_ref[i]) { if (sid == UID_HIFI3) { pr_info("uid_ref %s %x\n", slbc_uid_str[i], uid_ref[i]); } else if (sid == UID_AOV | sid == UID_AOV_DC | sid == UID_AOV_APU) { pr_info("uid_ref %s %x, screen off user\n", slbc_uid_str[i], uid_ref[i]); } else { pr_info("uid_ref %s %x, screen on user\n", slbc_uid_str[i], uid_ref[i]); WARN_ON(uid_ref[i]); } } } return 0; } static int slbc_resume_cb(struct device *dev) { int i; int sid; for (i = 0; i < UID_MAX; i++) { sid = slbc_get_sid_by_uid(i); if (sid != SID_NOT_FOUND && uid_ref[i]) { if (sid == UID_HIFI3) { pr_info("uid_ref %s %x\n", slbc_uid_str[i], uid_ref[i]); } else if (sid == UID_AOV | sid == UID_AOV_DC | sid == UID_AOV_APU) { pr_info("uid_ref %s %x, screen off user\n", slbc_uid_str[i], uid_ref[i]); WARN_ON(uid_ref[i]); } else { pr_info("uid_ref %s %x, screen on user\n", slbc_uid_str[i], uid_ref[i]); } } } return 0; } static const struct dev_pm_ops slbc_pm_ops = { .suspend = slbc_suspend, .resume = slbc_resume, .suspend_late = slbc_suspend_cb, .resume_early = slbc_resume_cb, }; static const struct of_device_id slbc_of_match[] = { { .compatible = "mediatek,mtk-slbc", }, {} }; static struct platform_driver slbc_pdrv = { .probe = slbc_probe, .remove = slbc_remove, .driver = { .name = "slbc", .owner = THIS_MODULE, .of_match_table = of_match_ptr(slbc_of_match), .pm = &slbc_pm_ops, }, }; static int __init slbc_module_init(void) { return platform_driver_register(&slbc_pdrv); } module_init(slbc_module_init); static void __exit slbc_module_exit(void) { platform_driver_unregister(&slbc_pdrv); } module_exit(slbc_module_exit); MODULE_SOFTDEP("pre: tinysys-scmi.ko"); MODULE_DESCRIPTION("SLBC Driver mt6985 v0.1"); MODULE_LICENSE("GPL");