xref: /rk3399_rockchip-uboot/cmd/pci.c (revision 10dd02abedc0b56a54ea4bd652d80c9d87786704)
12e192b24SSimon Glass /*
22e192b24SSimon Glass  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
32e192b24SSimon Glass  * Andreas Heppel <aheppel@sysgo.de>
42e192b24SSimon Glass  *
52e192b24SSimon Glass  * (C) Copyright 2002
62e192b24SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
72e192b24SSimon Glass  * Wolfgang Grandegger, DENX Software Engineering, wg@denx.de.
82e192b24SSimon Glass  *
92e192b24SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
102e192b24SSimon Glass  */
112e192b24SSimon Glass 
122e192b24SSimon Glass /*
132e192b24SSimon Glass  * PCI routines
142e192b24SSimon Glass  */
152e192b24SSimon Glass 
162e192b24SSimon Glass #include <common.h>
172e192b24SSimon Glass #include <bootretry.h>
182e192b24SSimon Glass #include <cli.h>
192e192b24SSimon Glass #include <command.h>
202e192b24SSimon Glass #include <console.h>
212e192b24SSimon Glass #include <dm.h>
222e192b24SSimon Glass #include <asm/processor.h>
232e192b24SSimon Glass #include <asm/io.h>
242e192b24SSimon Glass #include <pci.h>
252e192b24SSimon Glass 
262e192b24SSimon Glass struct pci_reg_info {
272e192b24SSimon Glass 	const char *name;
282e192b24SSimon Glass 	enum pci_size_t size;
292e192b24SSimon Glass 	u8 offset;
302e192b24SSimon Glass };
312e192b24SSimon Glass 
pci_byte_size(enum pci_size_t size)322e192b24SSimon Glass static int pci_byte_size(enum pci_size_t size)
332e192b24SSimon Glass {
342e192b24SSimon Glass 	switch (size) {
352e192b24SSimon Glass 	case PCI_SIZE_8:
362e192b24SSimon Glass 		return 1;
372e192b24SSimon Glass 	case PCI_SIZE_16:
382e192b24SSimon Glass 		return 2;
392e192b24SSimon Glass 	case PCI_SIZE_32:
402e192b24SSimon Glass 	default:
412e192b24SSimon Glass 		return 4;
422e192b24SSimon Glass 	}
432e192b24SSimon Glass }
442e192b24SSimon Glass 
pci_field_width(enum pci_size_t size)452e192b24SSimon Glass static int pci_field_width(enum pci_size_t size)
462e192b24SSimon Glass {
472e192b24SSimon Glass 	return pci_byte_size(size) * 2;
482e192b24SSimon Glass }
492e192b24SSimon Glass 
pci_show_regs(struct udevice * dev,struct pci_reg_info * regs)502e192b24SSimon Glass static void pci_show_regs(struct udevice *dev, struct pci_reg_info *regs)
512e192b24SSimon Glass {
522e192b24SSimon Glass 	for (; regs->name; regs++) {
532e192b24SSimon Glass 		unsigned long val;
542e192b24SSimon Glass 
552e192b24SSimon Glass 		dm_pci_read_config(dev, regs->offset, &val, regs->size);
562e192b24SSimon Glass 		printf("  %s =%*s%#.*lx\n", regs->name,
572e192b24SSimon Glass 		       (int)(28 - strlen(regs->name)), "",
582e192b24SSimon Glass 		       pci_field_width(regs->size), val);
592e192b24SSimon Glass 	}
602e192b24SSimon Glass }
612e192b24SSimon Glass 
pci_bar_show(struct udevice * dev)624225cb09SVladimir Oltean static int pci_bar_show(struct udevice *dev)
63e5f96a87SYehuda Yitschak {
64e5f96a87SYehuda Yitschak 	u8 header_type;
65e5f96a87SYehuda Yitschak 	int bar_cnt, bar_id, mem_type;
66e5f96a87SYehuda Yitschak 	bool is_64, is_io;
67e5f96a87SYehuda Yitschak 	u32 base_low, base_high;
68e5f96a87SYehuda Yitschak 	u32 size_low, size_high;
69e5f96a87SYehuda Yitschak 	u64 base, size;
70e5f96a87SYehuda Yitschak 	u32 reg_addr;
71e5f96a87SYehuda Yitschak 	int prefetchable;
72e5f96a87SYehuda Yitschak 
73e5f96a87SYehuda Yitschak 	dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type);
743751f4dcSPali Rohár 	header_type &= 0x7f;
75e5f96a87SYehuda Yitschak 
76e5f96a87SYehuda Yitschak 	if (header_type == PCI_HEADER_TYPE_CARDBUS) {
77e5f96a87SYehuda Yitschak 		printf("CardBus doesn't support BARs\n");
78e5f96a87SYehuda Yitschak 		return -ENOSYS;
793751f4dcSPali Rohár 	} else if (header_type != PCI_HEADER_TYPE_NORMAL &&
803751f4dcSPali Rohár 		   header_type != PCI_HEADER_TYPE_BRIDGE) {
813751f4dcSPali Rohár 		printf("unknown header type\n");
823751f4dcSPali Rohár 		return -ENOSYS;
83e5f96a87SYehuda Yitschak 	}
84e5f96a87SYehuda Yitschak 
85e5f96a87SYehuda Yitschak 	bar_cnt = (header_type == PCI_HEADER_TYPE_NORMAL) ? 6 : 2;
86e5f96a87SYehuda Yitschak 
87e5f96a87SYehuda Yitschak 	printf("ID   Base                Size                Width  Type\n");
88e5f96a87SYehuda Yitschak 	printf("----------------------------------------------------------\n");
89e5f96a87SYehuda Yitschak 
90e5f96a87SYehuda Yitschak 	bar_id = 0;
91e5f96a87SYehuda Yitschak 	reg_addr = PCI_BASE_ADDRESS_0;
92e5f96a87SYehuda Yitschak 	while (bar_cnt) {
93e5f96a87SYehuda Yitschak 		dm_pci_read_config32(dev, reg_addr, &base_low);
94e5f96a87SYehuda Yitschak 		dm_pci_write_config32(dev, reg_addr, 0xffffffff);
95e5f96a87SYehuda Yitschak 		dm_pci_read_config32(dev, reg_addr, &size_low);
96e5f96a87SYehuda Yitschak 		dm_pci_write_config32(dev, reg_addr, base_low);
97e5f96a87SYehuda Yitschak 		reg_addr += 4;
98e5f96a87SYehuda Yitschak 
99e5f96a87SYehuda Yitschak 		base = base_low & ~0xf;
100e5f96a87SYehuda Yitschak 		size = size_low & ~0xf;
101e5f96a87SYehuda Yitschak 		base_high = 0x0;
102e5f96a87SYehuda Yitschak 		size_high = 0xffffffff;
103e5f96a87SYehuda Yitschak 		is_64 = 0;
104e5f96a87SYehuda Yitschak 		prefetchable = base_low & PCI_BASE_ADDRESS_MEM_PREFETCH;
105e5f96a87SYehuda Yitschak 		is_io = base_low & PCI_BASE_ADDRESS_SPACE_IO;
106e5f96a87SYehuda Yitschak 		mem_type = base_low & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
107e5f96a87SYehuda Yitschak 
108e5f96a87SYehuda Yitschak 		if (mem_type == PCI_BASE_ADDRESS_MEM_TYPE_64) {
109e5f96a87SYehuda Yitschak 			dm_pci_read_config32(dev, reg_addr, &base_high);
110e5f96a87SYehuda Yitschak 			dm_pci_write_config32(dev, reg_addr, 0xffffffff);
111e5f96a87SYehuda Yitschak 			dm_pci_read_config32(dev, reg_addr, &size_high);
112e5f96a87SYehuda Yitschak 			dm_pci_write_config32(dev, reg_addr, base_high);
113e5f96a87SYehuda Yitschak 			bar_cnt--;
114e5f96a87SYehuda Yitschak 			reg_addr += 4;
115e5f96a87SYehuda Yitschak 			is_64 = 1;
116e5f96a87SYehuda Yitschak 		}
117e5f96a87SYehuda Yitschak 
118e5f96a87SYehuda Yitschak 		base = base | ((u64)base_high << 32);
119e5f96a87SYehuda Yitschak 		size = size | ((u64)size_high << 32);
120e5f96a87SYehuda Yitschak 
121e5f96a87SYehuda Yitschak 		if ((!is_64 && size_low) || (is_64 && size)) {
122e5f96a87SYehuda Yitschak 			size = ~size + 1;
123002ab4c6SKunihiko Hayashi 			printf(" %d   %#018llx  %#018llx  %d     %s   %s\n",
12484d7f916SSimon Glass 			       bar_id, (unsigned long long)base,
12584d7f916SSimon Glass 			       (unsigned long long)size, is_64 ? 64 : 32,
126e5f96a87SYehuda Yitschak 			       is_io ? "I/O" : "MEM",
127e5f96a87SYehuda Yitschak 			       prefetchable ? "Prefetchable" : "");
128e5f96a87SYehuda Yitschak 		}
129e5f96a87SYehuda Yitschak 
130e5f96a87SYehuda Yitschak 		bar_id++;
131e5f96a87SYehuda Yitschak 		bar_cnt--;
132e5f96a87SYehuda Yitschak 	}
133e5f96a87SYehuda Yitschak 
134e5f96a87SYehuda Yitschak 	return 0;
135e5f96a87SYehuda Yitschak }
136e5f96a87SYehuda Yitschak 
1372e192b24SSimon Glass static struct pci_reg_info regs_start[] = {
1382e192b24SSimon Glass 	{ "vendor ID", PCI_SIZE_16, PCI_VENDOR_ID },
1392e192b24SSimon Glass 	{ "device ID", PCI_SIZE_16, PCI_DEVICE_ID },
1402e192b24SSimon Glass 	{ "command register ID", PCI_SIZE_16, PCI_COMMAND },
1412e192b24SSimon Glass 	{ "status register", PCI_SIZE_16, PCI_STATUS },
1422e192b24SSimon Glass 	{ "revision ID", PCI_SIZE_8, PCI_REVISION_ID },
1432e192b24SSimon Glass 	{},
1442e192b24SSimon Glass };
1452e192b24SSimon Glass 
1462e192b24SSimon Glass static struct pci_reg_info regs_rest[] = {
1472e192b24SSimon Glass 	{ "sub class code", PCI_SIZE_8, PCI_CLASS_SUB_CODE },
1482e192b24SSimon Glass 	{ "programming interface", PCI_SIZE_8, PCI_CLASS_PROG },
1492e192b24SSimon Glass 	{ "cache line", PCI_SIZE_8, PCI_CACHE_LINE_SIZE },
1502e192b24SSimon Glass 	{ "latency time", PCI_SIZE_8, PCI_LATENCY_TIMER },
1512e192b24SSimon Glass 	{ "header type", PCI_SIZE_8, PCI_HEADER_TYPE },
1522e192b24SSimon Glass 	{ "BIST", PCI_SIZE_8, PCI_BIST },
1532e192b24SSimon Glass 	{ "base address 0", PCI_SIZE_32, PCI_BASE_ADDRESS_0 },
1542e192b24SSimon Glass 	{},
1552e192b24SSimon Glass };
1562e192b24SSimon Glass 
1572e192b24SSimon Glass static struct pci_reg_info regs_normal[] = {
1582e192b24SSimon Glass 	{ "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
1592e192b24SSimon Glass 	{ "base address 2", PCI_SIZE_32, PCI_BASE_ADDRESS_2 },
1602e192b24SSimon Glass 	{ "base address 3", PCI_SIZE_32, PCI_BASE_ADDRESS_3 },
1612e192b24SSimon Glass 	{ "base address 4", PCI_SIZE_32, PCI_BASE_ADDRESS_4 },
1622e192b24SSimon Glass 	{ "base address 5", PCI_SIZE_32, PCI_BASE_ADDRESS_5 },
1632e192b24SSimon Glass 	{ "cardBus CIS pointer", PCI_SIZE_32, PCI_CARDBUS_CIS },
1642e192b24SSimon Glass 	{ "sub system vendor ID", PCI_SIZE_16, PCI_SUBSYSTEM_VENDOR_ID },
1652e192b24SSimon Glass 	{ "sub system ID", PCI_SIZE_16, PCI_SUBSYSTEM_ID },
1662e192b24SSimon Glass 	{ "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS },
1672e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
1682e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
1692e192b24SSimon Glass 	{ "min Grant", PCI_SIZE_8, PCI_MIN_GNT },
1702e192b24SSimon Glass 	{ "max Latency", PCI_SIZE_8, PCI_MAX_LAT },
1712e192b24SSimon Glass 	{},
1722e192b24SSimon Glass };
1732e192b24SSimon Glass 
1742e192b24SSimon Glass static struct pci_reg_info regs_bridge[] = {
1752e192b24SSimon Glass 	{ "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
1762e192b24SSimon Glass 	{ "primary bus number", PCI_SIZE_8, PCI_PRIMARY_BUS },
1772e192b24SSimon Glass 	{ "secondary bus number", PCI_SIZE_8, PCI_SECONDARY_BUS },
1782e192b24SSimon Glass 	{ "subordinate bus number", PCI_SIZE_8, PCI_SUBORDINATE_BUS },
1792e192b24SSimon Glass 	{ "secondary latency timer", PCI_SIZE_8, PCI_SEC_LATENCY_TIMER },
1802e192b24SSimon Glass 	{ "IO base", PCI_SIZE_8, PCI_IO_BASE },
1812e192b24SSimon Glass 	{ "IO limit", PCI_SIZE_8, PCI_IO_LIMIT },
1822e192b24SSimon Glass 	{ "secondary status", PCI_SIZE_16, PCI_SEC_STATUS },
1832e192b24SSimon Glass 	{ "memory base", PCI_SIZE_16, PCI_MEMORY_BASE },
1842e192b24SSimon Glass 	{ "memory limit", PCI_SIZE_16, PCI_MEMORY_LIMIT },
1852e192b24SSimon Glass 	{ "prefetch memory base", PCI_SIZE_16, PCI_PREF_MEMORY_BASE },
1862e192b24SSimon Glass 	{ "prefetch memory limit", PCI_SIZE_16, PCI_PREF_MEMORY_LIMIT },
1872e192b24SSimon Glass 	{ "prefetch memory base upper", PCI_SIZE_32, PCI_PREF_BASE_UPPER32 },
1882e192b24SSimon Glass 	{ "prefetch memory limit upper", PCI_SIZE_32, PCI_PREF_LIMIT_UPPER32 },
1892e192b24SSimon Glass 	{ "IO base upper 16 bits", PCI_SIZE_16, PCI_IO_BASE_UPPER16 },
1902e192b24SSimon Glass 	{ "IO limit upper 16 bits", PCI_SIZE_16, PCI_IO_LIMIT_UPPER16 },
1912e192b24SSimon Glass 	{ "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS1 },
1922e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
1932e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
1942e192b24SSimon Glass 	{ "bridge control", PCI_SIZE_16, PCI_BRIDGE_CONTROL },
1952e192b24SSimon Glass 	{},
1962e192b24SSimon Glass };
1972e192b24SSimon Glass 
1982e192b24SSimon Glass static struct pci_reg_info regs_cardbus[] = {
1992e192b24SSimon Glass 	{ "capabilities", PCI_SIZE_8, PCI_CB_CAPABILITY_LIST },
2002e192b24SSimon Glass 	{ "secondary status", PCI_SIZE_16, PCI_CB_SEC_STATUS },
2012e192b24SSimon Glass 	{ "primary bus number", PCI_SIZE_8, PCI_CB_PRIMARY_BUS },
2022e192b24SSimon Glass 	{ "CardBus number", PCI_SIZE_8, PCI_CB_CARD_BUS },
2032e192b24SSimon Glass 	{ "subordinate bus number", PCI_SIZE_8, PCI_CB_SUBORDINATE_BUS },
2042e192b24SSimon Glass 	{ "CardBus latency timer", PCI_SIZE_8, PCI_CB_LATENCY_TIMER },
2052e192b24SSimon Glass 	{ "CardBus memory base 0", PCI_SIZE_32, PCI_CB_MEMORY_BASE_0 },
2062e192b24SSimon Glass 	{ "CardBus memory limit 0", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_0 },
2072e192b24SSimon Glass 	{ "CardBus memory base 1", PCI_SIZE_32, PCI_CB_MEMORY_BASE_1 },
2082e192b24SSimon Glass 	{ "CardBus memory limit 1", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_1 },
2092e192b24SSimon Glass 	{ "CardBus IO base 0", PCI_SIZE_16, PCI_CB_IO_BASE_0 },
2102e192b24SSimon Glass 	{ "CardBus IO base high 0", PCI_SIZE_16, PCI_CB_IO_BASE_0_HI },
2112e192b24SSimon Glass 	{ "CardBus IO limit 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0 },
2122e192b24SSimon Glass 	{ "CardBus IO limit high 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0_HI },
2132e192b24SSimon Glass 	{ "CardBus IO base 1", PCI_SIZE_16, PCI_CB_IO_BASE_1 },
2142e192b24SSimon Glass 	{ "CardBus IO base high 1", PCI_SIZE_16, PCI_CB_IO_BASE_1_HI },
2152e192b24SSimon Glass 	{ "CardBus IO limit 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1 },
2162e192b24SSimon Glass 	{ "CardBus IO limit high 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1_HI },
2172e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
2182e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
2192e192b24SSimon Glass 	{ "bridge control", PCI_SIZE_16, PCI_CB_BRIDGE_CONTROL },
2202e192b24SSimon Glass 	{ "subvendor ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_VENDOR_ID },
2212e192b24SSimon Glass 	{ "subdevice ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_ID },
2222e192b24SSimon Glass 	{ "PC Card 16bit base address", PCI_SIZE_32, PCI_CB_LEGACY_MODE_BASE },
2232e192b24SSimon Glass 	{},
2242e192b24SSimon Glass };
2252e192b24SSimon Glass 
2262e192b24SSimon Glass /**
2272e192b24SSimon Glass  * pci_header_show() - Show the header of the specified PCI device.
2282e192b24SSimon Glass  *
2292e192b24SSimon Glass  * @dev: Bus+Device+Function number
2302e192b24SSimon Glass  */
pci_header_show(struct udevice * dev)231853d3013SVladimir Oltean static void pci_header_show(struct udevice *dev)
2322e192b24SSimon Glass {
2332e192b24SSimon Glass 	unsigned long class, header_type;
2342e192b24SSimon Glass 
2352e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
2362e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_HEADER_TYPE, &header_type, PCI_SIZE_8);
2372e192b24SSimon Glass 	pci_show_regs(dev, regs_start);
2382e192b24SSimon Glass 	printf("  class code =                  0x%.2x (%s)\n", (int)class,
2392e192b24SSimon Glass 	       pci_class_str(class));
2402e192b24SSimon Glass 	pci_show_regs(dev, regs_rest);
2412e192b24SSimon Glass 
242713811f4SPali Rohár 	switch (header_type & 0x7f) {
2432e192b24SSimon Glass 	case PCI_HEADER_TYPE_NORMAL:	/* "normal" PCI device */
2442e192b24SSimon Glass 		pci_show_regs(dev, regs_normal);
2452e192b24SSimon Glass 		break;
2462e192b24SSimon Glass 	case PCI_HEADER_TYPE_BRIDGE:	/* PCI-to-PCI bridge */
2472e192b24SSimon Glass 		pci_show_regs(dev, regs_bridge);
2482e192b24SSimon Glass 		break;
2492e192b24SSimon Glass 	case PCI_HEADER_TYPE_CARDBUS:	/* PCI-to-CardBus bridge */
2502e192b24SSimon Glass 		pci_show_regs(dev, regs_cardbus);
2512e192b24SSimon Glass 		break;
2522e192b24SSimon Glass 
2532e192b24SSimon Glass 	default:
2542e192b24SSimon Glass 		printf("unknown header\n");
2552e192b24SSimon Glass 		break;
2562e192b24SSimon Glass     }
2572e192b24SSimon Glass }
2582e192b24SSimon Glass 
pciinfo_header(bool short_listing)2598bb098e9SPali Rohár static void pciinfo_header(bool short_listing)
2602e192b24SSimon Glass {
2612e192b24SSimon Glass 	if (short_listing) {
2622e192b24SSimon Glass 		printf("BusDevFun  VendorId   DeviceId   Device Class       Sub-Class\n");
2632e192b24SSimon Glass 		printf("_____________________________________________________________\n");
2642e192b24SSimon Glass 	}
2652e192b24SSimon Glass }
2662e192b24SSimon Glass 
2672e192b24SSimon Glass /**
2682e192b24SSimon Glass  * pci_header_show_brief() - Show the short-form PCI device header
2692e192b24SSimon Glass  *
2702e192b24SSimon Glass  * Reads and prints the header of the specified PCI device in short form.
2712e192b24SSimon Glass  *
2722e192b24SSimon Glass  * @dev: PCI device to show
2732e192b24SSimon Glass  */
pci_header_show_brief(struct udevice * dev)2742e192b24SSimon Glass static void pci_header_show_brief(struct udevice *dev)
2752e192b24SSimon Glass {
2762e192b24SSimon Glass 	ulong vendor, device;
2772e192b24SSimon Glass 	ulong class, subclass;
2782e192b24SSimon Glass 
2792e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_VENDOR_ID, &vendor, PCI_SIZE_16);
2802e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_DEVICE_ID, &device, PCI_SIZE_16);
2812e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
2822e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_SUB_CODE, &subclass, PCI_SIZE_8);
2832e192b24SSimon Glass 
2842e192b24SSimon Glass 	printf("0x%.4lx     0x%.4lx     %-23s 0x%.2lx\n",
2852e192b24SSimon Glass 	       vendor, device,
2862e192b24SSimon Glass 	       pci_class_str(class), subclass);
2872e192b24SSimon Glass }
2882e192b24SSimon Glass 
pciinfo(struct udevice * bus,bool short_listing,bool multi)2898bb098e9SPali Rohár static void pciinfo(struct udevice *bus, bool short_listing, bool multi)
2902e192b24SSimon Glass {
2912e192b24SSimon Glass 	struct udevice *dev;
2922e192b24SSimon Glass 
2938bb098e9SPali Rohár 	if (!multi)
2948bb098e9SPali Rohár 		printf("Scanning PCI devices on bus %d\n", bus->seq);
2958bb098e9SPali Rohár 
2968bb098e9SPali Rohár 	if (!multi || bus->seq == 0)
2978bb098e9SPali Rohár 		pciinfo_header(short_listing);
2982e192b24SSimon Glass 
2992e192b24SSimon Glass 	for (device_find_first_child(bus, &dev);
3002e192b24SSimon Glass 	     dev;
3012e192b24SSimon Glass 	     device_find_next_child(&dev)) {
3022e192b24SSimon Glass 		struct pci_child_platdata *pplat;
3032e192b24SSimon Glass 
3042e192b24SSimon Glass 		pplat = dev_get_parent_platdata(dev);
3052e192b24SSimon Glass 		if (short_listing) {
3062e192b24SSimon Glass 			printf("%02x.%02x.%02x   ", bus->seq,
3072e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
3082e192b24SSimon Glass 			pci_header_show_brief(dev);
3092e192b24SSimon Glass 		} else {
3102e192b24SSimon Glass 			printf("\nFound PCI device %02x.%02x.%02x:\n", bus->seq,
3112e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
3122e192b24SSimon Glass 			pci_header_show(dev);
3132e192b24SSimon Glass 		}
3142e192b24SSimon Glass 	}
3152e192b24SSimon Glass }
3162e192b24SSimon Glass 
3172e192b24SSimon Glass /**
3182e192b24SSimon Glass  * get_pci_dev() - Convert the "bus.device.function" identifier into a number
3192e192b24SSimon Glass  *
3202e192b24SSimon Glass  * @name: Device string in the form "bus.device.function" where each is in hex
3212e192b24SSimon Glass  * @return encoded pci_dev_t or -1 if the string was invalid
3222e192b24SSimon Glass  */
get_pci_dev(char * name)3232e192b24SSimon Glass static pci_dev_t get_pci_dev(char *name)
3242e192b24SSimon Glass {
3252e192b24SSimon Glass 	char cnum[12];
3262e192b24SSimon Glass 	int len, i, iold, n;
3272e192b24SSimon Glass 	int bdfs[3] = {0,0,0};
3282e192b24SSimon Glass 
3292e192b24SSimon Glass 	len = strlen(name);
3302e192b24SSimon Glass 	if (len > 8)
3312e192b24SSimon Glass 		return -1;
3322e192b24SSimon Glass 	for (i = 0, iold = 0, n = 0; i < len; i++) {
3332e192b24SSimon Glass 		if (name[i] == '.') {
3342e192b24SSimon Glass 			memcpy(cnum, &name[iold], i - iold);
3352e192b24SSimon Glass 			cnum[i - iold] = '\0';
3362e192b24SSimon Glass 			bdfs[n++] = simple_strtoul(cnum, NULL, 16);
3372e192b24SSimon Glass 			iold = i + 1;
3382e192b24SSimon Glass 		}
3392e192b24SSimon Glass 	}
3402e192b24SSimon Glass 	strcpy(cnum, &name[iold]);
3412e192b24SSimon Glass 	if (n == 0)
3422e192b24SSimon Glass 		n = 1;
3432e192b24SSimon Glass 	bdfs[n] = simple_strtoul(cnum, NULL, 16);
3442e192b24SSimon Glass 
3452e192b24SSimon Glass 	return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]);
3462e192b24SSimon Glass }
3472e192b24SSimon Glass 
pci_cfg_display(struct udevice * dev,ulong addr,enum pci_size_t size,ulong length)3482e192b24SSimon Glass static int pci_cfg_display(struct udevice *dev, ulong addr,
3492e192b24SSimon Glass 			   enum pci_size_t size, ulong length)
3502e192b24SSimon Glass {
3512e192b24SSimon Glass #define DISP_LINE_LEN	16
3522e192b24SSimon Glass 	ulong i, nbytes, linebytes;
3532e192b24SSimon Glass 	int byte_size;
3542e192b24SSimon Glass 	int rc = 0;
3552e192b24SSimon Glass 
3562e192b24SSimon Glass 	byte_size = pci_byte_size(size);
3572e192b24SSimon Glass 	if (length == 0)
3582e192b24SSimon Glass 		length = 0x40 / byte_size; /* Standard PCI config space */
3592e192b24SSimon Glass 
3602e192b24SSimon Glass 	/* Print the lines.
3612e192b24SSimon Glass 	 * once, and all accesses are with the specified bus width.
3622e192b24SSimon Glass 	 */
3632e192b24SSimon Glass 	nbytes = length * byte_size;
3642e192b24SSimon Glass 	do {
3652e192b24SSimon Glass 		printf("%08lx:", addr);
3662e192b24SSimon Glass 		linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
3672e192b24SSimon Glass 		for (i = 0; i < linebytes; i += byte_size) {
3682e192b24SSimon Glass 			unsigned long val;
3692e192b24SSimon Glass 
3702e192b24SSimon Glass 			dm_pci_read_config(dev, addr, &val, size);
3712e192b24SSimon Glass 			printf(" %0*lx", pci_field_width(size), val);
3722e192b24SSimon Glass 			addr += byte_size;
3732e192b24SSimon Glass 		}
3742e192b24SSimon Glass 		printf("\n");
3752e192b24SSimon Glass 		nbytes -= linebytes;
3762e192b24SSimon Glass 		if (ctrlc()) {
3772e192b24SSimon Glass 			rc = 1;
3782e192b24SSimon Glass 			break;
3792e192b24SSimon Glass 		}
3802e192b24SSimon Glass 	} while (nbytes > 0);
3812e192b24SSimon Glass 
3822e192b24SSimon Glass 	return (rc);
3832e192b24SSimon Glass }
3842e192b24SSimon Glass 
pci_cfg_modify(struct udevice * dev,ulong addr,ulong size,ulong value,int incrflag)3852e192b24SSimon Glass static int pci_cfg_modify(struct udevice *dev, ulong addr, ulong size,
3862e192b24SSimon Glass 			  ulong value, int incrflag)
3872e192b24SSimon Glass {
3882e192b24SSimon Glass 	ulong	i;
3892e192b24SSimon Glass 	int	nbytes;
3902e192b24SSimon Glass 	ulong val;
3912e192b24SSimon Glass 
3922e192b24SSimon Glass 	/* Print the address, followed by value.  Then accept input for
3932e192b24SSimon Glass 	 * the next value.  A non-converted value exits.
3942e192b24SSimon Glass 	 */
3952e192b24SSimon Glass 	do {
3962e192b24SSimon Glass 		printf("%08lx:", addr);
3972e192b24SSimon Glass 		dm_pci_read_config(dev, addr, &val, size);
3982e192b24SSimon Glass 		printf(" %0*lx", pci_field_width(size), val);
3992e192b24SSimon Glass 
4002e192b24SSimon Glass 		nbytes = cli_readline(" ? ");
4012e192b24SSimon Glass 		if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
4022e192b24SSimon Glass 			/* <CR> pressed as only input, don't modify current
4032e192b24SSimon Glass 			 * location and move to next. "-" pressed will go back.
4042e192b24SSimon Glass 			 */
4052e192b24SSimon Glass 			if (incrflag)
4062e192b24SSimon Glass 				addr += nbytes ? -size : size;
4072e192b24SSimon Glass 			nbytes = 1;
4082e192b24SSimon Glass 			/* good enough to not time out */
4092e192b24SSimon Glass 			bootretry_reset_cmd_timeout();
4102e192b24SSimon Glass 		}
4112e192b24SSimon Glass #ifdef CONFIG_BOOT_RETRY_TIME
4122e192b24SSimon Glass 		else if (nbytes == -2) {
4132e192b24SSimon Glass 			break;	/* timed out, exit the command	*/
4142e192b24SSimon Glass 		}
4152e192b24SSimon Glass #endif
4162e192b24SSimon Glass 		else {
4172e192b24SSimon Glass 			char *endp;
4182e192b24SSimon Glass 			i = simple_strtoul(console_buffer, &endp, 16);
4192e192b24SSimon Glass 			nbytes = endp - console_buffer;
4202e192b24SSimon Glass 			if (nbytes) {
4212e192b24SSimon Glass 				/* good enough to not time out
4222e192b24SSimon Glass 				 */
4232e192b24SSimon Glass 				bootretry_reset_cmd_timeout();
4242e192b24SSimon Glass 				dm_pci_write_config(dev, addr, i, size);
4252e192b24SSimon Glass 				if (incrflag)
4262e192b24SSimon Glass 					addr += size;
4272e192b24SSimon Glass 			}
4282e192b24SSimon Glass 		}
4292e192b24SSimon Glass 	} while (nbytes);
4302e192b24SSimon Glass 
4312e192b24SSimon Glass 	return 0;
4322e192b24SSimon Glass }
4332e192b24SSimon Glass 
434b997a73eSSimon Glass static const struct pci_flag_info {
435b997a73eSSimon Glass 	uint flag;
436b997a73eSSimon Glass 	const char *name;
437b997a73eSSimon Glass } pci_flag_info[] = {
438b997a73eSSimon Glass 	{ PCI_REGION_IO, "io" },
439b997a73eSSimon Glass 	{ PCI_REGION_PREFETCH, "prefetch" },
440b997a73eSSimon Glass 	{ PCI_REGION_SYS_MEMORY, "sysmem" },
441b997a73eSSimon Glass 	{ PCI_REGION_RO, "readonly" },
442b997a73eSSimon Glass 	{ PCI_REGION_IO, "io" },
443b997a73eSSimon Glass };
444b997a73eSSimon Glass 
pci_show_regions(struct udevice * bus)445b997a73eSSimon Glass static void pci_show_regions(struct udevice *bus)
446b997a73eSSimon Glass {
44762ad0baaSPali Rohár 	struct pci_controller *hose = dev_get_uclass_priv(pci_get_controller(bus));
448b997a73eSSimon Glass 	const struct pci_region *reg;
449b997a73eSSimon Glass 	int i, j;
450b997a73eSSimon Glass 
451b997a73eSSimon Glass 	if (!hose) {
452b997a73eSSimon Glass 		printf("Bus '%s' is not a PCI controller\n", bus->name);
453b997a73eSSimon Glass 		return;
454b997a73eSSimon Glass 	}
455b997a73eSSimon Glass 
45662ad0baaSPali Rohár 	printf("Buses %02x-%02x\n", hose->first_busno, hose->last_busno);
457002ab4c6SKunihiko Hayashi 	printf("#   %-18s %-18s %-18s  %s\n", "Bus start", "Phys start", "Size",
458b997a73eSSimon Glass 	       "Flags");
459b997a73eSSimon Glass 	for (i = 0, reg = hose->regions; i < hose->region_count; i++, reg++) {
460002ab4c6SKunihiko Hayashi 		printf("%d   %#018llx %#018llx %#018llx  ", i,
461b997a73eSSimon Glass 		       (unsigned long long)reg->bus_start,
462b997a73eSSimon Glass 		       (unsigned long long)reg->phys_start,
463b997a73eSSimon Glass 		       (unsigned long long)reg->size);
464b997a73eSSimon Glass 		if (!(reg->flags & PCI_REGION_TYPE))
465b997a73eSSimon Glass 			printf("mem ");
466b997a73eSSimon Glass 		for (j = 0; j < ARRAY_SIZE(pci_flag_info); j++) {
467b997a73eSSimon Glass 			if (reg->flags & pci_flag_info[j].flag)
468b997a73eSSimon Glass 				printf("%s ", pci_flag_info[j].name);
469b997a73eSSimon Glass 		}
470b997a73eSSimon Glass 		printf("\n");
471b997a73eSSimon Glass 	}
472b997a73eSSimon Glass }
473b997a73eSSimon Glass 
4742e192b24SSimon Glass /* PCI Configuration Space access commands
4752e192b24SSimon Glass  *
4762e192b24SSimon Glass  * Syntax:
4772e192b24SSimon Glass  *	pci display[.b, .w, .l] bus.device.function} [addr] [len]
4782e192b24SSimon Glass  *	pci next[.b, .w, .l] bus.device.function [addr]
4792e192b24SSimon Glass  *      pci modify[.b, .w, .l] bus.device.function [addr]
4802e192b24SSimon Glass  *      pci write[.b, .w, .l] bus.device.function addr value
4812e192b24SSimon Glass  */
do_pci(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])4822e192b24SSimon Glass static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
4832e192b24SSimon Glass {
4842e192b24SSimon Glass 	ulong addr = 0, value = 0, cmd_size = 0;
4852e192b24SSimon Glass 	enum pci_size_t size = PCI_SIZE_32;
4862e192b24SSimon Glass 	struct udevice *dev, *bus;
4878bb098e9SPali Rohár 	int busnum = -1;
4882e192b24SSimon Glass 	pci_dev_t bdf = 0;
4892e192b24SSimon Glass 	char cmd = 's';
4902e192b24SSimon Glass 	int ret = 0;
49162ad0baaSPali Rohár 	char *endp;
4922e192b24SSimon Glass 
4932e192b24SSimon Glass 	if (argc > 1)
4942e192b24SSimon Glass 		cmd = argv[1][0];
4952e192b24SSimon Glass 
4962e192b24SSimon Glass 	switch (cmd) {
4972e192b24SSimon Glass 	case 'd':		/* display */
4982e192b24SSimon Glass 	case 'n':		/* next */
4992e192b24SSimon Glass 	case 'm':		/* modify */
5002e192b24SSimon Glass 	case 'w':		/* write */
5012e192b24SSimon Glass 		/* Check for a size specification. */
5022e192b24SSimon Glass 		cmd_size = cmd_get_data_size(argv[1], 4);
5032e192b24SSimon Glass 		size = (cmd_size == 4) ? PCI_SIZE_32 : cmd_size - 1;
5042e192b24SSimon Glass 		if (argc > 3)
5052e192b24SSimon Glass 			addr = simple_strtoul(argv[3], NULL, 16);
5062e192b24SSimon Glass 		if (argc > 4)
5072e192b24SSimon Glass 			value = simple_strtoul(argv[4], NULL, 16);
5082e192b24SSimon Glass 	case 'h':		/* header */
509e5f96a87SYehuda Yitschak 	case 'b':		/* bars */
51021c9fbd8SShawn Lin 	case 'a':		/* AER */
511e8697d50SShawn Lin 	case 'x':		/* retrain link */
512*10dd02abSShawn Lin 	case 'f':		/* FLR */
5132e192b24SSimon Glass 		if (argc < 3)
5142e192b24SSimon Glass 			goto usage;
5152e192b24SSimon Glass 		if ((bdf = get_pci_dev(argv[2])) == -1)
5162e192b24SSimon Glass 			return 1;
5172e192b24SSimon Glass 		break;
5182e192b24SSimon Glass 	case 'e':
519e578b92cSStephen Warren 		pci_init();
520e578b92cSStephen Warren 		return 0;
521b997a73eSSimon Glass 	case 'r': /* no break */
5222e192b24SSimon Glass 	default:		/* scan bus */
5232e192b24SSimon Glass 		value = 1; /* short listing */
5242e192b24SSimon Glass 		if (argc > 1) {
525b997a73eSSimon Glass 			if (cmd != 'r' && argv[argc-1][0] == 'l') {
5262e192b24SSimon Glass 				value = 0;
5272e192b24SSimon Glass 				argc--;
5282e192b24SSimon Glass 			}
52962ad0baaSPali Rohár 			if (argc > 2 || (argc > 1 && cmd != 'r' && argv[1][0] != 's')) {
5308bb098e9SPali Rohár 				if (argv[argc - 1][0] != '*') {
5318bb098e9SPali Rohár 					busnum = simple_strtoul(argv[argc - 1], &endp, 16);
5328bb098e9SPali Rohár 					if (*endp)
5338bb098e9SPali Rohár 						goto usage;
5348bb098e9SPali Rohár 				}
53562ad0baaSPali Rohár 				argc--;
53662ad0baaSPali Rohár 			}
53762ad0baaSPali Rohár 			if (cmd == 'r' && argc > 2)
53862ad0baaSPali Rohár 				goto usage;
53962ad0baaSPali Rohár 			else if (cmd != 'r' && (argc > 2 || (argc == 2 && argv[1][0] != 's')))
54062ad0baaSPali Rohár 				goto usage;
54162ad0baaSPali Rohár 		}
5428bb098e9SPali Rohár 		if (busnum == -1) {
5438bb098e9SPali Rohár 			if (cmd != 'r') {
5448bb098e9SPali Rohár 				for (busnum = 0;
5458bb098e9SPali Rohár 				     uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus) == 0;
5468bb098e9SPali Rohár 				     busnum++)
5478bb098e9SPali Rohár 					pciinfo(bus, value, true);
5488bb098e9SPali Rohár 			} else {
5498bb098e9SPali Rohár 				for (busnum = 0;
5508bb098e9SPali Rohár 				     uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus) == 0;
5518bb098e9SPali Rohár 				     busnum++) {
5528bb098e9SPali Rohár 					/* Regions are controller specific so skip non-root buses */
5538bb098e9SPali Rohár 					if (device_is_on_pci_bus(bus))
5548bb098e9SPali Rohár 						continue;
5558bb098e9SPali Rohár 					pci_show_regions(bus);
5568bb098e9SPali Rohár 				}
5578bb098e9SPali Rohár 			}
5588bb098e9SPali Rohár 			return 0;
5598bb098e9SPali Rohár 		}
5602e192b24SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
5612e192b24SSimon Glass 		if (ret) {
5622e192b24SSimon Glass 			printf("No such bus\n");
5632e192b24SSimon Glass 			return CMD_RET_FAILURE;
5642e192b24SSimon Glass 		}
565b997a73eSSimon Glass 		if (cmd == 'r')
566b997a73eSSimon Glass 			pci_show_regions(bus);
567b997a73eSSimon Glass 		else
5688bb098e9SPali Rohár 			pciinfo(bus, value, false);
5692e192b24SSimon Glass 		return 0;
5702e192b24SSimon Glass 	}
5712e192b24SSimon Glass 
5722e192b24SSimon Glass 	ret = dm_pci_bus_find_bdf(bdf, &dev);
5732e192b24SSimon Glass 	if (ret) {
5742e192b24SSimon Glass 		printf("No such device\n");
5752e192b24SSimon Glass 		return CMD_RET_FAILURE;
5762e192b24SSimon Glass 	}
5772e192b24SSimon Glass 
5782e192b24SSimon Glass 	switch (argv[1][0]) {
5792e192b24SSimon Glass 	case 'h':		/* header */
5802e192b24SSimon Glass 		pci_header_show(dev);
5812e192b24SSimon Glass 		break;
5822e192b24SSimon Glass 	case 'd':		/* display */
5832e192b24SSimon Glass 		return pci_cfg_display(dev, addr, size, value);
5842e192b24SSimon Glass 	case 'n':		/* next */
5852e192b24SSimon Glass 		if (argc < 4)
5862e192b24SSimon Glass 			goto usage;
5872e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 0);
5882e192b24SSimon Glass 		break;
5892e192b24SSimon Glass 	case 'm':		/* modify */
5902e192b24SSimon Glass 		if (argc < 4)
5912e192b24SSimon Glass 			goto usage;
5922e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 1);
5932e192b24SSimon Glass 		break;
5942e192b24SSimon Glass 	case 'w':		/* write */
5952e192b24SSimon Glass 		if (argc < 5)
5962e192b24SSimon Glass 			goto usage;
5972e192b24SSimon Glass 		ret = dm_pci_write_config(dev, addr, value, size);
5982e192b24SSimon Glass 		break;
599e5f96a87SYehuda Yitschak 	case 'b':		/* bars */
600e5f96a87SYehuda Yitschak 		return pci_bar_show(dev);
60121c9fbd8SShawn Lin 	case 'a':		/* AER */
60221c9fbd8SShawn Lin 		return pci_aer_dump(dev, bdf);
603e8697d50SShawn Lin 	case 'x':		/* retrain link */
604e8697d50SShawn Lin 		return pci_retrain_link(dev, bdf);
605*10dd02abSShawn Lin 	case 'f':		/* do FLR */
606*10dd02abSShawn Lin 		return pci_reset_function(dev, bdf);
6072e192b24SSimon Glass 	default:
6082e192b24SSimon Glass 		ret = CMD_RET_USAGE;
6092e192b24SSimon Glass 		break;
6102e192b24SSimon Glass 	}
6112e192b24SSimon Glass 
6122e192b24SSimon Glass 	return ret;
6132e192b24SSimon Glass  usage:
6142e192b24SSimon Glass 	return CMD_RET_USAGE;
6152e192b24SSimon Glass }
6162e192b24SSimon Glass 
6172e192b24SSimon Glass /***************************************************/
6182e192b24SSimon Glass 
6192e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP
6202e192b24SSimon Glass static char pci_help_text[] =
6218bb098e9SPali Rohár 	"[bus|*] [long]\n"
6222e192b24SSimon Glass 	"    - short or long list of PCI devices on bus 'bus'\n"
62321c9fbd8SShawn Lin 	"pci aer b.d.f \n"
62421c9fbd8SShawn Lin 	"    - Dump PCI AER info\n"
625e8697d50SShawn Lin 	"pci x b.d.f \n"
626e8697d50SShawn Lin 	"    - Retrain the link\n"
627*10dd02abSShawn Lin 	"pci f b.d.f \n"
628*10dd02abSShawn Lin 	"    - Function level reset\n"
6292e192b24SSimon Glass 	"pci enum\n"
630e578b92cSStephen Warren 	"    - Enumerate PCI buses\n"
6312e192b24SSimon Glass 	"pci header b.d.f\n"
6322e192b24SSimon Glass 	"    - show header of PCI device 'bus.device.function'\n"
633e5f96a87SYehuda Yitschak 	"pci bar b.d.f\n"
634e5f96a87SYehuda Yitschak 	"    - show BARs base and size for device b.d.f'\n"
6358bb098e9SPali Rohár 	"pci regions [bus|*]\n"
636b997a73eSSimon Glass 	"    - show PCI regions\n"
6372e192b24SSimon Glass 	"pci display[.b, .w, .l] b.d.f [address] [# of objects]\n"
6382e192b24SSimon Glass 	"    - display PCI configuration space (CFG)\n"
6392e192b24SSimon Glass 	"pci next[.b, .w, .l] b.d.f address\n"
6402e192b24SSimon Glass 	"    - modify, read and keep CFG address\n"
6412e192b24SSimon Glass 	"pci modify[.b, .w, .l] b.d.f address\n"
6422e192b24SSimon Glass 	"    -  modify, auto increment CFG address\n"
6432e192b24SSimon Glass 	"pci write[.b, .w, .l] b.d.f address value\n"
6442e192b24SSimon Glass 	"    - write to CFG address";
6452e192b24SSimon Glass #endif
6462e192b24SSimon Glass 
6472e192b24SSimon Glass U_BOOT_CMD(
6482e192b24SSimon Glass 	pci,	5,	1,	do_pci,
6492e192b24SSimon Glass 	"list and access PCI Configuration Space", pci_help_text
6502e192b24SSimon Glass );
651