1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public |
3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. |
5 | * |
6 | * Copyright (C) 2013 Imagination Technologies Ltd. |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/debugfs.h> |
11 | #include <linux/seq_file.h> |
12 | #include <asm/cpu.h> |
13 | #include <asm/debug.h> |
14 | #include <asm/mipsregs.h> |
15 | |
16 | static void build_segment_config(char *str, unsigned int cfg) |
17 | { |
18 | unsigned int am; |
19 | static const char * const am_str[] = { |
20 | "UK" , "MK" , "MSK" , "MUSK" , "MUSUK" , "USK" , |
21 | "RSRVD" , "UUSK" }; |
22 | |
23 | /* Segment access mode. */ |
24 | am = (cfg & MIPS_SEGCFG_AM) >> MIPS_SEGCFG_AM_SHIFT; |
25 | str += sprintf(buf: str, fmt: "%-5s" , am_str[am]); |
26 | |
27 | /* |
28 | * Access modes MK, MSK and MUSK are mapped segments. Therefore |
29 | * there is no direct physical address mapping unless it becomes |
30 | * unmapped uncached at error level due to EU. |
31 | */ |
32 | if ((am == 0) || (am > 3) || (cfg & MIPS_SEGCFG_EU)) |
33 | str += sprintf(str, " %03lx" , |
34 | ((cfg & MIPS_SEGCFG_PA) >> MIPS_SEGCFG_PA_SHIFT)); |
35 | else |
36 | str += sprintf(buf: str, fmt: " UND" ); |
37 | |
38 | if ((am == 0) || (am > 3)) |
39 | str += sprintf(str, " %01ld" , |
40 | ((cfg & MIPS_SEGCFG_C) >> MIPS_SEGCFG_C_SHIFT)); |
41 | else |
42 | str += sprintf(buf: str, fmt: " U" ); |
43 | |
44 | /* Exception configuration. */ |
45 | str += sprintf(str, " %01ld\n" , |
46 | ((cfg & MIPS_SEGCFG_EU) >> MIPS_SEGCFG_EU_SHIFT)); |
47 | } |
48 | |
49 | static int segments_show(struct seq_file *m, void *v) |
50 | { |
51 | unsigned int segcfg; |
52 | char str[42]; |
53 | |
54 | seq_puts(m, s: "Segment Virtual Size Access Mode Physical Caching EU\n" ); |
55 | seq_puts(m, s: "------- ------- ---- ----------- -------- ------- --\n" ); |
56 | |
57 | segcfg = read_c0_segctl0(); |
58 | build_segment_config(str, cfg: segcfg); |
59 | seq_printf(m, fmt: " 0 e0000000 512M %s" , str); |
60 | |
61 | segcfg >>= 16; |
62 | build_segment_config(str, cfg: segcfg); |
63 | seq_printf(m, fmt: " 1 c0000000 512M %s" , str); |
64 | |
65 | segcfg = read_c0_segctl1(); |
66 | build_segment_config(str, cfg: segcfg); |
67 | seq_printf(m, fmt: " 2 a0000000 512M %s" , str); |
68 | |
69 | segcfg >>= 16; |
70 | build_segment_config(str, cfg: segcfg); |
71 | seq_printf(m, fmt: " 3 80000000 512M %s" , str); |
72 | |
73 | segcfg = read_c0_segctl2(); |
74 | build_segment_config(str, cfg: segcfg); |
75 | seq_printf(m, fmt: " 4 40000000 1G %s" , str); |
76 | |
77 | segcfg >>= 16; |
78 | build_segment_config(str, cfg: segcfg); |
79 | seq_printf(m, fmt: " 5 00000000 1G %s\n" , str); |
80 | |
81 | return 0; |
82 | } |
83 | DEFINE_SHOW_ATTRIBUTE(segments); |
84 | |
85 | static int __init segments_info(void) |
86 | { |
87 | if (cpu_has_segments) |
88 | debugfs_create_file(name: "segments" , S_IRUGO, parent: mips_debugfs_dir, NULL, |
89 | fops: &segments_fops); |
90 | return 0; |
91 | } |
92 | |
93 | device_initcall(segments_info); |
94 | |