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

📄 pci_common.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: pci_common.c,v 1.27.2.2 2002/02/01 00:56:44 davem Exp $ * pci_common.c: PCI controller common support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) */#include <linux/string.h>#include <linux/slab.h>#include <linux/init.h>#include <asm/pbm.h>/* Fix self device of BUS and hook it into BUS->self. * The pci_scan_bus does not do this for the host bridge. */void __init pci_fixup_host_bridge_self(struct pci_bus *pbus){	struct list_head *walk = &pbus->devices;	walk = walk->next;	while (walk != &pbus->devices) {		struct pci_dev *pdev = pci_dev_b(walk);		if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) {			pbus->self = pdev;			return;		}		walk = walk->next;	}	prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n");	prom_halt();}/* Find the OBP PROM device tree node for a PCI device. * Return zero if not found. */static int __init find_device_prom_node(struct pci_pbm_info *pbm,					struct pci_dev *pdev,					int bus_prom_node,					struct linux_prom_pci_registers *pregs,					int *nregs){	int node;	/*	 * Return the PBM's PROM node in case we are it's PCI device,	 * as the PBM's reg property is different to standard PCI reg	 * properties. We would delete this device entry otherwise,	 * which confuses XFree86's device probing...	 */	if ((pdev->bus->number == pbm->pci_bus->number) && (pdev->devfn == 0) &&	    (pdev->vendor == PCI_VENDOR_ID_SUN) &&	    (pdev->device == PCI_DEVICE_ID_SUN_PBM ||	     pdev->device == PCI_DEVICE_ID_SUN_SCHIZO ||	     pdev->device == PCI_DEVICE_ID_SUN_SABRE ||	     pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD)) {		*nregs = 0;		return bus_prom_node;	}	node = prom_getchild(bus_prom_node);	while (node != 0) {		int err = prom_getproperty(node, "reg",					   (char *)pregs,					   sizeof(*pregs) * PROMREG_MAX);		if (err == 0 || err == -1)			goto do_next_sibling;		if (((pregs[0].phys_hi >> 8) & 0xff) == pdev->devfn) {			*nregs = err / sizeof(*pregs);			return node;		}	do_next_sibling:		node = prom_getsibling(node);	}	return 0;}/* Remove a PCI device from the device trees, then * free it up.  Note that this must run before * the device's resources are registered because we * do not handle unregistering them here. */static void pci_device_delete(struct pci_dev *pdev){	list_del(&pdev->global_list);	list_del(&pdev->bus_list);	/* Ok, all references are gone, free it up. */	kfree(pdev);}/* Older versions of OBP on PCI systems encode 64-bit MEM * space assignments incorrectly, this fixes them up.  We also * take the opportunity here to hide other kinds of bogus * assignments. */static void __init fixup_obp_assignments(struct pci_dev *pdev,					 struct pcidev_cookie *pcp){	int i;	if (pdev->vendor == PCI_VENDOR_ID_AL &&	    (pdev->device == PCI_DEVICE_ID_AL_M7101 ||	     pdev->device == PCI_DEVICE_ID_AL_M1533)) {		int i;		/* Zap all of the normal resources, they are		 * meaningless and generate bogus resource collision		 * messages.  This is OpenBoot's ill-fated attempt to		 * represent the implicit resources that these devices		 * have.		 */		pcp->num_prom_assignments = 0;		for (i = 0; i < 6; i++) {			pdev->resource[i].start =				pdev->resource[i].end =				pdev->resource[i].flags = 0;		}		pdev->resource[PCI_ROM_RESOURCE].start =			pdev->resource[PCI_ROM_RESOURCE].end =			pdev->resource[PCI_ROM_RESOURCE].flags = 0;		return;	}	for (i = 0; i < pcp->num_prom_assignments; i++) {		struct linux_prom_pci_registers *ap;		int space;		ap = &pcp->prom_assignments[i];		space = ap->phys_hi >> 24;		if ((space & 0x3) == 2 &&		    (space & 0x4) != 0) {			ap->phys_hi &= ~(0x7 << 24);			ap->phys_hi |= 0x3 << 24;		}	}}/* Fill in the PCI device cookie sysdata for the given * PCI device.  This cookie is the means by which one * can get to OBP and PCI controller specific information * for a PCI device. */static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,				      struct pci_dev *pdev,				      int bus_prom_node){	struct linux_prom_pci_registers pregs[PROMREG_MAX];	struct pcidev_cookie *pcp;	int device_prom_node, nregs, err;	device_prom_node = find_device_prom_node(pbm, pdev, bus_prom_node,						 pregs, &nregs);	if (device_prom_node == 0) {		/* If it is not in the OBP device tree then		 * there must be a damn good reason for it.		 *		 * So what we do is delete the device from the		 * PCI device tree completely.  This scenerio		 * is seen, for example, on CP1500 for the		 * second EBUS/HappyMeal pair if the external		 * connector for it is not present.		 */		pci_device_delete(pdev);		return;	}	pcp = kmalloc(sizeof(*pcp), GFP_ATOMIC);	if (pcp == NULL) {		prom_printf("PCI_COOKIE: Fatal malloc error, aborting...\n");		prom_halt();	}	pcp->pbm = pbm;	pcp->prom_node = device_prom_node;	memcpy(pcp->prom_regs, pregs, sizeof(pcp->prom_regs));	pcp->num_prom_regs = nregs;	err = prom_getproperty(device_prom_node, "name",			       pcp->prom_name, sizeof(pcp->prom_name));	if (err > 0)		pcp->prom_name[err] = 0;	else		pcp->prom_name[0] = 0;	err = prom_getproperty(device_prom_node,			       "assigned-addresses",			       (char *)pcp->prom_assignments,			       sizeof(pcp->prom_assignments));	if (err == 0 || err == -1)		pcp->num_prom_assignments = 0;	else		pcp->num_prom_assignments =			(err / sizeof(pcp->prom_assignments[0]));	if (strcmp(pcp->prom_name, "ebus") == 0) {		struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX];		int iter;		/* EBUS is special... */		err = prom_getproperty(device_prom_node, "ranges",				       (char *)&erng[0], sizeof(erng));		if (err == 0 || err == -1) {			prom_printf("EBUS: Fatal error, no range property\n");			prom_halt();		}		err = (err / sizeof(erng[0]));		for(iter = 0; iter < err; iter++) {			struct linux_prom_ebus_ranges *ep = &erng[iter];			struct linux_prom_pci_registers *ap;			ap = &pcp->prom_assignments[iter];			ap->phys_hi = ep->parent_phys_hi;			ap->phys_mid = ep->parent_phys_mid;			ap->phys_lo = ep->parent_phys_lo;			ap->size_hi = 0;			ap->size_lo = ep->size;		}		pcp->num_prom_assignments = err;	}	fixup_obp_assignments(pdev, pcp);	pdev->sysdata = pcp;}void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,				    struct pci_pbm_info *pbm,				    int prom_node){	struct list_head *walk = &pbus->devices;	/* This loop is coded like this because the cookie	 * fillin routine can delete devices from the tree.	 */	walk = walk->next;	while (walk != &pbus->devices) {		struct pci_dev *pdev = pci_dev_b(walk);		struct list_head *walk_next = walk->next;		pdev_cookie_fillin(pbm, pdev, prom_node);		walk = walk_next;	}	walk = &pbus->children;	walk = walk->next;	while (walk != &pbus->children) {		struct pci_bus *this_pbus = pci_bus_b(walk);		struct pcidev_cookie *pcp = this_pbus->self->sysdata;		struct list_head *walk_next = walk->next;		pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node);		walk = walk_next;	}}static void __init bad_assignment(struct pci_dev *pdev,				  struct linux_prom_pci_registers *ap,				  struct resource *res,				  int do_prom_halt){	prom_printf("PCI: Bogus PROM assignment. BUS[%02x] DEVFN[%x]\n",		    pdev->bus->number, pdev->devfn);	if (ap)		prom_printf("PCI: phys[%08x:%08x:%08x] size[%08x:%08x]\n",			    ap->phys_hi, ap->phys_mid, ap->phys_lo,			    ap->size_hi, ap->size_lo);	if (res)		prom_printf("PCI: RES[%016lx-->%016lx:(%lx)]\n",			    res->start, res->end, res->flags);	prom_printf("Please email this information to davem@redhat.com\n");	if (do_prom_halt)		prom_halt();}static struct resource *__init get_root_resource(struct linux_prom_pci_registers *ap,			 struct pci_pbm_info *pbm){	int space = (ap->phys_hi >> 24) & 3;	switch (space) {	case 0:		/* Configuration space, silently ignore it. */		return NULL;	case 1:		/* 16-bit IO space */		return &pbm->io_space;	case 2:		/* 32-bit MEM space */		return &pbm->mem_space;	case 3:		/* 64-bit MEM space, these are allocated out of		 * the 32-bit mem_space range for the PBM, ie.		 * we just zero out the upper 32-bits.		 */		return &pbm->mem_space;	default:		printk("PCI: What is resource space %x? "		       "Tell davem@redhat.com about it!\n", space);		return NULL;	};}static struct resource *__init get_device_resource(struct linux_prom_pci_registers *ap,			   struct pci_dev *pdev){	struct resource *res;	int breg = (ap->phys_hi & 0xff);	int space = (ap->phys_hi >> 24) & 3;	switch (breg) {	case  PCI_ROM_ADDRESS:		/* It had better be MEM space. */		if (space != 2)			bad_assignment(pdev, ap, NULL, 0);		res = &pdev->resource[PCI_ROM_RESOURCE];		break;	case PCI_BASE_ADDRESS_0:	case PCI_BASE_ADDRESS_1:	case PCI_BASE_ADDRESS_2:	case PCI_BASE_ADDRESS_3:	case PCI_BASE_ADDRESS_4:	case PCI_BASE_ADDRESS_5:		res = &pdev->resource[(breg - PCI_BASE_ADDRESS_0) / 4];		break;	default:		bad_assignment(pdev, ap, NULL, 0);		res = NULL;		break;	};	return res;}static int __init pdev_resource_collisions_expected(struct pci_dev *pdev){	if (pdev->vendor != PCI_VENDOR_ID_SUN)		return 0;	if (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS ||	    pdev->device == PCI_DEVICE_ID_SUN_RIO_1394 ||	    pdev->device == PCI_DEVICE_ID_SUN_RIO_USB)		return 1;	return 0;}static void __init pdev_record_assignments(struct pci_pbm_info *pbm,					   struct pci_dev *pdev){	struct pcidev_cookie *pcp = pdev->sysdata;	int i;	for (i = 0; i < pcp->num_prom_assignments; i++) {		struct linux_prom_pci_registers *ap;		struct resource *root, *res;		/* The format of this property is specified in		 * the PCI Bus Binding to IEEE1275-1994.		 */		ap = &pcp->prom_assignments[i];		root = get_root_resource(ap, pbm);		res = get_device_resource(ap, pdev);		if (root == NULL || res == NULL ||		    res->flags == 0)			continue;		/* Ok we know which resource this PROM assignment is		 * for, sanity check it.		 */		if ((res->start & 0xffffffffUL) != ap->phys_lo)			bad_assignment(pdev, ap, res, 1);		/* If it is a 64-bit MEM space assignment, verify that		 * the resource is too and that the upper 32-bits match.		 */		if (((ap->phys_hi >> 24) & 3) == 3) {			if (((res->flags & IORESOURCE_MEM) == 0) ||			    ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)			     != PCI_BASE_ADDRESS_MEM_TYPE_64))				bad_assignment(pdev, ap, res, 1);			if ((res->start >> 32) != ap->phys_mid)				bad_assignment(pdev, ap, res, 1);			/* PBM cannot generate cpu initiated PIOs			 * to the full 64-bit space.  Therefore the			 * upper 32-bits better be zero.  If it is			 * not, just skip it and we will assign it			 * properly ourselves.			 */			if ((res->start >> 32) != 0UL) {				printk(KERN_ERR "PCI: OBP assigns out of range MEM address "				       "%016lx for region %ld on device %s\n",				       res->start, (res - &pdev->resource[0]), pdev->name);				continue;			}		}		/* Adjust the resource into the physical address space		 * of this PBM.		 */		pbm->parent->resource_adjust(pdev, res, root);		if (request_resource(root, res) < 0) {			/* OK, there is some conflict.  But this is fine			 * since we'll reassign it in the fixup pass.			 *			 * We notify the user that OBP made an error if it			 * is a case we don't expect.			 */			if (!pdev_resource_collisions_expected(pdev)) {				printk(KERN_ERR "PCI: Address space collision on region %ld "				       "[%016lx:%016lx] of device %s\n",				       (res - &pdev->resource[0]),				       res->start, res->end,				       pdev->name);			}		}	}}void __init pci_record_assignments(struct pci_pbm_info *pbm,				   struct pci_bus *pbus){	struct list_head *walk = &pbus->devices;	for (walk = walk->next; walk != &pbus->devices; walk = walk->next)		pdev_record_assignments(pbm, pci_dev_b(walk));	walk = &pbus->children;	for (walk = walk->next; walk != &pbus->children; walk = walk->next)		pci_record_assignments(pbm, pci_bus_b(walk));}/* Return non-zero if PDEV has implicit I/O resources even * though it may not have an I/O base address register * active. */static int __init has_implicit_io(struct pci_dev *pdev){	int class = pdev->class >> 8;	if (class == PCI_CLASS_NOT_DEFINED ||	    class == PCI_CLASS_NOT_DEFINED_VGA ||	    class == PCI_CLASS_STORAGE_IDE ||	    (pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)		return 1;	return 0;}static void __init pdev_assign_unassigned(struct pci_pbm_info *pbm,					  struct pci_dev *pdev){	u32 reg;	u16 cmd;	int i, io_seen, mem_seen;	io_seen = mem_seen = 0;	for (i = 0; i < PCI_NUM_RESOURCES; i++) {		struct resource *root, *res;		unsigned long size, min, max, align;		res = &pdev->resource[i];		if (res->flags & IORESOURCE_IO)			io_seen++;		else if (res->flags & IORESOURCE_MEM)			mem_seen++;		/* If it is already assigned or the resource does		 * not exist, there is nothing to do.		 */		if (res->parent != NULL || res->flags == 0UL)			continue;		/* Determine the root we allocate from. */		if (res->flags & IORESOURCE_IO) {			root = &pbm->io_space;			min = root->start + 0x400UL;			max = root->end;		} else {			root = &pbm->mem_space;			min = root->start;			max = min + 0x80000000UL;		}		size = res->end - res->start;		align = size + 1;		if (allocate_resource(root, res, size + 1, min, max, align, NULL, NULL) < 0) {			/* uh oh */			prom_printf("PCI: Failed to allocate resource %d for %s\n",				    i, pdev->name);			prom_halt();		}		/* Update PCI config space. */		pbm->parent->base_address_update(pdev, i);	}	/* Special case, disable the ROM.  Several devices

⌨️ 快捷键说明

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