⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pseries_pci.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * pSeries_pci.c * * Copyright (C) 2001 Dave Engebretsen, IBM Corporation * * pSeries specific routines for PCI. *  * Based on code from pci.c and chrp_pci.c * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */#include <linux/kernel.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/init.h>#include <linux/bootmem.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/init.h>#include <asm/pci-bridge.h>#include <asm/ppcdebug.h>#include <asm/naca.h>#include <asm/pci_dma.h>#include "xics.h"#include "open_pic.h"#include "pci.h"extern struct device_node *allnodes;/******************************************************************* * Forward declares of prototypes.  *******************************************************************/unsigned long find_and_init_phbs(void);struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ;void pSeries_pcibios_fixup(void);static int rtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval);/* RTAS tokens */static int read_pci_config;static int write_pci_config;static int ibm_read_pci_config;static int ibm_write_pci_config;static int s7a_workaround;/****************************************************************************** * * pSeries I/O Operations to access the PCI configuration space. * *****************************************************************************/#define RTAS_PCI_READ_OP(size, type, nbytes) \int __chrp \rtas_read_config_##size(struct device_node *dn, int offset, type val) {  \	unsigned long returnval = ~0L; \	unsigned long buid; \	unsigned int addr; \	int ret; \	 \	if (dn == NULL) { \		ret = -2; \	} else if (dn->status) { \		ret = -1; \	} else { \		addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \		buid = dn->phb->buid; \		if (buid) { \			ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, addr, buid >> 32, buid & 0xffffffff, nbytes); \                        if (ret < 0 || (returnval == 0xffffffff)) \                               ret = rtas_fake_read(dn, offset, nbytes, &returnval); \		} else { \			ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, nbytes); \		} \	} \	*val = returnval; \	return ret; \} \int __chrp \rtas_pci_read_config_##size(struct pci_dev *dev, int offset, type val) {  \        struct device_node *dn = pci_device_to_OF_node(dev); \	int ret = rtas_read_config_##size(dn, offset, val); \        /* udbg_printf("read bus=%x, devfn=%x, ret=%d phb=%lx, dn=%lx\n", dev->bus->number, dev->devfn, ret, dn ? dn->phb : 0, dn); */ \        return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; \}#define RTAS_PCI_WRITE_OP(size, type, nbytes) \int __chrp \rtas_write_config_##size(struct device_node *dn, int offset, type val) { \	unsigned long buid; \	unsigned int addr; \	int ret; \	 \	if (dn == NULL) { \		ret = -2; \	} else if (dn->status) { \		ret = -1; \	} else { \		buid = dn->phb->buid; \		addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \		if (buid) { \			ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, nbytes, (ulong) val); \		} else { \			ret = rtas_call(write_pci_config, 3, 1, NULL, addr, nbytes, (ulong)val); \		} \	} \	return ret; \} \int __chrp \rtas_pci_write_config_##size(struct pci_dev *dev, int offset, type val) { \	struct device_node*  dn = pci_device_to_OF_node(dev); \	int  ret = rtas_write_config_##size(dn, offset, val); \	/* udbg_printf("write bus=%x, devfn=%x, ret=%d phb=%lx, dn=%lx\n", dev->bus->number, dev->devfn, ret, dn ? dn->phb : 0, dn); */ \	return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; \}RTAS_PCI_READ_OP(byte, u8 *, 1)RTAS_PCI_READ_OP(word, u16 *, 2)RTAS_PCI_READ_OP(dword, u32 *, 4)RTAS_PCI_WRITE_OP(byte, u8, 1)RTAS_PCI_WRITE_OP(word, u16, 2)RTAS_PCI_WRITE_OP(dword, u32, 4)struct pci_ops rtas_pci_ops = {	rtas_pci_read_config_byte,	rtas_pci_read_config_word,	rtas_pci_read_config_dword,	rtas_pci_write_config_byte, 	rtas_pci_write_config_word,	rtas_pci_write_config_dword,};/* * Handle the case where rtas refuses to do a pci config read. * This currently only happens with some PHBs in which case we totally fake * out the values (and call it a speedwagaon -- something we could look up * in the device tree). */static intrtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval){	char *device_type = (char *)get_property(dn, "device_type", 0);	u32 *class_code = (u32 *)get_property(dn, "class-code", 0);	*returnval = ~0;	/* float by default */	/* udbg_printf("rtas_fake_read dn=%p, offset=0x%02x, nbytes=%d, device_type=%s\n", dn, offset, nbytes, device_type ? device_type : "<none>"); */	if (device_type && strcmp(device_type, "pci") != 0)		return -3;	/* Not a phb or bridge */	/* NOTE: class_code != NULL => EADS pci bridge.  Else a PHB */	if (nbytes == 1) {		if (offset == PCI_HEADER_TYPE)			*returnval = 0x80;	/* multifunction */		else if (offset == PCI_INTERRUPT_PIN || offset == PCI_INTERRUPT_LINE)			*returnval = 0;	} else if (nbytes == 2) {		if (offset == PCI_SUBSYSTEM_VENDOR_ID || offset == PCI_SUBSYSTEM_ID)			*returnval = 0;		else if (offset == PCI_COMMAND)			*returnval = PCI_COMMAND_PARITY|PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY;	} else if (nbytes == 4) {		if (offset == PCI_VENDOR_ID)			*returnval = 0x1014 | ((class_code ? 0x8b : 0x102) << 16); /* a phb */		else if (offset == PCI_REVISION_ID)			*returnval = (class_code ? PCI_CLASS_BRIDGE_PCI : PCI_CLASS_BRIDGE_HOST) << 16; /* revs are zero */		else if ((offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) || offset == PCI_ROM_ADDRESS)			*returnval = 0;	}	/* printk("fake: %s nbytes=%d, offset=%lx ret=%lx\n", class_code ? "EADS" : "PHB", nbytes, offset, *returnval); */	return 0;}/****************************************************************** * pci_read_irq_line * * Reads the Interrupt Pin to determine if interrupt is use by card. * If the interrupt is used, then gets the interrupt line from the  * openfirmware and sets it in the pci_dev and pci_config line. * ******************************************************************/int pci_read_irq_line(struct pci_dev *Pci_Dev){	u8 InterruptPin;	struct device_node *Node;    	pci_read_config_byte(Pci_Dev, PCI_INTERRUPT_PIN, &InterruptPin);	if (InterruptPin == 0) {		PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Interrupt used by device.\n",Pci_Dev->slot_name);		return 0;		}	Node = pci_device_to_OF_node(Pci_Dev);	if ( Node == NULL) { 		PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s Device Node not found.\n",Pci_Dev->slot_name);		return -1;		}	if (Node->n_intrs == 0) 	{		PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Device OF interrupts defined.\n",Pci_Dev->slot_name);		return -1;		}	Pci_Dev->irq = Node->intrs[0].line;	if (s7a_workaround) {		if (Pci_Dev->irq > 16)			Pci_Dev->irq -= 3;	}	pci_write_config_byte(Pci_Dev, PCI_INTERRUPT_LINE, Pci_Dev->irq);		PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n",Pci_Dev->slot_name,Pci_Dev->irq);	return 0;}/****************************************************************** * Find all PHBs in the system and initialize a set of data  * structures to represent them. ******************************************************************/unsigned long __initfind_and_init_phbs(void){        struct device_node *Pci_Node;        struct pci_controller *phb;        unsigned int root_addr_size_words = 0, this_addr_size_words = 0;	unsigned int this_addr_count = 0, range_stride;        unsigned int *ui_ptr = NULL, *ranges;        char *model;	struct pci_range64 range;	struct resource *res;	unsigned int memno, rlen, i, index;	unsigned int *opprop;	int has_isa = 0;        PPCDBG(PPCDBG_PHBINIT, "find_and_init_phbs\n"); 	read_pci_config = rtas_token("read-pci-config");	write_pci_config = rtas_token("write-pci-config");	ibm_read_pci_config = rtas_token("ibm,read-pci-config");	ibm_write_pci_config = rtas_token("ibm,write-pci-config");	if (naca->interrupt_controller == IC_OPEN_PIC) {		opprop = (unsigned int *)get_property(find_path_device("/"),				"platform-open-pic", NULL);	}	/* Get the root address word size. */	ui_ptr = (unsigned int *) get_property(find_path_device("/"), 					       "#size-cells", NULL);	if (ui_ptr) {		root_addr_size_words = *ui_ptr;	} else {		PPCDBG(PPCDBG_PHBINIT, "\tget #size-cells failed.\n"); 		return(-1);	}	if (find_type_devices("isa")) {		has_isa = 1;		PPCDBG(PPCDBG_PHBINIT, "\tFound an ISA bus.\n"); 	}	index = 0;	/******************************************************************	* Find all PHB devices and create an object for them.	******************************************************************/	for (Pci_Node = find_devices("pci"); Pci_Node != NULL; Pci_Node = Pci_Node->next) {		model = (char *) get_property(Pci_Node, "model", NULL);		if (model != NULL)  {			phb = alloc_phb(Pci_Node, model, root_addr_size_words);			if (phb == NULL) return(-1);		}		else {         		continue;		}				/* Get this node's address word size. */		ui_ptr = (unsigned int *) get_property(Pci_Node, "#size-cells", NULL);		if (ui_ptr)			this_addr_size_words = *ui_ptr;		else			this_addr_size_words = 1;		/* Get this node's address word count. */		ui_ptr = (unsigned int *) get_property(Pci_Node, "#address-cells", NULL);		if (ui_ptr)			this_addr_count = *ui_ptr;		else			this_addr_count = 3;				range_stride = this_addr_count + root_addr_size_words + this_addr_size_words;	      		memno = 0;		phb->io_base_phys = 0;         		ranges = (unsigned int *) get_property(Pci_Node, "ranges", &rlen);		PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen);                		for (i = 0; i < (rlen/sizeof(*ranges)); i+=range_stride) {		  	/* Put the PCI addr part of the current element into a 			 * '64' struct. 			 */		  	range = *((struct pci_range64 *)(ranges + i));			/* If this is a '32' element, map into a 64 struct. */			if ((range_stride * sizeof(int)) == 			   sizeof(struct pci_range32)) {				range.parent_addr = 					(unsigned long)(*(ranges + i + 3));				range.size = 					(((unsigned long)(*(ranges + i + 4)))<<32) | 					(*(ranges + i + 5));			} else {				range.parent_addr = 					(((unsigned long)(*(ranges + i + 3)))<<32) | 					(*(ranges + i + 4));				range.size = 					(((unsigned long)(*(ranges + i + 5)))<<32) | 					(*(ranges + i + 6));			}						PPCDBG(PPCDBG_PHBINIT, "\trange.parent_addr    = 0x%lx\n", 			       range.parent_addr);			PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.hi  = 0x%lx\n", 			       range.child_addr.a_hi);			PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.mid = 0x%lx\n", 			       range.child_addr.a_mid);			PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.lo  = 0x%lx\n", 			       range.child_addr.a_lo);			PPCDBG(PPCDBG_PHBINIT, "\trange.size           = 0x%lx\n", 			       range.size);			res = NULL;		        switch ((range.child_addr.a_hi >> 24) & 0x3) {			case 1:		/* I/O space */				PPCDBG(PPCDBG_PHBINIT, "\tIO Space\n");				phb->io_base_phys = range.parent_addr;				res = &phb->io_resource;				res->name = Pci_Node->full_name;				res->flags = IORESOURCE_IO;				phb->io_base_virt = __ioremap(phb->io_base_phys, range.size, _PAGE_NO_CACHE);				if (!pci_io_base) {					pci_io_base = (unsigned long)phb->io_base_virt;					if (has_isa)						isa_io_base = pci_io_base;				}				res->start = ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo));				res->start += (unsigned long)phb->io_base_virt - pci_io_base;				res->end =   res->start + range.size - 1;				res->parent = NULL;				res->sibling = NULL;				res->child = NULL;				phb->pci_io_offset = range.parent_addr - 					((((unsigned long)					   range.child_addr.a_mid) << 32) | 					 (range.child_addr.a_lo));				PPCDBG(PPCDBG_PHBINIT, "\tpci_io_offset  = 0x%lx\n", 				       phb->pci_io_offset);			  	break;			case 2:		/* mem space */				PPCDBG(PPCDBG_PHBINIT, "\tMem Space\n");				phb->pci_mem_offset = range.parent_addr - 					((((unsigned long)					   range.child_addr.a_mid) << 32) | 					 (range.child_addr.a_lo));				PPCDBG(PPCDBG_PHBINIT, "\tpci_mem_offset = 0x%lx\n", 				       phb->pci_mem_offset);				if (memno < sizeof(phb->mem_resources)/sizeof(phb->mem_resources[0])) {					res = &(phb->mem_resources[memno]);					++memno;					res->name = Pci_Node->full_name;					res->flags = IORESOURCE_MEM;					res->start = range.parent_addr;					res->end =   range.parent_addr + range.size - 1;					res->parent = NULL;					res->sibling = NULL;					res->child = NULL;

⌨️ 快捷键说明

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