From a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 Mon Sep 17 00:00:00 2001 From: Jinke Han Date: Tue, 8 Sep 2026 15:37:42 +0800 Subject: x86/kprobes: Fix crash when probing CS CALL instructions When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) Signed-off-by: Jinke Han Signed-off-by: Ingo Molnar Reviewed-by: Masami Hiramatsu (Google) Acked-by: Yafang Shao Acked-by: Borislav Petkov Cc: Peter Zijlstra Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000 --- drivers/parisc/Kconfig | 161 +++ drivers/parisc/Makefile | 27 + drivers/parisc/README.dino | 27 + drivers/parisc/asp.c | 133 +++ drivers/parisc/ccio-dma.c | 1586 +++++++++++++++++++++++++++++ drivers/parisc/dino.c | 1091 ++++++++++++++++++++ drivers/parisc/eisa.c | 468 +++++++++ drivers/parisc/eisa_eeprom.c | 100 ++ drivers/parisc/eisa_enumerator.c | 516 ++++++++++ drivers/parisc/gsc.c | 262 +++++ drivers/parisc/gsc.h | 47 + drivers/parisc/hppb.c | 105 ++ drivers/parisc/iommu-helpers.h | 182 ++++ drivers/parisc/iommu.h | 55 + drivers/parisc/iosapic.c | 986 ++++++++++++++++++ drivers/parisc/iosapic_private.h | 174 ++++ drivers/parisc/lasi.c | 239 +++++ drivers/parisc/lba_pci.c | 1760 ++++++++++++++++++++++++++++++++ drivers/parisc/led.c | 551 ++++++++++ drivers/parisc/pdc_stable.c | 1125 +++++++++++++++++++++ drivers/parisc/power.c | 266 +++++ drivers/parisc/sba_iommu.c | 2070 ++++++++++++++++++++++++++++++++++++++ drivers/parisc/superio.c | 494 +++++++++ drivers/parisc/wax.c | 140 +++ 24 files changed, 12565 insertions(+) create mode 100644 drivers/parisc/Kconfig create mode 100644 drivers/parisc/Makefile create mode 100644 drivers/parisc/README.dino create mode 100644 drivers/parisc/asp.c create mode 100644 drivers/parisc/ccio-dma.c create mode 100644 drivers/parisc/dino.c create mode 100644 drivers/parisc/eisa.c create mode 100644 drivers/parisc/eisa_eeprom.c create mode 100644 drivers/parisc/eisa_enumerator.c create mode 100644 drivers/parisc/gsc.c create mode 100644 drivers/parisc/gsc.h create mode 100644 drivers/parisc/hppb.c create mode 100644 drivers/parisc/iommu-helpers.h create mode 100644 drivers/parisc/iommu.h create mode 100644 drivers/parisc/iosapic.c create mode 100644 drivers/parisc/iosapic_private.h create mode 100644 drivers/parisc/lasi.c create mode 100644 drivers/parisc/lba_pci.c create mode 100644 drivers/parisc/led.c create mode 100644 drivers/parisc/pdc_stable.c create mode 100644 drivers/parisc/power.c create mode 100644 drivers/parisc/sba_iommu.c create mode 100644 drivers/parisc/superio.c create mode 100644 drivers/parisc/wax.c (limited to 'drivers/parisc') diff --git a/drivers/parisc/Kconfig b/drivers/parisc/Kconfig new file mode 100644 index 000000000..9cbcf1552 --- /dev/null +++ b/drivers/parisc/Kconfig @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "Bus options (PCI, PCMCIA, EISA, GSC, ISA)" + +config GSC + bool "VSC/GSC/HSC bus support" + select HAVE_EISA + select HAS_IOPORT + default y + help + The VSC, GSC and HSC busses were used from the earliest 700-series + workstations up to and including the C360/J2240 workstations. They + were also used in servers from the E-class to the K-class. They + are not found in B1000, C3000, J5000, A500, L1000, N4000 and upwards. + If in doubt, say "Y". + +config HPPB + bool "HP-PB bus support" + depends on GSC + help + The HP-PB bus was used in the Nova class and K-class servers. + If in doubt, say "Y" + +config IOMMU_CCIO + bool "U2/Uturn I/O MMU" + depends on GSC + help + Say Y here to enable DMA management routines for the first + generation of PA-RISC cache-coherent machines. Programs the + U2/Uturn chip in "Virtual Mode" and use the I/O MMU. + +config GSC_LASI + bool "Lasi I/O support" + depends on GSC + help + Say Y here to support the Lasi multifunction chip found in + many PA-RISC workstations & servers. It includes interfaces + for a parallel port, serial port, NCR 53c710 SCSI, Apricot + Ethernet, Harmony audio, PS/2 keyboard & mouse, ISDN, telephony + and floppy. Note that you must still enable all the individual + drivers for these chips. + +config GSC_WAX + bool "Wax I/O support" + depends on GSC + help + Say Y here to support the Wax multifunction chip found in some + older systems, including B/C/D/R class and 715/64, 715/80 and + 715/100. Wax includes an EISA adapter, a serial port (not always + used), a HIL interface chip and is also known to be used as the + GSC bridge for an X.25 GSC card. + +config ISA + bool "ISA support" + depends on EISA + help + If you want to plug an ISA card into your EISA bus, say Y here. + Most people should say N. + +config GSC_DINO + bool "GSCtoPCI/Dino PCI support" + depends on PCI && GSC + help + Say Y here to support the Dino & Cujo GSC to PCI bridges found in + machines from the B132 to the C360, the J2240 and the A180. Some + GSC/HSC cards (eg gigabit & dual 100 Mbit Ethernet) have a Dino on + the card, and you also need to say Y here if you have such a card. + Note that Dino also supplies one of the serial ports on certain + machines. If in doubt, say Y. + +config PCI_LBA + bool "LBA/Elroy PCI support" + depends on PCI + help + Say Y here to support the Elroy PCI Lower Bus Adapter. This is + present on B, C, J, L and N-class machines with 4-digit model + numbers and the A400/A500. + +config IOSAPIC + bool + depends on PCI_LBA + default PCI_LBA + +config IOMMU_SBA + bool + depends on PCI_LBA + default PCI_LBA + +endmenu + +menu "PA-RISC specific drivers" + +config SUPERIO + bool "SuperIO (SuckyIO) support" + depends on PCI_LBA + default y + help + Say Y here to support the SuperIO chip found in Bxxxx, C3xxx and + J5xxx+ machines. This enables IDE, Floppy, Parallel Port, and + Serial port on those machines. + +config CHASSIS_LCD_LED + bool "Chassis LCD and LED support" + depends on LEDS_CLASS=y + default y + select LEDS_TRIGGERS + help + Say Y here if you want to enable support for the Heartbeat, + Disk/Network activities LEDs on some PA-RISC machines, + or support for the LCD that can be found on recent material. + + This has nothing to do with LED State support for A and E class. + + If unsure, say Y. + +config PDC_CHASSIS + bool "PDC chassis state codes support" + default y + help + Say Y here if you want to enable support for Chassis codes. + That includes support for LED State front panel as found on E + class, and support for the GSP Virtual Front Panel (LED State and + message logging) as found on high end servers such as A, L and + N-class. + This driver will also display progress messages on LCD display, + such as "INI", "RUN" and "FLT", and might thus clobber messages + shown by the LED/LCD driver. + This driver updates the state panel (LED and/or LCD) upon system + state change (eg: boot, shutdown or panic). + + If unsure, say Y. + + +config PDC_CHASSIS_WARN + bool "PDC chassis warnings support" + depends on PROC_FS + default y + help + Say Y here if you want to enable support for Chassis warnings. + This will add a proc entry '/proc/chassis' giving some information + about the overall health state of the system. + This includes NVRAM battery level, overtemp or failures such as + fans or power units. + + If unsure, say Y. + + +config PDC_STABLE + tristate "PDC Stable Storage support" + depends on SYSFS + default y + help + Say Y here if you want to enable support for accessing Stable Storage + variables (PDC non volatile variables such as Primary Boot Path, + Console Path, Autoboot, Autosearch, etc) through SysFS. + + If unsure, say Y. + + To compile this driver as a module, choose M here. + The module will be called pdc_stable. + +endmenu diff --git a/drivers/parisc/Makefile b/drivers/parisc/Makefile new file mode 100644 index 000000000..69860a60b --- /dev/null +++ b/drivers/parisc/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile PCI and non-PCI devices in PA-RISC machines +# +# Keep the order below, e.g. +# - ccio before any potential subdevices +# - gsc is required before lasi and wax +# - asp and wax before the EISA adapters for the IRQ regions +# - EISA must come before PCI to be sure it gets IRQ region +# + +obj-$(CONFIG_IOSAPIC) += iosapic.o +obj-$(CONFIG_IOMMU_SBA) += sba_iommu.o +obj-$(CONFIG_PCI_LBA) += lba_pci.o +obj-$(CONFIG_IOMMU_CCIO) += ccio-dma.o + +obj-$(CONFIG_GSC) += gsc.o +obj-$(CONFIG_GSC_LASI) += lasi.o asp.o +obj-$(CONFIG_GSC_WAX) += wax.o +obj-$(CONFIG_EISA) += eisa.o eisa_enumerator.o eisa_eeprom.o +obj-$(CONFIG_HPPB) += hppb.o +obj-$(CONFIG_GSC_DINO) += dino.o + +obj-$(CONFIG_SUPERIO) += superio.o +obj-$(CONFIG_CHASSIS_LCD_LED) += led.o +obj-$(CONFIG_PDC_STABLE) += pdc_stable.o +obj-y += power.o diff --git a/drivers/parisc/README.dino b/drivers/parisc/README.dino new file mode 100644 index 000000000..162742699 --- /dev/null +++ b/drivers/parisc/README.dino @@ -0,0 +1,27 @@ +/* +** HP VISUALIZE Workstation PCI Bus Defect +** +** "HP has discovered a potential system defect that can affect +** the behavior of five models of HP VISUALIZE workstations when +** equipped with third-party or customer-installed PCI I/O expansion +** cards. The defect is limited to the HP C180, C160, C160L, B160L, +** and B132L VISUALIZE workstations, and will only be encountered +** when data is transmitted through PCI I/O expansion cards on the +** PCI bus. HP-supplied graphics cards that utilize the PCI bus are +** not affected." +** +** http://h20000.www2.hp.com/bizsupport/TechSupport/Home.jsp?locale=en_US&prodTypeId=12454&prodSeriesId=44443 +** +** Product First Good Serial Number +** C200/C240 (US) US67350000 +**B132L+/B180 (US) US67390000 +** C200 (Europe) 3713G01000 +** B180L (Europe) 3720G01000 +** +** Note that many boards were fixed/replaced under a free replacement +** program. Assume a machine is only "suspect" until proven otherwise. +** +** "The pci_check program will also be available as application +** patch PHSS_12295" +*/ + diff --git a/drivers/parisc/asp.c b/drivers/parisc/asp.c new file mode 100644 index 000000000..32af8f095 --- /dev/null +++ b/drivers/parisc/asp.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASP Device Driver + * + * (c) Copyright 2000 The Puffin Group Inc. + * + * (c) 2000-2023 by Helge Deller + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gsc.h" + +#define ASP_GSC_IRQ 3 /* hardcoded interrupt for GSC */ + +#define ASP_VER_OFFSET 0x20 /* offset of ASP version */ + +#define ASP_LED_ADDR 0xf0800020 + +#define VIPER_INT_WORD 0xFFFBF088 /* addr of viper interrupt word */ + +static struct gsc_asic asp; + +static void asp_choose_irq(struct parisc_device *dev, void *ctrl) +{ + int irq; + + switch (dev->id.sversion) { + case 0x71: irq = 9; break; /* SCSI */ + case 0x72: irq = 8; break; /* LAN */ + case 0x73: irq = 1; break; /* HIL */ + case 0x74: irq = 7; break; /* Centronics */ + case 0x75: irq = (dev->hw_path == 4) ? 5 : 6; break; /* RS232 */ + case 0x76: irq = 10; break; /* EISA BA */ + case 0x77: irq = 11; break; /* Graphics1 */ + case 0x7a: irq = 13; break; /* Audio (Bushmaster) */ + case 0x7b: irq = 13; break; /* Audio (Scorpio) */ + case 0x7c: irq = 3; break; /* FW SCSI */ + case 0x7d: irq = 4; break; /* FDDI */ + case 0x7f: irq = 13; break; /* Audio (Outfield) */ + default: return; /* Unknown */ + } + + gsc_asic_assign_irq(ctrl, irq, &dev->irq); + + switch (dev->id.sversion) { + case 0x73: irq = 2; break; /* i8042 High-priority */ + case 0x76: irq = 0; break; /* EISA BA */ + default: return; /* Other */ + } + + gsc_asic_assign_irq(ctrl, irq, &dev->aux_irq); +} + +/* There are two register ranges we're interested in. Interrupt / + * Status / LED are at 0xf080xxxx and Asp special registers are at + * 0xf082fxxx. PDC only tells us that Asp is at 0xf082f000, so for + * the purposes of interrupt handling, we have to tell other bits of + * the kernel to look at the other registers. + */ +#define ASP_INTERRUPT_ADDR 0xf0800000 + +static int __init asp_init_chip(struct parisc_device *dev) +{ + struct gsc_irq gsc_irq; + int ret; + + asp.version = gsc_readb(dev->hpa.start + ASP_VER_OFFSET) & 0xf; + asp.name = (asp.version == 1) ? "Asp" : "Cutoff"; + asp.hpa = ASP_INTERRUPT_ADDR; + + printk(KERN_INFO "%s version %d at 0x%lx found.\n", + asp.name, asp.version, (unsigned long)dev->hpa.start); + + /* the IRQ ASP should use */ + ret = -EBUSY; + dev->irq = gsc_claim_irq(&gsc_irq, ASP_GSC_IRQ); + if (dev->irq < 0) { + printk(KERN_ERR "%s(): cannot get GSC irq\n", __func__); + goto out; + } + + asp.eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data; + + ret = request_irq(gsc_irq.irq, gsc_asic_intr, 0, "asp", &asp); + if (ret < 0) + goto out; + + /* Program VIPER to interrupt on the ASP irq */ + gsc_writel((1 << (31 - ASP_GSC_IRQ)),VIPER_INT_WORD); + + /* Done init'ing, register this driver */ + ret = gsc_common_setup(dev, &asp); + if (ret) + goto out; + + gsc_fixup_irqs(dev, &asp, asp_choose_irq); + /* Mongoose is a sibling of Asp, not a child... */ + gsc_fixup_irqs(parisc_parent(dev), &asp, asp_choose_irq); + + /* initialize the chassis LEDs */ +#ifdef CONFIG_CHASSIS_LCD_LED + register_led_driver(DISPLAY_MODEL_OLD_ASP, LED_CMD_REG_NONE, + ASP_LED_ADDR); +#endif + + out: + return ret; +} + +static const struct parisc_device_id asp_tbl[] __initconst = { + { HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00070 }, + { 0, } +}; +MODULE_DEVICE_TABLE(parisc, asp_tbl); + +static struct parisc_driver asp_driver __refdata = { + .name = "asp", + .id_table = asp_tbl, + .probe = asp_init_chip, +}; + +static int __init asp_init(void) +{ + return register_parisc_driver(&asp_driver); +} +arch_initcall(asp_init); diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c new file mode 100644 index 000000000..dc1b6d11f --- /dev/null +++ b/drivers/parisc/ccio-dma.c @@ -0,0 +1,1586 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** ccio-dma.c: +** DMA management routines for first generation cache-coherent machines. +** Program U2/Uturn in "Virtual Mode" and use the I/O MMU. +** +** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2000 Ryan Bradetich +** (c) Copyright 2000 Hewlett-Packard Company +** +** "Real Mode" operation refers to U2/Uturn chip operation. +** U2/Uturn were designed to perform coherency checks w/o using +** the I/O MMU - basically what x86 does. +** +** Drawbacks of using Real Mode are: +** o outbound DMA is slower - U2 won't prefetch data (GSC+ XQL signal). +** o Inbound DMA less efficient - U2 can't use DMA_FAST attribute. +** o Ability to do scatter/gather in HW is lost. +** o Doesn't work under PCX-U/U+ machines since they didn't follow +** the coherency design originally worked out. Only PCX-W does. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for L1_CACHE_BYTES */ +#include +#include +#include +#include +#include /* for register_module() */ +#include + +#include "iommu.h" + +/* +** Choose "ccio" since that's what HP-UX calls it. +** Make it easier for folks to migrate from one to the other :^) +*/ +#define MODULE_NAME "ccio" + +#undef DEBUG_CCIO_RES +#undef DEBUG_CCIO_RUN +#undef DEBUG_CCIO_INIT +#undef DEBUG_CCIO_RUN_SG + +#ifdef CONFIG_PROC_FS +/* depends on proc fs support. But costs CPU performance. */ +#undef CCIO_COLLECT_STATS +#endif + +#ifdef DEBUG_CCIO_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#ifdef DEBUG_CCIO_RUN +#define DBG_RUN(x...) printk(x) +#else +#define DBG_RUN(x...) +#endif + +#ifdef DEBUG_CCIO_RES +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +#ifdef DEBUG_CCIO_RUN_SG +#define DBG_RUN_SG(x...) printk(x) +#else +#define DBG_RUN_SG(x...) +#endif + +#define WRITE_U32(value, addr) __raw_writel(value, addr) +#define READ_U32(addr) __raw_readl(addr) + +#define U2_IOA_RUNWAY 0x580 +#define U2_BC_GSC 0x501 +#define UTURN_IOA_RUNWAY 0x581 +#define UTURN_BC_GSC 0x502 + +#define IOA_NORMAL_MODE 0x00020080 /* IO_CONTROL to turn on CCIO */ +#define CMD_TLB_DIRECT_WRITE 35 /* IO_COMMAND for I/O TLB Writes */ +#define CMD_TLB_PURGE 33 /* IO_COMMAND to Purge I/O TLB entry */ + +struct ioa_registers { + /* Runway Supervisory Set */ + int32_t unused1[12]; + uint32_t io_command; /* Offset 12 */ + uint32_t io_status; /* Offset 13 */ + uint32_t io_control; /* Offset 14 */ + int32_t unused2[1]; + + /* Runway Auxiliary Register Set */ + uint32_t io_err_resp; /* Offset 0 */ + uint32_t io_err_info; /* Offset 1 */ + uint32_t io_err_req; /* Offset 2 */ + uint32_t io_err_resp_hi; /* Offset 3 */ + uint32_t io_tlb_entry_m; /* Offset 4 */ + uint32_t io_tlb_entry_l; /* Offset 5 */ + uint32_t unused3[1]; + uint32_t io_pdir_base; /* Offset 7 */ + uint32_t io_io_low_hv; /* Offset 8 */ + uint32_t io_io_high_hv; /* Offset 9 */ + uint32_t unused4[1]; + uint32_t io_chain_id_mask; /* Offset 11 */ + uint32_t unused5[2]; + uint32_t io_io_low; /* Offset 14 */ + uint32_t io_io_high; /* Offset 15 */ +}; + +/* +** IOA Registers +** ------------- +** +** Runway IO_CONTROL Register (+0x38) +** +** The Runway IO_CONTROL register controls the forwarding of transactions. +** +** | 0 ... 13 | 14 15 | 16 ... 21 | 22 | 23 24 | 25 ... 31 | +** | HV | TLB | reserved | HV | mode | reserved | +** +** o mode field indicates the address translation of transactions +** forwarded from Runway to GSC+: +** Mode Name Value Definition +** Off (default) 0 Opaque to matching addresses. +** Include 1 Transparent for matching addresses. +** Peek 3 Map matching addresses. +** +** + "Off" mode: Runway transactions which match the I/O range +** specified by the IO_IO_LOW/IO_IO_HIGH registers will be ignored. +** + "Include" mode: all addresses within the I/O range specified +** by the IO_IO_LOW and IO_IO_HIGH registers are transparently +** forwarded. This is the I/O Adapter's normal operating mode. +** + "Peek" mode: used during system configuration to initialize the +** GSC+ bus. Runway Write_Shorts in the address range specified by +** IO_IO_LOW and IO_IO_HIGH are forwarded through the I/O Adapter +** *AND* the GSC+ address is remapped to the Broadcast Physical +** Address space by setting the 14 high order address bits of the +** 32 bit GSC+ address to ones. +** +** o TLB field affects transactions which are forwarded from GSC+ to Runway. +** "Real" mode is the poweron default. +** +** TLB Mode Value Description +** Real 0 No TLB translation. Address is directly mapped and the +** virtual address is composed of selected physical bits. +** Error 1 Software fills the TLB manually. +** Normal 2 IOA fetches IO TLB misses from IO PDIR (in host memory). +** +** +** IO_IO_LOW_HV +0x60 (HV dependent) +** IO_IO_HIGH_HV +0x64 (HV dependent) +** IO_IO_LOW +0x78 (Architected register) +** IO_IO_HIGH +0x7c (Architected register) +** +** IO_IO_LOW and IO_IO_HIGH set the lower and upper bounds of the +** I/O Adapter address space, respectively. +** +** 0 ... 7 | 8 ... 15 | 16 ... 31 | +** 11111111 | 11111111 | address | +** +** Each LOW/HIGH pair describes a disjoint address space region. +** (2 per GSC+ port). Each incoming Runway transaction address is compared +** with both sets of LOW/HIGH registers. If the address is in the range +** greater than or equal to IO_IO_LOW and less than IO_IO_HIGH the transaction +** for forwarded to the respective GSC+ bus. +** Specify IO_IO_LOW equal to or greater than IO_IO_HIGH to avoid specifying +** an address space region. +** +** In order for a Runway address to reside within GSC+ extended address space: +** Runway Address [0:7] must identically compare to 8'b11111111 +** Runway Address [8:11] must be equal to IO_IO_LOW(_HV)[16:19] +** Runway Address [12:23] must be greater than or equal to +** IO_IO_LOW(_HV)[20:31] and less than IO_IO_HIGH(_HV)[20:31]. +** Runway Address [24:39] is not used in the comparison. +** +** When the Runway transaction is forwarded to GSC+, the GSC+ address is +** as follows: +** GSC+ Address[0:3] 4'b1111 +** GSC+ Address[4:29] Runway Address[12:37] +** GSC+ Address[30:31] 2'b00 +** +** All 4 Low/High registers must be initialized (by PDC) once the lower bus +** is interrogated and address space is defined. The operating system will +** modify the architectural IO_IO_LOW and IO_IO_HIGH registers following +** the PDC initialization. However, the hardware version dependent IO_IO_LOW +** and IO_IO_HIGH registers should not be subsequently altered by the OS. +** +** Writes to both sets of registers will take effect immediately, bypassing +** the queues, which ensures that subsequent Runway transactions are checked +** against the updated bounds values. However reads are queued, introducing +** the possibility of a read being bypassed by a subsequent write to the same +** register. This sequence can be avoided by having software wait for read +** returns before issuing subsequent writes. +*/ + +struct ioc { + struct ioa_registers __iomem *ioc_regs; /* I/O MMU base address */ + u8 *res_map; /* resource map, bit == pdir entry */ + __le64 *pdir_base; /* physical base address */ + u32 pdir_size; /* bytes, function of IOV Space size */ + u32 res_hint; /* next available IOVP - + circular search */ + u32 res_size; /* size of resource map in bytes */ + spinlock_t res_lock; + +#ifdef CCIO_COLLECT_STATS +#define CCIO_SEARCH_SAMPLE 0x100 + unsigned long avg_search[CCIO_SEARCH_SAMPLE]; + unsigned long avg_idx; /* current index into avg_search */ + unsigned long used_pages; + unsigned long msingle_calls; + unsigned long msingle_pages; + unsigned long msg_calls; + unsigned long msg_pages; + unsigned long usingle_calls; + unsigned long usingle_pages; + unsigned long usg_calls; + unsigned long usg_pages; +#endif + unsigned short cujo20_bug; + + /* STUFF We don't need in performance path */ + u32 chainid_shift; /* specify bit location of chain_id */ + struct ioc *next; /* Linked list of discovered iocs */ + const char *name; /* device name from firmware */ + unsigned int hw_path; /* the hardware path this ioc is associatd with */ + struct pci_dev *fake_pci_dev; /* the fake pci_dev for non-pci devs */ + struct resource mmio_region[2]; /* The "routed" MMIO regions */ +}; + +static struct ioc *ioc_list; +static int ioc_count; + +/************************************************************** +* +* I/O Pdir Resource Management +* +* Bits set in the resource map are in use. +* Each bit can represent a number of pages. +* LSbs represent lower addresses (IOVA's). +* +* This was copied from sba_iommu.c. Don't try to unify +* the two resource managers unless a way to have different +* allocation policies is also adjusted. We'd like to avoid +* I/O TLB thrashing by having resource allocation policy +* match the I/O TLB replacement policy. +* +***************************************************************/ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +/* Convert from IOVP to IOVA and vice versa. */ +#define CCIO_IOVA(iovp,offset) ((iovp) | (offset)) +#define CCIO_IOVP(iova) ((iova) & IOVP_MASK) + +#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) +#define MKIOVP(pdir_idx) ((long)(pdir_idx) << IOVP_SHIFT) +#define MKIOVA(iovp,offset) (dma_addr_t)((long)iovp | (long)offset) + +/* +** Don't worry about the 150% average search length on a miss. +** If the search wraps around, and passes the res_hint, it will +** cause the kernel to panic anyhow. +*/ +#define CCIO_SEARCH_LOOP(ioc, res_idx, mask, size) \ + for (; res_ptr < res_end; ++res_ptr) { \ + int ret;\ + unsigned int idx;\ + idx = (unsigned int)((unsigned long)res_ptr - (unsigned long)ioc->res_map); \ + ret = iommu_is_span_boundary(idx << 3, pages_needed, 0, boundary_size);\ + if ((0 == (*res_ptr & mask)) && !ret) { \ + *res_ptr |= mask; \ + res_idx = idx;\ + ioc->res_hint = res_idx + (size >> 3); \ + goto resource_found; \ + } \ + } + +#define CCIO_FIND_FREE_MAPPING(ioa, res_idx, mask, size) \ + u##size *res_ptr = (u##size *)&((ioc)->res_map[ioa->res_hint & ~((size >> 3) - 1)]); \ + u##size *res_end = (u##size *)&(ioc)->res_map[ioa->res_size]; \ + CCIO_SEARCH_LOOP(ioc, res_idx, mask, size); \ + res_ptr = (u##size *)&(ioc)->res_map[0]; \ + CCIO_SEARCH_LOOP(ioa, res_idx, mask, size); + +/* +** Find available bit in this ioa's resource map. +** Use a "circular" search: +** o Most IOVA's are "temporary" - avg search time should be small. +** o keep a history of what happened for debugging +** o KISS. +** +** Perf optimizations: +** o search for log2(size) bits at a time. +** o search for available resource bits using byte/word/whatever. +** o use different search for "large" (eg > 4 pages) or "very large" +** (eg > 16 pages) mappings. +*/ + +/** + * ccio_alloc_range - Allocate pages in the ioc's resource map. + * @ioc: The I/O Controller. + * @dev: The PCI device. + * @size: The requested number of bytes to be mapped into the + * I/O Pdir... + * + * This function searches the resource map of the ioc to locate a range + * of available pages for the requested size. + */ +static int +ccio_alloc_range(struct ioc *ioc, struct device *dev, size_t size) +{ + unsigned int pages_needed = size >> IOVP_SHIFT; + unsigned int res_idx; + unsigned long boundary_size; +#ifdef CCIO_COLLECT_STATS + unsigned long cr_start = mfctl(16); +#endif + + BUG_ON(pages_needed == 0); + BUG_ON((pages_needed * IOVP_SIZE) > DMA_CHUNK_SIZE); + + DBG_RES("%s() size: %zu pages_needed %d\n", + __func__, size, pages_needed); + + /* + ** "seek and ye shall find"...praying never hurts either... + ** ggg sacrifices another 710 to the computer gods. + */ + + boundary_size = dma_get_seg_boundary_nr_pages(dev, IOVP_SHIFT); + + if (pages_needed <= 8) { + /* + * LAN traffic will not thrash the TLB IFF the same NIC + * uses 8 adjacent pages to map separate payload data. + * ie the same byte in the resource bit map. + */ +#if 0 + /* FIXME: bit search should shift it's way through + * an unsigned long - not byte at a time. As it is now, + * we effectively allocate this byte to this mapping. + */ + unsigned long mask = ~(~0UL >> pages_needed); + CCIO_FIND_FREE_MAPPING(ioc, res_idx, mask, 8); +#else + CCIO_FIND_FREE_MAPPING(ioc, res_idx, 0xff, 8); +#endif + } else if (pages_needed <= 16) { + CCIO_FIND_FREE_MAPPING(ioc, res_idx, 0xffff, 16); + } else if (pages_needed <= 32) { + CCIO_FIND_FREE_MAPPING(ioc, res_idx, ~(unsigned int)0, 32); +#ifdef __LP64__ + } else if (pages_needed <= 64) { + CCIO_FIND_FREE_MAPPING(ioc, res_idx, ~0UL, 64); +#endif + } else { + panic("%s: %s() Too many pages to map. pages_needed: %u\n", + __FILE__, __func__, pages_needed); + } + + panic("%s: %s() I/O MMU is out of mapping resources.\n", __FILE__, + __func__); + +resource_found: + + DBG_RES("%s() res_idx %d res_hint: %d\n", + __func__, res_idx, ioc->res_hint); + +#ifdef CCIO_COLLECT_STATS + { + unsigned long cr_end = mfctl(16); + unsigned long tmp = cr_end - cr_start; + /* check for roll over */ + cr_start = (cr_end < cr_start) ? -(tmp) : (tmp); + } + ioc->avg_search[ioc->avg_idx++] = cr_start; + ioc->avg_idx &= CCIO_SEARCH_SAMPLE - 1; + ioc->used_pages += pages_needed; +#endif + /* + ** return the bit address. + */ + return res_idx << 3; +} + +#define CCIO_FREE_MAPPINGS(ioc, res_idx, mask, size) \ + u##size *res_ptr = (u##size *)&((ioc)->res_map[res_idx]); \ + BUG_ON((*res_ptr & mask) != mask); \ + *res_ptr &= ~(mask); + +/** + * ccio_free_range - Free pages from the ioc's resource map. + * @ioc: The I/O Controller. + * @iova: The I/O Virtual Address. + * @pages_mapped: The requested number of pages to be freed from the + * I/O Pdir. + * + * This function frees the resouces allocated for the iova. + */ +static void +ccio_free_range(struct ioc *ioc, dma_addr_t iova, unsigned long pages_mapped) +{ + unsigned long iovp = CCIO_IOVP(iova); + unsigned int res_idx = PDIR_INDEX(iovp) >> 3; + + BUG_ON(pages_mapped == 0); + BUG_ON((pages_mapped * IOVP_SIZE) > DMA_CHUNK_SIZE); + BUG_ON(pages_mapped > BITS_PER_LONG); + + DBG_RES("%s(): res_idx: %d pages_mapped %lu\n", + __func__, res_idx, pages_mapped); + +#ifdef CCIO_COLLECT_STATS + ioc->used_pages -= pages_mapped; +#endif + + if(pages_mapped <= 8) { +#if 0 + /* see matching comments in alloc_range */ + unsigned long mask = ~(~0UL >> pages_mapped); + CCIO_FREE_MAPPINGS(ioc, res_idx, mask, 8); +#else + CCIO_FREE_MAPPINGS(ioc, res_idx, 0xffUL, 8); +#endif + } else if(pages_mapped <= 16) { + CCIO_FREE_MAPPINGS(ioc, res_idx, 0xffffUL, 16); + } else if(pages_mapped <= 32) { + CCIO_FREE_MAPPINGS(ioc, res_idx, ~(unsigned int)0, 32); +#ifdef __LP64__ + } else if(pages_mapped <= 64) { + CCIO_FREE_MAPPINGS(ioc, res_idx, ~0UL, 64); +#endif + } else { + panic("%s:%s() Too many pages to unmap.\n", __FILE__, + __func__); + } +} + +/**************************************************************** +** +** CCIO dma_ops support routines +** +*****************************************************************/ + +typedef unsigned long space_t; +#define KERNEL_SPACE 0 + +/* +** DMA "Page Type" and Hints +** o if SAFE_DMA isn't set, mapping is for FAST_DMA. SAFE_DMA should be +** set for subcacheline DMA transfers since we don't want to damage the +** other part of a cacheline. +** o SAFE_DMA must be set for "memory" allocated via pci_alloc_consistent(). +** This bit tells U2 to do R/M/W for partial cachelines. "Streaming" +** data can avoid this if the mapping covers full cache lines. +** o STOP_MOST is needed for atomicity across cachelines. +** Apparently only "some EISA devices" need this. +** Using CONFIG_ISA is hack. Only the IOA with EISA under it needs +** to use this hint iff the EISA devices needs this feature. +** According to the U2 ERS, STOP_MOST enabled pages hurt performance. +** o PREFETCH should *not* be set for cases like Multiple PCI devices +** behind GSCtoPCI (dino) bus converter. Only one cacheline per GSC +** device can be fetched and multiply DMA streams will thrash the +** prefetch buffer and burn memory bandwidth. See 6.7.3 "Prefetch Rules +** and Invalidation of Prefetch Entries". +** +** FIXME: the default hints need to be per GSC device - not global. +** +** HP-UX dorks: linux device driver programming model is totally different +** than HP-UX's. HP-UX always sets HINT_PREFETCH since it's drivers +** do special things to work on non-coherent platforms...linux has to +** be much more careful with this. +*/ +#define IOPDIR_VALID 0x01UL +#define HINT_SAFE_DMA 0x02UL /* used for pci_alloc_consistent() pages */ +#ifdef CONFIG_EISA +#define HINT_STOP_MOST 0x04UL /* LSL support */ +#else +#define HINT_STOP_MOST 0x00UL /* only needed for "some EISA devices" */ +#endif +#define HINT_UDPATE_ENB 0x08UL /* not used/supported by U2 */ +#define HINT_PREFETCH 0x10UL /* for outbound pages which are not SAFE */ + + +/* +** Use direction (ie DMA_TO_DEVICE) to pick hint. +** ccio_alloc() depends on this to get SAFE_DMA +** when it passes in BIDIRECTIONAL flag. +*/ +static u32 hint_lookup[] = { + [DMA_BIDIRECTIONAL] = HINT_STOP_MOST | HINT_SAFE_DMA | IOPDIR_VALID, + [DMA_TO_DEVICE] = HINT_STOP_MOST | HINT_PREFETCH | IOPDIR_VALID, + [DMA_FROM_DEVICE] = HINT_STOP_MOST | IOPDIR_VALID, +}; + +/** + * ccio_io_pdir_entry - Initialize an I/O Pdir. + * @pdir_ptr: A pointer into I/O Pdir. + * @sid: The Space Identifier. + * @pba: The physical address. + * @hints: The DMA Hint. + * + * Given a physical address (pba, arg2) and space id, (sid, arg1), + * load the I/O PDIR entry pointed to by pdir_ptr (arg0). Each IO Pdir + * entry consists of 8 bytes as shown below (MSB == bit 0): + * + * + * WORD 0: + * +------+----------------+-----------------------------------------------+ + * | Phys | Virtual Index | Phys | + * | 0:3 | 0:11 | 4:19 | + * |4 bits| 12 bits | 16 bits | + * +------+----------------+-----------------------------------------------+ + * WORD 1: + * +-----------------------+-----------------------------------------------+ + * | Phys | Rsvd | Prefetch |Update |Rsvd |Lock |Safe |Valid | + * | 20:39 | | Enable |Enable | |Enable|DMA | | + * | 20 bits | 5 bits | 1 bit |1 bit |2 bits|1 bit |1 bit |1 bit | + * +-----------------------+-----------------------------------------------+ + * + * The virtual index field is filled with the results of the LCI + * (Load Coherence Index) instruction. The 8 bits used for the virtual + * index are bits 12:19 of the value returned by LCI. + */ +static void +ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, phys_addr_t pba, + unsigned long hints) +{ + register unsigned long pa; + register unsigned long ci; /* coherent index */ + + /* We currently only support kernel addresses */ + BUG_ON(sid != KERNEL_SPACE); + + /* + ** WORD 1 - low order word + ** "hints" parm includes the VALID bit! + ** "dep" clobbers the physical address offset bits as well. + */ + pa = pba; + asm volatile("depw %1,31,12,%0" : "+r" (pa) : "r" (hints)); + ((u32 *)pdir_ptr)[1] = (u32) pa; + + /* + ** WORD 0 - high order word + */ + +#ifdef __LP64__ + /* + ** get bits 12:15 of physical address + ** shift bits 16:31 of physical address + ** and deposit them + */ + asm volatile ("extrd,u %1,15,4,%0" : "=r" (ci) : "r" (pa)); + asm volatile ("extrd,u %1,31,16,%0" : "+r" (pa) : "r" (pa)); + asm volatile ("depd %1,35,4,%0" : "+r" (pa) : "r" (ci)); +#else + pa = 0; +#endif + /* + ** get CPU coherency index bits + ** Grab virtual index [0:11] + ** Deposit virt_idx bits into I/O PDIR word + */ + asm volatile ("lci %%r0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pba))); + asm volatile ("extru %1,19,12,%0" : "+r" (ci) : "r" (ci)); + asm volatile ("depw %1,15,12,%0" : "+r" (pa) : "r" (ci)); + + ((u32 *)pdir_ptr)[0] = (u32) pa; + + + /* FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360) + ** PCX-U/U+ do. (eg C200/C240) + ** PCX-T'? Don't know. (eg C110 or similar K-class) + ** + ** See PDC_MODEL/option 0/SW_CAP word for "Non-coherent IO-PDIR bit". + ** + ** "Since PCX-U employs an offset hash that is incompatible with + ** the real mode coherence index generation of U2, the PDIR entry + ** must be flushed to memory to retain coherence." + */ + asm_io_fdc(pdir_ptr); + asm_io_sync(); +} + +/** + * ccio_clear_io_tlb - Remove stale entries from the I/O TLB. + * @ioc: The I/O Controller. + * @iovp: The I/O Virtual Page. + * @byte_cnt: The requested number of bytes to be freed from the I/O Pdir. + * + * Purge invalid I/O PDIR entries from the I/O TLB. + * + * FIXME: Can we change the byte_cnt to pages_mapped? + */ +static void +ccio_clear_io_tlb(struct ioc *ioc, dma_addr_t iovp, size_t byte_cnt) +{ + u32 chain_size = 1 << ioc->chainid_shift; + + iovp &= IOVP_MASK; /* clear offset bits, just want pagenum */ + byte_cnt += chain_size; + + while(byte_cnt > chain_size) { + WRITE_U32(CMD_TLB_PURGE | iovp, &ioc->ioc_regs->io_command); + iovp += chain_size; + byte_cnt -= chain_size; + } +} + +/** + * ccio_mark_invalid - Mark the I/O Pdir entries invalid. + * @ioc: The I/O Controller. + * @iova: The I/O Virtual Address. + * @byte_cnt: The requested number of bytes to be freed from the I/O Pdir. + * + * Mark the I/O Pdir entries invalid and blow away the corresponding I/O + * TLB entries. + * + * FIXME: at some threshold it might be "cheaper" to just blow + * away the entire I/O TLB instead of individual entries. + * + * FIXME: Uturn has 256 TLB entries. We don't need to purge every + * PDIR entry - just once for each possible TLB entry. + * (We do need to maker I/O PDIR entries invalid regardless). + * + * FIXME: Can we change byte_cnt to pages_mapped? + */ +static void +ccio_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) +{ + u32 iovp = (u32)CCIO_IOVP(iova); + size_t saved_byte_cnt; + + /* round up to nearest page size */ + saved_byte_cnt = byte_cnt = ALIGN(byte_cnt, IOVP_SIZE); + + while(byte_cnt > 0) { + /* invalidate one page at a time */ + unsigned int idx = PDIR_INDEX(iovp); + char *pdir_ptr = (char *) &(ioc->pdir_base[idx]); + + BUG_ON(idx >= (ioc->pdir_size / sizeof(u64))); + pdir_ptr[7] = 0; /* clear only VALID bit */ + /* + ** FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360) + ** PCX-U/U+ do. (eg C200/C240) + ** See PDC_MODEL/option 0/SW_CAP for "Non-coherent IO-PDIR bit". + */ + asm_io_fdc(pdir_ptr); + + iovp += IOVP_SIZE; + byte_cnt -= IOVP_SIZE; + } + + asm_io_sync(); + ccio_clear_io_tlb(ioc, CCIO_IOVP(iova), saved_byte_cnt); +} + +/**************************************************************** +** +** CCIO dma_ops +** +*****************************************************************/ + +/** + * ccio_dma_supported - Verify the IOMMU supports the DMA address range. + * @dev: The PCI device. + * @mask: A bit mask describing the DMA address range of the device. + */ +static int +ccio_dma_supported(struct device *dev, u64 mask) +{ + if(dev == NULL) { + printk(KERN_ERR MODULE_NAME ": EISA/ISA/et al not supported\n"); + BUG(); + return 0; + } + + /* only support 32-bit or better devices (ie PCI/GSC) */ + return (int)(mask >= 0xffffffffUL); +} + +/** + * ccio_map_single - Map an address range into the IOMMU. + * @dev: The PCI device. + * @addr: The physical address of the DMA region. + * @size: The length of the DMA region. + * @direction: The direction of the DMA transaction (to/from device). + * + * This function implements the pci_map_single function. + */ +static dma_addr_t +ccio_map_single(struct device *dev, phys_addr_t addr, size_t size, + enum dma_data_direction direction) +{ + int idx; + struct ioc *ioc; + unsigned long flags; + dma_addr_t iovp; + dma_addr_t offset; + __le64 *pdir_start; + unsigned long hint = hint_lookup[(int)direction]; + + BUG_ON(!dev); + ioc = GET_IOC(dev); + if (!ioc) + return DMA_MAPPING_ERROR; + + BUG_ON(size <= 0); + + /* save offset bits */ + offset = offset_in_page(addr); + + /* round up to nearest IOVP_SIZE */ + size = ALIGN(size + offset, IOVP_SIZE); + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef CCIO_COLLECT_STATS + ioc->msingle_calls++; + ioc->msingle_pages += size >> IOVP_SHIFT; +#endif + + idx = ccio_alloc_range(ioc, dev, size); + iovp = (dma_addr_t)MKIOVP(idx); + + pdir_start = &(ioc->pdir_base[idx]); + + DBG_RUN("%s() %pa -> %#lx size: %zu\n", + __func__, &addr, (long)(iovp | offset), size); + + /* If not cacheline aligned, force SAFE_DMA on the whole mess */ + if ((size % L1_CACHE_BYTES) || (addr % L1_CACHE_BYTES)) + hint |= HINT_SAFE_DMA; + + while(size > 0) { + ccio_io_pdir_entry(pdir_start, KERNEL_SPACE, addr, hint); + + DBG_RUN(" pdir %p %08x%08x\n", + pdir_start, + (u32) (((u32 *) pdir_start)[0]), + (u32) (((u32 *) pdir_start)[1])); + ++pdir_start; + addr += IOVP_SIZE; + size -= IOVP_SIZE; + } + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + /* form complete address */ + return CCIO_IOVA(iovp, offset); +} + + +static dma_addr_t +ccio_map_phys(struct device *dev, phys_addr_t phys, size_t size, + enum dma_data_direction direction, unsigned long attrs) +{ + if (unlikely(attrs & DMA_ATTR_MMIO)) + return DMA_MAPPING_ERROR; + + return ccio_map_single(dev, phys, size, direction); +} + + +/** + * ccio_unmap_phys - Unmap an address range from the IOMMU. + * @dev: The PCI device. + * @iova: The start address of the DMA region. + * @size: The length of the DMA region. + * @direction: The direction of the DMA transaction (to/from device). + * @attrs: attributes + */ +static void +ccio_unmap_phys(struct device *dev, dma_addr_t iova, size_t size, + enum dma_data_direction direction, unsigned long attrs) +{ + struct ioc *ioc; + unsigned long flags; + dma_addr_t offset = iova & ~IOVP_MASK; + + BUG_ON(!dev); + ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } + + DBG_RUN("%s() iovp %#lx/%zx\n", + __func__, (long)iova, size); + + iova ^= offset; /* clear offset bits */ + size += offset; + size = ALIGN(size, IOVP_SIZE); + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef CCIO_COLLECT_STATS + ioc->usingle_calls++; + ioc->usingle_pages += size >> IOVP_SHIFT; +#endif + + ccio_mark_invalid(ioc, iova, size); + ccio_free_range(ioc, iova, (size >> IOVP_SHIFT)); + spin_unlock_irqrestore(&ioc->res_lock, flags); +} + +/** + * ccio_alloc - Allocate a consistent DMA mapping. + * @dev: The PCI device. + * @size: The length of the DMA region. + * @dma_handle: The DMA address handed back to the device (not the cpu). + * @flag: allocation flags + * @attrs: attributes + * + * This function implements the pci_alloc_consistent function. + */ +static void * +ccio_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag, + unsigned long attrs) +{ + void *ret; +#if 0 +/* GRANT Need to establish hierarchy for non-PCI devs as well +** and then provide matching gsc_map_xxx() functions for them as well. +*/ + if(!hwdev) { + /* only support PCI */ + *dma_handle = 0; + return 0; + } +#endif + ret = (void *) __get_free_pages(flag, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = ccio_map_single(dev, virt_to_phys(ret), size, + DMA_BIDIRECTIONAL); + } + + return ret; +} + +/** + * ccio_free - Free a consistent DMA mapping. + * @dev: The PCI device. + * @size: The length of the DMA region. + * @cpu_addr: The cpu address returned from ccio_alloc(). + * @dma_handle: The device address returned from ccio_alloc(). + * @attrs: attributes + * + * This function implements the pci_free_consistent function. + */ +static void +ccio_free(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle, unsigned long attrs) +{ + ccio_unmap_phys(dev, dma_handle, size, 0, 0); + free_pages((unsigned long)cpu_addr, get_order(size)); +} + +/* +** Since 0 is a valid pdir_base index value, can't use that +** to determine if a value is valid or not. Use a flag to indicate +** the SG list entry contains a valid pdir index. +*/ +#define PIDE_FLAG 0x80000000UL + +#ifdef CCIO_COLLECT_STATS +#define IOMMU_MAP_STATS +#endif +#include "iommu-helpers.h" + +/** + * ccio_map_sg - Map the scatter/gather list into the IOMMU. + * @dev: The PCI device. + * @sglist: The scatter/gather list to be mapped in the IOMMU. + * @nents: The number of entries in the scatter/gather list. + * @direction: The direction of the DMA transaction (to/from device). + * @attrs: attributes + * + * This function implements the pci_map_sg function. + */ +static int +ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents, + enum dma_data_direction direction, unsigned long attrs) +{ + struct ioc *ioc; + int coalesced, filled = 0; + unsigned long flags; + unsigned long hint = hint_lookup[(int)direction]; + unsigned long prev_len = 0, current_len = 0; + int i; + + BUG_ON(!dev); + ioc = GET_IOC(dev); + if (!ioc) + return -EINVAL; + + DBG_RUN_SG("%s() START %d entries\n", __func__, nents); + + /* Fast path single entry scatterlists. */ + if (nents == 1) { + sg_dma_address(sglist) = ccio_map_single(dev, + sg_phys(sglist), sglist->length, + direction); + sg_dma_len(sglist) = sglist->length; + return 1; + } + + for(i = 0; i < nents; i++) + prev_len += sglist[i].length; + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef CCIO_COLLECT_STATS + ioc->msg_calls++; +#endif + + /* + ** First coalesce the chunks and allocate I/O pdir space + ** + ** If this is one DMA stream, we can properly map using the + ** correct virtual address associated with each DMA page. + ** w/o this association, we wouldn't have coherent DMA! + ** Access to the virtual address is what forces a two pass algorithm. + */ + coalesced = iommu_coalesce_chunks(ioc, dev, sglist, nents, ccio_alloc_range); + + /* + ** Program the I/O Pdir + ** + ** map the virtual addresses to the I/O Pdir + ** o dma_address will contain the pdir index + ** o dma_len will contain the number of bytes to map + ** o page/offset contain the virtual address. + */ + filled = iommu_fill_pdir(ioc, sglist, nents, hint, ccio_io_pdir_entry); + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + BUG_ON(coalesced != filled); + + DBG_RUN_SG("%s() DONE %d mappings\n", __func__, filled); + + for (i = 0; i < filled; i++) + current_len += sg_dma_len(sglist + i); + + BUG_ON(current_len != prev_len); + + return filled; +} + +/** + * ccio_unmap_sg - Unmap the scatter/gather list from the IOMMU. + * @dev: The PCI device. + * @sglist: The scatter/gather list to be unmapped from the IOMMU. + * @nents: The number of entries in the scatter/gather list. + * @direction: The direction of the DMA transaction (to/from device). + * @attrs: attributes + * + * This function implements the pci_unmap_sg function. + */ +static void +ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, + enum dma_data_direction direction, unsigned long attrs) +{ + struct ioc *ioc; + + BUG_ON(!dev); + ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } + + DBG_RUN_SG("%s() START %d entries, %p,%x\n", + __func__, nents, sg_virt(sglist), sglist->length); + +#ifdef CCIO_COLLECT_STATS + ioc->usg_calls++; +#endif + + while (nents && sg_dma_len(sglist)) { + +#ifdef CCIO_COLLECT_STATS + ioc->usg_pages += sg_dma_len(sglist) >> PAGE_SHIFT; +#endif + ccio_unmap_phys(dev, sg_dma_address(sglist), + sg_dma_len(sglist), direction, 0); + ++sglist; + nents--; + } + + DBG_RUN_SG("%s() DONE (nents %d)\n", __func__, nents); +} + +static const struct dma_map_ops ccio_ops = { + .dma_supported = ccio_dma_supported, + .alloc = ccio_alloc, + .free = ccio_free, + .map_phys = ccio_map_phys, + .unmap_phys = ccio_unmap_phys, + .map_sg = ccio_map_sg, + .unmap_sg = ccio_unmap_sg, + .get_sgtable = dma_common_get_sgtable, + .alloc_pages_op = dma_common_alloc_pages, + .free_pages = dma_common_free_pages, +}; + +#ifdef CONFIG_PROC_FS +static int ccio_proc_info(struct seq_file *m, void *p) +{ + struct ioc *ioc = ioc_list; + + while (ioc != NULL) { + unsigned int total_pages = ioc->res_size << 3; +#ifdef CCIO_COLLECT_STATS + unsigned long avg = 0, min, max; + int j; +#endif + + seq_printf(m, "%s\n", ioc->name); + + seq_printf(m, "Cujo 2.0 bug : %s\n", + (ioc->cujo20_bug ? "yes" : "no")); + + seq_printf(m, "IO PDIR size : %d bytes (%d entries)\n", + total_pages * 8, total_pages); + +#ifdef CCIO_COLLECT_STATS + seq_printf(m, "IO PDIR entries : %ld free %ld used (%d%%)\n", + total_pages - ioc->used_pages, ioc->used_pages, + (int)(ioc->used_pages * 100 / total_pages)); +#endif + + seq_printf(m, "Resource bitmap : %d bytes (%d pages)\n", + ioc->res_size, total_pages); + +#ifdef CCIO_COLLECT_STATS + min = max = ioc->avg_search[0]; + for(j = 0; j < CCIO_SEARCH_SAMPLE; ++j) { + avg += ioc->avg_search[j]; + if(ioc->avg_search[j] > max) + max = ioc->avg_search[j]; + if(ioc->avg_search[j] < min) + min = ioc->avg_search[j]; + } + avg /= CCIO_SEARCH_SAMPLE; + seq_printf(m, " Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", + min, avg, max); + + seq_printf(m, "pci_map_single(): %8ld calls %8ld pages (avg %d/1000)\n", + ioc->msingle_calls, ioc->msingle_pages, + (int)((ioc->msingle_pages * 1000)/ioc->msingle_calls)); + + /* KLUGE - unmap_sg calls unmap_phys for each mapped page */ + min = ioc->usingle_calls - ioc->usg_calls; + max = ioc->usingle_pages - ioc->usg_pages; + seq_printf(m, "pci_unmap_single: %8ld calls %8ld pages (avg %d/1000)\n", + min, max, (int)((max * 1000)/min)); + + seq_printf(m, "pci_map_sg() : %8ld calls %8ld pages (avg %d/1000)\n", + ioc->msg_calls, ioc->msg_pages, + (int)((ioc->msg_pages * 1000)/ioc->msg_calls)); + + seq_printf(m, "pci_unmap_sg() : %8ld calls %8ld pages (avg %d/1000)\n\n\n", + ioc->usg_calls, ioc->usg_pages, + (int)((ioc->usg_pages * 1000)/ioc->usg_calls)); +#endif /* CCIO_COLLECT_STATS */ + + ioc = ioc->next; + } + + return 0; +} + +static int ccio_proc_bitmap_info(struct seq_file *m, void *p) +{ + struct ioc *ioc = ioc_list; + + while (ioc != NULL) { + seq_hex_dump(m, " ", DUMP_PREFIX_NONE, 32, 4, ioc->res_map, + ioc->res_size, false); + seq_putc(m, '\n'); + ioc = ioc->next; + break; /* XXX - remove me */ + } + + return 0; +} +#endif /* CONFIG_PROC_FS */ + +/** + * ccio_find_ioc - Find the ioc in the ioc_list + * @hw_path: The hardware path of the ioc. + * + * This function searches the ioc_list for an ioc that matches + * the provide hardware path. + */ +static struct ioc * ccio_find_ioc(int hw_path) +{ + int i; + struct ioc *ioc; + + ioc = ioc_list; + for (i = 0; i < ioc_count; i++) { + if (ioc->hw_path == hw_path) + return ioc; + + ioc = ioc->next; + } + + return NULL; +} + +/** + * ccio_get_iommu - Find the iommu which controls this device + * @dev: The parisc device. + * + * This function searches through the registered IOMMU's and returns + * the appropriate IOMMU for the device based on its hardware path. + */ +void * ccio_get_iommu(const struct parisc_device *dev) +{ + dev = find_pa_parent_type(dev, HPHW_IOA); + if (!dev) + return NULL; + + return ccio_find_ioc(dev->hw_path); +} + +#define CUJO_20_STEP 0x10000000 /* inc upper nibble */ + +/* Cujo 2.0 has a bug which will silently corrupt data being transferred + * to/from certain pages. To avoid this happening, we mark these pages + * as `used', and ensure that nothing will try to allocate from them. + */ +void __init ccio_cujo20_fixup(struct parisc_device *cujo, u32 iovp) +{ + unsigned int idx; + struct parisc_device *dev = parisc_parent(cujo); + struct ioc *ioc = ccio_get_iommu(dev); + u8 *res_ptr; + + ioc->cujo20_bug = 1; + res_ptr = ioc->res_map; + idx = PDIR_INDEX(iovp) >> 3; + + while (idx < ioc->res_size) { + res_ptr[idx] |= 0xff; + idx += PDIR_INDEX(CUJO_20_STEP) >> 3; + } +} + +#if 0 +/* GRANT - is this needed for U2 or not? */ + +/* +** Get the size of the I/O TLB for this I/O MMU. +** +** If spa_shift is non-zero (ie probably U2), +** then calculate the I/O TLB size using spa_shift. +** +** Otherwise we are supposed to get the IODC entry point ENTRY TLB +** and execute it. However, both U2 and Uturn firmware supplies spa_shift. +** I think only Java (K/D/R-class too?) systems don't do this. +*/ +static int +ccio_get_iotlb_size(struct parisc_device *dev) +{ + if (dev->spa_shift == 0) { + panic("%s() : Can't determine I/O TLB size.\n", __func__); + } + return (1 << dev->spa_shift); +} +#else + +/* Uturn supports 256 TLB entries */ +#define CCIO_CHAINID_SHIFT 8 +#define CCIO_CHAINID_MASK 0xff +#endif /* 0 */ + +/* We *can't* support JAVA (T600). Venture there at your own risk. */ +static const struct parisc_device_id ccio_tbl[] __initconst = { + { HPHW_IOA, HVERSION_REV_ANY_ID, U2_IOA_RUNWAY, 0xb }, /* U2 */ + { HPHW_IOA, HVERSION_REV_ANY_ID, UTURN_IOA_RUNWAY, 0xb }, /* UTurn */ + { 0, } +}; + +static int ccio_probe(struct parisc_device *dev); + +static struct parisc_driver ccio_driver __refdata = { + .name = "ccio", + .id_table = ccio_tbl, + .probe = ccio_probe, +}; + +/** + * ccio_ioc_init - Initialize the I/O Controller + * @ioc: The I/O Controller. + * + * Initialize the I/O Controller which includes setting up the + * I/O Page Directory, the resource map, and initalizing the + * U2/Uturn chip into virtual mode. + */ +static void __init +ccio_ioc_init(struct ioc *ioc) +{ + int i; + unsigned int iov_order; + u32 iova_space_size; + + /* + ** Determine IOVA Space size from memory size. + ** + ** Ideally, PCI drivers would register the maximum number + ** of DMA they can have outstanding for each device they + ** own. Next best thing would be to guess how much DMA + ** can be outstanding based on PCI Class/sub-class. Both + ** methods still require some "extra" to support PCI + ** Hot-Plug/Removal of PCI cards. (aka PCI OLARD). + */ + + iova_space_size = (u32) (totalram_pages() / count_parisc_driver(&ccio_driver)); + + /* limit IOVA space size to 1MB-1GB */ + + if (iova_space_size < (1 << (20 - PAGE_SHIFT))) { + iova_space_size = 1 << (20 - PAGE_SHIFT); +#ifdef __LP64__ + } else if (iova_space_size > (1 << (30 - PAGE_SHIFT))) { + iova_space_size = 1 << (30 - PAGE_SHIFT); +#endif + } + + /* + ** iova space must be log2() in size. + ** thus, pdir/res_map will also be log2(). + */ + + /* We could use larger page sizes in order to *decrease* the number + ** of mappings needed. (ie 8k pages means 1/2 the mappings). + ** + ** Note: Grant Grunder says "Using 8k I/O pages isn't trivial either + ** since the pages must also be physically contiguous - typically + ** this is the case under linux." + */ + + iov_order = get_order(iova_space_size << PAGE_SHIFT); + + /* iova_space_size is now bytes, not pages */ + iova_space_size = 1 << (iov_order + PAGE_SHIFT); + + ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64); + + BUG_ON(ioc->pdir_size > 8 * 1024 * 1024); /* max pdir size <= 8MB */ + + /* Verify it's a power of two */ + BUG_ON((1 << get_order(ioc->pdir_size)) != (ioc->pdir_size >> PAGE_SHIFT)); + + DBG_INIT("%s() hpa 0x%p mem %luMB IOV %dMB (%d bits)\n", + __func__, ioc->ioc_regs, + (unsigned long) totalram_pages() >> (20 - PAGE_SHIFT), + iova_space_size>>20, + iov_order + PAGE_SHIFT); + + ioc->pdir_base = (__le64 *)__get_free_pages(GFP_KERNEL, + get_order(ioc->pdir_size)); + if(NULL == ioc->pdir_base) { + panic("%s() could not allocate I/O Page Table\n", __func__); + } + memset(ioc->pdir_base, 0, ioc->pdir_size); + + BUG_ON((((unsigned long)ioc->pdir_base) & PAGE_MASK) != (unsigned long)ioc->pdir_base); + DBG_INIT(" base %p\n", ioc->pdir_base); + + /* resource map size dictated by pdir_size */ + ioc->res_size = (ioc->pdir_size / sizeof(u64)) >> 3; + DBG_INIT("%s() res_size 0x%x\n", __func__, ioc->res_size); + + ioc->res_map = (u8 *)__get_free_pages(GFP_KERNEL, + get_order(ioc->res_size)); + if(NULL == ioc->res_map) { + panic("%s() could not allocate resource map\n", __func__); + } + memset(ioc->res_map, 0, ioc->res_size); + + /* Initialize the res_hint to 16 */ + ioc->res_hint = 16; + + /* Initialize the spinlock */ + spin_lock_init(&ioc->res_lock); + + /* + ** Chainid is the upper most bits of an IOVP used to determine + ** which TLB entry an IOVP will use. + */ + ioc->chainid_shift = get_order(iova_space_size) + PAGE_SHIFT - CCIO_CHAINID_SHIFT; + DBG_INIT(" chainid_shift 0x%x\n", ioc->chainid_shift); + + /* + ** Initialize IOA hardware + */ + WRITE_U32(CCIO_CHAINID_MASK << ioc->chainid_shift, + &ioc->ioc_regs->io_chain_id_mask); + + WRITE_U32(virt_to_phys(ioc->pdir_base), + &ioc->ioc_regs->io_pdir_base); + + /* + ** Go to "Virtual Mode" + */ + WRITE_U32(IOA_NORMAL_MODE, &ioc->ioc_regs->io_control); + + /* + ** Initialize all I/O TLB entries to 0 (Valid bit off). + */ + WRITE_U32(0, &ioc->ioc_regs->io_tlb_entry_m); + WRITE_U32(0, &ioc->ioc_regs->io_tlb_entry_l); + + for(i = 1 << CCIO_CHAINID_SHIFT; i ; i--) { + WRITE_U32((CMD_TLB_DIRECT_WRITE | (i << ioc->chainid_shift)), + &ioc->ioc_regs->io_command); + } +} + +static void __init +ccio_init_resource(struct resource *res, char *name, void __iomem *ioaddr) +{ + int result; + + res->parent = NULL; + res->flags = IORESOURCE_MEM; + /* + * bracing ((signed) ...) are required for 64bit kernel because + * we only want to sign extend the lower 16 bits of the register. + * The upper 16-bits of range registers are hardcoded to 0xffff. + */ + res->start = (unsigned long)((signed) READ_U32(ioaddr) << 16); + res->end = (unsigned long)((signed) (READ_U32(ioaddr + 4) << 16) - 1); + res->name = name; + /* + * Check if this MMIO range is disable + */ + if (res->end + 1 == res->start) + return; + + /* On some platforms (e.g. K-Class), we have already registered + * resources for devices reported by firmware. Some are children + * of ccio. + * "insert" ccio ranges in the mmio hierarchy (/proc/iomem). + */ + result = insert_resource(&iomem_resource, res); + if (result < 0) { + printk(KERN_ERR "%s() failed to claim CCIO bus address space (%08lx,%08lx)\n", + __func__, (unsigned long)res->start, (unsigned long)res->end); + } +} + +static int __init ccio_init_resources(struct ioc *ioc) +{ + struct resource *res = ioc->mmio_region; + char *name = kmalloc(14, GFP_KERNEL); + if (unlikely(!name)) + return -ENOMEM; + snprintf(name, 14, "GSC Bus [%d/]", ioc->hw_path); + + ccio_init_resource(res, name, &ioc->ioc_regs->io_io_low); + ccio_init_resource(res + 1, name, &ioc->ioc_regs->io_io_low_hv); + return 0; +} + +static int new_ioc_area(struct resource *res, unsigned long size, + unsigned long min, unsigned long max, unsigned long align) +{ + if (max <= min) + return -EBUSY; + + res->start = (max - size + 1) &~ (align - 1); + res->end = res->start + size; + + /* We might be trying to expand the MMIO range to include + * a child device that has already registered it's MMIO space. + * Use "insert" instead of request_resource(). + */ + if (!insert_resource(&iomem_resource, res)) + return 0; + + return new_ioc_area(res, size, min, max - size, align); +} + +static int expand_ioc_area(struct resource *res, unsigned long size, + unsigned long min, unsigned long max, unsigned long align) +{ + unsigned long start, len; + + if (!res->parent) + return new_ioc_area(res, size, min, max, align); + + start = (res->start - size) &~ (align - 1); + len = res->end - start + 1; + if (start >= min) { + if (!adjust_resource(res, start, len)) + return 0; + } + + start = res->start; + len = ((size + res->end + align) &~ (align - 1)) - start; + if (start + len <= max) { + if (!adjust_resource(res, start, len)) + return 0; + } + + return -EBUSY; +} + +/* + * Dino calls this function. Beware that we may get called on systems + * which have no IOC (725, B180, C160L, etc) but do have a Dino. + * So it's legal to find no parent IOC. + * + * Some other issues: one of the resources in the ioc may be unassigned. + */ +int ccio_allocate_resource(const struct parisc_device *dev, + struct resource *res, unsigned long size, + unsigned long min, unsigned long max, unsigned long align) +{ + struct resource *parent = &iomem_resource; + struct ioc *ioc = ccio_get_iommu(dev); + if (!ioc) + goto out; + + parent = ioc->mmio_region; + if (parent->parent && + !allocate_resource(parent, res, size, min, max, align, NULL, NULL)) + return 0; + + if ((parent + 1)->parent && + !allocate_resource(parent + 1, res, size, min, max, align, + NULL, NULL)) + return 0; + + if (!expand_ioc_area(parent, size, min, max, align)) { + __raw_writel(((parent->start)>>16) | 0xffff0000, + &ioc->ioc_regs->io_io_low); + __raw_writel(((parent->end)>>16) | 0xffff0000, + &ioc->ioc_regs->io_io_high); + } else if (!expand_ioc_area(parent + 1, size, min, max, align)) { + parent++; + __raw_writel(((parent->start)>>16) | 0xffff0000, + &ioc->ioc_regs->io_io_low_hv); + __raw_writel(((parent->end)>>16) | 0xffff0000, + &ioc->ioc_regs->io_io_high_hv); + } else { + return -EBUSY; + } + + out: + return allocate_resource(parent, res, size, min, max, align, NULL,NULL); +} + +int ccio_request_resource(const struct parisc_device *dev, + struct resource *res) +{ + struct resource *parent; + struct ioc *ioc = ccio_get_iommu(dev); + + if (!ioc) { + parent = &iomem_resource; + } else if ((ioc->mmio_region->start <= res->start) && + (res->end <= ioc->mmio_region->end)) { + parent = ioc->mmio_region; + } else if (((ioc->mmio_region + 1)->start <= res->start) && + (res->end <= (ioc->mmio_region + 1)->end)) { + parent = ioc->mmio_region + 1; + } else { + return -EBUSY; + } + + /* "transparent" bus bridges need to register MMIO resources + * firmware assigned them. e.g. children of hppb.c (e.g. K-class) + * registered their resources in the PDC "bus walk" (See + * arch/parisc/kernel/inventory.c). + */ + return insert_resource(parent, res); +} + +/** + * ccio_probe - Determine if ccio should claim this device. + * @dev: The device which has been found + * + * Determine if ccio should claim this chip (return 0) or not (return 1). + * If so, initialize the chip and tell other partners in crime they + * have work to do. + */ +static int __init ccio_probe(struct parisc_device *dev) +{ + int i; + struct ioc *ioc, **ioc_p = &ioc_list; + struct pci_hba_data *hba; + + ioc = kzalloc_obj(struct ioc); + if (ioc == NULL) { + printk(KERN_ERR MODULE_NAME ": memory allocation failure\n"); + return -ENOMEM; + } + + ioc->name = dev->id.hversion == U2_IOA_RUNWAY ? "U2" : "UTurn"; + + printk(KERN_INFO "Found %s at 0x%lx\n", ioc->name, + (unsigned long)dev->hpa.start); + + for (i = 0; i < ioc_count; i++) { + ioc_p = &(*ioc_p)->next; + } + *ioc_p = ioc; + + ioc->hw_path = dev->hw_path; + ioc->ioc_regs = ioremap(dev->hpa.start, 4096); + if (!ioc->ioc_regs) { + kfree(ioc); + return -ENOMEM; + } + ccio_ioc_init(ioc); + if (ccio_init_resources(ioc)) { + iounmap(ioc->ioc_regs); + kfree(ioc); + return -ENOMEM; + } + hppa_dma_ops = &ccio_ops; + + hba = kzalloc_obj(*hba); + /* if this fails, no I/O cards will work, so may as well bug */ + BUG_ON(hba == NULL); + + hba->iommu = ioc; + dev->dev.platform_data = hba; + +#ifdef CONFIG_PROC_FS + if (ioc_count == 0) { + struct proc_dir_entry *runway; + + runway = proc_mkdir("bus/runway", NULL); + if (runway) { + proc_create_single(MODULE_NAME, 0, runway, + ccio_proc_info); + proc_create_single(MODULE_NAME"-bitmap", 0, runway, + ccio_proc_bitmap_info); + } + } +#endif + ioc_count++; + return 0; +} + +/** + * ccio_init - ccio initialization procedure. + * + * Register this driver. + */ +static int __init ccio_init(void) +{ + return register_parisc_driver(&ccio_driver); +} +arch_initcall(ccio_init); diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c new file mode 100644 index 000000000..4b96d9fda --- /dev/null +++ b/drivers/parisc/dino.c @@ -0,0 +1,1091 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** DINO manager +** +** (c) Copyright 1999 Red Hat Software +** (c) Copyright 1999 SuSE GmbH +** (c) Copyright 1999,2000 Hewlett-Packard Company +** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2006-2019 Helge Deller +** +** +** This module provides access to Dino PCI bus (config/IOport spaces) +** and helps manage Dino IRQ lines. +** +** Dino interrupt handling is a bit complicated. +** Dino always writes to the broadcast EIR via irr0 for now. +** (BIG WARNING: using broadcast EIR is a really bad thing for SMP!) +** Only one processor interrupt is used for the 11 IRQ line +** inputs to dino. +** +** The different between Built-in Dino and Card-Mode +** dino is in chip initialization and pci device initialization. +** +** Linux drivers can only use Card-Mode Dino if pci devices I/O port +** BARs are configured and used by the driver. Programming MMIO address +** requires substantial knowledge of available Host I/O address ranges +** is currently not supported. Port/Config accessor functions are the +** same. "BIOS" differences are handled within the existing routines. +*/ + +/* Changes : +** 2001-06-14 : Clement Moyroud (moyroudc@esiee.fr) +** - added support for the integrated RS232. +*/ + +/* +** TODO: create a virtual address for each Dino HPA. +** GSC code might be able to do this since IODC data tells us +** how many pages are used. PCI subsystem could (must?) do this +** for PCI drivers devices which implement/use MMIO registers. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include /* for struct irqaction */ +#include /* for spinlock_t and prototypes */ + +#include +#include +#include +#include + +#include "gsc.h" +#include "iommu.h" + +#undef DINO_DEBUG + +#ifdef DINO_DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* +** Config accessor functions only pass in the 8-bit bus number +** and not the 8-bit "PCI Segment" number. Each Dino will be +** assigned a PCI bus number based on "when" it's discovered. +** +** The "secondary" bus number is set to this before calling +** pci_scan_bus(). If any PPB's are present, the scan will +** discover them and update the "secondary" and "subordinate" +** fields in Dino's pci_bus structure. +** +** Changes in the configuration *will* result in a different +** bus number for each dino. +*/ + +#define is_card_dino(id) ((id)->hw_type == HPHW_A_DMA) +#define is_cujo(id) ((id)->hversion == 0x682) + +#define DINO_IAR0 0x004 +#define DINO_IODC_ADDR 0x008 +#define DINO_IODC_DATA_0 0x008 +#define DINO_IODC_DATA_1 0x008 +#define DINO_IRR0 0x00C +#define DINO_IAR1 0x010 +#define DINO_IRR1 0x014 +#define DINO_IMR 0x018 +#define DINO_IPR 0x01C +#define DINO_TOC_ADDR 0x020 +#define DINO_ICR 0x024 +#define DINO_ILR 0x028 +#define DINO_IO_COMMAND 0x030 +#define DINO_IO_STATUS 0x034 +#define DINO_IO_CONTROL 0x038 +#define DINO_IO_GSC_ERR_RESP 0x040 +#define DINO_IO_ERR_INFO 0x044 +#define DINO_IO_PCI_ERR_RESP 0x048 +#define DINO_IO_FBB_EN 0x05c +#define DINO_IO_ADDR_EN 0x060 +#define DINO_PCI_ADDR 0x064 +#define DINO_CONFIG_DATA 0x068 +#define DINO_IO_DATA 0x06c +#define DINO_MEM_DATA 0x070 /* Dino 3.x only */ +#define DINO_GSC2X_CONFIG 0x7b4 +#define DINO_GMASK 0x800 +#define DINO_PAMR 0x804 +#define DINO_PAPR 0x808 +#define DINO_DAMODE 0x80c +#define DINO_PCICMD 0x810 +#define DINO_PCISTS 0x814 +#define DINO_MLTIM 0x81c +#define DINO_BRDG_FEAT 0x820 +#define DINO_PCIROR 0x824 +#define DINO_PCIWOR 0x828 +#define DINO_TLTIM 0x830 + +#define DINO_IRQS 11 /* bits 0-10 are architected */ +#define DINO_IRR_MASK 0x5ff /* only 10 bits are implemented */ +#define DINO_LOCAL_IRQS (DINO_IRQS+1) + +#define DINO_MASK_IRQ(x) (1<<(x)) + +#define PCIINTA 0x001 +#define PCIINTB 0x002 +#define PCIINTC 0x004 +#define PCIINTD 0x008 +#define PCIINTE 0x010 +#define PCIINTF 0x020 +#define GSCEXTINT 0x040 +/* #define xxx 0x080 - bit 7 is "default" */ +/* #define xxx 0x100 - bit 8 not used */ +/* #define xxx 0x200 - bit 9 not used */ +#define RS232INT 0x400 + +struct dino_device +{ + struct pci_hba_data hba; /* 'C' inheritance - must be first */ + spinlock_t dinosaur_pen; + u32 imr; /* IRQ's which are enabled */ + struct gsc_irq gsc_irq; + int global_irq[DINO_LOCAL_IRQS]; /* map IMR bit to global irq */ +#ifdef DINO_DEBUG + unsigned int dino_irr0; /* save most recent IRQ line stat */ +#endif +}; + +static inline struct dino_device *DINO_DEV(struct pci_hba_data *hba) +{ + return container_of(hba, struct dino_device, hba); +} + +/* + * Dino Configuration Space Accessor Functions + */ + +#define DINO_CFG_TOK(bus,dfn,pos) ((u32) ((bus)<<16 | (dfn)<<8 | (pos))) + +/* + * keep the current highest bus count to assist in allocating busses. This + * tries to keep a global bus count total so that when we discover an + * entirely new bus, it can be given a unique bus number. + */ +static int dino_current_bus = 0; + +static int dino_cfg_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *val) +{ + struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; + u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); + void __iomem *base_addr = d->hba.base_addr; + unsigned long flags; + + DBG("%s: %p, %d, %d, %d\n", __func__, base_addr, devfn, where, + size); + spin_lock_irqsave(&d->dinosaur_pen, flags); + + /* tell HW which CFG address */ + __raw_writel(v, base_addr + DINO_PCI_ADDR); + + /* generate cfg read cycle */ + if (size == 1) { + *val = readb(base_addr + DINO_CONFIG_DATA + (where & 3)); + } else if (size == 2) { + *val = readw(base_addr + DINO_CONFIG_DATA + (where & 2)); + } else if (size == 4) { + *val = readl(base_addr + DINO_CONFIG_DATA); + } + + spin_unlock_irqrestore(&d->dinosaur_pen, flags); + return 0; +} + +/* + * Dino address stepping "feature": + * When address stepping, Dino attempts to drive the bus one cycle too soon + * even though the type of cycle (config vs. MMIO) might be different. + * The read of Ven/Prod ID is harmless and avoids Dino's address stepping. + */ +static int dino_cfg_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; + u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); + void __iomem *base_addr = d->hba.base_addr; + unsigned long flags; + + DBG("%s: %p, %d, %d, %d\n", __func__, base_addr, devfn, where, + size); + spin_lock_irqsave(&d->dinosaur_pen, flags); + + /* avoid address stepping feature */ + __raw_writel(v & 0xffffff00, base_addr + DINO_PCI_ADDR); + __raw_readl(base_addr + DINO_CONFIG_DATA); + + /* tell HW which CFG address */ + __raw_writel(v, base_addr + DINO_PCI_ADDR); + /* generate cfg read cycle */ + if (size == 1) { + writeb(val, base_addr + DINO_CONFIG_DATA + (where & 3)); + } else if (size == 2) { + writew(val, base_addr + DINO_CONFIG_DATA + (where & 2)); + } else if (size == 4) { + writel(val, base_addr + DINO_CONFIG_DATA); + } + + spin_unlock_irqrestore(&d->dinosaur_pen, flags); + return 0; +} + +static struct pci_ops dino_cfg_ops = { + .read = dino_cfg_read, + .write = dino_cfg_write, +}; + + +/* + * Dino "I/O Port" Space Accessor Functions + * + * Many PCI devices don't require use of I/O port space (eg Tulip, + * NCR720) since they export the same registers to both MMIO and + * I/O port space. Performance is going to stink if drivers use + * I/O port instead of MMIO. + */ + +#define DINO_PORT_IN(type, size, mask) \ +static u##size dino_in##size (struct pci_hba_data *d, u16 addr) \ +{ \ + u##size v; \ + unsigned long flags; \ + spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \ + /* tell HW which IO Port address */ \ + __raw_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \ + /* generate I/O PORT read cycle */ \ + v = read##type(d->base_addr+DINO_IO_DATA+(addr&mask)); \ + spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \ + return v; \ +} + +DINO_PORT_IN(b, 8, 3) +DINO_PORT_IN(w, 16, 2) +DINO_PORT_IN(l, 32, 0) + +#define DINO_PORT_OUT(type, size, mask) \ +static void dino_out##size (struct pci_hba_data *d, u16 addr, u##size val) \ +{ \ + unsigned long flags; \ + spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \ + /* tell HW which IO port address */ \ + __raw_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \ + /* generate cfg write cycle */ \ + write##type(val, d->base_addr+DINO_IO_DATA+(addr&mask)); \ + spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \ +} + +DINO_PORT_OUT(b, 8, 3) +DINO_PORT_OUT(w, 16, 2) +DINO_PORT_OUT(l, 32, 0) + +static struct pci_port_ops dino_port_ops = { + .inb = dino_in8, + .inw = dino_in16, + .inl = dino_in32, + .outb = dino_out8, + .outw = dino_out16, + .outl = dino_out32 +}; + +static void dino_mask_irq(struct irq_data *d) +{ + struct dino_device *dino_dev = irq_data_get_irq_chip_data(d); + int local_irq = gsc_find_local_irq(d->irq, dino_dev->global_irq, DINO_LOCAL_IRQS); + + DBG(KERN_WARNING "%s(0x%px, %d)\n", __func__, dino_dev, d->irq); + + /* Clear the matching bit in the IMR register */ + dino_dev->imr &= ~(DINO_MASK_IRQ(local_irq)); + __raw_writel(dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR); +} + +static void dino_unmask_irq(struct irq_data *d) +{ + struct dino_device *dino_dev = irq_data_get_irq_chip_data(d); + int local_irq = gsc_find_local_irq(d->irq, dino_dev->global_irq, DINO_LOCAL_IRQS); + u32 tmp; + + DBG(KERN_WARNING "%s(0x%px, %d)\n", __func__, dino_dev, d->irq); + + /* + ** clear pending IRQ bits + ** + ** This does NOT change ILR state! + ** See comment below for ILR usage. + */ + __raw_readl(dino_dev->hba.base_addr+DINO_IPR); + + /* set the matching bit in the IMR register */ + dino_dev->imr |= DINO_MASK_IRQ(local_irq); /* used in dino_isr() */ + __raw_writel( dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR); + + /* Emulate "Level Triggered" Interrupt + ** Basically, a driver is blowing it if the IRQ line is asserted + ** while the IRQ is disabled. But tulip.c seems to do that.... + ** Give 'em a kluge award and a nice round of applause! + ** + ** The gsc_write will generate an interrupt which invokes dino_isr(). + ** dino_isr() will read IPR and find nothing. But then catch this + ** when it also checks ILR. + */ + tmp = __raw_readl(dino_dev->hba.base_addr+DINO_ILR); + if (tmp & DINO_MASK_IRQ(local_irq)) { + DBG(KERN_WARNING "%s(): IRQ asserted! (ILR 0x%x)\n", + __func__, tmp); + gsc_writel(dino_dev->gsc_irq.txn_data, dino_dev->gsc_irq.txn_addr); + } +} + +#ifdef CONFIG_SMP +static int dino_set_affinity_irq(struct irq_data *d, const struct cpumask *dest, + bool force) +{ + struct dino_device *dino_dev = irq_data_get_irq_chip_data(d); + struct cpumask tmask; + int cpu_irq; + u32 eim; + + if (!cpumask_and(&tmask, dest, cpu_online_mask)) + return -EINVAL; + + cpu_irq = cpu_check_affinity(d, &tmask); + if (cpu_irq < 0) + return cpu_irq; + + dino_dev->gsc_irq.txn_addr = txn_affinity_addr(d->irq, cpu_irq); + eim = ((u32) dino_dev->gsc_irq.txn_addr) | dino_dev->gsc_irq.txn_data; + __raw_writel(eim, dino_dev->hba.base_addr+DINO_IAR0); + + irq_data_update_effective_affinity(d, &tmask); + + return IRQ_SET_MASK_OK; +} +#endif + +static struct irq_chip dino_interrupt_type = { + .name = "GSC-PCI", + .irq_unmask = dino_unmask_irq, + .irq_mask = dino_mask_irq, +#ifdef CONFIG_SMP + .irq_set_affinity = dino_set_affinity_irq, +#endif +}; + + +/* + * Handle a Processor interrupt generated by Dino. + * + * ilr_loop counter is a kluge to prevent a "stuck" IRQ line from + * wedging the CPU. Could be removed or made optional at some point. + */ +static irqreturn_t dino_isr(int irq, void *intr_dev) +{ + struct dino_device *dino_dev = intr_dev; + u32 mask; + int ilr_loop = 100; + + /* read and acknowledge pending interrupts */ +#ifdef DINO_DEBUG + dino_dev->dino_irr0 = +#endif + mask = __raw_readl(dino_dev->hba.base_addr+DINO_IRR0) & DINO_IRR_MASK; + + if (mask == 0) + return IRQ_NONE; + +ilr_again: + do { + int local_irq = __ffs(mask); + int irq = dino_dev->global_irq[local_irq]; + DBG(KERN_DEBUG "%s(%d, %p) mask 0x%x\n", + __func__, irq, intr_dev, mask); + generic_handle_irq(irq); + mask &= ~DINO_MASK_IRQ(local_irq); + } while (mask); + + /* Support for level triggered IRQ lines. + ** + ** Dropping this support would make this routine *much* faster. + ** But since PCI requires level triggered IRQ line to share lines... + ** device drivers may assume lines are level triggered (and not + ** edge triggered like EISA/ISA can be). + */ + mask = __raw_readl(dino_dev->hba.base_addr+DINO_ILR) & dino_dev->imr; + if (mask) { + if (--ilr_loop > 0) + goto ilr_again; + pr_warn_ratelimited("Dino 0x%px: stuck interrupt %d\n", + dino_dev->hba.base_addr, mask); + } + return IRQ_HANDLED; +} + +static void dino_assign_irq(struct dino_device *dino, int local_irq, int *irqp) +{ + int irq = gsc_assign_irq(&dino_interrupt_type, dino); + if (irq == NO_IRQ) + return; + + *irqp = irq; + dino->global_irq[local_irq] = irq; +} + +static void dino_choose_irq(struct parisc_device *dev, void *ctrl) +{ + int irq; + struct dino_device *dino = ctrl; + + switch (dev->id.sversion) { + case 0x00084: irq = 8; break; /* PS/2 */ + case 0x0008c: irq = 10; break; /* RS232 */ + case 0x00096: irq = 8; break; /* PS/2 */ + default: return; /* Unknown */ + } + + dino_assign_irq(dino, irq, &dev->irq); +} + + +/* + * Cirrus 6832 Cardbus reports wrong irq on RDI Tadpole PARISC Laptop (deller@gmx.de) + * (the irqs are off-by-one, not sure yet if this is a cirrus, dino-hardware or dino-driver problem...) + */ +static void quirk_cirrus_cardbus(struct pci_dev *dev) +{ + u8 new_irq = dev->irq - 1; + printk(KERN_INFO "PCI: Cirrus Cardbus IRQ fixup for %s, from %d to %d\n", + pci_name(dev), dev->irq, new_irq); + dev->irq = new_irq; +} +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6832, quirk_cirrus_cardbus ); + +#ifdef CONFIG_TULIP +/* Check if PCI device is behind a Card-mode Dino. */ +static int pci_dev_is_behind_card_dino(struct pci_dev *dev) +{ + struct dino_device *dino_dev; + + dino_dev = DINO_DEV(parisc_walk_tree(dev->bus->bridge)); + return is_card_dino(&dino_dev->hba.dev->id); +} + +static void pci_fixup_tulip(struct pci_dev *dev) +{ + if (!pci_dev_is_behind_card_dino(dev)) + return; + if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) + return; + pr_warn("%s: HP HSC-PCI Cards with card-mode Dino not yet supported.\n", + pci_name(dev)); + /* Disable this card by zeroing the PCI resources */ + memset(&dev->resource[0], 0, sizeof(dev->resource[0])); + memset(&dev->resource[1], 0, sizeof(dev->resource[1])); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_DEC, PCI_ANY_ID, pci_fixup_tulip); +#endif /* CONFIG_TULIP */ + +static void __init +dino_bios_init(void) +{ + DBG("dino_bios_init\n"); +} + +/* + * dino_card_setup - Set up the memory space for a Dino in card mode. + * @bus: the bus under this dino + * + * Claim an 8MB chunk of unused IO space and call the generic PCI routines + * to set up the addresses of the devices on this bus. + */ +#define _8MB 0x00800000UL +static void __init +dino_card_setup(struct pci_bus *bus, void __iomem *base_addr) +{ + int i; + struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge)); + struct resource *res; + char name[128]; + int size; + + res = &dino_dev->hba.lmmio_space; + res->flags = IORESOURCE_MEM; + size = scnprintf(name, sizeof(name), "Dino LMMIO (%s)", + dev_name(bus->bridge)); + res->name = kmalloc(size+1, GFP_KERNEL); + if(res->name) + strcpy((char *)res->name, name); + else + res->name = dino_dev->hba.lmmio_space.name; + + + if (ccio_allocate_resource(dino_dev->hba.dev, res, _8MB, + F_EXTEND(0xf0000000UL) | _8MB, + F_EXTEND(0xffffffffUL) &~ _8MB, _8MB) < 0) { + struct pci_dev *dev, *tmp; + + printk(KERN_ERR "Dino: cannot attach bus %s\n", + dev_name(bus->bridge)); + /* kill the bus, we can't do anything with it */ + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { + list_del(&dev->bus_list); + } + + return; + } + bus->resource[1] = res; + bus->resource[0] = &(dino_dev->hba.io_space); + + /* Now tell dino what range it has */ + for (i = 1; i < 31; i++) { + if (res->start == F_EXTEND(0xf0000000UL | (i * _8MB))) + break; + } + DBG("DINO GSC WRITE i=%d, start=%lx, dino addr = %p\n", + i, res->start, base_addr + DINO_IO_ADDR_EN); + __raw_writel(1 << i, base_addr + DINO_IO_ADDR_EN); +} + +static void __init +dino_card_fixup(struct pci_dev *dev) +{ + u32 irq_pin; + + /* + ** REVISIT: card-mode PCI-PCI expansion chassis do exist. + ** Not sure they were ever productized. + ** Die here since we'll die later in dino_inb() anyway. + */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + panic("Card-Mode Dino: PCI-PCI Bridge not supported\n"); + } + + /* + ** Set Latency Timer to 0xff (not a shared bus) + ** Set CACHELINE_SIZE. + */ + dino_cfg_write(dev->bus, dev->devfn, + PCI_CACHE_LINE_SIZE, 2, 0xff00 | L1_CACHE_BYTES/4); + + /* + ** Program INT_LINE for card-mode devices. + ** The cards are hardwired according to this algorithm. + ** And it doesn't matter if PPB's are present or not since + ** the IRQ lines bypass the PPB. + ** + ** "-1" converts INTA-D (1-4) to PCIINTA-D (0-3) range. + ** The additional "-1" adjusts for skewing the IRQ<->slot. + */ + dino_cfg_read(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 1, &irq_pin); + dev->irq = pci_swizzle_interrupt_pin(dev, irq_pin) - 1; + + /* Shouldn't really need to do this but it's in case someone tries + ** to bypass PCI services and look at the card themselves. + */ + dino_cfg_write(dev->bus, dev->devfn, PCI_INTERRUPT_LINE, 1, dev->irq); +} + +/* The alignment contraints for PCI bridges under dino */ +#define DINO_BRIDGE_ALIGN 0x100000 + + +static void __init +dino_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev; + struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge)); + + DBG(KERN_WARNING "%s(0x%px) bus %d platform_data 0x%px\n", + __func__, bus, bus->busn_res.start, + bus->bridge->platform_data); + + /* Firmware doesn't set up card-mode dino, so we have to */ + if (is_card_dino(&dino_dev->hba.dev->id)) { + dino_card_setup(bus, dino_dev->hba.base_addr); + } else if (bus->parent) { + int i; + + pci_read_bridge_bases(bus); + + + for(i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + if((bus->self->resource[i].flags & + (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + continue; + + if(bus->self->resource[i].flags & IORESOURCE_MEM) { + /* There's a quirk to alignment of + * bridge memory resources: the start + * is the alignment and start-end is + * the size. However, firmware will + * have assigned start and end, so we + * need to take this into account */ + bus->self->resource[i].end = bus->self->resource[i].end - bus->self->resource[i].start + DINO_BRIDGE_ALIGN; + bus->self->resource[i].start = DINO_BRIDGE_ALIGN; + + } + + DBG("DEBUG %s assigning %d [%pR]\n", + dev_name(&bus->self->dev), i, + &bus->self->resource[i]); + WARN_ON(pci_assign_resource(bus->self, i)); + DBG("DEBUG %s after assign %d [%pR]\n", + dev_name(&bus->self->dev), i, + &bus->self->resource[i]); + } + } + + + list_for_each_entry(dev, &bus->devices, bus_list) { + if (is_card_dino(&dino_dev->hba.dev->id)) + dino_card_fixup(dev); + + /* + ** P2PB's only have 2 BARs, no IRQs. + ** I'd like to just ignore them for now. + */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + pcibios_init_bridge(dev); + continue; + } + + /* null out the ROM resource if there is one (we don't + * care about an expansion rom on parisc, since it + * usually contains (x86) bios code) */ + dev->resource[PCI_ROM_RESOURCE].flags = 0; + + if(dev->irq == 255) { + +#define DINO_FIX_UNASSIGNED_INTERRUPTS +#ifdef DINO_FIX_UNASSIGNED_INTERRUPTS + + /* This code tries to assign an unassigned + * interrupt. Leave it disabled unless you + * *really* know what you're doing since the + * pin<->interrupt line mapping varies by bus + * and machine */ + + u32 irq_pin; + + dino_cfg_read(dev->bus, dev->devfn, + PCI_INTERRUPT_PIN, 1, &irq_pin); + irq_pin = pci_swizzle_interrupt_pin(dev, irq_pin) - 1; + printk(KERN_WARNING "Device %s has undefined IRQ, " + "setting to %d\n", pci_name(dev), irq_pin); + dino_cfg_write(dev->bus, dev->devfn, + PCI_INTERRUPT_LINE, 1, irq_pin); + dino_assign_irq(dino_dev, irq_pin, &dev->irq); +#else + dev->irq = 65535; + printk(KERN_WARNING "Device %s has unassigned IRQ\n", pci_name(dev)); +#endif + } else { + /* Adjust INT_LINE for that busses region */ + dino_assign_irq(dino_dev, dev->irq, &dev->irq); + } + } +} + + +static struct pci_bios_ops dino_bios_ops = { + .init = dino_bios_init, + .fixup_bus = dino_fixup_bus +}; + + +/* + * Initialise a DINO controller chip + */ +static void __init +dino_card_init(struct dino_device *dino_dev) +{ + u32 brdg_feat = 0x00784e05; + unsigned long status; + + status = __raw_readl(dino_dev->hba.base_addr+DINO_IO_STATUS); + if (status & 0x0000ff80) { + __raw_writel(0x00000005, + dino_dev->hba.base_addr+DINO_IO_COMMAND); + udelay(1); + } + + __raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_GMASK); + __raw_writel(0x00000001, dino_dev->hba.base_addr+DINO_IO_FBB_EN); + __raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_ICR); + +#if 1 +/* REVISIT - should be a runtime check (eg if (CPU_IS_PCX_L) ...) */ + /* + ** PCX-L processors don't support XQL like Dino wants it. + ** PCX-L2 ignore XQL signal and it doesn't matter. + */ + brdg_feat &= ~0x4; /* UXQL */ +#endif + __raw_writel( brdg_feat, dino_dev->hba.base_addr+DINO_BRDG_FEAT); + + /* + ** Don't enable address decoding until we know which I/O range + ** currently is available from the host. Only affects MMIO + ** and not I/O port space. + */ + __raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_IO_ADDR_EN); + + __raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_DAMODE); + __raw_writel(0x00222222, dino_dev->hba.base_addr+DINO_PCIROR); + __raw_writel(0x00222222, dino_dev->hba.base_addr+DINO_PCIWOR); + + __raw_writel(0x00000040, dino_dev->hba.base_addr+DINO_MLTIM); + __raw_writel(0x00000080, dino_dev->hba.base_addr+DINO_IO_CONTROL); + __raw_writel(0x0000008c, dino_dev->hba.base_addr+DINO_TLTIM); + + /* Disable PAMR before writing PAPR */ + __raw_writel(0x0000007e, dino_dev->hba.base_addr+DINO_PAMR); + __raw_writel(0x0000007f, dino_dev->hba.base_addr+DINO_PAPR); + __raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_PAMR); + + /* + ** Dino ERS encourages enabling FBB (0x6f). + ** We can't until we know *all* devices below us can support it. + ** (Something in device configuration header tells us). + */ + __raw_writel(0x0000004f, dino_dev->hba.base_addr+DINO_PCICMD); + + /* Somewhere, the PCI spec says give devices 1 second + ** to recover from the #RESET being de-asserted. + ** Experience shows most devices only need 10ms. + ** This short-cut speeds up booting significantly. + */ + mdelay(pci_post_reset_delay); +} + +static int __init +dino_bridge_init(struct dino_device *dino_dev, const char *name) +{ + unsigned long io_addr; + int result, i, count=0; + struct resource *res, *prevres = NULL; + /* + * Decoding IO_ADDR_EN only works for Built-in Dino + * since PDC has already initialized this. + */ + + io_addr = __raw_readl(dino_dev->hba.base_addr + DINO_IO_ADDR_EN); + if (io_addr == 0) { + printk(KERN_WARNING "%s: No PCI devices enabled.\n", name); + return -ENODEV; + } + + res = &dino_dev->hba.lmmio_space; + for (i = 0; i < 32; i++) { + unsigned long start, end; + + if((io_addr & (1 << i)) == 0) + continue; + + start = F_EXTEND(0xf0000000UL) | (i << 23); + end = start + 8 * 1024 * 1024 - 1; + + DBG("DINO RANGE %d is at 0x%lx-0x%lx\n", count, + start, end); + + if(prevres && prevres->end + 1 == start) { + prevres->end = end; + } else { + if(count >= DINO_MAX_LMMIO_RESOURCES) { + printk(KERN_ERR "%s is out of resource windows for range %d (0x%lx-0x%lx)\n", name, count, start, end); + break; + } + prevres = res; + res->start = start; + res->end = end; + res->flags = IORESOURCE_MEM; + res->name = kmalloc(64, GFP_KERNEL); + if(res->name) + snprintf((char *)res->name, 64, "%s LMMIO %d", + name, count); + res++; + count++; + } + } + + res = &dino_dev->hba.lmmio_space; + + for(i = 0; i < DINO_MAX_LMMIO_RESOURCES; i++) { + if(res[i].flags == 0) + break; + + result = ccio_request_resource(dino_dev->hba.dev, &res[i]); + if (result < 0) { + printk(KERN_ERR "%s: failed to claim PCI Bus address " + "space %d (%pR)!\n", name, i, &res[i]); + return result; + } + } + return 0; +} + +static int __init dino_common_init(struct parisc_device *dev, + struct dino_device *dino_dev, const char *name) +{ + int status; + u32 eim; + struct resource *res; + + pcibios_register_hba(&dino_dev->hba); + + pci_bios = &dino_bios_ops; /* used by pci_scan_bus() */ + pci_port = &dino_port_ops; + + /* + ** Note: SMP systems can make use of IRR1/IAR1 registers + ** But it won't buy much performance except in very + ** specific applications/configurations. Note Dino + ** still only has 11 IRQ input lines - just map some of them + ** to a different processor. + */ + dev->irq = gsc_alloc_irq(&dino_dev->gsc_irq); + eim = ((u32) dino_dev->gsc_irq.txn_addr) | dino_dev->gsc_irq.txn_data; + + /* + ** Dino needs a PA "IRQ" to get a processor's attention. + ** arch/parisc/kernel/irq.c returns an EIRR bit. + */ + if (dev->irq < 0) { + printk(KERN_WARNING "%s: gsc_alloc_irq() failed\n", name); + return 1; + } + + status = request_irq(dev->irq, dino_isr, 0, name, dino_dev); + if (status) { + printk(KERN_WARNING "%s: request_irq() failed with %d\n", + name, status); + return 1; + } + + /* Support the serial port which is sometimes attached on built-in + * Dino / Cujo chips. + */ + + gsc_fixup_irqs(dev, dino_dev, dino_choose_irq); + + /* + ** This enables DINO to generate interrupts when it sees + ** any of its inputs *change*. Just asserting an IRQ + ** before it's enabled (ie unmasked) isn't good enough. + */ + __raw_writel(eim, dino_dev->hba.base_addr+DINO_IAR0); + + /* + ** Some platforms don't clear Dino's IRR0 register at boot time. + ** Reading will clear it now. + */ + __raw_readl(dino_dev->hba.base_addr+DINO_IRR0); + + /* allocate I/O Port resource region */ + res = &dino_dev->hba.io_space; + if (!is_cujo(&dev->id)) { + res->name = "Dino I/O Port"; + } else { + res->name = "Cujo I/O Port"; + } + res->start = HBA_PORT_BASE(dino_dev->hba.hba_num); + res->end = res->start + (HBA_PORT_SPACE_SIZE - 1); + res->flags = IORESOURCE_IO; /* do not mark it busy ! */ + if (request_resource(&ioport_resource, res) < 0) { + printk(KERN_ERR "%s: request I/O Port region failed " + "0x%lx/%lx (hpa 0x%px)\n", + name, (unsigned long)res->start, (unsigned long)res->end, + dino_dev->hba.base_addr); + return 1; + } + + return 0; +} + +#define CUJO_RAVEN_ADDR F_EXTEND(0xf1000000UL) +#define CUJO_FIREHAWK_ADDR F_EXTEND(0xf1604000UL) +#define CUJO_RAVEN_BADPAGE 0x01003000UL +#define CUJO_FIREHAWK_BADPAGE 0x01607000UL + +static const char dino_vers[][4] = { + "2.0", + "2.1", + "3.0", + "3.1" +}; + +static const char cujo_vers[][4] = { + "1.0", + "2.0" +}; + +/* +** Determine if dino should claim this chip (return 0) or not (return 1). +** If so, initialize the chip appropriately (card-mode vs bridge mode). +** Much of the initialization is common though. +*/ +static int __init dino_probe(struct parisc_device *dev) +{ + struct dino_device *dino_dev; // Dino specific control struct + const char *version = "unknown"; + char *name; + int is_cujo = 0; + LIST_HEAD(resources); + struct pci_bus *bus; + unsigned long hpa = dev->hpa.start; + int max; + + name = "Dino"; + if (is_card_dino(&dev->id)) { + version = "3.x (card mode)"; + } else { + if (!is_cujo(&dev->id)) { + if (dev->id.hversion_rev < 4) { + version = dino_vers[dev->id.hversion_rev]; + } + } else { + name = "Cujo"; + is_cujo = 1; + if (dev->id.hversion_rev < 2) { + version = cujo_vers[dev->id.hversion_rev]; + } + } + } + + printk("%s version %s found at 0x%lx\n", name, version, hpa); + + if (!request_mem_region(hpa, PAGE_SIZE, name)) { + printk(KERN_ERR "DINO: Hey! Someone took my MMIO space (0x%lx)!\n", + hpa); + return 1; + } + + /* Check for bugs */ + if (is_cujo && dev->id.hversion_rev == 1) { +#ifdef CONFIG_IOMMU_CCIO + printk(KERN_WARNING "Enabling Cujo 2.0 bug workaround\n"); + if (hpa == (unsigned long)CUJO_RAVEN_ADDR) { + ccio_cujo20_fixup(dev, CUJO_RAVEN_BADPAGE); + } else if (hpa == (unsigned long)CUJO_FIREHAWK_ADDR) { + ccio_cujo20_fixup(dev, CUJO_FIREHAWK_BADPAGE); + } else { + printk("Don't recognise Cujo at address 0x%lx, not enabling workaround\n", hpa); + } +#endif + } else if (!is_cujo && !is_card_dino(&dev->id) && + dev->id.hversion_rev < 3) { + printk(KERN_WARNING +"The GSCtoPCI (Dino hrev %d) bus converter found may exhibit\n" +"data corruption. See Service Note Numbers: A4190A-01, A4191A-01.\n" +"Systems shipped after Aug 20, 1997 will not exhibit this problem.\n" +"Models affected: C180, C160, C160L, B160L, and B132L workstations.\n\n", + dev->id.hversion_rev); +/* REVISIT: why are C200/C240 listed in the README table but not +** "Models affected"? Could be an omission in the original literature. +*/ + } + + dino_dev = kzalloc_obj(struct dino_device); + if (!dino_dev) { + printk("dino_init_chip - couldn't alloc dino_device\n"); + return 1; + } + + dino_dev->hba.dev = dev; + dino_dev->hba.base_addr = ioremap(hpa, 4096); + dino_dev->hba.lmmio_space_offset = PCI_F_EXTEND; + spin_lock_init(&dino_dev->dinosaur_pen); + dino_dev->hba.iommu = ccio_get_iommu(dev); + + if (is_card_dino(&dev->id)) { + dino_card_init(dino_dev); + } else { + dino_bridge_init(dino_dev, name); + } + + if (dino_common_init(dev, dino_dev, name)) + return 1; + + dev->dev.platform_data = dino_dev; + + pci_add_resource_offset(&resources, &dino_dev->hba.io_space, + HBA_PORT_BASE(dino_dev->hba.hba_num)); + if (dino_dev->hba.lmmio_space.flags) + pci_add_resource_offset(&resources, &dino_dev->hba.lmmio_space, + dino_dev->hba.lmmio_space_offset); + if (dino_dev->hba.elmmio_space.flags) + pci_add_resource_offset(&resources, &dino_dev->hba.elmmio_space, + dino_dev->hba.lmmio_space_offset); + if (dino_dev->hba.gmmio_space.flags) + pci_add_resource(&resources, &dino_dev->hba.gmmio_space); + + dino_dev->hba.bus_num.start = dino_current_bus; + dino_dev->hba.bus_num.end = 255; + dino_dev->hba.bus_num.flags = IORESOURCE_BUS; + pci_add_resource(&resources, &dino_dev->hba.bus_num); + /* + ** It's not used to avoid chicken/egg problems + ** with configuration accessor functions. + */ + dino_dev->hba.hba_bus = bus = pci_create_root_bus(&dev->dev, + dino_current_bus, &dino_cfg_ops, NULL, &resources); + if (!bus) { + printk(KERN_ERR "ERROR: failed to scan PCI bus on %s (duplicate bus number %d?)\n", + dev_name(&dev->dev), dino_current_bus); + pci_free_resource_list(&resources); + /* increment the bus number in case of duplicates */ + dino_current_bus++; + return 0; + } + + max = pci_scan_child_bus(bus); + pci_bus_update_busn_res_end(bus, max); + + /* This code *depends* on scanning being single threaded + * if it isn't, this global bus number count will fail + */ + dino_current_bus = max + 1; + pci_bus_assign_resources(bus); + pci_bus_add_devices(bus); + return 0; +} + +/* + * Normally, we would just test sversion. But the Elroy PCI adapter has + * the same sversion as Dino, so we have to check hversion as well. + * Unfortunately, the J2240 PDC reports the wrong hversion for the first + * Dino, so we have to test for Dino, Cujo and Dino-in-a-J2240. + * For card-mode Dino, most machines report an sversion of 9D. But 715 + * and 725 firmware misreport it as 0x08080 for no adequately explained + * reason. + */ +static const struct parisc_device_id dino_tbl[] __initconst = { + { HPHW_A_DMA, HVERSION_REV_ANY_ID, 0x004, 0x0009D },/* Card-mode Dino */ + { HPHW_A_DMA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x08080 }, /* XXX */ + { HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x680, 0xa }, /* Bridge-mode Dino */ + { HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x682, 0xa }, /* Bridge-mode Cujo */ + { HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x05d, 0xa }, /* Dino in a J2240 */ + { 0, } +}; + +static struct parisc_driver dino_driver __refdata = { + .name = "dino", + .id_table = dino_tbl, + .probe = dino_probe, +}; + +/* + * One time initialization to let the world know Dino is here. + * This is the only routine which is NOT static. + * Must be called exactly once before pci_init(). + */ +static int __init dino_init(void) +{ + return register_parisc_driver(&dino_driver); +} +arch_initcall(dino_init); diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c new file mode 100644 index 000000000..b5b5413bf --- /dev/null +++ b/drivers/parisc/eisa.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * eisa.c - provide support for EISA adapters in PA-RISC machines + * + * Copyright (c) 2001 Matthew Wilcox for Hewlett Packard + * Copyright (c) 2001 Daniel Engstrom <5116@telia.com> + * + * There are two distinct EISA adapters. Mongoose is found in machines + * before the 712; then the Wax ASIC is used. To complicate matters, the + * Wax ASIC also includes a PS/2 and RS-232 controller, but those are + * dealt with elsewhere; this file is concerned only with the EISA portions + * of Wax. + * + * HINT: + * ----- + * To allow an ISA card to work properly in the EISA slot you need to + * set an edge trigger level. This may be done on the palo command line + * by adding the kernel parameter "eisa_irq_edge=n,n2,[...]]", with + * n and n2 as the irq levels you want to use. + * + * Example: "eisa_irq_edge=10,11" allows ISA cards to operate at + * irq levels 10 and 11. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iommu.h" + +#if 0 +#define EISA_DBG(msg, arg...) printk(KERN_DEBUG "eisa: " msg, ## arg) +#else +#define EISA_DBG(msg, arg...) +#endif + +#define SNAKES_EEPROM_BASE_ADDR 0xF0810400 +#define MIRAGE_EEPROM_BASE_ADDR 0xF00C0400 + +static DEFINE_SPINLOCK(eisa_irq_lock); + +void __iomem *eisa_eeprom_addr __read_mostly; + +/* We can only have one EISA adapter in the system because neither + * implementation can be flexed. + */ +static struct eisa_ba { + struct pci_hba_data hba; + unsigned long eeprom_addr; + struct eisa_root_device root; +} eisa_dev; + +/* Port ops */ + +static inline unsigned long eisa_permute(unsigned short port) +{ + if (port & 0x300) { + return 0xfc000000 | ((port & 0xfc00) >> 6) + | ((port & 0x3f8) << 9) | (port & 7); + } else { + return 0xfc000000 | port; + } +} + +unsigned char eisa_in8(unsigned short port) +{ + if (EISA_bus) + return gsc_readb(eisa_permute(port)); + return 0xff; +} + +unsigned short eisa_in16(unsigned short port) +{ + if (EISA_bus) + return le16_to_cpu(gsc_readw(eisa_permute(port))); + return 0xffff; +} + +unsigned int eisa_in32(unsigned short port) +{ + if (EISA_bus) + return le32_to_cpu(gsc_readl(eisa_permute(port))); + return 0xffffffff; +} + +void eisa_out8(unsigned char data, unsigned short port) +{ + if (EISA_bus) + gsc_writeb(data, eisa_permute(port)); +} + +void eisa_out16(unsigned short data, unsigned short port) +{ + if (EISA_bus) + gsc_writew(cpu_to_le16(data), eisa_permute(port)); +} + +void eisa_out32(unsigned int data, unsigned short port) +{ + if (EISA_bus) + gsc_writel(cpu_to_le32(data), eisa_permute(port)); +} + +#ifndef CONFIG_PCI +/* We call these directly without PCI. See asm/io.h. */ +EXPORT_SYMBOL(eisa_in8); +EXPORT_SYMBOL(eisa_in16); +EXPORT_SYMBOL(eisa_in32); +EXPORT_SYMBOL(eisa_out8); +EXPORT_SYMBOL(eisa_out16); +EXPORT_SYMBOL(eisa_out32); +#endif + +/* Interrupt handling */ + +/* cached interrupt mask registers */ +static int master_mask; +static int slave_mask; + +/* the trig level can be set with the + * eisa_irq_edge=n,n,n commandline parameter + * We should really read this from the EEPROM + * in the furure. + */ +/* irq 13,8,2,1,0 must be edge */ +static unsigned int eisa_irq_level __read_mostly; /* default to edge triggered */ + + +/* called by free irq */ +static void eisa_mask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; + unsigned long flags; + + EISA_DBG("disable irq %d\n", irq); + /* just mask for now */ + spin_lock_irqsave(&eisa_irq_lock, flags); + if (irq & 8) { + slave_mask |= (1 << (irq&7)); + eisa_out8(slave_mask, 0xa1); + } else { + master_mask |= (1 << (irq&7)); + eisa_out8(master_mask, 0x21); + } + spin_unlock_irqrestore(&eisa_irq_lock, flags); + EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21)); + EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1)); +} + +/* called by request irq */ +static void eisa_unmask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; + unsigned long flags; + EISA_DBG("enable irq %d\n", irq); + + spin_lock_irqsave(&eisa_irq_lock, flags); + if (irq & 8) { + slave_mask &= ~(1 << (irq&7)); + eisa_out8(slave_mask, 0xa1); + } else { + master_mask &= ~(1 << (irq&7)); + eisa_out8(master_mask, 0x21); + } + spin_unlock_irqrestore(&eisa_irq_lock, flags); + EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21)); + EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1)); +} + +static struct irq_chip eisa_interrupt_type = { + .name = "EISA", + .irq_unmask = eisa_unmask_irq, + .irq_mask = eisa_mask_irq, +}; + +static irqreturn_t eisa_irq(int wax_irq, void *intr_dev) +{ + int irq = gsc_readb(0xfc01f000); /* EISA supports 16 irqs */ + unsigned long flags; + + spin_lock_irqsave(&eisa_irq_lock, flags); + /* read IRR command */ + eisa_out8(0x0a, 0x20); + eisa_out8(0x0a, 0xa0); + + EISA_DBG("irq IAR %02x 8259-1 irr %02x 8259-2 irr %02x\n", + irq, eisa_in8(0x20), eisa_in8(0xa0)); + + /* read ISR command */ + eisa_out8(0x0a, 0x20); + eisa_out8(0x0a, 0xa0); + EISA_DBG("irq 8259-1 isr %02x imr %02x 8259-2 isr %02x imr %02x\n", + eisa_in8(0x20), eisa_in8(0x21), eisa_in8(0xa0), eisa_in8(0xa1)); + + irq &= 0xf; + + /* mask irq and write eoi */ + if (irq & 8) { + slave_mask |= (1 << (irq&7)); + eisa_out8(slave_mask, 0xa1); + eisa_out8(0x60 | (irq&7),0xa0);/* 'Specific EOI' to slave */ + eisa_out8(0x62, 0x20); /* 'Specific EOI' to master-IRQ2 */ + + } else { + master_mask |= (1 << (irq&7)); + eisa_out8(master_mask, 0x21); + eisa_out8(0x60|irq, 0x20); /* 'Specific EOI' to master */ + } + spin_unlock_irqrestore(&eisa_irq_lock, flags); + + generic_handle_irq(irq); + + spin_lock_irqsave(&eisa_irq_lock, flags); + /* unmask */ + if (irq & 8) { + slave_mask &= ~(1 << (irq&7)); + eisa_out8(slave_mask, 0xa1); + } else { + master_mask &= ~(1 << (irq&7)); + eisa_out8(master_mask, 0x21); + } + spin_unlock_irqrestore(&eisa_irq_lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t dummy_irq2_handler(int _, void *dev) +{ + printk(KERN_ALERT "eisa: uhh, irq2?\n"); + return IRQ_HANDLED; +} + +static void init_eisa_pic(void) +{ + unsigned long flags; + + spin_lock_irqsave(&eisa_irq_lock, flags); + + eisa_out8(0xff, 0x21); /* mask during init */ + eisa_out8(0xff, 0xa1); /* mask during init */ + + /* master pic */ + eisa_out8(0x11, 0x20); /* ICW1 */ + eisa_out8(0x00, 0x21); /* ICW2 */ + eisa_out8(0x04, 0x21); /* ICW3 */ + eisa_out8(0x01, 0x21); /* ICW4 */ + eisa_out8(0x40, 0x20); /* OCW2 */ + + /* slave pic */ + eisa_out8(0x11, 0xa0); /* ICW1 */ + eisa_out8(0x08, 0xa1); /* ICW2 */ + eisa_out8(0x02, 0xa1); /* ICW3 */ + eisa_out8(0x01, 0xa1); /* ICW4 */ + eisa_out8(0x40, 0xa0); /* OCW2 */ + + udelay(100); + + slave_mask = 0xff; + master_mask = 0xfb; + eisa_out8(slave_mask, 0xa1); /* OCW1 */ + eisa_out8(master_mask, 0x21); /* OCW1 */ + + /* setup trig level */ + EISA_DBG("EISA edge/level %04x\n", eisa_irq_level); + + eisa_out8(eisa_irq_level&0xff, 0x4d0); /* Set all irq's to edge */ + eisa_out8((eisa_irq_level >> 8) & 0xff, 0x4d1); + + EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21)); + EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1)); + EISA_DBG("pic0 edge/level %02x\n", eisa_in8(0x4d0)); + EISA_DBG("pic1 edge/level %02x\n", eisa_in8(0x4d1)); + + spin_unlock_irqrestore(&eisa_irq_lock, flags); +} + +/* Device initialisation */ + +#define is_mongoose(dev) (dev->id.sversion == 0x00076) + +static int __init eisa_probe(struct parisc_device *dev) +{ + int i, result; + + char *name = is_mongoose(dev) ? "Mongoose" : "Wax"; + + printk(KERN_INFO "%s EISA Adapter found at 0x%08lx\n", + name, (unsigned long)dev->hpa.start); + + eisa_dev.hba.dev = dev; + eisa_dev.hba.iommu = ccio_get_iommu(dev); + + eisa_dev.hba.lmmio_space.name = "EISA"; + eisa_dev.hba.lmmio_space.start = F_EXTEND(0xfc000000); + eisa_dev.hba.lmmio_space.end = F_EXTEND(0xffbfffff); + eisa_dev.hba.lmmio_space.flags = IORESOURCE_MEM; + result = ccio_request_resource(dev, &eisa_dev.hba.lmmio_space); + if (result < 0) { + printk(KERN_ERR "EISA: failed to claim EISA Bus address space!\n"); + return result; + } + eisa_dev.hba.io_space.name = "EISA"; + eisa_dev.hba.io_space.start = 0; + eisa_dev.hba.io_space.end = 0xffff; + eisa_dev.hba.lmmio_space.flags = IORESOURCE_IO; + result = request_resource(&ioport_resource, &eisa_dev.hba.io_space); + if (result < 0) { + printk(KERN_ERR "EISA: failed to claim EISA Bus port space!\n"); + return result; + } + pcibios_register_hba(&eisa_dev.hba); + + result = request_irq(dev->irq, eisa_irq, IRQF_SHARED, "EISA", &eisa_dev); + if (result) { + printk(KERN_ERR "EISA: request_irq failed!\n"); + goto error_release; + } + + /* Reserve IRQ2 */ + if (request_irq(2, dummy_irq2_handler, 0, "cascade", NULL)) + pr_err("Failed to request irq 2 (cascade)\n"); + for (i = 0; i < 16; i++) { + irq_set_chip_and_handler(i, &eisa_interrupt_type, + handle_simple_irq); + } + + EISA_bus = 1; + + if (dev->num_addrs) { + /* newer firmware hand out the eeprom address */ + eisa_dev.eeprom_addr = dev->addr[0]; + } else { + /* old firmware, need to figure out the box */ + if (is_mongoose(dev)) { + eisa_dev.eeprom_addr = SNAKES_EEPROM_BASE_ADDR; + } else { + eisa_dev.eeprom_addr = MIRAGE_EEPROM_BASE_ADDR; + } + } + eisa_eeprom_addr = ioremap(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH); + if (!eisa_eeprom_addr) { + result = -ENOMEM; + printk(KERN_ERR "EISA: ioremap failed!\n"); + goto error_free_irq; + } + result = eisa_enumerator(eisa_dev.eeprom_addr, &eisa_dev.hba.io_space, + &eisa_dev.hba.lmmio_space); + init_eisa_pic(); + + if (result >= 0) { + /* FIXME : Don't enumerate the bus twice. */ + eisa_dev.root.dev = &dev->dev; + dev_set_drvdata(&dev->dev, &eisa_dev.root); + eisa_dev.root.bus_base_addr = 0; + eisa_dev.root.res = &eisa_dev.hba.io_space; + eisa_dev.root.slots = result; + eisa_dev.root.dma_mask = 0xffffffff; /* wild guess */ + if (eisa_root_register (&eisa_dev.root)) { + printk(KERN_ERR "EISA: Failed to register EISA root\n"); + result = -ENOMEM; + goto error_iounmap; + } + } + + return 0; + +error_iounmap: + iounmap(eisa_eeprom_addr); +error_free_irq: + free_irq(dev->irq, &eisa_dev); +error_release: + release_resource(&eisa_dev.hba.io_space); + return result; +} + +static const struct parisc_device_id eisa_tbl[] __initconst = { + { HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00076 }, /* Mongoose */ + { HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00090 }, /* Wax EISA */ + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, eisa_tbl); + +static struct parisc_driver eisa_driver __refdata = { + .name = "eisa_ba", + .id_table = eisa_tbl, + .probe = eisa_probe, +}; + +static int __init parisc_eisa_init(void) +{ + return register_parisc_driver(&eisa_driver); +} +arch_initcall(parisc_eisa_init); + + +static unsigned int eisa_irq_configured; +void eisa_make_irq_level(int num) +{ + if (eisa_irq_configured& (1< 15 || val < 0) { + printk(KERN_ERR "eisa: EISA irq value are 0-15\n"); + cur = strchr(cur, ','); + if (cur) + cur++; + else + break; + continue; + } + if (val == 2) { + val = 9; + } + eisa_make_irq_edge(val); /* clear the corresponding bit */ + EISA_DBG("setting IRQ %d to edge-triggered mode\n", val); + + if ((cur = strchr(cur, ','))) { + cur++; + } else { + break; + } + } + return 1; +} + +__setup("eisa_irq_edge=", eisa_irq_setup); + diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c new file mode 100644 index 000000000..2058b4c5b --- /dev/null +++ b/drivers/parisc/eisa_eeprom.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EISA "eeprom" support routines + * + * Copyright (C) 2001 Thomas Bogendoerfer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static loff_t eisa_eeprom_llseek(struct file *file, loff_t offset, int origin) +{ + return fixed_size_llseek(file, offset, origin, HPEE_MAX_LENGTH); +} + +static ssize_t eisa_eeprom_read(struct file * file, + char __user *buf, size_t count, loff_t *ppos ) +{ + unsigned char *tmp; + ssize_t ret; + int i; + + if (*ppos < 0 || *ppos >= HPEE_MAX_LENGTH) + return 0; + + count = *ppos + count < HPEE_MAX_LENGTH ? count : HPEE_MAX_LENGTH - *ppos; + tmp = kmalloc(count, GFP_KERNEL); + if (tmp) { + for (i = 0; i < count; i++) + tmp[i] = readb(eisa_eeprom_addr+(*ppos)++); + + if (copy_to_user (buf, tmp, count)) + ret = -EFAULT; + else + ret = count; + kfree (tmp); + } else + ret = -ENOMEM; + + return ret; +} + +static int eisa_eeprom_open(struct inode *inode, struct file *file) +{ + if (file->f_mode & FMODE_WRITE) + return -EINVAL; + + return 0; +} + +static int eisa_eeprom_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The various file operations we support. + */ +static const struct file_operations eisa_eeprom_fops = { + .owner = THIS_MODULE, + .llseek = eisa_eeprom_llseek, + .read = eisa_eeprom_read, + .open = eisa_eeprom_open, + .release = eisa_eeprom_release, +}; + +static struct miscdevice eisa_eeprom_dev = { + EISA_EEPROM_MINOR, + "eisa_eeprom", + &eisa_eeprom_fops +}; + +static int __init eisa_eeprom_init(void) +{ + int retval; + + if (!eisa_eeprom_addr) + return -ENODEV; + + retval = misc_register(&eisa_eeprom_dev); + if (retval < 0) { + printk(KERN_ERR "EISA EEPROM: cannot register misc device.\n"); + return retval; + } + + printk(KERN_INFO "EISA EEPROM at 0x%px\n", eisa_eeprom_addr); + return 0; +} + +MODULE_DESCRIPTION("EISA eeprom driver"); +MODULE_LICENSE("GPL"); + +module_init(eisa_eeprom_init); diff --git a/drivers/parisc/eisa_enumerator.c b/drivers/parisc/eisa_enumerator.c new file mode 100644 index 000000000..e0a57086a --- /dev/null +++ b/drivers/parisc/eisa_enumerator.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * eisa_enumerator.c - provide support for EISA adapters in PA-RISC machines + * + * Copyright (c) 2002 Daniel Engstrom <5116@telia.com> + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* + * Todo: + * + * PORT init with MASK attr and other size than byte + * MEMORY with other decode than 20 bit + * CRC stuff + * FREEFORM stuff + */ + +#define EPI 0xc80 +#define NUM_SLOT 16 +#define SLOT2PORT(x) (x<<12) + + +/* macros to handle unaligned accesses and + * byte swapping. The data in the EEPROM is + * little-endian on the big-endian PAROSC */ +#define get_8(x) (*(u_int8_t*)(x)) + +static inline u_int16_t get_16(const unsigned char *x) +{ + return (x[1] << 8) | x[0]; +} + +static inline u_int32_t get_32(const unsigned char *x) +{ + return (x[3] << 24) | (x[2] << 16) | (x[1] << 8) | x[0]; +} + +static inline u_int32_t get_24(const unsigned char *x) +{ + return (x[2] << 24) | (x[1] << 16) | (x[0] << 8); +} + +static void print_eisa_id(char *s, u_int32_t id) +{ + char vendor[4]; + int rev; + int device; + + rev = id & 0xff; + id >>= 8; + device = id & 0xff; + id >>= 8; + vendor[3] = '\0'; + vendor[2] = '@' + (id & 0x1f); + id >>= 5; + vendor[1] = '@' + (id & 0x1f); + id >>= 5; + vendor[0] = '@' + (id & 0x1f); + id >>= 5; + + sprintf(s, "%s%02X%02X", vendor, device, rev); +} + +static int configure_memory(const unsigned char *buf, + struct resource *mem_parent, + char *name) +{ + int len; + u_int8_t c; + int i; + struct resource *res; + + len=0; + + for (i=0;iname = name; + res->start = mem_parent->start + get_24(buf+len+2); + res->end = res->start + get_16(buf+len+5)*1024; + res->flags = IORESOURCE_MEM; + pr_cont("memory %pR ", res); + result = request_resource(mem_parent, res); + if (result < 0) { + printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n"); + return result; + } + } + + len+=7; + + if (!(c & HPEE_MEMORY_MORE)) { + break; + } + } + + return len; +} + + +static int configure_irq(const unsigned char *buf) +{ + int len; + u_int8_t c; + int i; + + len=0; + + for (i=0;iname = board; + res->start = get_16(buf+len+1); + res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1; + res->flags = IORESOURCE_IO; + pr_cont("ioports %pR ", res); + result = request_resource(io_parent, res); + if (result < 0) { + printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n"); + return result; + } + } + + len+=3; + if (!(c & HPEE_PORT_MORE)) { + break; + } + } + + return len; +} + + +/* byte 1 and 2 is the port number to write + * and at byte 3 the value to write starts. + * I assume that there are and- and or- masks + * here when HPEE_PORT_INIT_MASK is set but I have + * not yet encountered this. */ +static int configure_port_init(const unsigned char *buf) +{ + int len=0; + u_int8_t c; + + while (len 80) { + printk(KERN_ERR "eisa_enumerator: type info field too long (%d, max is 80)\n", len); + } + + return 1+len; +} + +static int configure_function(const unsigned char *buf, int *more) +{ + /* the init field seems to be a two-byte field + * which is non-zero if there are an other function following + * I think it is the length of the function def + */ + *more = get_16(buf); + + return 2; +} + +static int parse_slot_config(int slot, + const unsigned char *buf, + struct eeprom_eisa_slot_info *es, + struct resource *io_parent, + struct resource *mem_parent) +{ + int res=0; + int function_len; + unsigned int pos=0; + unsigned int maxlen; + int num_func=0; + u_int8_t flags; + int p0; + + char *board; + int id_string_used=0; + + if (NULL == (board = kmalloc(8, GFP_KERNEL))) { + return -1; + } + print_eisa_id(board, es->eisa_slot_id); + printk(KERN_INFO "EISA slot %d: %s %s ", + slot, board, es->flags&HPEE_FLAG_BOARD_IS_ISA ? "ISA" : "EISA"); + + maxlen = es->config_data_length < HPEE_MAX_LENGTH ? + es->config_data_length : HPEE_MAX_LENGTH; + while ((pos < maxlen) && (num_func <= es->num_functions)) { + pos+=configure_function(buf+pos, &function_len); + + if (!function_len) { + break; + } + num_func++; + p0 = pos; + pos += configure_choise(buf+pos, &flags); + + if (flags & HPEE_FUNCTION_INFO_F_DISABLED) { + /* function disabled, skip silently */ + pos = p0 + function_len; + continue; + } + if (flags & HPEE_FUNCTION_INFO_CFG_FREE_FORM) { + /* I have no idea how to handle this */ + printk("function %d have free-form configuration, skipping ", + num_func); + pos = p0 + function_len; + continue; + } + + /* the ordering of the sections need + * more investigation. + * Currently I think that memory comaed before IRQ + * I assume the order is LSB to MSB in the + * info flags + * eg type, memory, irq, dma, port, HPEE_PORT_init + */ + + if (flags & HPEE_FUNCTION_INFO_HAVE_TYPE) { + pos += configure_type_string(buf+pos); + } + + if (flags & HPEE_FUNCTION_INFO_HAVE_MEMORY) { + id_string_used=1; + pos += configure_memory(buf+pos, mem_parent, board); + } + + if (flags & HPEE_FUNCTION_INFO_HAVE_IRQ) { + pos += configure_irq(buf+pos); + } + + if (flags & HPEE_FUNCTION_INFO_HAVE_DMA) { + pos += configure_dma(buf+pos); + } + + if (flags & HPEE_FUNCTION_INFO_HAVE_PORT) { + id_string_used=1; + pos += configure_port(buf+pos, io_parent, board); + } + + if (flags & HPEE_FUNCTION_INFO_HAVE_PORT_INIT) { + pos += configure_port_init(buf+pos); + } + + if (p0 + function_len < pos) { + printk(KERN_ERR "eisa_enumerator: function %d length mismatch " + "got %d, expected %d\n", + num_func, pos-p0, function_len); + res=-1; + break; + } + pos = p0 + function_len; + } + pr_cont("\n"); + if (!id_string_used) { + kfree(board); + } + + if (pos != es->config_data_length) { + printk(KERN_ERR "eisa_enumerator: config data length mismatch got %d, expected %d\n", + pos, es->config_data_length); + res=-1; + } + + if (num_func != es->num_functions) { + printk(KERN_ERR "eisa_enumerator: number of functions mismatch got %d, expected %d\n", + num_func, es->num_functions); + res=-2; + } + + return res; + +} + +static int init_slot(int slot, struct eeprom_eisa_slot_info *es) +{ + unsigned int id; + + char id_string[8]; + + if (!(es->slot_info&HPEE_SLOT_INFO_NO_READID)) { + /* try to read the id of the board in the slot */ + id = le32_to_cpu(inl(SLOT2PORT(slot)+EPI)); + + if (0xffffffff == id) { + /* Maybe we didn't expect a card to be here... */ + if (es->eisa_slot_id == 0xffffffff) + return -1; + + /* this board is not here or it does not + * support readid + */ + printk(KERN_ERR "EISA slot %d a configured board was not detected (", + slot); + + print_eisa_id(id_string, es->eisa_slot_id); + printk(" expected %s)\n", id_string); + + return -1; + + } + if (es->eisa_slot_id != id) { + print_eisa_id(id_string, id); + printk(KERN_ERR "EISA slot %d id mismatch: got %s", + slot, id_string); + + print_eisa_id(id_string, es->eisa_slot_id); + printk(" expected %s\n", id_string); + + return -1; + + } + } + + /* now: we need to enable the board if + * it supports enabling and run through + * the port init sction if present + * and finally record any interrupt polarity + */ + if (es->slot_features & HPEE_SLOT_FEATURES_ENABLE) { + /* enable board */ + outb(0x01| inb(SLOT2PORT(slot)+EPI+4), + SLOT2PORT(slot)+EPI+4); + } + + return 0; +} + + +int eisa_enumerator(unsigned long eeprom_addr, + struct resource *io_parent, struct resource *mem_parent) +{ + int i; + struct eeprom_header *eh; + static char eeprom_buf[HPEE_MAX_LENGTH]; + + for (i=0; i < HPEE_MAX_LENGTH; i++) { + eeprom_buf[i] = gsc_readb(eeprom_addr+i); + } + + printk(KERN_INFO "Enumerating EISA bus\n"); + + eh = (struct eeprom_header*)(eeprom_buf); + for (i=0;inum_slots;i++) { + struct eeprom_eisa_slot_info *es; + + es = (struct eeprom_eisa_slot_info*) + (&eeprom_buf[HPEE_SLOT_INFO(i)]); + + if (-1==init_slot(i+1, es)) { + continue; + } + + if (es->config_data_offset < HPEE_MAX_LENGTH) { + if (parse_slot_config(i+1, &eeprom_buf[es->config_data_offset], + es, io_parent, mem_parent)) { + return -1; + } + } else { + printk (KERN_WARNING "EISA EEPROM offset 0x%x out of range\n",es->config_data_offset); + return -1; + } + } + return eh->num_slots; +} + diff --git a/drivers/parisc/gsc.c b/drivers/parisc/gsc.c new file mode 100644 index 000000000..8ba778170 --- /dev/null +++ b/drivers/parisc/gsc.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Interrupt management for most GSC and related devices. + * + * (c) Copyright 1999 Alex deVries for The Puffin Group + * (c) Copyright 1999 Grant Grundler for Hewlett-Packard + * (c) Copyright 1999 Matthew Wilcox + * (c) Copyright 2000 Helge Deller + * (c) Copyright 2001 Matthew Wilcox for Hewlett-Packard + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gsc.h" + +#undef DEBUG + +#ifdef DEBUG +#define DEBPRINTK printk +#else +#define DEBPRINTK(x,...) +#endif + +int gsc_alloc_irq(struct gsc_irq *i) +{ + int irq = txn_alloc_irq(GSC_EIM_WIDTH); + if (irq < 0) { + printk("cannot get irq\n"); + return irq; + } + + i->txn_addr = txn_alloc_addr(irq); + i->txn_data = txn_alloc_data(irq); + i->irq = irq; + + return irq; +} + +int gsc_claim_irq(struct gsc_irq *i, int irq) +{ + int c = irq; + + irq += CPU_IRQ_BASE; /* virtualize the IRQ first */ + + irq = txn_claim_irq(irq); + if (irq < 0) { + printk("cannot claim irq %d\n", c); + return irq; + } + + i->txn_addr = txn_alloc_addr(irq); + i->txn_data = txn_alloc_data(irq); + i->irq = irq; + + return irq; +} + +EXPORT_SYMBOL(gsc_alloc_irq); +EXPORT_SYMBOL(gsc_claim_irq); + +/* Common interrupt demultiplexer used by Asp, Lasi & Wax. */ +irqreturn_t gsc_asic_intr(int gsc_asic_irq, void *dev) +{ + unsigned long irr; + struct gsc_asic *gsc_asic = dev; + + irr = gsc_readl(gsc_asic->hpa + OFFSET_IRR); + if (irr == 0) + return IRQ_NONE; + + DEBPRINTK("%s intr, mask=0x%x\n", gsc_asic->name, irr); + + do { + int local_irq = __ffs(irr); + unsigned int irq = gsc_asic->global_irq[local_irq]; + generic_handle_irq(irq); + irr &= ~(1 << local_irq); + } while (irr); + + return IRQ_HANDLED; +} + +int gsc_find_local_irq(unsigned int irq, int *global_irqs, int limit) +{ + int local_irq; + + for (local_irq = 0; local_irq < limit; local_irq++) { + if (global_irqs[local_irq] == irq) + return local_irq; + } + + return NO_IRQ; +} + +static void gsc_asic_mask_irq(struct irq_data *d) +{ + struct gsc_asic *irq_dev = irq_data_get_irq_chip_data(d); + int local_irq = gsc_find_local_irq(d->irq, irq_dev->global_irq, 32); + u32 imr; + + DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __func__, d->irq, + irq_dev->name, imr); + + /* Disable the IRQ line by clearing the bit in the IMR */ + imr = gsc_readl(irq_dev->hpa + OFFSET_IMR); + imr &= ~(1 << local_irq); + gsc_writel(imr, irq_dev->hpa + OFFSET_IMR); +} + +static void gsc_asic_unmask_irq(struct irq_data *d) +{ + struct gsc_asic *irq_dev = irq_data_get_irq_chip_data(d); + int local_irq = gsc_find_local_irq(d->irq, irq_dev->global_irq, 32); + u32 imr; + + DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __func__, d->irq, + irq_dev->name, imr); + + /* Enable the IRQ line by setting the bit in the IMR */ + imr = gsc_readl(irq_dev->hpa + OFFSET_IMR); + imr |= 1 << local_irq; + gsc_writel(imr, irq_dev->hpa + OFFSET_IMR); + /* + * FIXME: read IPR to make sure the IRQ isn't already pending. + * If so, we need to read IRR and manually call do_irq(). + */ +} + +#ifdef CONFIG_SMP +static int gsc_set_affinity_irq(struct irq_data *d, const struct cpumask *dest, + bool force) +{ + struct gsc_asic *gsc_dev = irq_data_get_irq_chip_data(d); + struct cpumask tmask; + int cpu_irq; + + if (!cpumask_and(&tmask, dest, cpu_online_mask)) + return -EINVAL; + + cpu_irq = cpu_check_affinity(d, &tmask); + if (cpu_irq < 0) + return cpu_irq; + + gsc_dev->gsc_irq.txn_addr = txn_affinity_addr(d->irq, cpu_irq); + gsc_dev->eim = ((u32) gsc_dev->gsc_irq.txn_addr) | gsc_dev->gsc_irq.txn_data; + + /* switch IRQ's for devices below LASI/WAX to other CPU */ + /* ASP chip (svers 0x70) does not support reprogramming */ + if (gsc_dev->gsc->id.sversion != 0x70) + gsc_writel(gsc_dev->eim, gsc_dev->hpa + OFFSET_IAR); + + irq_data_update_effective_affinity(d, &tmask); + + return IRQ_SET_MASK_OK; +} +#endif + + +static struct irq_chip gsc_asic_interrupt_type = { + .name = "GSC-ASIC", + .irq_unmask = gsc_asic_unmask_irq, + .irq_mask = gsc_asic_mask_irq, +#ifdef CONFIG_SMP + .irq_set_affinity = gsc_set_affinity_irq, +#endif +}; + +int gsc_assign_irq(struct irq_chip *type, void *data) +{ + static int irq = GSC_IRQ_BASE; + + if (irq > GSC_IRQ_MAX) + return NO_IRQ; + + irq_set_chip_and_handler(irq, type, handle_simple_irq); + irq_set_chip_data(irq, data); + + return irq++; +} + +void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp) +{ + int irq = asic->global_irq[local_irq]; + + if (irq <= 0) { + irq = gsc_assign_irq(&gsc_asic_interrupt_type, asic); + if (irq == NO_IRQ) + return; + + asic->global_irq[local_irq] = irq; + } + *irqp = irq; +} + +struct gsc_fixup_struct { + void (*choose_irq)(struct parisc_device *, void *); + void *ctrl; +}; + +static int gsc_fixup_irqs_callback(struct device *dev, void *data) +{ + struct parisc_device *padev = to_parisc_device(dev); + struct gsc_fixup_struct *gf = data; + + /* work-around for 715/64 and others which have parent + at path [5] and children at path [5/0/x] */ + if (padev->id.hw_type == HPHW_FAULTY) + gsc_fixup_irqs(padev, gf->ctrl, gf->choose_irq); + gf->choose_irq(padev, gf->ctrl); + + return 0; +} + +void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl, + void (*choose_irq)(struct parisc_device *, void *)) +{ + struct gsc_fixup_struct data = { + .choose_irq = choose_irq, + .ctrl = ctrl, + }; + + device_for_each_child(&parent->dev, &data, gsc_fixup_irqs_callback); +} + +int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic) +{ + struct resource *res; + int i; + + gsc_asic->gsc = parent; + + /* Initialise local irq -> global irq mapping */ + for (i = 0; i < 32; i++) { + gsc_asic->global_irq[i] = NO_IRQ; + } + + /* allocate resource region */ + res = request_mem_region(gsc_asic->hpa, 0x100000, gsc_asic->name); + if (res) { + res->flags = IORESOURCE_MEM; /* do not mark it busy ! */ + } + +#if 0 + printk(KERN_WARNING "%s IRQ %d EIM 0x%x", gsc_asic->name, + parent->irq, gsc_asic->eim); + if (gsc_readl(gsc_asic->hpa + OFFSET_IMR)) + printk(" IMR is non-zero! (0x%x)", + gsc_readl(gsc_asic->hpa + OFFSET_IMR)); + printk("\n"); +#endif + + return 0; +} diff --git a/drivers/parisc/gsc.h b/drivers/parisc/gsc.h new file mode 100644 index 000000000..73cbd0bb1 --- /dev/null +++ b/drivers/parisc/gsc.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drivers/parisc/gsc.h + * Declarations for functions in gsc.c + * Copyright (c) 2000-2002 Helge Deller, Matthew Wilcox + */ + +#include +#include +#include + +#define OFFSET_IRR 0x0000 /* Interrupt request register */ +#define OFFSET_IMR 0x0004 /* Interrupt mask register */ +#define OFFSET_IPR 0x0008 /* Interrupt pending register */ +#define OFFSET_ICR 0x000C /* Interrupt control register */ +#define OFFSET_IAR 0x0010 /* Interrupt address register */ + +/* PA I/O Architected devices support at least 5 bits in the EIM register. */ +#define GSC_EIM_WIDTH 5 + +struct gsc_irq { + unsigned long txn_addr; /* IRQ "target" */ + int txn_data; /* HW "IRQ" */ + int irq; /* virtual IRQ */ +}; + +struct gsc_asic { + struct parisc_device *gsc; + unsigned long hpa; + char *name; + int version; + int type; + int eim; + struct gsc_irq gsc_irq; + int global_irq[32]; +}; + +int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic); +int gsc_alloc_irq(struct gsc_irq *dev); /* dev needs an irq */ +int gsc_claim_irq(struct gsc_irq *dev, int irq); /* dev needs this irq */ +int gsc_assign_irq(struct irq_chip *type, void *data); +int gsc_find_local_irq(unsigned int irq, int *global_irq, int limit); +void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl, + void (*choose)(struct parisc_device *child, void *ctrl)); +void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp); + +irqreturn_t gsc_asic_intr(int irq, void *dev); diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c new file mode 100644 index 000000000..33709eafe --- /dev/null +++ b/drivers/parisc/hppb.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** hppb.c: +** HP-PB bus driver for the NOVA and K-Class systems. +** +** (c) Copyright 2002 Ryan Bradetich +** (c) Copyright 2002 Hewlett-Packard Company +** +** +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iommu.h" + +struct hppb_card { + unsigned long hpa; + struct resource mmio_region; + struct hppb_card *next; +}; + +static struct hppb_card hppb_card_head = { + .hpa = 0, + .next = NULL, +}; + +#define IO_IO_LOW offsetof(struct bc_module, io_io_low) +#define IO_IO_HIGH offsetof(struct bc_module, io_io_high) + +/** + * hppb_probe - Determine if the hppb driver should claim this device. + * @dev: The device which has been found + * + * Determine if hppb driver should claim this chip (return 0) or not + * (return 1). If so, initialize the chip and tell other partners in crime + * they have work to do. + */ +static int __init hppb_probe(struct parisc_device *dev) +{ + int status; + struct hppb_card *card = &hppb_card_head; + + while(card->next) { + card = card->next; + } + + if(card->hpa) { + card->next = kzalloc_obj(struct hppb_card); + if(!card->next) { + printk(KERN_ERR "HP-PB: Unable to allocate memory.\n"); + return 1; + } + card = card->next; + } + + card->hpa = dev->hpa.start; + card->mmio_region.name = "HP-PB Bus"; + card->mmio_region.flags = IORESOURCE_MEM; + + card->mmio_region.start = gsc_readl(dev->hpa.start + IO_IO_LOW); + card->mmio_region.end = gsc_readl(dev->hpa.start + IO_IO_HIGH) - 1; + + status = ccio_request_resource(dev, &card->mmio_region); + + pr_info("Found GeckoBoa at %pap, bus space %pR,%s claimed.\n", + &dev->hpa.start, + &card->mmio_region, + (status < 0) ? " not":"" ); + + return 0; +} + +static const struct parisc_device_id hppb_tbl[] __initconst = { + { HPHW_BCPORT, HVERSION_REV_ANY_ID, 0x500, 0xc }, /* E25 and K */ + { HPHW_BCPORT, 0x0, 0x501, 0xc }, /* E35 */ + { HPHW_BCPORT, 0x0, 0x502, 0xc }, /* E45 */ + { HPHW_BCPORT, 0x0, 0x503, 0xc }, /* E55 */ + { 0, } +}; + +static struct parisc_driver hppb_driver __refdata = { + .name = "gecko_boa", + .id_table = hppb_tbl, + .probe = hppb_probe, +}; + +/** + * hppb_init - HP-PB bus initialization procedure. + * + * Register this driver. + */ +static int __init hppb_init(void) +{ + return register_parisc_driver(&hppb_driver); +} +arch_initcall(hppb_init); diff --git a/drivers/parisc/iommu-helpers.h b/drivers/parisc/iommu-helpers.h new file mode 100644 index 000000000..0691884f5 --- /dev/null +++ b/drivers/parisc/iommu-helpers.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include + +/** + * iommu_fill_pdir - Insert coalesced scatter/gather chunks into the I/O Pdir. + * @ioc: The I/O Controller. + * @startsg: The scatter/gather list of coalesced chunks. + * @nents: The number of entries in the scatter/gather list. + * @hint: The DMA Hint. + * + * This function inserts the coalesced scatter/gather list chunks into the + * I/O Controller's I/O Pdir. + */ +static inline unsigned int +iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, + unsigned long hint, + void (*iommu_io_pdir_entry)(__le64 *, space_t, phys_addr_t, + unsigned long)) +{ + struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ + unsigned int n_mappings = 0; + unsigned long dma_offset = 0, dma_len = 0; + __le64 *pdirp = NULL; + + /* Horrible hack. For efficiency's sake, dma_sg starts one + * entry below the true start (it is immediately incremented + * in the loop) */ + dma_sg--; + + while (nents-- > 0) { + phys_addr_t paddr; + long size; + + DBG_RUN_SG(" %d : %08lx %p/%05x\n", nents, + (unsigned long)sg_dma_address(startsg), + sg_virt(startsg), startsg->length + ); + + + /* + ** Look for the start of a new DMA stream + */ + + if (sg_dma_address(startsg) & PIDE_FLAG) { + u32 pide = sg_dma_address(startsg) & ~PIDE_FLAG; + + BUG_ON(pdirp && (dma_len != sg_dma_len(dma_sg))); + + dma_sg++; + + dma_len = sg_dma_len(startsg); + sg_dma_len(startsg) = 0; + dma_offset = (unsigned long) pide & ~IOVP_MASK; + n_mappings++; +#if defined(ZX1_SUPPORT) + /* Pluto IOMMU IO Virt Address is not zero based */ + sg_dma_address(dma_sg) = pide | ioc->ibase; +#else + /* SBA, ccio, and dino are zero based. + * Trying to save a few CPU cycles for most users. + */ + sg_dma_address(dma_sg) = pide; +#endif + pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]); + prefetchw(pdirp); + } + + BUG_ON(pdirp == NULL); + + paddr = sg_phys(startsg); + sg_dma_len(dma_sg) += startsg->length; + size = startsg->length + dma_offset; + dma_offset = 0; +#ifdef IOMMU_MAP_STATS + ioc->msg_pages += startsg->length >> IOVP_SHIFT; +#endif + do { + iommu_io_pdir_entry(pdirp, KERNEL_SPACE, + paddr, hint); + paddr += IOVP_SIZE; + size -= IOVP_SIZE; + pdirp++; + } while(unlikely(size > 0)); + startsg++; + } + return(n_mappings); +} + + +/* +** First pass is to walk the SG list and determine where the breaks are +** in the DMA stream. Allocates PDIR entries but does not fill them. +** Returns the number of DMA chunks. +** +** Doing the fill separate from the coalescing/allocation keeps the +** code simpler. Future enhancement could make one pass through +** the sglist do both. +*/ + +static inline unsigned int +iommu_coalesce_chunks(struct ioc *ioc, struct device *dev, + struct scatterlist *startsg, int nents, + int (*iommu_alloc_range)(struct ioc *, struct device *, size_t)) +{ + struct scatterlist *contig_sg; /* contig chunk head */ + unsigned long dma_offset, dma_len; /* start/len of DMA stream */ + unsigned int n_mappings = 0; + unsigned int max_seg_size = min(dma_get_max_seg_size(dev), + (unsigned)DMA_CHUNK_SIZE); + unsigned int max_seg_boundary = dma_get_seg_boundary(dev) + 1; + if (max_seg_boundary) /* check if the addition above didn't overflow */ + max_seg_size = min(max_seg_size, max_seg_boundary); + + while (nents > 0) { + + /* + ** Prepare for first/next DMA stream + */ + contig_sg = startsg; + dma_len = startsg->length; + dma_offset = startsg->offset; + + /* PARANOID: clear entries */ + sg_dma_address(startsg) = 0; + sg_dma_len(startsg) = 0; + + /* + ** This loop terminates one iteration "early" since + ** it's always looking one "ahead". + */ + while(--nents > 0) { + unsigned long prev_end, sg_start; + + prev_end = (unsigned long)sg_virt(startsg) + + startsg->length; + + startsg++; + sg_start = (unsigned long)sg_virt(startsg); + + /* PARANOID: clear entries */ + sg_dma_address(startsg) = 0; + sg_dma_len(startsg) = 0; + + /* + ** First make sure current dma stream won't + ** exceed max_seg_size if we coalesce the + ** next entry. + */ + if (unlikely(ALIGN(dma_len + dma_offset + startsg->length, IOVP_SIZE) > + max_seg_size)) + break; + + /* + * Next see if we can append the next chunk (i.e. + * it must end on one page and begin on another, or + * it must start on the same address as the previous + * entry ended. + */ + if (unlikely((prev_end != sg_start) || + ((prev_end | sg_start) & ~PAGE_MASK))) + break; + + dma_len += startsg->length; + } + + /* + ** End of DMA Stream + ** Terminate last VCONTIG block. + ** Allocate space for DMA stream. + */ + sg_dma_len(contig_sg) = dma_len; + dma_len = ALIGN(dma_len + dma_offset, IOVP_SIZE); + sg_dma_address(contig_sg) = + PIDE_FLAG + | (iommu_alloc_range(ioc, dev, dma_len) << IOVP_SHIFT) + | dma_offset; + n_mappings++; + } + + return n_mappings; +} + diff --git a/drivers/parisc/iommu.h b/drivers/parisc/iommu.h new file mode 100644 index 000000000..240059cd8 --- /dev/null +++ b/drivers/parisc/iommu.h @@ -0,0 +1,55 @@ +#ifndef _IOMMU_H +#define _IOMMU_H 1 + +#include + +struct parisc_device; +struct ioc; + +static inline struct pci_hba_data *parisc_walk_tree(struct device *dev) +{ + struct device *otherdev; + + if (likely(dev->platform_data)) + return dev->platform_data; + + /* OK, just traverse the bus to find it */ + for (otherdev = dev->parent; + otherdev; + otherdev = otherdev->parent) { + if (otherdev->platform_data) { + dev->platform_data = otherdev->platform_data; + break; + } + } + + return dev->platform_data; +} + +static inline struct ioc *GET_IOC(struct device *dev) +{ + struct pci_hba_data *pdata = parisc_walk_tree(dev); + + if (!pdata) + return NULL; + return pdata->iommu; +} + +#ifdef CONFIG_IOMMU_CCIO +void *ccio_get_iommu(const struct parisc_device *dev); +int ccio_request_resource(const struct parisc_device *dev, + struct resource *res); +int ccio_allocate_resource(const struct parisc_device *dev, + struct resource *res, unsigned long size, + unsigned long min, unsigned long max, unsigned long align); +#else /* !CONFIG_IOMMU_CCIO */ +#define ccio_get_iommu(dev) NULL +#define ccio_request_resource(dev, res) insert_resource(&iomem_resource, res) +#define ccio_allocate_resource(dev, res, size, min, max, align) \ + allocate_resource(&iomem_resource, res, size, min, max, \ + align, NULL, NULL) +#endif /* !CONFIG_IOMMU_CCIO */ + +void *sba_get_iommu(struct parisc_device *dev); + +#endif /* _IOMMU_H */ diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c new file mode 100644 index 000000000..035c996e3 --- /dev/null +++ b/drivers/parisc/iosapic.c @@ -0,0 +1,986 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** I/O Sapic Driver - PCI interrupt line support +** +** (c) Copyright 1999 Grant Grundler +** (c) Copyright 1999 Hewlett-Packard Company +** +** +** The I/O sapic driver manages the Interrupt Redirection Table which is +** the control logic to convert PCI line based interrupts into a Message +** Signaled Interrupt (aka Transaction Based Interrupt, TBI). +** +** Acronyms +** -------- +** HPA Hard Physical Address (aka MMIO address) +** IRQ Interrupt ReQuest. Implies Line based interrupt. +** IRT Interrupt Routing Table (provided by PAT firmware) +** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA +** table which is implemented in I/O SAPIC. +** ISR Interrupt Service Routine. aka Interrupt handler. +** MSI Message Signaled Interrupt. PCI 2.2 functionality. +** aka Transaction Based Interrupt (or TBI). +** PA Precision Architecture. HP's RISC architecture. +** RISC Reduced Instruction Set Computer. +** +** +** What's a Message Signalled Interrupt? +** ------------------------------------- +** MSI is a write transaction which targets a processor and is similar +** to a processor write to memory or MMIO. MSIs can be generated by I/O +** devices as well as processors and require *architecture* to work. +** +** PA only supports MSI. So I/O subsystems must either natively generate +** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs +** (e.g. PCI and EISA). IA64 supports MSIs via a "local SAPIC" which +** acts on behalf of a processor. +** +** MSI allows any I/O device to interrupt any processor. This makes +** load balancing of the interrupt processing possible on an SMP platform. +** Interrupts are also ordered WRT to DMA data. It's possible on I/O +** coherent systems to completely eliminate PIO reads from the interrupt +** path. The device and driver must be designed and implemented to +** guarantee all DMA has been issued (issues about atomicity here) +** before the MSI is issued. I/O status can then safely be read from +** DMA'd data by the ISR. +** +** +** PA Firmware +** ----------- +** PA-RISC platforms have two fundamentally different types of firmware. +** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register +** and BARs similar to a traditional PC BIOS. +** The newer "PAT" firmware supports PDC calls which return tables. +** PAT firmware only initializes the PCI Console and Boot interface. +** With these tables, the OS can program all other PCI devices. +** +** One such PAT PDC call returns the "Interrupt Routing Table" (IRT). +** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC +** input line. If the IRT is not available, this driver assumes +** INTERRUPT_LINE register has been programmed by firmware. The latter +** case also means online addition of PCI cards can NOT be supported +** even if HW support is present. +** +** All platforms with PAT firmware to date (Oct 1999) use one Interrupt +** Routing Table for the entire platform. +** +** Where's the iosapic? +** -------------------- +** I/O sapic is part of the "Core Electronics Complex". And on HP platforms +** it's integrated as part of the PCI bus adapter, "lba". So no bus walk +** will discover I/O Sapic. I/O Sapic driver learns about each device +** when lba driver advertises the presence of the I/O sapic by calling +** iosapic_register(). +** +** +** IRQ handling notes +** ------------------ +** The IO-SAPIC can indicate to the CPU which interrupt was asserted. +** So, unlike the GSC-ASIC and Dino, we allocate one CPU interrupt per +** IO-SAPIC interrupt and call the device driver's handler directly. +** The IO-SAPIC driver hijacks the CPU interrupt handler so it can +** issue the End Of Interrupt command to the IO-SAPIC. +** +** Overview of exported iosapic functions +** -------------------------------------- +** (caveat: code isn't finished yet - this is just the plan) +** +** iosapic_init: +** o initialize globals (lock, etc) +** o try to read IRT. Presence of IRT determines if this is +** a PAT platform or not. +** +** iosapic_register(): +** o create iosapic_info instance data structure +** o allocate vector_info array for this iosapic +** o initialize vector_info - read corresponding IRdT? +** +** iosapic_xlate_pin: (only called by fixup_irq for PAT platform) +** o intr_pin = read cfg (INTERRUPT_PIN); +** o if (device under PCI-PCI bridge) +** translate slot/pin +** +** iosapic_fixup_irq: +** o if PAT platform (IRT present) +** intr_pin = iosapic_xlate_pin(isi,pcidev): +** intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin) +** save IRT entry into vector_info later +** write cfg INTERRUPT_LINE (with intr_line)? +** else +** intr_line = pcidev->irq +** IRT pointer = NULL +** endif +** o locate vector_info (needs: isi, intr_line) +** o allocate processor "irq" and get txn_addr/data +** o request_irq(processor_irq, iosapic_interrupt, vector_info,...) +** +** iosapic_enable_irq: +** o clear any pending IRQ on that line +** o enable IRdT - call enable_irq(vector[line]->processor_irq) +** o write EOI in case line is already asserted. +** +** iosapic_disable_irq: +** o disable IRdT - call disable_irq(vector[line]->processor_irq) +*/ + +#include + +#include +#include +#ifdef CONFIG_SUPERIO +#include +#endif + +#include +#include "iosapic_private.h" + +#define MODULE_NAME "iosapic" + +/* "local" compile flags */ +#undef PCI_BRIDGE_FUNCS +#undef DEBUG_IOSAPIC +#undef DEBUG_IOSAPIC_IRT + + +#ifdef DEBUG_IOSAPIC +#define DBG(x...) printk(x) +#else /* DEBUG_IOSAPIC */ +#define DBG(x...) +#endif /* DEBUG_IOSAPIC */ + +#ifdef DEBUG_IOSAPIC_IRT +#define DBG_IRT(x...) printk(x) +#else +#define DBG_IRT(x...) +#endif + +#ifdef CONFIG_64BIT +#define COMPARE_IRTE_ADDR(irte, hpa) ((irte)->dest_iosapic_addr == (hpa)) +#else +#define COMPARE_IRTE_ADDR(irte, hpa) \ + ((irte)->dest_iosapic_addr == ((hpa) | 0xffffffff00000000ULL)) +#endif + +#define IOSAPIC_REG_SELECT 0x00 +#define IOSAPIC_REG_WINDOW 0x10 +#define IOSAPIC_REG_EOI 0x40 + +#define IOSAPIC_REG_VERSION 0x1 + +#define IOSAPIC_IRDT_ENTRY(idx) (0x10+(idx)*2) +#define IOSAPIC_IRDT_ENTRY_HI(idx) (0x11+(idx)*2) + +static inline unsigned int iosapic_read(void __iomem *iosapic, unsigned int reg) +{ + writel(reg, iosapic + IOSAPIC_REG_SELECT); + return readl(iosapic + IOSAPIC_REG_WINDOW); +} + +static inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 val) +{ + writel(reg, iosapic + IOSAPIC_REG_SELECT); + writel(val, iosapic + IOSAPIC_REG_WINDOW); +} + +#define IOSAPIC_VERSION_MASK 0x000000ff +#define IOSAPIC_VERSION(ver) ((int) (ver & IOSAPIC_VERSION_MASK)) + +#define IOSAPIC_MAX_ENTRY_MASK 0x00ff0000 +#define IOSAPIC_MAX_ENTRY_SHIFT 0x10 +#define IOSAPIC_IRDT_MAX_ENTRY(ver) \ + (int) (((ver) & IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT) + +/* bits in the "low" I/O Sapic IRdT entry */ +#define IOSAPIC_IRDT_ENABLE 0x10000 +#define IOSAPIC_IRDT_PO_LOW 0x02000 +#define IOSAPIC_IRDT_LEVEL_TRIG 0x08000 +#define IOSAPIC_IRDT_MODE_LPRI 0x00100 + +/* bits in the "high" I/O Sapic IRdT entry */ +#define IOSAPIC_IRDT_ID_EID_SHIFT 0x10 + + +static DEFINE_SPINLOCK(iosapic_lock); + +static inline void iosapic_eoi(__le32 __iomem *addr, __le32 data) +{ + __raw_writel((__force u32)data, addr); +} + +/* +** REVISIT: future platforms may have more than one IRT. +** If so, the following three fields form a structure which +** then be linked into a list. Names are chosen to make searching +** for them easy - not necessarily accurate (eg "cell"). +** +** Alternative: iosapic_info could point to the IRT it's in. +** iosapic_register() could search a list of IRT's. +*/ +static struct irt_entry *irt_cell; +static size_t irt_num_entry; + +static struct irt_entry *iosapic_alloc_irt(int num_entries) +{ + return kzalloc_objs(struct irt_entry, num_entries); +} + +/** + * iosapic_load_irt - Fill in the interrupt routing table + * @cell_num: The cell number of the CPU we're currently executing on + * @irt: The address to place the new IRT at + * @return The number of entries found + * + * The "Get PCI INT Routing Table Size" option returns the number of + * entries in the PCI interrupt routing table for the cell specified + * in the cell_number argument. The cell number must be for a cell + * within the caller's protection domain. + * + * The "Get PCI INT Routing Table" option returns, for the cell + * specified in the cell_number argument, the PCI interrupt routing + * table in the caller allocated memory pointed to by mem_addr. + * We assume the IRT only contains entries for I/O SAPIC and + * calculate the size based on the size of I/O sapic entries. + * + * The PCI interrupt routing table entry format is derived from the + * IA64 SAL Specification 2.4. The PCI interrupt routing table defines + * the routing of PCI interrupt signals between the PCI device output + * "pins" and the IO SAPICs' input "lines" (including core I/O PCI + * devices). This table does NOT include information for devices/slots + * behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec. + * for the architected method of routing of IRQ's behind PPB's. + */ + + +static int __init +iosapic_load_irt(unsigned long cell_num, struct irt_entry **irt) +{ + long status; /* PDC return value status */ + struct irt_entry *table; /* start of interrupt routing tbl */ + unsigned long num_entries = 0UL; + + BUG_ON(!irt); + + if (is_pdc_pat()) { + /* Use pat pdc routine to get interrupt routing table size */ + status = pdc_pat_get_irt_size(&num_entries, cell_num); + DBG("calling get_irt_size (cell %ld) ", cell_num); + DBG("returned %ld, entries: %lu\n", status, num_entries); + + BUG_ON(status != PDC_OK); + BUG_ON(num_entries == 0); + + /* + ** allocate memory for interrupt routing table + ** This interface isn't really right. We are assuming + ** the contents of the table are exclusively + ** for I/O sapic devices. + */ + table = iosapic_alloc_irt(num_entries); + if (table == NULL) { + printk(KERN_WARNING MODULE_NAME ": read_irt : can " + "not alloc mem for IRT\n"); + return 0; + } + + /* get PCI INT routing table */ + status = pdc_pat_get_irt(table, cell_num); + DBG("pdc_pat_get_irt: %ld\n", status); + WARN_ON(status != PDC_OK); + } else { + /* + ** C3000/J5000 (and similar) platforms with Sprockets PDC + ** will return exactly one IRT for all iosapics. + ** So if we have one, don't need to get it again. + */ + if (irt_cell) + return 0; + + /* Should be using the Elroy's HPA, but it's ignored anyway */ + status = pdc_pci_irt_size(&num_entries, 0); + DBG("pdc_pci_irt_size: %ld\n", status); + + if (status != PDC_OK) { + /* Not a "legacy" system with I/O SAPIC either */ + return 0; + } + + BUG_ON(num_entries == 0); + + table = iosapic_alloc_irt(num_entries); + if (!table) { + printk(KERN_WARNING MODULE_NAME ": read_irt : can " + "not alloc mem for IRT\n"); + return 0; + } + + /* HPA ignored by this call too. */ + status = pdc_pci_irt(num_entries, 0, table); + BUG_ON(status != PDC_OK); + } + + /* return interrupt table address */ + *irt = table; + +#ifdef DEBUG_IOSAPIC_IRT +{ + struct irt_entry *p = table; + int i; + + printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num); + printk(MODULE_NAME " start = 0x%px num_entries %ld entry_size %d\n", + table, + num_entries, + (int) sizeof(struct irt_entry)); + + for (i = 0 ; i < num_entries ; i++, p++) { + printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n", + p->entry_type, p->entry_length, p->interrupt_type, + p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id, + p->src_seg_id, p->dest_iosapic_intin, + ((u32 *) p)[2], + ((u32 *) p)[3] + ); + } +} +#endif /* DEBUG_IOSAPIC_IRT */ + + return num_entries; +} + + +static int __init iosapic_init(void) +{ + unsigned long cell = 0; + +#ifdef __LP64__ + if (is_pdc_pat()) { + int status; + struct pdc_pat_cell_num cell_info; + + status = pdc_pat_cell_get_number(&cell_info); + if (status == PDC_OK) { + cell = cell_info.cell_num; + } + } +#endif + + /* get interrupt routing table for this cell */ + irt_num_entry = iosapic_load_irt(cell, &irt_cell); + if (irt_num_entry == 0) + irt_cell = NULL; /* old PDC w/o iosapic */ + + return 0; +} +arch_initcall(iosapic_init); + + +/* +** Return the IRT entry in case we need to look something else up. +*/ +static struct irt_entry * +irt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin) +{ + struct irt_entry *i = irt_cell; + int cnt; /* track how many entries we've looked at */ + u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1); + + DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin); + + for (cnt=0; cnt < irt_num_entry; cnt++, i++) { + + /* + ** Validate: entry_type, entry_length, interrupt_type + ** + ** Difference between validate vs compare is the former + ** should print debug info and is not expected to "fail" + ** on current platforms. + */ + if (i->entry_type != IRT_IOSAPIC_TYPE) { + DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type); + continue; + } + + if (i->entry_length != IRT_IOSAPIC_LENGTH) { + DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d length %d\n", i, cnt, i->entry_length); + continue; + } + + if (i->interrupt_type != IRT_VECTORED_INTR) { + DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d interrupt_type %d\n", i, cnt, i->interrupt_type); + continue; + } + + if (!COMPARE_IRTE_ADDR(i, isi->isi_hpa)) + continue; + + if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno) + continue; + + /* + ** Ignore: src_bus_id and rc_seg_id correlate with + ** iosapic_info->isi_hpa on HP platforms. + ** If needed, pass in "PFA" (aka config space addr) + ** instead of slot. + */ + + /* Found it! */ + return i; + } + + printk(KERN_WARNING MODULE_NAME ": 0x%lx : no IRT entry for slot %d, pin %d\n", + isi->isi_hpa, slot, intr_pin); + return NULL; +} + + +/* +** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges. +** Legacy PDC already does this translation for us and stores it in INTR_LINE. +** +** PAT PDC needs to basically do what legacy PDC does: +** o read PIN +** o adjust PIN in case device is "behind" a PPB +** (eg 4-port 100BT and SCSI/LAN "Combo Card") +** o convert slot/pin to I/O SAPIC input line. +** +** HP platforms only support: +** o one level of skewing for any number of PPBs +** o only support PCI-PCI Bridges. +*/ +static struct irt_entry * +iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) +{ + u8 intr_pin, intr_slot; + + pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin); + + DBG_IRT("iosapic_xlate_pin(%s) SLOT %d pin %d\n", + pci_name(pcidev), PCI_SLOT(pcidev->devfn), intr_pin); + + if (intr_pin == 0) { + /* The device does NOT support/use IRQ lines. */ + return NULL; + } + + /* Check if pcidev behind a PPB */ + if (pcidev->bus->parent) { + /* Convert pcidev INTR_PIN into something we + ** can lookup in the IRT. + */ +#ifdef PCI_BRIDGE_FUNCS + /* + ** Proposal #1: + ** + ** call implementation specific translation function + ** This is architecturally "cleaner". HP-UX doesn't + ** support other secondary bus types (eg. E/ISA) directly. + ** May be needed for other processor (eg IA64) architectures + ** or by some ambitous soul who wants to watch TV. + */ + if (pci_bridge_funcs->xlate_intr_line) { + intr_pin = pci_bridge_funcs->xlate_intr_line(pcidev); + } +#else /* PCI_BRIDGE_FUNCS */ + struct pci_bus *p = pcidev->bus; + /* + ** Proposal #2: + ** The "pin" is skewed ((pin + dev - 1) % 4). + ** + ** This isn't very clean since I/O SAPIC must assume: + ** - all platforms only have PCI busses. + ** - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA) + ** - IRQ routing is only skewed once regardless of + ** the number of PPB's between iosapic and device. + ** (Bit3 expansion chassis follows this rule) + ** + ** Advantage is it's really easy to implement. + */ + intr_pin = pci_swizzle_interrupt_pin(pcidev, intr_pin); +#endif /* PCI_BRIDGE_FUNCS */ + + /* + * Locate the host slot of the PPB. + */ + while (p->parent->parent) + p = p->parent; + + intr_slot = PCI_SLOT(p->self->devfn); + } else { + intr_slot = PCI_SLOT(pcidev->devfn); + } + DBG_IRT("iosapic_xlate_pin: bus %lld slot %d pin %d\n", + pcidev->bus->busn_res.start, intr_slot, intr_pin); + + return irt_find_irqline(isi, intr_slot, intr_pin); +} + +static void iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1) +{ + struct iosapic_info *isp = vi->iosapic; + u8 idx = vi->irqline; + + *dp0 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY(idx)); + *dp1 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY_HI(idx)); +} + + +static void iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1) +{ + struct iosapic_info *isp = vi->iosapic; + + DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %lx 0x%x 0x%x\n", + vi->irqline, isp->isi_hpa, dp0, dp1); + + iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0); + + /* Read the window register to flush the writes down to HW */ + dp0 = readl(isp->addr+IOSAPIC_REG_WINDOW); + + iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1); + + /* Read the window register to flush the writes down to HW */ + dp1 = readl(isp->addr+IOSAPIC_REG_WINDOW); +} + +/* +** set_irt prepares the data (dp0, dp1) according to the vector_info +** and target cpu (id_eid). dp0/dp1 are then used to program I/O SAPIC +** IRdT for the given "vector" (aka IRQ line). +*/ +static void +iosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1) +{ + u32 mode = 0; + struct irt_entry *p = vi->irte; + + if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO) + mode |= IOSAPIC_IRDT_PO_LOW; + + if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG) + mode |= IOSAPIC_IRDT_LEVEL_TRIG; + + /* + ** IA64 REVISIT + ** PA doesn't support EXTINT or LPRIO bits. + */ + + *dp0 = mode | (u32) vi->txn_data; + + /* + ** Extracting id_eid isn't a real clean way of getting it. + ** But the encoding is the same for both PA and IA64 platforms. + */ + if (is_pdc_pat()) { + /* + ** PAT PDC just hands it to us "right". + ** txn_addr comes from cpu_data[x].txn_addr. + */ + *dp1 = (u32) (vi->txn_addr); + } else { + /* + ** eg if base_addr == 0xfffa0000), + ** we want to get 0xa0ff0000. + ** + ** eid 0x0ff00000 -> 0x00ff0000 + ** id 0x000ff000 -> 0xff000000 + */ + *dp1 = (((u32)vi->txn_addr & 0x0ff00000) >> 4) | + (((u32)vi->txn_addr & 0x000ff000) << 12); + } + DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1); +} + + +static void iosapic_mask_irq(struct irq_data *d) +{ + unsigned long flags; + struct vector_info *vi = irq_data_get_irq_chip_data(d); + u32 d0, d1; + + spin_lock_irqsave(&iosapic_lock, flags); + iosapic_rd_irt_entry(vi, &d0, &d1); + d0 |= IOSAPIC_IRDT_ENABLE; + iosapic_wr_irt_entry(vi, d0, d1); + spin_unlock_irqrestore(&iosapic_lock, flags); +} + +static void iosapic_unmask_irq(struct irq_data *d) +{ + struct vector_info *vi = irq_data_get_irq_chip_data(d); + u32 d0, d1; + + /* data is initialized by fixup_irq */ + WARN_ON(vi->txn_irq == 0); + + iosapic_set_irt_data(vi, &d0, &d1); + iosapic_wr_irt_entry(vi, d0, d1); + +#ifdef DEBUG_IOSAPIC_IRT +{ + u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL); + printk("iosapic_enable_irq(): regs %p", vi->eoi_addr); + for ( ; t < vi->eoi_addr; t++) + printk(" %x", readl(t)); + printk("\n"); +} + +printk("iosapic_enable_irq(): sel "); +{ + struct iosapic_info *isp = vi->iosapic; + + for (d0=0x10; d0<0x1e; d0++) { + d1 = iosapic_read(isp->addr, d0); + printk(" %x", d1); + } +} +printk("\n"); +#endif + + /* + * Issuing I/O SAPIC an EOI causes an interrupt IFF IRQ line is + * asserted. IRQ generally should not be asserted when a driver + * enables their IRQ. It can lead to "interesting" race conditions + * in the driver initialization sequence. + */ + DBG(KERN_DEBUG "enable_irq(%d): eoi(%p, 0x%x)\n", d->irq, + vi->eoi_addr, vi->eoi_data); + iosapic_eoi(vi->eoi_addr, vi->eoi_data); +} + +static void iosapic_eoi_irq(struct irq_data *d) +{ + struct vector_info *vi = irq_data_get_irq_chip_data(d); + + iosapic_eoi(vi->eoi_addr, vi->eoi_data); + cpu_eoi_irq(d); +} + +#ifdef CONFIG_SMP +static int iosapic_set_affinity_irq(struct irq_data *d, + const struct cpumask *dest, bool force) +{ + struct vector_info *vi = irq_data_get_irq_chip_data(d); + u32 d0, d1, dummy_d0; + unsigned long flags; + int dest_cpu; + + dest_cpu = cpu_check_affinity(d, dest); + if (dest_cpu < 0) + return -1; + + irq_data_update_affinity(d, cpumask_of(dest_cpu)); + vi->txn_addr = txn_affinity_addr(d->irq, dest_cpu); + + spin_lock_irqsave(&iosapic_lock, flags); + /* d1 contains the destination CPU, so only want to set that + * entry */ + iosapic_rd_irt_entry(vi, &d0, &d1); + iosapic_set_irt_data(vi, &dummy_d0, &d1); + iosapic_wr_irt_entry(vi, d0, d1); + spin_unlock_irqrestore(&iosapic_lock, flags); + + return 0; +} +#endif + +static struct irq_chip iosapic_interrupt_type = { + .name = "IO-SAPIC-level", + .irq_unmask = iosapic_unmask_irq, + .irq_mask = iosapic_mask_irq, + .irq_ack = cpu_ack_irq, + .irq_eoi = iosapic_eoi_irq, +#ifdef CONFIG_SMP + .irq_set_affinity = iosapic_set_affinity_irq, +#endif +}; + +int iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev) +{ + struct iosapic_info *isi = isi_obj; + struct irt_entry *irte = NULL; /* only used if PAT PDC */ + struct vector_info *vi; + int isi_line; /* line used by device */ + + if (!isi) { + printk(KERN_WARNING MODULE_NAME ": hpa not registered for %s\n", + pci_name(pcidev)); + return -1; + } + +#ifdef CONFIG_SUPERIO + /* + * HACK ALERT! (non-compliant PCI device support) + * + * All SuckyIO interrupts are routed through the PIC's on function 1. + * But SuckyIO OHCI USB controller gets an IRT entry anyway because + * it advertises INT D for INT_PIN. Use that IRT entry to get the + * SuckyIO interrupt routing for PICs on function 1 (*BLEECCHH*). + */ + if (is_superio_device(pcidev)) { + /* We must call superio_fixup_irq() to register the pdev */ + pcidev->irq = superio_fixup_irq(pcidev); + + /* Don't return if need to program the IOSAPIC's IRT... */ + if (PCI_FUNC(pcidev->devfn) != SUPERIO_USB_FN) + return pcidev->irq; + } +#endif /* CONFIG_SUPERIO */ + + /* lookup IRT entry for isi/slot/pin set */ + irte = iosapic_xlate_pin(isi, pcidev); + if (!irte) { + printk("iosapic: no IRTE for %s (IRQ not connected?)\n", + pci_name(pcidev)); + return -1; + } + DBG_IRT("iosapic_fixup_irq(): irte %px %02x %02x %02x %02x %02x %02x %02x %04llx\n", + irte, + irte->entry_type, + irte->entry_length, + irte->polarity_trigger, + irte->src_bus_irq_devno, + irte->src_bus_id, + irte->src_seg_id, + irte->dest_iosapic_intin, + irte->dest_iosapic_addr); + isi_line = irte->dest_iosapic_intin; + + /* get vector info for this input line */ + vi = isi->isi_vector + isi_line; + DBG_IRT("iosapic_fixup_irq: line %d vi 0x%px\n", isi_line, vi); + + /* If this IRQ line has already been setup, skip it */ + if (vi->irte) + goto out; + + vi->irte = irte; + + /* + * Allocate processor IRQ + * + * XXX/FIXME The txn_alloc_irq() code and related code should be + * moved to enable_irq(). That way we only allocate processor IRQ + * bits for devices that actually have drivers claiming them. + * Right now we assign an IRQ to every PCI device present, + * regardless of whether it's used or not. + */ + vi->txn_irq = txn_alloc_irq(8); + + if (vi->txn_irq < 0) + panic("I/O sapic: couldn't get TXN IRQ\n"); + + /* enable_irq() will use txn_* to program IRdT */ + vi->txn_addr = txn_alloc_addr(vi->txn_irq); + vi->txn_data = txn_alloc_data(vi->txn_irq); + DBG_IRT("iosapic_fixup_irq() TXN: 0x%lx 0x%x\n", + vi->txn_addr, vi->txn_data); + + vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI; + vi->eoi_data = cpu_to_le32(vi->txn_data); + + cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi); + + out: + pcidev->irq = vi->txn_irq; + + DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n", + PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn), + pcidev->vendor, pcidev->device, isi_line, pcidev->irq); + + return pcidev->irq; +} + +static struct iosapic_info *iosapic_list; + +#ifdef CONFIG_64BIT +int iosapic_serial_irq(struct parisc_device *dev) +{ + struct iosapic_info *isi; + struct irt_entry *irte; + struct vector_info *vi; + int cnt; + int intin; + + intin = (dev->mod_info >> 24) & 15; + + /* lookup IRT entry for isi/slot/pin set */ + for (cnt = 0; cnt < irt_num_entry; cnt++) { + irte = &irt_cell[cnt]; + if (COMPARE_IRTE_ADDR(irte, dev->mod0) && + irte->dest_iosapic_intin == intin) + break; + } + if (cnt >= irt_num_entry) + return 0; /* no irq found, force polling */ + + DBG_IRT("iosapic_serial_irq(): irte %p %x %x %x %x %x %x %x %x\n", + irte, + irte->entry_type, + irte->entry_length, + irte->polarity_trigger, + irte->src_bus_irq_devno, + irte->src_bus_id, + irte->src_seg_id, + irte->dest_iosapic_intin, + (u32) irte->dest_iosapic_addr); + + /* search for iosapic */ + for (isi = iosapic_list; isi; isi = isi->isi_next) + if (isi->isi_hpa == dev->mod0) + break; + if (!isi) + return 0; /* no iosapic found, force polling */ + + /* get vector info for this input line */ + vi = isi->isi_vector + intin; + DBG_IRT("iosapic_serial_irq: line %d vi 0x%p\n", intin, vi); + + /* If this IRQ line has already been setup, skip it */ + if (vi->irte) + goto out; + + vi->irte = irte; + + /* + * Allocate processor IRQ + * + * XXX/FIXME The txn_alloc_irq() code and related code should be + * moved to enable_irq(). That way we only allocate processor IRQ + * bits for devices that actually have drivers claiming them. + * Right now we assign an IRQ to every PCI device present, + * regardless of whether it's used or not. + */ + vi->txn_irq = txn_alloc_irq(8); + + if (vi->txn_irq < 0) + panic("I/O sapic: couldn't get TXN IRQ\n"); + + /* enable_irq() will use txn_* to program IRdT */ + vi->txn_addr = txn_alloc_addr(vi->txn_irq); + vi->txn_data = txn_alloc_data(vi->txn_irq); + + vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI; + vi->eoi_data = cpu_to_le32(vi->txn_data); + + cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi); + + out: + + return vi->txn_irq; +} +EXPORT_SYMBOL(iosapic_serial_irq); +#endif + + +/* +** squirrel away the I/O Sapic Version +*/ +static unsigned int +iosapic_rd_version(struct iosapic_info *isi) +{ + return iosapic_read(isi->addr, IOSAPIC_REG_VERSION); +} + + +/* +** iosapic_register() is called by "drivers" with an integrated I/O SAPIC. +** Caller must be certain they have an I/O SAPIC and know its MMIO address. +** +** o allocate iosapic_info and add it to the list +** o read iosapic version and squirrel that away +** o read size of IRdT. +** o allocate and initialize isi_vector[] +** o allocate irq region +*/ +void *iosapic_register(unsigned long hpa, void __iomem *vaddr) +{ + struct iosapic_info *isi = NULL; + struct irt_entry *irte = irt_cell; + struct vector_info *vip; + int cnt; /* track how many entries we've looked at */ + + /* + * Astro based platforms can only support PCI OLARD if they implement + * PAT PDC. Legacy PDC omits LBAs with no PCI devices from the IRT. + * Search the IRT and ignore iosapic's which aren't in the IRT. + */ + for (cnt=0; cnt < irt_num_entry; cnt++, irte++) { + WARN_ON(IRT_IOSAPIC_TYPE != irte->entry_type); + if (COMPARE_IRTE_ADDR(irte, hpa)) + break; + } + + if (cnt >= irt_num_entry) { + DBG("iosapic_register() ignoring 0x%lx (NOT FOUND)\n", hpa); + return NULL; + } + + isi = kzalloc_obj(struct iosapic_info); + if (!isi) { + BUG(); + return NULL; + } + + isi->addr = vaddr; + isi->isi_hpa = hpa; + isi->isi_version = iosapic_rd_version(isi); + isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1; + DBG_IRT("iosapic_register: num vectors = %d\n", isi->isi_num_vectors); + + vip = isi->isi_vector = kzalloc_objs(struct vector_info, + isi->isi_num_vectors); + if (vip == NULL) { + kfree(isi); + return NULL; + } + + for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) { + vip->irqline = (unsigned char) cnt; + vip->iosapic = isi; + } + isi->isi_next = iosapic_list; + iosapic_list = isi; + return isi; +} + + +#ifdef DEBUG_IOSAPIC + +static void +iosapic_prt_irt(void *irt, long num_entry) +{ + unsigned int i, *irp = (unsigned int *) irt; + + + printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry); + + for (i=0; iirqline, vi); + printk(KERN_DEBUG "\t\tstatus: %.4x\n", vi->status); + printk(KERN_DEBUG "\t\ttxn_irq: %d\n", vi->txn_irq); + printk(KERN_DEBUG "\t\ttxn_addr: %lx\n", vi->txn_addr); + printk(KERN_DEBUG "\t\ttxn_data: %lx\n", vi->txn_data); + printk(KERN_DEBUG "\t\teoi_addr: %p\n", vi->eoi_addr); + printk(KERN_DEBUG "\t\teoi_data: %x\n", vi->eoi_data); +} + + +static void +iosapic_prt_isi(struct iosapic_info *isi) +{ + printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi); + printk(KERN_DEBUG "\t\tisi_hpa: %lx\n", isi->isi_hpa); + printk(KERN_DEBUG "\t\tisi_status: %x\n", isi->isi_status); + printk(KERN_DEBUG "\t\tisi_version: %x\n", isi->isi_version); + printk(KERN_DEBUG "\t\tisi_vector: %p\n", isi->isi_vector); +} +#endif /* DEBUG_IOSAPIC */ diff --git a/drivers/parisc/iosapic_private.h b/drivers/parisc/iosapic_private.h new file mode 100644 index 000000000..bd8ff4016 --- /dev/null +++ b/drivers/parisc/iosapic_private.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Private structs/constants for PARISC IOSAPIC support + * + * Copyright (C) 2000 Hewlett Packard (Grant Grundler) + * Copyright (C) 2000,2003 Grant Grundler (grundler at parisc-linux.org) + * Copyright (C) 2002 Matthew Wilcox (willy at parisc-linux.org) + */ + +/* +** This file is private to iosapic driver. +** If stuff needs to be used by another driver, move it to a common file. +** +** WARNING: fields most data structures here are ordered to make sure +** they pack nicely for 64-bit compilation. (ie sizeof(long) == 8) +*/ + + +/* +** Interrupt Routing Stuff +** ----------------------- +** The interrupt routing table consists of entries derived from +** MP Specification Draft 1.5. There is one interrupt routing +** table per cell. N- and L-class consist of a single cell. +*/ +struct irt_entry { + + /* Entry Type 139 identifies an I/O SAPIC interrupt entry */ + u8 entry_type; + + /* Entry Length 16 indicates entry is 16 bytes long */ + u8 entry_length; + + /* + ** Interrupt Type of 0 indicates a vectored interrupt, + ** all other values are reserved + */ + u8 interrupt_type; + + /* + ** PO and EL + ** Polarity of SAPIC I/O input signals: + ** 00 = Reserved + ** 01 = Active high + ** 10 = Reserved + ** 11 = Active low + ** Trigger mode of SAPIC I/O input signals: + ** 00 = Reserved + ** 01 = Edge-triggered + ** 10 = Reserved + ** 11 = Level-triggered + */ + u8 polarity_trigger; + + /* + ** IRQ and DEVNO + ** irq identifies PCI interrupt signal where + ** 0x0 corresponds to INT_A#, + ** 0x1 corresponds to INT_B#, + ** 0x2 corresponds to INT_C# + ** 0x3 corresponds to INT_D# + ** PCI device number where interrupt originates + */ + u8 src_bus_irq_devno; + + /* Source Bus ID identifies the bus where interrupt signal comes from */ + u8 src_bus_id; + + /* + ** Segment ID is unique across a protection domain and + ** identifies a segment of PCI buses (reserved in + ** MP Specification Draft 1.5) + */ + u8 src_seg_id; + + /* + ** Destination I/O SAPIC INTIN# identifies the INTIN n pin + ** to which the signal is connected + */ + u8 dest_iosapic_intin; + + /* + ** Destination I/O SAPIC Address identifies the I/O SAPIC + ** to which the signal is connected + */ + u64 dest_iosapic_addr; +}; + +#define IRT_IOSAPIC_TYPE 139 +#define IRT_IOSAPIC_LENGTH 16 + +#define IRT_VECTORED_INTR 0 + +#define IRT_PO_MASK 0x3 +#define IRT_ACTIVE_HI 1 +#define IRT_ACTIVE_LO 3 + +#define IRT_EL_MASK 0x3 +#define IRT_EL_SHIFT 2 +#define IRT_EDGE_TRIG 1 +#define IRT_LEVEL_TRIG 3 + +#define IRT_IRQ_MASK 0x3 +#define IRT_DEV_MASK 0x1f +#define IRT_DEV_SHIFT 2 + +#define IRT_IRQ_DEVNO_MASK ((IRT_DEV_MASK << IRT_DEV_SHIFT) | IRT_IRQ_MASK) + +#ifdef SUPPORT_MULTI_CELL +struct iosapic_irt { + struct iosapic_irt *irt_next; /* next routing table */ + struct irt_entry *irt_base; /* intr routing table address */ + size_t irte_count; /* number of entries in the table */ + size_t irte_size; /* size (bytes) of each entry */ +}; +#endif + +struct vector_info { + struct iosapic_info *iosapic; /* I/O SAPIC this vector is on */ + struct irt_entry *irte; /* IRT entry */ + __le32 __iomem *eoi_addr; /* precalculate EOI reg address */ + __le32 eoi_data; /* IA64: ? PA: swapped txn_data */ + int txn_irq; /* virtual IRQ number for processor */ + ulong txn_addr; /* IA64: id_eid PA: partial HPA */ + u32 txn_data; /* CPU interrupt bit */ + u8 status; /* status/flags */ + u8 irqline; /* INTINn(IRQ) */ +}; + + +struct iosapic_info { + struct iosapic_info * isi_next; /* list of I/O SAPIC */ + void __iomem * addr; /* remapped address */ + unsigned long isi_hpa; /* physical base address */ + struct vector_info * isi_vector; /* IRdT (IRQ line) array */ + int isi_num_vectors; /* size of IRdT array */ + int isi_status; /* status/flags */ + unsigned int isi_version; /* DEBUG: data fr version reg */ +}; + + + +#ifdef __IA64__ +/* +** PA risc does NOT have any local sapics. IA64 does. +** PIB (Processor Interrupt Block) is handled by Astro or Dew (Stretch CEC). +** +** PA: Get id_eid from IRT and hardcode PIB to 0xfeeNNNN0 +** Emulate the data on PAT platforms. +*/ +struct local_sapic_info { + struct local_sapic_info *lsi_next; /* point to next CPU info */ + int *lsi_cpu_id; /* point to logical CPU id */ + unsigned long *lsi_id_eid; /* point to IA-64 CPU id */ + int *lsi_status; /* point to CPU status */ + void *lsi_private; /* point to special info */ +}; + +/* +** "root" data structure which ties everything together. +** Should always be able to start with sapic_root and locate +** the desired information. +*/ +struct sapic_info { + struct sapic_info *si_next; /* info is per cell */ + int si_cellid; /* cell id */ + unsigned int si_status; /* status */ + char *si_pib_base; /* intr blk base address */ + local_sapic_info_t *si_local_info; + io_sapic_info_t *si_io_info; + extint_info_t *si_extint_info;/* External Intr info */ +}; +#endif + diff --git a/drivers/parisc/lasi.c b/drivers/parisc/lasi.c new file mode 100644 index 000000000..a5b80cd5c --- /dev/null +++ b/drivers/parisc/lasi.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LASI Device Driver + * + * (c) Copyright 1999 Red Hat Software + * Portions (c) Copyright 1999 The Puffin Group Inc. + * Portions (c) Copyright 1999 Hewlett-Packard + * + * by Alan Cox and + * Alex deVries + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gsc.h" + + +#define LASI_VER 0xC008 /* LASI Version */ + +#define LASI_IO_CONF 0x7FFFE /* LASI primary configuration register */ +#define LASI_IO_CONF2 0x7FFFF /* LASI secondary configuration register */ + +static void lasi_choose_irq(struct parisc_device *dev, void *ctrl) +{ + int irq; + + switch (dev->id.sversion) { + case 0x74: irq = 7; break; /* Centronics */ + case 0x7B: irq = 13; break; /* Audio */ + case 0x81: irq = 14; break; /* Lasi itself */ + case 0x82: irq = 9; break; /* SCSI */ + case 0x83: irq = 20; break; /* Floppy */ + case 0x84: irq = 26; break; /* PS/2 Keyboard */ + case 0x87: irq = 18; break; /* ISDN */ + case 0x8A: irq = 8; break; /* LAN */ + case 0x8C: irq = 5; break; /* RS232 */ + case 0x8D: irq = (dev->hw_path == 13) ? 16 : 17; break; + /* Telephone */ + default: return; /* unknown */ + } + + gsc_asic_assign_irq(ctrl, irq, &dev->irq); +} + +static void __init +lasi_init_irq(struct gsc_asic *this_lasi) +{ + unsigned long lasi_base = this_lasi->hpa; + + /* Stop LASI barking for a bit */ + gsc_writel(0x00000000, lasi_base+OFFSET_IMR); + + /* clear pending interrupts */ + gsc_readl(lasi_base+OFFSET_IRR); + + /* We're not really convinced we want to reset the onboard + * devices. Firmware does it for us... + */ + + /* Resets */ + /* gsc_writel(0xFFFFFFFF, lasi_base+0x2000);*/ /* Parallel */ + if(pdc_add_valid(lasi_base+0x4004) == PDC_OK) + gsc_writel(0xFFFFFFFF, lasi_base+0x4004); /* Audio */ + /* gsc_writel(0xFFFFFFFF, lasi_base+0x5000);*/ /* Serial */ + /* gsc_writel(0xFFFFFFFF, lasi_base+0x6000);*/ /* SCSI */ + gsc_writel(0xFFFFFFFF, lasi_base+0x7000); /* LAN */ + gsc_writel(0xFFFFFFFF, lasi_base+0x8000); /* Keyboard */ + gsc_writel(0xFFFFFFFF, lasi_base+0xA000); /* FDC */ + + /* Ok we hit it on the head with a hammer, our Dog is now + ** comatose and muzzled. Devices will now unmask LASI + ** interrupts as they are registered as irq's in the LASI range. + */ + /* XXX: I thought it was `awks that got `it on the `ead with an + * `ammer. -- willy + */ +} + + +/* + ** lasi_led_init() + ** + ** lasi_led_init() initializes the LED controller on the LASI. + ** + ** Since Mirage and Electra machines use a different LED + ** address register, we need to check for these machines + ** explicitly. + */ + +#ifndef CONFIG_CHASSIS_LCD_LED + +#define lasi_led_init(x) /* nothing */ + +#else + +static void __init lasi_led_init(unsigned long lasi_hpa) +{ + unsigned long datareg; + + switch (CPU_HVERSION) { + /* Gecko machines have only one single LED, which can be permanently + turned on by writing a zero into the power control register. */ + case 0x600: /* Gecko (712/60) */ + case 0x601: /* Gecko (712/80) */ + case 0x602: /* Gecko (712/100) */ + case 0x603: /* Anole 64 (743/64) */ + case 0x604: /* Anole 100 (743/100) */ + case 0x605: /* Gecko (712/120) */ + datareg = lasi_hpa + 0x0000C000; + gsc_writeb(0, datareg); + return; /* no need to register the LED interrupt-function */ + + /* Mirage and Electra machines need special offsets */ + case 0x60A: /* Mirage Jr (715/64) */ + case 0x60B: /* Mirage 100 */ + case 0x60C: /* Mirage 100+ */ + case 0x60D: /* Electra 100 */ + case 0x60E: /* Electra 120 */ + datareg = lasi_hpa - 0x00020000; + break; + + default: + datareg = lasi_hpa + 0x0000C000; + break; + } + + register_led_driver(DISPLAY_MODEL_LASI, LED_CMD_REG_NONE, datareg); +} +#endif + +/* + * lasi_power_off + * + * Function for lasi to turn off the power. This is accomplished by setting a + * 1 to PWR_ON_L in the Power Control Register + * + */ +static int lasi_power_off(struct sys_off_data *data) +{ + struct gsc_asic *lasi = data->cb_data; + + /* Power down the machine via Power Control Register */ + gsc_writel(0x02, lasi->hpa + 0x0000C000); + + /* might not be reached: */ + return NOTIFY_DONE; +} + +static int __init lasi_init_chip(struct parisc_device *dev) +{ + struct gsc_asic *lasi; + int ret; + + lasi = kzalloc_obj(*lasi); + if (!lasi) + return -ENOMEM; + + lasi->name = "Lasi"; + lasi->hpa = dev->hpa.start; + + /* Check the 4-bit (yes, only 4) version register */ + lasi->version = gsc_readl(lasi->hpa + LASI_VER) & 0xf; + printk(KERN_INFO "%s version %d at 0x%lx found.\n", + lasi->name, lasi->version, lasi->hpa); + + /* initialize the chassis LEDs really early */ + lasi_led_init(lasi->hpa); + + /* Stop LASI barking for a bit */ + lasi_init_irq(lasi); + + /* the IRQ lasi should use */ + dev->irq = gsc_alloc_irq(&lasi->gsc_irq); + if (dev->irq < 0) { + printk(KERN_ERR "%s(): cannot get GSC irq\n", + __func__); + kfree(lasi); + return -EBUSY; + } + + lasi->eim = ((u32) lasi->gsc_irq.txn_addr) | lasi->gsc_irq.txn_data; + + ret = request_irq(lasi->gsc_irq.irq, gsc_asic_intr, 0, "lasi", lasi); + if (ret < 0) { + goto err_free; + } + + /* enable IRQ's for devices below LASI */ + gsc_writel(lasi->eim, lasi->hpa + OFFSET_IAR); + + /* Done init'ing, register this driver */ + ret = gsc_common_setup(dev, lasi); + if (ret) { + goto err_irq; + } + + gsc_fixup_irqs(dev, lasi, lasi_choose_irq); + + /* register the LASI power off function */ + register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, lasi_power_off, lasi); + + return ret; + +err_irq: + free_irq(lasi->gsc_irq.irq, lasi); +err_free: + kfree(lasi); + return ret; +} + +static struct parisc_device_id lasi_tbl[] __initdata = { + { HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00081 }, + { 0, } +}; +MODULE_DEVICE_TABLE(parisc, lasi_tbl); + +static struct parisc_driver lasi_driver __refdata = { + .name = "lasi", + .id_table = lasi_tbl, + .probe = lasi_init_chip, +}; + +static int __init lasi_init(void) +{ + return register_parisc_driver(&lasi_driver); +} +arch_initcall(lasi_init); diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c new file mode 100644 index 000000000..5197a3c93 --- /dev/null +++ b/drivers/parisc/lba_pci.c @@ -0,0 +1,1760 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** +** PCI Lower Bus Adapter (LBA) manager +** +** (c) Copyright 1999,2000 Grant Grundler +** (c) Copyright 1999,2000 Hewlett-Packard Company +** +** +** +** This module primarily provides access to PCI bus (config/IOport +** spaces) on platforms with an SBA/LBA chipset. A/B/C/J/L/N-class +** with 4 digit model numbers - eg C3000 (and A400...sigh). +** +** LBA driver isn't as simple as the Dino driver because: +** (a) this chip has substantial bug fixes between revisions +** (Only one Dino bug has a software workaround :^( ) +** (b) has more options which we don't (yet) support (DMA hints, OLARD) +** (c) IRQ support lives in the I/O SAPIC driver (not with PCI driver) +** (d) play nicely with both PAT and "Legacy" PA-RISC firmware (PDC). +** (dino only deals with "Legacy" PDC) +** +** LBA driver passes the I/O SAPIC HPA to the I/O SAPIC driver. +** (I/O SAPIC is integratd in the LBA chip). +** +** FIXME: Add support to SBA and LBA drivers for DMA hint sets +** FIXME: Add support for PCI card hot-plug (OLARD). +*/ + +#include +#include +#include +#include +#include /* for __init */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include /* for register_parisc_driver() stuff */ +#include +#include /* read/write stuff */ + +#include "iommu.h" + +#undef DEBUG_LBA /* general stuff */ +#undef DEBUG_LBA_PORT /* debug I/O Port access */ +#undef DEBUG_LBA_CFG /* debug Config Space Access (ie PCI Bus walk) */ +#undef DEBUG_LBA_PAT /* debug PCI Resource Mgt code - PDC PAT only */ + +#undef FBB_SUPPORT /* Fast Back-Back xfers - NOT READY YET */ + + +#ifdef DEBUG_LBA +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef DEBUG_LBA_PORT +#define DBG_PORT(x...) printk(x) +#else +#define DBG_PORT(x...) +#endif + +#ifdef DEBUG_LBA_CFG +#define DBG_CFG(x...) printk(x) +#else +#define DBG_CFG(x...) +#endif + +#ifdef DEBUG_LBA_PAT +#define DBG_PAT(x...) printk(x) +#else +#define DBG_PAT(x...) +#endif + + +/* +** Config accessor functions only pass in the 8-bit bus number and not +** the 8-bit "PCI Segment" number. Each LBA will be assigned a PCI bus +** number based on what firmware wrote into the scratch register. +** +** The "secondary" bus number is set to this before calling +** pci_register_ops(). If any PPB's are present, the scan will +** discover them and update the "secondary" and "subordinate" +** fields in the pci_bus structure. +** +** Changes in the configuration *may* result in a different +** bus number for each LBA depending on what firmware does. +*/ + +#define MODULE_NAME "LBA" + +/* non-postable I/O port space, densely packed */ +#define LBA_PORT_BASE (PCI_F_EXTEND | 0xfee00000UL) +static void __iomem *astro_iop_base __read_mostly; + +static u32 lba_t32; + +/* lba flags */ +#define LBA_FLAG_SKIP_PROBE 0x10 + +#define LBA_SKIP_PROBE(d) ((d)->flags & LBA_FLAG_SKIP_PROBE) + +static inline struct lba_device *LBA_DEV(struct pci_hba_data *hba) +{ + return container_of(hba, struct lba_device, hba); +} + +/* +** Only allow 8 subsidiary busses per LBA +** Problem is the PCI bus numbering is globally shared. +*/ +#define LBA_MAX_NUM_BUSES 8 + +/************************************ + * LBA register read and write support + * + * BE WARNED: register writes are posted. + * (ie follow writes which must reach HW with a read) + */ +#define READ_U8(addr) __raw_readb(addr) +#define READ_U16(addr) __raw_readw(addr) +#define READ_U32(addr) __raw_readl(addr) +#define WRITE_U8(value, addr) __raw_writeb(value, addr) +#define WRITE_U16(value, addr) __raw_writew(value, addr) +#define WRITE_U32(value, addr) __raw_writel(value, addr) + +#define READ_REG8(addr) readb(addr) +#define READ_REG16(addr) readw(addr) +#define READ_REG32(addr) readl(addr) +#define READ_REG64(addr) readq(addr) +#define WRITE_REG8(value, addr) writeb(value, addr) +#define WRITE_REG16(value, addr) writew(value, addr) +#define WRITE_REG32(value, addr) writel(value, addr) + + +#define LBA_CFG_TOK(bus,dfn) ((u32) ((bus)<<16 | (dfn)<<8)) +#define LBA_CFG_BUS(tok) ((u8) ((tok)>>16)) +#define LBA_CFG_DEV(tok) ((u8) ((tok)>>11) & 0x1f) +#define LBA_CFG_FUNC(tok) ((u8) ((tok)>>8 ) & 0x7) + + +/* +** Extract LBA (Rope) number from HPA +** REVISIT: 16 ropes for Stretch/Ike? +*/ +#define ROPES_PER_IOC 8 +#define LBA_NUM(x) ((((unsigned long) x) >> 13) & (ROPES_PER_IOC-1)) + + +static void +lba_dump_res(struct resource *r, int d) +{ + int i; + + if (NULL == r) + return; + + printk(KERN_DEBUG "(%p)", r->parent); + for (i = d; i ; --i) printk(" "); + printk(KERN_DEBUG "%p [%lx,%lx]/%lx\n", r, + (long)r->start, (long)r->end, r->flags); + lba_dump_res(r->child, d+2); + lba_dump_res(r->sibling, d); +} + + +/* +** LBA rev 2.0, 2.1, 2.2, and 3.0 bus walks require a complex +** workaround for cfg cycles: +** -- preserve LBA state +** -- prevent any DMA from occurring +** -- turn on smart mode +** -- probe with config writes before doing config reads +** -- check ERROR_STATUS +** -- clear ERROR_STATUS +** -- restore LBA state +** +** The workaround is only used for device discovery. +*/ + +static int lba_device_present(u8 bus, u8 dfn, struct lba_device *d) +{ + u8 first_bus = d->hba.hba_bus->busn_res.start; + u8 last_sub_bus = d->hba.hba_bus->busn_res.end; + + if ((bus < first_bus) || + (bus > last_sub_bus) || + ((bus - first_bus) >= LBA_MAX_NUM_BUSES)) { + return 0; + } + + return 1; +} + + + +#define LBA_CFG_SETUP(d, tok) { \ + /* Save contents of error config register. */ \ + error_config = READ_REG32(d->hba.base_addr + LBA_ERROR_CONFIG); \ +\ + /* Save contents of status control register. */ \ + status_control = READ_REG32(d->hba.base_addr + LBA_STAT_CTL); \ +\ + /* For LBA rev 2.0, 2.1, 2.2, and 3.0, we must disable DMA \ + ** arbitration for full bus walks. \ + */ \ + /* Save contents of arb mask register. */ \ + arb_mask = READ_REG32(d->hba.base_addr + LBA_ARB_MASK); \ +\ + /* \ + * Turn off all device arbitration bits (i.e. everything \ + * except arbitration enable bit). \ + */ \ + WRITE_REG32(0x1, d->hba.base_addr + LBA_ARB_MASK); \ +\ + /* \ + * Set the smart mode bit so that master aborts don't cause \ + * LBA to go into PCI fatal mode (required). \ + */ \ + WRITE_REG32(error_config | LBA_SMART_MODE, d->hba.base_addr + LBA_ERROR_CONFIG); \ +} + + +#define LBA_CFG_PROBE(d, tok) { \ + /* \ + * Setup Vendor ID write and read back the address register \ + * to make sure that LBA is the bus master. \ + */ \ + WRITE_REG32(tok | PCI_VENDOR_ID, (d)->hba.base_addr + LBA_PCI_CFG_ADDR);\ + /* \ + * Read address register to ensure that LBA is the bus master, \ + * which implies that DMA traffic has stopped when DMA arb is off. \ + */ \ + lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ + /* \ + * Generate a cfg write cycle (will have no affect on \ + * Vendor ID register since read-only). \ + */ \ + WRITE_REG32(~0, (d)->hba.base_addr + LBA_PCI_CFG_DATA); \ + /* \ + * Make sure write has completed before proceeding further, \ + * i.e. before setting clear enable. \ + */ \ + lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ +} + + +/* + * HPREVISIT: + * -- Can't tell if config cycle got the error. + * + * OV bit is broken until rev 4.0, so can't use OV bit and + * LBA_ERROR_LOG_ADDR to tell if error belongs to config cycle. + * + * As of rev 4.0, no longer need the error check. + * + * -- Even if we could tell, we still want to return -1 + * for **ANY** error (not just master abort). + * + * -- Only clear non-fatal errors (we don't want to bring + * LBA out of pci-fatal mode). + * + * Actually, there is still a race in which + * we could be clearing a fatal error. We will + * live with this during our initial bus walk + * until rev 4.0 (no driver activity during + * initial bus walk). The initial bus walk + * has race conditions concerning the use of + * smart mode as well. + */ + +#define LBA_MASTER_ABORT_ERROR 0xc +#define LBA_FATAL_ERROR 0x10 + +#define LBA_CFG_MASTER_ABORT_CHECK(d, base, tok, error) { \ + u32 error_status = 0; \ + /* \ + * Set clear enable (CE) bit. Unset by HW when new \ + * errors are logged -- LBA HW ERS section 14.3.3). \ + */ \ + WRITE_REG32(status_control | CLEAR_ERRLOG_ENABLE, base + LBA_STAT_CTL); \ + error_status = READ_REG32(base + LBA_ERROR_STATUS); \ + if ((error_status & 0x1f) != 0) { \ + /* \ + * Fail the config read request. \ + */ \ + error = 1; \ + if ((error_status & LBA_FATAL_ERROR) == 0) { \ + /* \ + * Clear error status (if fatal bit not set) by setting \ + * clear error log bit (CL). \ + */ \ + WRITE_REG32(status_control | CLEAR_ERRLOG, base + LBA_STAT_CTL); \ + } \ + } \ +} + +#define LBA_CFG_TR4_ADDR_SETUP(d, addr) \ + WRITE_REG32(((addr) & ~3), (d)->hba.base_addr + LBA_PCI_CFG_ADDR); + +#define LBA_CFG_ADDR_SETUP(d, addr) { \ + WRITE_REG32(((addr) & ~3), (d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ + /* \ + * Read address register to ensure that LBA is the bus master, \ + * which implies that DMA traffic has stopped when DMA arb is off. \ + */ \ + lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ +} + + +#define LBA_CFG_RESTORE(d, base) { \ + /* \ + * Restore status control register (turn off clear enable). \ + */ \ + WRITE_REG32(status_control, base + LBA_STAT_CTL); \ + /* \ + * Restore error config register (turn off smart mode). \ + */ \ + WRITE_REG32(error_config, base + LBA_ERROR_CONFIG); \ + /* \ + * Restore arb mask register (reenables DMA arbitration). \ + */ \ + WRITE_REG32(arb_mask, base + LBA_ARB_MASK); \ +} + + + +static unsigned int +lba_rd_cfg(struct lba_device *d, u32 tok, u8 reg, u32 size) +{ + u32 data = ~0U; + int error = 0; + u32 arb_mask = 0; /* used by LBA_CFG_SETUP/RESTORE */ + u32 error_config = 0; /* used by LBA_CFG_SETUP/RESTORE */ + u32 status_control = 0; /* used by LBA_CFG_SETUP/RESTORE */ + + LBA_CFG_SETUP(d, tok); + LBA_CFG_PROBE(d, tok); + LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error); + if (!error) { + void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; + + LBA_CFG_ADDR_SETUP(d, tok | reg); + switch (size) { + case 1: data = (u32) READ_REG8(data_reg + (reg & 3)); break; + case 2: data = (u32) READ_REG16(data_reg+ (reg & 2)); break; + case 4: data = READ_REG32(data_reg); break; + } + } + LBA_CFG_RESTORE(d, d->hba.base_addr); + return(data); +} + + +static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data) +{ + struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; + u32 tok = LBA_CFG_TOK(local_bus, devfn); + void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; + + if ((pos > 255) || (devfn > 255)) + return -EINVAL; + +/* FIXME: B2K/C3600 workaround is always use old method... */ + /* if (!LBA_SKIP_PROBE(d)) */ { + /* original - Generate config cycle on broken elroy + with risk we will miss PCI bus errors. */ + *data = lba_rd_cfg(d, tok, pos, size); + DBG_CFG("%s(%x+%2x) -> 0x%x (a)\n", __func__, tok, pos, *data); + return 0; + } + + if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->busn_res.start, devfn, d)) { + DBG_CFG("%s(%x+%2x) -> -1 (b)\n", __func__, tok, pos); + /* either don't want to look or know device isn't present. */ + *data = ~0U; + return(0); + } + + /* Basic Algorithm + ** Should only get here on fully working LBA rev. + ** This is how simple the code should have been. + */ + LBA_CFG_ADDR_SETUP(d, tok | pos); + switch(size) { + case 1: *data = READ_REG8 (data_reg + (pos & 3)); break; + case 2: *data = READ_REG16(data_reg + (pos & 2)); break; + case 4: *data = READ_REG32(data_reg); break; + } + DBG_CFG("%s(%x+%2x) -> 0x%x (c)\n", __func__, tok, pos, *data); + return 0; +} + + +static void +lba_wr_cfg(struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size) +{ + int error __maybe_unused = 0; + u32 arb_mask = 0; + u32 error_config = 0; + u32 status_control = 0; + void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; + + LBA_CFG_SETUP(d, tok); + LBA_CFG_ADDR_SETUP(d, tok | reg); + switch (size) { + case 1: WRITE_REG8 (data, data_reg + (reg & 3)); break; + case 2: WRITE_REG16(data, data_reg + (reg & 2)); break; + case 4: WRITE_REG32(data, data_reg); break; + } + LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error); + LBA_CFG_RESTORE(d, d->hba.base_addr); +} + + +/* + * LBA 4.0 config write code implements non-postable semantics + * by doing a read of CONFIG ADDR after the write. + */ + +static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data) +{ + struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; + u32 tok = LBA_CFG_TOK(local_bus,devfn); + + if ((pos > 255) || (devfn > 255)) + return -EINVAL; + + if (!LBA_SKIP_PROBE(d)) { + /* Original Workaround */ + lba_wr_cfg(d, tok, pos, (u32) data, size); + DBG_CFG("%s(%x+%2x) = 0x%x (a)\n", __func__, tok, pos,data); + return 0; + } + + if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->busn_res.start, devfn, d))) { + DBG_CFG("%s(%x+%2x) = 0x%x (b)\n", __func__, tok, pos,data); + return 1; /* New Workaround */ + } + + DBG_CFG("%s(%x+%2x) = 0x%x (c)\n", __func__, tok, pos, data); + + /* Basic Algorithm */ + LBA_CFG_ADDR_SETUP(d, tok | pos); + switch(size) { + case 1: WRITE_REG8 (data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 3)); + break; + case 2: WRITE_REG16(data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 2)); + break; + case 4: WRITE_REG32(data, d->hba.base_addr + LBA_PCI_CFG_DATA); + break; + } + /* flush posted write */ + lba_t32 = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_ADDR); + return 0; +} + + +static struct pci_ops elroy_cfg_ops = { + .read = elroy_cfg_read, + .write = elroy_cfg_write, +}; + +/* + * The mercury_cfg_ops are slightly misnamed; they're also used for Elroy + * TR4.0 as no additional bugs were found in this areea between Elroy and + * Mercury + */ + +static int mercury_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data) +{ + struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; + u32 tok = LBA_CFG_TOK(local_bus, devfn); + void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; + + if ((pos > 255) || (devfn > 255)) + return -EINVAL; + + LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); + switch(size) { + case 1: + *data = READ_REG8(data_reg + (pos & 3)); + break; + case 2: + *data = READ_REG16(data_reg + (pos & 2)); + break; + case 4: + *data = READ_REG32(data_reg); break; + break; + } + + DBG_CFG("mercury_cfg_read(%x+%2x) -> 0x%x\n", tok, pos, *data); + return 0; +} + +/* + * LBA 4.0 config write code implements non-postable semantics + * by doing a read of CONFIG ADDR after the write. + */ + +static int mercury_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data) +{ + struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); + void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; + u32 tok = LBA_CFG_TOK(local_bus,devfn); + + if ((pos > 255) || (devfn > 255)) + return -EINVAL; + + DBG_CFG("%s(%x+%2x) <- 0x%x (c)\n", __func__, tok, pos, data); + + LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); + switch(size) { + case 1: + WRITE_REG8 (data, data_reg + (pos & 3)); + break; + case 2: + WRITE_REG16(data, data_reg + (pos & 2)); + break; + case 4: + WRITE_REG32(data, data_reg); + break; + } + + /* flush posted write */ + lba_t32 = READ_U32(d->hba.base_addr + LBA_PCI_CFG_ADDR); + return 0; +} + +static struct pci_ops mercury_cfg_ops = { + .read = mercury_cfg_read, + .write = mercury_cfg_write, +}; + + +static void +lba_bios_init(void) +{ + DBG(MODULE_NAME ": lba_bios_init\n"); +} + + +#ifdef CONFIG_64BIT + +/* + * truncate_pat_collision: Deal with overlaps or outright collisions + * between PAT PDC reported ranges. + * + * Broken PA8800 firmware will report lmmio range that + * overlaps with CPU HPA. Just truncate the lmmio range. + * + * BEWARE: conflicts with this lmmio range may be an + * elmmio range which is pointing down another rope. + * + * FIXME: only deals with one collision per range...theoretically we + * could have several. Supporting more than one collision will get messy. + */ +static unsigned long +truncate_pat_collision(struct resource *root, struct resource *new) +{ + unsigned long start = new->start; + unsigned long end = new->end; + struct resource *tmp = root->child; + + if (end <= start || start < root->start || !tmp) + return 0; + + /* find first overlap */ + while (tmp && tmp->end < start) + tmp = tmp->sibling; + + /* no entries overlap */ + if (!tmp) return 0; + + /* found one that starts behind the new one + ** Don't need to do anything. + */ + if (tmp->start >= end) return 0; + + if (tmp->start <= start) { + /* "front" of new one overlaps */ + new->start = tmp->end + 1; + + if (tmp->end >= end) { + /* AACCKK! totally overlaps! drop this range. */ + return 1; + } + } + + if (tmp->end < end ) { + /* "end" of new one overlaps */ + new->end = tmp->start - 1; + } + + printk(KERN_WARNING "LBA: Truncating lmmio_space [%lx/%lx] " + "to [%lx,%lx]\n", + start, end, + (long)new->start, (long)new->end ); + + return 0; /* truncation successful */ +} + +/* + * extend_lmmio_len: extend lmmio range to maximum length + * + * This is needed at least on C8000 systems to get the ATI FireGL card + * working. On other systems we will currently not extend the lmmio space. + */ +static unsigned long +extend_lmmio_len(unsigned long start, unsigned long end, unsigned long lba_len) +{ + struct resource *tmp; + + /* exit if not a C8000 */ + if (boot_cpu_data.cpu_type < mako) + return end; + + pr_debug("LMMIO mismatch: PAT length = 0x%lx, MASK register = 0x%lx\n", + end - start, lba_len); + + lba_len = min(lba_len+1, 256UL*1024*1024); /* limit to 256 MB */ + + pr_debug("LBA: lmmio_space [0x%lx-0x%lx] - original\n", start, end); + + + end += lba_len; + if (end < start) /* fix overflow */ + end = -1ULL; + + pr_debug("LBA: lmmio_space [0x%lx-0x%lx] - current\n", start, end); + + /* first overlap */ + for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) { + pr_debug("LBA: testing %pR\n", tmp); + if (tmp->start == start) + continue; /* ignore ourself */ + if (tmp->end < start) + continue; + if (tmp->start > end) + continue; + if (end >= tmp->start) + end = tmp->start - 1; + } + + pr_info("LBA: lmmio_space [0x%lx-0x%lx] - new\n", start, end); + + /* return new end */ + return end; +} + +#else +#define truncate_pat_collision(r,n) (0) +#endif + +static void pcibios_allocate_bridge_resources(struct pci_dev *dev) +{ + int idx; + struct resource *r; + + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + if (r->parent) /* Already allocated */ + continue; + if (!r->start || pci_claim_bridge_resource(dev, idx) < 0) { + /* + * Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. + */ + r->start = r->end = 0; + r->flags = 0; + } + } +} + +static void pcibios_allocate_bus_resources(struct pci_bus *bus) +{ + struct pci_bus *child; + + /* Depth-First Search on bus tree */ + if (bus->self) + pcibios_allocate_bridge_resources(bus->self); + list_for_each_entry(child, &bus->children, node) + pcibios_allocate_bus_resources(child); +} + + +/* +** The algorithm is generic code. +** But it needs to access local data structures to get the IRQ base. +** Could make this a "pci_fixup_irq(bus, region)" but not sure +** it's worth it. +** +** Called by do_pci_scan_bus() immediately after each PCI bus is walked. +** Resources aren't allocated until recursive buswalk below HBA is completed. +*/ +static void +lba_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev; +#ifdef FBB_SUPPORT + u16 status; +#endif + struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge)); + + DBG("lba_fixup_bus(0x%p) bus %d platform_data 0x%p\n", + bus, (int)bus->busn_res.start, bus->bridge->platform_data); + + /* + ** Properly Setup MMIO resources for this bus. + ** pci_alloc_primary_bus() mangles this. + */ + if (bus->parent) { + /* PCI-PCI Bridge */ + pci_read_bridge_bases(bus); + + /* check and allocate bridge resources */ + pcibios_allocate_bus_resources(bus); + } else { + /* Host-PCI Bridge */ + int err; + + DBG("lba_fixup_bus() %s [%lx/%lx]/%lx\n", + ldev->hba.io_space.name, + ldev->hba.io_space.start, ldev->hba.io_space.end, + ldev->hba.io_space.flags); + DBG("lba_fixup_bus() %s [%lx/%lx]/%lx\n", + ldev->hba.lmmio_space.name, + ldev->hba.lmmio_space.start, ldev->hba.lmmio_space.end, + ldev->hba.lmmio_space.flags); + + err = request_resource(&ioport_resource, &(ldev->hba.io_space)); + if (err < 0) { + lba_dump_res(&ioport_resource, 2); + BUG(); + } + + if (ldev->hba.elmmio_space.flags) { + err = request_resource(&iomem_resource, + &(ldev->hba.elmmio_space)); + if (err < 0) { + + printk("FAILED: lba_fixup_bus() request for " + "elmmio_space [%lx/%lx]\n", + (long)ldev->hba.elmmio_space.start, + (long)ldev->hba.elmmio_space.end); + + /* lba_dump_res(&iomem_resource, 2); */ + /* BUG(); */ + } + } + + if (ldev->hba.lmmio_space.flags) { + err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space)); + if (err < 0) { + printk(KERN_ERR "FAILED: lba_fixup_bus() request for " + "lmmio_space [%lx/%lx]\n", + (long)ldev->hba.lmmio_space.start, + (long)ldev->hba.lmmio_space.end); + } + } + +#ifdef CONFIG_64BIT + /* GMMIO is distributed range. Every LBA/Rope gets part it. */ + if (ldev->hba.gmmio_space.flags) { + err = request_resource(&iomem_resource, &(ldev->hba.gmmio_space)); + if (err < 0) { + printk("FAILED: lba_fixup_bus() request for " + "gmmio_space [%lx/%lx]\n", + (long)ldev->hba.gmmio_space.start, + (long)ldev->hba.gmmio_space.end); + lba_dump_res(&iomem_resource, 2); + BUG(); + } + } +#endif + + } + + list_for_each_entry(dev, &bus->devices, bus_list) { + int i; + + DBG("lba_fixup_bus() %s\n", pci_name(dev)); + + /* Virtualize Device/Bridge Resources. */ + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { + struct resource *res = &dev->resource[i]; + + /* If resource not allocated - skip it */ + if (!res->start) + continue; + + /* + ** FIXME: this will result in whinging for devices + ** that share expansion ROMs (think quad tulip), but + ** isn't harmful. + */ + pci_claim_resource(dev, i); + } + +#ifdef FBB_SUPPORT + /* + ** If one device does not support FBB transfers, + ** No one on the bus can be allowed to use them. + */ + (void) pci_read_config_word(dev, PCI_STATUS, &status); + bus->bridge_ctl &= ~(status & PCI_STATUS_FAST_BACK); +#endif + + /* + ** P2PB's have no IRQs. ignore them. + */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + pcibios_init_bridge(dev); + continue; + } + + /* Adjust INTERRUPT_LINE for this dev */ + iosapic_fixup_irq(ldev->iosapic_obj, dev); + } + +#ifdef FBB_SUPPORT +/* FIXME/REVISIT - finish figuring out to set FBB on both +** pci_setup_bridge() clobbers PCI_BRIDGE_CONTROL. +** Can't fixup here anyway....garr... +*/ + if (fbb_enable) { + if (bus->parent) { + u8 control; + /* enable on PPB */ + (void) pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &control); + (void) pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, control | PCI_STATUS_FAST_BACK); + + } else { + /* enable on LBA */ + } + fbb_enable = PCI_COMMAND_FAST_BACK; + } + + /* Lastly enable FBB/PERR/SERR on all devices too */ + list_for_each_entry(dev, &bus->devices, bus_list) { + (void) pci_read_config_word(dev, PCI_COMMAND, &status); + status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable; + (void) pci_write_config_word(dev, PCI_COMMAND, status); + } +#endif +} + + +static struct pci_bios_ops lba_bios_ops = { + .init = lba_bios_init, + .fixup_bus = lba_fixup_bus, +}; + + + + +/******************************************************* +** +** LBA Sprockets "I/O Port" Space Accessor Functions +** +** This set of accessor functions is intended for use with +** "legacy firmware" (ie Sprockets on Allegro/Forte boxes). +** +** Many PCI devices don't require use of I/O port space (eg Tulip, +** NCR720) since they export the same registers to both MMIO and +** I/O port space. In general I/O port space is slower than +** MMIO since drivers are designed so PIO writes can be posted. +** +********************************************************/ + +#define LBA_PORT_IN(size, mask) \ +static u##size lba_astro_in##size (struct pci_hba_data *d, u16 addr) \ +{ \ + u##size t; \ + t = READ_REG##size(astro_iop_base + addr); \ + DBG_PORT(" 0x%x\n", t); \ + return (t); \ +} + +LBA_PORT_IN( 8, 3) +LBA_PORT_IN(16, 2) +LBA_PORT_IN(32, 0) + + + +/* +** BUG X4107: Ordering broken - DMA RD return can bypass PIO WR +** +** Fixed in Elroy 2.2. The READ_U32(..., LBA_FUNC_ID) below is +** guarantee non-postable completion semantics - not avoid X4107. +** The READ_U32 only guarantees the write data gets to elroy but +** out to the PCI bus. We can't read stuff from I/O port space +** since we don't know what has side-effects. Attempting to read +** from configuration space would be suicidal given the number of +** bugs in that elroy functionality. +** +** Description: +** DMA read results can improperly pass PIO writes (X4107). The +** result of this bug is that if a processor modifies a location in +** memory after having issued PIO writes, the PIO writes are not +** guaranteed to be completed before a PCI device is allowed to see +** the modified data in a DMA read. +** +** Note that IKE bug X3719 in TR1 IKEs will result in the same +** symptom. +** +** Workaround: +** The workaround for this bug is to always follow a PIO write with +** a PIO read to the same bus before starting DMA on that PCI bus. +** +*/ +#define LBA_PORT_OUT(size, mask) \ +static void lba_astro_out##size (struct pci_hba_data *d, u16 addr, u##size val) \ +{ \ + DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __func__, d, addr, val); \ + WRITE_REG##size(val, astro_iop_base + addr); \ + if (LBA_DEV(d)->hw_rev < 3) \ + lba_t32 = READ_U32(d->base_addr + LBA_FUNC_ID); \ +} + +LBA_PORT_OUT( 8, 3) +LBA_PORT_OUT(16, 2) +LBA_PORT_OUT(32, 0) + + +static struct pci_port_ops lba_astro_port_ops = { + .inb = lba_astro_in8, + .inw = lba_astro_in16, + .inl = lba_astro_in32, + .outb = lba_astro_out8, + .outw = lba_astro_out16, + .outl = lba_astro_out32 +}; + + +#ifdef CONFIG_64BIT +#define PIOP_TO_GMMIO(lba, addr) \ + ((lba)->iop_base + (((addr)&0xFFFC)<<10) + ((addr)&3)) + +/******************************************************* +** +** LBA PAT "I/O Port" Space Accessor Functions +** +** This set of accessor functions is intended for use with +** "PAT PDC" firmware (ie Prelude/Rhapsody/Piranha boxes). +** +** This uses the PIOP space located in the first 64MB of GMMIO. +** Each rope gets a full 64*KB* (ie 4 bytes per page) this way. +** bits 1:0 stay the same. bits 15:2 become 25:12. +** Then add the base and we can generate an I/O Port cycle. +********************************************************/ +#undef LBA_PORT_IN +#define LBA_PORT_IN(size, mask) \ +static u##size lba_pat_in##size (struct pci_hba_data *l, u16 addr) \ +{ \ + u##size t; \ + DBG_PORT("%s(0x%p, 0x%x) ->", __func__, l, addr); \ + t = READ_REG##size(PIOP_TO_GMMIO(LBA_DEV(l), addr)); \ + DBG_PORT(" 0x%x\n", t); \ + return (t); \ +} + +LBA_PORT_IN( 8, 3) +LBA_PORT_IN(16, 2) +LBA_PORT_IN(32, 0) + + +#undef LBA_PORT_OUT +#define LBA_PORT_OUT(size, mask) \ +static void lba_pat_out##size (struct pci_hba_data *l, u16 addr, u##size val) \ +{ \ + void __iomem *where = PIOP_TO_GMMIO(LBA_DEV(l), addr); \ + DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __func__, l, addr, val); \ + WRITE_REG##size(val, where); \ + /* flush the I/O down to the elroy at least */ \ + lba_t32 = READ_U32(l->base_addr + LBA_FUNC_ID); \ +} + +LBA_PORT_OUT( 8, 3) +LBA_PORT_OUT(16, 2) +LBA_PORT_OUT(32, 0) + + +static struct pci_port_ops lba_pat_port_ops = { + .inb = lba_pat_in8, + .inw = lba_pat_in16, + .inl = lba_pat_in32, + .outb = lba_pat_out8, + .outw = lba_pat_out16, + .outl = lba_pat_out32 +}; + + + +/* +** make range information from PDC available to PCI subsystem. +** We make the PDC call here in order to get the PCI bus range +** numbers. The rest will get forwarded in pcibios_fixup_bus(). +** We don't have a struct pci_bus assigned to us yet. +*/ +static void +lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) +{ + unsigned long bytecnt; + long io_count __maybe_unused; + long status; /* PDC return status */ + long pa_count; + pdc_pat_cell_mod_maddr_block_t *pa_pdc_cell; /* PA_VIEW */ + pdc_pat_cell_mod_maddr_block_t *io_pdc_cell; /* IO_VIEW */ + int i; + + pa_pdc_cell = kzalloc_obj(pdc_pat_cell_mod_maddr_block_t); + if (!pa_pdc_cell) + return; + + io_pdc_cell = kzalloc_obj(pdc_pat_cell_mod_maddr_block_t); + if (!io_pdc_cell) { + kfree(pa_pdc_cell); + return; + } + + /* return cell module (IO view) */ + status = pdc_pat_cell_module(&bytecnt, pa_dev->pcell_loc, pa_dev->mod_index, + PA_VIEW, pa_pdc_cell); + pa_count = pa_pdc_cell->mod[1]; + + status |= pdc_pat_cell_module(&bytecnt, pa_dev->pcell_loc, pa_dev->mod_index, + IO_VIEW, io_pdc_cell); + io_count = io_pdc_cell->mod[1]; + + /* We've already done this once for device discovery...*/ + if (status != PDC_OK) { + panic("pdc_pat_cell_module() call failed for LBA!\n"); + } + + if (PAT_GET_ENTITY(pa_pdc_cell->mod_info) != PAT_ENTITY_LBA) { + panic("pdc_pat_cell_module() entity returned != PAT_ENTITY_LBA!\n"); + } + + /* + ** Inspect the resources PAT tells us about + */ + for (i = 0; i < pa_count; i++) { + struct { + unsigned long type; + unsigned long start; + unsigned long end; /* aka finish */ + } *p, *io; + struct resource *r; + + p = (void *) &(pa_pdc_cell->mod[2+i*3]); + io = (void *) &(io_pdc_cell->mod[2+i*3]); + DBG_PAT("PA #%d : TYPE 0x%08lx, start 0x%08lx, end 0x%08lx\n", + i, p->type, p->start, p->end); + DBG_PAT("IO #%d : TYPE 0x%08lx, start 0x%08lx, end 0x%08lx\n", + i, io->type, io->start, io->end); + + /* Convert the PAT range data to PCI "struct resource" */ + switch(p->type & 0xff) { + case PAT_PBNUM: + lba_dev->hba.bus_num.start = p->start; + lba_dev->hba.bus_num.end = p->end; + lba_dev->hba.bus_num.flags = IORESOURCE_BUS; + break; + + case PAT_LMMIO: + /* used to fix up pre-initialized MEM BARs */ + if (!lba_dev->hba.lmmio_space.flags) { + unsigned long lba_len; + + lba_len = ~READ_REG32(lba_dev->hba.base_addr + + LBA_LMMIO_MASK); + if ((p->end - p->start) != lba_len) + p->end = extend_lmmio_len(p->start, + p->end, lba_len); + + sprintf(lba_dev->hba.lmmio_name, + "PCI%02x LMMIO", + (int)lba_dev->hba.bus_num.start); + lba_dev->hba.lmmio_space_offset = p->start - + io->start; + r = &lba_dev->hba.lmmio_space; + r->name = lba_dev->hba.lmmio_name; + } else if (!lba_dev->hba.elmmio_space.flags) { + sprintf(lba_dev->hba.elmmio_name, + "PCI%02x ELMMIO", + (int)lba_dev->hba.bus_num.start); + r = &lba_dev->hba.elmmio_space; + r->name = lba_dev->hba.elmmio_name; + } else { + printk(KERN_WARNING MODULE_NAME + " only supports 2 LMMIO resources!\n"); + break; + } + + r->start = p->start; + r->end = p->end; + r->flags = IORESOURCE_MEM; + r->parent = r->sibling = r->child = NULL; + break; + + case PAT_GMMIO: + /* MMIO space > 4GB phys addr; for 64-bit BAR */ + sprintf(lba_dev->hba.gmmio_name, "PCI%02x GMMIO", + (int)lba_dev->hba.bus_num.start); + r = &lba_dev->hba.gmmio_space; + r->name = lba_dev->hba.gmmio_name; + r->start = p->start; + r->end = p->end; + r->flags = IORESOURCE_MEM; + r->parent = r->sibling = r->child = NULL; + break; + + case PAT_NPIOP: + printk(KERN_WARNING MODULE_NAME + " range[%d] : ignoring NPIOP (0x%lx)\n", + i, p->start); + break; + + case PAT_PIOP: + /* + ** Postable I/O port space is per PCI host adapter. + ** base of 64MB PIOP region + */ + lba_dev->iop_base = ioremap(p->start, 64 * 1024 * 1024); + + sprintf(lba_dev->hba.io_name, "PCI%02x Ports", + (int)lba_dev->hba.bus_num.start); + r = &lba_dev->hba.io_space; + r->name = lba_dev->hba.io_name; + r->start = HBA_PORT_BASE(lba_dev->hba.hba_num); + r->end = r->start + HBA_PORT_SPACE_SIZE - 1; + r->flags = IORESOURCE_IO; + r->parent = r->sibling = r->child = NULL; + break; + + default: + printk(KERN_WARNING MODULE_NAME + " range[%d] : unknown pat range type (0x%lx)\n", + i, p->type & 0xff); + break; + } + } + + kfree(pa_pdc_cell); + kfree(io_pdc_cell); +} +#else +/* keep compiler from complaining about missing declarations */ +#define lba_pat_port_ops lba_astro_port_ops +#define lba_pat_resources(pa_dev, lba_dev) +#endif /* CONFIG_64BIT */ + + +static void +lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) +{ + struct resource *r; + int lba_num; + + lba_dev->hba.lmmio_space_offset = PCI_F_EXTEND; + + /* + ** With "legacy" firmware, the lowest byte of FW_SCRATCH + ** represents bus->secondary and the second byte represents + ** bus->subsidiary (i.e. highest PPB programmed by firmware). + ** PCI bus walk *should* end up with the same result. + ** FIXME: But we don't have sanity checks in PCI or LBA. + */ + lba_num = READ_REG32(lba_dev->hba.base_addr + LBA_FW_SCRATCH); + r = &(lba_dev->hba.bus_num); + r->name = "LBA PCI Busses"; + r->start = lba_num & 0xff; + r->end = (lba_num>>8) & 0xff; + r->flags = IORESOURCE_BUS; + + /* Set up local PCI Bus resources - we don't need them for + ** Legacy boxes but it's nice to see in /proc/iomem. + */ + r = &(lba_dev->hba.lmmio_space); + sprintf(lba_dev->hba.lmmio_name, "PCI%02x LMMIO", + (int)lba_dev->hba.bus_num.start); + r->name = lba_dev->hba.lmmio_name; + +#if 1 + /* We want the CPU -> IO routing of addresses. + * The SBA BASE/MASK registers control CPU -> IO routing. + * Ask SBA what is routed to this rope/LBA. + */ + sba_distributed_lmmio(pa_dev, r); +#else + /* + * The LBA BASE/MASK registers control IO -> System routing. + * + * The following code works but doesn't get us what we want. + * Well, only because firmware (v5.0) on C3000 doesn't program + * the LBA BASE/MASE registers to be the exact inverse of + * the corresponding SBA registers. Other Astro/Pluto + * based platform firmware may do it right. + * + * Should someone want to mess with MSI, they may need to + * reprogram LBA BASE/MASK registers. Thus preserve the code + * below until MSI is known to work on C3000/A500/N4000/RP3440. + * + * Using the code below, /proc/iomem shows: + * ... + * f0000000-f0ffffff : PCI00 LMMIO + * f05d0000-f05d0000 : lcd_data + * f05d0008-f05d0008 : lcd_cmd + * f1000000-f1ffffff : PCI01 LMMIO + * f4000000-f4ffffff : PCI02 LMMIO + * f4000000-f4001fff : sym53c8xx + * f4002000-f4003fff : sym53c8xx + * f4004000-f40043ff : sym53c8xx + * f4005000-f40053ff : sym53c8xx + * f4007000-f4007fff : ohci_hcd + * f4008000-f40083ff : tulip + * f6000000-f6ffffff : PCI03 LMMIO + * f8000000-fbffffff : PCI00 ELMMIO + * fa100000-fa4fffff : stifb mmio + * fb000000-fb1fffff : stifb fb + * + * But everything listed under PCI02 actually lives under PCI00. + * This is clearly wrong. + * + * Asking SBA how things are routed tells the correct story: + * LMMIO_BASE/MASK/ROUTE f4000001 fc000000 00000000 + * DIR0_BASE/MASK/ROUTE fa000001 fe000000 00000006 + * DIR1_BASE/MASK/ROUTE f9000001 ff000000 00000004 + * DIR2_BASE/MASK/ROUTE f0000000 fc000000 00000000 + * DIR3_BASE/MASK/ROUTE f0000000 fc000000 00000000 + * + * Which looks like this in /proc/iomem: + * f4000000-f47fffff : PCI00 LMMIO + * f4000000-f4001fff : sym53c8xx + * ...[deteled core devices - same as above]... + * f4008000-f40083ff : tulip + * f4800000-f4ffffff : PCI01 LMMIO + * f6000000-f67fffff : PCI02 LMMIO + * f7000000-f77fffff : PCI03 LMMIO + * f9000000-f9ffffff : PCI02 ELMMIO + * fa000000-fbffffff : PCI03 ELMMIO + * fa100000-fa4fffff : stifb mmio + * fb000000-fb1fffff : stifb fb + * + * ie all Built-in core are under now correctly under PCI00. + * The "PCI02 ELMMIO" directed range is for: + * +-[02]---03.0 3Dfx Interactive, Inc. Voodoo 2 + * + * All is well now. + */ + r->start = READ_REG32(lba_dev->hba.base_addr + LBA_LMMIO_BASE); + if (r->start & 1) { + unsigned long rsize; + + r->flags = IORESOURCE_MEM; + /* mmio_mask also clears Enable bit */ + r->start &= mmio_mask; + r->start = PCI_HOST_ADDR(&lba_dev->hba, r->start); + rsize = ~ READ_REG32(lba_dev->hba.base_addr + LBA_LMMIO_MASK); + + /* + ** Each rope only gets part of the distributed range. + ** Adjust "window" for this rope. + */ + rsize /= ROPES_PER_IOC; + r->start += (rsize + 1) * LBA_NUM(pa_dev->hpa.start); + r->end = r->start + rsize; + } else { + r->end = r->start = 0; /* Not enabled. */ + } +#endif + + /* + ** "Directed" ranges are used when the "distributed range" isn't + ** sufficient for all devices below a given LBA. Typically devices + ** like graphics cards or X25 may need a directed range when the + ** bus has multiple slots (ie multiple devices) or the device + ** needs more than the typical 4 or 8MB a distributed range offers. + ** + ** The main reason for ignoring it now frigging complications. + ** Directed ranges may overlap (and have precedence) over + ** distributed ranges. Or a distributed range assigned to a unused + ** rope may be used by a directed range on a different rope. + ** Support for graphics devices may require fixing this + ** since they may be assigned a directed range which overlaps + ** an existing (but unused portion of) distributed range. + */ + r = &(lba_dev->hba.elmmio_space); + sprintf(lba_dev->hba.elmmio_name, "PCI%02x ELMMIO", + (int)lba_dev->hba.bus_num.start); + r->name = lba_dev->hba.elmmio_name; + +#if 1 + /* See comment which precedes call to sba_directed_lmmio() */ + sba_directed_lmmio(pa_dev, r); +#else + r->start = READ_REG32(lba_dev->hba.base_addr + LBA_ELMMIO_BASE); + + if (r->start & 1) { + unsigned long rsize; + r->flags = IORESOURCE_MEM; + /* mmio_mask also clears Enable bit */ + r->start &= mmio_mask; + r->start = PCI_HOST_ADDR(&lba_dev->hba, r->start); + rsize = READ_REG32(lba_dev->hba.base_addr + LBA_ELMMIO_MASK); + r->end = r->start + ~rsize; + } +#endif + + r = &(lba_dev->hba.io_space); + sprintf(lba_dev->hba.io_name, "PCI%02x Ports", + (int)lba_dev->hba.bus_num.start); + r->name = lba_dev->hba.io_name; + r->flags = IORESOURCE_IO; + r->start = READ_REG32(lba_dev->hba.base_addr + LBA_IOS_BASE) & ~1L; + r->end = r->start + (READ_REG32(lba_dev->hba.base_addr + LBA_IOS_MASK) ^ (HBA_PORT_SPACE_SIZE - 1)); + + /* Virtualize the I/O Port space ranges */ + lba_num = HBA_PORT_BASE(lba_dev->hba.hba_num); + r->start |= lba_num; + r->end |= lba_num; +} + + +/************************************************************************** +** +** LBA initialization code (HW and SW) +** +** o identify LBA chip itself +** o initialize LBA chip modes (HardFail) +** o FIXME: initialize DMA hints for reasonable defaults +** o enable configuration functions +** o call pci_register_ops() to discover devs (fixup/fixup_bus get invoked) +** +**************************************************************************/ + +static int __init +lba_hw_init(struct lba_device *d) +{ + u32 stat; + u32 bus_reset; /* PDC_PAT_BUG */ + +#if 0 + printk(KERN_DEBUG "LBA %lx STAT_CTL %Lx ERROR_CFG %Lx STATUS %Lx DMA_CTL %Lx\n", + d->hba.base_addr, + READ_REG64(d->hba.base_addr + LBA_STAT_CTL), + READ_REG64(d->hba.base_addr + LBA_ERROR_CONFIG), + READ_REG64(d->hba.base_addr + LBA_ERROR_STATUS), + READ_REG64(d->hba.base_addr + LBA_DMA_CTL) ); + printk(KERN_DEBUG " ARB mask %Lx pri %Lx mode %Lx mtlt %Lx\n", + READ_REG64(d->hba.base_addr + LBA_ARB_MASK), + READ_REG64(d->hba.base_addr + LBA_ARB_PRI), + READ_REG64(d->hba.base_addr + LBA_ARB_MODE), + READ_REG64(d->hba.base_addr + LBA_ARB_MTLT) ); + printk(KERN_DEBUG " HINT cfg 0x%Lx\n", + READ_REG64(d->hba.base_addr + LBA_HINT_CFG)); + printk(KERN_DEBUG " HINT reg "); + { int i; + for (i=LBA_HINT_BASE; i< (14*8 + LBA_HINT_BASE); i+=8) + printk(" %Lx", READ_REG64(d->hba.base_addr + i)); + } + printk("\n"); +#endif /* DEBUG_LBA_PAT */ + +#ifdef CONFIG_64BIT +/* + * FIXME add support for PDC_PAT_IO "Get slot status" - OLAR support + * Only N-Class and up can really make use of Get slot status. + * maybe L-class too but I've never played with it there. + */ +#endif + + /* PDC_PAT_BUG: exhibited in rev 40.48 on L2000 */ + bus_reset = READ_REG32(d->hba.base_addr + LBA_STAT_CTL + 4) & 1; + if (bus_reset) { + printk(KERN_DEBUG "NOTICE: PCI bus reset still asserted! (clearing)\n"); + } + + stat = READ_REG32(d->hba.base_addr + LBA_ERROR_CONFIG); + if (stat & LBA_SMART_MODE) { + printk(KERN_DEBUG "NOTICE: LBA in SMART mode! (cleared)\n"); + stat &= ~LBA_SMART_MODE; + WRITE_REG32(stat, d->hba.base_addr + LBA_ERROR_CONFIG); + } + + + /* + * Hard Fail vs. Soft Fail on PCI "Master Abort". + * + * "Master Abort" means the MMIO transaction timed out - usually due to + * the device not responding to an MMIO read. We would like HF to be + * enabled to find driver problems, though it means the system will + * crash with a HPMC. + * + * In SoftFail mode "~0L" is returned as a result of a timeout on the + * pci bus. This is like how PCI busses on x86 and most other + * architectures behave. In order to increase compatibility with + * existing (x86) PCI hardware and existing Linux drivers we enable + * Soft Faul mode on PA-RISC now too. + */ + stat = READ_REG32(d->hba.base_addr + LBA_STAT_CTL); +#if defined(ENABLE_HARDFAIL) + WRITE_REG32(stat | HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL); +#else + WRITE_REG32(stat & ~HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL); +#endif + + /* + ** Writing a zero to STAT_CTL.rf (bit 0) will clear reset signal + ** if it's not already set. If we just cleared the PCI Bus Reset + ** signal, wait a bit for the PCI devices to recover and setup. + */ + if (bus_reset) + mdelay(pci_post_reset_delay); + + if (0 == READ_REG32(d->hba.base_addr + LBA_ARB_MASK)) { + /* + ** PDC_PAT_BUG: PDC rev 40.48 on L2000. + ** B2000/C3600/J6000 also have this problem? + ** + ** Elroys with hot pluggable slots don't get configured + ** correctly if the slot is empty. ARB_MASK is set to 0 + ** and we can't master transactions on the bus if it's + ** not at least one. 0x3 enables elroy and first slot. + */ + printk(KERN_DEBUG "NOTICE: Enabling PCI Arbitration\n"); + WRITE_REG32(0x3, d->hba.base_addr + LBA_ARB_MASK); + } + + /* + ** FIXME: Hint registers are programmed with default hint + ** values by firmware. Hints should be sane even if we + ** can't reprogram them the way drivers want. + */ + return 0; +} + +/* + * Unfortunately, when firmware numbers busses, it doesn't take into account + * Cardbus bridges. So we have to renumber the busses to suit ourselves. + * Elroy/Mercury don't actually know what bus number they're attached to; + * we use bus 0 to indicate the directly attached bus and any other bus + * number will be taken care of by the PCI-PCI bridge. + */ +static unsigned int lba_next_bus = 0; + +/* + * Determine if lba should claim this chip (return 0) or not (return 1). + * If so, initialize the chip and tell other partners in crime they + * have work to do. + */ +static int __init +lba_driver_probe(struct parisc_device *dev) +{ + struct lba_device *lba_dev; + LIST_HEAD(resources); + struct pci_bus *lba_bus; + struct pci_ops *cfg_ops; + u32 func_class; + void *tmp_obj; + char *version; + void __iomem *addr; + int max; + + addr = ioremap(dev->hpa.start, 4096); + if (addr == NULL) + return -ENOMEM; + + /* Read HW Rev First */ + func_class = READ_REG32(addr + LBA_FCLASS); + + if (IS_ELROY(dev)) { + func_class &= 0xf; + switch (func_class) { + case 0: version = "TR1.0"; break; + case 1: version = "TR2.0"; break; + case 2: version = "TR2.1"; break; + case 3: version = "TR2.2"; break; + case 4: version = "TR3.0"; break; + case 5: version = "TR4.0"; break; + default: version = "TR4+"; + } + + printk(KERN_INFO "Elroy version %s (0x%x) found at 0x%lx\n", + version, func_class & 0xf, (long)dev->hpa.start); + + if (func_class < 2) { + printk(KERN_WARNING "Can't support LBA older than " + "TR2.1 - continuing under adversity.\n"); + } + +#if 0 +/* Elroy TR4.0 should work with simple algorithm. + But it doesn't. Still missing something. *sigh* +*/ + if (func_class > 4) { + cfg_ops = &mercury_cfg_ops; + } else +#endif + { + cfg_ops = &elroy_cfg_ops; + } + + } else if (IS_MERCURY(dev) || IS_QUICKSILVER(dev)) { + int major, minor; + + func_class &= 0xff; + major = func_class >> 4, minor = func_class & 0xf; + + /* We could use one printk for both Elroy and Mercury, + * but for the mask for func_class. + */ + printk(KERN_INFO "%s version TR%d.%d (0x%x) found at 0x%lx\n", + IS_MERCURY(dev) ? "Mercury" : "Quicksilver", major, + minor, func_class, (long)dev->hpa.start); + + cfg_ops = &mercury_cfg_ops; + } else { + printk(KERN_ERR "Unknown LBA found at 0x%lx\n", + (long)dev->hpa.start); + return -ENODEV; + } + + /* Tell I/O SAPIC driver we have a IRQ handler/region. */ + tmp_obj = iosapic_register(dev->hpa.start + LBA_IOSAPIC_BASE, + addr + LBA_IOSAPIC_BASE); + + /* NOTE: PCI devices (e.g. 103c:1005 graphics card) which don't + ** have an IRT entry will get NULL back from iosapic code. + */ + + lba_dev = kzalloc_obj(struct lba_device); + if (!lba_dev) { + printk(KERN_ERR "lba_init_chip - couldn't alloc lba_device\n"); + return(1); + } + + + /* ---------- First : initialize data we already have --------- */ + + lba_dev->hw_rev = func_class; + lba_dev->hba.base_addr = addr; + lba_dev->hba.dev = dev; + lba_dev->iosapic_obj = tmp_obj; /* save interrupt handle */ + lba_dev->hba.iommu = sba_get_iommu(dev); /* get iommu data */ + parisc_set_drvdata(dev, lba_dev); + + /* ------------ Second : initialize common stuff ---------- */ + pci_bios = &lba_bios_ops; + pcibios_register_hba(&lba_dev->hba); + spin_lock_init(&lba_dev->lba_lock); + + if (lba_hw_init(lba_dev)) + return(1); + + /* ---------- Third : setup I/O Port and MMIO resources --------- */ + + if (is_pdc_pat()) { + /* PDC PAT firmware uses PIOP region of GMMIO space. */ + pci_port = &lba_pat_port_ops; + /* Go ask PDC PAT what resources this LBA has */ + lba_pat_resources(dev, lba_dev); + } else { + if (!astro_iop_base) { + /* Sprockets PDC uses NPIOP region */ + astro_iop_base = ioremap(LBA_PORT_BASE, 64 * 1024); + pci_port = &lba_astro_port_ops; + } + + /* Poke the chip a bit for /proc output */ + lba_legacy_resources(dev, lba_dev); + } + + if (lba_dev->hba.bus_num.start < lba_next_bus) + lba_dev->hba.bus_num.start = lba_next_bus; + + /* Overlaps with elmmio can (and should) fail here. + * We will prune (or ignore) the distributed range. + * + * FIXME: SBA code should register all elmmio ranges first. + * that would take care of elmmio ranges routed + * to a different rope (already discovered) from + * getting registered *after* LBA code has already + * registered it's distributed lmmio range. + */ + if (truncate_pat_collision(&iomem_resource, + &(lba_dev->hba.lmmio_space))) { + printk(KERN_WARNING "LBA: lmmio_space [%lx/%lx] duplicate!\n", + (long)lba_dev->hba.lmmio_space.start, + (long)lba_dev->hba.lmmio_space.end); + lba_dev->hba.lmmio_space.flags = 0; + } + + pci_add_resource_offset(&resources, &lba_dev->hba.io_space, + HBA_PORT_BASE(lba_dev->hba.hba_num)); + if (lba_dev->hba.elmmio_space.flags) + pci_add_resource_offset(&resources, &lba_dev->hba.elmmio_space, + lba_dev->hba.lmmio_space_offset); + if (lba_dev->hba.lmmio_space.flags) + pci_add_resource_offset(&resources, &lba_dev->hba.lmmio_space, + lba_dev->hba.lmmio_space_offset); + if (lba_dev->hba.gmmio_space.flags) { + /* Not registering GMMIO space - according to docs it's not + * even used on HP-UX. */ + /* pci_add_resource(&resources, &lba_dev->hba.gmmio_space); */ + } + + pci_add_resource(&resources, &lba_dev->hba.bus_num); + + dev->dev.platform_data = lba_dev; + lba_bus = lba_dev->hba.hba_bus = + pci_create_root_bus(&dev->dev, lba_dev->hba.bus_num.start, + cfg_ops, NULL, &resources); + if (!lba_bus) { + pci_free_resource_list(&resources); + return 0; + } + + max = pci_scan_child_bus(lba_bus); + + /* This is in lieu of calling pci_assign_unassigned_resources() */ + if (is_pdc_pat()) { + /* assign resources to un-initialized devices */ + + DBG_PAT("LBA pci_bus_size_bridges()\n"); + pci_bus_size_bridges(lba_bus); + + DBG_PAT("LBA pci_bus_assign_resources()\n"); + pci_bus_assign_resources(lba_bus); + +#ifdef DEBUG_LBA_PAT + DBG_PAT("\nLBA PIOP resource tree\n"); + lba_dump_res(&lba_dev->hba.io_space, 2); + DBG_PAT("\nLBA LMMIO resource tree\n"); + lba_dump_res(&lba_dev->hba.lmmio_space, 2); +#endif + } + + /* + ** Once PCI register ops has walked the bus, access to config + ** space is restricted. Avoids master aborts on config cycles. + ** Early LBA revs go fatal on *any* master abort. + */ + if (cfg_ops == &elroy_cfg_ops) { + lba_dev->flags |= LBA_FLAG_SKIP_PROBE; + } + + lba_next_bus = max + 1; + pci_bus_add_devices(lba_bus); + + /* Whew! Finally done! Tell services we got this one covered. */ + return 0; +} + +static const struct parisc_device_id lba_tbl[] __initconst = { + { HPHW_BRIDGE, HVERSION_REV_ANY_ID, ELROY_HVERS, 0xa }, + { HPHW_BRIDGE, HVERSION_REV_ANY_ID, MERCURY_HVERS, 0xa }, + { HPHW_BRIDGE, HVERSION_REV_ANY_ID, QUICKSILVER_HVERS, 0xa }, + { 0, } +}; + +static struct parisc_driver lba_driver __refdata = { + .name = MODULE_NAME, + .id_table = lba_tbl, + .probe = lba_driver_probe, +}; + +/* +** One time initialization to let the world know the LBA was found. +** Must be called exactly once before pci_init(). +*/ +static int __init lba_init(void) +{ + return register_parisc_driver(&lba_driver); +} +arch_initcall(lba_init); + +/* +** Initialize the IBASE/IMASK registers for LBA (Elroy). +** Only called from sba_iommu.c in order to route ranges (MMIO vs DMA). +** sba_iommu is responsible for locking (none needed at init time). +*/ +void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask) +{ + void __iomem * base_addr = ioremap(lba->hpa.start, 4096); + + imask <<= 2; /* adjust for hints - 2 more bits */ + + /* Make sure we aren't trying to set bits that aren't writeable. */ + WARN_ON((ibase & 0x001fffff) != 0); + WARN_ON((imask & 0x001fffff) != 0); + + DBG("%s() ibase 0x%x imask 0x%x\n", __func__, ibase, imask); + WRITE_REG32( imask, base_addr + LBA_IMASK); + WRITE_REG32( ibase, base_addr + LBA_IBASE); + iounmap(base_addr); +} + + +/* + * The design of the Diva management card in rp34x0 machines (rp3410, rp3440) + * seems rushed, so that many built-in components simply don't work. + * The following quirks disable the serial AUX port and the built-in ATI RV100 + * Radeon 7000 graphics card which both don't have any external connectors and + * thus are useless, and even worse, e.g. the AUX port occupies ttyS0 and as + * such makes those machines the only PARISC machines on which we can't use + * ttyS0 as boot console. + */ +static void quirk_diva_ati_card(struct pci_dev *dev) +{ + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP || + dev->subsystem_device != 0x1292) + return; + + dev_info(&dev->dev, "Hiding Diva built-in ATI card"); + dev->device = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QY, + quirk_diva_ati_card); + +static void quirk_diva_aux_disable(struct pci_dev *dev) +{ + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP || + dev->subsystem_device != 0x1291) + return; + + dev_info(&dev->dev, "Hiding Diva built-in AUX serial device"); + dev->device = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, + quirk_diva_aux_disable); + +static void quirk_tosca_aux_disable(struct pci_dev *dev) +{ + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP || + dev->subsystem_device != 0x104a) + return; + + dev_info(&dev->dev, "Hiding Tosca secondary built-in AUX serial device"); + dev->device = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + quirk_tosca_aux_disable); diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c new file mode 100644 index 000000000..016c9d5a6 --- /dev/null +++ b/drivers/parisc/led.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Chassis LCD/LED driver for HP-PARISC workstations + * + * (c) Copyright 2000 Red Hat Software + * (c) Copyright 2000 Helge Deller + * (c) Copyright 2001 Randolph Chung + * (c) Copyright 2000-2023 Helge Deller + * + * The control of the LEDs and LCDs on PARISC machines has to be done + * completely in software. + * + * The LEDs can be configured at runtime in /sys/class/leds/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* HZ */ +#include +#include + +#define LED_HAS_LCD 1 +#define LED_HAS_LED 2 + +static unsigned char led_type; /* bitmask of LED_HAS_XXX */ +static unsigned char lastleds; /* LED state from most recent update */ +static unsigned char lcd_new_text; +static unsigned char lcd_text[20]; +static unsigned char lcd_no_led_support; /* KittyHawk doesn't support LED on its LCD */ + +struct lcd_block { + unsigned char command; /* stores the command byte */ + unsigned char on; /* value for turning LED on */ + unsigned char off; /* value for turning LED off */ +}; + +/* Structure returned by PDC_RETURN_CHASSIS_INFO */ +/* NOTE: we use unsigned long:16 two times, since the following member + lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */ +struct pdc_chassis_lcd_info_ret_block { + unsigned long model:16; /* DISPLAY_MODEL_XXXX */ + unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */ + unsigned long lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */ + unsigned long lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */ + unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */ + unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */ + unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */ + unsigned char act_enable; /* 0 = no activity (LCD only) */ + struct lcd_block heartbeat; + struct lcd_block disk_io; + struct lcd_block lan_rcv; + struct lcd_block lan_tx; + char _pad; +}; + + +/* LCD_CMD and LCD_DATA for KittyHawk machines */ +#define KITTYHAWK_LCD_CMD F_EXTEND(0xf0190000UL) +#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD + 1) + +/* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's + * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */ +static struct pdc_chassis_lcd_info_ret_block +lcd_info __attribute__((aligned(8))) = +{ + .model = DISPLAY_MODEL_NONE, + .lcd_width = 16, + .lcd_cmd_reg_addr = KITTYHAWK_LCD_CMD, + .lcd_data_reg_addr = KITTYHAWK_LCD_DATA, + .min_cmd_delay = 80, + .reset_cmd1 = 0x80, + .reset_cmd2 = 0xc0, +}; + +/* direct access to some of the lcd_info variables */ +#define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr +#define LCD_DATA_REG lcd_info.lcd_data_reg_addr +#define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */ + +/* ptr to LCD/LED-specific function */ +static void (*led_func_ptr) (unsigned char); + + +static void lcd_print_now(void) +{ + int i; + char *str = lcd_text; + + if (lcd_info.model != DISPLAY_MODEL_LCD) + return; + + if (!lcd_new_text) + return; + lcd_new_text = 0; + + /* Set LCD Cursor to 1st character */ + gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG); + udelay(lcd_info.min_cmd_delay); + + /* Print the string */ + for (i = 0; i < lcd_info.lcd_width; i++) { + gsc_writeb(*str ? *str++ : ' ', LCD_DATA_REG); + udelay(lcd_info.min_cmd_delay); + } +} + +/** + * lcd_print() + * + * @str: string to show on the LCD. If NULL, print current string again. + * + * Displays the given string on the LCD-Display of newer machines. + */ +void lcd_print(const char *str) +{ + /* copy display string to buffer for procfs */ + if (str) + strscpy(lcd_text, str, sizeof(lcd_text)); + lcd_new_text = 1; + + /* print now if LCD without any LEDs */ + if (led_type == LED_HAS_LCD) + lcd_print_now(); +} + +#define LED_DATA 0x01 /* data to shift (0:on 1:off) */ +#define LED_STROBE 0x02 /* strobe to clock data */ + +/** + * led_ASP_driver() - LED driver for the ASP controller chip + * + * @leds: bitmap representing the LED status + */ +static void led_ASP_driver(unsigned char leds) +{ + int i; + + leds = ~leds; + for (i = 0; i < 8; i++) { + unsigned char value; + value = (leds & 0x80) >> 7; + gsc_writeb( value, LED_DATA_REG ); + gsc_writeb( value | LED_STROBE, LED_DATA_REG ); + leds <<= 1; + } +} + +/** + * led_LASI_driver() - LED driver for the LASI controller chip + * + * @leds: bitmap representing the LED status + */ +static void led_LASI_driver(unsigned char leds) +{ + leds = ~leds; + gsc_writeb( leds, LED_DATA_REG ); +} + +/** + * led_LCD_driver() - LED & LCD driver for LCD chips + * + * @leds: bitmap representing the LED status + */ +static void led_LCD_driver(unsigned char leds) +{ + static const unsigned char mask[4] = { + LED_HEARTBEAT, LED_DISK_IO, + LED_LAN_RCV, LED_LAN_TX }; + + static struct lcd_block * const blockp[4] = { + &lcd_info.heartbeat, + &lcd_info.disk_io, + &lcd_info.lan_rcv, + &lcd_info.lan_tx + }; + static unsigned char latest_leds; + int i; + + for (i = 0; i < 4; ++i) { + if ((leds & mask[i]) == (latest_leds & mask[i])) + continue; + + gsc_writeb( blockp[i]->command, LCD_CMD_REG ); + udelay(lcd_info.min_cmd_delay); + + gsc_writeb( leds & mask[i] ? blockp[i]->on : + blockp[i]->off, LCD_DATA_REG ); + udelay(lcd_info.min_cmd_delay); + } + latest_leds = leds; + + lcd_print_now(); +} + + +/** + * lcd_system_halt() + * + * @nb: pointer to the notifier_block structure + * @event: the event (SYS_RESTART, SYS_HALT or SYS_POWER_OFF) + * @buf: pointer to a buffer (not used) + * + * Called by the reboot notifier chain at shutdown. Stops all + * LED/LCD activities. + */ +static int lcd_system_halt(struct notifier_block *nb, unsigned long event, void *buf) +{ + const char *txt; + + switch (event) { + case SYS_RESTART: txt = "SYSTEM RESTART"; + break; + case SYS_HALT: txt = "SYSTEM HALT"; + break; + case SYS_POWER_OFF: txt = "SYSTEM POWER OFF"; + break; + default: return NOTIFY_DONE; + } + + lcd_print(txt); + + return NOTIFY_OK; +} + +static struct notifier_block lcd_system_halt_notifier = { + .notifier_call = lcd_system_halt, +}; + +static void set_led(struct led_classdev *led_cdev, enum led_brightness brightness); + +struct hppa_led { + struct led_classdev led_cdev; + unsigned char led_bit; +}; +#define to_hppa_led(d) container_of(d, struct hppa_led, led_cdev) + +typedef void (*set_handler)(struct led_classdev *, enum led_brightness); +struct led_type { + const char *name; + set_handler handler; + const char *default_trigger; +}; + +#define NUM_LEDS_PER_BOARD 8 +struct hppa_drvdata { + struct hppa_led leds[NUM_LEDS_PER_BOARD]; +}; + +static void set_led(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + struct hppa_led *p = to_hppa_led(led_cdev); + unsigned char led_bit = p->led_bit; + + if (brightness == LED_OFF) + lastleds &= ~led_bit; + else + lastleds |= led_bit; + + if (led_func_ptr) + led_func_ptr(lastleds); +} + + +static int hppa_led_generic_probe(struct platform_device *pdev, + struct led_type *types) +{ + struct hppa_drvdata *p; + int i, err; + + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) { + struct led_classdev *lp = &p->leds[i].led_cdev; + + p->leds[i].led_bit = BIT(i); + lp->name = types[i].name; + lp->brightness = LED_FULL; + lp->brightness_set = types[i].handler; + lp->default_trigger = types[i].default_trigger; + err = led_classdev_register(&pdev->dev, lp); + if (err) { + dev_err(&pdev->dev, "Could not register %s LED\n", + lp->name); + for (i--; i >= 0; i--) + led_classdev_unregister(&p->leds[i].led_cdev); + return err; + } + } + + platform_set_drvdata(pdev, p); + + return 0; +} + +static void platform_led_remove(struct platform_device *pdev) +{ + struct hppa_drvdata *p = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) + led_classdev_unregister(&p->leds[i].led_cdev); +} + +static struct led_type mainboard_led_types[NUM_LEDS_PER_BOARD] = { + { + .name = "platform-lan-tx", + .handler = set_led, + .default_trigger = "tx", + }, + { + .name = "platform-lan-rx", + .handler = set_led, + .default_trigger = "rx", + }, + { + .name = "platform-disk", + .handler = set_led, + .default_trigger = "disk-activity", + }, + { + .name = "platform-heartbeat", + .handler = set_led, + .default_trigger = "heartbeat", + }, + { + .name = "platform-LED4", + .handler = set_led, + .default_trigger = "panic", + }, + { + .name = "platform-LED5", + .handler = set_led, + .default_trigger = "panic", + }, + { + .name = "platform-LED6", + .handler = set_led, + .default_trigger = "panic", + }, + { + .name = "platform-LED7", + .handler = set_led, + .default_trigger = "panic", + }, +}; + +static int platform_led_probe(struct platform_device *pdev) +{ + return hppa_led_generic_probe(pdev, mainboard_led_types); +} + +MODULE_ALIAS("platform:platform-leds"); + +static struct platform_driver hppa_mainboard_led_driver = { + .probe = platform_led_probe, + .remove = platform_led_remove, + .driver = { + .name = "platform-leds", + }, +}; + +static struct platform_driver * const drivers[] = { + &hppa_mainboard_led_driver, +}; + +static struct platform_device platform_leds = { + .name = "platform-leds", +}; + +/** + * register_led_driver() + * + * @model: model type, one of the DISPLAY_MODEL_XXXX values + * @cmd_reg: physical address of cmd register for the LED/LCD + * @data_reg: physical address of data register for the LED/LCD + * + * Registers a chassis LED or LCD which should be driven by this driver. + * Only PDC-based, LASI- or ASP-style LEDs and LCDs are supported. + */ +int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg) +{ + if (led_func_ptr || !data_reg) + return 1; + + /* No LEDs when running in QEMU */ + if (running_on_qemu) + return 1; + + lcd_info.model = model; /* store the values */ + LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? 0 : cmd_reg; + + switch (lcd_info.model) { + case DISPLAY_MODEL_LCD: + LCD_DATA_REG = data_reg; + pr_info("led: LCD display at %#lx and %#lx\n", + LCD_CMD_REG , LCD_DATA_REG); + led_func_ptr = led_LCD_driver; + if (lcd_no_led_support) + led_type = LED_HAS_LCD; + else + led_type = LED_HAS_LCD | LED_HAS_LED; + break; + + case DISPLAY_MODEL_LASI: + LED_DATA_REG = data_reg; + led_func_ptr = led_LASI_driver; + pr_info("led: LED display at %#lx\n", LED_DATA_REG); + led_type = LED_HAS_LED; + break; + + case DISPLAY_MODEL_OLD_ASP: + LED_DATA_REG = data_reg; + led_func_ptr = led_ASP_driver; + pr_info("led: LED (ASP-style) display at %#lx\n", + LED_DATA_REG); + led_type = LED_HAS_LED; + break; + + default: + pr_err("led: Unknown LCD/LED model type %d\n", lcd_info.model); + return 1; + } + + platform_register_drivers(drivers, ARRAY_SIZE(drivers)); + + return register_reboot_notifier(&lcd_system_halt_notifier); +} + +/** + * early_led_init() + * + * early_led_init() is called early in the bootup-process and asks the + * PDC for an usable chassis LCD or LED. If the PDC doesn't return any + * info, then a LED might be detected by the LASI or ASP drivers later. + * KittyHawk machines have often a buggy PDC, so that we explicitly check + * for those machines here. + */ +static int __init early_led_init(void) +{ + struct pdc_chassis_info chassis_info; + int ret; + + scnprintf(lcd_text, sizeof(lcd_text), + "Linux %s", init_utsname()->release); + lcd_new_text = 1; + + /* Work around the buggy PDC of KittyHawk-machines */ + switch (CPU_HVERSION) { + case 0x580: /* KittyHawk DC2-100 (K100) */ + case 0x581: /* KittyHawk DC3-120 (K210) */ + case 0x582: /* KittyHawk DC3 100 (K400) */ + case 0x583: /* KittyHawk DC3 120 (K410) */ + case 0x58B: /* KittyHawk DC2 100 (K200) */ + pr_info("LCD on KittyHawk-Machine found.\n"); + lcd_info.model = DISPLAY_MODEL_LCD; + /* KittyHawk has no LED support on its LCD, so skip LED detection */ + lcd_no_led_support = 1; + goto found; /* use the preinitialized values of lcd_info */ + } + + /* initialize the struct, so that we can check for valid return values */ + chassis_info.actcnt = chassis_info.maxcnt = 0; + + ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info)); + if (ret != PDC_OK) { +not_found: + lcd_info.model = DISPLAY_MODEL_NONE; + return 1; + } + + /* check the results. Some machines have a buggy PDC */ + if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt) + goto not_found; + + switch (lcd_info.model) { + case DISPLAY_MODEL_LCD: /* LCD display */ + if (chassis_info.actcnt < + offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1) + goto not_found; + if (!lcd_info.act_enable) { + /* PDC tells LCD should not be used. */ + goto not_found; + } + break; + + case DISPLAY_MODEL_NONE: /* no LED or LCD available */ + goto not_found; + + case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */ + if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32) + goto not_found; + break; + + default: + pr_warn("PDC reported unknown LCD/LED model %d\n", + lcd_info.model); + goto not_found; + } + +found: + /* register the LCD/LED driver */ + return register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG); +} +arch_initcall(early_led_init); + +/** + * register_led_regions() + * + * Register_led_regions() registers the LCD/LED regions for /procfs. + * At bootup - where the initialisation of the LCD/LED often happens + * not all internal structures of request_region() are properly set up, + * so that we delay the led-registration until after busdevices_init() + * has been executed. + */ +static void __init register_led_regions(void) +{ + switch (lcd_info.model) { + case DISPLAY_MODEL_LCD: + request_mem_region((unsigned long)LCD_CMD_REG, 1, "lcd_cmd"); + request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data"); + break; + case DISPLAY_MODEL_LASI: + case DISPLAY_MODEL_OLD_ASP: + request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data"); + break; + } +} + +static int __init startup_leds(void) +{ + if (platform_device_register(&platform_leds)) + printk(KERN_INFO "LED: failed to register LEDs\n"); + register_led_regions(); + return 0; +} +device_initcall(startup_leds); diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c new file mode 100644 index 000000000..16f4496bc --- /dev/null +++ b/drivers/parisc/pdc_stable.c @@ -0,0 +1,1125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Interfaces to retrieve and set PDC Stable options (firmware) + * + * Copyright (C) 2005-2006 Thibaut VARENE + * + * DEV NOTE: the PDC Procedures reference states that: + * "A minimum of 96 bytes of Stable Storage is required. Providing more than + * 96 bytes of Stable Storage is optional [...]. Failure to provide the + * optional locations from 96 to 192 results in the loss of certain + * functionality during boot." + * + * Since locations between 96 and 192 are the various paths, most (if not + * all) PA-RISC machines should have them. Anyway, for safety reasons, the + * following code can deal with just 96 bytes of Stable Storage, and all + * sizes between 96 and 192 bytes (provided they are multiple of struct + * pdc_module_path size, eg: 128, 160 and 192) to provide full information. + * One last word: there's one path we can always count on: the primary path. + * Anything above 224 bytes is used for 'osdep2' OS-dependent storage area. + * + * The first OS-dependent area should always be available. Obviously, this is + * not true for the other one. Also bear in mind that reading/writing from/to + * osdep2 is much more expensive than from/to osdep1. + * NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first + * 2 bytes of storage available right after OSID. That's a total of 4 bytes + * sacrificed: -ETOOLAZY :P + * + * The current policy wrt file permissions is: + * - write: root only + * - read: (reading triggers PDC calls) ? root only : everyone + * The rationale is that PDC calls could hog (DoS) the machine. + * + * TODO: + * - timer/fastsize write calls + */ + +#undef PDCS_DEBUG +#ifdef PDCS_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG fmt, ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PDCS_VERSION "0.30" +#define PDCS_PREFIX "PDC Stable Storage" + +#define PDCS_ADDR_PPRI 0x00 +#define PDCS_ADDR_OSID 0x40 +#define PDCS_ADDR_OSD1 0x48 +#define PDCS_ADDR_DIAG 0x58 +#define PDCS_ADDR_FSIZ 0x5C +#define PDCS_ADDR_PCON 0x60 +#define PDCS_ADDR_PALT 0x80 +#define PDCS_ADDR_PKBD 0xA0 +#define PDCS_ADDR_OSD2 0xE0 + +MODULE_AUTHOR("Thibaut VARENE "); +MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(PDCS_VERSION); + +/* holds Stable Storage size. Initialized once and for all, no lock needed */ +static unsigned long pdcs_size __read_mostly; + +/* holds OS ID. Initialized once and for all, hopefully to 0x0006 */ +static u16 pdcs_osid __read_mostly; + +/* This struct defines what we need to deal with a parisc pdc path entry */ +struct pdcspath_entry { + rwlock_t rw_lock; /* to protect path entry access */ + short ready; /* entry record is valid if != 0 */ + unsigned long addr; /* entry address in stable storage */ + char *name; /* entry name */ + struct pdc_module_path devpath; /* device path in parisc representation */ + struct device *dev; /* corresponding device */ + struct kobject kobj; +}; + +struct pdcspath_attribute { + struct attribute attr; + ssize_t (*show)(struct pdcspath_entry *entry, char *buf); + ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count); +}; + +#define PDCSPATH_ENTRY(_addr, _name) \ +struct pdcspath_entry pdcspath_entry_##_name = { \ + .ready = 0, \ + .addr = _addr, \ + .name = __stringify(_name), \ +}; + +#define PDCS_ATTR(_name, _mode, _show, _store) \ +struct kobj_attribute pdcs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +}; + +#define PATHS_ATTR(_name, _mode, _show, _store) \ +struct pdcspath_attribute paths_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +}; + +#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr) +#define to_pdcspath_entry(obj) container_of(obj, struct pdcspath_entry, kobj) + +/** + * pdcspath_fetch - This function populates the path entry structs. + * @entry: A pointer to an allocated pdcspath_entry. + * + * The general idea is that you don't read from the Stable Storage every time + * you access the files provided by the facilities. We store a copy of the + * content of the stable storage WRT various paths in these structs. We read + * these structs when reading the files, and we will write to these structs when + * writing to the files, and only then write them back to the Stable Storage. + * + * This function expects to be called with @entry->rw_lock write-hold. + */ +static int +pdcspath_fetch(struct pdcspath_entry *entry) +{ + struct pdc_module_path *devpath; + + if (!entry) + return -EINVAL; + + devpath = &entry->devpath; + + DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__, + entry, devpath, entry->addr); + + /* addr, devpath and count must be word aligned */ + if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) + return -EIO; + + /* Find the matching device. + NOTE: hardware_path overlays with pdc_module_path, so the nice cast can + be used */ + entry->dev = hwpath_to_device((struct hardware_path *)devpath); + + entry->ready = 1; + + DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); + + return 0; +} + +/** + * pdcspath_store - This function writes a path to stable storage. + * @entry: A pointer to an allocated pdcspath_entry. + * + * It can be used in two ways: either by passing it a preset devpath struct + * containing an already computed hardware path, or by passing it a device + * pointer, from which it'll find out the corresponding hardware path. + * For now we do not handle the case where there's an error in writing to the + * Stable Storage area, so you'd better not mess up the data :P + * + * This function expects to be called with @entry->rw_lock write-hold. + */ +static void +pdcspath_store(struct pdcspath_entry *entry) +{ + struct pdc_module_path *devpath; + + BUG_ON(!entry); + + devpath = &entry->devpath; + + /* We expect the caller to set the ready flag to 0 if the hardware + path struct provided is invalid, so that we know we have to fill it. + First case, we don't have a preset hwpath... */ + if (!entry->ready) { + /* ...but we have a device, map it */ + BUG_ON(!entry->dev); + device_to_hwpath(entry->dev, (struct hardware_path *)devpath); + } + /* else, we expect the provided hwpath to be valid. */ + + DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__, + entry, devpath, entry->addr); + + /* addr, devpath and count must be word aligned */ + if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) + WARN(1, KERN_ERR "%s: an error occurred when writing to PDC.\n" + "It is likely that the Stable Storage data has been corrupted.\n" + "Please check it carefully upon next reboot.\n", __func__); + + /* kobject is already registered */ + entry->ready = 2; + + DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); +} + +/** + * pdcspath_hwpath_read - This function handles hardware path pretty printing. + * @entry: An allocated and populated pdscpath_entry struct. + * @buf: The output buffer to write to. + * + * We will call this function to format the output of the hwpath attribute file. + */ +static ssize_t +pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf) +{ + char *out = buf; + struct pdc_module_path *devpath; + short i; + + if (!entry || !buf) + return -EINVAL; + + read_lock(&entry->rw_lock); + devpath = &entry->devpath; + i = entry->ready; + read_unlock(&entry->rw_lock); + + if (!i) /* entry is not ready */ + return -ENODATA; + + for (i = 0; i < 6; i++) { + if (devpath->path.bc[i] < 0) + continue; + out += sprintf(out, "%d/", devpath->path.bc[i]); + } + out += sprintf(out, "%u\n", (unsigned char)devpath->path.mod); + + return out - buf; +} + +/** + * pdcspath_hwpath_write - This function handles hardware path modifying. + * @entry: An allocated and populated pdscpath_entry struct. + * @buf: The input buffer to read from. + * @count: The number of bytes to be read. + * + * We will call this function to change the current hardware path. + * Hardware paths are to be given '/'-delimited, without brackets. + * We make sure that the provided path actually maps to an existing + * device, BUT nothing would prevent some foolish user to set the path to some + * PCI bridge or even a CPU... + * A better work around would be to make sure we are at the end of a device tree + * for instance, but it would be IMHO beyond the simple scope of that driver. + * The aim is to provide a facility. Data correctness is left to userland. + */ +static ssize_t +pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count) +{ + struct hardware_path hwpath; + unsigned short i; + char in[64], *temp; + struct device *dev; + int ret; + + if (!entry || !buf || !count) + return -EINVAL; + + /* We'll use a local copy of buf */ + count = min_t(size_t, count, sizeof(in)-1); + strscpy(in, buf, count + 1); + + /* Let's clean up the target. 0xff is a blank pattern */ + memset(&hwpath, 0xff, sizeof(hwpath)); + + /* First, pick the mod field (the last one of the input string) */ + if (!(temp = strrchr(in, '/'))) + return -EINVAL; + + hwpath.mod = simple_strtoul(temp+1, NULL, 10); + in[temp-in] = '\0'; /* truncate the remaining string. just precaution */ + DPRINTK("%s: mod: %d\n", __func__, hwpath.mod); + + /* Then, loop for each delimiter, making sure we don't have too many. + we write the bc fields in a down-top way. No matter what, we stop + before writing the last field. If there are too many fields anyway, + then the user is a moron and it'll be caught up later when we'll + check the consistency of the given hwpath. */ + for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) { + hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10); + in[temp-in] = '\0'; + DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]); + } + + /* Store the final field */ + hwpath.bc[i] = simple_strtoul(in, NULL, 10); + DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]); + + /* Now we check that the user isn't trying to lure us */ + if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) { + printk(KERN_WARNING "%s: attempt to set invalid \"%s\" " + "hardware path: %s\n", __func__, entry->name, buf); + return -EINVAL; + } + + /* So far so good, let's get in deep */ + write_lock(&entry->rw_lock); + entry->ready = 0; + entry->dev = dev; + + /* Now, dive in. Write back to the hardware */ + pdcspath_store(entry); + + /* Update the symlink to the real device */ + sysfs_remove_link(&entry->kobj, "device"); + write_unlock(&entry->rw_lock); + + ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); + WARN_ON(ret); + + printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n", + entry->name, buf); + + return count; +} + +/** + * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing. + * @entry: An allocated and populated pdscpath_entry struct. + * @buf: The output buffer to write to. + * + * We will call this function to format the output of the layer attribute file. + */ +static ssize_t +pdcspath_layer_read(struct pdcspath_entry *entry, char *buf) +{ + char *out = buf; + struct pdc_module_path *devpath; + short i; + + if (!entry || !buf) + return -EINVAL; + + read_lock(&entry->rw_lock); + devpath = &entry->devpath; + i = entry->ready; + read_unlock(&entry->rw_lock); + + if (!i) /* entry is not ready */ + return -ENODATA; + + for (i = 0; i < 6 && devpath->layers[i]; i++) + out += sprintf(out, "%u ", devpath->layers[i]); + + out += sprintf(out, "\n"); + + return out - buf; +} + +/** + * pdcspath_layer_write - This function handles extended layer modifying. + * @entry: An allocated and populated pdscpath_entry struct. + * @buf: The input buffer to read from. + * @count: The number of bytes to be read. + * + * We will call this function to change the current layer value. + * Layers are to be given '.'-delimited, without brackets. + * XXX beware we are far less checky WRT input data provided than for hwpath. + * Potential harm can be done, since there's no way to check the validity of + * the layer fields. + */ +static ssize_t +pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count) +{ + unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */ + unsigned short i; + char in[64], *temp; + + if (!entry || !buf || !count) + return -EINVAL; + + /* We'll use a local copy of buf */ + count = min_t(size_t, count, sizeof(in)-1); + strscpy(in, buf, count + 1); + + /* Let's clean up the target. 0 is a blank pattern */ + memset(&layers, 0, sizeof(layers)); + + /* First, pick the first layer */ + if (unlikely(!isdigit(*in))) + return -EINVAL; + layers[0] = simple_strtoul(in, NULL, 10); + DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]); + + temp = in; + for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) { + if (unlikely(!isdigit(*(++temp)))) + return -EINVAL; + layers[i] = simple_strtoul(temp, NULL, 10); + DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]); + } + + /* So far so good, let's get in deep */ + write_lock(&entry->rw_lock); + + /* First, overwrite the current layers with the new ones, not touching + the hardware path. */ + memcpy(&entry->devpath.layers, &layers, sizeof(layers)); + + /* Now, dive in. Write back to the hardware */ + pdcspath_store(entry); + write_unlock(&entry->rw_lock); + + printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n", + entry->name, buf); + + return count; +} + +/** + * pdcspath_attr_show - Generic read function call wrapper. + * @kobj: The kobject to get info from. + * @attr: The attribute looked upon. + * @buf: The output buffer. + */ +static ssize_t +pdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct pdcspath_entry *entry = to_pdcspath_entry(kobj); + struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr); + ssize_t ret = 0; + + if (pdcs_attr->show) + ret = pdcs_attr->show(entry, buf); + + return ret; +} + +/** + * pdcspath_attr_store - Generic write function call wrapper. + * @kobj: The kobject to write info to. + * @attr: The attribute to be modified. + * @buf: The input buffer. + * @count: The size of the buffer. + */ +static ssize_t +pdcspath_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct pdcspath_entry *entry = to_pdcspath_entry(kobj); + struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr); + ssize_t ret = 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (pdcs_attr->store) + ret = pdcs_attr->store(entry, buf, count); + + return ret; +} + +static const struct sysfs_ops pdcspath_attr_ops = { + .show = pdcspath_attr_show, + .store = pdcspath_attr_store, +}; + +/* These are the two attributes of any PDC path. */ +static PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write); +static PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write); + +static struct attribute *paths_subsys_attrs[] = { + &paths_attr_hwpath.attr, + &paths_attr_layer.attr, + NULL, +}; +ATTRIBUTE_GROUPS(paths_subsys); + +/* Specific kobject type for our PDC paths */ +static const struct kobj_type ktype_pdcspath = { + .sysfs_ops = &pdcspath_attr_ops, + .default_groups = paths_subsys_groups, +}; + +/* We hard define the 4 types of path we expect to find */ +static PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary); +static PDCSPATH_ENTRY(PDCS_ADDR_PCON, console); +static PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative); +static PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard); + +/* An array containing all PDC paths we will deal with */ +static struct pdcspath_entry *pdcspath_entries[] = { + &pdcspath_entry_primary, + &pdcspath_entry_alternative, + &pdcspath_entry_console, + &pdcspath_entry_keyboard, + NULL, +}; + + +/* For more insight of what's going on here, refer to PDC Procedures doc, + * Section PDC_STABLE */ + +/** + * pdcs_size_read - Stable Storage size output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + */ +static ssize_t pdcs_size_read(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + char *out = buf; + + if (!buf) + return -EINVAL; + + /* show the size of the stable storage */ + out += sprintf(out, "%ld\n", pdcs_size); + + return out - buf; +} + +/** + * pdcs_auto_read - Stable Storage autoboot/search flag output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag + */ +static ssize_t pdcs_auto_read(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf, int knob) +{ + char *out = buf; + struct pdcspath_entry *pathentry; + + if (!buf) + return -EINVAL; + + /* Current flags are stored in primary boot path entry */ + pathentry = &pdcspath_entry_primary; + + read_lock(&pathentry->rw_lock); + out += sprintf(out, "%s\n", (pathentry->devpath.path.flags & knob) ? + "On" : "Off"); + read_unlock(&pathentry->rw_lock); + + return out - buf; +} + +/** + * pdcs_autoboot_read - Stable Storage autoboot flag output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + */ +static ssize_t pdcs_autoboot_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT); +} + +/** + * pdcs_autosearch_read - Stable Storage autoboot flag output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + */ +static ssize_t pdcs_autosearch_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH); +} + +/** + * pdcs_timer_read - Stable Storage timer count output (in seconds). + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + * + * The value of the timer field correponds to a number of seconds in powers of 2. + */ +static ssize_t pdcs_timer_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *out = buf; + struct pdcspath_entry *pathentry; + + if (!buf) + return -EINVAL; + + /* Current flags are stored in primary boot path entry */ + pathentry = &pdcspath_entry_primary; + + /* print the timer value in seconds */ + read_lock(&pathentry->rw_lock); + out += sprintf(out, "%u\n", (pathentry->devpath.path.flags & PF_TIMER) ? + (1 << (pathentry->devpath.path.flags & PF_TIMER)) : 0); + read_unlock(&pathentry->rw_lock); + + return out - buf; +} + +/** + * pdcs_osid_read - Stable Storage OS ID register output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + */ +static ssize_t pdcs_osid_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *out = buf; + + if (!buf) + return -EINVAL; + + out += sprintf(out, "%s dependent data (0x%.4x)\n", + os_id_to_string(pdcs_osid), pdcs_osid); + + return out - buf; +} + +/** + * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + * + * This can hold 16 bytes of OS-Dependent data. + */ +static ssize_t pdcs_osdep1_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *out = buf; + u32 result[4]; + + if (!buf) + return -EINVAL; + + if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK) + return -EIO; + + out += sprintf(out, "0x%.8x\n", result[0]); + out += sprintf(out, "0x%.8x\n", result[1]); + out += sprintf(out, "0x%.8x\n", result[2]); + out += sprintf(out, "0x%.8x\n", result[3]); + + return out - buf; +} + +/** + * pdcs_diagnostic_read - Stable Storage Diagnostic register output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + * + * I have NFC how to interpret the content of that register ;-). + */ +static ssize_t pdcs_diagnostic_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *out = buf; + u32 result; + + if (!buf) + return -EINVAL; + + /* get diagnostic */ + if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK) + return -EIO; + + out += sprintf(out, "0x%.4x\n", (result >> 16)); + + return out - buf; +} + +/** + * pdcs_fastsize_read - Stable Storage FastSize register output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + * + * This register holds the amount of system RAM to be tested during boot sequence. + */ +static ssize_t pdcs_fastsize_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *out = buf; + u32 result; + + if (!buf) + return -EINVAL; + + /* get fast-size */ + if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK) + return -EIO; + + if ((result & 0x0F) < 0x0E) + out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256); + else + out += sprintf(out, "All"); + out += sprintf(out, "\n"); + + return out - buf; +} + +/** + * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The output buffer to write to. + * + * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available. + */ +static ssize_t pdcs_osdep2_read(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *out = buf; + unsigned long size; + unsigned short i; + u32 result; + + if (unlikely(pdcs_size <= 224)) + return -ENODATA; + + size = pdcs_size - 224; + + if (!buf) + return -EINVAL; + + for (i=0; irw_lock); + flags = pathentry->devpath.path.flags; + read_unlock(&pathentry->rw_lock); + + DPRINTK("%s: flags before: 0x%X\n", __func__, flags); + + temp = skip_spaces(in); + + c = *temp++ - '0'; + if ((c != 0) && (c != 1)) + goto parse_error; + if (c == 0) + flags &= ~knob; + else + flags |= knob; + + DPRINTK("%s: flags after: 0x%X\n", __func__, flags); + + /* So far so good, let's get in deep */ + write_lock(&pathentry->rw_lock); + + /* Change the path entry flags first */ + pathentry->devpath.path.flags = flags; + + /* Now, dive in. Write back to the hardware */ + pdcspath_store(pathentry); + write_unlock(&pathentry->rw_lock); + + printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n", + (knob & PF_AUTOBOOT) ? "autoboot" : "autosearch", + (flags & knob) ? "On" : "Off"); + + return count; + +parse_error: + printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__); + return -EINVAL; +} + +/** + * pdcs_autoboot_write - This function handles autoboot flag modifying. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The input buffer to read from. + * @count: The number of bytes to be read. + * + * We will call this function to change the current boot flags. + * We expect a precise syntax: + * \"n\" (n == 0 or 1) to toggle AutoSearch Off or On + */ +static ssize_t pdcs_autoboot_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOBOOT); +} + +/** + * pdcs_autosearch_write - This function handles autosearch flag modifying. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The input buffer to read from. + * @count: The number of bytes to be read. + * + * We will call this function to change the current boot flags. + * We expect a precise syntax: + * \"n\" (n == 0 or 1) to toggle AutoSearch Off or On + */ +static ssize_t pdcs_autosearch_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOSEARCH); +} + +/** + * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The input buffer to read from. + * @count: The number of bytes to be read. + * + * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte + * write approach. It's up to userspace to deal with it when constructing + * its input buffer. + */ +static ssize_t pdcs_osdep1_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + u8 in[16]; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (!buf || !count) + return -EINVAL; + + if (unlikely(pdcs_osid != OS_ID_LINUX)) + return -EPERM; + + if (count > 16) + return -EMSGSIZE; + + /* We'll use a local copy of buf */ + memset(in, 0, 16); + memcpy(in, buf, count); + + if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK) + return -EIO; + + return count; +} + +/** + * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input. + * @kobj: The kobject used to share data with userspace. + * @attr: The kobject attributes. + * @buf: The input buffer to read from. + * @count: The number of bytes to be read. + * + * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a + * byte-by-byte write approach. It's up to userspace to deal with it when + * constructing its input buffer. + */ +static ssize_t pdcs_osdep2_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long size; + unsigned short i; + u8 in[4]; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (!buf || !count) + return -EINVAL; + + if (unlikely(pdcs_size <= 224)) + return -ENOSYS; + + if (unlikely(pdcs_osid != OS_ID_LINUX)) + return -EPERM; + + size = pdcs_size - 224; + + if (count > size) + return -EMSGSIZE; + + /* We'll use a local copy of buf */ + + for (i=0; irw_lock); + + for (i = 0; (entry = pdcspath_entries[i]); i++) { + write_lock(&entry->rw_lock); + err = pdcspath_fetch(entry); + write_unlock(&entry->rw_lock); + + if (err < 0) + continue; + + entry->kobj.kset = paths_kset; + err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL, + "%s", entry->name); + if (err) { + kobject_put(&entry->kobj); + return err; + } + + /* kobject is now registered */ + write_lock(&entry->rw_lock); + entry->ready = 2; + write_unlock(&entry->rw_lock); + + /* Add a nice symlink to the real device */ + if (entry->dev) { + err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); + WARN_ON(err); + } + + kobject_uevent(&entry->kobj, KOBJ_ADD); + } + + return 0; +} + +/** + * pdcs_unregister_pathentries - Routine called when unregistering the module. + */ +static inline void +pdcs_unregister_pathentries(void) +{ + unsigned short i; + struct pdcspath_entry *entry; + + for (i = 0; (entry = pdcspath_entries[i]); i++) { + read_lock(&entry->rw_lock); + if (entry->ready >= 2) + kobject_put(&entry->kobj); + read_unlock(&entry->rw_lock); + } +} + +/* + * For now we register the stable subsystem with the firmware subsystem + * and the paths subsystem with the stable subsystem + */ +static int __init +pdc_stable_init(void) +{ + int rc = 0, error; + u32 result; + + /* find the size of the stable storage */ + if (pdc_stable_get_size(&pdcs_size) != PDC_OK) + return -ENODEV; + + /* make sure we have enough data */ + if (pdcs_size < 96) + return -ENODATA; + + printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); + + /* get OSID */ + if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) + return -EIO; + + /* the actual result is 16 bits away */ + pdcs_osid = (u16)(result >> 16); + + /* For now we'll register the directory at /sys/firmware/stable */ + stable_kobj = kobject_create_and_add("stable", firmware_kobj); + if (!stable_kobj) { + rc = -ENOMEM; + goto fail_firmreg; + } + + /* Don't forget the root entries */ + error = sysfs_create_group(stable_kobj, &pdcs_attr_group); + if (error) { + rc = -ENOMEM; + goto fail_ksetreg; + } + + /* register the paths kset as a child of the stable kset */ + paths_kset = kset_create_and_add("paths", NULL, stable_kobj); + if (!paths_kset) { + rc = -ENOMEM; + goto fail_ksetreg; + } + + /* now we create all "files" for the paths kset */ + if ((rc = pdcs_register_pathentries())) + goto fail_pdcsreg; + + return rc; + +fail_pdcsreg: + pdcs_unregister_pathentries(); + kset_unregister(paths_kset); + +fail_ksetreg: + kobject_put(stable_kobj); + +fail_firmreg: + printk(KERN_INFO PDCS_PREFIX " bailing out\n"); + return rc; +} + +static void __exit +pdc_stable_exit(void) +{ + pdcs_unregister_pathentries(); + kset_unregister(paths_kset); + kobject_put(stable_kobj); +} + + +module_init(pdc_stable_init); +module_exit(pdc_stable_exit); diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c new file mode 100644 index 000000000..9d6c7bf72 --- /dev/null +++ b/drivers/parisc/power.c @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * HP PARISC soft power switch driver + * + * Copyright (c) 2001-2023 Helge Deller + * + * HINT: + * Support of the soft power switch button may be enabled or disabled at + * runtime through the "/proc/sys/kernel/power" procfs entry. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DRIVER_NAME "powersw" +#define KTHREAD_NAME "kpowerswd" + +/* how often should the power button be polled ? */ +#define POWERSWITCH_POLL_PER_SEC 2 + +/* how long does the power button needs to be down until we react ? */ +#define POWERSWITCH_DOWN_SEC 2 + +/* assembly code to access special registers */ +/* taken from PCXL ERS page 82 */ +#define DIAG_CODE(code) (0x14000000 + ((code)<<5)) + +#define MFCPU_X(rDiagReg, t_ch, t_th, code) \ + (DIAG_CODE(code) + ((rDiagReg)<<21) + ((t_ch)<<16) + ((t_th)<<0) ) + +#define MTCPU(dr, gr) MFCPU_X(dr, gr, 0, 0x12) /* move value of gr to dr[dr] */ +#define MFCPU_C(dr, gr) MFCPU_X(dr, gr, 0, 0x30) /* for dr0 and dr8 only ! */ +#define MFCPU_T(dr, gr) MFCPU_X(dr, 0, gr, 0xa0) /* all dr except dr0 and dr8 */ + +#define __getDIAG(dr) ( { \ + register unsigned long __res asm("r28");\ + __asm__ __volatile__ ( \ + ".word %1" : "=&r" (__res) : "i" (MFCPU_T(dr,28) ) \ + ); \ + __res; \ +} ) + +/* local shutdown counter */ +static int shutdown_timer __read_mostly; + +/* check, give feedback and start shutdown after one second */ +static void process_shutdown(void) +{ + if (shutdown_timer == 0) + printk(KERN_ALERT KTHREAD_NAME ": Shutdown requested...\n"); + + shutdown_timer++; + + /* wait until the button was pressed for 1 second */ + if (shutdown_timer == (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC)) { + static const char msg[] = "Shutting down..."; + printk(KERN_INFO KTHREAD_NAME ": %s\n", msg); + lcd_print(msg); + + /* send kill signal */ + if (kill_cad_pid(SIGINT, 1)) { + /* just in case killing init process failed */ + machine_power_off(); + } + } +} + + +/* main power switch task struct */ +static struct task_struct *power_task; + +/* filename in /proc which can be used to enable/disable the power switch */ +#define SYSCTL_FILENAME "sys/kernel/power" + +/* soft power switch enabled/disabled */ +static int pwrsw_enabled __read_mostly = 1; + +static const struct ctl_table power_sysctl_table[] = { + { + .procname = "soft-power", + .data = &pwrsw_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, +}; + +static int __init init_power_sysctl(void) +{ + register_sysctl_init("kernel", power_sysctl_table); + return 0; +} + +arch_initcall(init_power_sysctl); + +/* main kernel thread worker. It polls the button state */ +static int kpowerswd(void *param) +{ + __set_current_state(TASK_RUNNING); + + do { + int button_not_pressed; + unsigned long soft_power_reg = (unsigned long) param; + + schedule_timeout_interruptible(pwrsw_enabled ? HZ : HZ/POWERSWITCH_POLL_PER_SEC); + + if (unlikely(!pwrsw_enabled)) + continue; + + if (soft_power_reg) { + /* + * Non-Gecko-style machines: + * Check the power switch status which is read from the + * real I/O location at soft_power_reg. + * Bit 31 ("the lowest bit) is the status of the power switch. + * This bit is "1" if the button is NOT pressed. + */ + button_not_pressed = (gsc_readl(soft_power_reg) & 0x1); + } else { + /* + * On gecko style machines (e.g. 712/xx and 715/xx) + * the power switch status is stored in Bit 0 ("the highest bit") + * of CPU diagnose register 25. + * Warning: Some machines never reset the DIAG flag, even if + * the button has been released again. + */ + button_not_pressed = (__getDIAG(25) & 0x80000000); + } + + if (likely(button_not_pressed)) { + if (unlikely(shutdown_timer && /* avoid writing if not necessary */ + shutdown_timer < (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC))) { + shutdown_timer = 0; + printk(KERN_INFO KTHREAD_NAME ": Shutdown request aborted.\n"); + } + } else + process_shutdown(); + + + } while (!kthread_should_stop()); + + return 0; +} + + +/* + * powerfail interruption handler (irq IRQ_FROM_REGION(CPU_IRQ_REGION)+2) + */ +#if 0 +static void powerfail_interrupt(int code, void *x) +{ + printk(KERN_CRIT "POWERFAIL INTERRUPTION !\n"); + poweroff(); +} +#endif + + + + +/* + * parisc_panic_event() is called by the panic handler. + * + * As soon as a panic occurs, our tasklets above will not + * be executed any longer. This function then re-enables + * the soft-power switch and allows the user to switch off + * the system. We rely in pdc_soft_power_button_panic() + * since this version spin_trylocks (instead of regular + * spinlock), preventing deadlocks on panic path. + */ +static int parisc_panic_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + /* re-enable the soft-power switch */ + pdc_soft_power_button_panic(0); + return NOTIFY_DONE; +} + +static struct notifier_block parisc_panic_block = { + .notifier_call = parisc_panic_event, + .priority = INT_MAX, +}; + +/* qemu soft power-off function */ +static int qemu_power_off(struct sys_off_data *data) +{ + /* this turns the system off via SeaBIOS */ + gsc_writel(0, (unsigned long) data->cb_data); + pdc_soft_power_button(1); + return NOTIFY_DONE; +} + +static int __init power_init(void) +{ + unsigned long ret; + unsigned long soft_power_reg; + +#if 0 + request_irq( IRQ_FROM_REGION(CPU_IRQ_REGION)+2, &powerfail_interrupt, + 0, "powerfail", NULL); +#endif + + /* enable the soft power switch if possible */ + ret = pdc_soft_power_info(&soft_power_reg); + if (ret == PDC_OK) + ret = pdc_soft_power_button(1); + if (ret != PDC_OK) + soft_power_reg = -1UL; + + switch (soft_power_reg) { + case 0: printk(KERN_INFO DRIVER_NAME ": Gecko-style soft power switch enabled.\n"); + break; + + case -1UL: printk(KERN_INFO DRIVER_NAME ": Soft power switch support not available.\n"); + return -ENODEV; + + default: printk(KERN_INFO DRIVER_NAME ": Soft power switch at 0x%08lx enabled.\n", + soft_power_reg); + } + + power_task = NULL; + if (running_on_qemu && soft_power_reg) + register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_DEFAULT, + qemu_power_off, (void *)soft_power_reg); + if (!running_on_qemu || soft_power_reg) + power_task = kthread_run(kpowerswd, (void*)soft_power_reg, + KTHREAD_NAME); + if (IS_ERR(power_task)) { + printk(KERN_ERR DRIVER_NAME ": thread creation failed. Driver not loaded.\n"); + pdc_soft_power_button(0); + return -EIO; + } + + /* Register a call for panic conditions. */ + atomic_notifier_chain_register(&panic_notifier_list, + &parisc_panic_block); + + return 0; +} + +static void __exit power_exit(void) +{ + kthread_stop(power_task); + + atomic_notifier_chain_unregister(&panic_notifier_list, + &parisc_panic_block); + + pdc_soft_power_button(0); +} + +arch_initcall(power_init); +module_exit(power_exit); + + +MODULE_AUTHOR("Helge Deller "); +MODULE_DESCRIPTION("Soft power switch driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c new file mode 100644 index 000000000..e0a148ab5 --- /dev/null +++ b/drivers/parisc/sba_iommu.c @@ -0,0 +1,2070 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** System Bus Adapter (SBA) I/O MMU manager +** +** (c) Copyright 2000-2004 Grant Grundler +** (c) Copyright 2004 Naresh Kumar Inna +** (c) Copyright 2000-2004 Hewlett-Packard Company +** +** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code) +** +** +** +** This module initializes the IOC (I/O Controller) found on B1000/C3000/ +** J5000/J7000/N-class/L-class machines and their successors. +** +** FIXME: add DMA hint support programming in both sba and lba modules. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +/* + * The semantics of 64 register access on 32bit systems can't be guaranteed + * by the C standard, we hope the _lo_hi() macros defining readq and writeq + * here will behave as expected. + */ +#include + +#include +#include +#include /* for DMA_CHUNK_SIZE */ + +#include /* for register_parisc_driver() stuff */ + +#include +#include +#include + +#include +#include /* for PAGE0 */ +#include /* for PDC_MODEL_* */ +#include /* for is_pdc_pat() */ +#include + +#include "iommu.h" + +#define MODULE_NAME "SBA" + +/* +** The number of debug flags is a clue - this code is fragile. +** Don't even think about messing with it unless you have +** plenty of 710's to sacrifice to the computer gods. :^) +*/ +#undef DEBUG_SBA_INIT +#undef DEBUG_SBA_RUN +#undef DEBUG_SBA_RUN_SG +#undef DEBUG_SBA_RESOURCE +#undef ASSERT_PDIR_SANITY +#undef DEBUG_LARGE_SG_ENTRIES + +#ifdef DEBUG_SBA_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#ifdef DEBUG_SBA_RUN +#define DBG_RUN(x...) printk(x) +#else +#define DBG_RUN(x...) +#endif + +#ifdef DEBUG_SBA_RUN_SG +#define DBG_RUN_SG(x...) printk(x) +#else +#define DBG_RUN_SG(x...) +#endif + + +#ifdef DEBUG_SBA_RESOURCE +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +#define DEFAULT_DMA_HINT_REG 0 + +struct sba_device *sba_list; +EXPORT_SYMBOL_GPL(sba_list); + +static unsigned long ioc_needs_fdc = 0; + +/* global count of IOMMUs in the system */ +static unsigned int global_ioc_cnt = 0; + +/* PA8700 (Piranha 2.2) bug workaround */ +static unsigned long piranha_bad_128k = 0; + +/* Looks nice and keeps the compiler happy */ +#define SBA_DEV(d) ((struct sba_device *) (d)) + +#ifdef CONFIG_AGP_PARISC +#define SBA_AGP_SUPPORT +#endif /*CONFIG_AGP_PARISC*/ + +#ifdef SBA_AGP_SUPPORT +static int sba_reserve_agpgart = 1; +module_param(sba_reserve_agpgart, int, 0444); +MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART"); +#endif + +static struct proc_dir_entry *proc_runway_root __ro_after_init; +static struct proc_dir_entry *proc_mckinley_root __ro_after_init; + +/************************************ +** SBA register read and write support +** +** BE WARNED: register writes are posted. +** (ie follow writes which must reach HW with a read) +** +** Superdome (in particular, REO) allows only 64-bit CSR accesses. +*/ +#define READ_REG32(addr) readl(addr) +#define READ_REG64(addr) readq(addr) +#define WRITE_REG32(val, addr) writel((val), (addr)) +#define WRITE_REG64(val, addr) writeq((val), (addr)) + +#ifdef CONFIG_64BIT +#define READ_REG(addr) READ_REG64(addr) +#define WRITE_REG(value, addr) WRITE_REG64(value, addr) +#else +#define READ_REG(addr) READ_REG32(addr) +#define WRITE_REG(value, addr) WRITE_REG32(value, addr) +#endif + +#ifdef DEBUG_SBA_INIT + +/* NOTE: When CONFIG_64BIT isn't defined, READ_REG64() is two 32-bit reads */ + +/** + * sba_dump_ranges - debugging only - print ranges assigned to this IOA + * @hpa: base address of the sba + * + * Print the MMIO and IO Port address ranges forwarded by an Astro/Ike/RIO + * IO Adapter (aka Bus Converter). + */ +static void +sba_dump_ranges(void __iomem *hpa) +{ + DBG_INIT("SBA at 0x%p\n", hpa); + DBG_INIT("IOS_DIST_BASE : %Lx\n", READ_REG64(hpa+IOS_DIST_BASE)); + DBG_INIT("IOS_DIST_MASK : %Lx\n", READ_REG64(hpa+IOS_DIST_MASK)); + DBG_INIT("IOS_DIST_ROUTE : %Lx\n", READ_REG64(hpa+IOS_DIST_ROUTE)); + DBG_INIT("\n"); + DBG_INIT("IOS_DIRECT_BASE : %Lx\n", READ_REG64(hpa+IOS_DIRECT_BASE)); + DBG_INIT("IOS_DIRECT_MASK : %Lx\n", READ_REG64(hpa+IOS_DIRECT_MASK)); + DBG_INIT("IOS_DIRECT_ROUTE: %Lx\n", READ_REG64(hpa+IOS_DIRECT_ROUTE)); +} + +/** + * sba_dump_tlb - debugging only - print IOMMU operating parameters + * @hpa: base address of the IOMMU + * + * Print the size/location of the IO MMU PDIR. + */ +static void sba_dump_tlb(void __iomem *hpa) +{ + DBG_INIT("IO TLB at 0x%p\n", hpa); + DBG_INIT("IOC_IBASE : 0x%Lx\n", READ_REG64(hpa+IOC_IBASE)); + DBG_INIT("IOC_IMASK : 0x%Lx\n", READ_REG64(hpa+IOC_IMASK)); + DBG_INIT("IOC_TCNFG : 0x%Lx\n", READ_REG64(hpa+IOC_TCNFG)); + DBG_INIT("IOC_PDIR_BASE: 0x%Lx\n", READ_REG64(hpa+IOC_PDIR_BASE)); + DBG_INIT("\n"); +} +#else +#define sba_dump_ranges(x) +#define sba_dump_tlb(x) +#endif /* DEBUG_SBA_INIT */ + + +#ifdef ASSERT_PDIR_SANITY + +/** + * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @msg: text to print ont the output line. + * @pide: pdir index. + * + * Print one entry of the IO MMU PDIR in human readable form. + */ +static void +sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) +{ + /* start printing from lowest pde in rval */ + __le64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]); + unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]); + uint rcnt; + + printk(KERN_DEBUG "SBA: %s rp %p bit %d rval 0x%lx\n", + msg, + rptr, pide & (BITS_PER_LONG - 1), *rptr); + + rcnt = 0; + while (rcnt < BITS_PER_LONG) { + printk(KERN_DEBUG "%s %2d %p %016Lx\n", + (rcnt == (pide & (BITS_PER_LONG - 1))) + ? " -->" : " ", + rcnt, ptr, *ptr ); + rcnt++; + ptr++; + } + printk(KERN_DEBUG "%s", msg); +} + + +/** + * sba_check_pdir - debugging only - consistency checker + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @msg: text to print ont the output line. + * + * Verify the resource map and pdir state is consistent + */ +static int +sba_check_pdir(struct ioc *ioc, char *msg) +{ + u32 *rptr_end = (u32 *) &(ioc->res_map[ioc->res_size]); + u32 *rptr = (u32 *) ioc->res_map; /* resource map ptr */ + u64 *pptr = ioc->pdir_base; /* pdir ptr */ + uint pide = 0; + + while (rptr < rptr_end) { + u32 rval = *rptr; + int rcnt = 32; /* number of bits we might check */ + + while (rcnt) { + /* Get last byte and highest bit from that */ + u32 pde = ((u32) (((char *)pptr)[7])) << 24; + if ((rval ^ pde) & 0x80000000) + { + /* + ** BUMMER! -- res_map != pdir -- + ** Dump rval and matching pdir entries + */ + sba_dump_pdir_entry(ioc, msg, pide); + return(1); + } + rcnt--; + rval <<= 1; /* try the next bit */ + pptr++; + pide++; + } + rptr++; /* look at next word of res_map */ + } + /* It'd be nice if we always got here :^) */ + return 0; +} + + +/** + * sba_dump_sg - debugging only - print Scatter-Gather list + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: head of the SG list + * @nents: number of entries in SG list + * + * print the SG list so we can verify it's correct by hand. + */ +static void +sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +{ + while (nents-- > 0) { + printk(KERN_DEBUG " %d : %08lx/%05x %p/%05x\n", + nents, + (unsigned long) sg_dma_address(startsg), + sg_dma_len(startsg), + sg_virt(startsg), startsg->length); + startsg++; + } +} + +#endif /* ASSERT_PDIR_SANITY */ + + + + +/************************************************************** +* +* I/O Pdir Resource Management +* +* Bits set in the resource map are in use. +* Each bit can represent a number of pages. +* LSbs represent lower addresses (IOVA's). +* +***************************************************************/ +#define PAGES_PER_RANGE 1 /* could increase this to 4 or 8 if needed */ + +/* Convert from IOVP to IOVA and vice versa. */ + +#ifdef ZX1_SUPPORT +/* Pluto (aka ZX1) boxes need to set or clear the ibase bits appropriately */ +#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset)) +#define SBA_IOVP(ioc,iova) ((iova) & (ioc)->iovp_mask) +#else +/* only support Astro and ancestors. Saves a few cycles in key places */ +#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((iovp) | (offset)) +#define SBA_IOVP(ioc,iova) (iova) +#endif + +#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) + +#define RESMAP_MASK(n) (~0UL << (BITS_PER_LONG - (n))) +#define RESMAP_IDX_MASK (sizeof(unsigned long) - 1) + +static unsigned long ptr_to_pide(struct ioc *ioc, unsigned long *res_ptr, + unsigned int bitshiftcnt) +{ + return (((unsigned long)res_ptr - (unsigned long)ioc->res_map) << 3) + + bitshiftcnt; +} + +/** + * sba_search_bitmap - find free space in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @dev: device to query the bitmap for + * @bits_wanted: number of entries we need. + * + * Find consecutive free bits in resource bitmap. + * Each bit represents one entry in the IO Pdir. + * Cool perf optimization: search for log2(size) bits at a time. + */ +static unsigned long +sba_search_bitmap(struct ioc *ioc, struct device *dev, + unsigned long bits_wanted) +{ + unsigned long *res_ptr = ioc->res_hint; + unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]); + unsigned long pide = ~0UL, tpide; + unsigned long boundary_size; + unsigned long shift; + int ret; + + boundary_size = dma_get_seg_boundary_nr_pages(dev, IOVP_SHIFT); + +#if defined(ZX1_SUPPORT) + BUG_ON(ioc->ibase & ~IOVP_MASK); + shift = ioc->ibase >> IOVP_SHIFT; +#else + shift = 0; +#endif + + if (bits_wanted > (BITS_PER_LONG/2)) { + /* Search word at a time - no mask needed */ + for(; res_ptr < res_end; ++res_ptr) { + tpide = ptr_to_pide(ioc, res_ptr, 0); + ret = iommu_is_span_boundary(tpide, bits_wanted, + shift, + boundary_size); + if ((*res_ptr == 0) && !ret) { + *res_ptr = RESMAP_MASK(bits_wanted); + pide = tpide; + break; + } + } + /* point to the next word on next pass */ + res_ptr++; + ioc->res_bitshift = 0; + } else { + /* + ** Search the resource bit map on well-aligned values. + ** "o" is the alignment. + ** We need the alignment to invalidate I/O TLB using + ** SBA HW features in the unmap path. + */ + unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT); + uint bitshiftcnt = ALIGN(ioc->res_bitshift, o); + unsigned long mask; + + if (bitshiftcnt >= BITS_PER_LONG) { + bitshiftcnt = 0; + res_ptr++; + } + mask = RESMAP_MASK(bits_wanted) >> bitshiftcnt; + + DBG_RES("%s() o %ld %p", __func__, o, res_ptr); + while(res_ptr < res_end) + { + DBG_RES(" %p %lx %lx\n", res_ptr, mask, *res_ptr); + WARN_ON(mask == 0); + tpide = ptr_to_pide(ioc, res_ptr, bitshiftcnt); + ret = iommu_is_span_boundary(tpide, bits_wanted, + shift, + boundary_size); + if ((((*res_ptr) & mask) == 0) && !ret) { + *res_ptr |= mask; /* mark resources busy! */ + pide = tpide; + break; + } + mask >>= o; + bitshiftcnt += o; + if (mask == 0) { + mask = RESMAP_MASK(bits_wanted); + bitshiftcnt=0; + res_ptr++; + } + } + /* look in the same word on the next pass */ + ioc->res_bitshift = bitshiftcnt + bits_wanted; + } + + /* wrapped ? */ + if (res_end <= res_ptr) { + ioc->res_hint = (unsigned long *) ioc->res_map; + ioc->res_bitshift = 0; + } else { + ioc->res_hint = res_ptr; + } + return (pide); +} + + +/** + * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @dev: device for which pages should be alloced + * @size: number of bytes to create a mapping for + * + * Given a size, find consecutive unmarked and then mark those bits in the + * resource bit map. + */ +static int +sba_alloc_range(struct ioc *ioc, struct device *dev, size_t size) +{ + unsigned int pages_needed = size >> IOVP_SHIFT; +#ifdef SBA_COLLECT_STATS + unsigned long cr_start = mfctl(16); +#endif + unsigned long pide; + + pide = sba_search_bitmap(ioc, dev, pages_needed); + if (pide >= (ioc->res_size << 3)) { + pide = sba_search_bitmap(ioc, dev, pages_needed); + if (pide >= (ioc->res_size << 3)) + panic("%s: I/O MMU @ %p is out of mapping resources\n", + __FILE__, ioc->ioc_hpa); + } + +#ifdef ASSERT_PDIR_SANITY + /* verify the first enable bit is clear */ + if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) { + sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide); + } +#endif + + DBG_RES("%s(%x) %d -> %lx hint %x/%x\n", + __func__, size, pages_needed, pide, + (uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map), + ioc->res_bitshift ); + +#ifdef SBA_COLLECT_STATS + { + unsigned long cr_end = mfctl(16); + unsigned long tmp = cr_end - cr_start; + /* check for roll over */ + cr_start = (cr_end < cr_start) ? -(tmp) : (tmp); + } + ioc->avg_search[ioc->avg_idx++] = cr_start; + ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1; + + ioc->used_pages += pages_needed; +#endif + + return (pide); +} + + +/** + * sba_free_range - unmark bits in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @iova: IO virtual address which was previously allocated. + * @size: number of bytes to create a mapping for + * + * clear bits in the ioc's resource map + */ +static void +sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size) +{ + unsigned long iovp = SBA_IOVP(ioc, iova); + unsigned int pide = PDIR_INDEX(iovp); + unsigned int ridx = pide >> 3; /* convert bit to byte address */ + unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]); + + int bits_not_wanted = size >> IOVP_SHIFT; + + /* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */ + unsigned long m = RESMAP_MASK(bits_not_wanted) >> (pide & (BITS_PER_LONG - 1)); + + DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n", + __func__, (uint) iova, size, + bits_not_wanted, m, pide, res_ptr, *res_ptr); + +#ifdef SBA_COLLECT_STATS + ioc->used_pages -= bits_not_wanted; +#endif + + *res_ptr &= ~m; +} + + +/************************************************************** +* +* "Dynamic DMA Mapping" support (aka "Coherent I/O") +* +***************************************************************/ + +#ifdef SBA_HINT_SUPPORT +#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir) +#endif + +typedef unsigned long space_t; +#define KERNEL_SPACE 0 + +/** + * sba_io_pdir_entry - fill in one IO PDIR entry + * @pdir_ptr: pointer to IO PDIR entry + * @sid: process Space ID - currently only support KERNEL_SPACE + * @pba: Physical address of buffer to map + * @hint: DMA hint set to use for this mapping + * + * SBA Mapping Routine + * + * Given a virtual address (vba, arg2) and space id, (sid, arg1) + * sba_io_pdir_entry() loads the I/O PDIR entry pointed to by + * pdir_ptr (arg0). + * Using the bass-ackwards HP bit numbering, Each IO Pdir entry + * for Astro/Ike looks like: + * + * + * 0 19 51 55 63 + * +-+---------------------+----------------------------------+----+--------+ + * |V| U | PPN[43:12] | U | VI | + * +-+---------------------+----------------------------------+----+--------+ + * + * Pluto is basically identical, supports fewer physical address bits: + * + * 0 23 51 55 63 + * +-+------------------------+-------------------------------+----+--------+ + * |V| U | PPN[39:12] | U | VI | + * +-+------------------------+-------------------------------+----+--------+ + * + * V == Valid Bit (Most Significant Bit is bit 0) + * U == Unused + * PPN == Physical Page Number + * VI == Virtual Index (aka Coherent Index) + * + * LPA instruction output is put into PPN field. + * LCI (Load Coherence Index) instruction provides the "VI" bits. + * + * We pre-swap the bytes since PCX-W is Big Endian and the + * IOMMU uses little endian for the pdir. + */ + +static void +sba_io_pdir_entry(__le64 *pdir_ptr, space_t sid, phys_addr_t pba, + unsigned long hint) +{ + register unsigned ci; /* coherent index */ + + asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pba))); + pba &= IOVP_MASK; + pba |= (ci >> PAGE_SHIFT) & 0xff; /* move CI (8 bits) into lowest byte */ + + /* set "valid" bit, swap and store into I/O Pdir */ + *pdir_ptr = cpu_to_le64((unsigned long)pba | SBA_PDIR_VALID_BIT); + + /* + * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit set + * (bit #61, big endian), we have to flush and sync every time + * IO-PDIR is changed in Ike/Astro. + */ + asm_io_fdc(pdir_ptr); +} + + +/** + * sba_mark_invalid - invalidate one or more IO PDIR entries + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @iova: IO Virtual Address mapped earlier + * @byte_cnt: number of bytes this mapping covers. + * + * Marking the IO PDIR entry(ies) as Invalid and invalidate + * corresponding IO TLB entry. The Ike PCOM (Purge Command Register) + * is to purge stale entries in the IO TLB when unmapping entries. + * + * The PCOM register supports purging of multiple pages, with a minium + * of 1 page and a maximum of 2GB. Hardware requires the address be + * aligned to the size of the range being purged. The size of the range + * must be a power of 2. The "Cool perf optimization" in the + * allocation routine helps keep that true. + */ +static void +sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) +{ + u32 iovp = (u32) SBA_IOVP(ioc,iova); + __le64 *pdir_ptr = &ioc->pdir_base[PDIR_INDEX(iovp)]; + +#ifdef ASSERT_PDIR_SANITY + /* Assert first pdir entry is set. + ** + ** Even though this is a big-endian machine, the entries + ** in the iopdir are little endian. That's why we look at + ** the byte at +7 instead of at +0. + */ + if (0x80 != (((u8 *) pdir_ptr)[7])) { + sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp)); + } +#endif + + if (byte_cnt > IOVP_SIZE) + { +#if 0 + unsigned long entries_per_cacheline = ioc_needs_fdc ? + L1_CACHE_ALIGN(((unsigned long) pdir_ptr)) + - (unsigned long) pdir_ptr; + : 262144; +#endif + + /* set "size" field for PCOM */ + iovp |= get_order(byte_cnt) + PAGE_SHIFT; + + do { + /* clear I/O Pdir entry "valid" bit first */ + ((u8 *) pdir_ptr)[7] = 0; + asm_io_fdc(pdir_ptr); + if (ioc_needs_fdc) { +#if 0 + entries_per_cacheline = L1_CACHE_SHIFT - 3; +#endif + } + pdir_ptr++; + byte_cnt -= IOVP_SIZE; + } while (byte_cnt > IOVP_SIZE); + } else + iovp |= IOVP_SHIFT; /* set "size" field for PCOM */ + + /* + ** clear I/O PDIR entry "valid" bit. + ** We have to R/M/W the cacheline regardless how much of the + ** pdir entry that we clobber. + ** The rest of the entry would be useful for debugging if we + ** could dump core on HPMC. + */ + ((u8 *) pdir_ptr)[7] = 0; + asm_io_fdc(pdir_ptr); + + WRITE_REG( SBA_IOVA(ioc, iovp, 0, 0), ioc->ioc_hpa+IOC_PCOM); +} + +/** + * sba_dma_supported - PCI driver can query DMA support + * @dev: instance of PCI owned by the driver that's asking + * @mask: number of address bits this PCI device can handle + * + * See Documentation/core-api/dma-api-howto.rst + */ +static int sba_dma_supported( struct device *dev, u64 mask) +{ + struct ioc *ioc; + + if (dev == NULL) { + printk(KERN_ERR MODULE_NAME ": EISA/ISA/et al not supported\n"); + BUG(); + return(0); + } + + ioc = GET_IOC(dev); + if (!ioc) + return 0; + + /* + * check if mask is >= than the current max IO Virt Address + * The max IO Virt address will *always* < 30 bits. + */ + return((int)(mask >= (ioc->ibase - 1 + + (ioc->pdir_size / sizeof(u64) * IOVP_SIZE) ))); +} + + +/** + * sba_map_single - map one buffer and return IOVA for DMA + * @dev: instance of PCI owned by the driver that's asking. + * @addr: driver buffer to map. + * @size: number of bytes to map in driver buffer. + * @direction: R/W or both. + * + * See Documentation/core-api/dma-api-howto.rst + */ +static dma_addr_t +sba_map_single(struct device *dev, phys_addr_t addr, size_t size, + enum dma_data_direction direction) +{ + struct ioc *ioc; + unsigned long flags; + dma_addr_t iovp; + dma_addr_t offset; + __le64 *pdir_start; + int pide; + + ioc = GET_IOC(dev); + if (!ioc) + return DMA_MAPPING_ERROR; + + /* save offset bits */ + offset = offset_in_page(addr); + + /* round up to nearest IOVP_SIZE */ + size = (size + offset + ~IOVP_MASK) & IOVP_MASK; + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef ASSERT_PDIR_SANITY + sba_check_pdir(ioc,"Check before sba_map_single()"); +#endif + +#ifdef SBA_COLLECT_STATS + ioc->msingle_calls++; + ioc->msingle_pages += size >> IOVP_SHIFT; +#endif + pide = sba_alloc_range(ioc, dev, size); + iovp = (dma_addr_t) pide << IOVP_SHIFT; + + DBG_RUN("%s() 0x%pa -> 0x%lx\n", + __func__, &addr, (long) iovp | offset); + + pdir_start = &(ioc->pdir_base[pide]); + + while (size > 0) { + sba_io_pdir_entry(pdir_start, KERNEL_SPACE, addr, 0); + + DBG_RUN(" pdir 0x%p %02x%02x%02x%02x%02x%02x%02x%02x\n", + pdir_start, + (u8) (((u8 *) pdir_start)[7]), + (u8) (((u8 *) pdir_start)[6]), + (u8) (((u8 *) pdir_start)[5]), + (u8) (((u8 *) pdir_start)[4]), + (u8) (((u8 *) pdir_start)[3]), + (u8) (((u8 *) pdir_start)[2]), + (u8) (((u8 *) pdir_start)[1]), + (u8) (((u8 *) pdir_start)[0]) + ); + + addr += IOVP_SIZE; + size -= IOVP_SIZE; + pdir_start++; + } + + /* force FDC ops in io_pdir_entry() to be visible to IOMMU */ + asm_io_sync(); + +#ifdef ASSERT_PDIR_SANITY + sba_check_pdir(ioc,"Check after sba_map_single()"); +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); + + /* form complete address */ + return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG); +} + + +static dma_addr_t +sba_map_phys(struct device *dev, phys_addr_t phys, size_t size, + enum dma_data_direction direction, unsigned long attrs) +{ + if (unlikely(attrs & DMA_ATTR_MMIO)) + return DMA_MAPPING_ERROR; + + return sba_map_single(dev, phys, size, direction); +} + + +/** + * sba_unmap_phys - unmap one IOVA and free resources + * @dev: instance of PCI owned by the driver that's asking. + * @iova: IOVA of driver buffer previously mapped. + * @size: number of bytes mapped in driver buffer. + * @direction: R/W or both. + * @attrs: attributes + * + * See Documentation/core-api/dma-api-howto.rst + */ +static void +sba_unmap_phys(struct device *dev, dma_addr_t iova, size_t size, + enum dma_data_direction direction, unsigned long attrs) +{ + struct ioc *ioc; +#if DELAYED_RESOURCE_CNT > 0 + struct sba_dma_pair *d; +#endif + unsigned long flags; + dma_addr_t offset; + + DBG_RUN("%s() iovp 0x%lx/%x\n", __func__, (long) iova, size); + + ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } + offset = iova & ~IOVP_MASK; + iova ^= offset; /* clear offset bits */ + size += offset; + size = ALIGN(size, IOVP_SIZE); + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef SBA_COLLECT_STATS + ioc->usingle_calls++; + ioc->usingle_pages += size >> IOVP_SHIFT; +#endif + + sba_mark_invalid(ioc, iova, size); + +#if DELAYED_RESOURCE_CNT > 0 + /* Delaying when we re-use a IO Pdir entry reduces the number + * of MMIO reads needed to flush writes to the PCOM register. + */ + d = &(ioc->saved[ioc->saved_cnt]); + d->iova = iova; + d->size = size; + if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) { + int cnt = ioc->saved_cnt; + while (cnt--) { + sba_free_range(ioc, d->iova, d->size); + d--; + } + ioc->saved_cnt = 0; + + READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ + } +#else /* DELAYED_RESOURCE_CNT == 0 */ + sba_free_range(ioc, iova, size); + + /* If fdc's were issued, force fdc's to be visible now */ + asm_io_sync(); + + READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ +#endif /* DELAYED_RESOURCE_CNT == 0 */ + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + /* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support. + ** For Astro based systems this isn't a big deal WRT performance. + ** As long as 2.4 kernels copyin/copyout data from/to userspace, + ** we don't need the syncdma. The issue here is I/O MMU cachelines + ** are *not* coherent in all cases. May be hwrev dependent. + ** Need to investigate more. + asm volatile("syncdma"); + */ +} + + +/** + * sba_alloc - allocate/map shared mem for DMA + * @hwdev: instance of PCI owned by the driver that's asking. + * @size: number of bytes mapped in driver buffer. + * @dma_handle: IOVA of new buffer. + * @gfp: allocation flags + * @attrs: attributes + * + * See Documentation/core-api/dma-api-howto.rst + */ +static void *sba_alloc(struct device *hwdev, size_t size, dma_addr_t *dma_handle, + gfp_t gfp, unsigned long attrs) +{ + void *ret; + + if (!hwdev) { + /* only support PCI */ + *dma_handle = 0; + return NULL; + } + + ret = (void *) __get_free_pages(gfp, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = sba_map_single(hwdev, virt_to_phys(ret), size, 0); + } + + return ret; +} + + +/** + * sba_free - free/unmap shared mem for DMA + * @hwdev: instance of PCI owned by the driver that's asking. + * @size: number of bytes mapped in driver buffer. + * @vaddr: virtual address IOVA of "consistent" buffer. + * @dma_handle: IO virtual address of "consistent" buffer. + * @attrs: attributes + * + * See Documentation/core-api/dma-api-howto.rst + */ +static void +sba_free(struct device *hwdev, size_t size, void *vaddr, + dma_addr_t dma_handle, unsigned long attrs) +{ + sba_unmap_phys(hwdev, dma_handle, size, 0, 0); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +/* +** Since 0 is a valid pdir_base index value, can't use that +** to determine if a value is valid or not. Use a flag to indicate +** the SG list entry contains a valid pdir index. +*/ +#define PIDE_FLAG 0x80000000UL + +#ifdef SBA_COLLECT_STATS +#define IOMMU_MAP_STATS +#endif +#include "iommu-helpers.h" + +#ifdef DEBUG_LARGE_SG_ENTRIES +int dump_run_sg = 0; +#endif + + +/** + * sba_map_sg - map Scatter/Gather list + * @dev: instance of PCI owned by the driver that's asking. + * @sglist: array of buffer/length pairs + * @nents: number of entries in list + * @direction: R/W or both. + * @attrs: attributes + * + * See Documentation/core-api/dma-api-howto.rst + */ +static int +sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, + enum dma_data_direction direction, unsigned long attrs) +{ + struct ioc *ioc; + int filled = 0; + unsigned long flags; + + DBG_RUN_SG("%s() START %d entries\n", __func__, nents); + + ioc = GET_IOC(dev); + if (!ioc) + return -EINVAL; + + /* Fast path single entry scatterlists. */ + if (nents == 1) { + sg_dma_address(sglist) = sba_map_single(dev, sg_phys(sglist), + sglist->length, direction); + sg_dma_len(sglist) = sglist->length; + return 1; + } + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check before sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check before sba_map_sg()"); + } +#endif + +#ifdef SBA_COLLECT_STATS + ioc->msg_calls++; +#endif + + /* + ** First coalesce the chunks and allocate I/O pdir space + ** + ** If this is one DMA stream, we can properly map using the + ** correct virtual address associated with each DMA page. + ** w/o this association, we wouldn't have coherent DMA! + ** Access to the virtual address is what forces a two pass algorithm. + */ + iommu_coalesce_chunks(ioc, dev, sglist, nents, sba_alloc_range); + + /* + ** Program the I/O Pdir + ** + ** map the virtual addresses to the I/O Pdir + ** o dma_address will contain the pdir index + ** o dma_len will contain the number of bytes to map + ** o address contains the virtual address. + */ + filled = iommu_fill_pdir(ioc, sglist, nents, 0, sba_io_pdir_entry); + + /* force FDC ops in io_pdir_entry() to be visible to IOMMU */ + asm_io_sync(); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check after sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check after sba_map_sg()\n"); + } +#endif + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + DBG_RUN_SG("%s() DONE %d mappings\n", __func__, filled); + + return filled; +} + + +/** + * sba_unmap_sg - unmap Scatter/Gather list + * @dev: instance of PCI owned by the driver that's asking. + * @sglist: array of buffer/length pairs + * @nents: number of entries in list + * @direction: R/W or both. + * @attrs: attributes + * + * See Documentation/core-api/dma-api-howto.rst + */ +static void +sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, + enum dma_data_direction direction, unsigned long attrs) +{ + struct ioc *ioc; +#ifdef ASSERT_PDIR_SANITY + unsigned long flags; +#endif + + DBG_RUN_SG("%s() START %d entries, %p,%x\n", + __func__, nents, sg_virt(sglist), sglist->length); + + ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } + +#ifdef SBA_COLLECT_STATS + ioc->usg_calls++; +#endif + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check before sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + + while (nents && sg_dma_len(sglist)) { + + sba_unmap_phys(dev, sg_dma_address(sglist), sg_dma_len(sglist), + direction, 0); +#ifdef SBA_COLLECT_STATS + ioc->usg_pages += ((sg_dma_address(sglist) & ~IOVP_MASK) + sg_dma_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT; + ioc->usingle_calls--; /* kluge since call is unmap_sg() */ +#endif + ++sglist; + nents--; + } + + DBG_RUN_SG("%s() DONE (nents %d)\n", __func__, nents); + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check after sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + +} + +static const struct dma_map_ops sba_ops = { + .dma_supported = sba_dma_supported, + .alloc = sba_alloc, + .free = sba_free, + .map_phys = sba_map_phys, + .unmap_phys = sba_unmap_phys, + .map_sg = sba_map_sg, + .unmap_sg = sba_unmap_sg, + .get_sgtable = dma_common_get_sgtable, + .alloc_pages_op = dma_common_alloc_pages, + .free_pages = dma_common_free_pages, +}; + + +/************************************************************************** +** +** SBA PAT PDC support +** +** o call pdc_pat_cell_module() +** o store ranges in PCI "resource" structures +** +**************************************************************************/ + +static void +sba_get_pat_resources(struct sba_device *sba_dev) +{ +#if 0 +/* +** TODO/REVISIT/FIXME: support for directed ranges requires calls to +** PAT PDC to program the SBA/LBA directed range registers...this +** burden may fall on the LBA code since it directly supports the +** PCI subsystem. It's not clear yet. - ggg +*/ +PAT_MOD(mod)->mod_info.mod_pages = PAT_GET_MOD_PAGES(temp); + FIXME : ??? +PAT_MOD(mod)->mod_info.dvi = PAT_GET_DVI(temp); + Tells where the dvi bits are located in the address. +PAT_MOD(mod)->mod_info.ioc = PAT_GET_IOC(temp); + FIXME : ??? +#endif +} + + +/************************************************************** +* +* Initialization and claim +* +***************************************************************/ +#define PIRANHA_ADDR_MASK 0x00160000UL /* bit 17,18,20 */ +#define PIRANHA_ADDR_VAL 0x00060000UL /* bit 17,18 on */ +static void * +sba_alloc_pdir(unsigned int pdir_size) +{ + unsigned long pdir_base; + unsigned long pdir_order = get_order(pdir_size); + + pdir_base = __get_free_pages(GFP_KERNEL, pdir_order); + if (NULL == (void *) pdir_base) { + panic("%s() could not allocate I/O Page Table\n", + __func__); + } + + /* If this is not PA8700 (PCX-W2) + ** OR newer than ver 2.2 + ** OR in a system that doesn't need VINDEX bits from SBA, + ** + ** then we aren't exposed to the HW bug. + */ + if ( ((boot_cpu_data.pdc.cpuid >> 5) & 0x7f) != 0x13 + || (boot_cpu_data.pdc.versions > 0x202) + || (boot_cpu_data.pdc.capabilities & 0x08L) ) + return (void *) pdir_base; + + /* + * PA8700 (PCX-W2, aka piranha) silent data corruption fix + * + * An interaction between PA8700 CPU (Ver 2.2 or older) and + * Ike/Astro can cause silent data corruption. This is only + * a problem if the I/O PDIR is located in memory such that + * (little-endian) bits 17 and 18 are on and bit 20 is off. + * + * Since the max IO Pdir size is 2MB, by cleverly allocating the + * right physical address, we can either avoid (IOPDIR <= 1MB) + * or minimize (2MB IO Pdir) the problem if we restrict the + * IO Pdir to a maximum size of 2MB-128K (1902K). + * + * Because we always allocate 2^N sized IO pdirs, either of the + * "bad" regions will be the last 128K if at all. That's easy + * to test for. + * + */ + if (pdir_order <= (19-12)) { + if (((virt_to_phys(pdir_base)+pdir_size-1) & PIRANHA_ADDR_MASK) == PIRANHA_ADDR_VAL) { + /* allocate a new one on 512k alignment */ + unsigned long new_pdir = __get_free_pages(GFP_KERNEL, (19-12)); + /* release original */ + free_pages(pdir_base, pdir_order); + + pdir_base = new_pdir; + + /* release excess */ + while (pdir_order < (19-12)) { + new_pdir += pdir_size; + free_pages(new_pdir, pdir_order); + pdir_order +=1; + pdir_size <<=1; + } + } + } else { + /* + ** 1MB or 2MB Pdir + ** Needs to be aligned on an "odd" 1MB boundary. + */ + unsigned long new_pdir = __get_free_pages(GFP_KERNEL, pdir_order+1); /* 2 or 4MB */ + + /* release original */ + free_pages( pdir_base, pdir_order); + + /* release first 1MB */ + free_pages(new_pdir, 20-12); + + pdir_base = new_pdir + 1024*1024; + + if (pdir_order > (20-12)) { + /* + ** 2MB Pdir. + ** + ** Flag tells init_bitmap() to mark bad 128k as used + ** and to reduce the size by 128k. + */ + piranha_bad_128k = 1; + + new_pdir += 3*1024*1024; + /* release last 1MB */ + free_pages(new_pdir, 20-12); + + /* release unusable 128KB */ + free_pages(new_pdir - 128*1024 , 17-12); + + pdir_size -= 128*1024; + } + } + + memset((void *) pdir_base, 0, pdir_size); + return (void *) pdir_base; +} + +struct ibase_data_struct { + struct ioc *ioc; + int ioc_num; +}; + +static int setup_ibase_imask_callback(struct device *dev, void *data) +{ + struct parisc_device *lba = to_parisc_device(dev); + struct ibase_data_struct *ibd = data; + int rope_num = (lba->hpa.start >> 13) & 0xf; + if (rope_num >> 3 == ibd->ioc_num) + lba_set_iregs(lba, ibd->ioc->ibase, ibd->ioc->imask); + return 0; +} + +/* setup Mercury or Elroy IBASE/IMASK registers. */ +static void +setup_ibase_imask(struct parisc_device *sba, struct ioc *ioc, int ioc_num) +{ + struct ibase_data_struct ibase_data = { + .ioc = ioc, + .ioc_num = ioc_num, + }; + + device_for_each_child(&sba->dev, &ibase_data, + setup_ibase_imask_callback); +} + +#ifdef SBA_AGP_SUPPORT +static int +sba_ioc_find_quicksilver(struct device *dev, void *data) +{ + int *agp_found = data; + struct parisc_device *lba = to_parisc_device(dev); + + if (IS_QUICKSILVER(lba)) + *agp_found = 1; + return 0; +} +#endif + +static void +sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num) +{ + u32 iova_space_mask; + u32 iova_space_size; + int iov_order, tcnfg; +#ifdef SBA_AGP_SUPPORT + int agp_found = 0; +#endif + /* + ** Firmware programs the base and size of a "safe IOVA space" + ** (one that doesn't overlap memory or LMMIO space) in the + ** IBASE and IMASK registers. + */ + ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & ~0x1fffffULL; + iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1; + + if ((ioc->ibase < 0xfed00000UL) && ((ioc->ibase + iova_space_size) > 0xfee00000UL)) { + printk("WARNING: IOV space overlaps local config and interrupt message, truncating\n"); + iova_space_size /= 2; + } + + /* + ** iov_order is always based on a 1GB IOVA space since we want to + ** turn on the other half for AGP GART. + */ + iov_order = get_order(iova_space_size >> (IOVP_SHIFT - PAGE_SHIFT)); + ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64); + + DBG_INIT("%s() hpa 0x%p IOV %dMB (%d bits)\n", + __func__, ioc->ioc_hpa, iova_space_size >> 20, + iov_order + PAGE_SHIFT); + + ioc->pdir_base = (void *) __get_free_pages(GFP_KERNEL, + get_order(ioc->pdir_size)); + if (!ioc->pdir_base) + panic("Couldn't allocate I/O Page Table\n"); + + memset(ioc->pdir_base, 0, ioc->pdir_size); + + DBG_INIT("%s() pdir %p size %x\n", + __func__, ioc->pdir_base, ioc->pdir_size); + +#ifdef SBA_HINT_SUPPORT + ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; + ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); + + DBG_INIT(" hint_shift_pdir %x hint_mask_pdir %lx\n", + ioc->hint_shift_pdir, ioc->hint_mask_pdir); +#endif + + WARN_ON((((unsigned long) ioc->pdir_base) & PAGE_MASK) != (unsigned long) ioc->pdir_base); + WRITE_REG(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE); + + /* build IMASK for IOC and Elroy */ + iova_space_mask = 0xffffffff; + iova_space_mask <<= (iov_order + PAGE_SHIFT); + ioc->imask = iova_space_mask; +#ifdef ZX1_SUPPORT + ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1); +#endif + sba_dump_tlb(ioc->ioc_hpa); + + setup_ibase_imask(sba, ioc, ioc_num); + + WRITE_REG(ioc->imask, ioc->ioc_hpa + IOC_IMASK); + +#ifdef CONFIG_64BIT + /* + ** Setting the upper bits makes checking for bypass addresses + ** a little faster later on. + */ + ioc->imask |= 0xFFFFFFFF00000000UL; +#endif + + /* Set I/O PDIR Page size to system page size */ + switch (PAGE_SHIFT) { + case 12: tcnfg = 0; break; /* 4K */ + case 13: tcnfg = 1; break; /* 8K */ + case 14: tcnfg = 2; break; /* 16K */ + case 16: tcnfg = 3; break; /* 64K */ + default: + panic(__FILE__ "Unsupported system page size %d", + 1 << PAGE_SHIFT); + break; + } + WRITE_REG(tcnfg, ioc->ioc_hpa + IOC_TCNFG); + + /* + ** Program the IOC's ibase and enable IOVA translation + ** Bit zero == enable bit. + */ + WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa + IOC_IBASE); + + /* + ** Clear I/O TLB of any possible entries. + ** (Yes. This is a bit paranoid...but so what) + */ + WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM); + +#ifdef SBA_AGP_SUPPORT + + /* + ** If an AGP device is present, only use half of the IOV space + ** for PCI DMA. Unfortunately we can't know ahead of time + ** whether GART support will actually be used, for now we + ** can just key on any AGP device found in the system. + ** We program the next pdir index after we stop w/ a key for + ** the GART code to handshake on. + */ + device_for_each_child(&sba->dev, &agp_found, sba_ioc_find_quicksilver); + + if (agp_found && sba_reserve_agpgart) { + printk(KERN_INFO "%s: reserving %dMb of IOVA space for agpgart\n", + __func__, (iova_space_size/2) >> 20); + ioc->pdir_size /= 2; + ioc->pdir_base[PDIR_INDEX(iova_space_size/2)] = SBA_AGPGART_COOKIE; + } +#endif /*SBA_AGP_SUPPORT*/ +} + +static void +sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) +{ + u32 iova_space_size, iova_space_mask; + unsigned int pdir_size, iov_order, tcnfg; + + /* + ** Determine IOVA Space size from memory size. + ** + ** Ideally, PCI drivers would register the maximum number + ** of DMA they can have outstanding for each device they + ** own. Next best thing would be to guess how much DMA + ** can be outstanding based on PCI Class/sub-class. Both + ** methods still require some "extra" to support PCI + ** Hot-Plug/Removal of PCI cards. (aka PCI OLARD). + ** + ** While we have 32-bits "IOVA" space, top two 2 bits are used + ** for DMA hints - ergo only 30 bits max. + */ + + iova_space_size = (u32) (totalram_pages()/global_ioc_cnt); + + /* limit IOVA space size to 1MB-1GB */ + if (iova_space_size < (1 << (20 - PAGE_SHIFT))) { + iova_space_size = 1 << (20 - PAGE_SHIFT); + } + else if (iova_space_size > (1 << (30 - PAGE_SHIFT))) { + iova_space_size = 1 << (30 - PAGE_SHIFT); + } + + /* + ** iova space must be log2() in size. + ** thus, pdir/res_map will also be log2(). + ** PIRANHA BUG: Exception is when IO Pdir is 2MB (gets reduced) + */ + iov_order = get_order(iova_space_size << PAGE_SHIFT); + + /* iova_space_size is now bytes, not pages */ + iova_space_size = 1 << (iov_order + PAGE_SHIFT); + + ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); + + DBG_INIT("%s() hpa %px mem %ldMB IOV %dMB (%d bits)\n", + __func__, + ioc->ioc_hpa, + (unsigned long) totalram_pages() >> (20 - PAGE_SHIFT), + iova_space_size>>20, + iov_order + PAGE_SHIFT); + + ioc->pdir_base = sba_alloc_pdir(pdir_size); + + DBG_INIT("%s() pdir %p size %x\n", + __func__, ioc->pdir_base, pdir_size); + +#ifdef SBA_HINT_SUPPORT + /* FIXME : DMA HINTs not used */ + ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; + ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); + + DBG_INIT(" hint_shift_pdir %x hint_mask_pdir %lx\n", + ioc->hint_shift_pdir, ioc->hint_mask_pdir); +#endif + + WRITE_REG64(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE); + + /* build IMASK for IOC and Elroy */ + iova_space_mask = 0xffffffff; + iova_space_mask <<= (iov_order + PAGE_SHIFT); + + /* + ** On C3000 w/512MB mem, HP-UX 10.20 reports: + ** ibase=0, imask=0xFE000000, size=0x2000000. + */ + ioc->ibase = 0; + ioc->imask = iova_space_mask; /* save it */ +#ifdef ZX1_SUPPORT + ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1); +#endif + + DBG_INIT("%s() IOV base %#lx mask %#0lx\n", + __func__, ioc->ibase, ioc->imask); + + /* + ** FIXME: Hint registers are programmed with default hint + ** values during boot, so hints should be sane even if we + ** can't reprogram them the way drivers want. + */ + + setup_ibase_imask(sba, ioc, ioc_num); + + /* + ** Program the IOC's ibase and enable IOVA translation + */ + WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE); + WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK); + + /* Set I/O PDIR Page size to system page size */ + switch (PAGE_SHIFT) { + case 12: tcnfg = 0; break; /* 4K */ + case 13: tcnfg = 1; break; /* 8K */ + case 14: tcnfg = 2; break; /* 16K */ + case 16: tcnfg = 3; break; /* 64K */ + default: + panic(__FILE__ "Unsupported system page size %d", + 1 << PAGE_SHIFT); + break; + } + /* Set I/O PDIR Page size to PAGE_SIZE (4k/16k/...) */ + WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG); + + /* + ** Clear I/O TLB of any possible entries. + ** (Yes. This is a bit paranoid...but so what) + */ + WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM); + + ioc->ibase = 0; /* used by SBA_IOVA and related macros */ + + DBG_INIT("%s() DONE\n", __func__); +} + + + +/************************************************************************** +** +** SBA initialization code (HW and SW) +** +** o identify SBA chip itself +** o initialize SBA chip modes (HardFail) +** o initialize SBA chip modes (HardFail) +** o FIXME: initialize DMA hints for reasonable defaults +** +**************************************************************************/ + +static void __iomem *ioc_remap(struct sba_device *sba_dev, unsigned int offset) +{ + return ioremap(sba_dev->dev->hpa.start + offset, SBA_FUNC_SIZE); +} + +static void sba_hw_init(struct sba_device *sba_dev) +{ + int i; + int num_ioc; + u64 ioc_ctl; + + if (!is_pdc_pat()) { + /* Shutdown the USB controller on Astro-based workstations. + ** Once we reprogram the IOMMU, the next DMA performed by + ** USB will HPMC the box. USB is only enabled if a + ** keyboard is present and found. + ** + ** With serial console, j6k v5.0 firmware says: + ** mem_kbd hpa 0xfee003f8 sba 0x0 pad 0x0 cl_class 0x7 + ** + ** FIXME: Using GFX+USB console at power up but direct + ** linux to serial console is still broken. + ** USB could generate DMA so we must reset USB. + ** The proper sequence would be: + ** o block console output + ** o reset USB device + ** o reprogram serial port + ** o unblock console output + */ + if (PAGE0->mem_kbd.cl_class == CL_KEYBD) { + pdc_io_reset_devices(); + } + + } + + +#if 0 +printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, + PAGE0->mem_boot.spa, PAGE0->mem_boot.pad, PAGE0->mem_boot.cl_class); + + /* + ** Need to deal with DMA from LAN. + ** Maybe use page zero boot device as a handle to talk + ** to PDC about which device to shutdown. + ** + ** Netbooting, j6k v5.0 firmware says: + ** mem_boot hpa 0xf4008000 sba 0x0 pad 0x0 cl_class 0x1002 + ** ARGH! invalid class. + */ + if ((PAGE0->mem_boot.cl_class != CL_RANDOM) + && (PAGE0->mem_boot.cl_class != CL_SEQU)) { + pdc_io_reset(); + } +#endif + + if (!IS_PLUTO(sba_dev->dev)) { + ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL); + DBG_INIT("%s() hpa %px ioc_ctl 0x%Lx ->", + __func__, sba_dev->sba_hpa, ioc_ctl); + ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE); + ioc_ctl |= IOC_CTRL_DD | IOC_CTRL_D4 | IOC_CTRL_TC; + /* j6700 v1.6 firmware sets 0x294f */ + /* A500 firmware sets 0x4d */ + + WRITE_REG(ioc_ctl, sba_dev->sba_hpa+IOC_CTRL); + +#ifdef DEBUG_SBA_INIT + ioc_ctl = READ_REG64(sba_dev->sba_hpa+IOC_CTRL); + DBG_INIT(" 0x%Lx\n", ioc_ctl); +#endif + } /* if !PLUTO */ + + if (IS_ASTRO(sba_dev->dev)) { + int err; + sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, ASTRO_IOC_OFFSET); + num_ioc = 1; + + sba_dev->chip_resv.name = "Astro Intr Ack"; + sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfef00000UL; + sba_dev->chip_resv.end = PCI_F_EXTEND | (0xff000000UL - 1) ; + err = request_resource(&iomem_resource, &(sba_dev->chip_resv)); + BUG_ON(err < 0); + + } else if (IS_PLUTO(sba_dev->dev)) { + int err; + + sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, PLUTO_IOC_OFFSET); + num_ioc = 1; + + sba_dev->chip_resv.name = "Pluto Intr/PIOP/VGA"; + sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfee00000UL; + sba_dev->chip_resv.end = PCI_F_EXTEND | (0xff200000UL - 1); + err = request_resource(&iomem_resource, &(sba_dev->chip_resv)); + WARN_ON(err < 0); + + sba_dev->iommu_resv.name = "IOVA Space"; + sba_dev->iommu_resv.start = 0x40000000UL; + sba_dev->iommu_resv.end = 0x50000000UL - 1; + err = request_resource(&iomem_resource, &(sba_dev->iommu_resv)); + WARN_ON(err < 0); + } else { + /* IKE, REO */ + sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(0)); + sba_dev->ioc[1].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(1)); + num_ioc = 2; + + /* TODO - LOOKUP Ike/Stretch chipset mem map */ + } + /* XXX: What about Reo Grande? */ + + sba_dev->num_ioc = num_ioc; + for (i = 0; i < num_ioc; i++) { + void __iomem *ioc_hpa = sba_dev->ioc[i].ioc_hpa; + unsigned int j; + + for (j=0; j < sizeof(u64) * ROPES_PER_IOC; j+=sizeof(u64)) { + + /* + * Clear ROPE(N)_CONFIG AO bit. + * Disables "NT Ordering" (~= !"Relaxed Ordering") + * Overrides bit 1 in DMA Hint Sets. + * Improves netperf UDP_STREAM by ~10% for bcm5701. + */ + if (IS_PLUTO(sba_dev->dev)) { + void __iomem *rope_cfg; + unsigned long cfg_val; + + rope_cfg = ioc_hpa + IOC_ROPE0_CFG + j; + cfg_val = READ_REG(rope_cfg); + cfg_val &= ~IOC_ROPE_AO; + WRITE_REG(cfg_val, rope_cfg); + } + + /* + ** Make sure the box crashes on rope errors. + */ + WRITE_REG(HF_ENABLE, ioc_hpa + ROPE0_CTL + j); + } + + /* flush out the last writes */ + READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); + + DBG_INIT(" ioc[%d] ROPE_CFG %#lx ROPE_DBG %lx\n", + i, + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40), + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50) + ); + DBG_INIT(" STATUS_CONTROL %#lx FLUSH_CTRL %#lx\n", + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108), + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) + ); + + if (IS_PLUTO(sba_dev->dev)) { + sba_ioc_init_pluto(sba_dev->dev, &(sba_dev->ioc[i]), i); + } else { + sba_ioc_init(sba_dev->dev, &(sba_dev->ioc[i]), i); + } + } +} + +static void +sba_common_init(struct sba_device *sba_dev) +{ + int i; + + /* add this one to the head of the list (order doesn't matter) + ** This will be useful for debugging - especially if we get coredumps + */ + sba_dev->next = sba_list; + sba_list = sba_dev; + + for(i=0; i< sba_dev->num_ioc; i++) { + int res_size; + /* resource map size dictated by pdir_size */ + res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */ + + /* Second part of PIRANHA BUG */ + if (piranha_bad_128k) { + res_size -= (128*1024)/sizeof(u64); + } + + res_size >>= 3; /* convert bit count to byte count */ + DBG_INIT("%s() res_size 0x%x\n", + __func__, res_size); + + sba_dev->ioc[i].res_size = res_size; + sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size)); + if (NULL == sba_dev->ioc[i].res_map) + { + panic("%s:%s() could not allocate resource map\n", + __FILE__, __func__ ); + } + + memset(sba_dev->ioc[i].res_map, 0, res_size); + /* next available IOVP - circular search */ + sba_dev->ioc[i].res_hint = (unsigned long *) + &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]); + +#ifdef ASSERT_PDIR_SANITY + /* Mark first bit busy - ie no IOVA 0 */ + sba_dev->ioc[i].res_map[0] = 0x80; + sba_dev->ioc[i].pdir_base[0] = (__force __le64) 0xeeffc0addbba0080ULL; +#endif + + /* Third (and last) part of PIRANHA BUG */ + if (piranha_bad_128k) { + /* region from +1408K to +1536 is un-usable. */ + + int idx_start = (1408*1024/sizeof(u64)) >> 3; + int idx_end = (1536*1024/sizeof(u64)) >> 3; + long *p_start = (long *) &(sba_dev->ioc[i].res_map[idx_start]); + long *p_end = (long *) &(sba_dev->ioc[i].res_map[idx_end]); + + /* mark that part of the io pdir busy */ + while (p_start < p_end) + *p_start++ = -1; + + } + DBG_INIT("%s() %d res_map %x %p\n", + __func__, i, res_size, sba_dev->ioc[i].res_map); + } + + spin_lock_init(&sba_dev->sba_lock); + ioc_needs_fdc = boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC; + +#ifdef DEBUG_SBA_INIT + /* + * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit set + * (bit #61, big endian), we have to flush and sync every time + * IO-PDIR is changed in Ike/Astro. + */ + if (ioc_needs_fdc) { + printk(KERN_INFO MODULE_NAME " FDC/SYNC required.\n"); + } else { + printk(KERN_INFO MODULE_NAME " IOC has cache coherent PDIR.\n"); + } +#endif +} + +#ifdef CONFIG_PROC_FS +static int sba_proc_info(struct seq_file *m, void *p) +{ + struct sba_device *sba_dev = sba_list; + struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */ + int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */ +#ifdef SBA_COLLECT_STATS + unsigned long avg = 0, min, max; +#endif + int i; + + seq_printf(m, "%s rev %d.%d\n", + sba_dev->name, + (sba_dev->hw_rev & 0x7) + 1, + (sba_dev->hw_rev & 0x18) >> 3); + seq_printf(m, "IO PDIR size : %d bytes (%d entries)\n", + (int)((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */ + total_pages); + + seq_printf(m, "Resource bitmap : %d bytes (%d pages)\n", + ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ + + seq_printf(m, "LMMIO_BASE/MASK/ROUTE %08x %08x %08x\n", + READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_BASE), + READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_MASK), + READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_ROUTE)); + + for (i=0; i<4; i++) + seq_printf(m, "DIR%d_BASE/MASK/ROUTE %08x %08x %08x\n", + i, + READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_BASE + i*0x18), + READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_MASK + i*0x18), + READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_ROUTE + i*0x18)); + +#ifdef SBA_COLLECT_STATS + seq_printf(m, "IO PDIR entries : %ld free %ld used (%d%%)\n", + total_pages - ioc->used_pages, ioc->used_pages, + (int)(ioc->used_pages * 100 / total_pages)); + + min = max = ioc->avg_search[0]; + for (i = 0; i < SBA_SEARCH_SAMPLE; i++) { + avg += ioc->avg_search[i]; + if (ioc->avg_search[i] > max) max = ioc->avg_search[i]; + if (ioc->avg_search[i] < min) min = ioc->avg_search[i]; + } + avg /= SBA_SEARCH_SAMPLE; + seq_printf(m, " Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", + min, avg, max); + + seq_printf(m, "pci_map_single(): %12ld calls %12ld pages (avg %d/1000)\n", + ioc->msingle_calls, ioc->msingle_pages, + (int)((ioc->msingle_pages * 1000)/ioc->msingle_calls)); + + /* KLUGE - unmap_sg calls unmap_single for each mapped page */ + min = ioc->usingle_calls; + max = ioc->usingle_pages - ioc->usg_pages; + seq_printf(m, "pci_unmap_single: %12ld calls %12ld pages (avg %d/1000)\n", + min, max, (int)((max * 1000)/min)); + + seq_printf(m, "pci_map_sg() : %12ld calls %12ld pages (avg %d/1000)\n", + ioc->msg_calls, ioc->msg_pages, + (int)((ioc->msg_pages * 1000)/ioc->msg_calls)); + + seq_printf(m, "pci_unmap_sg() : %12ld calls %12ld pages (avg %d/1000)\n", + ioc->usg_calls, ioc->usg_pages, + (int)((ioc->usg_pages * 1000)/ioc->usg_calls)); +#endif + + return 0; +} + +static int +sba_proc_bitmap_info(struct seq_file *m, void *p) +{ + struct sba_device *sba_dev = sba_list; + struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */ + + seq_hex_dump(m, " ", DUMP_PREFIX_NONE, 32, 4, ioc->res_map, + ioc->res_size, false); + seq_putc(m, '\n'); + + return 0; +} +#endif /* CONFIG_PROC_FS */ + +static const struct parisc_device_id sba_tbl[] __initconst = { + { HPHW_IOA, HVERSION_REV_ANY_ID, ASTRO_RUNWAY_PORT, 0xb }, + { HPHW_BCPORT, HVERSION_REV_ANY_ID, IKE_MERCED_PORT, 0xc }, + { HPHW_BCPORT, HVERSION_REV_ANY_ID, REO_MERCED_PORT, 0xc }, + { HPHW_BCPORT, HVERSION_REV_ANY_ID, REOG_MERCED_PORT, 0xc }, + { HPHW_IOA, HVERSION_REV_ANY_ID, PLUTO_MCKINLEY_PORT, 0xc }, + { 0, } +}; + +static int sba_driver_callback(struct parisc_device *); + +static struct parisc_driver sba_driver __refdata = { + .name = MODULE_NAME, + .id_table = sba_tbl, + .probe = sba_driver_callback, +}; + +/* +** Determine if sba should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +static int __init sba_driver_callback(struct parisc_device *dev) +{ + struct sba_device *sba_dev; + u32 func_class; + int i; + char *version; + void __iomem *sba_addr = ioremap(dev->hpa.start, SBA_FUNC_SIZE); + struct proc_dir_entry *root __maybe_unused; + + sba_dump_ranges(sba_addr); + + /* Read HW Rev First */ + func_class = READ_REG(sba_addr + SBA_FCLASS); + + if (IS_ASTRO(dev)) { + unsigned long fclass; + static char astro_rev[]="Astro ?.?"; + + /* Astro is broken...Read HW Rev First */ + fclass = READ_REG(sba_addr); + + astro_rev[6] = '1' + (char) (fclass & 0x7); + astro_rev[8] = '0' + (char) ((fclass & 0x18) >> 3); + version = astro_rev; + + } else if (IS_IKE(dev)) { + static char ike_rev[] = "Ike rev ?"; + ike_rev[8] = '0' + (char) (func_class & 0xff); + version = ike_rev; + } else if (IS_PLUTO(dev)) { + static char pluto_rev[]="Pluto ?.?"; + pluto_rev[6] = '0' + (char) ((func_class & 0xf0) >> 4); + pluto_rev[8] = '0' + (char) (func_class & 0x0f); + version = pluto_rev; + } else { + static char reo_rev[] = "REO rev ?"; + reo_rev[8] = '0' + (char) (func_class & 0xff); + version = reo_rev; + } + + if (!global_ioc_cnt) { + global_ioc_cnt = count_parisc_driver(&sba_driver); + + /* Astro and Pluto have one IOC per SBA */ + if ((!IS_ASTRO(dev)) || (!IS_PLUTO(dev))) + global_ioc_cnt *= 2; + } + + printk(KERN_INFO "%s found %s at 0x%llx\n", + MODULE_NAME, version, (unsigned long long)dev->hpa.start); + + sba_dev = kzalloc_obj(struct sba_device); + if (!sba_dev) { + printk(KERN_ERR MODULE_NAME " - couldn't alloc sba_device\n"); + return -ENOMEM; + } + + parisc_set_drvdata(dev, sba_dev); + + for(i=0; iioc[i].res_lock)); + + sba_dev->dev = dev; + sba_dev->hw_rev = func_class; + sba_dev->name = dev->name; + sba_dev->sba_hpa = sba_addr; + + sba_get_pat_resources(sba_dev); + sba_hw_init(sba_dev); + sba_common_init(sba_dev); + + hppa_dma_ops = &sba_ops; + + switch (dev->id.hversion) { + case PLUTO_MCKINLEY_PORT: + if (!proc_mckinley_root) + proc_mckinley_root = proc_mkdir("bus/mckinley", NULL); + root = proc_mckinley_root; + break; + case ASTRO_RUNWAY_PORT: + case IKE_MERCED_PORT: + default: + if (!proc_runway_root) + proc_runway_root = proc_mkdir("bus/runway", NULL); + root = proc_runway_root; + break; + } + + proc_create_single("sba_iommu", 0, root, sba_proc_info); + proc_create_single("sba_iommu-bitmap", 0, root, sba_proc_bitmap_info); + return 0; +} + +/* +** One time initialization to let the world know the SBA was found. +** This is the only routine which is NOT static. +** Must be called exactly once before pci_init(). +*/ +static int __init sba_init(void) +{ + return register_parisc_driver(&sba_driver); +} +arch_initcall(sba_init); + + +/** + * sba_get_iommu - Assign the iommu pointer for the pci bus controller. + * @pci_hba: The parisc device. + * + * Returns the appropriate IOMMU data for the given parisc PCI controller. + * This is cached and used later for PCI DMA Mapping. + */ +void * sba_get_iommu(struct parisc_device *pci_hba) +{ + struct parisc_device *sba_dev = parisc_parent(pci_hba); + struct sba_device *sba = dev_get_drvdata(&sba_dev->dev); + char t = sba_dev->id.hw_type; + int iocnum = (pci_hba->hw_path >> 3); /* IOC # */ + + WARN_ON((t != HPHW_IOA) && (t != HPHW_BCPORT)); + + return &(sba->ioc[iocnum]); +} + + +/** + * sba_directed_lmmio - return first directed LMMIO range routed to rope + * @pci_hba: The parisc device. + * @r: resource PCI host controller wants start/end fields assigned. + * + * For the given parisc PCI controller, determine if any direct ranges + * are routed down the corresponding rope. + */ +void sba_directed_lmmio(struct parisc_device *pci_hba, struct resource *r) +{ + struct parisc_device *sba_dev = parisc_parent(pci_hba); + struct sba_device *sba = dev_get_drvdata(&sba_dev->dev); + char t = sba_dev->id.hw_type; + int i; + int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */ + + BUG_ON((t!=HPHW_IOA) && (t!=HPHW_BCPORT)); + + r->start = r->end = 0; + + /* Astro has 4 directed ranges. Not sure about Ike/Pluto/et al */ + for (i=0; i<4; i++) { + int base, size; + void __iomem *reg = sba->sba_hpa + i*0x18; + + base = READ_REG32(reg + LMMIO_DIRECT0_BASE); + if ((base & 1) == 0) + continue; /* not enabled */ + + size = READ_REG32(reg + LMMIO_DIRECT0_ROUTE); + + if ((size & (ROPES_PER_IOC-1)) != rope) + continue; /* directed down different rope */ + + r->start = (base & ~1UL) | PCI_F_EXTEND; + size = ~ READ_REG32(reg + LMMIO_DIRECT0_MASK); + r->end = r->start + size; + r->flags = IORESOURCE_MEM; + } +} + + +/** + * sba_distributed_lmmio - return portion of distributed LMMIO range + * @pci_hba: The parisc device. + * @r: resource PCI host controller wants start/end fields assigned. + * + * For the given parisc PCI controller, return portion of distributed LMMIO + * range. The distributed LMMIO is always present and it's just a question + * of the base address and size of the range. + */ +void sba_distributed_lmmio(struct parisc_device *pci_hba, struct resource *r ) +{ + struct parisc_device *sba_dev = parisc_parent(pci_hba); + struct sba_device *sba = dev_get_drvdata(&sba_dev->dev); + char t = sba_dev->id.hw_type; + int base, size; + int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */ + + BUG_ON((t!=HPHW_IOA) && (t!=HPHW_BCPORT)); + + r->start = r->end = 0; + + base = READ_REG32(sba->sba_hpa + LMMIO_DIST_BASE); + if ((base & 1) == 0) { + BUG(); /* Gah! Distr Range wasn't enabled! */ + return; + } + + r->start = (base & ~1UL) | PCI_F_EXTEND; + + size = (~READ_REG32(sba->sba_hpa + LMMIO_DIST_MASK)) / ROPES_PER_IOC; + r->start += rope * (size + 1); /* adjust base for this rope */ + r->end = r->start + size; + r->flags = IORESOURCE_MEM; +} diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c new file mode 100644 index 000000000..3aaab40df --- /dev/null +++ b/drivers/parisc/superio.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* National Semiconductor NS87560UBD Super I/O controller used in + * HP [BCJ]x000 workstations. + * + * This chip is a horrid piece of engineering, and National + * denies any knowledge of its existence. Thus no datasheet is + * available off www.national.com. + * + * (C) Copyright 2000 Linuxcare, Inc. + * (C) Copyright 2000 Linuxcare Canada, Inc. + * (C) Copyright 2000 Martin K. Petersen + * (C) Copyright 2000 Alex deVries + * (C) Copyright 2001 John Marvin + * (C) Copyright 2003 Grant Grundler + * (C) Copyright 2005 Kyle McMartin + * (C) Copyright 2006 Helge Deller + * + * The initial version of this is by Martin Petersen. Alex deVries + * has spent a bit of time trying to coax it into working. + * + * Major changes to get basic interrupt infrastructure working to + * hopefully be able to support all SuperIO devices. Currently + * works with serial. -- John Marvin + * + * Converted superio_init() to be a PCI_FIXUP_FINAL callee. + * -- Kyle McMartin + */ + + +/* NOTES: + * + * Function 0 is an IDE controller. It is identical to a PC87415 IDE + * controller (and identifies itself as such). + * + * Function 1 is a "Legacy I/O" controller. Under this function is a + * whole mess of legacy I/O peripherals. Of course, HP hasn't enabled + * all the functionality in hardware, but the following is available: + * + * Two 16550A compatible serial controllers + * An IEEE 1284 compatible parallel port + * A floppy disk controller + * + * Function 2 is a USB controller. + * + * We must be incredibly careful during initialization. Since all + * interrupts are routed through function 1 (which is not allowed by + * the PCI spec), we need to program the PICs on the legacy I/O port + * *before* we attempt to set up IDE and USB. @#$!& + * + * According to HP, devices are only enabled by firmware if they have + * a physical device connected. + * + * Configuration register bits: + * 0x5A: FDC, SP1, IDE1, SP2, IDE2, PAR, Reserved, P92 + * 0x5B: RTC, 8259, 8254, DMA1, DMA2, KBC, P61, APM + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct superio_device sio_dev; + + +#undef DEBUG_SUPERIO_INIT + +#ifdef DEBUG_SUPERIO_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#define SUPERIO "SuperIO" +#define PFX SUPERIO ": " + +static irqreturn_t +superio_interrupt(int parent_irq, void *devp) +{ + u8 results; + u8 local_irq; + + /* Poll the 8259 to see if there's an interrupt. */ + outb (OCW3_POLL,IC_PIC1+0); + + results = inb(IC_PIC1+0); + + /* + * Bit 7: 1 = active Interrupt; 0 = no Interrupt pending + * Bits 6-3: zero + * Bits 2-0: highest priority, active requesting interrupt ID (0-7) + */ + if ((results & 0x80) == 0) { + /* I suspect "spurious" interrupts are from unmasking an IRQ. + * We don't know if an interrupt was/is pending and thus + * just call the handler for that IRQ as if it were pending. + */ + return IRQ_NONE; + } + + /* Check to see which device is interrupting */ + local_irq = results & 0x0f; + + if (local_irq == 2 || local_irq > 7) { + printk(KERN_ERR PFX "slave interrupted!\n"); + return IRQ_HANDLED; + } + + if (local_irq == 7) { + + /* Could be spurious. Check in service bits */ + + outb(OCW3_ISR,IC_PIC1+0); + results = inb(IC_PIC1+0); + if ((results & 0x80) == 0) { /* if ISR7 not set: spurious */ + printk(KERN_WARNING PFX "spurious interrupt!\n"); + return IRQ_HANDLED; + } + } + + /* Call the appropriate device's interrupt */ + generic_handle_irq(local_irq); + + /* set EOI - forces a new interrupt if a lower priority device + * still needs service. + */ + outb((OCW2_SEOI|local_irq),IC_PIC1 + 0); + return IRQ_HANDLED; +} + +/* Initialize Super I/O device */ +static void +superio_init(struct pci_dev *pcidev) +{ + struct superio_device *sio = &sio_dev; + struct pci_dev *pdev = sio->lio_pdev; + u16 word; + int ret; + + if (sio->suckyio_irq_enabled) + return; + + BUG_ON(!pdev); + BUG_ON(!sio->usb_pdev); + + /* use the IRQ iosapic found for USB INT D... */ + pdev->irq = sio->usb_pdev->irq; + + /* ...then properly fixup the USB to point at suckyio PIC */ + sio->usb_pdev->irq = superio_fixup_irq(sio->usb_pdev); + + printk(KERN_INFO PFX "Found NS87560 Legacy I/O device at %s (IRQ %i)\n", + pci_name(pdev), pdev->irq); + + pci_read_config_dword (pdev, SIO_SP1BAR, &sio->sp1_base); + sio->sp1_base &= ~1; + printk(KERN_INFO PFX "Serial port 1 at 0x%x\n", sio->sp1_base); + + pci_read_config_dword (pdev, SIO_SP2BAR, &sio->sp2_base); + sio->sp2_base &= ~1; + printk(KERN_INFO PFX "Serial port 2 at 0x%x\n", sio->sp2_base); + + pci_read_config_dword (pdev, SIO_PPBAR, &sio->pp_base); + sio->pp_base &= ~1; + printk(KERN_INFO PFX "Parallel port at 0x%x\n", sio->pp_base); + + pci_read_config_dword (pdev, SIO_FDCBAR, &sio->fdc_base); + sio->fdc_base &= ~1; + printk(KERN_INFO PFX "Floppy controller at 0x%x\n", sio->fdc_base); + pci_read_config_dword (pdev, SIO_ACPIBAR, &sio->acpi_base); + sio->acpi_base &= ~1; + printk(KERN_INFO PFX "ACPI at 0x%x\n", sio->acpi_base); + + request_region (IC_PIC1, 0x1f, "pic1"); + request_region (IC_PIC2, 0x1f, "pic2"); + request_region (sio->acpi_base, 0x1f, "acpi"); + + /* Enable the legacy I/O function */ + pci_read_config_word (pdev, PCI_COMMAND, &word); + word |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO; + pci_write_config_word (pdev, PCI_COMMAND, word); + + pci_set_master (pdev); + ret = pci_enable_device(pdev); + BUG_ON(ret < 0); /* not too much we can do about this... */ + + /* + * Next project is programming the onboard interrupt controllers. + * PDC hasn't done this for us, since it's using polled I/O. + * + * XXX Use dword writes to avoid bugs in Elroy or Suckyio Config + * space access. PCI is by nature a 32-bit bus and config + * space can be sensitive to that. + */ + + /* 0x64 - 0x67 : + DMA Rtg 2 + DMA Rtg 3 + DMA Chan Ctl + TRIGGER_1 == 0x82 USB & IDE level triggered, rest to edge + */ + pci_write_config_dword (pdev, 0x64, 0x82000000U); + + /* 0x68 - 0x6b : + TRIGGER_2 == 0x00 all edge triggered (not used) + CFG_IR_SER == 0x43 SerPort1 = IRQ3, SerPort2 = IRQ4 + CFG_IR_PF == 0x65 ParPort = IRQ5, FloppyCtlr = IRQ6 + CFG_IR_IDE == 0x07 IDE1 = IRQ7, reserved + */ + pci_write_config_dword (pdev, TRIGGER_2, 0x07654300U); + + /* 0x6c - 0x6f : + CFG_IR_INTAB == 0x00 + CFG_IR_INTCD == 0x10 USB = IRQ1 + CFG_IR_PS2 == 0x00 + CFG_IR_FXBUS == 0x00 + */ + pci_write_config_dword (pdev, CFG_IR_INTAB, 0x00001000U); + + /* 0x70 - 0x73 : + CFG_IR_USB == 0x00 not used. USB is connected to INTD. + CFG_IR_ACPI == 0x00 not used. + DMA Priority == 0x4c88 Power on default value. NFC. + */ + pci_write_config_dword (pdev, CFG_IR_USB, 0x4c880000U); + + /* PIC1 Initialization Command Word register programming */ + outb (0x11,IC_PIC1+0); /* ICW1: ICW4 write req | ICW1 */ + outb (0x00,IC_PIC1+1); /* ICW2: interrupt vector table - not used */ + outb (0x04,IC_PIC1+1); /* ICW3: Cascade */ + outb (0x01,IC_PIC1+1); /* ICW4: x86 mode */ + + /* PIC1 Program Operational Control Words */ + outb (0xff,IC_PIC1+1); /* OCW1: Mask all interrupts */ + outb (0xc2,IC_PIC1+0); /* OCW2: priority (3-7,0-2) */ + + /* PIC2 Initialization Command Word register programming */ + outb (0x11,IC_PIC2+0); /* ICW1: ICW4 write req | ICW1 */ + outb (0x00,IC_PIC2+1); /* ICW2: N/A */ + outb (0x02,IC_PIC2+1); /* ICW3: Slave ID code */ + outb (0x01,IC_PIC2+1); /* ICW4: x86 mode */ + + /* Program Operational Control Words */ + outb (0xff,IC_PIC1+1); /* OCW1: Mask all interrupts */ + outb (0x68,IC_PIC1+0); /* OCW3: OCW3 select | ESMM | SMM */ + + /* Write master mask reg */ + outb (0xff,IC_PIC1+1); + + /* Setup USB power regulation */ + outb(1, sio->acpi_base + USB_REG_CR); + if (inb(sio->acpi_base + USB_REG_CR) & 1) + printk(KERN_INFO PFX "USB regulator enabled\n"); + else + printk(KERN_ERR PFX "USB regulator not initialized!\n"); + + if (request_irq(pdev->irq, superio_interrupt, 0, + SUPERIO, (void *)sio)) { + + printk(KERN_ERR PFX "could not get irq\n"); + BUG(); + return; + } + + sio->suckyio_irq_enabled = 1; +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87560_LIO, superio_init); + +static void superio_mask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; + u8 r8; + + if ((irq < 1) || (irq == 2) || (irq > 7)) { + printk(KERN_ERR PFX "Illegal irq number.\n"); + BUG(); + return; + } + + /* Mask interrupt */ + + r8 = inb(IC_PIC1+1); + r8 |= (1 << irq); + outb (r8,IC_PIC1+1); +} + +static void superio_unmask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; + u8 r8; + + if ((irq < 1) || (irq == 2) || (irq > 7)) { + printk(KERN_ERR PFX "Illegal irq number (%d).\n", irq); + BUG(); + return; + } + + /* Unmask interrupt */ + r8 = inb(IC_PIC1+1); + r8 &= ~(1 << irq); + outb (r8,IC_PIC1+1); +} + +static struct irq_chip superio_interrupt_type = { + .name = SUPERIO, + .irq_unmask = superio_unmask_irq, + .irq_mask = superio_mask_irq, +}; + +#ifdef DEBUG_SUPERIO_INIT +static unsigned short expected_device[3] = { + PCI_DEVICE_ID_NS_87415, + PCI_DEVICE_ID_NS_87560_LIO, + PCI_DEVICE_ID_NS_87560_USB +}; +#endif + +int superio_fixup_irq(struct pci_dev *pcidev) +{ + int local_irq, i; + +#ifdef DEBUG_SUPERIO_INIT + int fn; + fn = PCI_FUNC(pcidev->devfn); + + /* Verify the function number matches the expected device id. */ + if (expected_device[fn] != pcidev->device) { + BUG(); + return -1; + } + printk(KERN_DEBUG "superio_fixup_irq(%s) ven 0x%x dev 0x%x from %ps\n", + pci_name(pcidev), + pcidev->vendor, pcidev->device, + __builtin_return_address(0)); +#endif + + for (i = 0; i < 16; i++) { + irq_set_chip_and_handler(i, &superio_interrupt_type, + handle_simple_irq); + } + + /* + * We don't allocate a SuperIO irq for the legacy IO function, + * since it is a "bridge". Instead, we will allocate irq's for + * each legacy device as they are initialized. + */ + + switch(pcidev->device) { + case PCI_DEVICE_ID_NS_87415: /* Function 0 */ + local_irq = IDE_IRQ; + break; + case PCI_DEVICE_ID_NS_87560_LIO: /* Function 1 */ + sio_dev.lio_pdev = pcidev; /* save for superio_init() */ + return -1; + case PCI_DEVICE_ID_NS_87560_USB: /* Function 2 */ + sio_dev.usb_pdev = pcidev; /* save for superio_init() */ + local_irq = USB_IRQ; + break; + default: + local_irq = -1; + BUG(); + break; + } + + return local_irq; +} + +static void __init superio_serial_init(void) +{ +#ifdef CONFIG_SERIAL_8250 + int retval; + struct uart_port serial_port; + + memset(&serial_port, 0, sizeof(serial_port)); + serial_port.iotype = UPIO_PORT; + serial_port.type = PORT_16550A; + serial_port.uartclk = 115200*16; + serial_port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | + UPF_BOOT_AUTOCONF; + + /* serial port #1 */ + serial_port.iobase = sio_dev.sp1_base; + serial_port.irq = SP1_IRQ; + serial_port.line = 0; + retval = early_serial_setup(&serial_port); + if (retval < 0) { + printk(KERN_WARNING PFX "Register Serial #0 failed.\n"); + return; + } + + /* serial port #2 */ + serial_port.iobase = sio_dev.sp2_base; + serial_port.irq = SP2_IRQ; + serial_port.line = 1; + retval = early_serial_setup(&serial_port); + if (retval < 0) + printk(KERN_WARNING PFX "Register Serial #1 failed.\n"); +#endif /* CONFIG_SERIAL_8250 */ +} + + +static void __init superio_parport_init(void) +{ +#ifdef CONFIG_PARPORT_PC + if (!parport_pc_probe_port(sio_dev.pp_base, + 0 /*base_hi*/, + PAR_IRQ, + PARPORT_DMA_NONE /* dma */, + NULL /*struct pci_dev* */, + 0 /* shared irq flags */)) + + printk(KERN_WARNING PFX "Probing parallel port failed.\n"); +#endif /* CONFIG_PARPORT_PC */ +} + + +static void superio_fixup_pci(struct pci_dev *pdev) +{ + u8 prog; + + pdev->class |= 0x5; + pci_write_config_byte(pdev, PCI_CLASS_PROG, pdev->class); + + pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog); + printk("PCI: Enabled native mode for NS87415 (pif=0x%x)\n", prog); +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, superio_fixup_pci); + + +static int __init +superio_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct superio_device *sio = &sio_dev; + + /* + ** superio_probe(00:0e.0) ven 0x100b dev 0x2 sv 0x0 sd 0x0 class 0x1018a + ** superio_probe(00:0e.1) ven 0x100b dev 0xe sv 0x0 sd 0x0 class 0x68000 + ** superio_probe(00:0e.2) ven 0x100b dev 0x12 sv 0x0 sd 0x0 class 0xc0310 + */ + DBG_INIT("superio_probe(%s) ven 0x%x dev 0x%x sv 0x%x sd 0x%x class 0x%x\n", + pci_name(dev), + dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device, + dev->class); + + BUG_ON(!sio->suckyio_irq_enabled); /* Enabled by PCI_FIXUP_FINAL */ + + if (dev->device == PCI_DEVICE_ID_NS_87560_LIO) { /* Function 1 */ + superio_parport_init(); + superio_serial_init(); + /* REVISIT XXX : superio_fdc_init() ? */ + return 0; + } else if (dev->device == PCI_DEVICE_ID_NS_87415) { /* Function 0 */ + DBG_INIT("superio_probe: ignoring IDE 87415\n"); + } else if (dev->device == PCI_DEVICE_ID_NS_87560_USB) { /* Function 2 */ + DBG_INIT("superio_probe: ignoring USB OHCI controller\n"); + } else { + DBG_INIT("superio_probe: WTF? Fire Extinguisher?\n"); + } + + /* Let appropriate other driver claim this device. */ + return -ENODEV; +} + +static const struct pci_device_id superio_tbl[] __initconst = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87560_LIO) }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87560_USB) }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415) }, + { 0, } +}; + +static struct pci_driver superio_driver __refdata = { + .name = SUPERIO, + .id_table = superio_tbl, + .probe = superio_probe, +}; + +module_pci_driver(superio_driver); diff --git a/drivers/parisc/wax.c b/drivers/parisc/wax.c new file mode 100644 index 000000000..a82a5e88e --- /dev/null +++ b/drivers/parisc/wax.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * WAX Device Driver + * + * (c) Copyright 2000 The Puffin Group Inc. + * + * (c) 2000-2023 by Helge Deller + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gsc.h" + +#define WAX_GSC_IRQ 7 /* Hardcoded Interrupt for GSC */ + +static void wax_choose_irq(struct parisc_device *dev, void *ctrl) +{ + int irq; + + switch (dev->id.sversion) { + case 0x73: irq = 1; break; /* i8042 General */ + case 0x8c: irq = 6; break; /* Serial */ + case 0x90: irq = 10; break; /* EISA */ + default: return; /* Unknown */ + } + + gsc_asic_assign_irq(ctrl, irq, &dev->irq); + + switch (dev->id.sversion) { + case 0x73: irq = 2; break; /* i8042 High-priority */ + case 0x90: irq = 0; break; /* EISA NMI */ + default: return; /* No secondary IRQ */ + } + + gsc_asic_assign_irq(ctrl, irq, &dev->aux_irq); +} + +static void __init +wax_init_irq(struct gsc_asic *wax) +{ + unsigned long base = wax->hpa; + + /* Wax-off */ + gsc_writel(0x00000000, base+OFFSET_IMR); + + /* clear pending interrupts */ + gsc_readl(base+OFFSET_IRR); + + /* We're not really convinced we want to reset the onboard + * devices. Firmware does it for us... + */ + + /* Resets */ +// gsc_writel(0xFFFFFFFF, base+0x1000); /* HIL */ +// gsc_writel(0xFFFFFFFF, base+0x2000); /* RS232-B on Wax */ +} + +static int __init wax_init_chip(struct parisc_device *dev) +{ + struct gsc_asic *wax; + struct parisc_device *parent; + int ret; + + wax = kzalloc_obj(*wax); + if (!wax) + return -ENOMEM; + + wax->name = "wax"; + wax->hpa = dev->hpa.start; + + wax->version = 0; /* gsc_readb(wax->hpa+WAX_VER); */ + printk(KERN_INFO "%s at 0x%lx found.\n", wax->name, wax->hpa); + + /* Stop wax hissing for a bit */ + wax_init_irq(wax); + + /* the IRQ wax should use */ + dev->irq = gsc_claim_irq(&wax->gsc_irq, WAX_GSC_IRQ); + if (dev->irq < 0) { + printk(KERN_ERR "%s(): cannot get GSC irq\n", + __func__); + kfree(wax); + return -EBUSY; + } + + wax->eim = ((u32) wax->gsc_irq.txn_addr) | wax->gsc_irq.txn_data; + + ret = request_irq(wax->gsc_irq.irq, gsc_asic_intr, 0, "wax", wax); + if (ret < 0) { + kfree(wax); + return ret; + } + + /* enable IRQ's for devices below WAX */ + gsc_writel(wax->eim, wax->hpa + OFFSET_IAR); + + /* Done init'ing, register this driver */ + ret = gsc_common_setup(dev, wax); + if (ret) { + kfree(wax); + return ret; + } + + gsc_fixup_irqs(dev, wax, wax_choose_irq); + /* On 715-class machines, Wax EISA is a sibling of Wax, not a child. */ + parent = parisc_parent(dev); + if (parent->id.hw_type != HPHW_IOA) { + gsc_fixup_irqs(parent, wax, wax_choose_irq); + } + + return ret; +} + +static const struct parisc_device_id wax_tbl[] __initconst = { + { HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008e }, + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, wax_tbl); + +static struct parisc_driver wax_driver __refdata = { + .name = "wax", + .id_table = wax_tbl, + .probe = wax_init_chip, +}; + +static int __init wax_init(void) +{ + return register_parisc_driver(&wax_driver); +} +arch_initcall(wax_init); -- cgit v1.3.1