| /* |
| * Blue Gene/P Common Node Services (CNS) wrappers |
| * |
| * These are declared in asm/bluegene.h but implemented here. |
| * |
| * Copyright 2003-2009 International Business Machines, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| * |
| * Author: Todd Inglett <tinglett@us.ibm.com> |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/of_platform.h> |
| #include <asm/pgtable.h> |
| #include <asm/bluegene.h> |
| #include <asm/bgcns.h> |
| #ifdef CONFIG_ZEPTO |
| #include <asm/udbg.h> |
| #include <asm/bgp_personality.h> |
| #include <mm/mmu_decl.h> |
| #include <linux/zepto_debug.h> |
| #endif |
| |
| /* The descriptor for CNS identifies location and entry point of firmware. |
| * We re-build it from data passed through the ibm,bluegene-cns device tree entry. |
| */ |
| BGCNS_Descriptor bgcnsd; |
| BGCNS_Descriptor bgcnsd_orig; |
| |
| /* These functions spin on specific errors when we can't print messages. |
| * They make it easy to find the cause of the error by finding the iar in the |
| * kernel System.map. |
| */ |
| static void noinline __init bgp_fatal_no_ibm_bluegene_cns(void) { for (;;); } |
| static void noinline __init bgp_fatal_no_base_va(void) { for (;;); } |
| static void noinline __init bgp_fatal_no_base_pa(void) { for (;;); } |
| static void noinline __init bgp_fatal_no_services(void) { for (;;); } |
| static void noinline __init bgp_fatal_no_size(void) { for (;;); } |
| static void noinline __init bgp_fatal_no_version(void) { for (;;); } |
| |
| /* Get the descriptor for CNS from the device tree. |
| * Don't inline so we can make out the stack trace easier when it isn't working. |
| */ |
| static void noinline __init get_cns_descriptor(BGCNS_Descriptor *bgcnsd) |
| { |
| int len; |
| const unsigned *reg; |
| struct device_node *devcns = of_find_node_by_path("/ibm,bluegene/cns"); |
| |
| if (!devcns) bgp_fatal_no_ibm_bluegene_cns(); |
| |
| reg = of_get_property(devcns, "base-va", &len); |
| if (!reg) bgp_fatal_no_base_va(); |
| bgcnsd->baseVirtualAddress = *reg; |
| reg = of_get_property(devcns, "base-pa", &len); |
| if (!reg) bgp_fatal_no_base_pa(); |
| bgcnsd->basePhysicalAddress = *reg; |
| bgcnsd->basePhysicalAddressERPN = 0; /* assumes DDR <= 4G */ |
| reg = of_get_property(devcns, "services", &len); |
| if (!reg) bgp_fatal_no_services(); |
| bgcnsd->services = (void *)(*reg); |
| reg = of_get_property(devcns, "size", &len); |
| if (!reg) bgp_fatal_no_size(); |
| bgcnsd->size = *reg; |
| reg = of_get_property(devcns, "version", &len); |
| if (!reg) bgp_fatal_no_version(); |
| bgcnsd->version = *reg; |
| } |
| |
| void __init ppc44x_update_tlb_hwater(void); /* from mm/44x_mmu.c */ |
| |
| //static void noinline __init map_cns(BGCNS_Descriptor *bgcnsd) |
| static void noinline map_cns(BGCNS_Descriptor *bgcnsd) |
| { |
| unsigned word0, word1, word2; |
| int entry = 62; /* We reserve one of the PPC44x_EARLY_TLBS in asm/mmu-44x.h */ |
| |
| word0 = (bgcnsd->baseVirtualAddress & 0xfffff000) | PPC44x_TLB_VALID | PPC44x_TLB_256K; |
| word1 = (bgcnsd->basePhysicalAddress & 0xfffff000) | (bgcnsd->basePhysicalAddressERPN & 0xf); |
| word2 = PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_M | PPC44x_TLB_WL1 | PPC44x_TLB_U2; |
| __asm__ __volatile__( |
| "tlbwe %1,%0,0\n" |
| "tlbwe %2,%0,1\n" |
| "tlbwe %3,%0,2\n" |
| "isync\n" : : "r" (entry), "r" (word0), "r" (word1), "r" (word2)); |
| } |
| |
| |
| #ifdef CONFIG_ZEPTO_COMPUTENODE |
| unsigned bgpers_rank; |
| #endif |
| |
| #ifdef CONFIG_ZEPTO_CNS_RELOCATION |
| char cns_buf[256*1024] |
| __attribute__((__section__(".cns.256KB_aligned"))); |
| |
| void erase_CNS_orig(void) |
| { |
| unsigned flags; |
| |
| local_save_flags(flags); |
| local_irq_disable(); |
| |
| map_cns(&bgcnsd_orig); |
| memset( (void*)bgcnsd.baseVirtualAddress, 0, bgcnsd.size ); |
| map_cns(&bgcnsd); |
| |
| local_irq_restore(flags); |
| |
| printk("Erased orig CNS pa:%08x\n", bgcnsd_orig.basePhysicalAddress); |
| } |
| #endif |
| |
| extern int map_page(unsigned long va, phys_addr_t pa, int flags); |
| |
| /* bgp_init_cns() might be called more than one time. |
| No interrupt occur here, btw? |
| */ |
| void __init bgp_init_cns(void) |
| { |
| unsigned long v_start, v_end, v, p; |
| |
| if (bgcnsd.size == 0) { |
| /* Get the descriptor, map CNS, and tell Linux about the mapping. */ |
| get_cns_descriptor(&bgcnsd); |
| v_start = bgcnsd.baseVirtualAddress; |
| v_end = v_start + bgcnsd.size; |
| v_start -= PAGE_SIZE; /* hack: reserve 1 extra page */ |
| v = v_start; |
| p = bgcnsd.basePhysicalAddress; /* always < 4G */ |
| |
| #ifdef CONFIG_ZEPTO_COMPUTENODE |
| { |
| BGP_Personality_t bgpers; |
| map_cns(&bgcnsd); /* to access the CNS region, udbg_printf, personality */ |
| bluegene_getPersonality(&bgpers, sizeof(BGP_Personality_t)); |
| bgpers_rank = bgpers.Network_Config.Rank; |
| } |
| #endif |
| |
| #ifdef CONFIG_ZEPTO_CNS_RELOCATION |
| { |
| unsigned flags; |
| |
| map_cns(&bgcnsd); /* to access the CNS region, udbg_printf, personality */ |
| |
| #ifdef CONFIG_PPC_EARLY_DEBUG_BGP |
| udbg_printf("Relocating CNS... pa:%08x to pa:%08x\n", |
| bgcnsd.basePhysicalAddress, (unsigned)cns_buf-(unsigned)PAGE_OFFSET); |
| #endif |
| local_save_flags(flags); |
| local_irq_disable(); |
| |
| bgcnsd_orig = bgcnsd; |
| bgcnsd.basePhysicalAddress = (unsigned)cns_buf - (unsigned)PAGE_OFFSET; |
| |
| /* simply copy CNS to pre-allocated space which is covered by kernel TLB */ |
| memcpy( (void*)cns_buf, (void*)bgcnsd_orig.baseVirtualAddress, bgcnsd_orig.size); |
| |
| asm volatile ("dccci 0,0" : : : "memory"); /* dcache all */ |
| asm volatile ("iccci 0,0" : : : "memory"); /* icache all */ |
| asm volatile ("isync"); |
| |
| map_cns(&bgcnsd); /* reload new tlb */ |
| |
| local_irq_restore(flags); |
| #ifdef CONFIG_PPC_EARLY_DEBUG_BGP |
| udbg_printf("CNS relocated\n"); |
| #endif |
| } |
| #else |
| /* We must be careful because we could hit 4G and wrap to v == 0. |
| * Hence the v > v_start check. |
| */ |
| for (; v < v_end && v > v_start; v += PAGE_SIZE, p += PAGE_SIZE) |
| map_page(v, p, _PAGE_RAM_TEXT); |
| #endif |
| } |
| |
| map_cns(&bgcnsd); |
| } |
| |
| /* Simple udbg_putc. We perform rudimentary buffering so it is readable. */ |
| static int bgp_udbg_cur = 0; |
| static char bgp_udbg_buf[256]; |
| void bgp_udbg_putc(char c) |
| { |
| #ifdef CONFIG_ZEPTO_COMPUTENODE |
| /* ZXXX: support command line later */ |
| if( bgpers_rank != 0 ) return; |
| #endif |
| |
| bgp_udbg_buf[bgp_udbg_cur++] = c; |
| if (c == '\n' || bgp_udbg_cur >= sizeof(bgp_udbg_buf)) { |
| if (bgcnsd.size) |
| bluegene_writeToMailboxConsole(bgp_udbg_buf, bgp_udbg_cur); |
| bgp_udbg_cur = 0; |
| } |
| } |
| |
| |
| #define CALLCNS(service) \ |
| ({ unsigned flags; \ |
| typeof(bgcnsd.services->service) ret; \ |
| local_save_flags(flags); \ |
| local_irq_disable(); \ |
| ret = bgcnsd.services->service; \ |
| local_irq_restore(flags); \ |
| ret; \ |
| }) |
| |
| #ifdef CONFIG_ZEPTO_CNS_RELOCATION |
| #define CALLCNS_ORIG(service) \ |
| ({ unsigned flags; \ |
| typeof(bgcnsd.services->service) ret; \ |
| local_save_flags(flags); \ |
| local_irq_disable(); \ |
| map_cns(&bgcnsd_orig); \ |
| ret = bgcnsd_orig.services->service; \ |
| map_cns(&bgcnsd); \ |
| local_irq_restore(flags); \ |
| ret; \ |
| }) |
| #endif |
| |
| |
| /* This returns non-zero if there is something in an input mailbox. */ |
| int bluegene_testInboxAttention(void) |
| { |
| /* ToDo: this should be fast. Read the DCR directly. */ |
| return CALLCNS(testInboxAttention()); |
| } |
| |
| int bluegene_testForOutboxCompletion(void) |
| { |
| return CALLCNS(testForOutboxCompletion()); |
| } |
| |
| int bluegene_writeRASEvent_nonBlocking(unsigned facility, |
| unsigned unit, |
| unsigned short err_code, |
| unsigned numDetails, |
| unsigned details[]) |
| { |
| return CALLCNS(writeRASEvent_nonBlocking(facility, unit, err_code, numDetails, details)); |
| } |
| |
| int bluegene_writeRASString(unsigned facility, |
| unsigned unit, |
| unsigned short err_code, |
| char* str) |
| { |
| return CALLCNS(writeRASString(facility, unit, err_code, str)); |
| } |
| |
| int bluegene_writeRASString_nonBlocking(unsigned facility, |
| unsigned unit, |
| unsigned short err_code, |
| char* str) |
| { |
| return CALLCNS(writeRASString_nonBlocking(facility, unit, err_code, str)); |
| } |
| |
| int bluegene_writeToMailboxConsole(char *msg, unsigned msglen) |
| { |
| return CALLCNS(writeToMailboxConsole(msg, msglen)); |
| } |
| |
| int bluegene_writeToMailboxConsole_nonBlocking(char *msg, unsigned msglen) |
| { |
| return CALLCNS(writeToMailboxConsole_nonBlocking(msg, msglen)); |
| } |
| |
| unsigned bluegene_readFromMailboxConsole(char *buf, unsigned bufsize) |
| { |
| return CALLCNS(readFromMailboxConsole(buf, bufsize)); |
| } |
| |
| int bluegene_macResetPHY(void) |
| { |
| return CALLCNS(macResetPHY()); |
| } |
| /* ! @brief Tests the MAC unit's link but does not block. */ |
| /* ! @param[in] link_type specifies the type of link to be tested. */ |
| /* ! @param[out] result points to the link status, which is valid only when the return code is */ |
| /* ! BGCNS_RC_COMPLETE. A value of one (1) indicates that the link is active; zero (0) */ |
| /* ! indicates that it is inactive. */ |
| /* ! @param[in] reset indicates whether this is the beginning (1) or a continuation (0) of a */ |
| /* ! test link sequence. That is, callers should initiate a sequence with reset=1 and then */ |
| /* ! if receiving a return code of BGCNS_RC_CONTINUE, should invoke this service again with */ |
| /* ! reset=0. */ |
| /* ! @param[in] timeoutInMillis the (approximate) number of milliseconds that this service can have */ |
| /* ! before returning. If the allotted time is not sufficient, the service will return BGCNS_RC_CONTINUE */ |
| /* ! to indicate that it needs additional time. */ |
| /* ! @return BGCNS_RC_COMPLETE if the test is complete (result is valid only in this case). BGCNS_RC_CONTINUE */ |
| /* ! if the reset operation is not yet complete. BGCNS_RC_ERROR if the reset operation failed. */ |
| int (*macTestLink_nonBlocking)(BGCNS_LinkType link_type, unsigned* result, int reset, unsigned timeoutInMillis); |
| |
| |
| int bluegene_macTestRxLink(void) |
| { |
| return CALLCNS(macTestLink(BGCNS_Receiver)); |
| } |
| |
| |
| int bluegene_macTestTxLink(void) |
| { |
| return CALLCNS(macTestLink(BGCNS_Transmitter)); |
| } |
| |
| int bluegene_takeCPU(unsigned cpu, void *arg, void (*entry)(unsigned cpu, void *arg)) |
| { |
| #ifdef CONFIG_ZEPTO_CNS_RELOCATION |
| return CALLCNS_ORIG(takeCPU(cpu, arg, entry)); |
| #else |
| return CALLCNS(takeCPU(cpu, arg, entry)); |
| #endif |
| } |
| |
| |
| #ifdef CONFIG_ZEPTO |
| |
| /* |
| nprocs mode |
| 4 VN |
| 2 DUAL |
| 1 SMP |
| |
| NOTE: this function is only called from arch/powerpc/syslib/bgdd/zepto_setup_treeroute.c |
| */ |
| #define _BGP_PERS_PROCESSCONFIG_SMP (0x0F000000) |
| #define _BGP_PERS_PROCESSCONFIG_VNM (0x08040201) |
| #define _BGP_PERS_PROCESSCONFIG_2x2 (0x0C030000) |
| |
| void bluegene_set_Kernel_Config_ProcessConfig(int nprocs) |
| { |
| BGP_Personality_t* pers = bgcnsd.services->getPersonalityData(); |
| switch(nprocs) { |
| case 4: |
| pers->Kernel_Config.ProcessConfig = _BGP_PERS_PROCESSCONFIG_VNM; |
| break; |
| case 2: |
| pers->Kernel_Config.ProcessConfig = _BGP_PERS_PROCESSCONFIG_2x2; |
| break; |
| default: |
| pers->Kernel_Config.ProcessConfig = _BGP_PERS_PROCESSCONFIG_SMP; |
| } |
| |
| zepto_debug(1,"bluegene_set_Kernel_Config_ProcessConfig nprocs=%d ProcessConfig=%08x\n", |
| nprocs, pers->Kernel_Config.ProcessConfig ); |
| } |
| #endif |
| |
| int bluegene_getPersonality(void *buff, unsigned buffSize) |
| { |
| int sz; |
| unsigned flags; |
| |
| local_save_flags(flags); |
| local_irq_disable(); |
| sz = bgcnsd.services->getPersonalitySize(); |
| if (sz > buffSize) |
| sz = buffSize; |
| memcpy(buff, bgcnsd.services->getPersonalityData(), sz); |
| local_irq_restore(flags); |
| |
| return sz; |
| } |
| |
| int bluegene_isIONode(void) |
| { |
| int ret; |
| unsigned flags; |
| |
| local_save_flags(flags); |
| local_irq_disable(); |
| ret = bgcnsd.services->isIONode(); |
| local_irq_restore(flags); |
| return ret; |
| } |
| |
| int bluegene_mapXEMAC(void* baseAddr) |
| { |
| return CALLCNS(mapDevice(BGCNS_XEMAC, baseAddr)); |
| } |
| |
| int bluegene_globalBarrier_nonBlocking(unsigned int channel, int reset, unsigned int timeoutInMillis) |
| { |
| return CALLCNS(globalBarrier_nonBlocking(channel,reset,timeoutInMillis)) ; |
| } |
| |
| EXPORT_SYMBOL(bluegene_getPersonality) ; |
| EXPORT_SYMBOL(bluegene_globalBarrier_nonBlocking) ; |
| EXPORT_SYMBOL(bgcnsd) ; |