18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Low-level SPU handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Arnd Bergmann <arndb@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#undef DEBUG 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/wait.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/linux_logo.h> 228c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 238c2ecf20Sopenharmony_ci#include <asm/spu.h> 248c2ecf20Sopenharmony_ci#include <asm/spu_priv1.h> 258c2ecf20Sopenharmony_ci#include <asm/spu_csa.h> 268c2ecf20Sopenharmony_ci#include <asm/xmon.h> 278c2ecf20Sopenharmony_ci#include <asm/prom.h> 288c2ecf20Sopenharmony_ci#include <asm/kexec.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciconst struct spu_management_ops *spu_management_ops; 318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_management_ops); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciconst struct spu_priv1_ops *spu_priv1_ops; 348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_priv1_ops); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct cbe_spu_info cbe_spu_info[MAX_NUMNODES]; 378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_spu_info); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * The spufs fault-handling code needs to call force_sig_fault to raise signals 418c2ecf20Sopenharmony_ci * on DMA errors. Export it here to avoid general kernel-wide access to this 428c2ecf20Sopenharmony_ci * function 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(force_sig_fault); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Protects cbe_spu_info and spu->number. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(spu_lock); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * List of all spus in the system. 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * This list is iterated by callers from irq context and callers that 558c2ecf20Sopenharmony_ci * want to sleep. Thus modifications need to be done with both 568c2ecf20Sopenharmony_ci * spu_full_list_lock and spu_full_list_mutex held, while iterating 578c2ecf20Sopenharmony_ci * through it requires either of these locks. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * In addition spu_full_list_lock protects all assignments to 608c2ecf20Sopenharmony_ci * spu->mm. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic LIST_HEAD(spu_full_list); 638c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(spu_full_list_lock); 648c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(spu_full_list_mutex); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_civoid spu_invalidate_slbs(struct spu *spu) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct spu_priv2 __iomem *priv2 = spu->priv2; 698c2ecf20Sopenharmony_ci unsigned long flags; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci spin_lock_irqsave(&spu->register_lock, flags); 728c2ecf20Sopenharmony_ci if (spu_mfc_sr1_get(spu) & MFC_STATE1_RELOCATE_MASK) 738c2ecf20Sopenharmony_ci out_be64(&priv2->slb_invalidate_all_W, 0UL); 748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spu->register_lock, flags); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_invalidate_slbs); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* This is called by the MM core when a segment size is changed, to 798c2ecf20Sopenharmony_ci * request a flush of all the SPEs using a given mm 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_civoid spu_flush_all_slbs(struct mm_struct *mm) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct spu *spu; 848c2ecf20Sopenharmony_ci unsigned long flags; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci spin_lock_irqsave(&spu_full_list_lock, flags); 878c2ecf20Sopenharmony_ci list_for_each_entry(spu, &spu_full_list, full_list) { 888c2ecf20Sopenharmony_ci if (spu->mm == mm) 898c2ecf20Sopenharmony_ci spu_invalidate_slbs(spu); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spu_full_list_lock, flags); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* The hack below stinks... try to do something better one of 958c2ecf20Sopenharmony_ci * these days... Does it even work properly with NR_CPUS == 1 ? 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic inline void mm_needs_global_tlbie(struct mm_struct *mm) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int nr = (NR_CPUS > 1) ? NR_CPUS : NR_CPUS + 1; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Global TLBIE broadcast required with SPEs. */ 1028c2ecf20Sopenharmony_ci bitmap_fill(cpumask_bits(mm_cpumask(mm)), nr); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_civoid spu_associate_mm(struct spu *spu, struct mm_struct *mm) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci unsigned long flags; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci spin_lock_irqsave(&spu_full_list_lock, flags); 1108c2ecf20Sopenharmony_ci spu->mm = mm; 1118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spu_full_list_lock, flags); 1128c2ecf20Sopenharmony_ci if (mm) 1138c2ecf20Sopenharmony_ci mm_needs_global_tlbie(mm); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_associate_mm); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciint spu_64k_pages_available(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci return mmu_psize_defs[MMU_PAGE_64K].shift != 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_64k_pages_available); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void spu_restart_dma(struct spu *spu) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct spu_priv2 __iomem *priv2 = spu->priv2; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags)) 1288c2ecf20Sopenharmony_ci out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); 1298c2ecf20Sopenharmony_ci else { 1308c2ecf20Sopenharmony_ci set_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags); 1318c2ecf20Sopenharmony_ci mb(); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic inline void spu_load_slb(struct spu *spu, int slbe, struct copro_slb *slb) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct spu_priv2 __iomem *priv2 = spu->priv2; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci pr_debug("%s: adding SLB[%d] 0x%016llx 0x%016llx\n", 1408c2ecf20Sopenharmony_ci __func__, slbe, slb->vsid, slb->esid); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci out_be64(&priv2->slb_index_W, slbe); 1438c2ecf20Sopenharmony_ci /* set invalid before writing vsid */ 1448c2ecf20Sopenharmony_ci out_be64(&priv2->slb_esid_RW, 0); 1458c2ecf20Sopenharmony_ci /* now it's safe to write the vsid */ 1468c2ecf20Sopenharmony_ci out_be64(&priv2->slb_vsid_RW, slb->vsid); 1478c2ecf20Sopenharmony_ci /* setting the new esid makes the entry valid again */ 1488c2ecf20Sopenharmony_ci out_be64(&priv2->slb_esid_RW, slb->esid); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int __spu_trap_data_seg(struct spu *spu, unsigned long ea) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct copro_slb slb; 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = copro_calculate_slb(spu->mm, ea, &slb); 1578c2ecf20Sopenharmony_ci if (ret) 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci spu_load_slb(spu, spu->slb_replace, &slb); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spu->slb_replace++; 1638c2ecf20Sopenharmony_ci if (spu->slb_replace >= 8) 1648c2ecf20Sopenharmony_ci spu->slb_replace = 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spu_restart_dma(spu); 1678c2ecf20Sopenharmony_ci spu->stats.slb_flt++; 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciextern int hash_page(unsigned long ea, unsigned long access, 1728c2ecf20Sopenharmony_ci unsigned long trap, unsigned long dsisr); //XXX 1738c2ecf20Sopenharmony_cistatic int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci pr_debug("%s, %llx, %lx\n", __func__, dsisr, ea); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * Handle kernel space hash faults immediately. User hash 1818c2ecf20Sopenharmony_ci * faults need to be deferred to process context. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci if ((dsisr & MFC_DSISR_PTE_NOT_FOUND) && 1848c2ecf20Sopenharmony_ci (get_region_id(ea) != USER_REGION_ID)) { 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci spin_unlock(&spu->register_lock); 1878c2ecf20Sopenharmony_ci ret = hash_page(ea, 1888c2ecf20Sopenharmony_ci _PAGE_PRESENT | _PAGE_READ | _PAGE_PRIVILEGED, 1898c2ecf20Sopenharmony_ci 0x300, dsisr); 1908c2ecf20Sopenharmony_ci spin_lock(&spu->register_lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (!ret) { 1938c2ecf20Sopenharmony_ci spu_restart_dma(spu); 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci spu->class_1_dar = ea; 1998c2ecf20Sopenharmony_ci spu->class_1_dsisr = dsisr; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci spu->stop_callback(spu, 1); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci spu->class_1_dar = 0; 2048c2ecf20Sopenharmony_ci spu->class_1_dsisr = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void __spu_kernel_slb(void *addr, struct copro_slb *slb) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci unsigned long ea = (unsigned long)addr; 2128c2ecf20Sopenharmony_ci u64 llp; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (get_region_id(ea) == LINEAR_MAP_REGION_ID) 2158c2ecf20Sopenharmony_ci llp = mmu_psize_defs[mmu_linear_psize].sllp; 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci llp = mmu_psize_defs[mmu_virtual_psize].sllp; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci slb->vsid = (get_kernel_vsid(ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | 2208c2ecf20Sopenharmony_ci SLB_VSID_KERNEL | llp; 2218c2ecf20Sopenharmony_ci slb->esid = (ea & ESID_MASK) | SLB_ESID_V; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * Given an array of @nr_slbs SLB entries, @slbs, return non-zero if the 2268c2ecf20Sopenharmony_ci * address @new_addr is present. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_cistatic inline int __slb_present(struct copro_slb *slbs, int nr_slbs, 2298c2ecf20Sopenharmony_ci void *new_addr) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned long ea = (unsigned long)new_addr; 2328c2ecf20Sopenharmony_ci int i; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < nr_slbs; i++) 2358c2ecf20Sopenharmony_ci if (!((slbs[i].esid ^ ea) & ESID_MASK)) 2368c2ecf20Sopenharmony_ci return 1; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/** 2428c2ecf20Sopenharmony_ci * Setup the SPU kernel SLBs, in preparation for a context save/restore. We 2438c2ecf20Sopenharmony_ci * need to map both the context save area, and the save/restore code. 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * Because the lscsa and code may cross segment boundaries, we check to see 2468c2ecf20Sopenharmony_ci * if mappings are required for the start and end of each range. We currently 2478c2ecf20Sopenharmony_ci * assume that the mappings are smaller that one segment - if not, something 2488c2ecf20Sopenharmony_ci * is seriously wrong. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_civoid spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa, 2518c2ecf20Sopenharmony_ci void *code, int code_size) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct copro_slb slbs[4]; 2548c2ecf20Sopenharmony_ci int i, nr_slbs = 0; 2558c2ecf20Sopenharmony_ci /* start and end addresses of both mappings */ 2568c2ecf20Sopenharmony_ci void *addrs[] = { 2578c2ecf20Sopenharmony_ci lscsa, (void *)lscsa + sizeof(*lscsa) - 1, 2588c2ecf20Sopenharmony_ci code, code + code_size - 1 2598c2ecf20Sopenharmony_ci }; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* check the set of addresses, and create a new entry in the slbs array 2628c2ecf20Sopenharmony_ci * if there isn't already a SLB for that address */ 2638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(addrs); i++) { 2648c2ecf20Sopenharmony_ci if (__slb_present(slbs, nr_slbs, addrs[i])) 2658c2ecf20Sopenharmony_ci continue; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci __spu_kernel_slb(addrs[i], &slbs[nr_slbs]); 2688c2ecf20Sopenharmony_ci nr_slbs++; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci spin_lock_irq(&spu->register_lock); 2728c2ecf20Sopenharmony_ci /* Add the set of SLBs */ 2738c2ecf20Sopenharmony_ci for (i = 0; i < nr_slbs; i++) 2748c2ecf20Sopenharmony_ci spu_load_slb(spu, i, &slbs[i]); 2758c2ecf20Sopenharmony_ci spin_unlock_irq(&spu->register_lock); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_setup_kernel_slbs); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic irqreturn_t 2808c2ecf20Sopenharmony_cispu_irq_class_0(int irq, void *data) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct spu *spu; 2838c2ecf20Sopenharmony_ci unsigned long stat, mask; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci spu = data; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci spin_lock(&spu->register_lock); 2888c2ecf20Sopenharmony_ci mask = spu_int_mask_get(spu, 0); 2898c2ecf20Sopenharmony_ci stat = spu_int_stat_get(spu, 0) & mask; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci spu->class_0_pending |= stat; 2928c2ecf20Sopenharmony_ci spu->class_0_dar = spu_mfc_dar_get(spu); 2938c2ecf20Sopenharmony_ci spu->stop_callback(spu, 0); 2948c2ecf20Sopenharmony_ci spu->class_0_pending = 0; 2958c2ecf20Sopenharmony_ci spu->class_0_dar = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci spu_int_stat_clear(spu, 0, stat); 2988c2ecf20Sopenharmony_ci spin_unlock(&spu->register_lock); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic irqreturn_t 3048c2ecf20Sopenharmony_cispu_irq_class_1(int irq, void *data) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct spu *spu; 3078c2ecf20Sopenharmony_ci unsigned long stat, mask, dar, dsisr; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spu = data; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* atomically read & clear class1 status. */ 3128c2ecf20Sopenharmony_ci spin_lock(&spu->register_lock); 3138c2ecf20Sopenharmony_ci mask = spu_int_mask_get(spu, 1); 3148c2ecf20Sopenharmony_ci stat = spu_int_stat_get(spu, 1) & mask; 3158c2ecf20Sopenharmony_ci dar = spu_mfc_dar_get(spu); 3168c2ecf20Sopenharmony_ci dsisr = spu_mfc_dsisr_get(spu); 3178c2ecf20Sopenharmony_ci if (stat & CLASS1_STORAGE_FAULT_INTR) 3188c2ecf20Sopenharmony_ci spu_mfc_dsisr_set(spu, 0ul); 3198c2ecf20Sopenharmony_ci spu_int_stat_clear(spu, 1, stat); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci pr_debug("%s: %lx %lx %lx %lx\n", __func__, mask, stat, 3228c2ecf20Sopenharmony_ci dar, dsisr); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (stat & CLASS1_SEGMENT_FAULT_INTR) 3258c2ecf20Sopenharmony_ci __spu_trap_data_seg(spu, dar); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (stat & CLASS1_STORAGE_FAULT_INTR) 3288c2ecf20Sopenharmony_ci __spu_trap_data_map(spu, dar, dsisr); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (stat & CLASS1_LS_COMPARE_SUSPEND_ON_GET_INTR) 3318c2ecf20Sopenharmony_ci ; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (stat & CLASS1_LS_COMPARE_SUSPEND_ON_PUT_INTR) 3348c2ecf20Sopenharmony_ci ; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci spu->class_1_dsisr = 0; 3378c2ecf20Sopenharmony_ci spu->class_1_dar = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci spin_unlock(&spu->register_lock); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return stat ? IRQ_HANDLED : IRQ_NONE; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic irqreturn_t 3458c2ecf20Sopenharmony_cispu_irq_class_2(int irq, void *data) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct spu *spu; 3488c2ecf20Sopenharmony_ci unsigned long stat; 3498c2ecf20Sopenharmony_ci unsigned long mask; 3508c2ecf20Sopenharmony_ci const int mailbox_intrs = 3518c2ecf20Sopenharmony_ci CLASS2_MAILBOX_THRESHOLD_INTR | CLASS2_MAILBOX_INTR; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci spu = data; 3548c2ecf20Sopenharmony_ci spin_lock(&spu->register_lock); 3558c2ecf20Sopenharmony_ci stat = spu_int_stat_get(spu, 2); 3568c2ecf20Sopenharmony_ci mask = spu_int_mask_get(spu, 2); 3578c2ecf20Sopenharmony_ci /* ignore interrupts we're not waiting for */ 3588c2ecf20Sopenharmony_ci stat &= mask; 3598c2ecf20Sopenharmony_ci /* mailbox interrupts are level triggered. mask them now before 3608c2ecf20Sopenharmony_ci * acknowledging */ 3618c2ecf20Sopenharmony_ci if (stat & mailbox_intrs) 3628c2ecf20Sopenharmony_ci spu_int_mask_and(spu, 2, ~(stat & mailbox_intrs)); 3638c2ecf20Sopenharmony_ci /* acknowledge all interrupts before the callbacks */ 3648c2ecf20Sopenharmony_ci spu_int_stat_clear(spu, 2, stat); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pr_debug("class 2 interrupt %d, %lx, %lx\n", irq, stat, mask); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (stat & CLASS2_MAILBOX_INTR) 3698c2ecf20Sopenharmony_ci spu->ibox_callback(spu); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (stat & CLASS2_SPU_STOP_INTR) 3728c2ecf20Sopenharmony_ci spu->stop_callback(spu, 2); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (stat & CLASS2_SPU_HALT_INTR) 3758c2ecf20Sopenharmony_ci spu->stop_callback(spu, 2); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (stat & CLASS2_SPU_DMA_TAG_GROUP_COMPLETE_INTR) 3788c2ecf20Sopenharmony_ci spu->mfc_callback(spu); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (stat & CLASS2_MAILBOX_THRESHOLD_INTR) 3818c2ecf20Sopenharmony_ci spu->wbox_callback(spu); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci spu->stats.class2_intr++; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci spin_unlock(&spu->register_lock); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return stat ? IRQ_HANDLED : IRQ_NONE; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int spu_request_irqs(struct spu *spu) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int ret = 0; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (spu->irqs[0]) { 3958c2ecf20Sopenharmony_ci snprintf(spu->irq_c0, sizeof (spu->irq_c0), "spe%02d.0", 3968c2ecf20Sopenharmony_ci spu->number); 3978c2ecf20Sopenharmony_ci ret = request_irq(spu->irqs[0], spu_irq_class_0, 3988c2ecf20Sopenharmony_ci 0, spu->irq_c0, spu); 3998c2ecf20Sopenharmony_ci if (ret) 4008c2ecf20Sopenharmony_ci goto bail0; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci if (spu->irqs[1]) { 4038c2ecf20Sopenharmony_ci snprintf(spu->irq_c1, sizeof (spu->irq_c1), "spe%02d.1", 4048c2ecf20Sopenharmony_ci spu->number); 4058c2ecf20Sopenharmony_ci ret = request_irq(spu->irqs[1], spu_irq_class_1, 4068c2ecf20Sopenharmony_ci 0, spu->irq_c1, spu); 4078c2ecf20Sopenharmony_ci if (ret) 4088c2ecf20Sopenharmony_ci goto bail1; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci if (spu->irqs[2]) { 4118c2ecf20Sopenharmony_ci snprintf(spu->irq_c2, sizeof (spu->irq_c2), "spe%02d.2", 4128c2ecf20Sopenharmony_ci spu->number); 4138c2ecf20Sopenharmony_ci ret = request_irq(spu->irqs[2], spu_irq_class_2, 4148c2ecf20Sopenharmony_ci 0, spu->irq_c2, spu); 4158c2ecf20Sopenharmony_ci if (ret) 4168c2ecf20Sopenharmony_ci goto bail2; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cibail2: 4218c2ecf20Sopenharmony_ci if (spu->irqs[1]) 4228c2ecf20Sopenharmony_ci free_irq(spu->irqs[1], spu); 4238c2ecf20Sopenharmony_cibail1: 4248c2ecf20Sopenharmony_ci if (spu->irqs[0]) 4258c2ecf20Sopenharmony_ci free_irq(spu->irqs[0], spu); 4268c2ecf20Sopenharmony_cibail0: 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void spu_free_irqs(struct spu *spu) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci if (spu->irqs[0]) 4338c2ecf20Sopenharmony_ci free_irq(spu->irqs[0], spu); 4348c2ecf20Sopenharmony_ci if (spu->irqs[1]) 4358c2ecf20Sopenharmony_ci free_irq(spu->irqs[1], spu); 4368c2ecf20Sopenharmony_ci if (spu->irqs[2]) 4378c2ecf20Sopenharmony_ci free_irq(spu->irqs[2], spu); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_civoid spu_init_channels(struct spu *spu) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci static const struct { 4438c2ecf20Sopenharmony_ci unsigned channel; 4448c2ecf20Sopenharmony_ci unsigned count; 4458c2ecf20Sopenharmony_ci } zero_list[] = { 4468c2ecf20Sopenharmony_ci { 0x00, 1, }, { 0x01, 1, }, { 0x03, 1, }, { 0x04, 1, }, 4478c2ecf20Sopenharmony_ci { 0x18, 1, }, { 0x19, 1, }, { 0x1b, 1, }, { 0x1d, 1, }, 4488c2ecf20Sopenharmony_ci }, count_list[] = { 4498c2ecf20Sopenharmony_ci { 0x00, 0, }, { 0x03, 0, }, { 0x04, 0, }, { 0x15, 16, }, 4508c2ecf20Sopenharmony_ci { 0x17, 1, }, { 0x18, 0, }, { 0x19, 0, }, { 0x1b, 0, }, 4518c2ecf20Sopenharmony_ci { 0x1c, 1, }, { 0x1d, 0, }, { 0x1e, 1, }, 4528c2ecf20Sopenharmony_ci }; 4538c2ecf20Sopenharmony_ci struct spu_priv2 __iomem *priv2; 4548c2ecf20Sopenharmony_ci int i; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci priv2 = spu->priv2; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* initialize all channel data to zero */ 4598c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zero_list); i++) { 4608c2ecf20Sopenharmony_ci int count; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci out_be64(&priv2->spu_chnlcntptr_RW, zero_list[i].channel); 4638c2ecf20Sopenharmony_ci for (count = 0; count < zero_list[i].count; count++) 4648c2ecf20Sopenharmony_ci out_be64(&priv2->spu_chnldata_RW, 0); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* initialize channel counts to meaningful values */ 4688c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(count_list); i++) { 4698c2ecf20Sopenharmony_ci out_be64(&priv2->spu_chnlcntptr_RW, count_list[i].channel); 4708c2ecf20Sopenharmony_ci out_be64(&priv2->spu_chnlcnt_RW, count_list[i].count); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_init_channels); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic struct bus_type spu_subsys = { 4768c2ecf20Sopenharmony_ci .name = "spu", 4778c2ecf20Sopenharmony_ci .dev_name = "spu", 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciint spu_add_dev_attr(struct device_attribute *attr) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct spu *spu; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 4858c2ecf20Sopenharmony_ci list_for_each_entry(spu, &spu_full_list, full_list) 4868c2ecf20Sopenharmony_ci device_create_file(&spu->dev, attr); 4878c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_add_dev_attr); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciint spu_add_dev_attr_group(struct attribute_group *attrs) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct spu *spu; 4968c2ecf20Sopenharmony_ci int rc = 0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 4998c2ecf20Sopenharmony_ci list_for_each_entry(spu, &spu_full_list, full_list) { 5008c2ecf20Sopenharmony_ci rc = sysfs_create_group(&spu->dev.kobj, attrs); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* we're in trouble here, but try unwinding anyway */ 5038c2ecf20Sopenharmony_ci if (rc) { 5048c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: can't create sysfs group '%s'\n", 5058c2ecf20Sopenharmony_ci __func__, attrs->name); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(spu, 5088c2ecf20Sopenharmony_ci &spu_full_list, full_list) 5098c2ecf20Sopenharmony_ci sysfs_remove_group(&spu->dev.kobj, attrs); 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return rc; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_add_dev_attr_group); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_civoid spu_remove_dev_attr(struct device_attribute *attr) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct spu *spu; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 5268c2ecf20Sopenharmony_ci list_for_each_entry(spu, &spu_full_list, full_list) 5278c2ecf20Sopenharmony_ci device_remove_file(&spu->dev, attr); 5288c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_remove_dev_attr); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_civoid spu_remove_dev_attr_group(struct attribute_group *attrs) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct spu *spu; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 5378c2ecf20Sopenharmony_ci list_for_each_entry(spu, &spu_full_list, full_list) 5388c2ecf20Sopenharmony_ci sysfs_remove_group(&spu->dev.kobj, attrs); 5398c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_remove_dev_attr_group); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int spu_create_dev(struct spu *spu) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci int ret; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci spu->dev.id = spu->number; 5488c2ecf20Sopenharmony_ci spu->dev.bus = &spu_subsys; 5498c2ecf20Sopenharmony_ci ret = device_register(&spu->dev); 5508c2ecf20Sopenharmony_ci if (ret) { 5518c2ecf20Sopenharmony_ci printk(KERN_ERR "Can't register SPU %d with sysfs\n", 5528c2ecf20Sopenharmony_ci spu->number); 5538c2ecf20Sopenharmony_ci return ret; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci sysfs_add_device_to_node(&spu->dev, spu->node); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int __init create_spu(void *data) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct spu *spu; 5648c2ecf20Sopenharmony_ci int ret; 5658c2ecf20Sopenharmony_ci static int number; 5668c2ecf20Sopenharmony_ci unsigned long flags; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = -ENOMEM; 5698c2ecf20Sopenharmony_ci spu = kzalloc(sizeof (*spu), GFP_KERNEL); 5708c2ecf20Sopenharmony_ci if (!spu) 5718c2ecf20Sopenharmony_ci goto out; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci spu->alloc_state = SPU_FREE; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci spin_lock_init(&spu->register_lock); 5768c2ecf20Sopenharmony_ci spin_lock(&spu_lock); 5778c2ecf20Sopenharmony_ci spu->number = number++; 5788c2ecf20Sopenharmony_ci spin_unlock(&spu_lock); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ret = spu_create_spu(spu, data); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (ret) 5838c2ecf20Sopenharmony_ci goto out_free; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci spu_mfc_sdr_setup(spu); 5868c2ecf20Sopenharmony_ci spu_mfc_sr1_set(spu, 0x33); 5878c2ecf20Sopenharmony_ci ret = spu_request_irqs(spu); 5888c2ecf20Sopenharmony_ci if (ret) 5898c2ecf20Sopenharmony_ci goto out_destroy; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ret = spu_create_dev(spu); 5928c2ecf20Sopenharmony_ci if (ret) 5938c2ecf20Sopenharmony_ci goto out_free_irqs; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci mutex_lock(&cbe_spu_info[spu->node].list_mutex); 5968c2ecf20Sopenharmony_ci list_add(&spu->cbe_list, &cbe_spu_info[spu->node].spus); 5978c2ecf20Sopenharmony_ci cbe_spu_info[spu->node].n_spus++; 5988c2ecf20Sopenharmony_ci mutex_unlock(&cbe_spu_info[spu->node].list_mutex); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 6018c2ecf20Sopenharmony_ci spin_lock_irqsave(&spu_full_list_lock, flags); 6028c2ecf20Sopenharmony_ci list_add(&spu->full_list, &spu_full_list); 6038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spu_full_list_lock, flags); 6048c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci spu->stats.util_state = SPU_UTIL_IDLE_LOADED; 6078c2ecf20Sopenharmony_ci spu->stats.tstamp = ktime_get_ns(); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&spu->aff_list); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci goto out; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ciout_free_irqs: 6148c2ecf20Sopenharmony_ci spu_free_irqs(spu); 6158c2ecf20Sopenharmony_ciout_destroy: 6168c2ecf20Sopenharmony_ci spu_destroy_spu(spu); 6178c2ecf20Sopenharmony_ciout_free: 6188c2ecf20Sopenharmony_ci kfree(spu); 6198c2ecf20Sopenharmony_ciout: 6208c2ecf20Sopenharmony_ci return ret; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic const char *spu_state_names[] = { 6248c2ecf20Sopenharmony_ci "user", "system", "iowait", "idle" 6258c2ecf20Sopenharmony_ci}; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic unsigned long long spu_acct_time(struct spu *spu, 6288c2ecf20Sopenharmony_ci enum spu_utilization_state state) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci unsigned long long time = spu->stats.times[state]; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * If the spu is idle or the context is stopped, utilization 6348c2ecf20Sopenharmony_ci * statistics are not updated. Apply the time delta from the 6358c2ecf20Sopenharmony_ci * last recorded state of the spu. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci if (spu->stats.util_state == state) 6388c2ecf20Sopenharmony_ci time += ktime_get_ns() - spu->stats.tstamp; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return time / NSEC_PER_MSEC; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic ssize_t spu_stat_show(struct device *dev, 6458c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct spu *spu = container_of(dev, struct spu, dev); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return sprintf(buf, "%s %llu %llu %llu %llu " 6508c2ecf20Sopenharmony_ci "%llu %llu %llu %llu %llu %llu %llu %llu\n", 6518c2ecf20Sopenharmony_ci spu_state_names[spu->stats.util_state], 6528c2ecf20Sopenharmony_ci spu_acct_time(spu, SPU_UTIL_USER), 6538c2ecf20Sopenharmony_ci spu_acct_time(spu, SPU_UTIL_SYSTEM), 6548c2ecf20Sopenharmony_ci spu_acct_time(spu, SPU_UTIL_IOWAIT), 6558c2ecf20Sopenharmony_ci spu_acct_time(spu, SPU_UTIL_IDLE_LOADED), 6568c2ecf20Sopenharmony_ci spu->stats.vol_ctx_switch, 6578c2ecf20Sopenharmony_ci spu->stats.invol_ctx_switch, 6588c2ecf20Sopenharmony_ci spu->stats.slb_flt, 6598c2ecf20Sopenharmony_ci spu->stats.hash_flt, 6608c2ecf20Sopenharmony_ci spu->stats.min_flt, 6618c2ecf20Sopenharmony_ci spu->stats.maj_flt, 6628c2ecf20Sopenharmony_ci spu->stats.class2_intr, 6638c2ecf20Sopenharmony_ci spu->stats.libassist); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(stat, 0444, spu_stat_show, NULL); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistruct crash_spu_info { 6718c2ecf20Sopenharmony_ci struct spu *spu; 6728c2ecf20Sopenharmony_ci u32 saved_spu_runcntl_RW; 6738c2ecf20Sopenharmony_ci u32 saved_spu_status_R; 6748c2ecf20Sopenharmony_ci u32 saved_spu_npc_RW; 6758c2ecf20Sopenharmony_ci u64 saved_mfc_sr1_RW; 6768c2ecf20Sopenharmony_ci u64 saved_mfc_dar; 6778c2ecf20Sopenharmony_ci u64 saved_mfc_dsisr; 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci#define CRASH_NUM_SPUS 16 /* Enough for current hardware */ 6818c2ecf20Sopenharmony_cistatic struct crash_spu_info crash_spu_info[CRASH_NUM_SPUS]; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic void crash_kexec_stop_spus(void) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct spu *spu; 6868c2ecf20Sopenharmony_ci int i; 6878c2ecf20Sopenharmony_ci u64 tmp; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci for (i = 0; i < CRASH_NUM_SPUS; i++) { 6908c2ecf20Sopenharmony_ci if (!crash_spu_info[i].spu) 6918c2ecf20Sopenharmony_ci continue; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci spu = crash_spu_info[i].spu; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci crash_spu_info[i].saved_spu_runcntl_RW = 6968c2ecf20Sopenharmony_ci in_be32(&spu->problem->spu_runcntl_RW); 6978c2ecf20Sopenharmony_ci crash_spu_info[i].saved_spu_status_R = 6988c2ecf20Sopenharmony_ci in_be32(&spu->problem->spu_status_R); 6998c2ecf20Sopenharmony_ci crash_spu_info[i].saved_spu_npc_RW = 7008c2ecf20Sopenharmony_ci in_be32(&spu->problem->spu_npc_RW); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci crash_spu_info[i].saved_mfc_dar = spu_mfc_dar_get(spu); 7038c2ecf20Sopenharmony_ci crash_spu_info[i].saved_mfc_dsisr = spu_mfc_dsisr_get(spu); 7048c2ecf20Sopenharmony_ci tmp = spu_mfc_sr1_get(spu); 7058c2ecf20Sopenharmony_ci crash_spu_info[i].saved_mfc_sr1_RW = tmp; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK; 7088c2ecf20Sopenharmony_ci spu_mfc_sr1_set(spu, tmp); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci __delay(200); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic void crash_register_spus(struct list_head *list) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct spu *spu; 7178c2ecf20Sopenharmony_ci int ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci list_for_each_entry(spu, list, full_list) { 7208c2ecf20Sopenharmony_ci if (WARN_ON(spu->number >= CRASH_NUM_SPUS)) 7218c2ecf20Sopenharmony_ci continue; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci crash_spu_info[spu->number].spu = spu; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci ret = crash_shutdown_register(&crash_kexec_stop_spus); 7278c2ecf20Sopenharmony_ci if (ret) 7288c2ecf20Sopenharmony_ci printk(KERN_ERR "Could not register SPU crash handler"); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci#else 7328c2ecf20Sopenharmony_cistatic inline void crash_register_spus(struct list_head *list) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci#endif 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic void spu_shutdown(void) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct spu *spu; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 7428c2ecf20Sopenharmony_ci list_for_each_entry(spu, &spu_full_list, full_list) { 7438c2ecf20Sopenharmony_ci spu_free_irqs(spu); 7448c2ecf20Sopenharmony_ci spu_destroy_spu(spu); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic struct syscore_ops spu_syscore_ops = { 7508c2ecf20Sopenharmony_ci .shutdown = spu_shutdown, 7518c2ecf20Sopenharmony_ci}; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int __init init_spu_base(void) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci int i, ret = 0; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUMNODES; i++) { 7588c2ecf20Sopenharmony_ci mutex_init(&cbe_spu_info[i].list_mutex); 7598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cbe_spu_info[i].spus); 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (!spu_management_ops) 7638c2ecf20Sopenharmony_ci goto out; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* create system subsystem for spus */ 7668c2ecf20Sopenharmony_ci ret = subsys_system_register(&spu_subsys, NULL); 7678c2ecf20Sopenharmony_ci if (ret) 7688c2ecf20Sopenharmony_ci goto out; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci ret = spu_enumerate_spus(create_spu); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (ret < 0) { 7738c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Error initializing spus\n", 7748c2ecf20Sopenharmony_ci __func__); 7758c2ecf20Sopenharmony_ci goto out_unregister_subsys; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (ret > 0) 7798c2ecf20Sopenharmony_ci fb_append_extra_logo(&logo_spe_clut224, ret); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci mutex_lock(&spu_full_list_mutex); 7828c2ecf20Sopenharmony_ci xmon_register_spus(&spu_full_list); 7838c2ecf20Sopenharmony_ci crash_register_spus(&spu_full_list); 7848c2ecf20Sopenharmony_ci mutex_unlock(&spu_full_list_mutex); 7858c2ecf20Sopenharmony_ci spu_add_dev_attr(&dev_attr_stat); 7868c2ecf20Sopenharmony_ci register_syscore_ops(&spu_syscore_ops); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci spu_init_affinity(); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci out_unregister_subsys: 7938c2ecf20Sopenharmony_ci bus_unregister(&spu_subsys); 7948c2ecf20Sopenharmony_ci out: 7958c2ecf20Sopenharmony_ci return ret; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_cidevice_initcall(init_spu_base); 798