diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /arch/parisc/kernel/firmware.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'arch/parisc/kernel/firmware.c')
-rw-r--r-- | arch/parisc/kernel/firmware.c | 1867 |
1 files changed, 1867 insertions, 0 deletions
diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c new file mode 100644 index 000000000..6817892a2 --- /dev/null +++ b/arch/parisc/kernel/firmware.c @@ -0,0 +1,1867 @@ +// 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 + +unsigned long pdc_result[NUM_PDC_RESULT] __aligned(8); +unsigned long pdc_result2[NUM_PDC_RESULT] __aligned(8); + +#ifdef CONFIG_64BIT +#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 __ro_after_init = 2; +#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 0xf0f0f0f000000000UL | (u32)address; + + if((address & 0xf0000000) == 0xf0000000) + return 0xffffffff00000000UL | (u32)address; + } +#endif + return address; +} + +/** + * convert_to_wide - Convert the return buffer addresses into kernel addresses. + * @address: 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); + 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 != 2) + 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. + * @result: The return buffer. + * @chassis_info: 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. + * @retval: -1 on error, 0 on success. Other value are PDC errors + * + * 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 + * @retval: -1 on error, 0 on success + */ +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_rendenzvous - Stop currently executing CPU + * @retval: -1 on error, 0 on success + */ +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) +{ + spin_lock(&pdc_lock); +} + +/** + * pdc_cpu_rendezvous_unlock - Unlock PDC after reaching rendezvous state + */ +void pdc_cpu_rendezvous_unlock(void) +{ + spin_unlock(&pdc_lock); +} + +/** + * pdc_pat_get_PDC_entrypoint - Get PDC entry point for current CPU + * @retval: -1 on error, 0 on success + */ +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 + * @retval: -1 on error, 0 on success + */ +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); + 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); + + pdc_addr_info->mod_addr = f_extend(pdc_addr_info->mod_addr); + return retval; +} + +/** + * pdc_model_info - Return model information about the processor. + * @model: The return buffer. + * + * Returns the version numbers, identifiers, and capabilities from the processor module. + */ +int pdc_model_info(struct pdc_model *model) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_INFO, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + memcpy(model, pdc_result, sizeof(*model)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_sysmodel - Get the system model name. + * @name: A char array of at least 81 characters. + * + * Get system model name from PDC ROM (e.g. 9000/715 or 9000/778/B160L). + * Using OS_ID_HPUX will return the equivalent of the 'modelname' command + * on HP/UX. + */ +int pdc_model_sysmodel(unsigned int os_id, char *name) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_SYSMODEL, __pa(pdc_result), + os_id, __pa(name)); + convert_to_wide(pdc_result); + + if (retval == PDC_OK) { + name[pdc_result[0]] = '\0'; /* add trailing '\0' */ + } else { + name[0] = 0; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_versions - Identify the version number of each processor. + * @cpu_id: The return buffer. + * @id: The id of the processor to check. + * + * Returns the version number for each processor component. + * + * This comment was here before, but I do not know what it means :( -RB + * id: 0 = cpu revision, 1 = boot-rom-version + */ +int pdc_model_versions(unsigned long *versions, int id) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_VERSIONS, __pa(pdc_result), id); + convert_to_wide(pdc_result); + *versions = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_cpuid - Returns the CPU_ID. + * @cpu_id: The return buffer. + * + * Returns the CPU_ID value which uniquely identifies the cpu portion of + * the processor module. + */ +int pdc_model_cpuid(unsigned long *cpu_id) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + pdc_result[0] = 0; /* preset zero (call may not be implemented!) */ + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_CPU_ID, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + *cpu_id = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_capabilities - Returns the platform capabilities. + * @capabilities: The return buffer. + * + * Returns information about platform support for 32- and/or 64-bit + * OSes, IO-PDIR coherency, and virtual aliasing. + */ +int pdc_model_capabilities(unsigned long *capabilities) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + pdc_result[0] = 0; /* preset zero (call may not be implemented!) */ + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_CAPABILITIES, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + if (retval == PDC_OK) { + *capabilities = pdc_result[0]; + } else { + *capabilities = PDC_MODEL_OS32; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_platform_info - Returns machine product and serial number. + * @orig_prod_num: Return buffer for original product number. + * @current_prod_num: Return buffer for current product number. + * @serial_no: Return buffer for serial number. + * + * Returns strings containing the original and current product numbers and the + * serial number of the system. + */ +int pdc_model_platform_info(char *orig_prod_num, char *current_prod_num, + char *serial_no) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_GET_PLATFORM_INFO, + __pa(orig_prod_num), __pa(current_prod_num), __pa(serial_no)); + convert_to_wide(pdc_result); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_cache_info - Return cache and TLB information. + * @cache_info: The return buffer. + * + * Returns information about the processor's cache and TLB. + */ +int pdc_cache_info(struct pdc_cache_info *cache_info) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_CACHE, PDC_CACHE_INFO, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + memcpy(cache_info, pdc_result, sizeof(*cache_info)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_spaceid_bits - Return whether Space ID hashing is turned on. + * @space_bits: Should be 0, if not, bad mojo! + * + * Returns information about Space ID hashing. + */ +int pdc_spaceid_bits(unsigned long *space_bits) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + pdc_result[0] = 0; + retval = mem_pdc_call(PDC_CACHE, PDC_CACHE_RET_SPID, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + *space_bits = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +#ifndef CONFIG_PA20 +/** + * pdc_btlb_info - Return block TLB information. + * @btlb: The return buffer. + * + * Returns information about the hardware Block TLB. + */ +int pdc_btlb_info(struct pdc_btlb_info *btlb) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0); + memcpy(btlb, pdc_result, sizeof(*btlb)); + spin_unlock_irqrestore(&pdc_lock, flags); + + if(retval < 0) { + btlb->max_size = 0; + } + return retval; +} + +/** + * pdc_mem_map_hpa - Find fixed module information. + * @address: The return buffer + * @mod_path: pointer to dev path structure. + * + * This call was developed for S700 workstations to allow the kernel to find + * the I/O devices (Core I/O). In the future (Kittyhawk and beyond) this + * call will be replaced (on workstations) by the architected PDC_SYSTEM_MAP + * call. + * + * This call is supported by all existing S700 workstations (up to Gecko). + */ +int pdc_mem_map_hpa(struct pdc_memory_map *address, + struct pdc_module_path *mod_path) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + memcpy(pdc_result2, mod_path, sizeof(*mod_path)); + retval = mem_pdc_call(PDC_MEM_MAP, PDC_MEM_MAP_HPA, __pa(pdc_result), + __pa(pdc_result2)); + memcpy(address, pdc_result, sizeof(*address)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* !CONFIG_PA20 */ + +/** + * pdc_lan_station_id - Get the LAN address. + * @lan_addr: The return buffer. + * @hpa: The network device HPA. + * + * Get the LAN station address when it is not directly available from the LAN hardware. + */ +int pdc_lan_station_id(char *lan_addr, unsigned long hpa) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_LAN_STATION_ID, PDC_LAN_STATION_ID_READ, + __pa(pdc_result), hpa); + if (retval < 0) { + /* FIXME: else read MAC from NVRAM */ + memset(lan_addr, 0, PDC_LAN_STATION_ID_SIZE); + } else { + memcpy(lan_addr, pdc_result, PDC_LAN_STATION_ID_SIZE); + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +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 transfer. 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + 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_irqrestore(&pdc_lock, flags); + + 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 transfer. 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + 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_irqrestore(&pdc_lock, flags); + + 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_RETURN_SIZE, __pa(pdc_result)); + *size = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_VERIFY_CONTENTS); + spin_unlock_irqrestore(&pdc_lock, flags); + + 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_INITIALIZE); + spin_unlock_irqrestore(&pdc_lock, flags); + + 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. + * @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. + * Most ncr/sym cards won't have an entry and just use whatever + * capabilities of the card are (eg Ultra, LVD). But there are + * several cases where it's useful: + * o set SCSI id for Multi-initiator clusters, + * 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, struct pdc_initiator *initiator) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + +/* BCJ-XXXX series boxes. E.G. "9000/785/C3000" */ +#define IS_SPROCKETS() (strlen(boot_cpu_data.pdc.sys_model_name) == 14 && \ + strncmp(boot_cpu_data.pdc.sys_model_name, "9000/785", 8) == 0) + + retval = mem_pdc_call(PDC_INITIATOR, PDC_GET_INITIATOR, + __pa(pdc_result), __pa(hwpath)); + if (retval < PDC_OK) + goto out; + + if (pdc_result[0] < 16) { + initiator->host_id = pdc_result[0]; + } else { + initiator->host_id = -1; + } + + /* + * 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()) { + initiator->width = pdc_result[4]; + initiator->mode = pdc_result[5]; + } else { + initiator->width = -1; + initiator->mode = -1; + } + + out: + spin_unlock_irqrestore(&pdc_lock, flags); + + return (retval >= PDC_OK); +} +EXPORT_SYMBOL(pdc_get_initiator); + + +/** + * pdc_pci_irt_size - Get the number of entries in the interrupt routing table. + * @num_entries: The return value. + * @hpa: The HPA for the device. + * + * This PDC function returns the number of entries in the specified cell's + * interrupt table. + * Similar to PDC_PAT stuff - but added for Forte/Allegro boxes + */ +int pdc_pci_irt_size(unsigned long *num_entries, unsigned long hpa) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SIZE, + __pa(pdc_result), hpa); + convert_to_wide(pdc_result); + *num_entries = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pci_irt - Get the PCI interrupt routing table. + * @num_entries: The number of entries in the table. + * @hpa: The Hard Physical Address of the device. + * @tbl: + * + * Get the PCI interrupt routing table for the device at the given HPA. + * Similar to PDC_PAT stuff - but added for Forte/Allegro boxes + */ +int pdc_pci_irt(unsigned long num_entries, unsigned long hpa, void *tbl) +{ + int retval; + unsigned long flags; + + BUG_ON((unsigned long)tbl & 0x7); + + spin_lock_irqsave(&pdc_lock, flags); + pdc_result[0] = num_entries; + retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL, + __pa(pdc_result), hpa, __pa(tbl)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + + +#if 0 /* UNTEST CODE - left here in case someone needs it */ + +/** + * pdc_pci_config_read - read PCI config space. + * @hpa token from PDC to indicate which PCI device + * @pci_addr configuration space address to read from + * + * Read PCI Configuration space *before* linux PCI subsystem is running. + */ +unsigned int pdc_pci_config_read(void *hpa, unsigned long cfg_addr) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + pdc_result[0] = 0; + pdc_result[1] = 0; + retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_READ_CONFIG, + __pa(pdc_result), hpa, cfg_addr&~3UL, 4UL); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval ? ~0 : (unsigned int) pdc_result[0]; +} + + +/** + * pdc_pci_config_write - read PCI config space. + * @hpa token from PDC to indicate which PCI device + * @pci_addr configuration space address to write + * @val value we want in the 32-bit register + * + * Write PCI Configuration space *before* linux PCI subsystem is running. + */ +void pdc_pci_config_write(void *hpa, unsigned long cfg_addr, unsigned int val) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + pdc_result[0] = 0; + retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_WRITE_CONFIG, + __pa(pdc_result), hpa, + cfg_addr&~3UL, 4UL, (unsigned long) val); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* UNTESTED CODE */ + +/** + * pdc_tod_read - Read the Time-Of-Day clock. + * @tod: The return buffer: + * + * Read the Time-Of-Day clock + */ +int pdc_tod_read(struct pdc_tod *tod) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_TOD, PDC_TOD_READ, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + memcpy(tod, pdc_result, sizeof(*tod)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_tod_read); + +int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MEM, PDC_MEM_MEMINFO, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + memcpy(rinfo, pdc_result, sizeof(*rinfo)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *pret, + unsigned long *pdt_entries_ptr) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MEM, PDC_MEM_READ_PDT, __pa(pdc_result), + __pa(pdt_entries_ptr)); + if (retval == PDC_OK) { + convert_to_wide(pdc_result); + memcpy(pret, pdc_result, sizeof(*pret)); + } + spin_unlock_irqrestore(&pdc_lock, flags); + +#ifdef CONFIG_64BIT + /* + * 64-bit kernels should not call this PDT function in narrow mode. + * The pdt_entries_ptr array above will now contain 32-bit values + */ + if (WARN_ON_ONCE((retval == PDC_OK) && parisc_narrow_firmware)) + return PDC_ERROR; +#endif + + return retval; +} + +/** + * pdc_pim_toc11 - Fetch TOC PIM 1.1 data from firmware. + * @ret: pointer to return buffer + */ +int pdc_pim_toc11(struct pdc_toc_pim_11 *ret) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PIM, PDC_PIM_TOC, __pa(pdc_result), + __pa(ret), sizeof(*ret)); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +/** + * pdc_pim_toc20 - Fetch TOC PIM 2.0 data from firmware. + * @ret: pointer to return buffer + */ +int pdc_pim_toc20(struct pdc_toc_pim_20 *ret) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PIM, PDC_PIM_TOC, __pa(pdc_result), + __pa(ret), sizeof(*ret)); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +/** + * pdc_tod_set - Set the Time-Of-Day clock. + * @sec: The number of seconds since epoch. + * @usec: The number of micro seconds. + * + * Set the Time-Of-Day clock. + */ +int pdc_tod_set(unsigned long sec, unsigned long usec) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_TOD, PDC_TOD_WRITE, sec, usec); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_tod_set); + +#ifdef CONFIG_64BIT +int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr, + struct pdc_memory_table *tbl, unsigned long entries) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MEM, PDC_MEM_TABLE, __pa(pdc_result), __pa(pdc_result2), entries); + convert_to_wide(pdc_result); + memcpy(r_addr, pdc_result, sizeof(*r_addr)); + memcpy(tbl, pdc_result2, entries * sizeof(*tbl)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* CONFIG_64BIT */ + +/* FIXME: Is this pdc used? I could not find type reference to ftc_bitmap + * so I guessed at unsigned long. Someone who knows what this does, can fix + * it later. :) + */ +int pdc_do_firm_test_reset(unsigned long ftc_bitmap) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BROADCAST_RESET, PDC_DO_FIRM_TEST_RESET, + PDC_FIRM_TEST_MAGIC, ftc_bitmap); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_do_reset - Reset the system. + * + * Reset the system. + */ +int pdc_do_reset(void) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BROADCAST_RESET, PDC_DO_RESET); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_soft_power_info - Enable soft power switch. + * @power_reg: address of soft power register + * + * Return the absolute address of the soft power switch register + */ +int __init pdc_soft_power_info(unsigned long *power_reg) +{ + int retval; + unsigned long flags; + + *power_reg = (unsigned long) (-1); + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_SOFT_POWER, PDC_SOFT_POWER_INFO, __pa(pdc_result), 0); + if (retval == PDC_OK) { + convert_to_wide(pdc_result); + *power_reg = f_extend(pdc_result[0]); + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_soft_power_button - Control the soft power button behaviour + * @sw_control: 0 for hardware control, 1 for software control + * + * + * This PDC function places the soft power button under software or + * hardware control. + * Under software control the OS may control to when to allow to shut + * down the system. Under hardware control pressing the power button + * powers off the system immediately. + */ +int pdc_soft_power_button(int sw_control) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_SOFT_POWER, PDC_SOFT_POWER_ENABLE, __pa(pdc_result), sw_control); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_io_reset - Hack to avoid overlapping range registers of Bridges devices. + * Primarily a problem on T600 (which parisc-linux doesn't support) but + * who knows what other platform firmware might do with this OS "hook". + */ +void pdc_io_reset(void) +{ + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + mem_pdc_call(PDC_IO, PDC_IO_RESET, 0); + spin_unlock_irqrestore(&pdc_lock, flags); +} + +/* + * pdc_io_reset_devices - Hack to Stop USB controller + * + * If PDC used the usb controller, the usb controller + * is still running and will crash the machines during iommu + * setup, because of still running DMA. This PDC call + * stops the USB controller. + * Normally called after calling pdc_io_reset(). + */ +void pdc_io_reset_devices(void) +{ + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + mem_pdc_call(PDC_IO, PDC_IO_RESET_DEVICES, 0); + spin_unlock_irqrestore(&pdc_lock, flags); +} + +#endif /* defined(BOOTLOADER) */ + +/* locked by pdc_lock */ +static char iodc_dbuf[4096] __page_aligned_bss; + +/** + * pdc_iodc_print - Console print using IODC. + * @str: the string to output. + * @count: length of str + * + * Note that only these special chars are architected for console IODC io: + * BEL, BS, CR, and LF. Others are passed through. + * Since the HP console requires CR+LF to perform a 'newline', we translate + * "\n" to "\r\n". + */ +int pdc_iodc_print(const unsigned char *str, unsigned count) +{ + unsigned int i, found = 0; + unsigned long flags; + + count = min_t(unsigned int, count, sizeof(iodc_dbuf)); + + spin_lock_irqsave(&pdc_lock, flags); + for (i = 0; i < count;) { + switch(str[i]) { + case '\n': + iodc_dbuf[i+0] = '\r'; + iodc_dbuf[i+1] = '\n'; + i += 2; + found = 1; + goto print; + default: + iodc_dbuf[i] = str[i]; + i++; + break; + } + } + +print: + real32_call(PAGE0->mem_cons.iodc_io, + (unsigned long)PAGE0->mem_cons.hpa, ENTRY_IO_COUT, + PAGE0->mem_cons.spa, __pa(PAGE0->mem_cons.dp.layers), + __pa(pdc_result), 0, __pa(iodc_dbuf), i, 0); + spin_unlock_irqrestore(&pdc_lock, flags); + + return i - found; +} + +#if !defined(BOOTLOADER) +/** + * pdc_iodc_getc - Read a character (non-blocking) from the PDC console. + * + * Read a character (non-blocking) from the PDC console, returns -1 if + * key is not present. + */ +int pdc_iodc_getc(void) +{ + int ch; + int status; + unsigned long flags; + + /* Bail if no console input device. */ + if (!PAGE0->mem_kbd.iodc_io) + return 0; + + /* wait for a keyboard (rs232)-input */ + spin_lock_irqsave(&pdc_lock, flags); + real32_call(PAGE0->mem_kbd.iodc_io, + (unsigned long)PAGE0->mem_kbd.hpa, ENTRY_IO_CIN, + PAGE0->mem_kbd.spa, __pa(PAGE0->mem_kbd.dp.layers), + __pa(pdc_result), 0, __pa(iodc_dbuf), 1, 0); + + ch = *iodc_dbuf; + /* like convert_to_wide() but for first return value only: */ + status = *(int *)&pdc_result; + spin_unlock_irqrestore(&pdc_lock, flags); + + if (status == 0) + return -1; + + return ch; +} + +int pdc_sti_call(unsigned long func, unsigned long flags, + unsigned long inptr, unsigned long outputr, + unsigned long glob_cfg) +{ + int retval; + unsigned long irqflags; + + spin_lock_irqsave(&pdc_lock, irqflags); + retval = real32_call(func, flags, inptr, outputr, glob_cfg); + spin_unlock_irqrestore(&pdc_lock, irqflags); + + return retval; +} +EXPORT_SYMBOL(pdc_sti_call); + +#ifdef CONFIG_64BIT +/** + * pdc_pat_cell_get_number - Returns the cell number. + * @cell_info: The return buffer. + * + * This PDC call returns the cell number of the cell from which the call + * is made. + */ +int pdc_pat_cell_get_number(struct pdc_pat_cell_num *cell_info) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CELL, PDC_PAT_CELL_GET_NUMBER, __pa(pdc_result)); + memcpy(cell_info, pdc_result, sizeof(*cell_info)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_cell_module - Retrieve the cell's module information. + * @actcnt: The number of bytes written to mem_addr. + * @ploc: The physical location. + * @mod: The module index. + * @view_type: The view of the address type. + * @mem_addr: The return buffer. + * + * This PDC call returns information about each module attached to the cell + * at the specified location. + */ +int pdc_pat_cell_module(unsigned long *actcnt, unsigned long ploc, unsigned long mod, + unsigned long view_type, void *mem_addr) +{ + int retval; + unsigned long flags; + static struct pdc_pat_cell_mod_maddr_block result __attribute__ ((aligned (8))); + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CELL, PDC_PAT_CELL_MODULE, __pa(pdc_result), + ploc, mod, view_type, __pa(&result)); + if(!retval) { + *actcnt = pdc_result[0]; + memcpy(mem_addr, &result, *actcnt); + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_cell_info - Retrieve the cell's information. + * @info: The pointer to a struct pdc_pat_cell_info_rtn_block. + * @actcnt: The number of bytes which should be written to info. + * @offset: offset of the structure. + * @cell_number: The cell number which should be asked, or -1 for current cell. + * + * This PDC call returns information about the given cell (or all cells). + */ +int pdc_pat_cell_info(struct pdc_pat_cell_info_rtn_block *info, + unsigned long *actcnt, unsigned long offset, + unsigned long cell_number) +{ + int retval; + unsigned long flags; + struct pdc_pat_cell_info_rtn_block result; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CELL, PDC_PAT_CELL_GET_INFO, + __pa(pdc_result), __pa(&result), *actcnt, + offset, cell_number); + if (!retval) { + *actcnt = pdc_result[0]; + memcpy(info, &result, *actcnt); + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_cpu_get_number - Retrieve the cpu number. + * @cpu_info: The return buffer. + * @hpa: The Hard Physical Address of the CPU. + * + * Retrieve the cpu number for the cpu at the specified HPA. + */ +int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, unsigned long hpa) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_GET_NUMBER, + __pa(&pdc_result), hpa); + memcpy(cpu_info, pdc_result, sizeof(*cpu_info)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_get_irt_size - Retrieve the number of entries in the cell's interrupt table. + * @num_entries: The return value. + * @cell_num: The target cell. + * + * This PDC function returns the number of entries in the specified cell's + * interrupt table. + */ +int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_GET_PCI_ROUTING_TABLE_SIZE, + __pa(pdc_result), cell_num); + *num_entries = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_get_irt - Retrieve the cell's interrupt table. + * @r_addr: The return buffer. + * @cell_num: The target cell. + * + * This PDC function returns the actual interrupt table for the specified cell. + */ +int pdc_pat_get_irt(void *r_addr, unsigned long cell_num) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_GET_PCI_ROUTING_TABLE, + __pa(r_addr), cell_num); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_pd_get_addr_map - Retrieve information about memory address ranges. + * @actlen: The return buffer. + * @mem_addr: Pointer to the memory buffer. + * @count: The number of bytes to read from the buffer. + * @offset: The offset with respect to the beginning of the buffer. + * + */ +int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr, + unsigned long count, unsigned long offset) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_PD, PDC_PAT_PD_GET_ADDR_MAP, __pa(pdc_result), + __pa(pdc_result2), count, offset); + *actual_len = pdc_result[0]; + memcpy(mem_addr, pdc_result2, *actual_len); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_pd_get_PDC_interface_revisions - Retrieve PDC interface revisions. + * @legacy_rev: The legacy revision. + * @pat_rev: The PAT revision. + * @pdc_cap: The PDC capabilities. + * + */ +int pdc_pat_pd_get_pdc_revisions(unsigned long *legacy_rev, + unsigned long *pat_rev, unsigned long *pdc_cap) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_PD, PDC_PAT_PD_GET_PDC_INTERF_REV, + __pa(pdc_result)); + if (retval == PDC_OK) { + *legacy_rev = pdc_result[0]; + *pat_rev = pdc_result[1]; + *pdc_cap = pdc_result[2]; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + 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]; break; + case 2: *(u16 *)mem_addr = (u16) pdc_result[0]; break; + case 4: *(u32 *)mem_addr = (u32) pdc_result[0]; break; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + 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; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_WRITE, + pci_addr, pci_size, val); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_pdc_info - Retrieve information about page deallocation table + * @rinfo: memory pdt information + * + */ +int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_INFO, + __pa(&pdc_result)); + if (retval == PDC_OK) + memcpy(rinfo, &pdc_result, sizeof(*rinfo)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_pdt_cell_info - Retrieve information about page deallocation + * table of a cell + * @rinfo: memory pdt information + * @cell: cell number + * + */ +int pdc_pat_mem_pdt_cell_info(struct pdc_pat_mem_cell_pdt_retinfo *rinfo, + unsigned long cell) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_CELL_INFO, + __pa(&pdc_result), cell); + if (retval == PDC_OK) + memcpy(rinfo, &pdc_result, sizeof(*rinfo)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_read_cell_pdt - Read PDT entries from (old) PAT firmware + * @pret: array of PDT entries + * @pdt_entries_ptr: ptr to hold number of PDT entries + * @max_entries: maximum number of entries to be read + * + */ +int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, + unsigned long *pdt_entries_ptr, unsigned long max_entries) +{ + int retval; + unsigned long flags, entries; + + spin_lock_irqsave(&pdc_lock, flags); + /* PDC_PAT_MEM_CELL_READ is available on early PAT machines only */ + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_CELL_READ, + __pa(&pdc_result), parisc_cell_num, + __pa(pdt_entries_ptr)); + + if (retval == PDC_OK) { + /* build up return value as for PDC_PAT_MEM_PD_READ */ + entries = min(pdc_result[0], max_entries); + pret->pdt_entries = entries; + pret->actual_count_bytes = entries * sizeof(unsigned long); + } + + spin_unlock_irqrestore(&pdc_lock, flags); + WARN_ON(retval == PDC_OK && pdc_result[0] > max_entries); + + return retval; +} +/** + * pdc_pat_mem_read_pd_pdt - Read PDT entries from (newer) PAT firmware + * @pret: array of PDT entries + * @pdt_entries_ptr: ptr to hold number of PDT entries + * @count: number of bytes to read + * @offset: offset to start (in bytes) + * + */ +int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, + unsigned long *pdt_entries_ptr, unsigned long count, + unsigned long offset) +{ + int retval; + unsigned long flags, entries; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_READ, + __pa(&pdc_result), __pa(pdt_entries_ptr), + count, offset); + + if (retval == PDC_OK) { + entries = min(pdc_result[0], count); + pret->actual_count_bytes = entries; + pret->pdt_entries = entries / sizeof(unsigned long); + } + + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_get_dimm_phys_location - Get physical DIMM slot via PAT firmware + * @pret: ptr to hold returned information + * @phys_addr: physical address to examine + * + */ +int pdc_pat_mem_get_dimm_phys_location( + struct pdc_pat_mem_phys_mem_location *pret, + unsigned long phys_addr) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_ADDRESS, + __pa(&pdc_result), phys_addr); + + if (retval == PDC_OK) + memcpy(pret, &pdc_result, sizeof(*pret)); + + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* CONFIG_64BIT */ +#endif /* defined(BOOTLOADER) */ + + +/***************** 32-bit real-mode calls ***********/ +/* The struct below is used + * to overlay real_stack (real2.S), preparing a 32-bit call frame. + * real32_call_asm() then uses this stack in narrow real mode + */ + +struct narrow_stack { + /* use int, not long which is 64 bits */ + unsigned int arg13; + unsigned int arg12; + unsigned int arg11; + unsigned int arg10; + unsigned int arg9; + unsigned int arg8; + unsigned int arg7; + unsigned int arg6; + unsigned int arg5; + unsigned int arg4; + unsigned int arg3; + unsigned int arg2; + unsigned int arg1; + unsigned int arg0; + unsigned int frame_marker[8]; + unsigned int sp; + /* in reality, there's nearly 8k of stack after this */ +}; + +long real32_call(unsigned long fn, ...) +{ + va_list args; + extern struct narrow_stack real_stack; + extern unsigned long real32_call_asm(unsigned int *, + unsigned int *, + unsigned int); + + va_start(args, fn); + real_stack.arg0 = va_arg(args, unsigned int); + real_stack.arg1 = va_arg(args, unsigned int); + real_stack.arg2 = va_arg(args, unsigned int); + real_stack.arg3 = va_arg(args, unsigned int); + real_stack.arg4 = va_arg(args, unsigned int); + real_stack.arg5 = va_arg(args, unsigned int); + real_stack.arg6 = va_arg(args, unsigned int); + real_stack.arg7 = va_arg(args, unsigned int); + real_stack.arg8 = va_arg(args, unsigned int); + real_stack.arg9 = va_arg(args, unsigned int); + real_stack.arg10 = va_arg(args, unsigned int); + real_stack.arg11 = va_arg(args, unsigned int); + real_stack.arg12 = va_arg(args, unsigned int); + real_stack.arg13 = va_arg(args, unsigned int); + va_end(args); + + return real32_call_asm(&real_stack.sp, &real_stack.arg0, fn); +} + +#ifdef CONFIG_64BIT +/***************** 64-bit real-mode calls ***********/ + +struct wide_stack { + unsigned long arg0; + unsigned long arg1; + unsigned long arg2; + unsigned long arg3; + unsigned long arg4; + unsigned long arg5; + unsigned long arg6; + unsigned long arg7; + unsigned long arg8; + unsigned long arg9; + unsigned long arg10; + unsigned long arg11; + unsigned long arg12; + unsigned long arg13; + unsigned long frame_marker[2]; /* rp, previous sp */ + unsigned long sp; + /* in reality, there's nearly 8k of stack after this */ +}; + +long real64_call(unsigned long fn, ...) +{ + va_list args; + extern struct wide_stack real64_stack; + extern unsigned long real64_call_asm(unsigned long *, + unsigned long *, + unsigned long); + + va_start(args, fn); + 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(&real64_stack.sp, &real64_stack.arg0, fn); +} + +#endif /* CONFIG_64BIT */ |