Skip to content
Snippets Groups Projects
pci-uclass.c 28.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		break;
    	default:
    		return value;
    	}
    	shift = (offset & off_mask) * 8;
    	ldata = (value & val_mask) << shift;
    	mask = val_mask << shift;
    	value = (old & ~mask) | ldata;
    
    	return value;
    }
    
    
    int pci_get_regions(struct udevice *dev, struct pci_region **iop,
    		    struct pci_region **memp, struct pci_region **prefp)
    {
    	struct udevice *bus = pci_get_controller(dev);
    	struct pci_controller *hose = dev_get_uclass_priv(bus);
    	int i;
    
    	*iop = NULL;
    	*memp = NULL;
    	*prefp = NULL;
    	for (i = 0; i < hose->region_count; i++) {
    		switch (hose->regions[i].flags) {
    		case PCI_REGION_IO:
    			if (!*iop || (*iop)->size < hose->regions[i].size)
    				*iop = hose->regions + i;
    			break;
    		case PCI_REGION_MEM:
    			if (!*memp || (*memp)->size < hose->regions[i].size)
    				*memp = hose->regions + i;
    			break;
    		case (PCI_REGION_MEM | PCI_REGION_PREFETCH):
    			if (!*prefp || (*prefp)->size < hose->regions[i].size)
    				*prefp = hose->regions + i;
    			break;
    		}
    	}
    
    	return (*iop != NULL) + (*memp != NULL) + (*prefp != NULL);
    }
    
    
    u32 dm_pci_read_bar32(struct udevice *dev, int barnum)
    {
    	u32 addr;
    	int bar;
    
    	bar = PCI_BASE_ADDRESS_0 + barnum * 4;
    	dm_pci_read_config32(dev, bar, &addr);
    	if (addr & PCI_BASE_ADDRESS_SPACE_IO)
    		return addr & PCI_BASE_ADDRESS_IO_MASK;
    	else
    		return addr & PCI_BASE_ADDRESS_MEM_MASK;
    }
    
    
    void dm_pci_write_bar32(struct udevice *dev, int barnum, u32 addr)
    {
    	int bar;
    
    	bar = PCI_BASE_ADDRESS_0 + barnum * 4;
    	dm_pci_write_config32(dev, bar, addr);
    }
    
    
    static int _dm_pci_bus_to_phys(struct udevice *ctlr,
    			       pci_addr_t bus_addr, unsigned long flags,
    			       unsigned long skip_mask, phys_addr_t *pa)
    {
    	struct pci_controller *hose = dev_get_uclass_priv(ctlr);
    	struct pci_region *res;
    	int i;
    
    	for (i = 0; i < hose->region_count; i++) {
    		res = &hose->regions[i];
    
    		if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
    			continue;
    
    		if (res->flags & skip_mask)
    			continue;
    
    		if (bus_addr >= res->bus_start &&
    		    (bus_addr - res->bus_start) < res->size) {
    			*pa = (bus_addr - res->bus_start + res->phys_start);
    			return 0;
    		}
    	}
    
    	return 1;
    }
    
    phys_addr_t dm_pci_bus_to_phys(struct udevice *dev, pci_addr_t bus_addr,
    			       unsigned long flags)
    {
    	phys_addr_t phys_addr = 0;
    	struct udevice *ctlr;
    	int ret;
    
    	/* The root controller has the region information */
    	ctlr = pci_get_controller(dev);
    
    	/*
    	 * if PCI_REGION_MEM is set we do a two pass search with preference
    	 * on matches that don't have PCI_REGION_SYS_MEMORY set
    	 */
    	if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
    		ret = _dm_pci_bus_to_phys(ctlr, bus_addr,
    					  flags, PCI_REGION_SYS_MEMORY,
    					  &phys_addr);
    		if (!ret)
    			return phys_addr;
    	}
    
    	ret = _dm_pci_bus_to_phys(ctlr, bus_addr, flags, 0, &phys_addr);
    
    	if (ret)
    		puts("pci_hose_bus_to_phys: invalid physical address\n");
    
    	return phys_addr;
    }
    
    int _dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
    			unsigned long flags, unsigned long skip_mask,
    			pci_addr_t *ba)
    {
    	struct pci_region *res;
    	struct udevice *ctlr;
    	pci_addr_t bus_addr;
    	int i;
    	struct pci_controller *hose;
    
    	/* The root controller has the region information */
    	ctlr = pci_get_controller(dev);
    	hose = dev_get_uclass_priv(ctlr);
    
    	for (i = 0; i < hose->region_count; i++) {
    		res = &hose->regions[i];
    
    		if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
    			continue;
    
    		if (res->flags & skip_mask)
    			continue;
    
    		bus_addr = phys_addr - res->phys_start + res->bus_start;
    
    		if (bus_addr >= res->bus_start &&
    		    (bus_addr - res->bus_start) < res->size) {
    			*ba = bus_addr;
    			return 0;
    		}
    	}
    
    	return 1;
    }
    
    pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
    			      unsigned long flags)
    {
    	pci_addr_t bus_addr = 0;
    	int ret;
    
    	/*
    	 * if PCI_REGION_MEM is set we do a two pass search with preference
    	 * on matches that don't have PCI_REGION_SYS_MEMORY set
    	 */
    	if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
    		ret = _dm_pci_phys_to_bus(dev, phys_addr, flags,
    					  PCI_REGION_SYS_MEMORY, &bus_addr);
    		if (!ret)
    			return bus_addr;
    	}
    
    	ret = _dm_pci_phys_to_bus(dev, phys_addr, flags, 0, &bus_addr);
    
    	if (ret)
    		puts("pci_hose_phys_to_bus: invalid physical address\n");
    
    	return bus_addr;
    }
    
    void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
    {
    	pci_addr_t pci_bus_addr;
    	u32 bar_response;
    
    	/* read BAR address */
    	dm_pci_read_config32(dev, bar, &bar_response);
    	pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
    
    	/*
    	 * Pass "0" as the length argument to pci_bus_to_virt.  The arg
    	 * isn't actualy used on any platform because u-boot assumes a static
    	 * linear mapping.  In the future, this could read the BAR size
    	 * and pass that as the size if needed.
    	 */
    	return dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
    }
    
    
    UCLASS_DRIVER(pci) = {
    	.id		= UCLASS_PCI,
    	.name		= "pci",
    
    	.flags		= DM_UC_FLAG_SEQ_ALIAS,
    
    	.post_bind	= pci_uclass_post_bind,
    	.pre_probe	= pci_uclass_pre_probe,
    	.post_probe	= pci_uclass_post_probe,
    	.child_post_bind = pci_uclass_child_post_bind,
    	.per_device_auto_alloc_size = sizeof(struct pci_controller),
    	.per_child_platdata_auto_alloc_size =
    			sizeof(struct pci_child_platdata),
    };
    
    static const struct dm_pci_ops pci_bridge_ops = {
    	.read_config	= pci_bridge_read_config,
    	.write_config	= pci_bridge_write_config,
    };
    
    static const struct udevice_id pci_bridge_ids[] = {
    	{ .compatible = "pci-bridge" },
    	{ }
    };
    
    U_BOOT_DRIVER(pci_bridge_drv) = {
    	.name		= "pci_bridge_drv",
    	.id		= UCLASS_PCI,
    	.of_match	= pci_bridge_ids,
    	.ops		= &pci_bridge_ops,
    };
    
    UCLASS_DRIVER(pci_generic) = {
    	.id		= UCLASS_PCI_GENERIC,
    	.name		= "pci_generic",
    };
    
    static const struct udevice_id pci_generic_ids[] = {
    	{ .compatible = "pci-generic" },
    	{ }
    };
    
    U_BOOT_DRIVER(pci_generic_drv) = {
    	.name		= "pci_generic_drv",
    	.id		= UCLASS_PCI_GENERIC,
    	.of_match	= pci_generic_ids,
    };
    
    
    void pci_init(void)
    {
    	struct udevice *bus;
    
    	/*
    	 * Enumerate all known controller devices. Enumeration has the side-
    	 * effect of probing them, so PCIe devices will be enumerated too.
    	 */
    	for (uclass_first_device(UCLASS_PCI, &bus);
    	     bus;
    	     uclass_next_device(&bus)) {
    		;
    	}
    }