/* * Exynos regulator support. * * Copyright (c) 2016 Samsung Electronics Co., Ltd. * http://www.samsung.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #define EXYNOS_FLEXPMU_DBG_PREFIX "EXYNOS-FLEXPMU-DBG: " #define DATA_LINE (16) #define DATA_IDX (8) #define FLEXPMU_DBG_FUNC_READ(__name) \ exynos_flexpmu_dbg_ ## __name ## _read #define BUF_MAX_LINE 10 #define BUF_LINE_SIZE 255 #define BUF_SIZE (BUF_MAX_LINE * BUF_LINE_SIZE) #define DEC_PRINT 1 #define HEX_PRINT 2 /* enum for debugfs files */ enum flexpmu_debugfs_id { FID_CPU_STATUS, FID_SEQ_STATUS, FID_CUR_SEQ, FID_SW_FLAG, FID_SEQ_COUNT, FID_MIF_ALWAYS_ON, FID_LPM_COUNT, FID_APM_REQ_INFO, #if defined(CONFIG_SOC_S5E9925) FID_MID_DVS_EN, #endif FID_MAX }; char *flexpmu_debugfs_name[FID_MAX] = { "cpu_status", "seq_status", "cur_sequence", "sw_flag", "seq_count", "mif_always_on", "lpm_count", "apm_req_info", #if defined(CONFIG_SOC_S5E9925) "mid_dvs_en", #endif }; /* enum for data lines */ enum data_id { DID_CPU_STATUS, /* 0 */ DID_SEQ_STATUS, DID_CUR_SEQ0, DID_CUR_SEQ1, DID_PWR_MODE0, /* 4 */ DID_PWR_MODE1, DID_PWR_MODE2, DID_PWR_MODE3, DID_PWR_MODE4, DID_PWR_MODE5, DID_SW_FLAG, DID_IRQ_STATUS, /* 11 */ DID_IRQ_DATA, DID_IPC_AP_STATUS, DID_IPC_AP_RXDATA, DID_IPC_AP_TXDATA, DID_SOC_COUNT, DID_MIF_COUNT, DID_IPC_VTS0, DID_SLEEP_WAKEUP, DID_LOCAL_PWR, DID_MIF_ALWAYS_ON, /* 21 */ DID_AP_COUNT_SLEEP, DID_MIF_COUNT_SLEEP, DID_AP_COUNT_SICD, DID_MIF_COUNT_SICD, DID_DSUPD_COUNT, DID_CUR_PMD, DID_CPU_INFORM01, DID_CPU_INFORM23, DID_CPU_INFORM45, DID_CPU_INFORM67, DID_INT_REG01, DID_INT_REG02, DID_INT_REG03, DID_INT_REG04, DID_INT_REG05, DID_INT_REG06, DID_INT_REG07, DID_INT_REG08, DID_INT_REG09, DID_INT_REG10, DID_INT_REG11, #if defined(CONFIG_SOC_S5E9925) DID_MIFAP0, DID_MIFAP1, DID_MIFAUD0, DID_MIFAUD1, DID_MIFVTS0, DID_MIFVTS1, DID_MIFCP0, DID_MIFCP1, DID_MID_DVS_EN, #elif defined(CONFIG_SOC_S5E8825) DID_CMGP_REQ, DID_LOCAL01, DID_LOCAL02, DID_LOCAL03, DID_RCO_DIV, DID_MIFAUD0, DID_MIFAUD1, DID_MIFAUD2, DID_MIFVTS0, DID_MIFVTS1, DID_MIFVTS2, DID_MIFCHUB0, DID_MIFCHUB1, DID_MIFCHUB2, DID_MIFCP0, DID_MIFCP1, DID_MIFCP2, DID_MIFGNSS0, DID_MIFGNSS1, DID_MIFGNSS2, DID_MIFWLBT0, DID_MIFWLBT1, DID_MIFWLBT2, DID_MIFAP0, DID_MIFAP1, DID_MIFAP2, DID_TC_CP0, DID_TC_CP1, DID_TC_CP2, DID_TC_GNSS0, DID_TC_GNSS1, DID_TC_GNSS2, DID_TC_WLBT0, DID_TC_WLBT1, DID_TC_WLBT2, DID_PWRCP0, DID_PWRCP1, DID_PWRCP2, DID_PWRGNSS0, DID_PWRGNSS1, DID_PWRGNSS2, DID_PWRWLBT0, DID_PWRWLBT1, DID_PWRWLBT2, DID_pENTIME, DID_SICD_PHY, #endif DID_MAX }; struct flexpmu_dbg_print_arg { char prefix[BUF_LINE_SIZE]; int print_type; }; struct dbgfs_info { int fid; struct dentry *den; struct file_operations fops; }; struct dbgfs_info *flexpmu_dbg_info; void __iomem *flexpmu_dbg_base; static struct dentry *flexpmu_dbg_root; struct flexpmu_apm_req_info { unsigned int active_req_tick; unsigned int last_rel_tick; unsigned int total_count; unsigned int total_time_tick; unsigned long long int active_since_us; unsigned long long int last_rel_us; unsigned long long int total_time_us; bool active_flag; }; void __iomem *rtc_base; #if defined(CONFIG_SOC_S5E9925) #define MIF_MASTER_MAX 4 char *flexpmu_master_name[MIF_MASTER_MAX] = { "MIF_AP", "MIF_AUD", "MIF_VTS", "MIF_CP", }; #elif defined(CONFIG_SOC_S5E8825) #define MIF_MASTER_MAX 7 char *flexpmu_master_name[MIF_MASTER_MAX] = { "MIF_AUD", "MIF_VTS", "MIF_CHUB", "MIF_CP", "MIF_GNSS", "MIF_WLBT", "MIF_AP", }; #endif struct flexpmu_apm_req_info apm_req[MIF_MASTER_MAX]; u32 acpm_get_mifdn_count(void) { return __raw_readl(flexpmu_dbg_base + (DATA_LINE * DID_MIF_COUNT_SLEEP) + DATA_IDX + 4); } EXPORT_SYMBOL_GPL(acpm_get_mifdn_count); u32 acpm_get_apsocdn_count(void) { return __raw_readl(flexpmu_dbg_base + (DATA_LINE * DID_AP_COUNT_SLEEP) + DATA_IDX + 4); } EXPORT_SYMBOL_GPL(acpm_get_apsocdn_count); u32 acpm_get_early_wakeup_count(void) { return __raw_readl(flexpmu_dbg_base + (DATA_LINE * DID_AP_COUNT_SLEEP) + DATA_IDX); } EXPORT_SYMBOL_GPL(acpm_get_early_wakeup_count); /*notify to flexpmu, it is SICD w MIF(is_dsu_cpd=0) or SICD wo MIF(is_dsu_cpd=1).*/ u32 acpm_noti_dsu_cpd(bool is_dsu_cpd) { __raw_writel(is_dsu_cpd, flexpmu_dbg_base + (DATA_LINE * DID_MIF_ALWAYS_ON) + DATA_IDX); return 0; } EXPORT_SYMBOL_GPL(acpm_noti_dsu_cpd); u32 acpm_get_dsu_cpd(void) { return __raw_readl(flexpmu_dbg_base + (DATA_LINE * DID_MIF_ALWAYS_ON) + DATA_IDX); } EXPORT_SYMBOL_GPL(acpm_get_dsu_cpd); #define MIF_REQ_MASK (0x00FF0000) #define MIF_REQ_SHIFT (16) u32 acpm_get_mif_request(void) { u32 reg = __raw_readl(flexpmu_dbg_base + (DATA_LINE * DID_SW_FLAG) + DATA_IDX + 4); return ((reg & MIF_REQ_MASK) >> MIF_REQ_SHIFT); } EXPORT_SYMBOL_GPL(acpm_get_mif_request); static ssize_t print_dataline_2(int did, struct flexpmu_dbg_print_arg *print_arg, ssize_t len, char *buf, int *data_count) { int data[2]; ssize_t line_len; int i; for (i = 0; i < 2; i++) { if (print_arg[*data_count].print_type == DEC_PRINT) { data[i] = __raw_readl(flexpmu_dbg_base + (DATA_LINE * did) + DATA_IDX + i * 4); line_len = snprintf(buf + len, BUF_SIZE - len, "%s : %d\n", print_arg[*data_count].prefix, data[i]); if (line_len > 0) len += line_len; } else if (print_arg[*data_count].print_type == HEX_PRINT) { data[i] = __raw_readl(flexpmu_dbg_base + (DATA_LINE * did) + DATA_IDX + i * 4); line_len = snprintf(buf + len, BUF_SIZE - len, "%s : 0x%x\n", print_arg[*data_count].prefix, data[i]); if (line_len > 0) len += line_len; } *data_count += 1; } return len; } static ssize_t print_dataline_8(int did, struct flexpmu_dbg_print_arg *print_arg, ssize_t len, char *buf, int *data_count) { int data[8]; ssize_t line_len; int i; for (i = 0; i < 8; i++) { if (print_arg[*data_count].print_type == DEC_PRINT) { data[i] = __raw_readb(flexpmu_dbg_base + (DATA_LINE * did) + DATA_IDX + i); line_len = snprintf(buf + len, BUF_SIZE - len, "%s : %d\n", print_arg[*data_count].prefix, data[i]); if (line_len > 0) len += line_len; } else if (print_arg[*data_count].print_type == HEX_PRINT) { data[i] = __raw_readb(flexpmu_dbg_base + (DATA_LINE * did) + DATA_IDX + i); line_len = snprintf(buf + len, BUF_SIZE - len, "%s : 0x%x\n", print_arg[*data_count].prefix, data[i]); if (line_len > 0) len += line_len; } *data_count += 1; } return len; } static struct flexpmu_dbg_print_arg *exynos_flexpmu_dbg_alloc_print_arg(int nargs, ...) { struct flexpmu_dbg_print_arg *print_arg; va_list args; int i; if (nargs > BUF_MAX_LINE || nargs <= 0) return NULL; print_arg = kzalloc(sizeof(struct flexpmu_dbg_print_arg) * BUF_MAX_LINE, GFP_KERNEL); va_start(args, nargs); for (i = 0; i < nargs; i++) { char *prefix = va_arg(args, char *); int print_type = va_arg(args, int); if (prefix != NULL) { strncpy(print_arg[i].prefix, prefix, strlen(prefix)); print_arg[i].print_type = print_type; } } va_end(args); return print_arg; } static ssize_t exynos_flexpmu_dbg_cpu_status_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(8, "CL0_CPU0", DEC_PRINT, "CL0_CPU1", DEC_PRINT, "CL0_CPU2", DEC_PRINT, "CL0_CPU3", DEC_PRINT, "CL1_CPU0", DEC_PRINT, "CL1_CPU1", DEC_PRINT, "CL1_CPU2", DEC_PRINT, "CL1_CPU3", DEC_PRINT ); ret = print_dataline_8(DID_CPU_STATUS, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } static ssize_t exynos_flexpmu_dbg_seq_status_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(7, "SOC seq", DEC_PRINT, "MIF seq", DEC_PRINT, NULL, 0, NULL, 0, "nonCPU CL0", DEC_PRINT, "nonCPU CL1", DEC_PRINT, NULL, 0 ); ret = print_dataline_8(DID_SEQ_STATUS, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } static ssize_t exynos_flexpmu_dbg_cur_sequence_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(4, "UP Sequence", DEC_PRINT, "DOWN Sequence", DEC_PRINT, "Access Type", DEC_PRINT, "Seq Index", DEC_PRINT ); ret = print_dataline_2(DID_CUR_SEQ0, print_arg, ret, buf, &data_count); ret = print_dataline_2(DID_CUR_SEQ1, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } static ssize_t exynos_flexpmu_dbg_sw_flag_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(8, "Hotplug out flag", HEX_PRINT, "Reset-Release flag", HEX_PRINT, NULL, 0, NULL, 0, "CHUB ref_count", DEC_PRINT, NULL, 0, "MIF req_Master", HEX_PRINT, "MIF req_count", DEC_PRINT ); ret = print_dataline_8(DID_SW_FLAG, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } static ssize_t exynos_flexpmu_dbg_seq_count_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(4, "Early Wakeup", DEC_PRINT, "SOC sequence", DEC_PRINT, NULL, 0, "MIF sequence", DEC_PRINT ); ret = print_dataline_2(DID_SOC_COUNT, print_arg, ret, buf, &data_count); ret = print_dataline_2(DID_MIF_COUNT, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } static ssize_t exynos_flexpmu_dbg_mif_always_on_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(2, NULL, 0, "MIF always on", DEC_PRINT ); ret = print_dataline_2(DID_MIF_ALWAYS_ON, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } #if defined(CONFIG_SOC_S5E9925) static ssize_t exynos_flexpmu_dbg_mid_dvs_en_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(2, "MID DVS states", HEX_PRINT, "MID DVS enable", DEC_PRINT ); ret = print_dataline_2(DID_MID_DVS_EN, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } #endif static ssize_t exynos_flexpmu_dbg_lpm_count_read(int fid, char *buf) { ssize_t ret = 0; int data_count = 0; struct flexpmu_dbg_print_arg *print_arg = exynos_flexpmu_dbg_alloc_print_arg(10, "[SLEEP] Early wakeup", DEC_PRINT, "[SLEEP] SOC seq down", DEC_PRINT, NULL, 0, "[SLEEP] MIF seq down", DEC_PRINT, "[SICD] Early wakeup", DEC_PRINT, "[SICD] SOC seq down", DEC_PRINT, NULL, 0, "[SICD] MIF seq down", DEC_PRINT, NULL, 0, "[DSUPD] SOC seq down", DEC_PRINT ); ret = print_dataline_2(DID_AP_COUNT_SLEEP, print_arg, ret, buf, &data_count); ret = print_dataline_2(DID_MIF_COUNT_SLEEP, print_arg, ret, buf, &data_count); ret = print_dataline_2(DID_AP_COUNT_SICD, print_arg, ret, buf, &data_count); ret = print_dataline_2(DID_MIF_COUNT_SICD, print_arg, ret, buf, &data_count); ret = print_dataline_2(DID_DSUPD_COUNT, print_arg, ret, buf, &data_count); kfree(print_arg); return ret; } #define RTC_TICK_TO_US 976 /* 1024 Hz : 1tick = 976.5625us */ #define CURTICCNT_0 0x90 static ssize_t exynos_flexpmu_dbg_apm_req_info_read(int fid, char *buf) { size_t ret = 0; unsigned long long int curr_tick = 0; int i = 0; if (!rtc_base) { ret = snprintf(buf + ret, BUF_SIZE - ret, "%s\n", "This node is not supported.\n"); return ret; } curr_tick = __raw_readl(rtc_base + CURTICCNT_0); ret += snprintf(buf + ret, BUF_SIZE - ret, "%s: %lld\n", "curr_time", curr_tick * RTC_TICK_TO_US); ret += snprintf(buf + ret, BUF_SIZE - ret, "%8s %32s %32s %32s %32s\n", "Master", "active_since(us ago)", "last_rel_time(us ago)", "total_req_time(us)", "req_count"); for (i = 0; i < MIF_MASTER_MAX; i++) { #if defined(CONFIG_SOC_S5E9925) apm_req[i].active_req_tick = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAP0 + i * 2)) + DATA_IDX); apm_req[i].last_rel_tick = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAP0 + i * 2)) + DATA_IDX + 4); apm_req[i].total_count = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAP1 + i * 2)) + DATA_IDX); apm_req[i].total_time_tick = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAP1 + i * 2)) + DATA_IDX + 4); #elif defined(CONFIG_SOC_S5E8825) apm_req[i].active_req_tick = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAUD0 + i * 3)) + DATA_IDX); apm_req[i].last_rel_tick = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAUD0 + i * 3)) + DATA_IDX + 4); apm_req[i].total_count = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAUD1 + i * 3)) + DATA_IDX); apm_req[i].total_time_tick = __raw_readl(flexpmu_dbg_base + (DATA_LINE * (DID_MIFAUD1 + i * 3)) + DATA_IDX + 4); #endif if (apm_req[i].last_rel_tick > 0) { apm_req[i].last_rel_us = (curr_tick - apm_req[i].last_rel_tick) * RTC_TICK_TO_US; } apm_req[i].total_time_us = apm_req[i].total_time_tick * RTC_TICK_TO_US; if (apm_req[i].active_req_tick == 0) { apm_req[i].active_flag = false; apm_req[i].active_since_us = 0; } else { apm_req[i].active_flag = true; apm_req[i].active_since_us = (curr_tick - apm_req[i].active_req_tick) * RTC_TICK_TO_US; apm_req[i].total_time_us += apm_req[i].active_since_us; } if (BUF_SIZE - (BUF_MAX_LINE * 3) > ret) { ret += snprintf(buf + ret, BUF_SIZE - ret, "%8s : %32lld %32lld %32lld %32d\n", flexpmu_master_name[i], apm_req[i].active_since_us, apm_req[i].last_rel_us, apm_req[i].total_time_us, apm_req[i].total_count); } } return ret; } static ssize_t (*flexpmu_debugfs_read_fptr[FID_MAX])(int, char *) = { FLEXPMU_DBG_FUNC_READ(cpu_status), FLEXPMU_DBG_FUNC_READ(seq_status), FLEXPMU_DBG_FUNC_READ(cur_sequence), FLEXPMU_DBG_FUNC_READ(sw_flag), FLEXPMU_DBG_FUNC_READ(seq_count), FLEXPMU_DBG_FUNC_READ(mif_always_on), FLEXPMU_DBG_FUNC_READ(lpm_count), FLEXPMU_DBG_FUNC_READ(apm_req_info), #if defined(CONFIG_SOC_S5E9925) FLEXPMU_DBG_FUNC_READ(mid_dvs_en), #endif }; static ssize_t exynos_flexpmu_dbg_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct dbgfs_info *d2f = file->private_data; ssize_t ret; char *buf = NULL; buf = kzalloc(sizeof(char) * BUF_SIZE, GFP_KERNEL); ret = flexpmu_debugfs_read_fptr[d2f->fid](d2f->fid, buf); if (ret > sizeof(char) * BUF_SIZE) return ret; ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); kfree(buf); return ret; } static ssize_t exynos_flexpmu_dbg_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct dbgfs_info *f2d = file->private_data; ssize_t ret; char buf[32]; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); if (ret < 0) return ret; switch (f2d->fid) { case FID_MIF_ALWAYS_ON: if (buf[0] == '0') { __raw_writel(0, flexpmu_dbg_base + (DATA_LINE * DID_MIF_ALWAYS_ON) + 0xC); } if (buf[0] == '1') { __raw_writel(1, flexpmu_dbg_base + (DATA_LINE * DID_MIF_ALWAYS_ON) + 0xC); } break; #if defined(CONFIG_SOC_S5E9925) case FID_MID_DVS_EN: if (buf[0] == '0') { __raw_writel(0, flexpmu_dbg_base + (DATA_LINE * DID_MID_DVS_EN) + 0xC); } if (buf[0] == '1') { __raw_writel(1, flexpmu_dbg_base + (DATA_LINE * DID_MID_DVS_EN) + 0xC); } break; #endif default: break; } return ret; } void exynos_flexpmu_dbg_set_sleep_req(void) { __raw_writel(1, flexpmu_dbg_base + (DATA_LINE * DID_SLEEP_WAKEUP) + 0x8); } EXPORT_SYMBOL_GPL(exynos_flexpmu_dbg_set_sleep_req); void exynos_flexpmu_dbg_clr_wakeup_req(void) { __raw_writel(0, flexpmu_dbg_base + (DATA_LINE * DID_SLEEP_WAKEUP) + 0xC); } EXPORT_SYMBOL_GPL(exynos_flexpmu_dbg_clr_wakeup_req); static int exynos_flexpmu_dbg_probe(struct platform_device *pdev) { int ret = 0; const __be32 *prop; unsigned int len = 0; unsigned int data_base = 0; unsigned int data_size = 0; unsigned int disable_mifdown = 0; int i; flexpmu_dbg_info = kzalloc(sizeof(struct dbgfs_info) * FID_MAX, GFP_KERNEL); if (!flexpmu_dbg_info) { pr_err("%s %s: can not allocate mem for flexpmu_dbg_info\n", EXYNOS_FLEXPMU_DBG_PREFIX, __func__); ret = -ENOMEM; goto err_flexpmu_info; } prop = of_get_property(pdev->dev.of_node, "data-base", &len); if (!prop) { pr_err("%s %s: can not read data-base in DT\n", EXYNOS_FLEXPMU_DBG_PREFIX, __func__); ret = -EINVAL; goto err_dbgfs_probe; } data_base = be32_to_cpup(prop); prop = of_get_property(pdev->dev.of_node, "data-size", &len); if (!prop) { pr_err("%s %s: can not read data-base in DT\n", EXYNOS_FLEXPMU_DBG_PREFIX, __func__); ret = -EINVAL; goto err_dbgfs_probe; } data_size = be32_to_cpup(prop); if (data_base && data_size) flexpmu_dbg_base = ioremap(data_base, data_size); flexpmu_dbg_root = debugfs_create_dir("flexpmu-dbg", NULL); if (!flexpmu_dbg_root) { pr_err("%s %s: could not create debugfs root dir\n", EXYNOS_FLEXPMU_DBG_PREFIX, __func__); ret = -ENOMEM; goto err_dbgfs_probe; } for (i = 0; i < FID_MAX; i++) { flexpmu_dbg_info[i].fid = i; flexpmu_dbg_info[i].fops.open = simple_open; flexpmu_dbg_info[i].fops.read = exynos_flexpmu_dbg_read; flexpmu_dbg_info[i].fops.write = exynos_flexpmu_dbg_write; flexpmu_dbg_info[i].fops.llseek = default_llseek; flexpmu_dbg_info[i].den = debugfs_create_file(flexpmu_debugfs_name[i], 0644, flexpmu_dbg_root, &flexpmu_dbg_info[i], &flexpmu_dbg_info[i].fops); } rtc_base = of_iomap(pdev->dev.of_node, 0); if (!rtc_base) { dev_info(&pdev->dev, "apm_req_info node is not available!\n"); } of_property_read_u32(pdev->dev.of_node, "disable_mifdown", &disable_mifdown); if (disable_mifdown) __raw_writel(0, flexpmu_dbg_base + (DATA_LINE * DID_MIF_ALWAYS_ON) + 0xC); platform_set_drvdata(pdev, flexpmu_dbg_info); return 0; err_dbgfs_probe: kfree(flexpmu_dbg_info); err_flexpmu_info: return ret; } static int exynos_flexpmu_dbg_remove(struct platform_device *pdev) { struct dbgfs_info *flexpmu_dbg_info = platform_get_drvdata(pdev); debugfs_remove_recursive(flexpmu_dbg_root); kfree(flexpmu_dbg_info); platform_set_drvdata(pdev, NULL); return 0; } static const struct of_device_id exynos_flexpmu_dbg_match[] = { { .compatible = "samsung,exynos-flexpmu-dbg", }, {}, }; static struct platform_driver exynos_flexpmu_dbg_drv = { .probe = exynos_flexpmu_dbg_probe, .remove = exynos_flexpmu_dbg_remove, .driver = { .name = "exynos_flexpmu_dbg", .owner = THIS_MODULE, .of_match_table = exynos_flexpmu_dbg_match, }, }; static int exynos_flexpmu_dbg_init(void) { return platform_driver_register(&exynos_flexpmu_dbg_drv); } late_initcall(exynos_flexpmu_dbg_init); MODULE_LICENSE("GPL");