/*
* arch/parisc/kernel/firmware.c - safe PDC access routines
*
* PDC == Processor Dependent Code
*
* See http://www.parisc-linux.org/documentation/index.html
* 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 Thibaut VARENE <varenet@parisc-linux.org>
*
* 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.
*
*/
/* 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 <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/system.h>
#include <asm/processor.h> /* for boot_cpu_data */
static DEFINE_SPINLOCK(pdc_lock);
static unsigned long pdc_result[32] __attribute__ ((aligned (8)));
static unsigned long pdc_result2[32] __attribute__ ((aligned (8)));
#ifdef __LP64__
#define WIDE_FIRMWARE 0x1
#define NARROW_FIRMWARE 0x2
/* Firmware needs to be initially set to narrow to determine the
* actual firmware width. */
int parisc_narrow_firmware = 1;
#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 __LP64__
long real64_call(unsigned long function, ...);
#endif
long real32_call(unsigned long function, ...);
#ifdef __LP64__