X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fparisc%2Fkernel%2Ffirmware.c;h=2dc06b8e18171f0e485d750d85f56be4ac197802;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=8127a709887055de27284928649ffe5f0f5f81ec;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c index 8127a7098..2dc06b8e1 100644 --- a/arch/parisc/kernel/firmware.c +++ b/arch/parisc/kernel/firmware.c @@ -11,6 +11,7 @@ * Copyright 1999 The Puffin Group, (Alex deVries, David Kennedy) * Copyright 2003 Grant Grundler * Copyright 2003,2004 Ryan Bradetich + * Copyright 2004 Thibaut VARENE * * 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 @@ -65,10 +66,11 @@ #include #include +#include #include #include /* for boot_cpu_data */ -static spinlock_t pdc_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(pdc_lock); static unsigned long pdc_result[32] __attribute__ ((aligned (8))); static unsigned long pdc_result2[32] __attribute__ ((aligned (8))); @@ -78,18 +80,18 @@ static unsigned long pdc_result2[32] __attribute__ ((aligned (8))); /* Firmware needs to be initially set to narrow to determine the * actual firmware width. */ -int parisc_narrow_firmware = 1; +int parisc_narrow_firmware __read_mostly = 1; #endif -/* on all currently-supported platforms, IODC I/O calls are always - * 32-bit calls, and MEM_PDC calls are always the same width as the OS. - * This means Cxxx boxes can't run wide kernels right now. -PB +/* 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. * - * CONFIG_PDC_NARROW has been added to allow 64-bit kernels to run on - * systems with 32-bit MEM_PDC calls. This will allow wide kernels to - * run on Cxxx boxes now. -RB - * - * Note that 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 __LP64__ @@ -118,10 +120,10 @@ static unsigned long f_extend(unsigned long address) #ifdef __LP64__ if(unlikely(parisc_narrow_firmware)) { if((address & 0xff000000) == 0xf0000000) - return 0xf0f0f0f000000000 | (u32)address; + return 0xf0f0f0f000000000UL | (u32)address; if((address & 0xf0000000) == 0xf0000000) - return 0xffffffff00000000 | (u32)address; + return 0xffffffff00000000UL | (u32)address; } #endif return address; @@ -176,7 +178,9 @@ void __init set_firmware_width(void) */ void pdc_emergency_unlock(void) { - spin_unlock(&pdc_lock); + /* Spinlock DEBUG code freaks out if we unconditionally unlock */ + if (spin_is_locked(&pdc_lock)) + spin_unlock(&pdc_lock); } @@ -234,11 +238,11 @@ int __init pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_inf #ifdef __LP64__ int pdc_pat_chassis_send_log(unsigned long state, unsigned long data) { + int retval = 0; + if (!is_pdc_pat()) return -1; - int retval = 0; - spin_lock_irq(&pdc_lock); retval = mem_pdc_call(PDC_PAT_CHASSIS_LOG, PDC_PAT_CHASSIS_WRITE_LOG, __pa(&state), __pa(&data)); spin_unlock_irq(&pdc_lock); @@ -570,14 +574,118 @@ int pdc_lan_station_id(char *lan_addr, unsigned long hpa) } EXPORT_SYMBOL(pdc_lan_station_id); +/** + * pdc_stable_read - Read data from Stable Storage. + * @staddr: Stable Storage address to access. + * @memaddr: The memory address where Stable Storage data shall be copied. + * @count: number of bytes to transfert. count is multiple of 4. + * + * This PDC call reads from the Stable Storage address supplied in staddr + * and copies count bytes to the memory address memaddr. + * The call will fail if staddr+count > PDC_STABLE size. + */ +int pdc_stable_read(unsigned long staddr, void *memaddr, unsigned long count) +{ + int retval; + + spin_lock_irq(&pdc_lock); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_READ, staddr, + __pa(pdc_result), count); + convert_to_wide(pdc_result); + memcpy(memaddr, pdc_result, count); + spin_unlock_irq(&pdc_lock); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_read); + +/** + * pdc_stable_write - Write data to Stable Storage. + * @staddr: Stable Storage address to access. + * @memaddr: The memory address where Stable Storage data shall be read from. + * @count: number of bytes to transfert. count is multiple of 4. + * + * This PDC call reads count bytes from the supplied memaddr address, + * and copies count bytes to the Stable Storage address staddr. + * The call will fail if staddr+count > PDC_STABLE size. + */ +int pdc_stable_write(unsigned long staddr, void *memaddr, unsigned long count) +{ + int retval; + + spin_lock_irq(&pdc_lock); + memcpy(pdc_result, memaddr, count); + convert_to_wide(pdc_result); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_WRITE, staddr, + __pa(pdc_result), count); + spin_unlock_irq(&pdc_lock); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_write); + +/** + * pdc_stable_get_size - Get Stable Storage size in bytes. + * @size: pointer where the size will be stored. + * + * This PDC call returns the number of bytes in the processor's Stable + * Storage, which is the number of contiguous bytes implemented in Stable + * Storage starting from staddr=0. size in an unsigned 64-bit integer + * which is a multiple of four. + */ +int pdc_stable_get_size(unsigned long *size) +{ + int retval; + + spin_lock_irq(&pdc_lock); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_RETURN_SIZE, __pa(pdc_result)); + *size = pdc_result[0]; + spin_unlock_irq(&pdc_lock); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_get_size); + +/** + * pdc_stable_verify_contents - Checks that Stable Storage contents are valid. + * + * This PDC call is meant to be used to check the integrity of the current + * contents of Stable Storage. + */ +int pdc_stable_verify_contents(void) +{ + int retval; + + spin_lock_irq(&pdc_lock); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_VERIFY_CONTENTS); + spin_unlock_irq(&pdc_lock); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_verify_contents); + +/** + * pdc_stable_initialize - Sets Stable Storage contents to zero and initialize + * the validity indicator. + * + * This PDC call will erase all contents of Stable Storage. Use with care! + */ +int pdc_stable_initialize(void) +{ + int retval; + + spin_lock_irq(&pdc_lock); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_INITIALIZE); + spin_unlock_irq(&pdc_lock); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_initialize); /** * pdc_get_initiator - Get the SCSI Interface Card params (SCSI ID, SDTR, SE or LVD) * @hwpath: fully bc.mod style path to the device. - * @scsi_id: what someone told firmware the ID should be. - * @period: time in cycles - * @width: 8 or 16-bit wide bus - * @mode: 0,1,2 -> SE,HVD,LVD signalling mode + * @initiator: the array to return the result into * * Get the SCSI operational parameters from PDC. * Needed since HPUX never used BIOS or symbios card NVRAM. @@ -588,8 +696,7 @@ EXPORT_SYMBOL(pdc_lan_station_id); * o cable too long (ie SE scsi 10Mhz won't support 6m length), * o bus width exported is less than what the interface chip supports. */ -int pdc_get_initiator(struct hardware_path *hwpath, unsigned char *scsi_id, - unsigned long *period, char *width, char *mode) +int pdc_get_initiator(struct hardware_path *hwpath, struct pdc_initiator *initiator) { int retval; @@ -601,44 +708,38 @@ int pdc_get_initiator(struct hardware_path *hwpath, unsigned char *scsi_id, retval = mem_pdc_call(PDC_INITIATOR, PDC_GET_INITIATOR, __pa(pdc_result), __pa(hwpath)); - if (retval < PDC_OK) - goto fail; - - *scsi_id = (unsigned char) pdc_result[0]; + goto out; - /* convert Bus speed in Mhz to period (in 1/10 ns) */ - switch (pdc_result[1]) { - /* - * case 0: driver determines rate - * case -1: Settings are uninitialized. - */ - case 5: *period = 2000; break; - case 10: *period = 1000; break; - case 20: *period = 500; break; - case 40: *period = 250; break; - case 80: *period = 125; break; - default: /* Do nothing */ break; + if (pdc_result[0] < 16) { + initiator->host_id = pdc_result[0]; + } else { + initiator->host_id = -1; } - /* - * pdc_result[2] PDC suggested SCSI id - * pdc_result[3] PDC suggested SCSI rate + /* + * Sprockets and Piranha return 20 or 40 (MT/s). Prelude returns + * 1, 2, 5 or 10 for 5, 10, 20 or 40 MT/s, respectively */ + switch (pdc_result[1]) { + case 1: initiator->factor = 50; break; + case 2: initiator->factor = 25; break; + case 5: initiator->factor = 12; break; + case 25: initiator->factor = 10; break; + case 20: initiator->factor = 12; break; + case 40: initiator->factor = 10; break; + default: initiator->factor = -1; break; + } if (IS_SPROCKETS()) { - /* 0 == 8-bit, 1 == 16-bit */ - *width = (char) pdc_result[4]; - - /* ...in case someone needs it in the future. - * sym53c8xx.c comments say it can't autodetect - * for 825/825A/875 chips. - * 0 == SE, 1 == HVD, 2 == LVD - */ - *mode = (char) pdc_result[5]; + initiator->width = pdc_result[4]; + initiator->mode = pdc_result[5]; + } else { + initiator->width = -1; + initiator->mode = -1; } - fail: + out: spin_unlock_irq(&pdc_lock); return (retval >= PDC_OK); } @@ -681,6 +782,8 @@ int pdc_pci_irt(unsigned long num_entries, unsigned long hpa, void *tbl) { int retval; + BUG_ON((unsigned long)tbl & 0x7); + spin_lock_irq(&pdc_lock); pdc_result[0] = num_entries; retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL, @@ -811,7 +914,7 @@ int pdc_do_firm_test_reset(unsigned long ftc_bitmap) * * Reset the system. */ -int pdc_do_reset() +int pdc_do_reset(void) { int retval; @@ -1146,6 +1249,49 @@ int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr, return retval; } + +/** + * pdc_pat_io_pci_cfg_read - Read PCI configuration space. + * @pci_addr: PCI configuration space address for which the read request is being made. + * @pci_size: Size of read in bytes. Valid values are 1, 2, and 4. + * @mem_addr: Pointer to return memory buffer. + * + */ +int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *mem_addr) +{ + int retval; + spin_lock_irq(&pdc_lock); + retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_READ, + __pa(pdc_result), pci_addr, pci_size); + switch(pci_size) { + case 1: *(u8 *) mem_addr = (u8) pdc_result[0]; + case 2: *(u16 *)mem_addr = (u16) pdc_result[0]; + case 4: *(u32 *)mem_addr = (u32) pdc_result[0]; + } + spin_unlock_irq(&pdc_lock); + + return retval; +} + +/** + * pdc_pat_io_pci_cfg_write - Retrieve information about memory address ranges. + * @pci_addr: PCI configuration space address for which the write request is being made. + * @pci_size: Size of write in bytes. Valid values are 1, 2, and 4. + * @value: Pointer to 1, 2, or 4 byte value in low order end of argument to be + * written to PCI Config space. + * + */ +int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val) +{ + int retval; + + spin_lock_irq(&pdc_lock); + retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_WRITE, + pci_addr, pci_size, val); + spin_unlock_irq(&pdc_lock); + + return retval; +} #endif /* __LP64__ */ @@ -1230,29 +1376,29 @@ struct wide_stack { long real64_call(unsigned long fn, ...) { va_list args; - extern struct wide_stack real_stack; + extern struct wide_stack real64_stack; extern unsigned long real64_call_asm(unsigned long *, unsigned long *, unsigned long); va_start(args, fn); - real_stack.arg0 = va_arg(args, unsigned long); - real_stack.arg1 = va_arg(args, unsigned long); - real_stack.arg2 = va_arg(args, unsigned long); - real_stack.arg3 = va_arg(args, unsigned long); - real_stack.arg4 = va_arg(args, unsigned long); - real_stack.arg5 = va_arg(args, unsigned long); - real_stack.arg6 = va_arg(args, unsigned long); - real_stack.arg7 = va_arg(args, unsigned long); - real_stack.arg8 = va_arg(args, unsigned long); - real_stack.arg9 = va_arg(args, unsigned long); - real_stack.arg10 = va_arg(args, unsigned long); - real_stack.arg11 = va_arg(args, unsigned long); - real_stack.arg12 = va_arg(args, unsigned long); - real_stack.arg13 = va_arg(args, unsigned long); + real64_stack.arg0 = va_arg(args, unsigned long); + real64_stack.arg1 = va_arg(args, unsigned long); + real64_stack.arg2 = va_arg(args, unsigned long); + real64_stack.arg3 = va_arg(args, unsigned long); + real64_stack.arg4 = va_arg(args, unsigned long); + real64_stack.arg5 = va_arg(args, unsigned long); + real64_stack.arg6 = va_arg(args, unsigned long); + real64_stack.arg7 = va_arg(args, unsigned long); + real64_stack.arg8 = va_arg(args, unsigned long); + real64_stack.arg9 = va_arg(args, unsigned long); + real64_stack.arg10 = va_arg(args, unsigned long); + real64_stack.arg11 = va_arg(args, unsigned long); + real64_stack.arg12 = va_arg(args, unsigned long); + real64_stack.arg13 = va_arg(args, unsigned long); va_end(args); - return real64_call_asm(&real_stack.sp, &real_stack.arg0, fn); + return real64_call_asm(&real64_stack.sp, &real64_stack.arg0, fn); } #endif /* __LP64__ */