11a2a6d7eSDeng-Cheng Zhu /* 21a2a6d7eSDeng-Cheng Zhu * This file is subject to the terms and conditions of the GNU General Public 31a2a6d7eSDeng-Cheng Zhu * License. See the file "COPYING" in the main directory of this archive 41a2a6d7eSDeng-Cheng Zhu * for more details. 51a2a6d7eSDeng-Cheng Zhu * 61a2a6d7eSDeng-Cheng Zhu * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved. 71a2a6d7eSDeng-Cheng Zhu * Copyright (C) 2013 Imagination Technologies Ltd. 81a2a6d7eSDeng-Cheng Zhu */ 91a2a6d7eSDeng-Cheng Zhu #include <linux/kernel.h> 101a2a6d7eSDeng-Cheng Zhu #include <linux/device.h> 111a2a6d7eSDeng-Cheng Zhu #include <linux/fs.h> 121a2a6d7eSDeng-Cheng Zhu #include <linux/slab.h> 131a2a6d7eSDeng-Cheng Zhu #include <linux/export.h> 141a2a6d7eSDeng-Cheng Zhu 151a2a6d7eSDeng-Cheng Zhu #include <asm/mipsregs.h> 161a2a6d7eSDeng-Cheng Zhu #include <asm/mipsmtregs.h> 171a2a6d7eSDeng-Cheng Zhu #include <asm/mips_mt.h> 181a2a6d7eSDeng-Cheng Zhu #include <asm/vpe.h> 191a2a6d7eSDeng-Cheng Zhu 201a2a6d7eSDeng-Cheng Zhu static int major; 211a2a6d7eSDeng-Cheng Zhu 221a2a6d7eSDeng-Cheng Zhu /* The number of TCs and VPEs physically available on the core */ 231a2a6d7eSDeng-Cheng Zhu static int hw_tcs, hw_vpes; 241a2a6d7eSDeng-Cheng Zhu 251a2a6d7eSDeng-Cheng Zhu /* We are prepared so configure and start the VPE... */ 261a2a6d7eSDeng-Cheng Zhu int vpe_run(struct vpe *v) 271a2a6d7eSDeng-Cheng Zhu { 281a2a6d7eSDeng-Cheng Zhu unsigned long flags, val, dmt_flag; 291a2a6d7eSDeng-Cheng Zhu struct vpe_notifications *n; 301a2a6d7eSDeng-Cheng Zhu unsigned int vpeflags; 311a2a6d7eSDeng-Cheng Zhu struct tc *t; 321a2a6d7eSDeng-Cheng Zhu 331a2a6d7eSDeng-Cheng Zhu /* check we are the Master VPE */ 341a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 351a2a6d7eSDeng-Cheng Zhu val = read_c0_vpeconf0(); 361a2a6d7eSDeng-Cheng Zhu if (!(val & VPECONF0_MVP)) { 371a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: only Master VPE's are able to config MT\n"); 381a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 391a2a6d7eSDeng-Cheng Zhu 401a2a6d7eSDeng-Cheng Zhu return -1; 411a2a6d7eSDeng-Cheng Zhu } 421a2a6d7eSDeng-Cheng Zhu 431a2a6d7eSDeng-Cheng Zhu dmt_flag = dmt(); 441a2a6d7eSDeng-Cheng Zhu vpeflags = dvpe(); 451a2a6d7eSDeng-Cheng Zhu 461a2a6d7eSDeng-Cheng Zhu if (list_empty(&v->tc)) { 471a2a6d7eSDeng-Cheng Zhu evpe(vpeflags); 481a2a6d7eSDeng-Cheng Zhu emt(dmt_flag); 491a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 501a2a6d7eSDeng-Cheng Zhu 511a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: No TC's associated with VPE %d\n", 521a2a6d7eSDeng-Cheng Zhu v->minor); 531a2a6d7eSDeng-Cheng Zhu 541a2a6d7eSDeng-Cheng Zhu return -ENOEXEC; 551a2a6d7eSDeng-Cheng Zhu } 561a2a6d7eSDeng-Cheng Zhu 571a2a6d7eSDeng-Cheng Zhu t = list_first_entry(&v->tc, struct tc, tc); 581a2a6d7eSDeng-Cheng Zhu 591a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 601a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 611a2a6d7eSDeng-Cheng Zhu 621a2a6d7eSDeng-Cheng Zhu settc(t->index); 631a2a6d7eSDeng-Cheng Zhu 641a2a6d7eSDeng-Cheng Zhu /* should check it is halted, and not activated */ 651a2a6d7eSDeng-Cheng Zhu if ((read_tc_c0_tcstatus() & TCSTATUS_A) || 661a2a6d7eSDeng-Cheng Zhu !(read_tc_c0_tchalt() & TCHALT_H)) { 671a2a6d7eSDeng-Cheng Zhu evpe(vpeflags); 681a2a6d7eSDeng-Cheng Zhu emt(dmt_flag); 691a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 701a2a6d7eSDeng-Cheng Zhu 711a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: TC %d is already active!\n", 721a2a6d7eSDeng-Cheng Zhu t->index); 731a2a6d7eSDeng-Cheng Zhu 741a2a6d7eSDeng-Cheng Zhu return -ENOEXEC; 751a2a6d7eSDeng-Cheng Zhu } 761a2a6d7eSDeng-Cheng Zhu 771a2a6d7eSDeng-Cheng Zhu /* 781a2a6d7eSDeng-Cheng Zhu * Write the address we want it to start running from in the TCPC 791a2a6d7eSDeng-Cheng Zhu * register. 801a2a6d7eSDeng-Cheng Zhu */ 811a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcrestart((unsigned long)v->__start); 821a2a6d7eSDeng-Cheng Zhu write_tc_c0_tccontext((unsigned long)0); 831a2a6d7eSDeng-Cheng Zhu 841a2a6d7eSDeng-Cheng Zhu /* 851a2a6d7eSDeng-Cheng Zhu * Mark the TC as activated, not interrupt exempt and not dynamically 861a2a6d7eSDeng-Cheng Zhu * allocatable 871a2a6d7eSDeng-Cheng Zhu */ 881a2a6d7eSDeng-Cheng Zhu val = read_tc_c0_tcstatus(); 891a2a6d7eSDeng-Cheng Zhu val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A; 901a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(val); 911a2a6d7eSDeng-Cheng Zhu 921a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H); 931a2a6d7eSDeng-Cheng Zhu 941a2a6d7eSDeng-Cheng Zhu /* 951a2a6d7eSDeng-Cheng Zhu * The sde-kit passes 'memsize' to __start in $a3, so set something 961a2a6d7eSDeng-Cheng Zhu * here... Or set $a3 to zero and define DFLT_STACK_SIZE and 971a2a6d7eSDeng-Cheng Zhu * DFLT_HEAP_SIZE when you compile your program 981a2a6d7eSDeng-Cheng Zhu */ 991a2a6d7eSDeng-Cheng Zhu mttgpr(6, v->ntcs); 1001a2a6d7eSDeng-Cheng Zhu mttgpr(7, physical_memsize); 1011a2a6d7eSDeng-Cheng Zhu 1021a2a6d7eSDeng-Cheng Zhu /* set up VPE1 */ 1031a2a6d7eSDeng-Cheng Zhu /* 1041a2a6d7eSDeng-Cheng Zhu * bind the TC to VPE 1 as late as possible so we only have the final 1051a2a6d7eSDeng-Cheng Zhu * VPE registers to set up, and so an EJTAG probe can trigger on it 1061a2a6d7eSDeng-Cheng Zhu */ 1071a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1); 1081a2a6d7eSDeng-Cheng Zhu 1091a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA)); 1101a2a6d7eSDeng-Cheng Zhu 1111a2a6d7eSDeng-Cheng Zhu back_to_back_c0_hazard(); 1121a2a6d7eSDeng-Cheng Zhu 1131a2a6d7eSDeng-Cheng Zhu /* Set up the XTC bit in vpeconf0 to point at our tc */ 1141a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC)) 1151a2a6d7eSDeng-Cheng Zhu | (t->index << VPECONF0_XTC_SHIFT)); 1161a2a6d7eSDeng-Cheng Zhu 1171a2a6d7eSDeng-Cheng Zhu back_to_back_c0_hazard(); 1181a2a6d7eSDeng-Cheng Zhu 1191a2a6d7eSDeng-Cheng Zhu /* enable this VPE */ 1201a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); 1211a2a6d7eSDeng-Cheng Zhu 1221a2a6d7eSDeng-Cheng Zhu /* clear out any left overs from a previous program */ 1231a2a6d7eSDeng-Cheng Zhu write_vpe_c0_status(0); 1241a2a6d7eSDeng-Cheng Zhu write_vpe_c0_cause(0); 1251a2a6d7eSDeng-Cheng Zhu 1261a2a6d7eSDeng-Cheng Zhu /* take system out of configuration state */ 1271a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 1281a2a6d7eSDeng-Cheng Zhu 1291a2a6d7eSDeng-Cheng Zhu /* 1301a2a6d7eSDeng-Cheng Zhu * SMTC/SMVP kernels manage VPE enable independently, 1311a2a6d7eSDeng-Cheng Zhu * but uniprocessor kernels need to turn it on, even 1321a2a6d7eSDeng-Cheng Zhu * if that wasn't the pre-dvpe() state. 1331a2a6d7eSDeng-Cheng Zhu */ 1341a2a6d7eSDeng-Cheng Zhu #ifdef CONFIG_SMP 1351a2a6d7eSDeng-Cheng Zhu evpe(vpeflags); 1361a2a6d7eSDeng-Cheng Zhu #else 1371a2a6d7eSDeng-Cheng Zhu evpe(EVPE_ENABLE); 1381a2a6d7eSDeng-Cheng Zhu #endif 1391a2a6d7eSDeng-Cheng Zhu emt(dmt_flag); 1401a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 1411a2a6d7eSDeng-Cheng Zhu 1421a2a6d7eSDeng-Cheng Zhu list_for_each_entry(n, &v->notify, list) 1431a2a6d7eSDeng-Cheng Zhu n->start(VPE_MODULE_MINOR); 1441a2a6d7eSDeng-Cheng Zhu 1451a2a6d7eSDeng-Cheng Zhu return 0; 1461a2a6d7eSDeng-Cheng Zhu } 1471a2a6d7eSDeng-Cheng Zhu 1481a2a6d7eSDeng-Cheng Zhu void cleanup_tc(struct tc *tc) 1491a2a6d7eSDeng-Cheng Zhu { 1501a2a6d7eSDeng-Cheng Zhu unsigned long flags; 1511a2a6d7eSDeng-Cheng Zhu unsigned int mtflags, vpflags; 1521a2a6d7eSDeng-Cheng Zhu int tmp; 1531a2a6d7eSDeng-Cheng Zhu 1541a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 1551a2a6d7eSDeng-Cheng Zhu mtflags = dmt(); 1561a2a6d7eSDeng-Cheng Zhu vpflags = dvpe(); 1571a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 1581a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 1591a2a6d7eSDeng-Cheng Zhu 1601a2a6d7eSDeng-Cheng Zhu settc(tc->index); 1611a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcstatus(); 1621a2a6d7eSDeng-Cheng Zhu 1631a2a6d7eSDeng-Cheng Zhu /* mark not allocated and not dynamically allocatable */ 1641a2a6d7eSDeng-Cheng Zhu tmp &= ~(TCSTATUS_A | TCSTATUS_DA); 1651a2a6d7eSDeng-Cheng Zhu tmp |= TCSTATUS_IXMT; /* interrupt exempt */ 1661a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(tmp); 1671a2a6d7eSDeng-Cheng Zhu 1681a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H); 1691a2a6d7eSDeng-Cheng Zhu mips_ihb(); 1701a2a6d7eSDeng-Cheng Zhu 1711a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 1721a2a6d7eSDeng-Cheng Zhu evpe(vpflags); 1731a2a6d7eSDeng-Cheng Zhu emt(mtflags); 1741a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 1751a2a6d7eSDeng-Cheng Zhu } 1761a2a6d7eSDeng-Cheng Zhu 1771a2a6d7eSDeng-Cheng Zhu /* module wrapper entry points */ 1781a2a6d7eSDeng-Cheng Zhu /* give me a vpe */ 1791a2a6d7eSDeng-Cheng Zhu void *vpe_alloc(void) 1801a2a6d7eSDeng-Cheng Zhu { 1811a2a6d7eSDeng-Cheng Zhu int i; 1821a2a6d7eSDeng-Cheng Zhu struct vpe *v; 1831a2a6d7eSDeng-Cheng Zhu 1841a2a6d7eSDeng-Cheng Zhu /* find a vpe */ 1851a2a6d7eSDeng-Cheng Zhu for (i = 1; i < MAX_VPES; i++) { 1861a2a6d7eSDeng-Cheng Zhu v = get_vpe(i); 1871a2a6d7eSDeng-Cheng Zhu if (v != NULL) { 1881a2a6d7eSDeng-Cheng Zhu v->state = VPE_STATE_INUSE; 1891a2a6d7eSDeng-Cheng Zhu return v; 1901a2a6d7eSDeng-Cheng Zhu } 1911a2a6d7eSDeng-Cheng Zhu } 1921a2a6d7eSDeng-Cheng Zhu return NULL; 1931a2a6d7eSDeng-Cheng Zhu } 1941a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_alloc); 1951a2a6d7eSDeng-Cheng Zhu 1961a2a6d7eSDeng-Cheng Zhu /* start running from here */ 1971a2a6d7eSDeng-Cheng Zhu int vpe_start(void *vpe, unsigned long start) 1981a2a6d7eSDeng-Cheng Zhu { 1991a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe; 2001a2a6d7eSDeng-Cheng Zhu 2011a2a6d7eSDeng-Cheng Zhu v->__start = start; 2021a2a6d7eSDeng-Cheng Zhu return vpe_run(v); 2031a2a6d7eSDeng-Cheng Zhu } 2041a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_start); 2051a2a6d7eSDeng-Cheng Zhu 2061a2a6d7eSDeng-Cheng Zhu /* halt it for now */ 2071a2a6d7eSDeng-Cheng Zhu int vpe_stop(void *vpe) 2081a2a6d7eSDeng-Cheng Zhu { 2091a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe; 2101a2a6d7eSDeng-Cheng Zhu struct tc *t; 2111a2a6d7eSDeng-Cheng Zhu unsigned int evpe_flags; 2121a2a6d7eSDeng-Cheng Zhu 2131a2a6d7eSDeng-Cheng Zhu evpe_flags = dvpe(); 2141a2a6d7eSDeng-Cheng Zhu 2151a2a6d7eSDeng-Cheng Zhu t = list_entry(v->tc.next, struct tc, tc); 2161a2a6d7eSDeng-Cheng Zhu if (t != NULL) { 2171a2a6d7eSDeng-Cheng Zhu settc(t->index); 2181a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA); 2191a2a6d7eSDeng-Cheng Zhu } 2201a2a6d7eSDeng-Cheng Zhu 2211a2a6d7eSDeng-Cheng Zhu evpe(evpe_flags); 2221a2a6d7eSDeng-Cheng Zhu 2231a2a6d7eSDeng-Cheng Zhu return 0; 2241a2a6d7eSDeng-Cheng Zhu } 2251a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_stop); 2261a2a6d7eSDeng-Cheng Zhu 2271a2a6d7eSDeng-Cheng Zhu /* I've done with it thank you */ 2281a2a6d7eSDeng-Cheng Zhu int vpe_free(void *vpe) 2291a2a6d7eSDeng-Cheng Zhu { 2301a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe; 2311a2a6d7eSDeng-Cheng Zhu struct tc *t; 2321a2a6d7eSDeng-Cheng Zhu unsigned int evpe_flags; 2331a2a6d7eSDeng-Cheng Zhu 2341a2a6d7eSDeng-Cheng Zhu t = list_entry(v->tc.next, struct tc, tc); 2351a2a6d7eSDeng-Cheng Zhu if (t == NULL) 2361a2a6d7eSDeng-Cheng Zhu return -ENOEXEC; 2371a2a6d7eSDeng-Cheng Zhu 2381a2a6d7eSDeng-Cheng Zhu evpe_flags = dvpe(); 2391a2a6d7eSDeng-Cheng Zhu 2401a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 2411a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 2421a2a6d7eSDeng-Cheng Zhu 2431a2a6d7eSDeng-Cheng Zhu settc(t->index); 2441a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA); 2451a2a6d7eSDeng-Cheng Zhu 2461a2a6d7eSDeng-Cheng Zhu /* halt the TC */ 2471a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H); 2481a2a6d7eSDeng-Cheng Zhu mips_ihb(); 2491a2a6d7eSDeng-Cheng Zhu 2501a2a6d7eSDeng-Cheng Zhu /* mark the TC unallocated */ 2511a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A); 2521a2a6d7eSDeng-Cheng Zhu 2531a2a6d7eSDeng-Cheng Zhu v->state = VPE_STATE_UNUSED; 2541a2a6d7eSDeng-Cheng Zhu 2551a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 2561a2a6d7eSDeng-Cheng Zhu evpe(evpe_flags); 2571a2a6d7eSDeng-Cheng Zhu 2581a2a6d7eSDeng-Cheng Zhu return 0; 2591a2a6d7eSDeng-Cheng Zhu } 2601a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_free); 2611a2a6d7eSDeng-Cheng Zhu 2621a2a6d7eSDeng-Cheng Zhu static ssize_t store_kill(struct device *dev, struct device_attribute *attr, 2631a2a6d7eSDeng-Cheng Zhu const char *buf, size_t len) 2641a2a6d7eSDeng-Cheng Zhu { 2651a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index()); 2661a2a6d7eSDeng-Cheng Zhu struct vpe_notifications *notifier; 2671a2a6d7eSDeng-Cheng Zhu 2681a2a6d7eSDeng-Cheng Zhu list_for_each_entry(notifier, &vpe->notify, list) 2691a2a6d7eSDeng-Cheng Zhu notifier->stop(aprp_cpu_index()); 2701a2a6d7eSDeng-Cheng Zhu 2711a2a6d7eSDeng-Cheng Zhu release_progmem(vpe->load_addr); 2721a2a6d7eSDeng-Cheng Zhu cleanup_tc(get_tc(aprp_cpu_index())); 2731a2a6d7eSDeng-Cheng Zhu vpe_stop(vpe); 2741a2a6d7eSDeng-Cheng Zhu vpe_free(vpe); 2751a2a6d7eSDeng-Cheng Zhu 2761a2a6d7eSDeng-Cheng Zhu return len; 2771a2a6d7eSDeng-Cheng Zhu } 2781a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR(kill, S_IWUSR, NULL, store_kill); 2791a2a6d7eSDeng-Cheng Zhu 2801a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_show(struct device *cd, struct device_attribute *attr, 2811a2a6d7eSDeng-Cheng Zhu char *buf) 2821a2a6d7eSDeng-Cheng Zhu { 2831a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index()); 2841a2a6d7eSDeng-Cheng Zhu 2851a2a6d7eSDeng-Cheng Zhu return sprintf(buf, "%d\n", vpe->ntcs); 2861a2a6d7eSDeng-Cheng Zhu } 2871a2a6d7eSDeng-Cheng Zhu 2881a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_store(struct device *dev, struct device_attribute *attr, 2891a2a6d7eSDeng-Cheng Zhu const char *buf, size_t len) 2901a2a6d7eSDeng-Cheng Zhu { 2911a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index()); 2921a2a6d7eSDeng-Cheng Zhu unsigned long new; 2931a2a6d7eSDeng-Cheng Zhu int ret; 2941a2a6d7eSDeng-Cheng Zhu 2951a2a6d7eSDeng-Cheng Zhu ret = kstrtoul(buf, 0, &new); 2961a2a6d7eSDeng-Cheng Zhu if (ret < 0) 2971a2a6d7eSDeng-Cheng Zhu return ret; 2981a2a6d7eSDeng-Cheng Zhu 2991a2a6d7eSDeng-Cheng Zhu if (new == 0 || new > (hw_tcs - aprp_cpu_index())) 3001a2a6d7eSDeng-Cheng Zhu return -EINVAL; 3011a2a6d7eSDeng-Cheng Zhu 3021a2a6d7eSDeng-Cheng Zhu vpe->ntcs = new; 3031a2a6d7eSDeng-Cheng Zhu 3041a2a6d7eSDeng-Cheng Zhu return len; 3051a2a6d7eSDeng-Cheng Zhu } 3061a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR_RW(ntcs); 3071a2a6d7eSDeng-Cheng Zhu 3081a2a6d7eSDeng-Cheng Zhu static struct attribute *vpe_attrs[] = { 3091a2a6d7eSDeng-Cheng Zhu &dev_attr_kill.attr, 3101a2a6d7eSDeng-Cheng Zhu &dev_attr_ntcs.attr, 3111a2a6d7eSDeng-Cheng Zhu NULL, 3121a2a6d7eSDeng-Cheng Zhu }; 3131a2a6d7eSDeng-Cheng Zhu ATTRIBUTE_GROUPS(vpe); 3141a2a6d7eSDeng-Cheng Zhu 3151a2a6d7eSDeng-Cheng Zhu static void vpe_device_release(struct device *cd) 3161a2a6d7eSDeng-Cheng Zhu { 3171a2a6d7eSDeng-Cheng Zhu kfree(cd); 3181a2a6d7eSDeng-Cheng Zhu } 3191a2a6d7eSDeng-Cheng Zhu 3201a2a6d7eSDeng-Cheng Zhu static struct class vpe_class = { 3211a2a6d7eSDeng-Cheng Zhu .name = "vpe", 3221a2a6d7eSDeng-Cheng Zhu .owner = THIS_MODULE, 3231a2a6d7eSDeng-Cheng Zhu .dev_release = vpe_device_release, 3241a2a6d7eSDeng-Cheng Zhu .dev_groups = vpe_groups, 3251a2a6d7eSDeng-Cheng Zhu }; 3261a2a6d7eSDeng-Cheng Zhu 3271a2a6d7eSDeng-Cheng Zhu static struct device vpe_device; 3281a2a6d7eSDeng-Cheng Zhu 3291a2a6d7eSDeng-Cheng Zhu int __init vpe_module_init(void) 3301a2a6d7eSDeng-Cheng Zhu { 3311a2a6d7eSDeng-Cheng Zhu unsigned int mtflags, vpflags; 3321a2a6d7eSDeng-Cheng Zhu unsigned long flags, val; 3331a2a6d7eSDeng-Cheng Zhu struct vpe *v = NULL; 3341a2a6d7eSDeng-Cheng Zhu struct tc *t; 3351a2a6d7eSDeng-Cheng Zhu int tc, err; 3361a2a6d7eSDeng-Cheng Zhu 3371a2a6d7eSDeng-Cheng Zhu if (!cpu_has_mipsmt) { 3381a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: not a MIPS MT capable processor\n"); 3391a2a6d7eSDeng-Cheng Zhu return -ENODEV; 3401a2a6d7eSDeng-Cheng Zhu } 3411a2a6d7eSDeng-Cheng Zhu 3421a2a6d7eSDeng-Cheng Zhu if (vpelimit == 0) { 3431a2a6d7eSDeng-Cheng Zhu pr_warn("No VPEs reserved for AP/SP, not initialize VPE loader\n" 3441a2a6d7eSDeng-Cheng Zhu "Pass maxvpes=<n> argument as kernel argument\n"); 3451a2a6d7eSDeng-Cheng Zhu 3461a2a6d7eSDeng-Cheng Zhu return -ENODEV; 3471a2a6d7eSDeng-Cheng Zhu } 3481a2a6d7eSDeng-Cheng Zhu 3491a2a6d7eSDeng-Cheng Zhu if (aprp_cpu_index() == 0) { 3501a2a6d7eSDeng-Cheng Zhu pr_warn("No TCs reserved for AP/SP, not initialize VPE loader\n" 3511a2a6d7eSDeng-Cheng Zhu "Pass maxtcs=<n> argument as kernel argument\n"); 3521a2a6d7eSDeng-Cheng Zhu 3531a2a6d7eSDeng-Cheng Zhu return -ENODEV; 3541a2a6d7eSDeng-Cheng Zhu } 3551a2a6d7eSDeng-Cheng Zhu 3561a2a6d7eSDeng-Cheng Zhu major = register_chrdev(0, VPE_MODULE_NAME, &vpe_fops); 3571a2a6d7eSDeng-Cheng Zhu if (major < 0) { 3581a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: unable to register character device\n"); 3591a2a6d7eSDeng-Cheng Zhu return major; 3601a2a6d7eSDeng-Cheng Zhu } 3611a2a6d7eSDeng-Cheng Zhu 3621a2a6d7eSDeng-Cheng Zhu err = class_register(&vpe_class); 3631a2a6d7eSDeng-Cheng Zhu if (err) { 3641a2a6d7eSDeng-Cheng Zhu pr_err("vpe_class registration failed\n"); 3651a2a6d7eSDeng-Cheng Zhu goto out_chrdev; 3661a2a6d7eSDeng-Cheng Zhu } 3671a2a6d7eSDeng-Cheng Zhu 3681a2a6d7eSDeng-Cheng Zhu device_initialize(&vpe_device); 3691a2a6d7eSDeng-Cheng Zhu vpe_device.class = &vpe_class, 3701a2a6d7eSDeng-Cheng Zhu vpe_device.parent = NULL, 3711a2a6d7eSDeng-Cheng Zhu dev_set_name(&vpe_device, "vpe1"); 3721a2a6d7eSDeng-Cheng Zhu vpe_device.devt = MKDEV(major, VPE_MODULE_MINOR); 3731a2a6d7eSDeng-Cheng Zhu err = device_add(&vpe_device); 3741a2a6d7eSDeng-Cheng Zhu if (err) { 3751a2a6d7eSDeng-Cheng Zhu pr_err("Adding vpe_device failed\n"); 3761a2a6d7eSDeng-Cheng Zhu goto out_class; 3771a2a6d7eSDeng-Cheng Zhu } 3781a2a6d7eSDeng-Cheng Zhu 3791a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 3801a2a6d7eSDeng-Cheng Zhu mtflags = dmt(); 3811a2a6d7eSDeng-Cheng Zhu vpflags = dvpe(); 3821a2a6d7eSDeng-Cheng Zhu 3831a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 3841a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 3851a2a6d7eSDeng-Cheng Zhu 3861a2a6d7eSDeng-Cheng Zhu val = read_c0_mvpconf0(); 3871a2a6d7eSDeng-Cheng Zhu hw_tcs = (val & MVPCONF0_PTC) + 1; 3881a2a6d7eSDeng-Cheng Zhu hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; 3891a2a6d7eSDeng-Cheng Zhu 3901a2a6d7eSDeng-Cheng Zhu for (tc = aprp_cpu_index(); tc < hw_tcs; tc++) { 3911a2a6d7eSDeng-Cheng Zhu /* 3921a2a6d7eSDeng-Cheng Zhu * Must re-enable multithreading temporarily or in case we 3931a2a6d7eSDeng-Cheng Zhu * reschedule send IPIs or similar we might hang. 3941a2a6d7eSDeng-Cheng Zhu */ 3951a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 3961a2a6d7eSDeng-Cheng Zhu evpe(vpflags); 3971a2a6d7eSDeng-Cheng Zhu emt(mtflags); 3981a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 3991a2a6d7eSDeng-Cheng Zhu t = alloc_tc(tc); 4001a2a6d7eSDeng-Cheng Zhu if (!t) { 4011a2a6d7eSDeng-Cheng Zhu err = -ENOMEM; 4021a2a6d7eSDeng-Cheng Zhu goto out_dev; 4031a2a6d7eSDeng-Cheng Zhu } 4041a2a6d7eSDeng-Cheng Zhu 4051a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 4061a2a6d7eSDeng-Cheng Zhu mtflags = dmt(); 4071a2a6d7eSDeng-Cheng Zhu vpflags = dvpe(); 4081a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 4091a2a6d7eSDeng-Cheng Zhu 4101a2a6d7eSDeng-Cheng Zhu /* VPE's */ 4111a2a6d7eSDeng-Cheng Zhu if (tc < hw_tcs) { 4121a2a6d7eSDeng-Cheng Zhu settc(tc); 4131a2a6d7eSDeng-Cheng Zhu 4141a2a6d7eSDeng-Cheng Zhu v = alloc_vpe(tc); 4151a2a6d7eSDeng-Cheng Zhu if (v == NULL) { 4161a2a6d7eSDeng-Cheng Zhu pr_warn("VPE: unable to allocate VPE\n"); 4171a2a6d7eSDeng-Cheng Zhu goto out_reenable; 4181a2a6d7eSDeng-Cheng Zhu } 4191a2a6d7eSDeng-Cheng Zhu 4201a2a6d7eSDeng-Cheng Zhu v->ntcs = hw_tcs - aprp_cpu_index(); 4211a2a6d7eSDeng-Cheng Zhu 4221a2a6d7eSDeng-Cheng Zhu /* add the tc to the list of this vpe's tc's. */ 4231a2a6d7eSDeng-Cheng Zhu list_add(&t->tc, &v->tc); 4241a2a6d7eSDeng-Cheng Zhu 4251a2a6d7eSDeng-Cheng Zhu /* deactivate all but vpe0 */ 4261a2a6d7eSDeng-Cheng Zhu if (tc >= aprp_cpu_index()) { 4271a2a6d7eSDeng-Cheng Zhu unsigned long tmp = read_vpe_c0_vpeconf0(); 4281a2a6d7eSDeng-Cheng Zhu 4291a2a6d7eSDeng-Cheng Zhu tmp &= ~VPECONF0_VPA; 4301a2a6d7eSDeng-Cheng Zhu 4311a2a6d7eSDeng-Cheng Zhu /* master VPE */ 4321a2a6d7eSDeng-Cheng Zhu tmp |= VPECONF0_MVP; 4331a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(tmp); 4341a2a6d7eSDeng-Cheng Zhu } 4351a2a6d7eSDeng-Cheng Zhu 4361a2a6d7eSDeng-Cheng Zhu /* disable multi-threading with TC's */ 4371a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & 4381a2a6d7eSDeng-Cheng Zhu ~VPECONTROL_TE); 4391a2a6d7eSDeng-Cheng Zhu 4401a2a6d7eSDeng-Cheng Zhu if (tc >= vpelimit) { 4411a2a6d7eSDeng-Cheng Zhu /* 4421a2a6d7eSDeng-Cheng Zhu * Set config to be the same as vpe0, 4431a2a6d7eSDeng-Cheng Zhu * particularly kseg0 coherency alg 4441a2a6d7eSDeng-Cheng Zhu */ 4451a2a6d7eSDeng-Cheng Zhu write_vpe_c0_config(read_c0_config()); 4461a2a6d7eSDeng-Cheng Zhu } 4471a2a6d7eSDeng-Cheng Zhu } 4481a2a6d7eSDeng-Cheng Zhu 4491a2a6d7eSDeng-Cheng Zhu /* TC's */ 4501a2a6d7eSDeng-Cheng Zhu t->pvpe = v; /* set the parent vpe */ 4511a2a6d7eSDeng-Cheng Zhu 4521a2a6d7eSDeng-Cheng Zhu if (tc >= aprp_cpu_index()) { 4531a2a6d7eSDeng-Cheng Zhu unsigned long tmp; 4541a2a6d7eSDeng-Cheng Zhu 4551a2a6d7eSDeng-Cheng Zhu settc(tc); 4561a2a6d7eSDeng-Cheng Zhu 4571a2a6d7eSDeng-Cheng Zhu /* Any TC that is bound to VPE0 gets left as is - in 4581a2a6d7eSDeng-Cheng Zhu * case we are running SMTC on VPE0. A TC that is bound 4591a2a6d7eSDeng-Cheng Zhu * to any other VPE gets bound to VPE0, ideally I'd like 4601a2a6d7eSDeng-Cheng Zhu * to make it homeless but it doesn't appear to let me 4611a2a6d7eSDeng-Cheng Zhu * bind a TC to a non-existent VPE. Which is perfectly 4621a2a6d7eSDeng-Cheng Zhu * reasonable. 4631a2a6d7eSDeng-Cheng Zhu * 4641a2a6d7eSDeng-Cheng Zhu * The (un)bound state is visible to an EJTAG probe so 4651a2a6d7eSDeng-Cheng Zhu * may notify GDB... 4661a2a6d7eSDeng-Cheng Zhu */ 4671a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcbind(); 4681a2a6d7eSDeng-Cheng Zhu if (tmp & TCBIND_CURVPE) { 4691a2a6d7eSDeng-Cheng Zhu /* tc is bound >vpe0 */ 4701a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE); 4711a2a6d7eSDeng-Cheng Zhu 4721a2a6d7eSDeng-Cheng Zhu t->pvpe = get_vpe(0); /* set the parent vpe */ 4731a2a6d7eSDeng-Cheng Zhu } 4741a2a6d7eSDeng-Cheng Zhu 4751a2a6d7eSDeng-Cheng Zhu /* halt the TC */ 4761a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H); 4771a2a6d7eSDeng-Cheng Zhu mips_ihb(); 4781a2a6d7eSDeng-Cheng Zhu 4791a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcstatus(); 4801a2a6d7eSDeng-Cheng Zhu 4811a2a6d7eSDeng-Cheng Zhu /* mark not activated and not dynamically allocatable */ 4821a2a6d7eSDeng-Cheng Zhu tmp &= ~(TCSTATUS_A | TCSTATUS_DA); 4831a2a6d7eSDeng-Cheng Zhu tmp |= TCSTATUS_IXMT; /* interrupt exempt */ 4841a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(tmp); 4851a2a6d7eSDeng-Cheng Zhu } 4861a2a6d7eSDeng-Cheng Zhu } 4871a2a6d7eSDeng-Cheng Zhu 4881a2a6d7eSDeng-Cheng Zhu out_reenable: 4891a2a6d7eSDeng-Cheng Zhu /* release config state */ 4901a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 4911a2a6d7eSDeng-Cheng Zhu 4921a2a6d7eSDeng-Cheng Zhu evpe(vpflags); 4931a2a6d7eSDeng-Cheng Zhu emt(mtflags); 4941a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 4951a2a6d7eSDeng-Cheng Zhu 4961a2a6d7eSDeng-Cheng Zhu return 0; 4971a2a6d7eSDeng-Cheng Zhu 4981a2a6d7eSDeng-Cheng Zhu out_dev: 4991a2a6d7eSDeng-Cheng Zhu device_del(&vpe_device); 5001a2a6d7eSDeng-Cheng Zhu 5011a2a6d7eSDeng-Cheng Zhu out_class: 5021a2a6d7eSDeng-Cheng Zhu class_unregister(&vpe_class); 5031a2a6d7eSDeng-Cheng Zhu 5041a2a6d7eSDeng-Cheng Zhu out_chrdev: 5051a2a6d7eSDeng-Cheng Zhu unregister_chrdev(major, VPE_MODULE_NAME); 5061a2a6d7eSDeng-Cheng Zhu 5071a2a6d7eSDeng-Cheng Zhu return err; 5081a2a6d7eSDeng-Cheng Zhu } 5091a2a6d7eSDeng-Cheng Zhu 5101a2a6d7eSDeng-Cheng Zhu void __exit vpe_module_exit(void) 5111a2a6d7eSDeng-Cheng Zhu { 5121a2a6d7eSDeng-Cheng Zhu struct vpe *v, *n; 5131a2a6d7eSDeng-Cheng Zhu 5141a2a6d7eSDeng-Cheng Zhu device_del(&vpe_device); 5151a2a6d7eSDeng-Cheng Zhu class_unregister(&vpe_class); 5161a2a6d7eSDeng-Cheng Zhu unregister_chrdev(major, VPE_MODULE_NAME); 5171a2a6d7eSDeng-Cheng Zhu 5181a2a6d7eSDeng-Cheng Zhu /* No locking needed here */ 5191a2a6d7eSDeng-Cheng Zhu list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) { 5201a2a6d7eSDeng-Cheng Zhu if (v->state != VPE_STATE_UNUSED) 5211a2a6d7eSDeng-Cheng Zhu release_vpe(v); 5221a2a6d7eSDeng-Cheng Zhu } 5231a2a6d7eSDeng-Cheng Zhu } 524