// SPDX-License-Identifier: GPL-2.0-or-later/* * arch/parisc/kernel/firmware.c - safe PDC access routines * * PDC == Processor Dependent Code * * See PDC documentation at * https://parisc.wiki.kernel.org/index.php/Technical_Documentation * for documentation describing the entry points and calling * conventions defined below. * * Copyright 1999 SuSE GmbH Nuernberg (Philipp Rumpf, prumpf@tux.org) * Copyright 1999 The Puffin Group, (Alex deVries, David Kennedy) * Copyright 2003 Grant Grundler <grundler parisc-linux org> * Copyright 2003,2004 Ryan Bradetich <rbrad@parisc-linux.org> * Copyright 2004,2006 Thibaut VARENE <varenet@parisc-linux.org> */ /* I think it would be in everyone's best interest to follow this * guidelines when writing PDC wrappers: * * - the name of the pdc wrapper should match one of the macros * used for the first two arguments * - don't use caps for random parts of the name * - use the static PDC result buffers and "copyout" to structs * supplied by the caller to encapsulate alignment restrictions * - hold pdc_lock while in PDC or using static result buffers * - use __pa() to convert virtual (kernel) pointers to physical * ones. * - the name of the struct used for pdc return values should equal * one of the macros used for the first two arguments to the * corresponding PDC call * - keep the order of arguments * - don't be smart (setting trailing NUL bytes for strings, return * something useful even if the call failed) unless you are sure * it's not going to affect functionality or performance * * Example: * int pdc_cache_info(struct pdc_cache_info *cache_info ) * { * int retval; * * spin_lock_irq(&pdc_lock); * retval = mem_pdc_call(PDC_CACHE,PDC_CACHE_INFO,__pa(cache_info),0); * convert_to_wide(pdc_result); * memcpy(cache_info, pdc_result, sizeof(*cache_info)); * spin_unlock_irq(&pdc_lock); * * return retval; * } * prumpf 991016 */ #include <linux/stdarg.h> #include <linux/delay.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/string.h>#include <linux/spinlock.h> #include <asm/page.h>#include <asm/pdc.h>#include <asm/pdcpat.h>#include <asm/processor.h> /* for boot_cpu_data */ #if defined(BOOTLOADER)# undef spin_lock_irqsave# define spin_lock_irqsave(a, b) { b = 1; }# undef spin_unlock_irqrestore# define spin_unlock_irqrestore(a, b)#elsestatic DEFINE_SPINLOCK(pdc_lock);#endif static unsigned long pdc_result[NUM_PDC_RESULT] __aligned(8);static unsigned long pdc_result2[NUM_PDC_RESULT] __aligned(8); #ifdef CONFIG_64BIT#define WIDE_FIRMWARE PDC_MODEL_OS64#define NARROW_FIRMWARE PDC_MODEL_OS32 /* Firmware needs to be initially set to narrow to determine the * actual firmware width. */int parisc_narrow_firmware __ro_after_init = NARROW_FIRMWARE;#endif /* On most currently-supported platforms, IODC I/O calls are 32-bit calls * and MEM_PDC calls are always the same width as the OS. * Some PAT boxes may have 64-bit IODC I/O. * * Ryan Bradetich added the now obsolete CONFIG_PDC_NARROW to allow * 64-bit kernels to run on systems with 32-bit MEM_PDC calls. * This allowed wide kernels to run on Cxxx boxes. * We now detect 32-bit-only PDC and dynamically switch to 32-bit mode * when running a 64-bit kernel on such boxes (e.g. C200 or C360). */ #ifdef CONFIG_64BITlong real64_call(unsigned long function, ...);#endiflong real32_call(unsigned long function, ...); #ifdef CONFIG_64BIT# define MEM_PDC (unsigned long)(PAGE0->mem_pdc_hi) << 32 | PAGE0->mem_pdc# define mem_pdc_call(args...) unlikely(parisc_narrow_firmware) ? real32_call(MEM_PDC, args) : real64_call(MEM_PDC, args)#else# define MEM_PDC (unsigned long)PAGE0->mem_pdc# define mem_pdc_call(args...) real32_call(MEM_PDC, args)#endif /** * f_extend - Convert PDC addresses to kernel addresses. * @address: Address returned from PDC. * * This function is used to convert PDC addresses into kernel addresses * when the PDC address size and kernel address size are different. */static unsigned long f_extend(unsigned long address){#ifdef CONFIG_64BIT if(unlikely(parisc_narrow_firmware)) { if((address & 0xff000000) == 0xf0000000) return (0xfffffff0UL << 32) | (u32)address; if((address & 0xf0000000) == 0xf0000000) return (0xffffffffUL << 32) | (u32)address; }#endif return address;} /** * convert_to_wide - Convert the return buffer addresses into kernel addresses. * @addr: The return buffer from PDC. * * This function is used to convert the return buffer addresses retrieved from PDC * into kernel addresses when the PDC address size and kernel address size are * different. */static void convert_to_wide(unsigned long *addr){#ifdef CONFIG_64BIT int i; unsigned int *p = (unsigned int *)addr; if (unlikely(parisc_narrow_firmware)) { for (i = (NUM_PDC_RESULT-1); i >= 0; --i) addr[i] = p[i]; }#endif} #ifdef CONFIG_64BITvoid set_firmware_width_unlocked(void){ int ret; ret = mem_pdc_call(PDC_MODEL, PDC_MODEL_CAPABILITIES, __pa(pdc_result), 0); if (ret < 0) return; convert_to_wide(pdc_result); if (pdc_result[0] != NARROW_FIRMWARE) parisc_narrow_firmware = 0;} /** * set_firmware_width - Determine if the firmware is wide or narrow. * * This function must be called before any pdc_* function that uses the * convert_to_wide function. */void set_firmware_width(void){ unsigned long flags; /* already initialized? */ if (parisc_narrow_firmware != NARROW_FIRMWARE) return; spin_lock_irqsave(&pdc_lock, flags); set_firmware_width_unlocked(); spin_unlock_irqrestore(&pdc_lock, flags);}#elsevoid set_firmware_width_unlocked(void){ return;} void set_firmware_width(void){ return;}#endif /*CONFIG_64BIT*/ #if !defined(BOOTLOADER)/** * pdc_emergency_unlock - Unlock the linux pdc lock * * This call unlocks the linux pdc lock in case we need some PDC functions * (like pdc_add_valid) during kernel stack dump. */void pdc_emergency_unlock(void){ /* Spinlock DEBUG code freaks out if we unconditionally unlock */ if (spin_is_locked(&pdc_lock)) spin_unlock(&pdc_lock);} /** * pdc_add_valid - Verify address can be accessed without causing a HPMC. * @address: Address to be verified. * * This PDC call attempts to read from the specified address and verifies * if the address is valid. * * The return value is PDC_OK (0) in case accessing this address is valid. */int pdc_add_valid(unsigned long address){ int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_ADD_VALID, PDC_ADD_VALID_VERIFY, address); spin_unlock_irqrestore(&pdc_lock, flags); return retval;}EXPORT_SYMBOL(pdc_add_valid); /** * pdc_instr - Get instruction that invokes PDCE_CHECK in HPMC handler. * @instr: Pointer to variable which will get instruction opcode. * * The return value is PDC_OK (0) in case call succeeded. */int __init pdc_instr(unsigned int *instr){ int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_INSTR, 0UL, __pa(pdc_result)); convert_to_wide(pdc_result); *instr = pdc_result[0]; spin_unlock_irqrestore(&pdc_lock, flags); return retval;} /** * pdc_chassis_info - Return chassis information. * @chassis_info: The memory buffer address. * @led_info: The size of the memory buffer address. * @len: The size of the memory buffer address. * * An HVERSION dependent call for returning the chassis information. */int __init pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_info, unsigned long len){ int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); memcpy(&pdc_result, chassis_info, sizeof(*chassis_info)); memcpy(&pdc_result2, led_info, len); retval = mem_pdc_call(PDC_CHASSIS, PDC_RETURN_CHASSIS_INFO, __pa(pdc_result), __pa(pdc_result2), len); memcpy(chassis_info, pdc_result, sizeof(*chassis_info)); memcpy(led_info, pdc_result2, len); spin_unlock_irqrestore(&pdc_lock, flags); return retval;} /** * pdc_pat_chassis_send_log - Sends a PDC PAT CHASSIS log message. * @state: state of the machine * @data: value for that state * * Must be correctly formatted or expect system crash */#ifdef CONFIG_64BITint pdc_pat_chassis_send_log(unsigned long state, unsigned long data){ int retval = 0; unsigned long flags; if (!is_pdc_pat()) return -1; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_PAT_CHASSIS_LOG, PDC_PAT_CHASSIS_WRITE_LOG, __pa(&state), __pa(&data)); spin_unlock_irqrestore(&pdc_lock, flags); return retval;}#endif /** * pdc_chassis_disp - Updates chassis code * @disp: value to show on display */int pdc_chassis_disp(unsigned long disp){ int retval = 0; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_DISP, disp); spin_unlock_irqrestore(&pdc_lock, flags); return retval;} /** * __pdc_cpu_rendezvous - Stop currently executing CPU and do not return. */int __pdc_cpu_rendezvous(void){ if (is_pdc_pat()) return mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_RENDEZVOUS); else return mem_pdc_call(PDC_PROC, 1, 0);} /** * pdc_cpu_rendezvous_lock - Lock PDC while transitioning to rendezvous state */void pdc_cpu_rendezvous_lock(void) __acquires(&pdc_lock){ spin_lock(&pdc_lock);} /** * pdc_cpu_rendezvous_unlock - Unlock PDC after reaching rendezvous state */void pdc_cpu_rendezvous_unlock(void) __releases(&pdc_lock){ spin_unlock(&pdc_lock);} /** * pdc_pat_get_PDC_entrypoint - Get PDC entry point for current CPU * @pdc_entry: pointer to where the PDC entry point should be stored */int pdc_pat_get_PDC_entrypoint(unsigned long *pdc_entry){ int retval = 0; unsigned long flags; if (!IS_ENABLED(CONFIG_SMP) || !is_pdc_pat()) { *pdc_entry = MEM_PDC; return 0; } spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_GET_PDC_ENTRYPOINT, __pa(pdc_result)); *pdc_entry = pdc_result[0]; spin_unlock_irqrestore(&pdc_lock, flags); return retval;}/** * pdc_chassis_warn - Fetches chassis warnings * @warn: The warning value to be shown */int pdc_chassis_warn(unsigned long *warn){ int retval = 0; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_WARN, __pa(pdc_result)); *warn = pdc_result[0]; spin_unlock_irqrestore(&pdc_lock, flags); return retval;} int pdc_coproc_cfg_unlocked(struct pdc_coproc_cfg *pdc_coproc_info){ int ret; ret = mem_pdc_call(PDC_COPROC, PDC_COPROC_CFG, __pa(pdc_result)); convert_to_wide(pdc_result); pdc_coproc_info->ccr_functional = pdc_result[0]; pdc_coproc_info->ccr_present = pdc_result[1]; pdc_coproc_info->revision = pdc_result[17]; pdc_coproc_info->model = pdc_result[18]; return ret;} /** * pdc_coproc_cfg - To identify coprocessors attached to the processor. * @pdc_coproc_info: Return buffer address. * * This PDC call returns the presence and status of all the coprocessors * attached to the processor. */int pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info){ int ret; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); ret = pdc_coproc_cfg_unlocked(pdc_coproc_info); spin_unlock_irqrestore(&pdc_lock, flags); return ret;} /** * pdc_iodc_read - Read data from the modules IODC. * @actcnt: The actual number of bytes. * @hpa: The HPA of the module for the iodc read. * @index: The iodc entry point. * @iodc_data: A buffer memory for the iodc options. * @iodc_data_size: Size of the memory buffer. * * This PDC call reads from the IODC of the module specified by the hpa * argument. */int pdc_iodc_read(unsigned long *actcnt, unsigned long hpa, unsigned int index, void *iodc_data, unsigned int iodc_data_size){ int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_IODC, PDC_IODC_READ, __pa(pdc_result), hpa, index, __pa(pdc_result2), iodc_data_size); convert_to_wide(pdc_result); *actcnt = pdc_result[0]; memcpy(iodc_data, pdc_result2, iodc_data_size); spin_unlock_irqrestore(&pdc_lock, flags); return retval;}EXPORT_SYMBOL(pdc_iodc_read); /** * pdc_system_map_find_mods - Locate unarchitected modules. * @pdc_mod_info: Return buffer address. * @mod_path: pointer to dev path structure. * @mod_index: fixed address module index. * * To locate and identify modules which reside at fixed I/O addresses, which * do not self-identify via architected bus walks. */int pdc_system_map_find_mods(struct pdc_system_map_mod_info *pdc_mod_info, struct pdc_module_path *mod_path, long mod_index){ int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); memcpy(pdc_result2, mod_path, sizeof(*mod_path)); retval = mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_MODULE, __pa(pdc_result), __pa(pdc_result2), mod_index); convert_to_wide(pdc_result); memcpy(pdc_mod_info, pdc_result, sizeof(*pdc_mod_info)); memcpy(mod_path, pdc_result2, sizeof(*mod_path)); spin_unlock_irqrestore(&pdc_lock, flags); pdc_mod_info->mod_addr = f_extend(pdc_mod_info->mod_addr); return retval;} /** * pdc_system_map_find_addrs - Retrieve additional address ranges. * @pdc_addr_info: Return buffer address. * @mod_index: Fixed address module index. * @addr_index: Address range index. * * Retrieve additional information about subsequent address ranges for modules * with multiple address ranges. */int pdc_system_map_find_addrs(struct pdc_system_map_addr_info *pdc_addr_info, long mod_index, long addr_index){ int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_ADDRESS, __pa(pdc_result), mod_index, addr_index); convert_to_wide(pdc_result); memcpy(pdc_addr_info, pdc_result, sizeof(*pdc_addr_info)); spin_unlock_irqrestore(&pdc_lock, flags); Lines 1–500 of 1957. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * arch/parisc/kernel/firmware.c - safe PDC access routines * * PDC == Processor Dependent Code * * See PDC documentation at * https://parisc.wiki.kernel.org/index.php/Technical_Documentation * for documentation describing the entry points and calling * conventions defined below. * * Copyright 1999 SuSE GmbH Nuernberg (Philipp Rumpf, prumpf@tux.org) * Copyright 1999 The Puffin Group, (Alex deVries, David Kennedy) * Copyright 2003 Grant Grundler <grundler parisc-linux org> * Copyright 2003,2004 Ryan Bradetich <rbrad@parisc-linux.org> * Copyright 2004,2006 Thibaut VARENE <varenet@parisc-linux.org> */ /* I think it would be in everyone's best interest to follow this * guidelines when writing PDC wrappers: * * - the name of the pdc wrapper should match one of the macros * used for the first two arguments * - don't use caps for random parts of the name * - use the static PDC result buffers and "copyout" to structs * supplied by the caller to encapsulate alignment restrictions * - hold pdc_lock while in PDC or using static result buffers * - use __pa() to convert virtual (kernel) pointers to physical * ones. * - the name of the struct used for pdc return values should equal * one of the macros used for the first two arguments to the * corresponding PDC call * - keep the order of arguments * - don't be smart (setting trailing NUL bytes for strings, return * something useful even if the call failed) unless you are sure * it's not going to affect functionality or performance * * Example: * int pdc_cache_info(struct pdc_cache_info *cache_info ) * { * int retval; * * spin_lock_irq(&pdc_lock); * retval = mem_pdc_call(PDC_CACHE,PDC_CACHE_INFO,__pa(cache_info),0); * convert_to_wide(pdc_result); * memcpy(cache_info, pdc_result, sizeof(*cache_info)); * spin_unlock_irq(&pdc_lock); * * return retval; * } * prumpf 991016 */ #include <linux/stdarg.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> #include <linux/spinlock.h> #include <asm/page.h> #include <asm/pdc.h> #include <asm/pdcpat.h> #include <asm/processor.h> /* for boot_cpu_data */ #if defined(BOOTLOADER) # undef spin_lock_irqsave # define spin_lock_irqsave(a, b) { b = 1; } # undef spin_unlock_irqrestore # define spin_unlock_irqrestore(a, b) #else static DEFINE_SPINLOCK(pdc_lock); #endif static unsigned long pdc_result[NUM_PDC_RESULT] __aligned(8); static unsigned long pdc_result2[NUM_PDC_RESULT] __aligned(8); #ifdef CONFIG_64BIT #define WIDE_FIRMWARE PDC_MODEL_OS64 #define NARROW_FIRMWARE PDC_MODEL_OS32 /* Firmware needs to be initially set to narrow to determine the * actual firmware width. */ int parisc_narrow_firmware __ro_after_init = NARROW_FIRMWARE; #endif /* On most currently-supported platforms, IODC I/O calls are 32-bit calls * and MEM_PDC calls are always the same width as the OS. * Some PAT boxes may have 64-bit IODC I/O. * * Ryan Bradetich added the now obsolete CONFIG_PDC_NARROW to allow * 64-bit kernels to run on systems with 32-bit MEM_PDC calls. * This allowed wide kernels to run on Cxxx boxes. * We now detect 32-bit-only PDC and dynamically switch to 32-bit mode * when running a 64-bit kernel on such boxes (e.g. C200 or C360). */ #ifdef CONFIG_64BIT long real64_call(unsigned long function, ...); #endif long real32_call(unsigned long function, ...); #ifdef CONFIG_64BIT # define MEM_PDC (unsigned long)(PAGE0->mem_pdc_hi) << 32 | PAGE0->mem_pdc # define mem_pdc_call(args...) unlikely(parisc_narrow_firmware) ? real32_call(MEM_PDC, args) : real64_call(MEM_PDC, args) #else # define MEM_PDC (unsigned long)PAGE0->mem_pdc # define mem_pdc_call(args...) real32_call(MEM_PDC, args) #endif /** * f_extend - Convert PDC addresses to kernel addresses. * @address: Address returned from PDC. * * This function is used to convert PDC addresses into kernel addresses * when the PDC address size and kernel address size are different. */ static unsigned long f_extend(unsigned long address) { #ifdef CONFIG_64BIT if(unlikely(parisc_narrow_firmware)) { if((address & 0xff000000) == 0xf0000000) return (0xfffffff0UL << 32) | (u32)address; if((address & 0xf0000000) == 0xf0000000) return (0xffffffffUL << 32) | (u32)address; } #endif return address; } /** * convert_to_wide - Convert the return buffer addresses into kernel addresses. * @addr: The return buffer from PDC. * * This function is used to convert the return buffer addresses retrieved from PDC * into kernel addresses when the PDC address size and kernel address size are * different. */ static void convert_to_wide(unsigned long *addr) { #ifdef CONFIG_64BIT int i; unsigned int *p = (unsigned int *)addr; if (unlikely(parisc_narrow_firmware)) { for (i = (NUM_PDC_RESULT-1); i >= 0; --i) addr[i] = p[i]; } #endif } #ifdef CONFIG_64BIT void set_firmware_width_unlocked(void) { int ret; ret = mem_pdc_call(PDC_MODEL, PDC_MODEL_CAPABILITIES, __pa(pdc_result), 0); if (ret < 0) return; convert_to_wide(pdc_result); if (pdc_result[0] != NARROW_FIRMWARE) parisc_narrow_firmware = 0; } /** * set_firmware_width - Determine if the firmware is wide or narrow. * * This function must be called before any pdc_* function that uses the * convert_to_wide function. */ void set_firmware_width(void) { unsigned long flags; /* already initialized? */ if (parisc_narrow_firmware != NARROW_FIRMWARE) return; spin_lock_irqsave(&pdc_lock, flags); set_firmware_width_unlocked(); spin_unlock_irqrestore(&pdc_lock, flags); } #else void set_firmware_width_unlocked(void) { return; } void set_firmware_width(void) { return; } #endif /*CONFIG_64BIT*/ #if !defined(BOOTLOADER) /** * pdc_emergency_unlock - Unlock the linux pdc lock * * This call unlocks the linux pdc lock in case we need some PDC functions * (like pdc_add_valid) during kernel stack dump. */ void pdc_emergency_unlock(void) { /* Spinlock DEBUG code freaks out if we unconditionally unlock */ if (spin_is_locked(&pdc_lock)) spin_unlock(&pdc_lock); } /** * pdc_add_valid - Verify address can be accessed without causing a HPMC. * @address: Address to be verified. * * This PDC call attempts to read from the specified address and verifies * if the address is valid. * * The return value is PDC_OK (0) in case accessing this address is valid. */ int pdc_add_valid(unsigned long address) { int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_ADD_VALID, PDC_ADD_VALID_VERIFY, address); spin_unlock_irqrestore(&pdc_lock, flags); return retval; } EXPORT_SYMBOL(pdc_add_valid); /** * pdc_instr - Get instruction that invokes PDCE_CHECK in HPMC handler. * @instr: Pointer to variable which will get instruction opcode. * * The return value is PDC_OK (0) in case call succeeded. */ int __init pdc_instr(unsigned int *instr) { int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_INSTR, 0UL, __pa(pdc_result)); convert_to_wide(pdc_result); *instr = pdc_result[0]; spin_unlock_irqrestore(&pdc_lock, flags); return retval; } /** * pdc_chassis_info - Return chassis information. * @chassis_info: The memory buffer address. * @led_info: The size of the memory buffer address. * @len: The size of the memory buffer address. * * An HVERSION dependent call for returning the chassis information. */ int __init pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_info, unsigned long len) { int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); memcpy(&pdc_result, chassis_info, sizeof(*chassis_info)); memcpy(&pdc_result2, led_info, len); retval = mem_pdc_call(PDC_CHASSIS, PDC_RETURN_CHASSIS_INFO, __pa(pdc_result), __pa(pdc_result2), len); memcpy(chassis_info, pdc_result, sizeof(*chassis_info)); memcpy(led_info, pdc_result2, len); spin_unlock_irqrestore(&pdc_lock, flags); return retval; } /** * pdc_pat_chassis_send_log - Sends a PDC PAT CHASSIS log message. * @state: state of the machine * @data: value for that state * * Must be correctly formatted or expect system crash */ #ifdef CONFIG_64BIT int pdc_pat_chassis_send_log(unsigned long state, unsigned long data) { int retval = 0; unsigned long flags; if (!is_pdc_pat()) return -1; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_PAT_CHASSIS_LOG, PDC_PAT_CHASSIS_WRITE_LOG, __pa(&state), __pa(&data)); spin_unlock_irqrestore(&pdc_lock, flags); return retval; } #endif /** * pdc_chassis_disp - Updates chassis code * @disp: value to show on display */ int pdc_chassis_disp(unsigned long disp) { int retval = 0; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_DISP, disp); spin_unlock_irqrestore(&pdc_lock, flags); return retval; } /** * __pdc_cpu_rendezvous - Stop currently executing CPU and do not return. */ int __pdc_cpu_rendezvous(void) { if (is_pdc_pat()) return mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_RENDEZVOUS); else return mem_pdc_call(PDC_PROC, 1, 0); } /** * pdc_cpu_rendezvous_lock - Lock PDC while transitioning to rendezvous state */ void pdc_cpu_rendezvous_lock(void) __acquires(&pdc_lock) { spin_lock(&pdc_lock); } /** * pdc_cpu_rendezvous_unlock - Unlock PDC after reaching rendezvous state */ void pdc_cpu_rendezvous_unlock(void) __releases(&pdc_lock) { spin_unlock(&pdc_lock); } /** * pdc_pat_get_PDC_entrypoint - Get PDC entry point for current CPU * @pdc_entry: pointer to where the PDC entry point should be stored */ int pdc_pat_get_PDC_entrypoint(unsigned long *pdc_entry) { int retval = 0; unsigned long flags; if (!IS_ENABLED(CONFIG_SMP) || !is_pdc_pat()) { *pdc_entry = MEM_PDC; return 0; } spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_GET_PDC_ENTRYPOINT, __pa(pdc_result)); *pdc_entry = pdc_result[0]; spin_unlock_irqrestore(&pdc_lock, flags); return retval; } /** * pdc_chassis_warn - Fetches chassis warnings * @warn: The warning value to be shown */ int pdc_chassis_warn(unsigned long *warn) { int retval = 0; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_WARN, __pa(pdc_result)); *warn = pdc_result[0]; spin_unlock_irqrestore(&pdc_lock, flags); return retval; } int pdc_coproc_cfg_unlocked(struct pdc_coproc_cfg *pdc_coproc_info) { int ret; ret = mem_pdc_call(PDC_COPROC, PDC_COPROC_CFG, __pa(pdc_result)); convert_to_wide(pdc_result); pdc_coproc_info->ccr_functional = pdc_result[0]; pdc_coproc_info->ccr_present = pdc_result[1]; pdc_coproc_info->revision = pdc_result[17]; pdc_coproc_info->model = pdc_result[18]; return ret; } /** * pdc_coproc_cfg - To identify coprocessors attached to the processor. * @pdc_coproc_info: Return buffer address. * * This PDC call returns the presence and status of all the coprocessors * attached to the processor. */ int pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info) { int ret; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); ret = pdc_coproc_cfg_unlocked(pdc_coproc_info); spin_unlock_irqrestore(&pdc_lock, flags); return ret; } /** * pdc_iodc_read - Read data from the modules IODC. * @actcnt: The actual number of bytes. * @hpa: The HPA of the module for the iodc read. * @index: The iodc entry point. * @iodc_data: A buffer memory for the iodc options. * @iodc_data_size: Size of the memory buffer. * * This PDC call reads from the IODC of the module specified by the hpa * argument. */ int pdc_iodc_read(unsigned long *actcnt, unsigned long hpa, unsigned int index, void *iodc_data, unsigned int iodc_data_size) { int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_IODC, PDC_IODC_READ, __pa(pdc_result), hpa, index, __pa(pdc_result2), iodc_data_size); convert_to_wide(pdc_result); *actcnt = pdc_result[0]; memcpy(iodc_data, pdc_result2, iodc_data_size); spin_unlock_irqrestore(&pdc_lock, flags); return retval; } EXPORT_SYMBOL(pdc_iodc_read); /** * pdc_system_map_find_mods - Locate unarchitected modules. * @pdc_mod_info: Return buffer address. * @mod_path: pointer to dev path structure. * @mod_index: fixed address module index. * * To locate and identify modules which reside at fixed I/O addresses, which * do not self-identify via architected bus walks. */ int pdc_system_map_find_mods(struct pdc_system_map_mod_info *pdc_mod_info, struct pdc_module_path *mod_path, long mod_index) { int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); memcpy(pdc_result2, mod_path, sizeof(*mod_path)); retval = mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_MODULE, __pa(pdc_result), __pa(pdc_result2), mod_index); convert_to_wide(pdc_result); memcpy(pdc_mod_info, pdc_result, sizeof(*pdc_mod_info)); memcpy(mod_path, pdc_result2, sizeof(*mod_path)); spin_unlock_irqrestore(&pdc_lock, flags); pdc_mod_info->mod_addr = f_extend(pdc_mod_info->mod_addr); return retval; } /** * pdc_system_map_find_addrs - Retrieve additional address ranges. * @pdc_addr_info: Return buffer address. * @mod_index: Fixed address module index. * @addr_index: Address range index. * * Retrieve additional information about subsequent address ranges for modules * with multiple address ranges. */ int pdc_system_map_find_addrs(struct pdc_system_map_addr_info *pdc_addr_info, long mod_index, long addr_index) { int retval; unsigned long flags; spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_ADDRESS, __pa(pdc_result), mod_index, addr_index); convert_to_wide(pdc_result); memcpy(pdc_addr_info, pdc_result, sizeof(*pdc_addr_info)); spin_unlock_irqrestore(&pdc_lock, flags);