| 1 | --- a/arch/mips/Kconfig |
| 2 | +++ b/arch/mips/Kconfig |
| 3 | @@ -1646,6 +1646,28 @@ |
| 4 | Includes a loader for loading an elf relocatable object |
| 5 | onto another VPE and running it. |
| 6 | |
| 7 | +config IFX_VPE_EXT |
| 8 | + bool "IFX APRP Extensions" |
| 9 | + depends on MIPS_VPE_LOADER |
| 10 | + default y |
| 11 | + help |
| 12 | + IFX included extensions in APRP |
| 13 | + |
| 14 | +config PERFCTRS |
| 15 | + bool "34K Performance counters" |
| 16 | + depends on MIPS_MT && PROC_FS |
| 17 | + default n |
| 18 | + help |
| 19 | + 34K Performance counter through /proc |
| 20 | + |
| 21 | +config MTSCHED |
| 22 | + bool "Support mtsched priority configuration for TCs" |
| 23 | + depends on MIPS_MT && PROC_FS |
| 24 | + default y |
| 25 | + help |
| 26 | + Support for mtsched priority configuration for TCs through |
| 27 | + /proc/mips/mtsched |
| 28 | + |
| 29 | config MIPS_MT_SMTC_IM_BACKSTOP |
| 30 | bool "Use per-TC register bits as backstop for inhibited IM bits" |
| 31 | depends on MIPS_MT_SMTC |
| 32 | --- a/arch/mips/include/asm/mipsmtregs.h |
| 33 | +++ b/arch/mips/include/asm/mipsmtregs.h |
| 34 | @@ -28,14 +28,34 @@ |
| 35 | #define read_c0_vpeconf0() __read_32bit_c0_register($1, 2) |
| 36 | #define write_c0_vpeconf0(val) __write_32bit_c0_register($1, 2, val) |
| 37 | |
| 38 | +#define read_c0_vpeconf1() __read_32bit_c0_register($1, 3) |
| 39 | +#define write_c0_vpeconf1(val) __write_32bit_c0_register($1, 3, val) |
| 40 | + |
| 41 | +#define read_c0_vpeschedule() __read_32bit_c0_register($1, 5) |
| 42 | +#define write_c0_vpeschedule(val) __write_32bit_c0_register($1, 5, val) |
| 43 | + |
| 44 | +#define read_c0_vpeschefback() __read_32bit_c0_register($1, 6) |
| 45 | +#define write_c0_vpeschefback(val) __write_32bit_c0_register($1, 6, val) |
| 46 | + |
| 47 | +#define read_c0_vpeopt() __read_32bit_c0_register($1, 7) |
| 48 | +#define write_c0_vpeopt(val) __write_32bit_c0_register($1, 7, val) |
| 49 | + |
| 50 | #define read_c0_tcstatus() __read_32bit_c0_register($2, 1) |
| 51 | #define write_c0_tcstatus(val) __write_32bit_c0_register($2, 1, val) |
| 52 | |
| 53 | #define read_c0_tcbind() __read_32bit_c0_register($2, 2) |
| 54 | +#define write_c0_tcbind(val) __write_32bit_c0_register($2, 2, val) |
| 55 | |
| 56 | #define read_c0_tccontext() __read_32bit_c0_register($2, 5) |
| 57 | #define write_c0_tccontext(val) __write_32bit_c0_register($2, 5, val) |
| 58 | |
| 59 | +#define read_c0_tcschedule() __read_32bit_c0_register($2, 6) |
| 60 | +#define write_c0_tcschedule(val) __write_32bit_c0_register($2, 6, val) |
| 61 | + |
| 62 | +#define read_c0_tcschefback() __read_32bit_c0_register($2, 7) |
| 63 | +#define write_c0_tcschefback(val) __write_32bit_c0_register($2, 7, val) |
| 64 | + |
| 65 | + |
| 66 | #else /* Assembly */ |
| 67 | /* |
| 68 | * Macros for use in assembly language code |
| 69 | @@ -74,6 +94,8 @@ |
| 70 | #define MVPCONTROL_STLB_SHIFT 2 |
| 71 | #define MVPCONTROL_STLB (_ULCAST_(1) << MVPCONTROL_STLB_SHIFT) |
| 72 | |
| 73 | +#define MVPCONTROL_CPA_SHIFT 3 |
| 74 | +#define MVPCONTROL_CPA (_ULCAST_(1) << MVPCONTROL_CPA_SHIFT) |
| 75 | |
| 76 | /* MVPConf0 fields */ |
| 77 | #define MVPCONF0_PTC_SHIFT 0 |
| 78 | @@ -84,6 +106,8 @@ |
| 79 | #define MVPCONF0_TCA ( _ULCAST_(1) << MVPCONF0_TCA_SHIFT) |
| 80 | #define MVPCONF0_PTLBE_SHIFT 16 |
| 81 | #define MVPCONF0_PTLBE (_ULCAST_(0x3ff) << MVPCONF0_PTLBE_SHIFT) |
| 82 | +#define MVPCONF0_PCP_SHIFT 27 |
| 83 | +#define MVPCONF0_PCP (_ULCAST_(1) << MVPCONF0_PCP_SHIFT) |
| 84 | #define MVPCONF0_TLBS_SHIFT 29 |
| 85 | #define MVPCONF0_TLBS (_ULCAST_(1) << MVPCONF0_TLBS_SHIFT) |
| 86 | #define MVPCONF0_M_SHIFT 31 |
| 87 | @@ -121,9 +145,25 @@ |
| 88 | #define VPECONF0_VPA (_ULCAST_(1) << VPECONF0_VPA_SHIFT) |
| 89 | #define VPECONF0_MVP_SHIFT 1 |
| 90 | #define VPECONF0_MVP (_ULCAST_(1) << VPECONF0_MVP_SHIFT) |
| 91 | +#define VPECONF0_ICS_SHIFT 16 |
| 92 | +#define VPECONF0_ICS (_ULCAST_(1) << VPECONF0_ICS_SHIFT) |
| 93 | +#define VPECONF0_DCS_SHIFT 17 |
| 94 | +#define VPECONF0_DCS (_ULCAST_(1) << VPECONF0_DCS_SHIFT) |
| 95 | #define VPECONF0_XTC_SHIFT 21 |
| 96 | #define VPECONF0_XTC (_ULCAST_(0xff) << VPECONF0_XTC_SHIFT) |
| 97 | |
| 98 | +/* VPEOpt fields */ |
| 99 | +#define VPEOPT_DWX_SHIFT 0 |
| 100 | +#define VPEOPT_IWX_SHIFT 8 |
| 101 | +#define VPEOPT_IWX0 ( _ULCAST_(0x1) << VPEOPT_IWX_SHIFT) |
| 102 | +#define VPEOPT_IWX1 ( _ULCAST_(0x2) << VPEOPT_IWX_SHIFT) |
| 103 | +#define VPEOPT_IWX2 ( _ULCAST_(0x4) << VPEOPT_IWX_SHIFT) |
| 104 | +#define VPEOPT_IWX3 ( _ULCAST_(0x8) << VPEOPT_IWX_SHIFT) |
| 105 | +#define VPEOPT_DWX0 ( _ULCAST_(0x1) << VPEOPT_DWX_SHIFT) |
| 106 | +#define VPEOPT_DWX1 ( _ULCAST_(0x2) << VPEOPT_DWX_SHIFT) |
| 107 | +#define VPEOPT_DWX2 ( _ULCAST_(0x4) << VPEOPT_DWX_SHIFT) |
| 108 | +#define VPEOPT_DWX3 ( _ULCAST_(0x8) << VPEOPT_DWX_SHIFT) |
| 109 | + |
| 110 | /* TCStatus fields (per TC) */ |
| 111 | #define TCSTATUS_TASID (_ULCAST_(0xff)) |
| 112 | #define TCSTATUS_IXMT_SHIFT 10 |
| 113 | @@ -350,6 +390,14 @@ |
| 114 | #define write_vpe_c0_vpecontrol(val) mttc0(1, 1, val) |
| 115 | #define read_vpe_c0_vpeconf0() mftc0(1, 2) |
| 116 | #define write_vpe_c0_vpeconf0(val) mttc0(1, 2, val) |
| 117 | +#define read_vpe_c0_vpeschedule() mftc0(1, 5) |
| 118 | +#define write_vpe_c0_vpeschedule(val) mttc0(1, 5, val) |
| 119 | +#define read_vpe_c0_vpeschefback() mftc0(1, 6) |
| 120 | +#define write_vpe_c0_vpeschefback(val) mttc0(1, 6, val) |
| 121 | +#define read_vpe_c0_vpeopt() mftc0(1, 7) |
| 122 | +#define write_vpe_c0_vpeopt(val) mttc0(1, 7, val) |
| 123 | +#define read_vpe_c0_wired() mftc0(6, 0) |
| 124 | +#define write_vpe_c0_wired(val) mttc0(6, 0, val) |
| 125 | #define read_vpe_c0_count() mftc0(9, 0) |
| 126 | #define write_vpe_c0_count(val) mttc0(9, 0, val) |
| 127 | #define read_vpe_c0_status() mftc0(12, 0) |
| 128 | @@ -381,6 +429,12 @@ |
| 129 | #define write_tc_c0_tchalt(val) mttc0(2, 4, val) |
| 130 | #define read_tc_c0_tccontext() mftc0(2, 5) |
| 131 | #define write_tc_c0_tccontext(val) mttc0(2, 5, val) |
| 132 | +#define read_tc_c0_tcschedule() mftc0(2, 6) |
| 133 | +#define write_tc_c0_tcschedule(val) mttc0(2, 6, val) |
| 134 | +#define read_tc_c0_tcschefback() mftc0(2, 7) |
| 135 | +#define write_tc_c0_tcschefback(val) mttc0(2, 7, val) |
| 136 | +#define read_tc_c0_entryhi() mftc0(10, 0) |
| 137 | +#define write_tc_c0_entryhi(val) mttc0(10, 0, val) |
| 138 | |
| 139 | /* GPR */ |
| 140 | #define read_tc_gpr_sp() mftgpr(29) |
| 141 | --- a/arch/mips/kernel/Makefile |
| 142 | +++ b/arch/mips/kernel/Makefile |
| 143 | @@ -78,7 +78,8 @@ |
| 144 | |
| 145 | obj-$(CONFIG_KGDB) += kgdb.o |
| 146 | obj-$(CONFIG_PROC_FS) += proc.o |
| 147 | - |
| 148 | +obj-$(CONFIG_MTSCHED) += mtsched_proc.o |
| 149 | +obj-$(CONFIG_PERFCTRS) += perf_proc.o |
| 150 | obj-$(CONFIG_64BIT) += cpu-bugs64.o |
| 151 | |
| 152 | obj-$(CONFIG_I8253) += i8253.o |
| 153 | --- a/arch/mips/kernel/mips-mt.c |
| 154 | +++ b/arch/mips/kernel/mips-mt.c |
| 155 | @@ -21,26 +21,96 @@ |
| 156 | #include <asm/cacheflush.h> |
| 157 | |
| 158 | int vpelimit; |
| 159 | - |
| 160 | static int __init maxvpes(char *str) |
| 161 | { |
| 162 | get_option(&str, &vpelimit); |
| 163 | - |
| 164 | return 1; |
| 165 | } |
| 166 | - |
| 167 | __setup("maxvpes=", maxvpes); |
| 168 | |
| 169 | int tclimit; |
| 170 | - |
| 171 | static int __init maxtcs(char *str) |
| 172 | { |
| 173 | get_option(&str, &tclimit); |
| 174 | + return 1; |
| 175 | +} |
| 176 | +__setup("maxtcs=", maxtcs); |
| 177 | |
| 178 | +#ifdef CONFIG_IFX_VPE_EXT |
| 179 | +int stlb; |
| 180 | +static int __init istlbshared(char *str) |
| 181 | +{ |
| 182 | + get_option(&str, &stlb); |
| 183 | return 1; |
| 184 | } |
| 185 | +__setup("vpe_tlb_shared=", istlbshared); |
| 186 | |
| 187 | -__setup("maxtcs=", maxtcs); |
| 188 | +int vpe0_wired; |
| 189 | +static int __init vpe0wired(char *str) |
| 190 | +{ |
| 191 | + get_option(&str, &vpe0_wired); |
| 192 | + return 1; |
| 193 | +} |
| 194 | +__setup("vpe0_wired_tlb_entries=", vpe0wired); |
| 195 | + |
| 196 | +int vpe1_wired; |
| 197 | +static int __init vpe1wired(char *str) |
| 198 | +{ |
| 199 | + get_option(&str, &vpe1_wired); |
| 200 | + return 1; |
| 201 | +} |
| 202 | +__setup("vpe1_wired_tlb_entries=", vpe1wired); |
| 203 | + |
| 204 | +#ifdef CONFIG_MIPS_MT_SMTC |
| 205 | +extern int nostlb; |
| 206 | +#endif |
| 207 | +void configure_tlb(void) |
| 208 | +{ |
| 209 | + int vpeflags, tcflags, tlbsiz; |
| 210 | + unsigned int config1val; |
| 211 | + vpeflags = dvpe(); |
| 212 | + tcflags = dmt(); |
| 213 | + write_c0_vpeconf0((read_c0_vpeconf0() | VPECONF0_MVP)); |
| 214 | + write_c0_mvpcontrol((read_c0_mvpcontrol() | MVPCONTROL_VPC)); |
| 215 | + mips_ihb(); |
| 216 | + //printk("stlb = %d, vpe0_wired = %d vpe1_wired=%d\n", stlb,vpe0_wired, vpe1_wired); |
| 217 | + if (stlb) { |
| 218 | + if (!(read_c0_mvpconf0() & MVPCONF0_TLBS)) { |
| 219 | + emt(tcflags); |
| 220 | + evpe(vpeflags); |
| 221 | + return; |
| 222 | + } |
| 223 | + |
| 224 | + write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_STLB); |
| 225 | + write_c0_wired(vpe0_wired + vpe1_wired); |
| 226 | + if (((read_vpe_c0_config() & MIPS_CONF_MT) >> 7) == 1) { |
| 227 | + config1val = read_vpe_c0_config1(); |
| 228 | + tlbsiz = (((config1val >> 25) & 0x3f) + 1); |
| 229 | + if (tlbsiz > 64) |
| 230 | + tlbsiz = 64; |
| 231 | + cpu_data[0].tlbsize = tlbsiz; |
| 232 | + current_cpu_data.tlbsize = tlbsiz; |
| 233 | + } |
| 234 | + |
| 235 | + } |
| 236 | + else { |
| 237 | + write_c0_mvpcontrol(read_c0_mvpcontrol() & ~MVPCONTROL_STLB); |
| 238 | + write_c0_wired(vpe0_wired); |
| 239 | + } |
| 240 | + |
| 241 | + ehb(); |
| 242 | + write_c0_mvpcontrol((read_c0_mvpcontrol() & ~MVPCONTROL_VPC)); |
| 243 | + ehb(); |
| 244 | + local_flush_tlb_all(); |
| 245 | + |
| 246 | + printk("Wired TLB entries for Linux read_c0_wired() = %d\n", read_c0_wired()); |
| 247 | +#ifdef CONFIG_MIPS_MT_SMTC |
| 248 | + nostlb = !stlb; |
| 249 | +#endif |
| 250 | + emt(tcflags); |
| 251 | + evpe(vpeflags); |
| 252 | +} |
| 253 | +#endif |
| 254 | |
| 255 | /* |
| 256 | * Dump new MIPS MT state for the core. Does not leave TCs halted. |
| 257 | @@ -78,18 +148,18 @@ |
| 258 | if ((read_tc_c0_tcbind() & TCBIND_CURVPE) == i) { |
| 259 | printk(" VPE %d\n", i); |
| 260 | printk(" VPEControl : %08lx\n", |
| 261 | - read_vpe_c0_vpecontrol()); |
| 262 | + read_vpe_c0_vpecontrol()); |
| 263 | printk(" VPEConf0 : %08lx\n", |
| 264 | - read_vpe_c0_vpeconf0()); |
| 265 | + read_vpe_c0_vpeconf0()); |
| 266 | printk(" VPE%d.Status : %08lx\n", |
| 267 | - i, read_vpe_c0_status()); |
| 268 | + i, read_vpe_c0_status()); |
| 269 | printk(" VPE%d.EPC : %08lx %pS\n", |
| 270 | - i, read_vpe_c0_epc(), |
| 271 | - (void *) read_vpe_c0_epc()); |
| 272 | + i, read_vpe_c0_epc(), |
| 273 | + (void *) read_vpe_c0_epc()); |
| 274 | printk(" VPE%d.Cause : %08lx\n", |
| 275 | - i, read_vpe_c0_cause()); |
| 276 | + i, read_vpe_c0_cause()); |
| 277 | printk(" VPE%d.Config7 : %08lx\n", |
| 278 | - i, read_vpe_c0_config7()); |
| 279 | + i, read_vpe_c0_config7()); |
| 280 | break; /* Next VPE */ |
| 281 | } |
| 282 | } |
| 283 | @@ -287,6 +357,9 @@ |
| 284 | printk("Mapped %ld ITC cells starting at 0x%08x\n", |
| 285 | ((itcblkgrn & 0x7fe00000) >> 20), itc_base); |
| 286 | } |
| 287 | +#ifdef CONFIG_IFX_VPE_EXT |
| 288 | + configure_tlb(); |
| 289 | +#endif |
| 290 | } |
| 291 | |
| 292 | /* |
| 293 | --- a/arch/mips/kernel/proc.c |
| 294 | +++ b/arch/mips/kernel/proc.c |
| 295 | @@ -7,6 +7,7 @@ |
| 296 | #include <linux/kernel.h> |
| 297 | #include <linux/sched.h> |
| 298 | #include <linux/seq_file.h> |
| 299 | +#include <linux/proc_fs.h> |
| 300 | #include <asm/bootinfo.h> |
| 301 | #include <asm/cpu.h> |
| 302 | #include <asm/cpu-features.h> |
| 303 | @@ -108,3 +109,19 @@ |
| 304 | .stop = c_stop, |
| 305 | .show = show_cpuinfo, |
| 306 | }; |
| 307 | + |
| 308 | +/* |
| 309 | + * Support for MIPS/local /proc hooks in /proc/mips/ |
| 310 | + */ |
| 311 | + |
| 312 | +static struct proc_dir_entry *mips_proc = NULL; |
| 313 | + |
| 314 | +struct proc_dir_entry *get_mips_proc_dir(void) |
| 315 | +{ |
| 316 | + /* |
| 317 | + * This ought not to be preemptable. |
| 318 | + */ |
| 319 | + if(mips_proc == NULL) |
| 320 | + mips_proc = proc_mkdir("mips", NULL); |
| 321 | + return(mips_proc); |
| 322 | +} |
| 323 | --- a/arch/mips/kernel/smtc.c |
| 324 | +++ b/arch/mips/kernel/smtc.c |
| 325 | @@ -1328,6 +1328,13 @@ |
| 326 | asid = asid_cache(cpu); |
| 327 | |
| 328 | do { |
| 329 | +#ifdef CONFIG_IFX_VPE_EXT |
| 330 | + /* If TLB is shared between AP and RP (AP is running SMTC), |
| 331 | + leave out max ASID i.e., ASID_MASK for RP |
| 332 | + */ |
| 333 | + if (!nostlb && ((asid & ASID_MASK) == (ASID_MASK - 1))) |
| 334 | + asid++; |
| 335 | +#endif |
| 336 | if (!((asid += ASID_INC) & ASID_MASK) ) { |
| 337 | if (cpu_has_vtag_icache) |
| 338 | flush_icache_all(); |
| 339 | --- a/arch/mips/kernel/vpe.c |
| 340 | +++ b/arch/mips/kernel/vpe.c |
| 341 | @@ -77,6 +77,58 @@ |
| 342 | static int kspd_events_reqd; |
| 343 | #endif |
| 344 | |
| 345 | +#ifdef CONFIG_IFX_VPE_EXT |
| 346 | +static int is_sdepgm; |
| 347 | +extern int stlb; |
| 348 | +extern int vpe0_wired; |
| 349 | +extern int vpe1_wired; |
| 350 | +unsigned int vpe1_load_addr; |
| 351 | + |
| 352 | +static int __init load_address(char *str) |
| 353 | +{ |
| 354 | + get_option(&str, &vpe1_load_addr); |
| 355 | + return 1; |
| 356 | +} |
| 357 | +__setup("vpe1_load_addr=", load_address); |
| 358 | + |
| 359 | +#include <asm/mipsmtregs.h> |
| 360 | +#define write_vpe_c0_wired(val) mttc0(6, 0, val) |
| 361 | + |
| 362 | +#ifndef COMMAND_LINE_SIZE |
| 363 | +# define COMMAND_LINE_SIZE 512 |
| 364 | +#endif |
| 365 | + |
| 366 | +char command_line[COMMAND_LINE_SIZE * 2]; |
| 367 | + |
| 368 | +static unsigned int vpe1_mem; |
| 369 | +static int __init vpe1mem(char *str) |
| 370 | +{ |
| 371 | + vpe1_mem = memparse(str, &str); |
| 372 | + return 1; |
| 373 | +} |
| 374 | +__setup("vpe1_mem=", vpe1mem); |
| 375 | + |
| 376 | +uint32_t vpe1_wdog_ctr; |
| 377 | +static int __init wdog_ctr(char *str) |
| 378 | +{ |
| 379 | + get_option(&str, &vpe1_wdog_ctr); |
| 380 | + return 1; |
| 381 | +} |
| 382 | + |
| 383 | +__setup("vpe1_wdog_ctr_addr=", wdog_ctr); |
| 384 | +EXPORT_SYMBOL(vpe1_wdog_ctr); |
| 385 | + |
| 386 | +uint32_t vpe1_wdog_timeout; |
| 387 | +static int __init wdog_timeout(char *str) |
| 388 | +{ |
| 389 | + get_option(&str, &vpe1_wdog_timeout); |
| 390 | + return 1; |
| 391 | +} |
| 392 | + |
| 393 | +__setup("vpe1_wdog_timeout=", wdog_timeout); |
| 394 | +EXPORT_SYMBOL(vpe1_wdog_timeout); |
| 395 | + |
| 396 | +#endif |
| 397 | /* grab the likely amount of memory we will need. */ |
| 398 | #ifdef CONFIG_MIPS_VPE_LOADER_TOM |
| 399 | #define P_SIZE (2 * 1024 * 1024) |
| 400 | @@ -269,6 +321,13 @@ |
| 401 | void *addr; |
| 402 | |
| 403 | #ifdef CONFIG_MIPS_VPE_LOADER_TOM |
| 404 | +#ifdef CONFIG_IFX_VPE_EXT |
| 405 | + if (vpe1_load_addr) { |
| 406 | + memset((void *)vpe1_load_addr, 0, len); |
| 407 | + return (void *)vpe1_load_addr; |
| 408 | + } |
| 409 | +#endif |
| 410 | + |
| 411 | /* |
| 412 | * This means you must tell Linux to use less memory than you |
| 413 | * physically have, for example by passing a mem= boot argument. |
| 414 | @@ -747,6 +806,12 @@ |
| 415 | } |
| 416 | |
| 417 | /* Write the address we want it to start running from in the TCPC register. */ |
| 418 | +#if defined(CONFIG_IFX_VPE_EXT) && 0 |
| 419 | + if (stlb) |
| 420 | + write_vpe_c0_wired(vpe0_wired + vpe1_wired); |
| 421 | + else |
| 422 | + write_vpe_c0_wired(vpe1_wired); |
| 423 | +#endif |
| 424 | write_tc_c0_tcrestart((unsigned long)v->__start); |
| 425 | write_tc_c0_tccontext((unsigned long)0); |
| 426 | |
| 427 | @@ -760,6 +825,20 @@ |
| 428 | |
| 429 | write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H); |
| 430 | |
| 431 | +#if defined(CONFIG_IFX_VPE_EXT) && 0 |
| 432 | + /* |
| 433 | + * $a2 & $a3 are used to pass command line parameters to VPE1. $a2 |
| 434 | + * points to the start of the command line string and $a3 points to |
| 435 | + * the end of the string. This convention is identical to the Linux |
| 436 | + * kernel boot parameter passing mechanism. Please note that $a3 is |
| 437 | + * used to pass physical memory size or 0 in SDE tool kit. So, if you |
| 438 | + * are passing comand line parameters through $a2 & $a3 SDE programs |
| 439 | + * don't work as desired. |
| 440 | + */ |
| 441 | + mttgpr(6, command_line); |
| 442 | + mttgpr(7, (command_line + strlen(command_line))); |
| 443 | + if (is_sdepgm) |
| 444 | +#endif |
| 445 | /* |
| 446 | * The sde-kit passes 'memsize' to __start in $a3, so set something |
| 447 | * here... Or set $a3 to zero and define DFLT_STACK_SIZE and |
| 448 | @@ -834,6 +913,9 @@ |
| 449 | if ( (v->__start == 0) || (v->shared_ptr == NULL)) |
| 450 | return -1; |
| 451 | |
| 452 | +#ifdef CONFIG_IFX_VPE_EXT |
| 453 | + is_sdepgm = 1; |
| 454 | +#endif |
| 455 | return 0; |
| 456 | } |
| 457 | |
| 458 | @@ -995,6 +1077,15 @@ |
| 459 | (unsigned long)v->load_addr + v->len); |
| 460 | |
| 461 | if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { |
| 462 | +#ifdef CONFIG_IFX_VPE_EXT |
| 463 | + if (vpe1_load_addr) { |
| 464 | + /* Conversion to KSEG1 is required ??? */ |
| 465 | + v->__start = KSEG1ADDR(vpe1_load_addr); |
| 466 | + is_sdepgm = 0; |
| 467 | + return 0; |
| 468 | + } |
| 469 | +#endif |
| 470 | + |
| 471 | if (v->__start == 0) { |
| 472 | printk(KERN_WARNING "VPE loader: program does not contain " |
| 473 | "a __start symbol\n"); |
| 474 | @@ -1065,6 +1156,9 @@ |
| 475 | struct vpe_notifications *not; |
| 476 | struct vpe *v; |
| 477 | int ret; |
| 478 | +#ifdef CONFIG_IFX_VPE_EXT |
| 479 | + int progsize; |
| 480 | +#endif |
| 481 | |
| 482 | if (minor != iminor(inode)) { |
| 483 | /* assume only 1 device at the moment. */ |
| 484 | @@ -1091,14 +1185,22 @@ |
| 485 | cleanup_tc(get_tc(tclimit)); |
| 486 | } |
| 487 | |
| 488 | +#ifdef CONFIG_IFX_VPE_EXT |
| 489 | + progsize = (vpe1_mem != 0) ? vpe1_mem : P_SIZE; |
| 490 | + //printk("progsize = %x\n", progsize); |
| 491 | + v->pbuffer = vmalloc(progsize); |
| 492 | + v->plen = progsize; |
| 493 | +#else |
| 494 | /* this of-course trashes what was there before... */ |
| 495 | v->pbuffer = vmalloc(P_SIZE); |
| 496 | v->plen = P_SIZE; |
| 497 | +#endif |
| 498 | v->load_addr = NULL; |
| 499 | v->len = 0; |
| 500 | - |
| 501 | +#if 0 |
| 502 | v->uid = filp->f_cred->fsuid; |
| 503 | v->gid = filp->f_cred->fsgid; |
| 504 | +#endif |
| 505 | |
| 506 | #ifdef CONFIG_MIPS_APSP_KSPD |
| 507 | /* get kspd to tell us when a syscall_exit happens */ |
| 508 | @@ -1351,6 +1453,133 @@ |
| 509 | cleanup_tc(get_tc(sp_id)); |
| 510 | } |
| 511 | #endif |
| 512 | +#ifdef CONFIG_IFX_VPE_EXT |
| 513 | +int32_t vpe1_sw_start(void* sw_start_addr, uint32_t tcmask, uint32_t flags) |
| 514 | +{ |
| 515 | + enum vpe_state state; |
| 516 | + struct vpe *v = get_vpe(tclimit); |
| 517 | + struct vpe_notifications *not; |
| 518 | + |
| 519 | + if (tcmask || flags) { |
| 520 | + printk(KERN_WARNING "Currently tcmask and flags should be 0.\ |
| 521 | + other values not supported\n"); |
| 522 | + return -1; |
| 523 | + } |
| 524 | + |
| 525 | + state = xchg(&v->state, VPE_STATE_INUSE); |
| 526 | + if (state != VPE_STATE_UNUSED) { |
| 527 | + vpe_stop(v); |
| 528 | + |
| 529 | + list_for_each_entry(not, &v->notify, list) { |
| 530 | + not->stop(tclimit); |
| 531 | + } |
| 532 | + } |
| 533 | + |
| 534 | + v->__start = (unsigned long)sw_start_addr; |
| 535 | + is_sdepgm = 0; |
| 536 | + |
| 537 | + if (!vpe_run(v)) { |
| 538 | + printk(KERN_DEBUG "VPE loader: VPE1 running successfully\n"); |
| 539 | + return 0; |
| 540 | + } |
| 541 | + return -1; |
| 542 | +} |
| 543 | + |
| 544 | +EXPORT_SYMBOL(vpe1_sw_start); |
| 545 | + |
| 546 | +int32_t vpe1_sw_stop(uint32_t flags) |
| 547 | +{ |
| 548 | + struct vpe *v = get_vpe(tclimit); |
| 549 | + |
| 550 | + if (!vpe_free(v)) { |
| 551 | + printk(KERN_DEBUG "RP Stopped\n"); |
| 552 | + return 0; |
| 553 | + } |
| 554 | + else |
| 555 | + return -1; |
| 556 | +} |
| 557 | + |
| 558 | +EXPORT_SYMBOL(vpe1_sw_stop); |
| 559 | + |
| 560 | +uint32_t vpe1_get_load_addr (uint32_t flags) |
| 561 | +{ |
| 562 | + return vpe1_load_addr; |
| 563 | +} |
| 564 | + |
| 565 | +EXPORT_SYMBOL(vpe1_get_load_addr); |
| 566 | + |
| 567 | +uint32_t vpe1_get_max_mem (uint32_t flags) |
| 568 | +{ |
| 569 | + if (!vpe1_mem) |
| 570 | + return P_SIZE; |
| 571 | + else |
| 572 | + return vpe1_mem; |
| 573 | +} |
| 574 | + |
| 575 | +EXPORT_SYMBOL(vpe1_get_max_mem); |
| 576 | + |
| 577 | +void* vpe1_get_cmdline_argument(void) |
| 578 | +{ |
| 579 | + return saved_command_line; |
| 580 | +} |
| 581 | + |
| 582 | +EXPORT_SYMBOL(vpe1_get_cmdline_argument); |
| 583 | + |
| 584 | +int32_t vpe1_set_boot_param(char *field, char *value, char flags) |
| 585 | +{ |
| 586 | + char *ptr, string[64]; |
| 587 | + int start_off, end_off; |
| 588 | + if (!field) |
| 589 | + return -1; |
| 590 | + strcpy(string, field); |
| 591 | + if (value) { |
| 592 | + strcat(string, "="); |
| 593 | + strcat(string, value); |
| 594 | + strcat(command_line, " "); |
| 595 | + strcat(command_line, string); |
| 596 | + } |
| 597 | + else { |
| 598 | + ptr = strstr(command_line, string); |
| 599 | + if (ptr) { |
| 600 | + start_off = ptr - command_line; |
| 601 | + ptr += strlen(string); |
| 602 | + while ((*ptr != ' ') && (*ptr != '\0')) |
| 603 | + ptr++; |
| 604 | + end_off = ptr - command_line; |
| 605 | + command_line[start_off] = '\0'; |
| 606 | + strcat (command_line, command_line+end_off); |
| 607 | + } |
| 608 | + } |
| 609 | + return 0; |
| 610 | +} |
| 611 | + |
| 612 | +EXPORT_SYMBOL(vpe1_set_boot_param); |
| 613 | + |
| 614 | +int32_t vpe1_get_boot_param(char *field, char **value, char flags) |
| 615 | +{ |
| 616 | + char *ptr, string[64]; |
| 617 | + int i = 0; |
| 618 | + if (!field) |
| 619 | + return -1; |
| 620 | + if ((ptr = strstr(command_line, field))) { |
| 621 | + ptr += strlen(field) + 1; /* including = */ |
| 622 | + while ((*ptr != ' ') && (*ptr != '\0')) |
| 623 | + string[i++] = *ptr++; |
| 624 | + string[i] = '\0'; |
| 625 | + *value = kmalloc((strlen(string) + 1), GFP_KERNEL); |
| 626 | + if (*value != NULL) |
| 627 | + strcpy(*value, string); |
| 628 | + } |
| 629 | + else |
| 630 | + *value = NULL; |
| 631 | + |
| 632 | + return 0; |
| 633 | +} |
| 634 | + |
| 635 | +EXPORT_SYMBOL(vpe1_get_boot_param); |
| 636 | + |
| 637 | +extern void configure_tlb(void); |
| 638 | +#endif |
| 639 | |
| 640 | static ssize_t store_kill(struct device *dev, struct device_attribute *attr, |
| 641 | const char *buf, size_t len) |
| 642 | @@ -1432,6 +1661,18 @@ |
| 643 | printk("VPE loader: not a MIPS MT capable processor\n"); |
| 644 | return -ENODEV; |
| 645 | } |
| 646 | +#ifdef CONFIG_IFX_VPE_EXT |
| 647 | +#ifndef CONFIG_MIPS_MT_SMTC |
| 648 | + configure_tlb(); |
| 649 | +#endif |
| 650 | +#endif |
| 651 | + |
| 652 | +#ifndef CONFIG_MIPS_MT_SMTC |
| 653 | + if (!vpelimit) |
| 654 | + vpelimit = 1; |
| 655 | + if (!tclimit) |
| 656 | + tclimit = 1; |
| 657 | +#endif |
| 658 | |
| 659 | if (vpelimit == 0) { |
| 660 | printk(KERN_WARNING "No VPEs reserved for AP/SP, not " |
| 661 | @@ -1476,10 +1717,12 @@ |
| 662 | mtflags = dmt(); |
| 663 | vpflags = dvpe(); |
| 664 | |
| 665 | + back_to_back_c0_hazard(); |
| 666 | + |
| 667 | /* Put MVPE's into 'configuration state' */ |
| 668 | set_c0_mvpcontrol(MVPCONTROL_VPC); |
| 669 | |
| 670 | - /* dump_mtregs(); */ |
| 671 | + dump_mtregs(); |
| 672 | |
| 673 | val = read_c0_mvpconf0(); |
| 674 | hw_tcs = (val & MVPCONF0_PTC) + 1; |
| 675 | @@ -1491,6 +1734,7 @@ |
| 676 | * reschedule send IPIs or similar we might hang. |
| 677 | */ |
| 678 | clear_c0_mvpcontrol(MVPCONTROL_VPC); |
| 679 | + back_to_back_c0_hazard(); |
| 680 | evpe(vpflags); |
| 681 | emt(mtflags); |
| 682 | local_irq_restore(flags); |
| 683 | @@ -1516,6 +1760,7 @@ |
| 684 | } |
| 685 | |
| 686 | v->ntcs = hw_tcs - tclimit; |
| 687 | + write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1); |
| 688 | |
| 689 | /* add the tc to the list of this vpe's tc's. */ |
| 690 | list_add(&t->tc, &v->tc); |
| 691 | @@ -1584,6 +1829,7 @@ |
| 692 | out_reenable: |
| 693 | /* release config state */ |
| 694 | clear_c0_mvpcontrol(MVPCONTROL_VPC); |
| 695 | + back_to_back_c0_hazard(); |
| 696 | |
| 697 | evpe(vpflags); |
| 698 | emt(mtflags); |
| 699 | --- /dev/null |
| 700 | +++ b/arch/mips/kernel/mtsched_proc.c |
| 701 | @@ -0,0 +1,279 @@ |
| 702 | +/* |
| 703 | + * /proc hooks for MIPS MT scheduling policy management for 34K cores |
| 704 | + * |
| 705 | + * This program is free software; you can distribute it and/or modify it |
| 706 | + * under the terms of the GNU General Public License (Version 2) as |
| 707 | + * published by the Free Software Foundation. |
| 708 | + * |
| 709 | + * This program is distributed in the hope it will be useful, but WITHOUT |
| 710 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 711 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 712 | + * for more details. |
| 713 | + * |
| 714 | + * You should have received a copy of the GNU General Public License along |
| 715 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 716 | + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. |
| 717 | + * |
| 718 | + * Copyright (C) 2006 Mips Technologies, Inc |
| 719 | + */ |
| 720 | + |
| 721 | +#include <linux/kernel.h> |
| 722 | + |
| 723 | +#include <asm/cpu.h> |
| 724 | +#include <asm/processor.h> |
| 725 | +#include <asm/system.h> |
| 726 | +#include <asm/mipsregs.h> |
| 727 | +#include <asm/mipsmtregs.h> |
| 728 | +#include <asm/uaccess.h> |
| 729 | +#include <linux/proc_fs.h> |
| 730 | + |
| 731 | +static struct proc_dir_entry *mtsched_proc; |
| 732 | + |
| 733 | +#ifndef CONFIG_MIPS_MT_SMTC |
| 734 | +#define NTCS 2 |
| 735 | +#else |
| 736 | +#define NTCS NR_CPUS |
| 737 | +#endif |
| 738 | +#define NVPES 2 |
| 739 | + |
| 740 | +int lastvpe = 1; |
| 741 | +int lasttc = 8; |
| 742 | + |
| 743 | +static int proc_read_mtsched(char *page, char **start, off_t off, |
| 744 | + int count, int *eof, void *data) |
| 745 | +{ |
| 746 | + int totalen = 0; |
| 747 | + int len; |
| 748 | + |
| 749 | + int i; |
| 750 | + int vpe; |
| 751 | + int mytc; |
| 752 | + unsigned long flags; |
| 753 | + unsigned int mtflags; |
| 754 | + unsigned int haltstate; |
| 755 | + unsigned int vpes_checked[NVPES]; |
| 756 | + unsigned int vpeschedule[NVPES]; |
| 757 | + unsigned int vpeschefback[NVPES]; |
| 758 | + unsigned int tcschedule[NTCS]; |
| 759 | + unsigned int tcschefback[NTCS]; |
| 760 | + |
| 761 | + /* Dump the state of the MIPS MT scheduling policy manager */ |
| 762 | + /* Inititalize control state */ |
| 763 | + for(i = 0; i < NVPES; i++) { |
| 764 | + vpes_checked[i] = 0; |
| 765 | + vpeschedule[i] = 0; |
| 766 | + vpeschefback[i] = 0; |
| 767 | + } |
| 768 | + for(i = 0; i < NTCS; i++) { |
| 769 | + tcschedule[i] = 0; |
| 770 | + tcschefback[i] = 0; |
| 771 | + } |
| 772 | + |
| 773 | + /* Disable interrupts and multithreaded issue */ |
| 774 | + local_irq_save(flags); |
| 775 | + mtflags = dvpe(); |
| 776 | + |
| 777 | + /* Then go through the TCs, halt 'em, and extract the values */ |
| 778 | + mytc = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT; |
| 779 | + for(i = 0; i < NTCS; i++) { |
| 780 | + if(i == mytc) { |
| 781 | + /* No need to halt ourselves! */ |
| 782 | + tcschedule[i] = read_c0_tcschedule(); |
| 783 | + tcschefback[i] = read_c0_tcschefback(); |
| 784 | + /* If VPE bound to TC hasn't been checked, do it */ |
| 785 | + vpe = read_c0_tcbind() & TCBIND_CURVPE; |
| 786 | + if(!vpes_checked[vpe]) { |
| 787 | + vpeschedule[vpe] = read_c0_vpeschedule(); |
| 788 | + vpeschefback[vpe] = read_c0_vpeschefback(); |
| 789 | + vpes_checked[vpe] = 1; |
| 790 | + } |
| 791 | + } else { |
| 792 | + settc(i); |
| 793 | + haltstate = read_tc_c0_tchalt(); |
| 794 | + write_tc_c0_tchalt(TCHALT_H); |
| 795 | + mips_ihb(); |
| 796 | + tcschedule[i] = read_tc_c0_tcschedule(); |
| 797 | + tcschefback[i] = read_tc_c0_tcschefback(); |
| 798 | + /* If VPE bound to TC hasn't been checked, do it */ |
| 799 | + vpe = read_tc_c0_tcbind() & TCBIND_CURVPE; |
| 800 | + if(!vpes_checked[vpe]) { |
| 801 | + vpeschedule[vpe] = read_vpe_c0_vpeschedule(); |
| 802 | + vpeschefback[vpe] = read_vpe_c0_vpeschefback(); |
| 803 | + vpes_checked[vpe] = 1; |
| 804 | + } |
| 805 | + if(!haltstate) write_tc_c0_tchalt(0); |
| 806 | + } |
| 807 | + } |
| 808 | + /* Re-enable MT and interrupts */ |
| 809 | + evpe(mtflags); |
| 810 | + local_irq_restore(flags); |
| 811 | + |
| 812 | + for(vpe=0; vpe < NVPES; vpe++) { |
| 813 | + len = sprintf(page, "VPE[%d].VPEschedule = 0x%08x\n", |
| 814 | + vpe, vpeschedule[vpe]); |
| 815 | + totalen += len; |
| 816 | + page += len; |
| 817 | + len = sprintf(page, "VPE[%d].VPEschefback = 0x%08x\n", |
| 818 | + vpe, vpeschefback[vpe]); |
| 819 | + totalen += len; |
| 820 | + page += len; |
| 821 | + } |
| 822 | + for(i=0; i < NTCS; i++) { |
| 823 | + len = sprintf(page, "TC[%d].TCschedule = 0x%08x\n", |
| 824 | + i, tcschedule[i]); |
| 825 | + totalen += len; |
| 826 | + page += len; |
| 827 | + len = sprintf(page, "TC[%d].TCschefback = 0x%08x\n", |
| 828 | + i, tcschefback[i]); |
| 829 | + totalen += len; |
| 830 | + page += len; |
| 831 | + } |
| 832 | + return totalen; |
| 833 | +} |
| 834 | + |
| 835 | +/* |
| 836 | + * Write to perf counter registers based on text input |
| 837 | + */ |
| 838 | + |
| 839 | +#define TXTBUFSZ 100 |
| 840 | + |
| 841 | +static int proc_write_mtsched(struct file *file, const char *buffer, |
| 842 | + unsigned long count, void *data) |
| 843 | +{ |
| 844 | + int len = 0; |
| 845 | + char mybuf[TXTBUFSZ]; |
| 846 | + /* At most, we will set up 9 TCs and 2 VPEs, 11 entries in all */ |
| 847 | + char entity[1]; //, entity1[1]; |
| 848 | + int number[1]; |
| 849 | + unsigned long value[1]; |
| 850 | + int nparsed = 0 , index = 0; |
| 851 | + unsigned long flags; |
| 852 | + unsigned int mtflags; |
| 853 | + unsigned int haltstate; |
| 854 | + unsigned int tcbindval; |
| 855 | + |
| 856 | + if(count >= TXTBUFSZ) len = TXTBUFSZ-1; |
| 857 | + else len = count; |
| 858 | + memset(mybuf,0,TXTBUFSZ); |
| 859 | + if(copy_from_user(mybuf, buffer, len)) return -EFAULT; |
| 860 | + |
| 861 | + nparsed = sscanf(mybuf, "%c%d %lx", |
| 862 | + &entity[0] ,&number[0], &value[0]); |
| 863 | + |
| 864 | + /* |
| 865 | + * Having acquired the inputs, which might have |
| 866 | + * generated exceptions and preemptions, |
| 867 | + * program the registers. |
| 868 | + */ |
| 869 | + /* Disable interrupts and multithreaded issue */ |
| 870 | + local_irq_save(flags); |
| 871 | + mtflags = dvpe(); |
| 872 | + |
| 873 | + if(entity[index] == 't' ) { |
| 874 | + /* Set TCSchedule or TCScheFBack of specified TC */ |
| 875 | + if(number[index] > NTCS) goto skip; |
| 876 | + /* If it's our own TC, do it direct */ |
| 877 | + if(number[index] == |
| 878 | + ((read_c0_tcbind() & TCBIND_CURTC) |
| 879 | + >> TCBIND_CURTC_SHIFT)) { |
| 880 | + if(entity[index] == 't') |
| 881 | + write_c0_tcschedule(value[index]); |
| 882 | + else |
| 883 | + write_c0_tcschefback(value[index]); |
| 884 | + } else { |
| 885 | + /* Otherwise, we do it via MTTR */ |
| 886 | + settc(number[index]); |
| 887 | + haltstate = read_tc_c0_tchalt(); |
| 888 | + write_tc_c0_tchalt(TCHALT_H); |
| 889 | + mips_ihb(); |
| 890 | + if(entity[index] == 't') |
| 891 | + write_tc_c0_tcschedule(value[index]); |
| 892 | + else |
| 893 | + write_tc_c0_tcschefback(value[index]); |
| 894 | + mips_ihb(); |
| 895 | + if(!haltstate) write_tc_c0_tchalt(0); |
| 896 | + } |
| 897 | + } else if(entity[index] == 'v') { |
| 898 | + /* Set VPESchedule of specified VPE */ |
| 899 | + if(number[index] > NVPES) goto skip; |
| 900 | + tcbindval = read_c0_tcbind(); |
| 901 | + /* Are we doing this to our current VPE? */ |
| 902 | + if((tcbindval & TCBIND_CURVPE) == number[index]) { |
| 903 | + /* Then life is simple */ |
| 904 | + write_c0_vpeschedule(value[index]); |
| 905 | + } else { |
| 906 | + /* |
| 907 | + * Bind ourselves to the other VPE long enough |
| 908 | + * to program the bind value. |
| 909 | + */ |
| 910 | + write_c0_tcbind((tcbindval & ~TCBIND_CURVPE) |
| 911 | + | number[index]); |
| 912 | + mips_ihb(); |
| 913 | + write_c0_vpeschedule(value[index]); |
| 914 | + mips_ihb(); |
| 915 | + /* Restore previous binding */ |
| 916 | + write_c0_tcbind(tcbindval); |
| 917 | + mips_ihb(); |
| 918 | + } |
| 919 | + } |
| 920 | + |
| 921 | + else if(entity[index] == 'r') { |
| 922 | + unsigned int vpes_checked[2], vpe ,i , mytc; |
| 923 | + vpes_checked[0] = vpes_checked[1] = 0; |
| 924 | + |
| 925 | + /* Then go through the TCs, halt 'em, and extract the values */ |
| 926 | + mytc = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT; |
| 927 | + |
| 928 | + for(i = 0; i < NTCS; i++) { |
| 929 | + if(i == mytc) { |
| 930 | + /* No need to halt ourselves! */ |
| 931 | + write_c0_vpeschefback(0); |
| 932 | + write_c0_tcschefback(0); |
| 933 | + } else { |
| 934 | + settc(i); |
| 935 | + haltstate = read_tc_c0_tchalt(); |
| 936 | + write_tc_c0_tchalt(TCHALT_H); |
| 937 | + mips_ihb(); |
| 938 | + write_tc_c0_tcschefback(0); |
| 939 | + /* If VPE bound to TC hasn't been checked, do it */ |
| 940 | + vpe = read_tc_c0_tcbind() & TCBIND_CURVPE; |
| 941 | + if(!vpes_checked[vpe]) { |
| 942 | + write_vpe_c0_vpeschefback(0); |
| 943 | + vpes_checked[vpe] = 1; |
| 944 | + } |
| 945 | + if(!haltstate) write_tc_c0_tchalt(0); |
| 946 | + } |
| 947 | + } |
| 948 | + } |
| 949 | + else { |
| 950 | + printk ("\n Usage : <t/v><0/1> <Hex Value>\n Example : t0 0x01\n"); |
| 951 | + } |
| 952 | + |
| 953 | +skip: |
| 954 | + /* Re-enable MT and interrupts */ |
| 955 | + evpe(mtflags); |
| 956 | + local_irq_restore(flags); |
| 957 | + return (len); |
| 958 | +} |
| 959 | + |
| 960 | +static int __init init_mtsched_proc(void) |
| 961 | +{ |
| 962 | + extern struct proc_dir_entry *get_mips_proc_dir(void); |
| 963 | + struct proc_dir_entry *mips_proc_dir; |
| 964 | + |
| 965 | + if (!cpu_has_mipsmt) { |
| 966 | + printk("mtsched: not a MIPS MT capable processor\n"); |
| 967 | + return -ENODEV; |
| 968 | + } |
| 969 | + |
| 970 | + mips_proc_dir = get_mips_proc_dir(); |
| 971 | + |
| 972 | + mtsched_proc = create_proc_entry("mtsched", 0644, mips_proc_dir); |
| 973 | + mtsched_proc->read_proc = proc_read_mtsched; |
| 974 | + mtsched_proc->write_proc = proc_write_mtsched; |
| 975 | + |
| 976 | + return 0; |
| 977 | +} |
| 978 | + |
| 979 | +/* Automagically create the entry */ |
| 980 | +module_init(init_mtsched_proc); |
| 981 | --- /dev/null |
| 982 | +++ b/arch/mips/kernel/perf_proc.c |
| 983 | @@ -0,0 +1,191 @@ |
| 984 | +/* |
| 985 | + * /proc hooks for CPU performance counter support for SMTC kernel |
| 986 | + * (and ultimately others) |
| 987 | + * Copyright (C) 2006 Mips Technologies, Inc |
| 988 | + */ |
| 989 | + |
| 990 | +#include <linux/kernel.h> |
| 991 | + |
| 992 | +#include <asm/cpu.h> |
| 993 | +#include <asm/processor.h> |
| 994 | +#include <asm/system.h> |
| 995 | +#include <asm/mipsregs.h> |
| 996 | +#include <asm/uaccess.h> |
| 997 | +#include <linux/proc_fs.h> |
| 998 | + |
| 999 | +/* |
| 1000 | + * /proc diagnostic and statistics hooks |
| 1001 | + */ |
| 1002 | + |
| 1003 | + |
| 1004 | +/* Internal software-extended event counters */ |
| 1005 | + |
| 1006 | +static unsigned long long extencount[4] = {0,0,0,0}; |
| 1007 | + |
| 1008 | +static struct proc_dir_entry *perf_proc; |
| 1009 | + |
| 1010 | +static int proc_read_perf(char *page, char **start, off_t off, |
| 1011 | + int count, int *eof, void *data) |
| 1012 | +{ |
| 1013 | + int totalen = 0; |
| 1014 | + int len; |
| 1015 | + |
| 1016 | + len = sprintf(page, "PerfCnt[0].Ctl : 0x%08x\n", read_c0_perfctrl0()); |
| 1017 | + totalen += len; |
| 1018 | + page += len; |
| 1019 | + len = sprintf(page, "PerfCnt[0].Cnt : %Lu\n", |
| 1020 | + extencount[0] + (unsigned long long)((unsigned)read_c0_perfcntr0())); |
| 1021 | + totalen += len; |
| 1022 | + page += len; |
| 1023 | + len = sprintf(page, "PerfCnt[1].Ctl : 0x%08x\n", read_c0_perfctrl1()); |
| 1024 | + totalen += len; |
| 1025 | + page += len; |
| 1026 | + len = sprintf(page, "PerfCnt[1].Cnt : %Lu\n", |
| 1027 | + extencount[1] + (unsigned long long)((unsigned)read_c0_perfcntr1())); |
| 1028 | + totalen += len; |
| 1029 | + page += len; |
| 1030 | + len = sprintf(page, "PerfCnt[2].Ctl : 0x%08x\n", read_c0_perfctrl2()); |
| 1031 | + totalen += len; |
| 1032 | + page += len; |
| 1033 | + len = sprintf(page, "PerfCnt[2].Cnt : %Lu\n", |
| 1034 | + extencount[2] + (unsigned long long)((unsigned)read_c0_perfcntr2())); |
| 1035 | + totalen += len; |
| 1036 | + page += len; |
| 1037 | + len = sprintf(page, "PerfCnt[3].Ctl : 0x%08x\n", read_c0_perfctrl3()); |
| 1038 | + totalen += len; |
| 1039 | + page += len; |
| 1040 | + len = sprintf(page, "PerfCnt[3].Cnt : %Lu\n", |
| 1041 | + extencount[3] + (unsigned long long)((unsigned)read_c0_perfcntr3())); |
| 1042 | + totalen += len; |
| 1043 | + page += len; |
| 1044 | + |
| 1045 | + return totalen; |
| 1046 | +} |
| 1047 | + |
| 1048 | +/* |
| 1049 | + * Write to perf counter registers based on text input |
| 1050 | + */ |
| 1051 | + |
| 1052 | +#define TXTBUFSZ 100 |
| 1053 | + |
| 1054 | +static int proc_write_perf(struct file *file, const char *buffer, |
| 1055 | + unsigned long count, void *data) |
| 1056 | +{ |
| 1057 | + int len; |
| 1058 | + int nparsed; |
| 1059 | + int index; |
| 1060 | + char mybuf[TXTBUFSZ]; |
| 1061 | + |
| 1062 | + int which[4]; |
| 1063 | + unsigned long control[4]; |
| 1064 | + long long ctrdata[4]; |
| 1065 | + |
| 1066 | + if(count >= TXTBUFSZ) len = TXTBUFSZ-1; |
| 1067 | + else len = count; |
| 1068 | + memset(mybuf,0,TXTBUFSZ); |
| 1069 | + if(copy_from_user(mybuf, buffer, len)) return -EFAULT; |
| 1070 | + |
| 1071 | + nparsed = sscanf(mybuf, |
| 1072 | + "%d %lx %Ld %d %lx %Ld %d %lx %Ld %d %lx %Ld", |
| 1073 | + &which[0], &control[0], &ctrdata[0], |
| 1074 | + &which[1], &control[1], &ctrdata[1], |
| 1075 | + &which[2], &control[2], &ctrdata[2], |
| 1076 | + &which[3], &control[3], &ctrdata[3]); |
| 1077 | + |
| 1078 | + for(index = 0; nparsed >= 3; index++) { |
| 1079 | + switch (which[index]) { |
| 1080 | + case 0: |
| 1081 | + write_c0_perfctrl0(control[index]); |
| 1082 | + if(ctrdata[index] != -1) { |
| 1083 | + extencount[0] = (unsigned long long)ctrdata[index]; |
| 1084 | + write_c0_perfcntr0((unsigned long)0); |
| 1085 | + } |
| 1086 | + break; |
| 1087 | + case 1: |
| 1088 | + write_c0_perfctrl1(control[index]); |
| 1089 | + if(ctrdata[index] != -1) { |
| 1090 | + extencount[1] = (unsigned long long)ctrdata[index]; |
| 1091 | + write_c0_perfcntr1((unsigned long)0); |
| 1092 | + } |
| 1093 | + break; |
| 1094 | + case 2: |
| 1095 | + write_c0_perfctrl2(control[index]); |
| 1096 | + if(ctrdata[index] != -1) { |
| 1097 | + extencount[2] = (unsigned long long)ctrdata[index]; |
| 1098 | + write_c0_perfcntr2((unsigned long)0); |
| 1099 | + } |
| 1100 | + break; |
| 1101 | + case 3: |
| 1102 | + write_c0_perfctrl3(control[index]); |
| 1103 | + if(ctrdata[index] != -1) { |
| 1104 | + extencount[3] = (unsigned long long)ctrdata[index]; |
| 1105 | + write_c0_perfcntr3((unsigned long)0); |
| 1106 | + } |
| 1107 | + break; |
| 1108 | + } |
| 1109 | + nparsed -= 3; |
| 1110 | + } |
| 1111 | + return (len); |
| 1112 | +} |
| 1113 | + |
| 1114 | +extern int (*perf_irq)(void); |
| 1115 | + |
| 1116 | +/* |
| 1117 | + * Invoked when timer interrupt vector picks up a perf counter overflow |
| 1118 | + */ |
| 1119 | + |
| 1120 | +static int perf_proc_irq(void) |
| 1121 | +{ |
| 1122 | + unsigned long snapshot; |
| 1123 | + |
| 1124 | + /* |
| 1125 | + * It would be nice to do this as a loop, but we don't have |
| 1126 | + * indirect access to CP0 registers. |
| 1127 | + */ |
| 1128 | + snapshot = read_c0_perfcntr0(); |
| 1129 | + if ((long)snapshot < 0) { |
| 1130 | + extencount[0] += |
| 1131 | + (unsigned long long)((unsigned)read_c0_perfcntr0()); |
| 1132 | + write_c0_perfcntr0(0); |
| 1133 | + } |
| 1134 | + snapshot = read_c0_perfcntr1(); |
| 1135 | + if ((long)snapshot < 0) { |
| 1136 | + extencount[1] += |
| 1137 | + (unsigned long long)((unsigned)read_c0_perfcntr1()); |
| 1138 | + write_c0_perfcntr1(0); |
| 1139 | + } |
| 1140 | + snapshot = read_c0_perfcntr2(); |
| 1141 | + if ((long)snapshot < 0) { |
| 1142 | + extencount[2] += |
| 1143 | + (unsigned long long)((unsigned)read_c0_perfcntr2()); |
| 1144 | + write_c0_perfcntr2(0); |
| 1145 | + } |
| 1146 | + snapshot = read_c0_perfcntr3(); |
| 1147 | + if ((long)snapshot < 0) { |
| 1148 | + extencount[3] += |
| 1149 | + (unsigned long long)((unsigned)read_c0_perfcntr3()); |
| 1150 | + write_c0_perfcntr3(0); |
| 1151 | + } |
| 1152 | + return 0; |
| 1153 | +} |
| 1154 | + |
| 1155 | +static int __init init_perf_proc(void) |
| 1156 | +{ |
| 1157 | + extern struct proc_dir_entry *get_mips_proc_dir(void); |
| 1158 | + |
| 1159 | + struct proc_dir_entry *mips_proc_dir = get_mips_proc_dir(); |
| 1160 | + |
| 1161 | + write_c0_perfcntr0(0); |
| 1162 | + write_c0_perfcntr1(0); |
| 1163 | + write_c0_perfcntr2(0); |
| 1164 | + write_c0_perfcntr3(0); |
| 1165 | + perf_proc = create_proc_entry("perf", 0644, mips_proc_dir); |
| 1166 | + perf_proc->read_proc = proc_read_perf; |
| 1167 | + perf_proc->write_proc = proc_write_perf; |
| 1168 | + perf_irq = perf_proc_irq; |
| 1169 | + |
| 1170 | + return 0; |
| 1171 | +} |
| 1172 | + |
| 1173 | +/* Automagically create the entry */ |
| 1174 | +module_init(init_perf_proc); |
| 1175 | |