18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* National Semiconductor NS87560UBD Super I/O controller used in 38c2ecf20Sopenharmony_ci * HP [BCJ]x000 workstations. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This chip is a horrid piece of engineering, and National 68c2ecf20Sopenharmony_ci * denies any knowledge of its existence. Thus no datasheet is 78c2ecf20Sopenharmony_ci * available off www.national.com. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (C) Copyright 2000 Linuxcare, Inc. 108c2ecf20Sopenharmony_ci * (C) Copyright 2000 Linuxcare Canada, Inc. 118c2ecf20Sopenharmony_ci * (C) Copyright 2000 Martin K. Petersen <mkp@linuxcare.com> 128c2ecf20Sopenharmony_ci * (C) Copyright 2000 Alex deVries <alex@onefishtwo.ca> 138c2ecf20Sopenharmony_ci * (C) Copyright 2001 John Marvin <jsm fc hp com> 148c2ecf20Sopenharmony_ci * (C) Copyright 2003 Grant Grundler <grundler parisc-linux org> 158c2ecf20Sopenharmony_ci * (C) Copyright 2005 Kyle McMartin <kyle@parisc-linux.org> 168c2ecf20Sopenharmony_ci * (C) Copyright 2006 Helge Deller <deller@gmx.de> 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * The initial version of this is by Martin Peterson. Alex deVries 198c2ecf20Sopenharmony_ci * has spent a bit of time trying to coax it into working. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Major changes to get basic interrupt infrastructure working to 228c2ecf20Sopenharmony_ci * hopefully be able to support all SuperIO devices. Currently 238c2ecf20Sopenharmony_ci * works with serial. -- John Marvin <jsm@fc.hp.com> 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Converted superio_init() to be a PCI_FIXUP_FINAL callee. 268c2ecf20Sopenharmony_ci * -- Kyle McMartin <kyle@parisc-linux.org> 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* NOTES: 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Function 0 is an IDE controller. It is identical to a PC87415 IDE 338c2ecf20Sopenharmony_ci * controller (and identifies itself as such). 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Function 1 is a "Legacy I/O" controller. Under this function is a 368c2ecf20Sopenharmony_ci * whole mess of legacy I/O peripherals. Of course, HP hasn't enabled 378c2ecf20Sopenharmony_ci * all the functionality in hardware, but the following is available: 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * Two 16550A compatible serial controllers 408c2ecf20Sopenharmony_ci * An IEEE 1284 compatible parallel port 418c2ecf20Sopenharmony_ci * A floppy disk controller 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Function 2 is a USB controller. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * We must be incredibly careful during initialization. Since all 468c2ecf20Sopenharmony_ci * interrupts are routed through function 1 (which is not allowed by 478c2ecf20Sopenharmony_ci * the PCI spec), we need to program the PICs on the legacy I/O port 488c2ecf20Sopenharmony_ci * *before* we attempt to set up IDE and USB. @#$!& 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * According to HP, devices are only enabled by firmware if they have 518c2ecf20Sopenharmony_ci * a physical device connected. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Configuration register bits: 548c2ecf20Sopenharmony_ci * 0x5A: FDC, SP1, IDE1, SP2, IDE2, PAR, Reserved, P92 558c2ecf20Sopenharmony_ci * 0x5B: RTC, 8259, 8254, DMA1, DMA2, KBC, P61, APM 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#include <linux/errno.h> 608c2ecf20Sopenharmony_ci#include <linux/init.h> 618c2ecf20Sopenharmony_ci#include <linux/module.h> 628c2ecf20Sopenharmony_ci#include <linux/types.h> 638c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 648c2ecf20Sopenharmony_ci#include <linux/ioport.h> 658c2ecf20Sopenharmony_ci#include <linux/serial.h> 668c2ecf20Sopenharmony_ci#include <linux/pci.h> 678c2ecf20Sopenharmony_ci#include <linux/parport.h> 688c2ecf20Sopenharmony_ci#include <linux/parport_pc.h> 698c2ecf20Sopenharmony_ci#include <linux/termios.h> 708c2ecf20Sopenharmony_ci#include <linux/tty.h> 718c2ecf20Sopenharmony_ci#include <linux/serial_core.h> 728c2ecf20Sopenharmony_ci#include <linux/serial_8250.h> 738c2ecf20Sopenharmony_ci#include <linux/delay.h> 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#include <asm/io.h> 768c2ecf20Sopenharmony_ci#include <asm/hardware.h> 778c2ecf20Sopenharmony_ci#include <asm/superio.h> 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct superio_device sio_dev; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#undef DEBUG_SUPERIO_INIT 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#ifdef DEBUG_SUPERIO_INIT 858c2ecf20Sopenharmony_ci#define DBG_INIT(x...) printk(x) 868c2ecf20Sopenharmony_ci#else 878c2ecf20Sopenharmony_ci#define DBG_INIT(x...) 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define SUPERIO "SuperIO" 918c2ecf20Sopenharmony_ci#define PFX SUPERIO ": " 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic irqreturn_t 948c2ecf20Sopenharmony_cisuperio_interrupt(int parent_irq, void *devp) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci u8 results; 978c2ecf20Sopenharmony_ci u8 local_irq; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Poll the 8259 to see if there's an interrupt. */ 1008c2ecf20Sopenharmony_ci outb (OCW3_POLL,IC_PIC1+0); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci results = inb(IC_PIC1+0); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * Bit 7: 1 = active Interrupt; 0 = no Interrupt pending 1068c2ecf20Sopenharmony_ci * Bits 6-3: zero 1078c2ecf20Sopenharmony_ci * Bits 2-0: highest priority, active requesting interrupt ID (0-7) 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci if ((results & 0x80) == 0) { 1108c2ecf20Sopenharmony_ci /* I suspect "spurious" interrupts are from unmasking an IRQ. 1118c2ecf20Sopenharmony_ci * We don't know if an interrupt was/is pending and thus 1128c2ecf20Sopenharmony_ci * just call the handler for that IRQ as if it were pending. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci return IRQ_NONE; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Check to see which device is interrupting */ 1188c2ecf20Sopenharmony_ci local_irq = results & 0x0f; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (local_irq == 2 || local_irq > 7) { 1218c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "slave interrupted!\n"); 1228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (local_irq == 7) { 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Could be spurious. Check in service bits */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci outb(OCW3_ISR,IC_PIC1+0); 1308c2ecf20Sopenharmony_ci results = inb(IC_PIC1+0); 1318c2ecf20Sopenharmony_ci if ((results & 0x80) == 0) { /* if ISR7 not set: spurious */ 1328c2ecf20Sopenharmony_ci printk(KERN_WARNING PFX "spurious interrupt!\n"); 1338c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Call the appropriate device's interrupt */ 1388c2ecf20Sopenharmony_ci generic_handle_irq(local_irq); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* set EOI - forces a new interrupt if a lower priority device 1418c2ecf20Sopenharmony_ci * still needs service. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci outb((OCW2_SEOI|local_irq),IC_PIC1 + 0); 1448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Initialize Super I/O device */ 1488c2ecf20Sopenharmony_cistatic void 1498c2ecf20Sopenharmony_cisuperio_init(struct pci_dev *pcidev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct superio_device *sio = &sio_dev; 1528c2ecf20Sopenharmony_ci struct pci_dev *pdev = sio->lio_pdev; 1538c2ecf20Sopenharmony_ci u16 word; 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (sio->suckyio_irq_enabled) 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci BUG_ON(!pdev); 1608c2ecf20Sopenharmony_ci BUG_ON(!sio->usb_pdev); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* use the IRQ iosapic found for USB INT D... */ 1638c2ecf20Sopenharmony_ci pdev->irq = sio->usb_pdev->irq; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* ...then properly fixup the USB to point at suckyio PIC */ 1668c2ecf20Sopenharmony_ci sio->usb_pdev->irq = superio_fixup_irq(sio->usb_pdev); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Found NS87560 Legacy I/O device at %s (IRQ %i)\n", 1698c2ecf20Sopenharmony_ci pci_name(pdev), pdev->irq); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci pci_read_config_dword (pdev, SIO_SP1BAR, &sio->sp1_base); 1728c2ecf20Sopenharmony_ci sio->sp1_base &= ~1; 1738c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Serial port 1 at 0x%x\n", sio->sp1_base); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pci_read_config_dword (pdev, SIO_SP2BAR, &sio->sp2_base); 1768c2ecf20Sopenharmony_ci sio->sp2_base &= ~1; 1778c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Serial port 2 at 0x%x\n", sio->sp2_base); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci pci_read_config_dword (pdev, SIO_PPBAR, &sio->pp_base); 1808c2ecf20Sopenharmony_ci sio->pp_base &= ~1; 1818c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Parallel port at 0x%x\n", sio->pp_base); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci pci_read_config_dword (pdev, SIO_FDCBAR, &sio->fdc_base); 1848c2ecf20Sopenharmony_ci sio->fdc_base &= ~1; 1858c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Floppy controller at 0x%x\n", sio->fdc_base); 1868c2ecf20Sopenharmony_ci pci_read_config_dword (pdev, SIO_ACPIBAR, &sio->acpi_base); 1878c2ecf20Sopenharmony_ci sio->acpi_base &= ~1; 1888c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "ACPI at 0x%x\n", sio->acpi_base); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci request_region (IC_PIC1, 0x1f, "pic1"); 1918c2ecf20Sopenharmony_ci request_region (IC_PIC2, 0x1f, "pic2"); 1928c2ecf20Sopenharmony_ci request_region (sio->acpi_base, 0x1f, "acpi"); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Enable the legacy I/O function */ 1958c2ecf20Sopenharmony_ci pci_read_config_word (pdev, PCI_COMMAND, &word); 1968c2ecf20Sopenharmony_ci word |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO; 1978c2ecf20Sopenharmony_ci pci_write_config_word (pdev, PCI_COMMAND, word); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci pci_set_master (pdev); 2008c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 2018c2ecf20Sopenharmony_ci BUG_ON(ret < 0); /* not too much we can do about this... */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Next project is programming the onboard interrupt controllers. 2058c2ecf20Sopenharmony_ci * PDC hasn't done this for us, since it's using polled I/O. 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * XXX Use dword writes to avoid bugs in Elroy or Suckyio Config 2088c2ecf20Sopenharmony_ci * space access. PCI is by nature a 32-bit bus and config 2098c2ecf20Sopenharmony_ci * space can be sensitive to that. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 0x64 - 0x67 : 2138c2ecf20Sopenharmony_ci DMA Rtg 2 2148c2ecf20Sopenharmony_ci DMA Rtg 3 2158c2ecf20Sopenharmony_ci DMA Chan Ctl 2168c2ecf20Sopenharmony_ci TRIGGER_1 == 0x82 USB & IDE level triggered, rest to edge 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci pci_write_config_dword (pdev, 0x64, 0x82000000U); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 0x68 - 0x6b : 2218c2ecf20Sopenharmony_ci TRIGGER_2 == 0x00 all edge triggered (not used) 2228c2ecf20Sopenharmony_ci CFG_IR_SER == 0x43 SerPort1 = IRQ3, SerPort2 = IRQ4 2238c2ecf20Sopenharmony_ci CFG_IR_PF == 0x65 ParPort = IRQ5, FloppyCtlr = IRQ6 2248c2ecf20Sopenharmony_ci CFG_IR_IDE == 0x07 IDE1 = IRQ7, reserved 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci pci_write_config_dword (pdev, TRIGGER_2, 0x07654300U); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* 0x6c - 0x6f : 2298c2ecf20Sopenharmony_ci CFG_IR_INTAB == 0x00 2308c2ecf20Sopenharmony_ci CFG_IR_INTCD == 0x10 USB = IRQ1 2318c2ecf20Sopenharmony_ci CFG_IR_PS2 == 0x00 2328c2ecf20Sopenharmony_ci CFG_IR_FXBUS == 0x00 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci pci_write_config_dword (pdev, CFG_IR_INTAB, 0x00001000U); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 0x70 - 0x73 : 2378c2ecf20Sopenharmony_ci CFG_IR_USB == 0x00 not used. USB is connected to INTD. 2388c2ecf20Sopenharmony_ci CFG_IR_ACPI == 0x00 not used. 2398c2ecf20Sopenharmony_ci DMA Priority == 0x4c88 Power on default value. NFC. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci pci_write_config_dword (pdev, CFG_IR_USB, 0x4c880000U); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* PIC1 Initialization Command Word register programming */ 2448c2ecf20Sopenharmony_ci outb (0x11,IC_PIC1+0); /* ICW1: ICW4 write req | ICW1 */ 2458c2ecf20Sopenharmony_ci outb (0x00,IC_PIC1+1); /* ICW2: interrupt vector table - not used */ 2468c2ecf20Sopenharmony_ci outb (0x04,IC_PIC1+1); /* ICW3: Cascade */ 2478c2ecf20Sopenharmony_ci outb (0x01,IC_PIC1+1); /* ICW4: x86 mode */ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* PIC1 Program Operational Control Words */ 2508c2ecf20Sopenharmony_ci outb (0xff,IC_PIC1+1); /* OCW1: Mask all interrupts */ 2518c2ecf20Sopenharmony_ci outb (0xc2,IC_PIC1+0); /* OCW2: priority (3-7,0-2) */ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* PIC2 Initialization Command Word register programming */ 2548c2ecf20Sopenharmony_ci outb (0x11,IC_PIC2+0); /* ICW1: ICW4 write req | ICW1 */ 2558c2ecf20Sopenharmony_ci outb (0x00,IC_PIC2+1); /* ICW2: N/A */ 2568c2ecf20Sopenharmony_ci outb (0x02,IC_PIC2+1); /* ICW3: Slave ID code */ 2578c2ecf20Sopenharmony_ci outb (0x01,IC_PIC2+1); /* ICW4: x86 mode */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Program Operational Control Words */ 2608c2ecf20Sopenharmony_ci outb (0xff,IC_PIC1+1); /* OCW1: Mask all interrupts */ 2618c2ecf20Sopenharmony_ci outb (0x68,IC_PIC1+0); /* OCW3: OCW3 select | ESMM | SMM */ 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Write master mask reg */ 2648c2ecf20Sopenharmony_ci outb (0xff,IC_PIC1+1); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Setup USB power regulation */ 2678c2ecf20Sopenharmony_ci outb(1, sio->acpi_base + USB_REG_CR); 2688c2ecf20Sopenharmony_ci if (inb(sio->acpi_base + USB_REG_CR) & 1) 2698c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "USB regulator enabled\n"); 2708c2ecf20Sopenharmony_ci else 2718c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "USB regulator not initialized!\n"); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (request_irq(pdev->irq, superio_interrupt, 0, 2748c2ecf20Sopenharmony_ci SUPERIO, (void *)sio)) { 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "could not get irq\n"); 2778c2ecf20Sopenharmony_ci BUG(); 2788c2ecf20Sopenharmony_ci return; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci sio->suckyio_irq_enabled = 1; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87560_LIO, superio_init); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void superio_mask_irq(struct irq_data *d) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 2888c2ecf20Sopenharmony_ci u8 r8; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if ((irq < 1) || (irq == 2) || (irq > 7)) { 2918c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Illegal irq number.\n"); 2928c2ecf20Sopenharmony_ci BUG(); 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Mask interrupt */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci r8 = inb(IC_PIC1+1); 2998c2ecf20Sopenharmony_ci r8 |= (1 << irq); 3008c2ecf20Sopenharmony_ci outb (r8,IC_PIC1+1); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void superio_unmask_irq(struct irq_data *d) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 3068c2ecf20Sopenharmony_ci u8 r8; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if ((irq < 1) || (irq == 2) || (irq > 7)) { 3098c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Illegal irq number (%d).\n", irq); 3108c2ecf20Sopenharmony_ci BUG(); 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Unmask interrupt */ 3158c2ecf20Sopenharmony_ci r8 = inb(IC_PIC1+1); 3168c2ecf20Sopenharmony_ci r8 &= ~(1 << irq); 3178c2ecf20Sopenharmony_ci outb (r8,IC_PIC1+1); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic struct irq_chip superio_interrupt_type = { 3218c2ecf20Sopenharmony_ci .name = SUPERIO, 3228c2ecf20Sopenharmony_ci .irq_unmask = superio_unmask_irq, 3238c2ecf20Sopenharmony_ci .irq_mask = superio_mask_irq, 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#ifdef DEBUG_SUPERIO_INIT 3278c2ecf20Sopenharmony_cistatic unsigned short expected_device[3] = { 3288c2ecf20Sopenharmony_ci PCI_DEVICE_ID_NS_87415, 3298c2ecf20Sopenharmony_ci PCI_DEVICE_ID_NS_87560_LIO, 3308c2ecf20Sopenharmony_ci PCI_DEVICE_ID_NS_87560_USB 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci#endif 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciint superio_fixup_irq(struct pci_dev *pcidev) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int local_irq, i; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci#ifdef DEBUG_SUPERIO_INIT 3398c2ecf20Sopenharmony_ci int fn; 3408c2ecf20Sopenharmony_ci fn = PCI_FUNC(pcidev->devfn); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Verify the function number matches the expected device id. */ 3438c2ecf20Sopenharmony_ci if (expected_device[fn] != pcidev->device) { 3448c2ecf20Sopenharmony_ci BUG(); 3458c2ecf20Sopenharmony_ci return -1; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci printk(KERN_DEBUG "superio_fixup_irq(%s) ven 0x%x dev 0x%x from %ps\n", 3488c2ecf20Sopenharmony_ci pci_name(pcidev), 3498c2ecf20Sopenharmony_ci pcidev->vendor, pcidev->device, 3508c2ecf20Sopenharmony_ci __builtin_return_address(0)); 3518c2ecf20Sopenharmony_ci#endif 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 3548c2ecf20Sopenharmony_ci irq_set_chip_and_handler(i, &superio_interrupt_type, 3558c2ecf20Sopenharmony_ci handle_simple_irq); 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * We don't allocate a SuperIO irq for the legacy IO function, 3608c2ecf20Sopenharmony_ci * since it is a "bridge". Instead, we will allocate irq's for 3618c2ecf20Sopenharmony_ci * each legacy device as they are initialized. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci switch(pcidev->device) { 3658c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_NS_87415: /* Function 0 */ 3668c2ecf20Sopenharmony_ci local_irq = IDE_IRQ; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_NS_87560_LIO: /* Function 1 */ 3698c2ecf20Sopenharmony_ci sio_dev.lio_pdev = pcidev; /* save for superio_init() */ 3708c2ecf20Sopenharmony_ci return -1; 3718c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_NS_87560_USB: /* Function 2 */ 3728c2ecf20Sopenharmony_ci sio_dev.usb_pdev = pcidev; /* save for superio_init() */ 3738c2ecf20Sopenharmony_ci local_irq = USB_IRQ; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci local_irq = -1; 3778c2ecf20Sopenharmony_ci BUG(); 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return local_irq; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void __init superio_serial_init(void) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIAL_8250 3878c2ecf20Sopenharmony_ci int retval; 3888c2ecf20Sopenharmony_ci struct uart_port serial_port; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci memset(&serial_port, 0, sizeof(serial_port)); 3918c2ecf20Sopenharmony_ci serial_port.iotype = UPIO_PORT; 3928c2ecf20Sopenharmony_ci serial_port.type = PORT_16550A; 3938c2ecf20Sopenharmony_ci serial_port.uartclk = 115200*16; 3948c2ecf20Sopenharmony_ci serial_port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | 3958c2ecf20Sopenharmony_ci UPF_BOOT_AUTOCONF; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* serial port #1 */ 3988c2ecf20Sopenharmony_ci serial_port.iobase = sio_dev.sp1_base; 3998c2ecf20Sopenharmony_ci serial_port.irq = SP1_IRQ; 4008c2ecf20Sopenharmony_ci serial_port.line = 0; 4018c2ecf20Sopenharmony_ci retval = early_serial_setup(&serial_port); 4028c2ecf20Sopenharmony_ci if (retval < 0) { 4038c2ecf20Sopenharmony_ci printk(KERN_WARNING PFX "Register Serial #0 failed.\n"); 4048c2ecf20Sopenharmony_ci return; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* serial port #2 */ 4088c2ecf20Sopenharmony_ci serial_port.iobase = sio_dev.sp2_base; 4098c2ecf20Sopenharmony_ci serial_port.irq = SP2_IRQ; 4108c2ecf20Sopenharmony_ci serial_port.line = 1; 4118c2ecf20Sopenharmony_ci retval = early_serial_setup(&serial_port); 4128c2ecf20Sopenharmony_ci if (retval < 0) 4138c2ecf20Sopenharmony_ci printk(KERN_WARNING PFX "Register Serial #1 failed.\n"); 4148c2ecf20Sopenharmony_ci#endif /* CONFIG_SERIAL_8250 */ 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void __init superio_parport_init(void) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci#ifdef CONFIG_PARPORT_PC 4218c2ecf20Sopenharmony_ci if (!parport_pc_probe_port(sio_dev.pp_base, 4228c2ecf20Sopenharmony_ci 0 /*base_hi*/, 4238c2ecf20Sopenharmony_ci PAR_IRQ, 4248c2ecf20Sopenharmony_ci PARPORT_DMA_NONE /* dma */, 4258c2ecf20Sopenharmony_ci NULL /*struct pci_dev* */, 4268c2ecf20Sopenharmony_ci 0 /* shared irq flags */)) 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci printk(KERN_WARNING PFX "Probing parallel port failed.\n"); 4298c2ecf20Sopenharmony_ci#endif /* CONFIG_PARPORT_PC */ 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void superio_fixup_pci(struct pci_dev *pdev) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci u8 prog; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci pdev->class |= 0x5; 4388c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, PCI_CLASS_PROG, pdev->class); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog); 4418c2ecf20Sopenharmony_ci printk("PCI: Enabled native mode for NS87415 (pif=0x%x)\n", prog); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, superio_fixup_pci); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int __init 4478c2ecf20Sopenharmony_cisuperio_probe(struct pci_dev *dev, const struct pci_device_id *id) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct superio_device *sio = &sio_dev; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci ** superio_probe(00:0e.0) ven 0x100b dev 0x2 sv 0x0 sd 0x0 class 0x1018a 4538c2ecf20Sopenharmony_ci ** superio_probe(00:0e.1) ven 0x100b dev 0xe sv 0x0 sd 0x0 class 0x68000 4548c2ecf20Sopenharmony_ci ** superio_probe(00:0e.2) ven 0x100b dev 0x12 sv 0x0 sd 0x0 class 0xc0310 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci DBG_INIT("superio_probe(%s) ven 0x%x dev 0x%x sv 0x%x sd 0x%x class 0x%x\n", 4578c2ecf20Sopenharmony_ci pci_name(dev), 4588c2ecf20Sopenharmony_ci dev->vendor, dev->device, 4598c2ecf20Sopenharmony_ci dev->subsystem_vendor, dev->subsystem_device, 4608c2ecf20Sopenharmony_ci dev->class); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci BUG_ON(!sio->suckyio_irq_enabled); /* Enabled by PCI_FIXUP_FINAL */ 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (dev->device == PCI_DEVICE_ID_NS_87560_LIO) { /* Function 1 */ 4658c2ecf20Sopenharmony_ci superio_parport_init(); 4668c2ecf20Sopenharmony_ci superio_serial_init(); 4678c2ecf20Sopenharmony_ci /* REVISIT XXX : superio_fdc_init() ? */ 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci } else if (dev->device == PCI_DEVICE_ID_NS_87415) { /* Function 0 */ 4708c2ecf20Sopenharmony_ci DBG_INIT("superio_probe: ignoring IDE 87415\n"); 4718c2ecf20Sopenharmony_ci } else if (dev->device == PCI_DEVICE_ID_NS_87560_USB) { /* Function 2 */ 4728c2ecf20Sopenharmony_ci DBG_INIT("superio_probe: ignoring USB OHCI controller\n"); 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci DBG_INIT("superio_probe: WTF? Fire Extinguisher?\n"); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Let appropriate other driver claim this device. */ 4788c2ecf20Sopenharmony_ci return -ENODEV; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic const struct pci_device_id superio_tbl[] __initconst = { 4828c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87560_LIO) }, 4838c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87560_USB) }, 4848c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415) }, 4858c2ecf20Sopenharmony_ci { 0, } 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic struct pci_driver superio_driver __refdata = { 4898c2ecf20Sopenharmony_ci .name = SUPERIO, 4908c2ecf20Sopenharmony_ci .id_table = superio_tbl, 4918c2ecf20Sopenharmony_ci .probe = superio_probe, 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cimodule_pci_driver(superio_driver); 495