dino.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,078 行 · 第 1/3 页
C
1,078 行
/*** DINO manager**** (c) Copyright 1999 Red Hat Software** (c) Copyright 1999 SuSE GmbH** (c) Copyright 1999,2000 Hewlett-Packard Company** (c) Copyright 2000 Grant Grundler**** This program is free software; you can redistribute it and/or modify** it under the terms of the GNU General Public License as published by** the Free Software Foundation; either version 2 of the License, or** (at your option) any later version.**** This module provides access to Dino PCI bus (config/IOport spaces)** and helps manage Dino IRQ lines.**** Dino interrupt handling is a bit complicated.** Dino always writes to the broadcast EIR via irr0 for now.** (BIG WARNING: using broadcast EIR is a really bad thing for SMP!)** Only one processor interrupt is used for the 11 IRQ line ** inputs to dino.**** The different between Built-in Dino and Card-Mode** dino is in chip initialization and pci device initialization.**** Linux drivers can only use Card-Mode Dino if pci devices I/O port** BARs are configured and used by the driver. Programming MMIO address ** requires substantial knowledge of available Host I/O address ranges** is currently not supported. Port/Config accessor functions are the** same. "BIOS" differences are handled within the existing routines.*//* Changes :** 2001-06-14 : Clement Moyroud (moyroudc@esiee.fr)** - added support for the integrated RS232. *//*** TODO: create a virtual address for each Dino HPA.** GSC code might be able to do this since IODC data tells us** how many pages are used. PCI subsystem could (must?) do this** for PCI drivers devices which implement/use MMIO registers.*/#include <linux/config.h>#include <linux/delay.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h> /* for struct irqaction */#include <linux/spinlock.h> /* for spinlock_t and prototypes */#include <asm/pdc.h>#include <asm/page.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/hardware.h>#include "gsc.h"#undef DINO_DEBUG#ifdef DINO_DEBUG#define DBG(x...) printk(x)#else#define DBG(x...)#endif/*** Config accessor functions only pass in the 8-bit bus number** and not the 8-bit "PCI Segment" number. Each Dino will be** assigned a PCI bus number based on "when" it's discovered.**** The "secondary" bus number is set to this before calling** pci_scan_bus(). If any PPB's are present, the scan will** discover them and update the "secondary" and "subordinate"** fields in Dino's pci_bus structure.**** Changes in the configuration *will* result in a different** bus number for each dino.*/#define is_card_dino(id) ((id)->hw_type == HPHW_A_DMA)#define DINO_IAR0 0x004#define DINO_IODC_ADDR 0x008#define DINO_IODC_DATA_0 0x008#define DINO_IODC_DATA_1 0x008#define DINO_IRR0 0x00C#define DINO_IAR1 0x010#define DINO_IRR1 0x014#define DINO_IMR 0x018#define DINO_IPR 0x01C#define DINO_TOC_ADDR 0x020#define DINO_ICR 0x024#define DINO_ILR 0x028#define DINO_IO_COMMAND 0x030#define DINO_IO_STATUS 0x034#define DINO_IO_CONTROL 0x038#define DINO_IO_GSC_ERR_RESP 0x040#define DINO_IO_ERR_INFO 0x044#define DINO_IO_PCI_ERR_RESP 0x048#define DINO_IO_FBB_EN 0x05c#define DINO_IO_ADDR_EN 0x060#define DINO_PCI_ADDR 0x064#define DINO_CONFIG_DATA 0x068#define DINO_IO_DATA 0x06c#define DINO_MEM_DATA 0x070 /* Dino 3.x only */#define DINO_GSC2X_CONFIG 0x7b4#define DINO_GMASK 0x800#define DINO_PAMR 0x804#define DINO_PAPR 0x808#define DINO_DAMODE 0x80c#define DINO_PCICMD 0x810#define DINO_PCISTS 0x814#define DINO_MLTIM 0x81c#define DINO_BRDG_FEAT 0x820#define DINO_PCIROR 0x824#define DINO_PCIWOR 0x828#define DINO_TLTIM 0x830#define DINO_IRQS 11 /* bits 0-10 are architected */#define DINO_IRR_MASK 0x5ff /* only 10 bits are implemented */#define DINO_MASK_IRQ(x) (1<<(x))#define PCIINTA 0x001#define PCIINTB 0x002#define PCIINTC 0x004#define PCIINTD 0x008#define PCIINTE 0x010#define PCIINTF 0x020#define GSCEXTINT 0x040/* #define xxx 0x080 - bit 7 is "default" *//* #define xxx 0x100 - bit 8 not used *//* #define xxx 0x200 - bit 9 not used */#define RS232INT 0x400struct dino_device{ struct pci_hba_data hba; /* 'C' inheritance - must be first */ spinlock_t dinosaur_pen; unsigned long txn_addr; /* EIR addr to generate interrupt */ u32 txn_data; /* EIR data assign to each dino */ int irq; /* Virtual IRQ dino uses */ struct irq_region *dino_region; /* region for this Dino */ u32 imr; /* IRQ's which are enabled */ #ifdef DINO_DEBUG unsigned int dino_irr0; /* save most recent IRQ line stat */ #endif};/* Looks nice and keeps the compiler happy */#define DINO_DEV(d) ((struct dino_device *) d)/* * Dino Configuration Space Accessor Functions */#define DINO_CFG_TOK(bus,dfn,pos) ((u32) ((bus)<<16 | (dfn)<<8 | (pos)))/* * keep the current highest bus count to assist in allocating busses. This * tries to keep a global bus count total so that when we discover an * entirely new bus, it can be given a unique bus number. */static int dino_current_bus = 0;static int dino_cfg_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val){ struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); unsigned long base_addr = d->hba.base_addr; unsigned long flags; spin_lock_irqsave(&d->dinosaur_pen, flags); /* tell HW which CFG address */ gsc_writel(v, base_addr + DINO_PCI_ADDR); /* generate cfg read cycle */ if (size == 1) { *val = gsc_readb(base_addr + DINO_CONFIG_DATA + (where & 3)); } else if (size == 2) { *val = le16_to_cpu(gsc_readw(base_addr + DINO_CONFIG_DATA + (where & 2))); } else if (size == 4) { *val = le32_to_cpu(gsc_readl(base_addr + DINO_CONFIG_DATA)); } spin_unlock_irqrestore(&d->dinosaur_pen, flags); return 0;}/* * Dino address stepping "feature": * When address stepping, Dino attempts to drive the bus one cycle too soon * even though the type of cycle (config vs. MMIO) might be different. * The read of Ven/Prod ID is harmless and avoids Dino's address stepping. */static int dino_cfg_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val){ struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); unsigned long base_addr = d->hba.base_addr; unsigned long flags; spin_lock_irqsave(&d->dinosaur_pen, flags); /* avoid address stepping feature */ gsc_writel(v & 0xffffff00, base_addr + DINO_PCI_ADDR); gsc_readl(base_addr + DINO_CONFIG_DATA); /* tell HW which CFG address */ gsc_writel(v, base_addr + DINO_PCI_ADDR); /* generate cfg read cycle */ if (size == 1) { gsc_writeb(val, base_addr + DINO_CONFIG_DATA + (where & 3)); } else if (size == 2) { gsc_writew(cpu_to_le16(val), base_addr + DINO_CONFIG_DATA + (where & 2)); } else if (size == 4) { gsc_writel(cpu_to_le32(val), base_addr + DINO_CONFIG_DATA); } spin_unlock_irqrestore(&d->dinosaur_pen, flags); return 0;}static struct pci_ops dino_cfg_ops = { .read = dino_cfg_read, .write = dino_cfg_write,};/* * Dino "I/O Port" Space Accessor Functions * * Many PCI devices don't require use of I/O port space (eg Tulip, * NCR720) since they export the same registers to both MMIO and * I/O port space. Performance is going to stink if drivers use * I/O port instead of MMIO. */#define cpu_to_le8(x) (x)#define le8_to_cpu(x) (x)#define DINO_PORT_IN(type, size, mask) \static u##size dino_in##size (struct pci_hba_data *d, u16 addr) \{ \ u##size v; \ unsigned long flags; \ spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \ /* tell HW which IO Port address */ \ gsc_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \ /* generate I/O PORT read cycle */ \ v = gsc_read##type(d->base_addr+DINO_IO_DATA+(addr&mask)); \ spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \ return le##size##_to_cpu(v); \}DINO_PORT_IN(b, 8, 3)DINO_PORT_IN(w, 16, 2)DINO_PORT_IN(l, 32, 0)#define DINO_PORT_OUT(type, size, mask) \static void dino_out##size (struct pci_hba_data *d, u16 addr, u##size val) \{ \ unsigned long flags; \ spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \ /* tell HW which IO port address */ \ gsc_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \ /* generate cfg write cycle */ \ gsc_write##type(cpu_to_le##size(val), d->base_addr+DINO_IO_DATA+(addr&mask)); \ spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \}DINO_PORT_OUT(b, 8, 3)DINO_PORT_OUT(w, 16, 2)DINO_PORT_OUT(l, 32, 0)struct pci_port_ops dino_port_ops = { .inb = dino_in8, .inw = dino_in16, .inl = dino_in32, .outb = dino_out8, .outw = dino_out16, .outl = dino_out32};static voiddino_mask_irq(void *irq_dev, int irq){ struct dino_device *dino_dev = DINO_DEV(irq_dev); DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, irq_dev, irq); if (NULL == irq_dev || irq > DINO_IRQS || irq < 0) { printk(KERN_WARNING "%s(0x%lx, %d) - not a dino irq?\n", __FUNCTION__, (long) irq_dev, irq); BUG(); } else { /* ** Clear the matching bit in the IMR register */ dino_dev->imr &= ~(DINO_MASK_IRQ(irq)); gsc_writel(dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR); }}static voiddino_unmask_irq(void *irq_dev, int irq){ struct dino_device *dino_dev = DINO_DEV(irq_dev); u32 tmp; DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, irq_dev, irq); if (NULL == irq_dev || irq > DINO_IRQS) { printk(KERN_WARNING "%s(): %d not a dino irq?\n", __FUNCTION__, irq); BUG(); return; } /* set the matching bit in the IMR register */ dino_dev->imr |= DINO_MASK_IRQ(irq); /* used in dino_isr() */ gsc_writel( dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR); /* Emulate "Level Triggered" Interrupt ** Basically, a driver is blowing it if the IRQ line is asserted ** while the IRQ is disabled. But tulip.c seems to do that.... ** Give 'em a kluge award and a nice round of applause! ** ** The gsc_write will generate an interrupt which invokes dino_isr(). ** dino_isr() will read IPR and find nothing. But then catch this ** when it also checks ILR. */ tmp = gsc_readl(dino_dev->hba.base_addr+DINO_ILR); if (tmp & DINO_MASK_IRQ(irq)) { DBG(KERN_WARNING "%s(): IRQ asserted! (ILR 0x%x)\n", __FUNCTION__, tmp); gsc_writel(dino_dev->txn_data, dino_dev->txn_addr); }}static void
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?