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; 295792bf64SSteven J. Hill struct vpe_notifications *notifier; 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 /* 95*91dc288fSRandy Dunlap * We don't pass the memsize here, so VPE programs need to be 96*91dc288fSRandy Dunlap * compiled with DFLT_STACK_SIZE and DFLT_HEAP_SIZE defined. 971a2a6d7eSDeng-Cheng Zhu */ 98*91dc288fSRandy Dunlap mttgpr(7, 0); 991a2a6d7eSDeng-Cheng Zhu mttgpr(6, v->ntcs); 1001a2a6d7eSDeng-Cheng Zhu 1011a2a6d7eSDeng-Cheng Zhu /* set up VPE1 */ 1021a2a6d7eSDeng-Cheng Zhu /* 1031a2a6d7eSDeng-Cheng Zhu * bind the TC to VPE 1 as late as possible so we only have the final 1041a2a6d7eSDeng-Cheng Zhu * VPE registers to set up, and so an EJTAG probe can trigger on it 1051a2a6d7eSDeng-Cheng Zhu */ 1061a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1); 1071a2a6d7eSDeng-Cheng Zhu 1081a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA)); 1091a2a6d7eSDeng-Cheng Zhu 1101a2a6d7eSDeng-Cheng Zhu back_to_back_c0_hazard(); 1111a2a6d7eSDeng-Cheng Zhu 1121a2a6d7eSDeng-Cheng Zhu /* Set up the XTC bit in vpeconf0 to point at our tc */ 1131a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC)) 1141a2a6d7eSDeng-Cheng Zhu | (t->index << VPECONF0_XTC_SHIFT)); 1151a2a6d7eSDeng-Cheng Zhu 1161a2a6d7eSDeng-Cheng Zhu back_to_back_c0_hazard(); 1171a2a6d7eSDeng-Cheng Zhu 1181a2a6d7eSDeng-Cheng Zhu /* enable this VPE */ 1191a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); 1201a2a6d7eSDeng-Cheng Zhu 1211a2a6d7eSDeng-Cheng Zhu /* clear out any left overs from a previous program */ 1221a2a6d7eSDeng-Cheng Zhu write_vpe_c0_status(0); 1231a2a6d7eSDeng-Cheng Zhu write_vpe_c0_cause(0); 1241a2a6d7eSDeng-Cheng Zhu 1251a2a6d7eSDeng-Cheng Zhu /* take system out of configuration state */ 1261a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 1271a2a6d7eSDeng-Cheng Zhu 1281a2a6d7eSDeng-Cheng Zhu /* 129b633648cSRalf Baechle * SMVP kernels manage VPE enable independently, but uniprocessor 130b633648cSRalf Baechle * kernels need to turn it on, even if that wasn't the pre-dvpe() state. 1311a2a6d7eSDeng-Cheng Zhu */ 1321a2a6d7eSDeng-Cheng Zhu #ifdef CONFIG_SMP 1331a2a6d7eSDeng-Cheng Zhu evpe(vpeflags); 1341a2a6d7eSDeng-Cheng Zhu #else 1351a2a6d7eSDeng-Cheng Zhu evpe(EVPE_ENABLE); 1361a2a6d7eSDeng-Cheng Zhu #endif 1371a2a6d7eSDeng-Cheng Zhu emt(dmt_flag); 1381a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 1391a2a6d7eSDeng-Cheng Zhu 1405792bf64SSteven J. Hill list_for_each_entry(notifier, &v->notify, list) 1415792bf64SSteven J. Hill notifier->start(VPE_MODULE_MINOR); 1421a2a6d7eSDeng-Cheng Zhu 1431a2a6d7eSDeng-Cheng Zhu return 0; 1441a2a6d7eSDeng-Cheng Zhu } 1451a2a6d7eSDeng-Cheng Zhu 1461a2a6d7eSDeng-Cheng Zhu void cleanup_tc(struct tc *tc) 1471a2a6d7eSDeng-Cheng Zhu { 1481a2a6d7eSDeng-Cheng Zhu unsigned long flags; 1491a2a6d7eSDeng-Cheng Zhu unsigned int mtflags, vpflags; 1501a2a6d7eSDeng-Cheng Zhu int tmp; 1511a2a6d7eSDeng-Cheng Zhu 1521a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 1531a2a6d7eSDeng-Cheng Zhu mtflags = dmt(); 1541a2a6d7eSDeng-Cheng Zhu vpflags = dvpe(); 1551a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 1561a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 1571a2a6d7eSDeng-Cheng Zhu 1581a2a6d7eSDeng-Cheng Zhu settc(tc->index); 1591a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcstatus(); 1601a2a6d7eSDeng-Cheng Zhu 1611a2a6d7eSDeng-Cheng Zhu /* mark not allocated and not dynamically allocatable */ 1621a2a6d7eSDeng-Cheng Zhu tmp &= ~(TCSTATUS_A | TCSTATUS_DA); 1631a2a6d7eSDeng-Cheng Zhu tmp |= TCSTATUS_IXMT; /* interrupt exempt */ 1641a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(tmp); 1651a2a6d7eSDeng-Cheng Zhu 1661a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H); 1671a2a6d7eSDeng-Cheng Zhu mips_ihb(); 1681a2a6d7eSDeng-Cheng Zhu 1691a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 1701a2a6d7eSDeng-Cheng Zhu evpe(vpflags); 1711a2a6d7eSDeng-Cheng Zhu emt(mtflags); 1721a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 1731a2a6d7eSDeng-Cheng Zhu } 1741a2a6d7eSDeng-Cheng Zhu 1751a2a6d7eSDeng-Cheng Zhu /* module wrapper entry points */ 1761a2a6d7eSDeng-Cheng Zhu /* give me a vpe */ 1771a2a6d7eSDeng-Cheng Zhu void *vpe_alloc(void) 1781a2a6d7eSDeng-Cheng Zhu { 1791a2a6d7eSDeng-Cheng Zhu int i; 1801a2a6d7eSDeng-Cheng Zhu struct vpe *v; 1811a2a6d7eSDeng-Cheng Zhu 1821a2a6d7eSDeng-Cheng Zhu /* find a vpe */ 1831a2a6d7eSDeng-Cheng Zhu for (i = 1; i < MAX_VPES; i++) { 1841a2a6d7eSDeng-Cheng Zhu v = get_vpe(i); 1851a2a6d7eSDeng-Cheng Zhu if (v != NULL) { 1861a2a6d7eSDeng-Cheng Zhu v->state = VPE_STATE_INUSE; 1871a2a6d7eSDeng-Cheng Zhu return v; 1881a2a6d7eSDeng-Cheng Zhu } 1891a2a6d7eSDeng-Cheng Zhu } 1901a2a6d7eSDeng-Cheng Zhu return NULL; 1911a2a6d7eSDeng-Cheng Zhu } 1921a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_alloc); 1931a2a6d7eSDeng-Cheng Zhu 1941a2a6d7eSDeng-Cheng Zhu /* start running from here */ 1951a2a6d7eSDeng-Cheng Zhu int vpe_start(void *vpe, unsigned long start) 1961a2a6d7eSDeng-Cheng Zhu { 1971a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe; 1981a2a6d7eSDeng-Cheng Zhu 1991a2a6d7eSDeng-Cheng Zhu v->__start = start; 2001a2a6d7eSDeng-Cheng Zhu return vpe_run(v); 2011a2a6d7eSDeng-Cheng Zhu } 2021a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_start); 2031a2a6d7eSDeng-Cheng Zhu 2041a2a6d7eSDeng-Cheng Zhu /* halt it for now */ 2051a2a6d7eSDeng-Cheng Zhu int vpe_stop(void *vpe) 2061a2a6d7eSDeng-Cheng Zhu { 2071a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe; 2081a2a6d7eSDeng-Cheng Zhu struct tc *t; 2091a2a6d7eSDeng-Cheng Zhu unsigned int evpe_flags; 2101a2a6d7eSDeng-Cheng Zhu 2111a2a6d7eSDeng-Cheng Zhu evpe_flags = dvpe(); 2121a2a6d7eSDeng-Cheng Zhu 2131a2a6d7eSDeng-Cheng Zhu t = list_entry(v->tc.next, struct tc, tc); 2141a2a6d7eSDeng-Cheng Zhu if (t != NULL) { 2151a2a6d7eSDeng-Cheng Zhu settc(t->index); 2161a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA); 2171a2a6d7eSDeng-Cheng Zhu } 2181a2a6d7eSDeng-Cheng Zhu 2191a2a6d7eSDeng-Cheng Zhu evpe(evpe_flags); 2201a2a6d7eSDeng-Cheng Zhu 2211a2a6d7eSDeng-Cheng Zhu return 0; 2221a2a6d7eSDeng-Cheng Zhu } 2231a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_stop); 2241a2a6d7eSDeng-Cheng Zhu 2251a2a6d7eSDeng-Cheng Zhu /* I've done with it thank you */ 2261a2a6d7eSDeng-Cheng Zhu int vpe_free(void *vpe) 2271a2a6d7eSDeng-Cheng Zhu { 2281a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe; 2291a2a6d7eSDeng-Cheng Zhu struct tc *t; 2301a2a6d7eSDeng-Cheng Zhu unsigned int evpe_flags; 2311a2a6d7eSDeng-Cheng Zhu 2321a2a6d7eSDeng-Cheng Zhu t = list_entry(v->tc.next, struct tc, tc); 2331a2a6d7eSDeng-Cheng Zhu if (t == NULL) 2341a2a6d7eSDeng-Cheng Zhu return -ENOEXEC; 2351a2a6d7eSDeng-Cheng Zhu 2361a2a6d7eSDeng-Cheng Zhu evpe_flags = dvpe(); 2371a2a6d7eSDeng-Cheng Zhu 2381a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 2391a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 2401a2a6d7eSDeng-Cheng Zhu 2411a2a6d7eSDeng-Cheng Zhu settc(t->index); 2421a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA); 2431a2a6d7eSDeng-Cheng Zhu 2441a2a6d7eSDeng-Cheng Zhu /* halt the TC */ 2451a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H); 2461a2a6d7eSDeng-Cheng Zhu mips_ihb(); 2471a2a6d7eSDeng-Cheng Zhu 2481a2a6d7eSDeng-Cheng Zhu /* mark the TC unallocated */ 2491a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A); 2501a2a6d7eSDeng-Cheng Zhu 2511a2a6d7eSDeng-Cheng Zhu v->state = VPE_STATE_UNUSED; 2521a2a6d7eSDeng-Cheng Zhu 2531a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 2541a2a6d7eSDeng-Cheng Zhu evpe(evpe_flags); 2551a2a6d7eSDeng-Cheng Zhu 2561a2a6d7eSDeng-Cheng Zhu return 0; 2571a2a6d7eSDeng-Cheng Zhu } 2581a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_free); 2591a2a6d7eSDeng-Cheng Zhu 2601a2a6d7eSDeng-Cheng Zhu static ssize_t store_kill(struct device *dev, struct device_attribute *attr, 2611a2a6d7eSDeng-Cheng Zhu const char *buf, size_t len) 2621a2a6d7eSDeng-Cheng Zhu { 2631a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index()); 2641a2a6d7eSDeng-Cheng Zhu struct vpe_notifications *notifier; 2651a2a6d7eSDeng-Cheng Zhu 2661a2a6d7eSDeng-Cheng Zhu list_for_each_entry(notifier, &vpe->notify, list) 2671a2a6d7eSDeng-Cheng Zhu notifier->stop(aprp_cpu_index()); 2681a2a6d7eSDeng-Cheng Zhu 2691a2a6d7eSDeng-Cheng Zhu release_progmem(vpe->load_addr); 2701a2a6d7eSDeng-Cheng Zhu cleanup_tc(get_tc(aprp_cpu_index())); 2711a2a6d7eSDeng-Cheng Zhu vpe_stop(vpe); 2721a2a6d7eSDeng-Cheng Zhu vpe_free(vpe); 2731a2a6d7eSDeng-Cheng Zhu 2741a2a6d7eSDeng-Cheng Zhu return len; 2751a2a6d7eSDeng-Cheng Zhu } 2761a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR(kill, S_IWUSR, NULL, store_kill); 2771a2a6d7eSDeng-Cheng Zhu 2781a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_show(struct device *cd, struct device_attribute *attr, 2791a2a6d7eSDeng-Cheng Zhu char *buf) 2801a2a6d7eSDeng-Cheng Zhu { 2811a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index()); 2821a2a6d7eSDeng-Cheng Zhu 2831a2a6d7eSDeng-Cheng Zhu return sprintf(buf, "%d\n", vpe->ntcs); 2841a2a6d7eSDeng-Cheng Zhu } 2851a2a6d7eSDeng-Cheng Zhu 2861a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_store(struct device *dev, struct device_attribute *attr, 2871a2a6d7eSDeng-Cheng Zhu const char *buf, size_t len) 2881a2a6d7eSDeng-Cheng Zhu { 2891a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index()); 2901a2a6d7eSDeng-Cheng Zhu unsigned long new; 2911a2a6d7eSDeng-Cheng Zhu int ret; 2921a2a6d7eSDeng-Cheng Zhu 2931a2a6d7eSDeng-Cheng Zhu ret = kstrtoul(buf, 0, &new); 2941a2a6d7eSDeng-Cheng Zhu if (ret < 0) 2951a2a6d7eSDeng-Cheng Zhu return ret; 2961a2a6d7eSDeng-Cheng Zhu 2971a2a6d7eSDeng-Cheng Zhu if (new == 0 || new > (hw_tcs - aprp_cpu_index())) 2981a2a6d7eSDeng-Cheng Zhu return -EINVAL; 2991a2a6d7eSDeng-Cheng Zhu 3001a2a6d7eSDeng-Cheng Zhu vpe->ntcs = new; 3011a2a6d7eSDeng-Cheng Zhu 3021a2a6d7eSDeng-Cheng Zhu return len; 3031a2a6d7eSDeng-Cheng Zhu } 3041a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR_RW(ntcs); 3051a2a6d7eSDeng-Cheng Zhu 3061a2a6d7eSDeng-Cheng Zhu static struct attribute *vpe_attrs[] = { 3071a2a6d7eSDeng-Cheng Zhu &dev_attr_kill.attr, 3081a2a6d7eSDeng-Cheng Zhu &dev_attr_ntcs.attr, 3091a2a6d7eSDeng-Cheng Zhu NULL, 3101a2a6d7eSDeng-Cheng Zhu }; 3111a2a6d7eSDeng-Cheng Zhu ATTRIBUTE_GROUPS(vpe); 3121a2a6d7eSDeng-Cheng Zhu 3131a2a6d7eSDeng-Cheng Zhu static void vpe_device_release(struct device *cd) 3141a2a6d7eSDeng-Cheng Zhu { 3151a2a6d7eSDeng-Cheng Zhu } 3161a2a6d7eSDeng-Cheng Zhu 3171a2a6d7eSDeng-Cheng Zhu static struct class vpe_class = { 3181a2a6d7eSDeng-Cheng Zhu .name = "vpe", 3191a2a6d7eSDeng-Cheng Zhu .owner = THIS_MODULE, 3201a2a6d7eSDeng-Cheng Zhu .dev_release = vpe_device_release, 3211a2a6d7eSDeng-Cheng Zhu .dev_groups = vpe_groups, 3221a2a6d7eSDeng-Cheng Zhu }; 3231a2a6d7eSDeng-Cheng Zhu 3241a2a6d7eSDeng-Cheng Zhu static struct device vpe_device; 3251a2a6d7eSDeng-Cheng Zhu 3261a2a6d7eSDeng-Cheng Zhu int __init vpe_module_init(void) 3271a2a6d7eSDeng-Cheng Zhu { 3281a2a6d7eSDeng-Cheng Zhu unsigned int mtflags, vpflags; 3291a2a6d7eSDeng-Cheng Zhu unsigned long flags, val; 3301a2a6d7eSDeng-Cheng Zhu struct vpe *v = NULL; 3311a2a6d7eSDeng-Cheng Zhu struct tc *t; 3321a2a6d7eSDeng-Cheng Zhu int tc, err; 3331a2a6d7eSDeng-Cheng Zhu 3341a2a6d7eSDeng-Cheng Zhu if (!cpu_has_mipsmt) { 3351a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: not a MIPS MT capable processor\n"); 3361a2a6d7eSDeng-Cheng Zhu return -ENODEV; 3371a2a6d7eSDeng-Cheng Zhu } 3381a2a6d7eSDeng-Cheng Zhu 3391a2a6d7eSDeng-Cheng Zhu if (vpelimit == 0) { 3401a2a6d7eSDeng-Cheng Zhu pr_warn("No VPEs reserved for AP/SP, not initialize VPE loader\n" 3411a2a6d7eSDeng-Cheng Zhu "Pass maxvpes=<n> argument as kernel argument\n"); 3421a2a6d7eSDeng-Cheng Zhu 3431a2a6d7eSDeng-Cheng Zhu return -ENODEV; 3441a2a6d7eSDeng-Cheng Zhu } 3451a2a6d7eSDeng-Cheng Zhu 3461a2a6d7eSDeng-Cheng Zhu if (aprp_cpu_index() == 0) { 3471a2a6d7eSDeng-Cheng Zhu pr_warn("No TCs reserved for AP/SP, not initialize VPE loader\n" 3481a2a6d7eSDeng-Cheng Zhu "Pass maxtcs=<n> argument as kernel argument\n"); 3491a2a6d7eSDeng-Cheng Zhu 3501a2a6d7eSDeng-Cheng Zhu return -ENODEV; 3511a2a6d7eSDeng-Cheng Zhu } 3521a2a6d7eSDeng-Cheng Zhu 3531a2a6d7eSDeng-Cheng Zhu major = register_chrdev(0, VPE_MODULE_NAME, &vpe_fops); 3541a2a6d7eSDeng-Cheng Zhu if (major < 0) { 3551a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: unable to register character device\n"); 3561a2a6d7eSDeng-Cheng Zhu return major; 3571a2a6d7eSDeng-Cheng Zhu } 3581a2a6d7eSDeng-Cheng Zhu 3591a2a6d7eSDeng-Cheng Zhu err = class_register(&vpe_class); 3601a2a6d7eSDeng-Cheng Zhu if (err) { 3611a2a6d7eSDeng-Cheng Zhu pr_err("vpe_class registration failed\n"); 3621a2a6d7eSDeng-Cheng Zhu goto out_chrdev; 3631a2a6d7eSDeng-Cheng Zhu } 3641a2a6d7eSDeng-Cheng Zhu 3651a2a6d7eSDeng-Cheng Zhu device_initialize(&vpe_device); 3663a845b30SZheng Yongjun vpe_device.class = &vpe_class; 3673a845b30SZheng Yongjun vpe_device.parent = NULL; 3681a2a6d7eSDeng-Cheng Zhu dev_set_name(&vpe_device, "vpe1"); 3691a2a6d7eSDeng-Cheng Zhu vpe_device.devt = MKDEV(major, VPE_MODULE_MINOR); 3701a2a6d7eSDeng-Cheng Zhu err = device_add(&vpe_device); 3711a2a6d7eSDeng-Cheng Zhu if (err) { 3721a2a6d7eSDeng-Cheng Zhu pr_err("Adding vpe_device failed\n"); 3731a2a6d7eSDeng-Cheng Zhu goto out_class; 3741a2a6d7eSDeng-Cheng Zhu } 3751a2a6d7eSDeng-Cheng Zhu 3761a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 3771a2a6d7eSDeng-Cheng Zhu mtflags = dmt(); 3781a2a6d7eSDeng-Cheng Zhu vpflags = dvpe(); 3791a2a6d7eSDeng-Cheng Zhu 3801a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */ 3811a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 3821a2a6d7eSDeng-Cheng Zhu 3831a2a6d7eSDeng-Cheng Zhu val = read_c0_mvpconf0(); 3841a2a6d7eSDeng-Cheng Zhu hw_tcs = (val & MVPCONF0_PTC) + 1; 3851a2a6d7eSDeng-Cheng Zhu hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; 3861a2a6d7eSDeng-Cheng Zhu 3871a2a6d7eSDeng-Cheng Zhu for (tc = aprp_cpu_index(); tc < hw_tcs; tc++) { 3881a2a6d7eSDeng-Cheng Zhu /* 3891a2a6d7eSDeng-Cheng Zhu * Must re-enable multithreading temporarily or in case we 3901a2a6d7eSDeng-Cheng Zhu * reschedule send IPIs or similar we might hang. 3911a2a6d7eSDeng-Cheng Zhu */ 3921a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 3931a2a6d7eSDeng-Cheng Zhu evpe(vpflags); 3941a2a6d7eSDeng-Cheng Zhu emt(mtflags); 3951a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 3961a2a6d7eSDeng-Cheng Zhu t = alloc_tc(tc); 3971a2a6d7eSDeng-Cheng Zhu if (!t) { 3981a2a6d7eSDeng-Cheng Zhu err = -ENOMEM; 3991a2a6d7eSDeng-Cheng Zhu goto out_dev; 4001a2a6d7eSDeng-Cheng Zhu } 4011a2a6d7eSDeng-Cheng Zhu 4021a2a6d7eSDeng-Cheng Zhu local_irq_save(flags); 4031a2a6d7eSDeng-Cheng Zhu mtflags = dmt(); 4041a2a6d7eSDeng-Cheng Zhu vpflags = dvpe(); 4051a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC); 4061a2a6d7eSDeng-Cheng Zhu 4071a2a6d7eSDeng-Cheng Zhu /* VPE's */ 4081a2a6d7eSDeng-Cheng Zhu if (tc < hw_tcs) { 4091a2a6d7eSDeng-Cheng Zhu settc(tc); 4101a2a6d7eSDeng-Cheng Zhu 4111a2a6d7eSDeng-Cheng Zhu v = alloc_vpe(tc); 4121a2a6d7eSDeng-Cheng Zhu if (v == NULL) { 4131a2a6d7eSDeng-Cheng Zhu pr_warn("VPE: unable to allocate VPE\n"); 4141a2a6d7eSDeng-Cheng Zhu goto out_reenable; 4151a2a6d7eSDeng-Cheng Zhu } 4161a2a6d7eSDeng-Cheng Zhu 4171a2a6d7eSDeng-Cheng Zhu v->ntcs = hw_tcs - aprp_cpu_index(); 4181a2a6d7eSDeng-Cheng Zhu 4191a2a6d7eSDeng-Cheng Zhu /* add the tc to the list of this vpe's tc's. */ 4201a2a6d7eSDeng-Cheng Zhu list_add(&t->tc, &v->tc); 4211a2a6d7eSDeng-Cheng Zhu 4221a2a6d7eSDeng-Cheng Zhu /* deactivate all but vpe0 */ 4231a2a6d7eSDeng-Cheng Zhu if (tc >= aprp_cpu_index()) { 4241a2a6d7eSDeng-Cheng Zhu unsigned long tmp = read_vpe_c0_vpeconf0(); 4251a2a6d7eSDeng-Cheng Zhu 4261a2a6d7eSDeng-Cheng Zhu tmp &= ~VPECONF0_VPA; 4271a2a6d7eSDeng-Cheng Zhu 4281a2a6d7eSDeng-Cheng Zhu /* master VPE */ 4291a2a6d7eSDeng-Cheng Zhu tmp |= VPECONF0_MVP; 4301a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(tmp); 4311a2a6d7eSDeng-Cheng Zhu } 4321a2a6d7eSDeng-Cheng Zhu 4331a2a6d7eSDeng-Cheng Zhu /* disable multi-threading with TC's */ 4341a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & 4351a2a6d7eSDeng-Cheng Zhu ~VPECONTROL_TE); 4361a2a6d7eSDeng-Cheng Zhu 4371a2a6d7eSDeng-Cheng Zhu if (tc >= vpelimit) { 4381a2a6d7eSDeng-Cheng Zhu /* 4391a2a6d7eSDeng-Cheng Zhu * Set config to be the same as vpe0, 4401a2a6d7eSDeng-Cheng Zhu * particularly kseg0 coherency alg 4411a2a6d7eSDeng-Cheng Zhu */ 4421a2a6d7eSDeng-Cheng Zhu write_vpe_c0_config(read_c0_config()); 4431a2a6d7eSDeng-Cheng Zhu } 4441a2a6d7eSDeng-Cheng Zhu } 4451a2a6d7eSDeng-Cheng Zhu 4461a2a6d7eSDeng-Cheng Zhu /* TC's */ 4471a2a6d7eSDeng-Cheng Zhu t->pvpe = v; /* set the parent vpe */ 4481a2a6d7eSDeng-Cheng Zhu 4491a2a6d7eSDeng-Cheng Zhu if (tc >= aprp_cpu_index()) { 4501a2a6d7eSDeng-Cheng Zhu unsigned long tmp; 4511a2a6d7eSDeng-Cheng Zhu 4521a2a6d7eSDeng-Cheng Zhu settc(tc); 4531a2a6d7eSDeng-Cheng Zhu 454b633648cSRalf Baechle /* 455b633648cSRalf Baechle * A TC that is bound to any other VPE gets bound to 456b633648cSRalf Baechle * VPE0, ideally I'd like to make it homeless but it 457b633648cSRalf Baechle * doesn't appear to let me bind a TC to a non-existent 458b633648cSRalf Baechle * VPE. Which is perfectly reasonable. 4591a2a6d7eSDeng-Cheng Zhu * 4601a2a6d7eSDeng-Cheng Zhu * The (un)bound state is visible to an EJTAG probe so 4611a2a6d7eSDeng-Cheng Zhu * may notify GDB... 4621a2a6d7eSDeng-Cheng Zhu */ 4631a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcbind(); 4641a2a6d7eSDeng-Cheng Zhu if (tmp & TCBIND_CURVPE) { 4651a2a6d7eSDeng-Cheng Zhu /* tc is bound >vpe0 */ 4661a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE); 4671a2a6d7eSDeng-Cheng Zhu 4681a2a6d7eSDeng-Cheng Zhu t->pvpe = get_vpe(0); /* set the parent vpe */ 4691a2a6d7eSDeng-Cheng Zhu } 4701a2a6d7eSDeng-Cheng Zhu 4711a2a6d7eSDeng-Cheng Zhu /* halt the TC */ 4721a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H); 4731a2a6d7eSDeng-Cheng Zhu mips_ihb(); 4741a2a6d7eSDeng-Cheng Zhu 4751a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcstatus(); 4761a2a6d7eSDeng-Cheng Zhu 4771a2a6d7eSDeng-Cheng Zhu /* mark not activated and not dynamically allocatable */ 4781a2a6d7eSDeng-Cheng Zhu tmp &= ~(TCSTATUS_A | TCSTATUS_DA); 4791a2a6d7eSDeng-Cheng Zhu tmp |= TCSTATUS_IXMT; /* interrupt exempt */ 4801a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(tmp); 4811a2a6d7eSDeng-Cheng Zhu } 4821a2a6d7eSDeng-Cheng Zhu } 4831a2a6d7eSDeng-Cheng Zhu 4841a2a6d7eSDeng-Cheng Zhu out_reenable: 4851a2a6d7eSDeng-Cheng Zhu /* release config state */ 4861a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC); 4871a2a6d7eSDeng-Cheng Zhu 4881a2a6d7eSDeng-Cheng Zhu evpe(vpflags); 4891a2a6d7eSDeng-Cheng Zhu emt(mtflags); 4901a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags); 4911a2a6d7eSDeng-Cheng Zhu 4921a2a6d7eSDeng-Cheng Zhu return 0; 4931a2a6d7eSDeng-Cheng Zhu 4941a2a6d7eSDeng-Cheng Zhu out_dev: 4951a2a6d7eSDeng-Cheng Zhu device_del(&vpe_device); 4961a2a6d7eSDeng-Cheng Zhu 4971a2a6d7eSDeng-Cheng Zhu out_class: 4985822e8ccSYang Yingliang put_device(&vpe_device); 4991a2a6d7eSDeng-Cheng Zhu class_unregister(&vpe_class); 5001a2a6d7eSDeng-Cheng Zhu 5011a2a6d7eSDeng-Cheng Zhu out_chrdev: 5021a2a6d7eSDeng-Cheng Zhu unregister_chrdev(major, VPE_MODULE_NAME); 5031a2a6d7eSDeng-Cheng Zhu 5041a2a6d7eSDeng-Cheng Zhu return err; 5051a2a6d7eSDeng-Cheng Zhu } 5061a2a6d7eSDeng-Cheng Zhu 5071a2a6d7eSDeng-Cheng Zhu void __exit vpe_module_exit(void) 5081a2a6d7eSDeng-Cheng Zhu { 5091a2a6d7eSDeng-Cheng Zhu struct vpe *v, *n; 5101a2a6d7eSDeng-Cheng Zhu 5115822e8ccSYang Yingliang device_unregister(&vpe_device); 5121a2a6d7eSDeng-Cheng Zhu class_unregister(&vpe_class); 5131a2a6d7eSDeng-Cheng Zhu unregister_chrdev(major, VPE_MODULE_NAME); 5141a2a6d7eSDeng-Cheng Zhu 5151a2a6d7eSDeng-Cheng Zhu /* No locking needed here */ 5161a2a6d7eSDeng-Cheng Zhu list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) { 5171a2a6d7eSDeng-Cheng Zhu if (v->state != VPE_STATE_UNUSED) 5181a2a6d7eSDeng-Cheng Zhu release_vpe(v); 5191a2a6d7eSDeng-Cheng Zhu } 5201a2a6d7eSDeng-Cheng Zhu } 521