1/* 2 * arch/blackfin/kernel/cplbinfo.c - display CPLB status 3 * 4 * Copyright 2004-2008 Analog Devices Inc. 5 * 6 * Licensed under the GPL-2 or later. 7 */ 8 9#include <linux/ctype.h> 10#include <linux/module.h> 11#include <linux/kernel.h> 12#include <linux/init.h> 13#include <linux/proc_fs.h> 14#include <linux/seq_file.h> 15#include <linux/uaccess.h> 16 17#include <asm/cplbinit.h> 18#include <asm/blackfin.h> 19 20static char const page_strtbl[][4] = { 21 "1K", "4K", "1M", "4M", 22#ifdef CONFIG_BF60x 23 "16K", "64K", "16M", "64M", 24#endif 25}; 26#define page(flags) (((flags) & 0x70000) >> 16) 27#define strpage(flags) page_strtbl[page(flags)] 28 29struct cplbinfo_data { 30 loff_t pos; 31 char cplb_type; 32 u32 mem_control; 33 struct cplb_entry *tbl; 34 int switched; 35}; 36 37static void cplbinfo_print_header(struct seq_file *m) 38{ 39 seq_printf(m, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n"); 40} 41 42static int cplbinfo_nomore(struct cplbinfo_data *cdata) 43{ 44 return cdata->pos >= MAX_CPLBS; 45} 46 47static int cplbinfo_show(struct seq_file *m, void *p) 48{ 49 struct cplbinfo_data *cdata; 50 unsigned long data, addr; 51 loff_t pos; 52 53 cdata = p; 54 pos = cdata->pos; 55 addr = cdata->tbl[pos].addr; 56 data = cdata->tbl[pos].data; 57 58 seq_printf(m, 59 "%d\t0x%08lx\t%05lx\t%s\t%c\t%c\t%c\t%c\n", 60 (int)pos, addr, data, strpage(data), 61 (data & CPLB_USER_RD) ? 'Y' : 'N', 62 (data & CPLB_USER_WR) ? 'Y' : 'N', 63 (data & CPLB_SUPV_WR) ? 'Y' : 'N', 64 pos < cdata->switched ? 'N' : 'Y'); 65 66 return 0; 67} 68 69static void cplbinfo_seq_init(struct cplbinfo_data *cdata, unsigned int cpu) 70{ 71 if (cdata->cplb_type == 'I') { 72 cdata->mem_control = bfin_read_IMEM_CONTROL(); 73 cdata->tbl = icplb_tbl[cpu]; 74 cdata->switched = first_switched_icplb; 75 } else { 76 cdata->mem_control = bfin_read_DMEM_CONTROL(); 77 cdata->tbl = dcplb_tbl[cpu]; 78 cdata->switched = first_switched_dcplb; 79 } 80} 81 82static void *cplbinfo_start(struct seq_file *m, loff_t *pos) 83{ 84 struct cplbinfo_data *cdata = m->private; 85 86 if (!*pos) { 87 seq_printf(m, "%cCPLBs are %sabled: 0x%x\n", cdata->cplb_type, 88 (cdata->mem_control & ENDCPLB ? "en" : "dis"), 89 cdata->mem_control); 90 cplbinfo_print_header(m); 91 } else if (cplbinfo_nomore(cdata)) 92 return NULL; 93 94 get_cpu(); 95 return cdata; 96} 97 98static void *cplbinfo_next(struct seq_file *m, void *p, loff_t *pos) 99{ 100 struct cplbinfo_data *cdata = p; 101 cdata->pos = ++(*pos); 102 if (cplbinfo_nomore(cdata)) 103 return NULL; 104 else 105 return cdata; 106} 107 108static void cplbinfo_stop(struct seq_file *m, void *p) 109{ 110 put_cpu(); 111} 112 113static const struct seq_operations cplbinfo_sops = { 114 .start = cplbinfo_start, 115 .next = cplbinfo_next, 116 .stop = cplbinfo_stop, 117 .show = cplbinfo_show, 118}; 119 120#define CPLBINFO_DCPLB_FLAG 0x80000000 121 122static int cplbinfo_open(struct inode *inode, struct file *file) 123{ 124 char cplb_type; 125 unsigned int cpu = (unsigned long)PDE_DATA(file_inode(file)); 126 int ret; 127 struct seq_file *m; 128 struct cplbinfo_data *cdata; 129 130 cplb_type = cpu & CPLBINFO_DCPLB_FLAG ? 'D' : 'I'; 131 cpu &= ~CPLBINFO_DCPLB_FLAG; 132 133 if (!cpu_online(cpu)) 134 return -ENODEV; 135 136 ret = seq_open_private(file, &cplbinfo_sops, sizeof(*cdata)); 137 if (ret) 138 return ret; 139 m = file->private_data; 140 cdata = m->private; 141 142 cdata->pos = 0; 143 cdata->cplb_type = cplb_type; 144 cplbinfo_seq_init(cdata, cpu); 145 146 return 0; 147} 148 149static const struct file_operations cplbinfo_fops = { 150 .open = cplbinfo_open, 151 .read = seq_read, 152 .llseek = seq_lseek, 153 .release = seq_release_private, 154}; 155 156static int __init cplbinfo_init(void) 157{ 158 struct proc_dir_entry *cplb_dir, *cpu_dir; 159 char buf[10]; 160 unsigned int cpu; 161 162 cplb_dir = proc_mkdir("cplbinfo", NULL); 163 if (!cplb_dir) 164 return -ENOMEM; 165 166 for_each_possible_cpu(cpu) { 167 sprintf(buf, "cpu%i", cpu); 168 cpu_dir = proc_mkdir(buf, cplb_dir); 169 if (!cpu_dir) 170 return -ENOMEM; 171 172 proc_create_data("icplb", S_IRUGO, cpu_dir, &cplbinfo_fops, 173 (void *)cpu); 174 proc_create_data("dcplb", S_IRUGO, cpu_dir, &cplbinfo_fops, 175 (void *)(cpu | CPLBINFO_DCPLB_FLAG)); 176 } 177 178 return 0; 179} 180late_initcall(cplbinfo_init); 181