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 + -
显示快捷键?