diff options
Diffstat (limited to 'drivers/platform/sprd/busmonitor.c')
| -rw-r--r-- | drivers/platform/sprd/busmonitor.c | 2111 |
1 files changed, 2111 insertions, 0 deletions
diff --git a/drivers/platform/sprd/busmonitor.c b/drivers/platform/sprd/busmonitor.c new file mode 100644 index 00000000..10bd2148 --- /dev/null +++ b/drivers/platform/sprd/busmonitor.c @@ -0,0 +1,2111 @@ +/*copyright (C) 2014 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/semaphore.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> +#include <linux/sysfs.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/timer.h> +#include <linux/timex.h> +#include <linux/rtc.h> + +#include <soc/sprd/sci.h> +#include <soc/sprd/hardware.h> +#include <soc/sprd/sci_glb_regs.h> +#include <soc/sprd/irqs.h> +#include <soc/sprd/busmonitor.h> +#include <soc/sprd/iomap.h> +#include <soc/sprd/pm_debug.h> +#include <soc/sprd/sprd_persistent_clock.h> + + +#ifdef CONFIG_OF +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#endif + +ulong axi_bm_base = 0; +ulong ahb_bm_base = 0; + +#define SPRD_AXI_BM_OFFSET 0x10000 +#define SPRD_AHB_BM_OFFSET 0x100000 +#define SPRD_AXIBM0_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM0) +#define SPRD_AXIBM1_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM1) +#define SPRD_AXIBM2_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM2) +#define SPRD_AXIBM3_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM3) +#define SPRD_AXIBM4_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM4) +#define SPRD_AXIBM5_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM5) +#define SPRD_AXIBM6_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM6) +#define SPRD_AXIBM7_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM7) +#define SPRD_AXIBM8_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM8) +#define SPRD_AXIBM9_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM9) + +#define SPRD_BM0_BASE (ahb_bm_base + CHN0 * SPRD_AHB_BM_OFFSET) +#define SPRD_BM1_BASE (ahb_bm_base + CHN1 * SPRD_AHB_BM_OFFSET) +#define SPRD_BM2_BASE (ahb_bm_base + CHN2 * SPRD_AHB_BM_OFFSET) + +#define PUB_APB_BM_CNT_START REG_PUB_APB_BUSMON_CNT_START +#define PUB_APB_BM_CFG REG_PUB_APB_BUSMON_CFG + +#define AP_AHB_AHB_EB REG_AP_AHB_AHB_EB +#define AP_AHB_AHB_RST REG_AP_AHB_AHB_RST +#define AP_AHB_MISC_CFG REG_AP_AHB_MISC_CFG + +struct memory_layout{ + u32 dram_start; + u32 dram_end; + + u32 ap_start1; + u32 ap_end1; + + u32 shm_start; + u32 shm_end; + + u32 ap_start2; + u32 ap_end2; + + u32 cpgge_start; + u32 cpgge_end; + + u32 cpw_start; + u32 cpw_end; + + u32 cpwcn_start; + u32 cpwcn_end; + + u32 cptl_start; + u32 cptl_end; + + u32 ap_start3; + u32 ap_end3; + + u32 fb_start; + u32 fb_end; + + u32 ion_start; + u32 ion_end; +}; + +static struct memory_layout memory_layout; + +static u32 bm_count = 0; + +static DEFINE_SPINLOCK(bm_lock); + +static ulong __sci_get_bm_base(u32 bm_index) +{ + switch (bm_index) { + case AXI_BM0: + return SPRD_AXIBM0_BASE; + case AXI_BM1: + return SPRD_AXIBM1_BASE; + case AXI_BM2: + return SPRD_AXIBM2_BASE; + case AXI_BM3: + return SPRD_AXIBM3_BASE; + case AXI_BM4: + return SPRD_AXIBM4_BASE; + case AXI_BM5: + return SPRD_AXIBM5_BASE; + case AXI_BM6: + return SPRD_AXIBM6_BASE; + case AXI_BM7: + return SPRD_AXIBM7_BASE; + case AXI_BM8: + return SPRD_AXIBM8_BASE; + case AXI_BM9: + return SPRD_AXIBM9_BASE; + case AHB_BM0: + return SPRD_BM0_BASE; + case AHB_BM1: + return SPRD_BM1_BASE; + case AHB_BM2: + return SPRD_BM2_BASE; + default: + break; + } + return -EINVAL; +} + +static void __sci_bm_init(void) +{ + u32 bm_index; + volatile struct sci_bm_reg *bm_reg; + + for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) { + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_index); + memset((void *)bm_reg, 0x0, sizeof(struct sci_bm_reg)); + } +} + +static inline void __sci_axi_bm_chn_en(int chn) +{ + ulong val, base_reg; + + base_reg = __sci_get_bm_base(chn); + val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG)); + val |= (BM_CHN_EN); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); +} + +static inline void __sci_axi_bm_chn_int_clr(int chn) +{ + ulong base_reg, val; + + base_reg = __sci_get_bm_base(chn); + val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG)); + val |= (BM_INT_CLR); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); +} + +static inline void __sci_axi_bm_chn_int_disable(int chn) +{ + ulong base_reg, val; + + base_reg = __sci_get_bm_base(chn); + val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG)); + val &= ~(BM_INT_EN); + val |= (BM_INT_CLR); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); +} + +static inline void __sci_axi_bm_chn_int_enable(int chn) +{ + ulong base_reg, val; + + base_reg = __sci_get_bm_base(chn); + val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG)); + val |= (BM_INT_CLR | BM_INT_EN); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); +} + +static inline ulong __sci_axi_bm_chn_cnt_bw(int chn) +{ + ulong base_reg, rbw, wbw; + + base_reg = __sci_get_bm_base(chn); + rbw = __raw_readl((volatile void *)(base_reg + AXI_BM_RBW_IN_WIN_REG)); + wbw = __raw_readl((volatile void *)(base_reg + AXI_BM_WBW_IN_WIN_REG)); + if(rbw || wbw) + BM_INFO(" chn:%d, rbw:%lu, wbw:%lu \n", chn, rbw, wbw ); + + return (rbw+wbw); +} + +static void __sci_axi_bm_cnt_start(void) +{ + ulong bm_index, base_reg, val; + + for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) { + base_reg = __sci_get_bm_base(bm_index); + val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG)); + val |= (BM_CHN_EN | BM_CNT_EN | BM_CNT_START | BM_INT_EN); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); + } + return; +} + +static void __sci_axi_bm_cnt_stop(void) +{ + ulong bm_index, base_reg, val; + + for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) { + base_reg = __sci_get_bm_base(bm_index); + val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG)); + val &= ~(BM_CNT_START | BM_INT_EN);//disable cnt and disable int + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); + } + return; +} + +static void __sci_bm_int_clr(void) +{ + int bm_index; + + for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) { + __sci_axi_bm_chn_int_clr(bm_index); + } +} + +static void __sci_axi_bm_int_disable(void) +{ + int bm_index; + + for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) { + __sci_axi_bm_chn_int_disable(bm_index); + } +} + +static void __sci_axi_bm_int_enable(void) +{ + int bm_index; + + for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) { + __sci_axi_bm_chn_int_enable(bm_index); + } +} + +/* performance count clear */ +static void __sci_axi_bm_cnt_clr(void) +{ + ulong bm_index, base_reg, val; + + for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) { + base_reg = __sci_get_bm_base(bm_index); + val = __raw_readl((volatile void *)base_reg); + val |= (BM_CHN_EN | BM_CNT_EN); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); + val &= ~(BM_CNT_START); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); + val |= (BM_CNT_CLR); + __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG)); + } + return; +} + +static void __sci_axi_bm_set_winlen(void) +{ + ulong bm_index, base_reg, axi_clk, win_len; + /*the win len is 10ms*/ +#ifdef CONFIG_ARCH_SCX30G + axi_clk = 640;//emc_clk_get(); +#else + //axi_clk = __raw_readl(REG_AON_APB_DPLL_CFG) & 0x7ff; + //axi_clk = axi_clk << 2; + axi_clk = 640; +#endif + /*the win_len = (axi_clk / 1000) * 10 */ + win_len = axi_clk * 10000; + for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) { + base_reg = __sci_get_bm_base(bm_index); + __raw_writel(win_len, (volatile void *)(base_reg + AXI_BM_CNT_WIN_LEN_REG)); + } +} + +static int __sci_bm_glb_reset_and_enable(u32 bm_index, bool is_enable) +{ + ulong reg_en, reg_rst, bit_en, bit_rst; + + switch (bm_index) { + case AXI_BM0: + case AXI_BM1: + case AXI_BM2: + case AXI_BM3: + case AXI_BM4: + case AXI_BM5: + case AXI_BM6: + case AXI_BM7: + case AXI_BM8: + case AXI_BM9: + reg_en = PUB_APB_BM_CFG;//REG_PUB_APB_BUSMON_CFG; + reg_rst = PUB_APB_BM_CFG; + bit_en = BIT(16 + bm_index); + bit_rst = BIT(bm_index); + break; + case AHB_BM0: + case AHB_BM1: + case AHB_BM2: + reg_en = AP_AHB_AHB_EB;// REG_AP_AHB_AHB_EB; + reg_rst = AP_AHB_AHB_RST; //REG_AP_AHB_AHB_RST; + bit_en = BIT(14 + bm_index - AHB_BM0); + bit_rst = BIT(17 + bm_index - AHB_BM0); + break; + default: + return -EINVAL; + } + sci_glb_set(reg_rst, bit_rst); + sci_glb_clr(reg_rst, bit_rst); + + if (is_enable) + sci_glb_set(reg_en, bit_en); + else + sci_glb_clr(reg_en, bit_en); + return SPRD_BM_SUCCESS; +} + +static void __sci_bm_glb_count_enable(bool is_enable) +{ + ulong reg_val = 0; + + reg_val = sci_glb_read(PUB_APB_BM_CNT_START, 0x1); + if (is_enable) { + if (!reg_val) + sci_glb_set(PUB_APB_BM_CNT_START, BIT(0)); + } else { + sci_glb_clr(PUB_APB_BM_CNT_START, BIT(0)); + } +} + +static void __sci_bm_glb_ahb_disable(void) +{ + int bm_index; + + for (bm_index = AHB_BM0; bm_index < BM_SIZE; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, false); +} + +static int __sci_bm_get_chn_sel(u32 bm_index) +{ + ulong reg_val, chn_sel; + + reg_val = sci_glb_read(AP_AHB_MISC_CFG, 0xFFFFFFFF); + + switch (bm_index) { + case AHB_BM0: + reg_val &= ((ulong)0x3 << 4); + chn_sel = (reg_val >> 4) + AHB_BM0_CHN0; + break; + case AHB_BM1: + reg_val &= ((ulong)0x3 << 8); + chn_sel = (reg_val >> 8) + AHB_BM1_CHN0; + break; + case AHB_BM2: + reg_val &= ((ulong)0x3 << 10); + chn_sel = (reg_val >> 10) + AHB_BM2_CHN0; + break; + default: + return -EINVAL; + } + return chn_sel; +} + +static int __sci_bm_chn_sel(u32 bm_index, u32 chn_id) +{ + ulong reg_val; + + reg_val = sci_glb_read(AP_AHB_MISC_CFG, 0xffffffff); + + switch (bm_index) { + case AHB_BM0: + reg_val &= ~(0x3 << 4); + reg_val |= (chn_id & 0x3) << 4; + break; + case AHB_BM1: + reg_val &= ~(0x3 << 8); + reg_val |= (chn_id & 0x3) << 8; + break; + case AHB_BM2: + reg_val &= ~(0x3 << 10); + reg_val |= (chn_id & 0x3) << 10; + break; + default: + return -EINVAL; + } + sci_glb_write(AP_AHB_MISC_CFG, reg_val, 0xff0); + + return SPRD_BM_SUCCESS; +} + +static void __sci_bm_store_int_info(u32 bm_index) +{ + ulong reg_addr, mask_id; + + reg_addr = __sci_get_bm_base(bm_index); + if(bm_ctn_dbg.bm_continue_dbg == false){ + debug_bm_int_info[bm_index].bm_index = bm_index; + debug_bm_int_info[bm_index].msk_addr = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ADDR_REG)); + debug_bm_int_info[bm_index].msk_cmd = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_CMD_REG)); + debug_bm_int_info[bm_index].msk_data_l = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_L_REG)); + debug_bm_int_info[bm_index].msk_data_h = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_H_REG)); + if(bm_index <= AXI_BM9){ + mask_id = __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ID_REG)); + debug_bm_int_info[bm_index].msk_id = mask_id >> 2 ? 0 : mask_id; + }else{ + mask_id = sci_glb_read(REG_AP_AHB_MISC_CFG, 0xFFFFFFFF); + debug_bm_int_info[bm_index].msk_id = 0; + } + BM_ERR("bm int info:\nBM CHN: %d\nOverlap ADDR: 0x%X\nOverlap CMD: 0x%X\nOverlap DATA: 0x%X 0x%X\nOverlap ID: %s--%ld\n", + debug_bm_int_info[bm_index].bm_index, + debug_bm_int_info[bm_index].msk_addr, + debug_bm_int_info[bm_index].msk_cmd, + debug_bm_int_info[bm_index].msk_data_l, + debug_bm_int_info[bm_index].msk_data_h, + bm_chn_name[bm_index].chn_name, + mask_id); + }else{ + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].bm_index = bm_index; + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_addr = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ADDR_REG)); + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_cmd = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_CMD_REG)); + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_l = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_L_REG)); + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_h = + __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_H_REG)); + if(bm_index <= AXI_BM9){ + mask_id = __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ID_REG)); + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_id = mask_id >> 2 ? 0 : mask_id; + }else{ + mask_id = sci_glb_read(REG_AP_AHB_MISC_CFG, 0xFFFFFFFF); + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_id = 0; + } + + BM_ERR("bm ctn info:\nBM CHN: %d\nOverlap ADDR: 0x%X\nOverlap CMD: 0x%X\nOverlap DATA: 0x%X 0x%X\nOverlap ID: %s--%ld\n", + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].bm_index, + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_addr, + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_cmd, + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_l, + bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_h, + bm_chn_name[bm_index].chn_name, + mask_id); + bm_ctn_dbg.current_cnt++; + } +} + +static irqreturn_t __sci_bm_isr(int irq_num, void *dev) +{ + ulong bm_index, rwbw_cnt, bm_chn, bm_reg, bm_int; + struct bm_per_info *bm_info; + struct timeval tv; + struct rtc_time tm; + + bm_info = (struct bm_per_info *)per_buf; + spin_lock(&bm_lock); + + /*only one precess handle the bm's irq is this case */ + if (!bm_irq_in_process) { + bm_irq_in_process = true; + } else { + spin_unlock(&bm_lock); + return IRQ_NONE; + } + + bm_reg = __sci_get_bm_base(AXI_BM0); + if(__raw_readl((volatile void *)(bm_reg + AXI_BM_INTC_REG)) & BM_CNT_EN) + { + if(bm_info == NULL){ + BM_ERR("BM irq ERR, trigger info: int 0x%x, min addr 0x%x, max addr 0x%x, cnt len 0x%x\n", + __raw_readl((volatile void *)(bm_reg + AXI_BM_INTC_REG)), + __raw_readl((volatile void *)(bm_reg + AXI_BM_ADDR_MIN_REG)), + __raw_readl((volatile void *)(bm_reg + AXI_BM_ADDR_MAX_REG)), + __raw_readl((volatile void *)(bm_reg + AXI_BM_CNT_WIN_LEN_REG))); + return IRQ_NONE; + } + __sci_axi_bm_cnt_stop(); + + rwbw_cnt = 0x0; + /*count stop time stamp */ + bm_info[buf_write_index].t_stop = get_sys_cnt();//__raw_readl((const volatile void *)(syscnt + 0xc));//((const volatile void *)(SPRD_SYSCNT_BASE + 0xc)); + bm_info[buf_write_index].count = bm_count; + + for (bm_chn = AXI_BM0; bm_chn <= AXI_BM9; bm_chn++) { + bm_reg = __sci_get_bm_base(bm_chn); + bm_info[buf_write_index].per_data[bm_chn][0] = + __raw_readl((volatile void *)(bm_reg + AXI_BM_RTRANS_IN_WIN_REG)); + bm_info[buf_write_index].per_data[bm_chn][1] = + __raw_readl((volatile void *)(bm_reg + AXI_BM_RBW_IN_WIN_REG)); + bm_info[buf_write_index].per_data[bm_chn][2] = + __raw_readl((volatile void *)(bm_reg + AXI_BM_RLATENCY_IN_WIN_REG)); + + bm_info[buf_write_index].per_data[bm_chn][3] = + __raw_readl((volatile void *)(bm_reg + AXI_BM_WTRANS_IN_WIN_REG)); + bm_info[buf_write_index].per_data[bm_chn][4] = + __raw_readl((volatile void *)(bm_reg + AXI_BM_WBW_IN_WIN_REG)); + bm_info[buf_write_index].per_data[bm_chn][5] = + __raw_readl((volatile void *)(bm_reg + AXI_BM_WLATENCY_IN_WIN_REG)); + + rwbw_cnt += bm_info[buf_write_index].per_data[bm_chn][1]; + rwbw_cnt += bm_info[buf_write_index].per_data[bm_chn][4]; + } + if (++buf_write_index == PER_COUNT_RECORD_SIZE) { + buf_write_index = 0; + buf_skip_cnt++; + } + + /*wake up the thread to output log per 4 second*/ + if ((buf_write_index == 0) || + buf_write_index == (PER_COUNT_RECORD_SIZE >> 1) ) { + /*need to skip the star buf, because it includes a lot of usefull info,skip 8s buf*/ + if(buf_skip_cnt > 1) + up(&bm_seam); + } + __sci_bm_int_clr(); + __sci_axi_bm_cnt_clr(); + __sci_axi_bm_set_winlen(); + + /*count start time stamp */ + do_gettimeofday(&tv); + rtc_time_to_tm(tv.tv_sec,&tm); + tm.tm_hour = tm.tm_hour < 16 ? (tm.tm_hour + 8) : (tm.tm_hour - 16); + bm_info[buf_write_index].t_start = get_sys_cnt();//__raw_readl((const volatile void *)(syscnt + 0xc));//((const volatile void *)(SPRD_SYSCNT_BASE + 0xc)); + bm_info[buf_write_index].tmp1 = (tm.tm_hour * 10000) + (tm.tm_min * 100) + tm.tm_sec;//emc_clk_get(); + bm_info[buf_write_index].tmp2 = 640;//__raw_readl(REG_AON_APB_DPLL_CFG); + + __sci_axi_bm_cnt_start(); + + bm_irq_in_process = false; + spin_unlock(&bm_lock); + + return IRQ_HANDLED; + } + + for(bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) + { + bm_reg = __sci_get_bm_base(bm_index); + if(bm_index < AHB_BM0) + bm_int = __raw_readl((volatile void *)(bm_reg + AXI_BM_INTC_REG)); + else + bm_int = __raw_readl((volatile void *)(bm_reg + AHB_BM_INTC_REG)); + if (bm_int & BM_INT_MSK_STS) { + __sci_bm_store_int_info(bm_index); + if((bm_ctn_dbg.bm_continue_dbg == true) && (bm_ctn_dbg.current_cnt <= BM_CONTINUE_DEBUG_SIZE)){ + bm_int &= ~BM_INT_EN; + bm_int |= (BM_INT_CLR | BM_INT_EN); + }else{ + bm_int &= ~BM_INT_EN; + } + __raw_writel(bm_int, (volatile void *)bm_reg); + if (bm_callback_set[bm_index].fun) + bm_callback_set[bm_index].fun(bm_callback_set[bm_index].data); + if(bm_st_info.bm_stack_st == true){ + BM_ERR("Bus Monitor output stack!\n"); + dump_stack(); + } + if(true == bm_st_info.bm_panic_st){ + BM_ERR("Bus Monitor enter panic!\n"); + BUG(); + } + } + } + bm_irq_in_process = false; + spin_unlock(&bm_lock); + + return IRQ_HANDLED; +} + +unsigned int dmc_mon_cnt_bw(void) +{ + int chn; + ulong cnt = 0; + + if(bm_st_info.bm_dbg_st != true){ + if(true == bm_st_info.bm_dfs_off_st) + return 0xFFFFFFFF; + for (chn = AXI_BM0; chn <= AXI_BM9; chn++) + cnt += __sci_axi_bm_chn_cnt_bw(chn); + return cnt; + }else + return 0xFFFFFFFF; +} + +void dmc_mon_cnt_clr(void) +{ + if(bm_st_info.bm_dbg_st != true) + __sci_axi_bm_cnt_clr(); + return; +} + +void dmc_mon_cnt_start(void) +{ + if(bm_st_info.bm_dbg_st != true){ + __sci_axi_bm_cnt_start(); + __sci_bm_glb_count_enable(true); + __sci_axi_bm_cnt_start(); + } + return; +} + +void dmc_mon_cnt_stop(void) +{ + if(bm_st_info.bm_dbg_st != true){ + __sci_bm_glb_count_enable(false); + __sci_axi_bm_cnt_stop(); + } + return; +} + +void dmc_mon_resume(void) +{ + return; +} + +int sci_bm_set_point(enum sci_bm_index bm_index, enum sci_bm_chn chn, + const struct sci_bm_cfg *cfg, void(*call_back)(void *), void *data) +{ + int ret; + u32 ddr_size = 0; + struct device_node *np = NULL; + struct resource res; + volatile struct sci_bm_reg *bm_reg; + + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_index); + /*clean the irq status*/ + bm_reg->intc |= BM_INT_CLR; + bm_reg->intc = 0x0; + memset((void *)bm_reg, 0x0, sizeof(*bm_reg)); + +#ifdef CONFIG_OF + np = of_find_node_by_name(NULL, "memory"); + if(np){ + ret = of_address_to_resource(np, 0, &res); + if(!ret) + ddr_size = res.end - res.start; + } + /* if monitor ddr addr, it needs addr wrap*/ + if(cfg->addr_min >= 0x80000000 && "ERROR_NAME" != bm_chn_name[bm_index].chn_name){ + if(ddr_size == 0x1fffffff)//512M + bm_store_vale[bm_index].bm_mask = 0xE0000000; + else if(ddr_size == 0x3fffffff)//1G + bm_store_vale[bm_index].bm_mask = 0xC0000000; + else// >= 4G, do not need mask + bm_store_vale[bm_index].bm_mask = 0x00000000; + }else + bm_store_vale[bm_index].bm_mask = 0x00000000; +#endif + + if ("ERROR_NAME" != bm_chn_name[bm_index].chn_name){ + if (bm_index < AHB_BM0) { + bm_reg->addr_min = cfg->addr_min & (~bm_store_vale[bm_index].bm_mask); + bm_reg->addr_max = cfg->addr_max & (~bm_store_vale[bm_index].bm_mask); + bm_reg->addr_msk = bm_store_vale[bm_index].bm_mask; + + /* the interrupt just trigger by addr range for axi + * busmonitor, so set the data range difficult to + * access. + */ + if (bm_chn_name[bm_index].chn_type) { + bm_reg->data_min_l = 0x0fffffff; + bm_reg->data_min_h = 0x0fffffff; + bm_reg->data_max_l = 0x0; + bm_reg->data_max_h = 0x0; + bm_reg->data_msk_l = 0x0; + bm_reg->data_msk_h = 0x0; + } else { + bm_reg->data_min_l = 0x0; + bm_reg->data_min_h = 0x0; + bm_reg->data_max_l = 0xffffffff; + bm_reg->data_max_h = 0xffffffff; + bm_reg->data_msk_l = 0x0; + bm_reg->data_msk_h = 0x0; + } + + switch (cfg->bm_mode) { + case R_MODE: + bm_reg->cfg = 0x1; + break; + case W_MODE: + bm_reg->cfg = 0x3; + break; + case RW_MODE: + bm_reg->cfg = 0x0; + break; + default: + return -EINVAL; + } + + bm_reg->intc |= BM_INT_EN | BM_CHN_EN; + + } else { + bm_reg->addr_min = cfg->addr_min & (~bm_store_vale[bm_index].bm_mask); + bm_reg->addr_max = cfg->addr_max & (~bm_store_vale[bm_index].bm_mask); + bm_reg->addr_msk = bm_store_vale[bm_index].bm_mask; + bm_reg->data_min_l = cfg->data_min_l; + bm_reg->data_min_h = cfg->data_min_h; + bm_reg->data_max_l = cfg->data_max_l; + bm_reg->data_max_h = cfg->data_max_h; + bm_reg->data_msk_l = 0x0; + bm_reg->data_msk_h = 0x0; + + switch (cfg->bm_mode) { + case R_MODE: + bm_reg->cfg = 0x1; + break; + case W_MODE: + bm_reg->cfg = 0x3; + break; + case RW_MODE: + bm_reg->cfg = 0x0; + break; + default: + return -EINVAL; + } + + ret = __sci_bm_chn_sel(bm_index, chn); + if (ret < 0) + return ret; + + bm_reg->intc |= BM_INT_EN | BM_CHN_EN; + } + } + + bm_callback_set[bm_index].fun = call_back; + bm_callback_set[bm_index].data = data; + + return SPRD_BM_SUCCESS; +} + +void sci_bm_unset_point(enum sci_bm_index bm_index) +{ + ulong reg_addr; + + reg_addr = __sci_get_bm_base(bm_index); + + __raw_writel(BM_INT_CLR | BM_CHN_EN, (volatile void *)reg_addr); + + __raw_writel(0x0, (volatile void *)reg_addr); + + bm_callback_set[bm_index].fun = NULL; +} + +void sci_bm_set_perform_point(void) +{ + __sci_bm_init(); + __sci_axi_bm_cnt_clr(); + __sci_bm_int_clr(); + + __sci_axi_bm_set_winlen(); + __sci_bm_glb_count_enable(false); + __sci_axi_bm_cnt_start(); + __sci_bm_glb_count_enable(true); +} + +static void sci_bm_def_val_set_by_dts(void) +{ + u32 bm_chn, ret; + struct sci_bm_cfg bm_cfg; + //default set is monitor CP write AP memory. + for(bm_chn = AXI_BM0; bm_chn <= AXI_BM9; bm_chn++){ + if(!strcmp(bm_chn_name[bm_chn].chn_name, "CPU") || + !strcmp(bm_chn_name[bm_chn].chn_name, "DISP") || + !strcmp(bm_chn_name[bm_chn].chn_name, "GPU") || + !strcmp(bm_chn_name[bm_chn].chn_name, "AP/ZIP") || + !strcmp(bm_chn_name[bm_chn].chn_name, "ZIP") || + !strcmp(bm_chn_name[bm_chn].chn_name, "AP") || + !strcmp(bm_chn_name[bm_chn].chn_name, "MM") || + !strcmp(bm_chn_name[bm_chn].chn_name, "ERROR_NAME")) + continue; + bm_cfg.addr_min = memory_layout.ap_start3; + bm_cfg.addr_max = memory_layout.ap_end3; + bm_cfg.bm_mode = W_MODE; + ret = sci_bm_set_point(bm_chn, CHN0, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return; + } +} + +static void sci_bm_def_val_set(void) +{ + u32 bm_chn, ret; + struct sci_bm_cfg bm_cfg; + + for (bm_chn = AXI_BM0; bm_chn < BM_SIZE; bm_chn++){ + if((0x00000000 == bm_def_value[bm_chn].str_addr) && (0x00000000 == bm_def_value[bm_chn].end_addr)) + continue; + bm_cfg.addr_min = bm_def_value[bm_chn].str_addr; + bm_cfg.addr_max = bm_def_value[bm_chn].end_addr; + bm_cfg.bm_mode = bm_def_value[bm_chn].mode; + ret = sci_bm_set_point(bm_chn, bm_def_value[bm_chn].chn_sel, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return; + } +} + +static int sci_bm_output_log(void *p) +{ + mm_segment_t old_fs; + int ret; + + while (1) { + down(&bm_seam); + + if (!log_file) { + log_file = filp_open(LOG_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(log_file) || !log_file || !log_file->f_dentry) { + BM_ERR("file_open(%s) for create failed\n", LOG_FILE_PATH); + return -ENODEV; + } + } + switch (buf_write_index) { + case 0: + buf_read_index = PER_COUNT_RECORD_SIZE >> 1; + break; + case (PER_COUNT_RECORD_SIZE >> 1): + buf_read_index = 0x0; + break; + default: + BM_ERR("get buf_read_indiex failed!\n"); + } + old_fs = get_fs(); + set_fs(get_ds()); + ret = vfs_write(log_file, + (const char *)(per_buf + buf_read_index * sizeof(struct bm_per_info)), + sizeof(struct bm_per_info) *(PER_COUNT_RECORD_SIZE >> 1), + &log_file->f_pos); + + set_fs(old_fs); + + /*raw back file write*/ + if (log_file->f_pos >= (sizeof(struct bm_per_info) * LOG_FILE_MAX_RECORDS)) { + log_file->f_pos = 0x0; + } + } + + filp_close(log_file, NULL); + + return 0; +} + +static ssize_t sci_bm_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if(bm_st_info.bm_dbg_st == true) + return sprintf(buf, "Bus Monitor in debug mode!\n"); + else + return sprintf(buf, "Bus Monitor in bandwidth mode!\n"); +} + +static ssize_t sci_bm_chn_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 bm_index; + char occ_info[30] = {}; + char chn_info[30*BM_CHANNEL_SIZE] = {}; + + for(bm_index = AXI_BM0; bm_index < BM_CHANNEL_SIZE; bm_index++){ + sprintf(occ_info, "%s\n", bm_chn_name[bm_index].chn_name); + strcat(chn_info, occ_info); + } + return sprintf(buf, "%s\n", chn_info); +} + +static ssize_t sci_bm_axi_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 bm_index; + ulong reg_addr, str_addr, end_addr; + char chn_info[58]; + char info_buf[58*10] = {}; + + for(bm_index = AXI_BM0; bm_index < AHB_BM0; bm_index++){ + reg_addr = __sci_get_bm_base(bm_index); + str_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MIN_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask); + end_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MAX_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask); + sprintf(chn_info, "%d 0x%08lX 0x%08lX %s\n", bm_index, str_addr, end_addr, bm_chn_name[bm_index].chn_name); + strcat(info_buf, chn_info); + } + BM_INFO("%s\n", info_buf); + return sprintf(buf, "%s\n", info_buf); +} + +static ssize_t sci_bm_axi_dbg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned char start[12], end[12], chn[6], mod[3]; + unsigned long start_addr, end_addr, channel, bm_index; + struct sci_bm_cfg bm_cfg; + u32 rd_wt; + int ret,i; + + sscanf(buf, "%s %s %s %s",chn, start, end, mod); + + ret = strict_strtoul(start, 0, &start_addr); + if (ret) + BM_ERR("start %s is not in hex or decimal form.\n", buf); + ret = strict_strtoul(end, 0, &end_addr); + if (ret) + BM_ERR("end %s is not in hex or decimal form.\n", buf); + if((chn[0] >= '0') && (chn[0] <= '9')) + channel = chn[0] - '0'; + else if(strcmp(chn, "all") == 0) + channel = BM_DEBUG_ALL_CHANNEL; + else{ + BM_ERR("please input a legal channel number! e.g: 0-9,all."); + return EINVAL; + } + if((strcmp(mod, "r") == 0)||(strcmp(mod, "R") == 0)) + rd_wt = R_MODE; + else if((strcmp(mod, "w") == 0)||(strcmp(mod, "W") == 0)) + rd_wt = W_MODE; + else if((strcmp(mod, "rw") == 0)||(strcmp(mod, "RW") == 0)) + rd_wt = RW_MODE; + else{ + BM_ERR("please input a legal channel mode! e.g: r,w,rw"); + return EINVAL; + } + + BM_INFO("str addr 0x%lx end addr 0x%lx chn %ld rw %d\n", start_addr, end_addr, channel, rd_wt); + if(((channel > AXI_BM9) && (channel != BM_DEBUG_ALL_CHANNEL)) || (rd_wt > RW_MODE) || (start_addr > end_addr)) + return -EINVAL; + + /*for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, true);*/ + bm_st_info.bm_dbg_st = true; + bm_st_info.bm_dfs_off_st = true; + + if(channel != BM_DEBUG_ALL_CHANNEL){ + bm_cfg.addr_min = (u32)start_addr; + bm_cfg.addr_max = (u32)end_addr; + bm_cfg.bm_mode = rd_wt; + ret = sci_bm_set_point(channel, CHN0, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return ret; + }else{ + bm_cfg.addr_min = (u32)start_addr; + bm_cfg.addr_max = (u32)end_addr; + bm_cfg.bm_mode = rd_wt; + for(i = AXI_BM0; i < AHB_BM0; i++){ + ret = sci_bm_set_point(i, CHN0, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return ret; + } + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_ahb_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 bm_index, chn_sel; + ulong reg_addr, str_addr, end_addr, str_data, end_data; + char chn_info[84]; + char info_buf[84*3] = {}; + + for(bm_index = AHB_BM0; bm_index < BM_SIZE; bm_index++){ + reg_addr = __sci_get_bm_base(bm_index); + str_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MIN_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask); + end_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MAX_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask); + str_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MIN_L_REG)); + end_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MAX_L_REG)); + chn_sel = __sci_bm_get_chn_sel(bm_index); + sprintf(chn_info, "%d 0x%08lX 0x%08lX 0x%08lX 0x%08lX %s\n", bm_index-10, str_addr, end_addr, + str_data, end_data, bm_chn_name[chn_sel].chn_name); + strcat(info_buf, chn_info); + } + BM_INFO("%s\n", info_buf); + return sprintf(buf, "%s\n", info_buf); +} + +static ssize_t sci_bm_ahb_dbg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned char addr_start[12], data_min[12]; + unsigned char addr_end[12], data_max[12]; + unsigned long start_addr, end_addr, min_data, max_data, channel, chn_sel, bm_index; + struct sci_bm_cfg bm_cfg; + int ret; + + sscanf(buf, "%ld %ld %s %s %s %s", &channel, &chn_sel, addr_start, addr_end, data_min, data_max); + + ret = strict_strtoul(addr_start, 0, &start_addr); + if (ret) + BM_ERR("start addr %s is not in hex or decimal form.\n", buf); + ret = strict_strtoul(addr_end, 0, &end_addr); + if (ret) + BM_ERR("end addr %s is not in hex or decimal form.\n", buf); + ret = strict_strtoul(data_min, 0, &min_data); + if (ret) + BM_ERR("start data %s is not in hex or decimal form.\n", buf); + ret = strict_strtoul(data_max, 0, &max_data); + if (ret) + BM_ERR("end data %s is not in hex or decimal form.\n", buf); + + BM_INFO("str addr 0x%lX end addr 0x%lX min data 0x%lX max data 0x%lX\n", start_addr, end_addr, min_data, max_data); + if((channel > 3) || (chn_sel > 4) || (start_addr > end_addr) || (min_data > max_data)) + return -EINVAL; + + __sci_bm_glb_reset_and_enable(channel + AHB_BM0, true); + bm_st_info.bm_dbg_st = true; + bm_st_info.bm_dfs_off_st = true; + + bm_cfg.addr_min = (u32)start_addr; + bm_cfg.addr_max = (u32)end_addr; + bm_cfg.data_min_l = (u32)min_data; + bm_cfg.data_min_h = 0x0; + bm_cfg.data_max_l = (u32)max_data; + bm_cfg.data_max_h = 0x0; + bm_cfg.bm_mode = W_MODE; + ret = sci_bm_set_point(channel + AHB_BM0, chn_sel, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return ret; + return strnlen(buf, count); +} + +static ssize_t sci_bm_bandwidth_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u32 bw_en, bm_index; + struct task_struct *t; + + bm_st_info.bm_dfs_off_st = false; + sscanf(buf, "%d", &bw_en); + if(bw_en){ + BM_INFO("bm bandwidth mode enable!!!\n"); + bm_st_info.bm_dbg_st = false; + if(per_buf == NULL){ + per_buf = kmalloc(PER_COUNT_BUF_SIZE, GFP_KERNEL); + if (!per_buf) + BM_ERR("kmalloc failed!\n"); + sema_init(&bm_seam, 0); + t = kthread_run(sci_bm_output_log, NULL, "%s", "bm_per_log"); + if (IS_ERR(t)) { + BM_ERR("bm probe: Failed to run thread bm_per_log\n"); + kthread_stop(t); + return 0; + } + } + for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) { + __sci_bm_glb_reset_and_enable(bm_index, true); + bm_store_vale[bm_index].bm_mask= 0; + } + sci_bm_set_perform_point(); + msleep(100); + }else{ + BM_INFO("bm bandwidth mode disable!!!\n"); + bm_st_info.bm_dbg_st = true; + if(per_buf != NULL){ + kfree(per_buf); + per_buf = NULL; + if(log_file != NULL){ + filp_close(log_file, NULL); + log_file = NULL; + buf_write_index = 0; + bm_count = 0; + } + } + __sci_axi_bm_cnt_clr(); + __sci_bm_int_clr(); + __sci_bm_glb_count_enable(false); + for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) { + __sci_bm_glb_reset_and_enable(bm_index, false); + bm_store_vale[bm_index].bm_mask= 0; + } + __sci_bm_glb_count_enable(false); + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_occur_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 bm_index; + char occ_info[116] = {}; + char chn_info[116*21] = {}; + + for(bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++){ + if(debug_bm_int_info[bm_index].msk_addr != 0){ + sprintf(occ_info, " %s\n addr: 0x%X\n CMD: 0x%X\n msk_data_l: 0x%X\n msk_data_h: 0x%X\n id 0x%X\n", + bm_chn_name[bm_index].chn_name, + debug_bm_int_info[bm_index].msk_addr, + debug_bm_int_info[bm_index].msk_cmd, + debug_bm_int_info[bm_index].msk_data_l, + debug_bm_int_info[bm_index].msk_data_h, + debug_bm_int_info[bm_index].msk_id); + strcat(chn_info, occ_info); + } + } + if(chn_info[1] == 0) + sprintf(chn_info, ":-) ! No action was monitored by BM!!!\n"); + + return sprintf(buf, "%s\n", chn_info); +} + +static ssize_t sci_bm_continue_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 bm_index; + char occ_info[96] = {}; + char chn_info[96*50] = {}; + + for(bm_index = 0; bm_index < bm_ctn_dbg.loop_cnt; bm_index++){ + if(bm_ctn_dbg.bm_ctn_info[bm_index].msk_addr != 0){ + sprintf(occ_info, " %d\n addr: 0x%X\n CMD: 0x%X\n msk_data_l: 0x%X\n msk_data_h: 0x%X\n id 0x%X\n", + bm_ctn_dbg.bm_ctn_info[bm_index].bm_index, + bm_ctn_dbg.bm_ctn_info[bm_index].msk_addr, + bm_ctn_dbg.bm_ctn_info[bm_index].msk_cmd, + bm_ctn_dbg.bm_ctn_info[bm_index].msk_data_l, + bm_ctn_dbg.bm_ctn_info[bm_index].msk_data_h, + bm_ctn_dbg.bm_ctn_info[bm_index].msk_id); + strcat(chn_info, occ_info); + } + } + if(chn_info[1] == 0) + sprintf(chn_info, ":-) ! No action was monitored by BM!!!\n"); + + return sprintf(buf, "%s\n", chn_info); +} + +static ssize_t sci_bm_continue_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u32 ctn_num; + sscanf(buf, "%d", &ctn_num); + if(ctn_num){ + BM_INFO("Set BM support continue debug!!!\n"); + bm_ctn_dbg.bm_continue_dbg = true; + if(ctn_num > BM_CONTINUE_DEBUG_SIZE) + bm_ctn_dbg.loop_cnt = BM_CONTINUE_DEBUG_SIZE; + else + bm_ctn_dbg.loop_cnt = ctn_num; + }else{ + BM_INFO("Set BM do not support continue debug!!!\n"); + bm_ctn_dbg.bm_continue_dbg = false; + bm_ctn_dbg.loop_cnt = 0; + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_dfs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if(bm_st_info.bm_dfs_off_st == true) + return sprintf(buf, "The BM DFS is closed.\n"); + else + return sprintf(buf, "The BM DFS is open.\n"); +} + +static ssize_t sci_bm_dfs_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u32 dfs_flg; + sscanf(buf, "%d", &dfs_flg); + if(dfs_flg){ + BM_INFO("Disable BM DFS.\n"); + bm_st_info.bm_dfs_off_st = true; + }else{ + BM_INFO("Reopen BM DFS.\n"); + bm_st_info.bm_dfs_off_st = false; + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_panic_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if(bm_st_info.bm_panic_st == true) + return sprintf(buf, "The BM panic is open.\n"); + else + return sprintf(buf, "The BM panic is close.\n"); +} + +static ssize_t sci_bm_panic_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u32 panic_flg; + sscanf(buf, "%d", &panic_flg); + if(panic_flg){ + BM_INFO("Reopen BM panic.\n"); + bm_st_info.bm_panic_st = true; + }else{ + BM_INFO("Disable BM panic.\n"); + bm_st_info.bm_panic_st = false; + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_stack_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if(bm_st_info.bm_stack_st == true) + return sprintf(buf, "The BM stack is open.\n"); + else + return sprintf(buf, "The BM stack is close.\n"); +} + +static ssize_t sci_bm_stack_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u32 panic_flg; + sscanf(buf, "%d", &panic_flg); + if(panic_flg){ + BM_INFO("Reopen BM DFS.\n"); + bm_st_info.bm_stack_st = true; + }else{ + BM_INFO("Disable BM DFS.\n"); + bm_st_info.bm_stack_st = false; + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_disable_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u32 dis_val; + sscanf(buf, "%d", &dis_val); + if(dis_val){ + BM_INFO("Set BM disable count %d\n", bm_count); + __raw_writel(0x0, (volatile void *)(REG_PUB_APB_BUSMON_CFG)); + }else{ + bm_count++; + BM_INFO("Set BM enable count %d\n", bm_count); + __raw_writel(BM_CHANNEL_ENABLE_FLAGE, (volatile void *)(REG_PUB_APB_BUSMON_CFG)); + } + return strnlen(buf, count); +} + +static ssize_t sci_bm_bus_status_show(struct device * dev, + struct device_attribute * attr,const char * buf) +{ + u32 bm_index; + ulong reg_addr, axi_val; + char axi_info[20] = {0}, axi_buf[10*20] = {0}; + + for(bm_index = AXI_BM0; bm_index < AHB_BM0; bm_index++){ + reg_addr = __sci_get_bm_base(bm_index); + axi_val = __raw_readl((volatile void *)(reg_addr + AXI_BM_BUS_STATUS_REG)); + sprintf(axi_info, "0x%08lX\n", axi_val); + strcat(axi_buf, axi_info); + } + BM_INFO("%s\n", axi_buf); + return sprintf(buf, "%s\n", axi_buf); +} + +static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, + sci_bm_state_show, NULL); + +static DEVICE_ATTR(chn, S_IRUGO | S_IWUSR, + sci_bm_chn_show, NULL); + +static DEVICE_ATTR(axi_dbg, S_IRUGO | S_IWUSR, + sci_bm_axi_dbg_show, sci_bm_axi_dbg_store); + +static DEVICE_ATTR(ahb_dbg, S_IRUGO | S_IWUSR, + sci_bm_ahb_dbg_show, sci_bm_ahb_dbg_store); + +static DEVICE_ATTR(bandwidth, S_IRUGO | S_IWUSR, + NULL, sci_bm_bandwidth_store); + +static DEVICE_ATTR(occur, S_IRUGO | S_IWUSR, + sci_bm_occur_show, NULL); + +static DEVICE_ATTR(continue, S_IRUGO | S_IWUSR, + sci_bm_continue_show, sci_bm_continue_store); + +static DEVICE_ATTR(dfs_off, S_IRUGO | S_IWUSR, + sci_bm_dfs_show, sci_bm_dfs_store); + +static DEVICE_ATTR(panic, S_IRUGO | S_IWUSR, + sci_bm_panic_show, sci_bm_panic_store); + +static DEVICE_ATTR(stack, S_IRUGO | S_IWUSR, + sci_bm_stack_show, sci_bm_stack_store); + +static DEVICE_ATTR(disable, S_IRUGO | S_IWUSR, + NULL, sci_bm_disable_store); + +static DEVICE_ATTR(bus_status, S_IRUGO | S_IWUSR, + sci_bm_bus_status_show, NULL); + + +static struct attribute *bm_attrs[] = { + &dev_attr_state.attr, + &dev_attr_chn.attr, + &dev_attr_axi_dbg.attr, + &dev_attr_ahb_dbg.attr, + &dev_attr_bandwidth.attr, + &dev_attr_occur.attr, + &dev_attr_continue.attr, + &dev_attr_dfs_off.attr, + &dev_attr_panic.attr, + &dev_attr_stack.attr, + &dev_attr_disable.attr, + &dev_attr_bus_status.attr, + NULL, +}; + +static struct attribute_group bm_attr_group = { + .attrs = bm_attrs, +}; + +#define BM_MAX(x, y) (x > y ? x : y) +static int sci_bm_get_mem_layout(void) +{ + struct device_node *np = NULL; + struct resource res; + int ret; + char *devname; + + np = of_find_node_by_name(NULL, "memory"); + if(np){ + ret = of_address_to_resource(np, 0, &res); + if(!ret){ + memory_layout.dram_start = res.start; + memory_layout.dram_end = res.end; + } + } + + np = of_find_compatible_node(NULL, NULL, "sprd,sipc"); + if(np){ + ret = of_address_to_resource(np, 0, &res); + if(!ret){ + memory_layout.shm_start = res.start; + memory_layout.shm_end = res.end; + } + } + + for_each_compatible_node(np, NULL, "sprd,scproc"){ + if(np){ + of_property_read_string(np, "sprd,name", (const char **)&devname); + if(!strcmp(devname, "cpw")){ + ret = of_address_to_resource(np, 0, &res); + if(!ret){ + memory_layout.cpw_start = res.start; + memory_layout.cpw_end = res.end; + } + }else if(!strcmp(devname, "cpgge")){ + ret = of_address_to_resource(np, 0, &res); + if(!ret){ + memory_layout.cpgge_start = res.start; + memory_layout.cpgge_end = res.end; + } + }else if(!strcmp(devname, "cpwcn")){ + ret = of_address_to_resource(np, 0, &res); + if(!ret){ + memory_layout.cpwcn_start = res.start; + memory_layout.cpwcn_end = res.end; + } + }else if(!strcmp(devname, "cptl")){ + ret = of_address_to_resource(np, 0, &res); + if(!ret){ + memory_layout.cptl_start = res.start; + memory_layout.cptl_end = res.end; + } + } + } + } + + np = of_find_compatible_node(NULL, NULL, "sprd,sprdfb"); + if(np){ + u32 val[2] = { 0 }; + ret = of_property_read_u32_array(np, "sprd,fb_mem", val, 2); + if(!ret){ + memory_layout.fb_start = val[0]; + memory_layout.fb_end = val[0] + val[1] - 1; + } + } + + np = of_find_compatible_node(NULL, NULL, "sprd,ion-sprd"); + if(np){ + struct device_node *child = NULL; + char *reg_name = NULL; + u32 val[2] = { 0 }; + + for_each_child_of_node(np, child){ + of_property_read_string(child, "reg-names", (const char **)®_name); + if(!strcmp("ion_heap_carveout_overlay", reg_name)){ + ret = of_property_read_u32_array(child, "sprd,ion-heap-mem", val, 2); + if(!ret){ + memory_layout.ion_start = val[0]; + memory_layout.ion_end= val[0] + val[1] - 1; + } + } + } + } + memory_layout.ap_start3 = BM_MAX(memory_layout.cpw_end, memory_layout.cpgge_end); + memory_layout.ap_start3 = BM_MAX(memory_layout.ap_start3, memory_layout.cpwcn_end); + memory_layout.ap_start3 = BM_MAX(memory_layout.ap_start3, memory_layout.cptl_end) + 1; + + memory_layout.ap_start1 = memory_layout.dram_start; + memory_layout.ap_end1 = memory_layout.shm_start - 1; + memory_layout.ap_start2 = memory_layout.shm_end + 1; + memory_layout.ap_end2 = memory_layout.cpgge_start -1; + //memory_layout.ap_start3 = memory_layout.cptl_end + 1; + memory_layout.ap_end3 = memory_layout.fb_start -1; + + BM_ERR("%s: dram: 0x%08X ~ 0x%08X\n", __func__, memory_layout.dram_start, memory_layout.dram_end); + BM_ERR("%s: ap_1: 0x%08X ~ 0x%08X\n", __func__, memory_layout.ap_start1, memory_layout.ap_end1); + BM_ERR("%s: shm : 0x%08X ~ 0x%08X\n", __func__, memory_layout.shm_start, memory_layout.shm_end); + BM_ERR("%s: ap_2: 0x%08X ~ 0x%08X\n", __func__, memory_layout.ap_start2, memory_layout.ap_end2); + BM_ERR("%s: cpge: 0x%08X ~ 0x%08X\n", __func__, memory_layout.cpgge_start, memory_layout.cpgge_end); + BM_ERR("%s: cpw : 0x%08X ~ 0x%08X\n", __func__, memory_layout.cpw_start, memory_layout.cpw_end); + BM_ERR("%s: cpwcn: 0x%08X ~ 0x%08X\n", __func__, memory_layout.cpwcn_start, memory_layout.cpwcn_end); + BM_ERR("%s: cptl: 0x%08X ~ 0x%08X\n", __func__, memory_layout.cptl_start, memory_layout.cptl_end); + BM_ERR("%s: ap_3: 0x%08X ~ 0x%08X\n", __func__, memory_layout.ap_start3, memory_layout.ap_end3); + BM_ERR("%s: fb : 0x%08X ~ 0x%08X\n", __func__, memory_layout.fb_start, memory_layout.fb_end); + BM_ERR("%s: ion : 0x%08X ~ 0x%08X\n", __func__, memory_layout.ion_start, memory_layout.ion_end); + + return 0; +} + +static void sci_bm_init_name_info(void) +{ + u32 bm_index, bm_chn, bm_no_name = 0; + u32 axi_chn_nr, axi_chn_type; + //init axi bm name info. + for(bm_chn = 0; bm_chn < bm_st_info.axi_bm_cnt; bm_chn++){ + for(bm_index = 3; bm_index < BM_AXI_TOTAL_CHN_NAME + 3; bm_index++){ + axi_chn_nr = (*(&bm_st_info.bm_def_mode + bm_index)) & 0xff; + axi_chn_type = ((*(&bm_st_info.bm_def_mode + bm_index)) >> 31) & 0x01; + if(bm_chn == axi_chn_nr){ + bm_chn_name[bm_chn].chn_name = bm_name_list[bm_index - 3].chn_name; + bm_chn_name[bm_chn].chn_type = axi_chn_type; + break; + } + } + if (NULL == bm_chn_name[bm_chn].chn_name) + bm_chn_name[bm_chn].chn_name = "ERROR_NAME"; + } + //init ahb bm name info. + for(bm_chn = bm_st_info.axi_bm_cnt; bm_chn < BM_AXI_TOTAL_CHN_NAME + BM_AHB_TOTAL_CHN_NAME + 3; bm_chn++){ + for(bm_index = BM_AXI_TOTAL_CHN_NAME + 3; bm_index < BM_AXI_TOTAL_CHN_NAME + BM_AHB_TOTAL_CHN_NAME + 3; bm_index++){ + if(bm_chn == ((*(&bm_st_info.bm_def_mode + bm_index)) + bm_st_info.axi_bm_cnt)){ + bm_chn_name[bm_chn - bm_no_name].chn_name = bm_name_list[bm_index - 3].chn_name; + break; + } + } + if("ERROR_NAME" == bm_chn_name[bm_chn - bm_no_name].chn_name){ + //del the no name chn. to get a continue name list. + bm_no_name++; + } + } +} + +static void sci_bm_get_hw_info(void) +{ + struct device_node *np = NULL; + u32 val[2]; + + np = of_find_compatible_node(NULL, NULL, "sprd,sprd_bm"); + if(np){ + //Get the bus monitor default mode. + if(of_property_read_u32(np, "sprd,bm_status", &val[0])){ + BM_WRN("it must be sure that the bus monitor is in pref mode!\n"); + bm_st_info.bm_def_mode = 0; + }else{ + bm_st_info.bm_def_mode = val[0]; + BM_INFO("Bus Monitor mode: 0 - Pref; 1 - Debug. Current mode %d\n", bm_st_info.bm_def_mode); + } + if(of_property_read_u32_array(np, "sprd,bm_count", &val[0], 2)){ + BM_WRN("it must be sure that the there is no count para!\n"); + bm_st_info.axi_bm_cnt = 10; + bm_st_info.ahb_bm_chn = 11; + }else{ + bm_st_info.axi_bm_cnt = val[0]; + bm_st_info.ahb_bm_chn = val[1]; + } + //Get AXI bus monitor arch. + if(of_property_read_u32_array(np, "sprd,cpu_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CPU chn!\n"); + bm_st_info.cpu_chn = 0xff; + }else{ + bm_st_info.cpu_chn = val[0]; + bm_st_info.cpu_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,disp_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not DISP chn!\n"); + bm_st_info.disp_chn = 0xff; + }else{ + bm_st_info.disp_chn = val[0]; + bm_st_info.disp_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,gpu_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not GPU chn!\n"); + bm_st_info.gpu_chn = 0xff; + }else{ + bm_st_info.gpu_chn = val[0]; + bm_st_info.gpu_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,ap_zip_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP/ZIP chn!\n"); + bm_st_info.ap_zip_chn = 0xff; + }else{ + bm_st_info.ap_zip_chn = val[0]; + bm_st_info.ap_zip_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,zip_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not ZIP chn!\n"); + bm_st_info.zip_chn = 0xff; + }else{ + bm_st_info.zip_chn = val[0]; + bm_st_info.zip_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,ap_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP chn!\n"); + bm_st_info.ap_chn = 0xff; + }else{ + bm_st_info.ap_chn = val[0]; + bm_st_info.ap_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,mm_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not MM chn!\n"); + bm_st_info.mm_chn = 0xff; + }else{ + bm_st_info.mm_chn = val[0]; + bm_st_info.mm_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp0_arm0_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP0 ARM0 chn!\n"); + bm_st_info.cp0_arm0_chn = 0xff; + }else{ + bm_st_info.cp0_arm0_chn = val[0]; + bm_st_info.cp0_arm0_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp0_arm1_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP0 ARM1 chn!\n"); + bm_st_info.cp0_arm1_chn = 0xff; + }else{ + bm_st_info.cp0_arm1_chn = val[0]; + bm_st_info.cp0_arm1_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp0_dsp_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP0 DSP chn!\n"); + bm_st_info.cp0_dsp_chn = 0xff; + }else{ + bm_st_info.cp0_dsp_chn = val[0]; + bm_st_info.cp0_dsp_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp0_arm0_1_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP0 ARM0/1 chn!\n"); + bm_st_info.cp0_arm0_1_chn = 0xff; + }else{ + bm_st_info.cp0_arm0_1_chn = val[0]; + bm_st_info.cp0_arm0_1_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp1_lte_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP1 LTE chn!\n"); + bm_st_info.cp1_lte_chn = 0xff; + }else{ + bm_st_info.cp1_lte_chn = val[0]; + bm_st_info.cp1_lte_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp1_dsp_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP1 DSP chn!\n"); + bm_st_info.cp1_dsp_chn = 0xff; + }else{ + bm_st_info.cp1_dsp_chn = val[0]; + bm_st_info.cp1_dsp_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp1_arm_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP1 ARM chn!\n"); + bm_st_info.cp1_arm_chn = 0xff; + }else{ + bm_st_info.cp1_arm_chn = val[0]; + bm_st_info.cp1_arm_chn |= (val[1] << 31); + } + if(of_property_read_u32_array(np, "sprd,cp2_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not CP2 chn!\n"); + bm_st_info.cp2_chn = 0xff; + }else{ + bm_st_info.cp2_chn = val[0]; + bm_st_info.cp2_chn |= (val[1] << 31); + } + + //Get the AHB bus monitor arch + if(of_property_read_u32_array(np, "sprd,ap_dap_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP DAP chn!\n"); + bm_st_info.ap_dap = 0xff; + }else + bm_st_info.ap_dap = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_cpu_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP CPU chn!\n"); + bm_st_info.ap_cpu = 0xff; + }else + bm_st_info.ap_cpu = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_dma_r_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP DMA READ chn!\n"); + bm_st_info.ap_dma_r = 0xff; + }else + bm_st_info.ap_dma_r = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_dma_w_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP DMA WRITE chn!\n"); + bm_st_info.ap_dma_w = 0xff; + }else + bm_st_info.ap_dma_w = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_sdio_0_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP SDIO 0 chn!\n"); + bm_st_info.ap_sdio_0 = 0xff; + }else + bm_st_info.ap_sdio_0 = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_sdio_1_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP SDIO 1 chn!\n"); + bm_st_info.ap_sdio_1 = 0xff; + }else + bm_st_info.ap_sdio_1 = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_sdio_2_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP SDIO 2 chn!\n"); + bm_st_info.ap_sdio_2 = 0xff; + }else + bm_st_info.ap_sdio_2 = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_emmc_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP EMMC chn!\n"); + bm_st_info.ap_emmc = 0xff; + }else + bm_st_info.ap_emmc = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_nfc_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP NFC chn!\n"); + bm_st_info.ap_nfc = 0xff; + }else + bm_st_info.ap_nfc = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_usb_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP USB chn!\n"); + bm_st_info.ap_usb = 0xff; + }else + bm_st_info.ap_usb = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_hsic_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP HSIC chn!\n"); + bm_st_info.ap_hsic = 0xff; + }else + bm_st_info.ap_hsic = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_otg_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP OTG chn!\n"); + bm_st_info.ap_otg = 0xff; + }else + bm_st_info.ap_otg = val[1] + (val[0] << 2); + if(of_property_read_u32_array(np, "sprd,ap_nandc_chn", &val[0], 2)){ + BM_WRN("it must be sure that there is not AP NANDC chn!\n"); + bm_st_info.ap_nandc = 0xff; + }else + bm_st_info.ap_nandc = val[1] + (val[0] << 2); + } + sci_bm_init_name_info(); +} + +static int sci_bm_suspend(struct platform_device *pdev, pm_message_t state) +{ + u32 bm_chn, bm_mode; + ulong reg_addr; + + if(true == bm_st_info.bm_dbg_st){ + for (bm_chn = AXI_BM0; bm_chn < BM_SIZE; bm_chn++){ + reg_addr = __sci_get_bm_base(bm_chn); + bm_store_vale[bm_chn].str_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MIN_REG)) + (0x80000000 & bm_store_vale[bm_chn].bm_mask); + bm_store_vale[bm_chn].end_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MAX_REG)) + (0x80000000 & bm_store_vale[bm_chn].bm_mask); + bm_mode = __raw_readl((volatile void *)(reg_addr + AXI_BM_CFG_REG)) & 0x3; + if(0 == bm_mode) + bm_store_vale[bm_chn].mode = RW_MODE; + else if(1 == bm_mode) + bm_store_vale[bm_chn].mode = R_MODE; + else if(3 == bm_mode) + bm_store_vale[bm_chn].mode = W_MODE; + if(bm_chn >= AHB_BM0){ + bm_store_vale[bm_chn].min_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MIN_L_REG)); + bm_store_vale[bm_chn].max_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MAX_L_REG)); + bm_mode = sci_glb_read(REG_AP_AHB_MISC_CFG, 0xFFFFFFFF); + switch(bm_chn){ + case AHB_BM0: + bm_store_vale[bm_chn].chn_sel = (bm_mode >> 4) & 0x3; + break; + case AHB_BM1: + bm_store_vale[bm_chn].chn_sel = (bm_mode >> 8) & 0x3; + break; + case AHB_BM2: + bm_store_vale[bm_chn].chn_sel = (bm_mode >> 10) & 0x3; + break; + default: + break; + } + } + } + } + return 0; +} + +static int sci_bm_resume(struct platform_device *pdev) +{ + u32 bm_chn, ret; + struct sci_bm_cfg bm_cfg; + + __sci_bm_init(); + __sci_bm_int_clr(); + if(true == bm_st_info.bm_dbg_st){ + for (bm_chn = AXI_BM0; bm_chn < BM_SIZE; bm_chn++){ + if((0x00000000 == bm_store_vale[bm_chn].str_addr) && (0x00000000 == bm_store_vale[bm_chn].end_addr)) + continue; + bm_cfg.addr_min = bm_store_vale[bm_chn].str_addr; + bm_cfg.addr_max = bm_store_vale[bm_chn].end_addr; + bm_cfg.bm_mode = bm_store_vale[bm_chn].mode; + if(bm_chn >= AHB_BM0){ + bm_cfg.data_min_l = bm_store_vale[bm_chn].min_data; + bm_cfg.data_min_h = 0; + bm_cfg.data_max_l = bm_store_vale[bm_chn].max_data; + bm_cfg.data_max_h = 0; + } + ret = sci_bm_set_point(bm_chn, bm_store_vale[bm_chn].chn_sel, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return ret; + } + } + return 0; +} + +static int sci_bm_probe(struct platform_device *pdev) +{ + int ret; + u32 bm_index; + struct resource *res = NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "sprd_bm get io resource failed!\n"); + return -ENODEV; + } + axi_bm_base = (unsigned long)devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); + if (!axi_bm_base){ + dev_err(&pdev->dev, "sprd_bm get base address failed!\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "sprd_ahb_bm get io resource failed!\n"); + return -ENODEV; + } + ahb_bm_base = (unsigned long)devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); + if (!ahb_bm_base){ + dev_err(&pdev->dev, "sprd_ahb_bm get base address failed!\n"); + return -ENODEV; + } + + ret = request_irq(IRQ_AXI_BM_PUB_INT, __sci_bm_isr, IRQF_TRIGGER_NONE, pdev->name, pdev); + if (ret) + return ret; + ret = request_irq(IRQ_BM0_INT, __sci_bm_isr, IRQF_SHARED, pdev->name, pdev); + if (ret) + return ret; + ret = request_irq(IRQ_BM1_INT, __sci_bm_isr, IRQF_SHARED, pdev->name, pdev); + if (ret) + return ret; + ret = request_irq(IRQ_BM2_INT, __sci_bm_isr, IRQF_SHARED, pdev->name, pdev); + if (ret) + return ret; + sci_bm_get_hw_info(); + for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, true); + __sci_bm_glb_count_enable(true); + __sci_bm_int_clr(); + __sci_bm_init(); + __sci_bm_glb_ahb_disable(); /*for power consumption*/ + ret = sysfs_create_group(&pdev->dev.kobj, &bm_attr_group); + if (ret) { + BM_ERR("Unable to export sysfs\n"); + return ret; + } + + if(1 == bm_st_info.bm_def_mode){ + sci_bm_get_mem_layout(); + sci_bm_def_val_set_by_dts(); + bm_st_info.bm_dbg_st = true; + bm_st_info.bm_stack_st = true; + bm_st_info.bm_panic_st = true; + //bm_ctn_dbg.bm_continue_dbg= true; + //bm_ctn_dbg.loop_cnt = 20; + bm_st_info.bm_dfs_off_st = true; + BM_ERR("Bus Monitor default config set success!!!\n"); + } + BM_INFO("sci_bm_probe done!\n"); + return SPRD_BM_SUCCESS; +} + +static int sci_bm_remove(struct platform_device *pdev) +{ + u32 bm_index; + + for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, false); + __sci_bm_glb_count_enable(false); + + sysfs_remove_group(&pdev->dev.kobj, &bm_attr_group); + free_irq(IRQ_AXI_BM_PUB_INT, pdev); + free_irq(IRQ_BM0_INT, pdev); + free_irq(IRQ_BM1_INT, pdev); + free_irq(IRQ_BM2_INT, pdev); + return SPRD_BM_SUCCESS; +} + +static int sci_bm_open(struct inode *inode, struct file *filp) +{ + BM_INFO("%s!\n", __func__); + return 0; +} + +static int sci_bm_release(struct inode *inode, struct file *filp) +{ + BM_INFO("%s!\n", __func__); + return 0; +} + +static long sci_bm_ioctl(struct file *filp, unsigned int cmd, unsigned long args) +{ + struct sci_bm_cfg bm_cfg; + struct task_struct *t; + struct timeval tv; + struct rtc_time tm; + volatile struct sci_bm_reg *bm_reg; + unsigned long bm_user; + unsigned char bm_name[18] = {0}; + u32 i, ret, bm_index; + + if(cmd >= (BM_CHANNELS << BM_CHN_NAME_CMD_OFFSET)){ + if(BM_CHANNELS == (cmd >> BM_CHN_NAME_CMD_OFFSET)){ + bm_index = cmd & 0xf; + cmd = cmd >> BM_CHN_NAME_CMD_OFFSET; + } + } + if(_IOC_TYPE(cmd) >= BM_CMD_MAX) + return -EINVAL; + + switch(cmd){ + case BM_STATE: + if(bm_st_info.bm_dbg_st) + bm_user = 1; + if(put_user(bm_user, (unsigned long __user *)args)) + return -EFAULT; + break; + case BM_CHANNELS: + for(i = 0; i < strlen(bm_chn_name[bm_index].chn_name); i++) + bm_name[i] = bm_chn_name[bm_index].chn_name[i]; + bm_name[i] = '\0'; + if(copy_to_user((unsigned char __user *)args, + &bm_name, strlen(bm_name))) + return -EFAULT; + break; + case BM_AXI_DEBUG_SET://set axi debug point + if(copy_from_user(&bm_cfg, (struct sci_bm_cfg __user *)args, + sizeof(struct sci_bm_cfg))) + return -EFAULT; + ret = sci_bm_set_point(bm_cfg.chn, CHN0, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return ret; + bm_st_info.bm_dbg_st = true; + bm_st_info.bm_dfs_off_st = true; + break; + case BM_AHB_DEBUG_SET://set ahb debug point + if(copy_from_user(&bm_cfg, (struct sci_bm_cfg __user *)args, + sizeof(struct sci_bm_cfg))) + return -EFAULT; + ret = sci_bm_set_point(bm_cfg.chn, bm_cfg.data, &bm_cfg, NULL, NULL); + if(SPRD_BM_SUCCESS != ret) + return ret; + bm_st_info.bm_dbg_st = true; + bm_st_info.bm_dfs_off_st = true; + break; + case BM_PERFORM_SET://set performance point + if(per_buf == NULL){ + per_buf = kmalloc(PER_COUNT_BUF_SIZE, GFP_KERNEL); + if (!per_buf) + BM_ERR("kmalloc failed!\n"); + sema_init(&bm_seam, 0); + t = kthread_run(sci_bm_output_log, NULL, "%s", "bm_per_log"); + if (IS_ERR(t)) { + BM_ERR("bm probe: Failed to run thread bm_per_log\n"); + kthread_stop(t); + return -EFAULT; + } + } + for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, true); + sci_bm_set_perform_point(); + msleep(100); + bm_st_info.bm_dbg_st = false; + bm_st_info.bm_dfs_off_st = false; + break; + case BM_PERFORM_UNSET://unset performance point + if(per_buf != NULL) + kfree(per_buf); + for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, false); + __sci_bm_glb_count_enable(false); + break; + case BM_OCCUR://read dbg info + if(copy_to_user((struct bm_debug_info __user *)args, + debug_bm_int_info, sizeof(struct bm_debug_info))) + return -EFAULT; + break; + case BM_CONTINUE_SET://set continue statue + if(copy_from_user(&bm_ctn_dbg, (struct bm_continue_debug __user *)args, + sizeof(struct bm_continue_debug))) + return -EFAULT; + break; + case BM_CONTINUE_UNSET: + bm_ctn_dbg.bm_continue_dbg = false; + break; + case BM_DFS_SET://set DFS statue + case BM_DFS_UNSET: + if(get_user(bm_user,(unsigned long __user *)(&args))) + return -EFAULT; + bm_st_info.bm_dfs_off_st = bm_user; + break; + case BM_PANIC_SET://set DFS statue + case BM_PANIC_UNSET: + if(get_user(bm_user,(unsigned long __user *)(&args))) + return -EFAULT; + bm_st_info.bm_panic_st = bm_user; + break; + case BM_BW_CNT_START: + dmc_mon_cnt_start(); + break; + case BM_BW_CNT_STOP: + dmc_mon_cnt_stop(); + break; + case BM_BW_CNT_RESUME: + break; + case BM_BW_CNT: + bm_user = dmc_mon_cnt_bw(); + if(put_user(bm_user, (unsigned long __user *)args)) + return -EFAULT; + case BM_BW_CNT_CLR: + dmc_mon_cnt_clr(); + break; + case BM_DBG_INT_CLR: + __sci_axi_bm_int_disable(); + break; + case BM_DBG_INT_SET: + __sci_axi_bm_int_enable(); + break; + case BM_DISABLE: + __raw_writel(0x0, (volatile void *)(REG_PUB_APB_BUSMON_CFG)); + break; + case BM_ENABLE: + __raw_writel(BM_CHANNEL_ENABLE_FLAGE, (volatile void *)(REG_PUB_APB_BUSMON_CFG)); + break; + case BM_PROF_SET: + for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, true); + __sci_bm_init(); + __sci_axi_bm_cnt_clr(); + __sci_bm_int_clr(); + __sci_bm_glb_count_enable(false); + __sci_axi_bm_cnt_start(); + __sci_bm_glb_count_enable(true); + break; + case BM_PROF_CLR: + for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) + __sci_bm_glb_reset_and_enable(bm_index, false); + __sci_bm_glb_count_enable(false); + break; + case BM_CHN_CNT: + if(put_user(bm_st_info.axi_bm_cnt, (unsigned long __user *)args)) + return -EFAULT; + break; + case BM_RD_CNT: + copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long)); + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user); + bm_user = (unsigned long)(bm_reg->rtrans_in_win); + if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long))) + return -EFAULT; + break; + case BM_WR_CNT: + copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long)); + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user); + bm_user = (unsigned long)(bm_reg->wtrans_in_win); + if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long))) + return -EFAULT; + break; + case BM_RD_BW: + copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long)); + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user); + bm_user = (unsigned long)(bm_reg->rbw_in_win); + if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long))) + return -EFAULT; + break; + case BM_WR_BW: + copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long)); + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user); + bm_user = (unsigned long)(bm_reg->wbw_in_win); + if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long))) + return -EFAULT; + break; + case BM_RD_LATENCY: + copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long)); + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user); + bm_user = (unsigned long)(bm_reg->rlatency_in_win); + if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long))) + return -EFAULT; + break; + case BM_WR_LATENCY: + copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long)); + bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user); + bm_user = (unsigned long)(bm_reg->wlatency_in_win); + if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long))) + return -EFAULT; + break; + case BM_KERNEL_TIME: + do_gettimeofday(&tv); + //rtc_time_to_tm(tv.tv_sec,&tm); + //bm_user = (tm.tm_hour * 10000) + (tm.tm_min * 100) + tm.tm_sec; + if(put_user(tv.tv_sec, (unsigned long __user *)args)) + return -EFAULT; + break; + default: + return -EINVAL; + } + return 0; +} + +static struct file_operations bm_fops = { + .owner = THIS_MODULE, + .open = sci_bm_open, + .release = sci_bm_release, + .unlocked_ioctl = sci_bm_ioctl, +}; + +static struct miscdevice bm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sprd_bm", + .fops = &bm_fops, +}; + +static const struct of_device_id bm_of_match[] = { + { .compatible = "sprd,sprd_bm", }, + { } +}; + +static struct platform_driver sprd_bm_driver = { + .probe = sci_bm_probe, + .remove = sci_bm_remove, + .suspend = sci_bm_suspend, + .resume = sci_bm_resume, + .driver = { + .owner = THIS_MODULE, + .name = "sprd_bm", + .of_match_table = bm_of_match, + }, +}; + +static int __init sci_bm_init(void) +{ + misc_register(&bm_misc); + return platform_driver_register(&sprd_bm_driver); +} + +static void __exit sci_bm_exit(void) +{ + platform_driver_unregister(&sprd_bm_driver); + misc_deregister(&bm_misc); +} + +module_init(sci_bm_init); +module_exit(sci_bm_exit); + +EXPORT_SYMBOL_GPL(dmc_mon_cnt_bw); +EXPORT_SYMBOL_GPL(dmc_mon_cnt_clr); +EXPORT_SYMBOL_GPL(dmc_mon_cnt_start); +EXPORT_SYMBOL_GPL(dmc_mon_cnt_stop); +EXPORT_SYMBOL_GPL(dmc_mon_resume); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("eric.long<eric.long@spreadtrum.com>"); +MODULE_DESCRIPTION("spreadtrum platform busmonitor driver"); + + |
