dino.c

来自「linux 内核源代码」· C语言 代码 · 共 1,071 行 · 第 1/3 页

C
1,071
字号
/***	DINO manager****	(c) Copyright 1999 Red Hat Software**	(c) Copyright 1999 SuSE GmbH**	(c) Copyright 1999,2000 Hewlett-Packard Company**	(c) Copyright 2000 Grant Grundler**	(c) Copyright 2006 Helge Deller****	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/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/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 is_cujo(id)		((id)->hversion == 0x682)#define DINO_IAR0		0x004#define DINO_IODC_ADDR		0x008#define DINO_IODC_DATA_0	0x008#define DINO_IODC_DATA_1	0x008#define DINO_IRR0		0x00C#define DINO_IAR1		0x010#define DINO_IRR1		0x014#define DINO_IMR		0x018#define DINO_IPR		0x01C#define DINO_TOC_ADDR		0x020#define DINO_ICR		0x024#define DINO_ILR		0x028#define DINO_IO_COMMAND		0x030#define DINO_IO_STATUS		0x034#define DINO_IO_CONTROL		0x038#define DINO_IO_GSC_ERR_RESP	0x040#define DINO_IO_ERR_INFO	0x044#define DINO_IO_PCI_ERR_RESP	0x048#define DINO_IO_FBB_EN		0x05c#define DINO_IO_ADDR_EN		0x060#define DINO_PCI_ADDR		0x064#define DINO_CONFIG_DATA	0x068#define DINO_IO_DATA		0x06c#define DINO_MEM_DATA		0x070	/* Dino 3.x only */#define DINO_GSC2X_CONFIG	0x7b4#define DINO_GMASK		0x800#define DINO_PAMR		0x804#define DINO_PAPR		0x808#define DINO_DAMODE		0x80c#define DINO_PCICMD		0x810#define DINO_PCISTS		0x814#define DINO_MLTIM		0x81c#define DINO_BRDG_FEAT		0x820#define DINO_PCIROR		0x824#define DINO_PCIWOR		0x828#define DINO_TLTIM		0x830#define DINO_IRQS 11		/* bits 0-10 are architected */#define DINO_IRR_MASK	0x5ff	/* only 10 bits are implemented */#define DINO_LOCAL_IRQS (DINO_IRQS+1)#define DINO_MASK_IRQ(x)	(1<<(x))#define PCIINTA   0x001#define PCIINTB   0x002#define PCIINTC   0x004#define PCIINTD   0x008#define PCIINTE   0x010#define PCIINTF   0x020#define GSCEXTINT 0x040/* #define xxx       0x080 - bit 7 is "default" *//* #define xxx    0x100 - bit 8 not used *//* #define xxx    0x200 - bit 9 not used */#define RS232INT  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 */ 	u32 			imr;	  /* IRQ's which are enabled */ 	int			global_irq[DINO_LOCAL_IRQS]; /* map IMR bit to global irq */#ifdef DINO_DEBUG	unsigned int		dino_irr0; /* save most recent IRQ line stat */#endif};/* 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);	void __iomem *base_addr = d->hba.base_addr;	unsigned long flags;	DBG("%s: %p, %d, %d, %d\n", __FUNCTION__, base_addr, devfn, where,									size);	spin_lock_irqsave(&d->dinosaur_pen, flags);	/* tell HW which CFG address */	__raw_writel(v, base_addr + DINO_PCI_ADDR);	/* generate cfg read cycle */	if (size == 1) {		*val = readb(base_addr + DINO_CONFIG_DATA + (where & 3));	} else if (size == 2) {		*val = readw(base_addr + DINO_CONFIG_DATA + (where & 2));	} else if (size == 4) {		*val = readl(base_addr + DINO_CONFIG_DATA);	}	spin_unlock_irqrestore(&d->dinosaur_pen, flags);	return 0;}/* * Dino address stepping "feature": * When address stepping, Dino attempts to drive the bus one cycle too soon * even though the type of cycle (config vs. MMIO) might be different.  * The read of Ven/Prod ID is harmless and avoids Dino's address stepping. */static int dino_cfg_write(struct pci_bus *bus, unsigned int devfn, int where,	int size, u32 val){	struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge));	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;	u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3);	void __iomem *base_addr = d->hba.base_addr;	unsigned long flags;	DBG("%s: %p, %d, %d, %d\n", __FUNCTION__, base_addr, devfn, where,									size);	spin_lock_irqsave(&d->dinosaur_pen, flags);	/* avoid address stepping feature */	__raw_writel(v & 0xffffff00, base_addr + DINO_PCI_ADDR);	__raw_readl(base_addr + DINO_CONFIG_DATA);	/* tell HW which CFG address */	__raw_writel(v, base_addr + DINO_PCI_ADDR);	/* generate cfg read cycle */	if (size == 1) {		writeb(val, base_addr + DINO_CONFIG_DATA + (where & 3));	} else if (size == 2) {		writew(val, base_addr + DINO_CONFIG_DATA + (where & 2));	} else if (size == 4) {		writel(val, base_addr + DINO_CONFIG_DATA);	}	spin_unlock_irqrestore(&d->dinosaur_pen, flags);	return 0;}static struct pci_ops dino_cfg_ops = {	.read =		dino_cfg_read,	.write =	dino_cfg_write,};/* * Dino "I/O Port" Space Accessor Functions * * Many PCI devices don't require use of I/O port space (eg Tulip, * NCR720) since they export the same registers to both MMIO and * I/O port space.  Performance is going to stink if drivers use * I/O port instead of MMIO. */#define DINO_PORT_IN(type, size, mask) \static u##size dino_in##size (struct pci_hba_data *d, u16 addr) \{ \	u##size v; \	unsigned long flags; \	spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \	/* tell HW which IO Port address */ \	__raw_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \	/* generate I/O PORT read cycle */ \	v = read##type(d->base_addr+DINO_IO_DATA+(addr&mask)); \	spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \	return v; \}DINO_PORT_IN(b,  8, 3)DINO_PORT_IN(w, 16, 2)DINO_PORT_IN(l, 32, 0)#define DINO_PORT_OUT(type, size, mask) \static void dino_out##size (struct pci_hba_data *d, u16 addr, u##size val) \{ \	unsigned long flags; \	spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \	/* tell HW which IO port address */ \	__raw_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \	/* generate cfg write cycle */ \	write##type(val, d->base_addr+DINO_IO_DATA+(addr&mask)); \	spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \}DINO_PORT_OUT(b,  8, 3)DINO_PORT_OUT(w, 16, 2)DINO_PORT_OUT(l, 32, 0)struct pci_port_ops dino_port_ops = {	.inb	= dino_in8,	.inw	= dino_in16,	.inl	= dino_in32,	.outb	= dino_out8,	.outw	= dino_out16,	.outl	= dino_out32};static void dino_disable_irq(unsigned int irq){	struct dino_device *dino_dev = irq_desc[irq].chip_data;	int local_irq = gsc_find_local_irq(irq, dino_dev->global_irq, DINO_LOCAL_IRQS);	DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, dino_dev, irq);	/* Clear the matching bit in the IMR register */	dino_dev->imr &= ~(DINO_MASK_IRQ(local_irq));	__raw_writel(dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR);}static void dino_enable_irq(unsigned int irq){	struct dino_device *dino_dev = irq_desc[irq].chip_data;	int local_irq = gsc_find_local_irq(irq, dino_dev->global_irq, DINO_LOCAL_IRQS);	u32 tmp;	DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, dino_dev, irq);	/*	** clear pending IRQ bits	**	** This does NOT change ILR state!	** See comment below for ILR usage.	*/	__raw_readl(dino_dev->hba.base_addr+DINO_IPR);	/* set the matching bit in the IMR register */	dino_dev->imr |= DINO_MASK_IRQ(local_irq);	/* used in dino_isr() */	__raw_writel( dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR);	/* Emulate "Level Triggered" Interrupt	** Basically, a driver is blowing it if the IRQ line is asserted	** while the IRQ is disabled.  But tulip.c seems to do that....	** Give 'em a kluge award and a nice round of applause!	**	** The gsc_write will generate an interrupt which invokes dino_isr().	** dino_isr() will read IPR and find nothing. But then catch this	** when it also checks ILR.	*/	tmp = __raw_readl(dino_dev->hba.base_addr+DINO_ILR);	if (tmp & DINO_MASK_IRQ(local_irq)) {		DBG(KERN_WARNING "%s(): IRQ asserted! (ILR 0x%x)\n",				__FUNCTION__, tmp);		gsc_writel(dino_dev->txn_data, dino_dev->txn_addr);	}}static unsigned int dino_startup_irq(unsigned int irq){	dino_enable_irq(irq);	return 0;}static struct hw_interrupt_type dino_interrupt_type = {	.typename	= "GSC-PCI",	.startup	= dino_startup_irq,	.shutdown	= dino_disable_irq,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?