Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+161
View File
@@ -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
+27
View File
@@ -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
+27
View File
@@ -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"
*/
+133
View File
@@ -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 <deller@gmx.de>
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/led.h>
#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);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+463
View File
@@ -0,0 +1,463 @@
// 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 <linux/init.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/eisa.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/processor.h>
#include <asm/parisc-device.h>
#include <asm/delay.h>
#include <asm/eisa_bus.h>
#include <asm/eisa_eeprom.h>
#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<<num)) {
printk(KERN_WARNING
"IRQ %d polarity configured twice (last to level)\n",
num);
}
eisa_irq_level |= (1<<num); /* set the corresponding bit */
eisa_irq_configured |= (1<<num); /* set the corresponding bit */
}
void eisa_make_irq_edge(int num)
{
if (eisa_irq_configured& (1<<num)) {
printk(KERN_WARNING
"IRQ %d polarity configured twice (last to edge)\n",
num);
}
eisa_irq_level &= ~(1<<num); /* clear the corresponding bit */
eisa_irq_configured |= (1<<num); /* set the corresponding bit */
}
static int __init eisa_irq_setup(char *str)
{
char *cur = str;
int val;
EISA_DBG("IRQ setup\n");
while (cur != NULL) {
char *pe;
val = (int) simple_strtoul(cur, &pe, 0);
if (val > 15 || val < 0) {
printk(KERN_ERR "eisa: EISA irq value are 0-15\n");
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);
+99
View File
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* EISA "eeprom" support routines
*
* Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <asm/eisa_eeprom.h>
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_LICENSE("GPL");
module_init(eisa_eeprom_init);
@@ -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 <linux/ioport.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include <asm/eisa_bus.h>
#include <asm/eisa_eeprom.h>
/*
* 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;i<HPEE_MEMORY_MAX_ENT;i++) {
c = get_8(buf+len);
if (NULL != (res = kzalloc_obj(struct resource))) {
int result;
res->name = 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;i<HPEE_IRQ_MAX_ENT;i++) {
c = get_8(buf+len);
pr_cont("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
if (c & HPEE_IRQ_TRIG_LEVEL) {
eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
} else {
eisa_make_irq_edge(c & HPEE_IRQ_CHANNEL_MASK);
}
len+=2;
/* hpux seems to allow for
* two bytes of irq data but only defines one of
* them, I think */
if (!(c & HPEE_IRQ_MORE)) {
break;
}
}
return len;
}
static int configure_dma(const unsigned char *buf)
{
int len;
u_int8_t c;
int i;
len=0;
for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
c = get_8(buf+len);
pr_cont("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
/* fixme: maybe initialize the dma channel withthe timing ? */
len+=2;
if (!(c & HPEE_DMA_MORE)) {
break;
}
}
return len;
}
static int configure_port(const unsigned char *buf, struct resource *io_parent,
char *board)
{
int len;
u_int8_t c;
int i;
struct resource *res;
int result;
len=0;
for (i=0;i<HPEE_PORT_MAX_ENT;i++) {
c = get_8(buf+len);
if (NULL != (res = kzalloc_obj(struct resource))) {
res->name = 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<HPEE_PORT_INIT_MAX_LEN) {
int s=0;
c = get_8(buf+len);
switch (c & HPEE_PORT_INIT_WIDTH_MASK) {
case HPEE_PORT_INIT_WIDTH_BYTE:
s=1;
if (c & HPEE_PORT_INIT_MASK) {
printk(KERN_WARNING "port_init: unverified mask attribute\n");
outb((inb(get_16(buf+len+1) &
get_8(buf+len+3)) |
get_8(buf+len+4)), get_16(buf+len+1));
} else {
outb(get_8(buf+len+3), get_16(buf+len+1));
}
break;
case HPEE_PORT_INIT_WIDTH_WORD:
s=2;
if (c & HPEE_PORT_INIT_MASK) {
printk(KERN_WARNING "port_init: unverified mask attribute\n");
outw((inw(get_16(buf+len+1)) &
get_16(buf+len+3)) |
get_16(buf+len+5),
get_16(buf+len+1));
} else {
outw(cpu_to_le16(get_16(buf+len+3)), get_16(buf+len+1));
}
break;
case HPEE_PORT_INIT_WIDTH_DWORD:
s=4;
if (c & HPEE_PORT_INIT_MASK) {
printk(KERN_WARNING "port_init: unverified mask attribute\n");
outl((inl(get_16(buf+len+1) &
get_32(buf+len+3)) |
get_32(buf+len+7)), get_16(buf+len+1));
} else {
outl(cpu_to_le32(get_32(buf+len+3)), get_16(buf+len+1));
}
break;
default:
printk(KERN_ERR "Invalid port init word %02x\n", c);
return 0;
}
if (c & HPEE_PORT_INIT_MASK) {
s*=2;
}
len+=s+3;
if (!(c & HPEE_PORT_INIT_MORE)) {
break;
}
}
return len;
}
static int configure_choise(const unsigned char *buf, u_int8_t *info)
{
int len;
/* theis record contain the value of the functions
* configuration choises and an info byte which
* describes which other records to expect in this
* function */
len = get_8(buf);
*info=get_8(buf+len+1);
return len+2;
}
static int configure_type_string(const unsigned char *buf)
{
int len;
/* just skip past the type field */
len = get_8(buf);
if (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;i<eh->num_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;
}
+262
View File
@@ -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 <linux/bitops.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/hardware.h>
#include <asm/io.h>
#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;
}
+47
View File
@@ -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 <linux/interrupt.h>
#include <asm/hardware.h>
#include <asm/parisc-device.h>
#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);
+105
View File
@@ -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 <linux/types.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/parisc-device.h>
#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);
+182
View File
@@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/prefetch.h>
/**
* 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;
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef _IOMMU_H
#define _IOMMU_H 1
#include <linux/pci.h>
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 */
+986
View File
@@ -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 <linux/pci.h>
#include <asm/pdc.h>
#include <asm/pdcpat.h>
#ifdef CONFIG_SUPERIO
#include <asm/superio.h>
#endif
#include <asm/ropes.h>
#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; i<num_entry; i++, irp += 4) {
printk(KERN_DEBUG "%p : %2d %.8x %.8x %.8x %.8x\n",
irp, i, irp[0], irp[1], irp[2], irp[3]);
}
}
static void
iosapic_prt_vi(struct vector_info *vi)
{
printk(KERN_DEBUG MODULE_NAME ": vector_info[%d] is at %p\n", vi->irqline, 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 */
@@ -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
+239
View File
@@ -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 <alan@redhat.com> and
* Alex deVries <alex@onefishtwo.ca>
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/types.h>
#include <linux/reboot.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/led.h>
#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);
File diff suppressed because it is too large Load Diff
+551
View File
@@ -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 <hdeller@redhat.com>
* (c) Copyright 2001 Randolph Chung <tausq@debian.org>
* (c) Copyright 2000-2023 Helge Deller <deller@gmx.de>
*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/utsname.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/hardware.h>
#include <asm/param.h> /* HZ */
#include <asm/led.h>
#include <asm/pdc.h>
#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);
File diff suppressed because it is too large Load Diff
+266
View File
@@ -0,0 +1,266 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* HP PARISC soft power switch driver
*
* Copyright (c) 2001-2023 Helge Deller <deller@gmx.de>
*
* HINT:
* Support of the soft power switch button may be enabled or disabled at
* runtime through the "/proc/sys/kernel/power" procfs entry.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/panic_notifier.h>
#include <linux/reboot.h>
#include <linux/sched/signal.h>
#include <linux/kthread.h>
#include <linux/pm.h>
#include <asm/pdc.h>
#include <asm/io.h>
#include <asm/led.h>
#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 <deller@gmx.de>");
MODULE_DESCRIPTION("Soft power switch driver");
MODULE_LICENSE("Dual BSD/GPL");
File diff suppressed because it is too large Load Diff
+494
View File
@@ -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 <mkp@linuxcare.com>
* (C) Copyright 2000 Alex deVries <alex@onefishtwo.ca>
* (C) Copyright 2001 John Marvin <jsm fc hp com>
* (C) Copyright 2003 Grant Grundler <grundler parisc-linux org>
* (C) Copyright 2005 Kyle McMartin <kyle@parisc-linux.org>
* (C) Copyright 2006 Helge Deller <deller@gmx.de>
*
* The initial version of this is by Martin Peterson. 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 <jsm@fc.hp.com>
*
* Converted superio_init() to be a PCI_FIXUP_FINAL callee.
* -- Kyle McMartin <kyle@parisc-linux.org>
*/
/* 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 <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/serial.h>
#include <linux/pci.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
#include <linux/termios.h>
#include <linux/tty.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/superio.h>
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);
+140
View File
@@ -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 <deller@gmx.de>
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/hardware.h>
#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);