xref: /rk3399_rockchip-uboot/cmd/pci.c (revision 3751f4dcdb135d4e1c1965191bb1dd6d39b1092e)
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 
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 
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 
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 
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);
74*3751f4dcSPali 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;
79*3751f4dcSPali Rohár 	} else if (header_type != PCI_HEADER_TYPE_NORMAL &&
80*3751f4dcSPali Rohár 		   header_type != PCI_HEADER_TYPE_BRIDGE) {
81*3751f4dcSPali Rohár 		printf("unknown header type\n");
82*3751f4dcSPali 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  */
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 
2422e192b24SSimon Glass 	switch (header_type & 0x03) {
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 
259efe4ea7cSVladimir Oltean static void pciinfo_header(int busnum, bool short_listing)
2602e192b24SSimon Glass {
2612e192b24SSimon Glass 	printf("Scanning PCI devices on bus %d\n", busnum);
2622e192b24SSimon Glass 
2632e192b24SSimon Glass 	if (short_listing) {
2642e192b24SSimon Glass 		printf("BusDevFun  VendorId   DeviceId   Device Class       Sub-Class\n");
2652e192b24SSimon Glass 		printf("_____________________________________________________________\n");
2662e192b24SSimon Glass 	}
2672e192b24SSimon Glass }
2682e192b24SSimon Glass 
2692e192b24SSimon Glass /**
2702e192b24SSimon Glass  * pci_header_show_brief() - Show the short-form PCI device header
2712e192b24SSimon Glass  *
2722e192b24SSimon Glass  * Reads and prints the header of the specified PCI device in short form.
2732e192b24SSimon Glass  *
2742e192b24SSimon Glass  * @dev: PCI device to show
2752e192b24SSimon Glass  */
2762e192b24SSimon Glass static void pci_header_show_brief(struct udevice *dev)
2772e192b24SSimon Glass {
2782e192b24SSimon Glass 	ulong vendor, device;
2792e192b24SSimon Glass 	ulong class, subclass;
2802e192b24SSimon Glass 
2812e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_VENDOR_ID, &vendor, PCI_SIZE_16);
2822e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_DEVICE_ID, &device, PCI_SIZE_16);
2832e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
2842e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_SUB_CODE, &subclass, PCI_SIZE_8);
2852e192b24SSimon Glass 
2862e192b24SSimon Glass 	printf("0x%.4lx     0x%.4lx     %-23s 0x%.2lx\n",
2872e192b24SSimon Glass 	       vendor, device,
2882e192b24SSimon Glass 	       pci_class_str(class), subclass);
2892e192b24SSimon Glass }
2902e192b24SSimon Glass 
2912e192b24SSimon Glass static void pciinfo(struct udevice *bus, bool short_listing)
2922e192b24SSimon Glass {
2932e192b24SSimon Glass 	struct udevice *dev;
2942e192b24SSimon Glass 
2952e192b24SSimon Glass 	pciinfo_header(bus->seq, short_listing);
2962e192b24SSimon Glass 
2972e192b24SSimon Glass 	for (device_find_first_child(bus, &dev);
2982e192b24SSimon Glass 	     dev;
2992e192b24SSimon Glass 	     device_find_next_child(&dev)) {
3002e192b24SSimon Glass 		struct pci_child_platdata *pplat;
3012e192b24SSimon Glass 
3022e192b24SSimon Glass 		pplat = dev_get_parent_platdata(dev);
3032e192b24SSimon Glass 		if (short_listing) {
3042e192b24SSimon Glass 			printf("%02x.%02x.%02x   ", bus->seq,
3052e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
3062e192b24SSimon Glass 			pci_header_show_brief(dev);
3072e192b24SSimon Glass 		} else {
3082e192b24SSimon Glass 			printf("\nFound PCI device %02x.%02x.%02x:\n", bus->seq,
3092e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
3102e192b24SSimon Glass 			pci_header_show(dev);
3112e192b24SSimon Glass 		}
3122e192b24SSimon Glass 	}
3132e192b24SSimon Glass }
3142e192b24SSimon Glass 
3152e192b24SSimon Glass /**
3162e192b24SSimon Glass  * get_pci_dev() - Convert the "bus.device.function" identifier into a number
3172e192b24SSimon Glass  *
3182e192b24SSimon Glass  * @name: Device string in the form "bus.device.function" where each is in hex
3192e192b24SSimon Glass  * @return encoded pci_dev_t or -1 if the string was invalid
3202e192b24SSimon Glass  */
3212e192b24SSimon Glass static pci_dev_t get_pci_dev(char *name)
3222e192b24SSimon Glass {
3232e192b24SSimon Glass 	char cnum[12];
3242e192b24SSimon Glass 	int len, i, iold, n;
3252e192b24SSimon Glass 	int bdfs[3] = {0,0,0};
3262e192b24SSimon Glass 
3272e192b24SSimon Glass 	len = strlen(name);
3282e192b24SSimon Glass 	if (len > 8)
3292e192b24SSimon Glass 		return -1;
3302e192b24SSimon Glass 	for (i = 0, iold = 0, n = 0; i < len; i++) {
3312e192b24SSimon Glass 		if (name[i] == '.') {
3322e192b24SSimon Glass 			memcpy(cnum, &name[iold], i - iold);
3332e192b24SSimon Glass 			cnum[i - iold] = '\0';
3342e192b24SSimon Glass 			bdfs[n++] = simple_strtoul(cnum, NULL, 16);
3352e192b24SSimon Glass 			iold = i + 1;
3362e192b24SSimon Glass 		}
3372e192b24SSimon Glass 	}
3382e192b24SSimon Glass 	strcpy(cnum, &name[iold]);
3392e192b24SSimon Glass 	if (n == 0)
3402e192b24SSimon Glass 		n = 1;
3412e192b24SSimon Glass 	bdfs[n] = simple_strtoul(cnum, NULL, 16);
3422e192b24SSimon Glass 
3432e192b24SSimon Glass 	return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]);
3442e192b24SSimon Glass }
3452e192b24SSimon Glass 
3462e192b24SSimon Glass static int pci_cfg_display(struct udevice *dev, ulong addr,
3472e192b24SSimon Glass 			   enum pci_size_t size, ulong length)
3482e192b24SSimon Glass {
3492e192b24SSimon Glass #define DISP_LINE_LEN	16
3502e192b24SSimon Glass 	ulong i, nbytes, linebytes;
3512e192b24SSimon Glass 	int byte_size;
3522e192b24SSimon Glass 	int rc = 0;
3532e192b24SSimon Glass 
3542e192b24SSimon Glass 	byte_size = pci_byte_size(size);
3552e192b24SSimon Glass 	if (length == 0)
3562e192b24SSimon Glass 		length = 0x40 / byte_size; /* Standard PCI config space */
3572e192b24SSimon Glass 
3582e192b24SSimon Glass 	/* Print the lines.
3592e192b24SSimon Glass 	 * once, and all accesses are with the specified bus width.
3602e192b24SSimon Glass 	 */
3612e192b24SSimon Glass 	nbytes = length * byte_size;
3622e192b24SSimon Glass 	do {
3632e192b24SSimon Glass 		printf("%08lx:", addr);
3642e192b24SSimon Glass 		linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
3652e192b24SSimon Glass 		for (i = 0; i < linebytes; i += byte_size) {
3662e192b24SSimon Glass 			unsigned long val;
3672e192b24SSimon Glass 
3682e192b24SSimon Glass 			dm_pci_read_config(dev, addr, &val, size);
3692e192b24SSimon Glass 			printf(" %0*lx", pci_field_width(size), val);
3702e192b24SSimon Glass 			addr += byte_size;
3712e192b24SSimon Glass 		}
3722e192b24SSimon Glass 		printf("\n");
3732e192b24SSimon Glass 		nbytes -= linebytes;
3742e192b24SSimon Glass 		if (ctrlc()) {
3752e192b24SSimon Glass 			rc = 1;
3762e192b24SSimon Glass 			break;
3772e192b24SSimon Glass 		}
3782e192b24SSimon Glass 	} while (nbytes > 0);
3792e192b24SSimon Glass 
3802e192b24SSimon Glass 	return (rc);
3812e192b24SSimon Glass }
3822e192b24SSimon Glass 
3832e192b24SSimon Glass static int pci_cfg_modify(struct udevice *dev, ulong addr, ulong size,
3842e192b24SSimon Glass 			  ulong value, int incrflag)
3852e192b24SSimon Glass {
3862e192b24SSimon Glass 	ulong	i;
3872e192b24SSimon Glass 	int	nbytes;
3882e192b24SSimon Glass 	ulong val;
3892e192b24SSimon Glass 
3902e192b24SSimon Glass 	/* Print the address, followed by value.  Then accept input for
3912e192b24SSimon Glass 	 * the next value.  A non-converted value exits.
3922e192b24SSimon Glass 	 */
3932e192b24SSimon Glass 	do {
3942e192b24SSimon Glass 		printf("%08lx:", addr);
3952e192b24SSimon Glass 		dm_pci_read_config(dev, addr, &val, size);
3962e192b24SSimon Glass 		printf(" %0*lx", pci_field_width(size), val);
3972e192b24SSimon Glass 
3982e192b24SSimon Glass 		nbytes = cli_readline(" ? ");
3992e192b24SSimon Glass 		if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
4002e192b24SSimon Glass 			/* <CR> pressed as only input, don't modify current
4012e192b24SSimon Glass 			 * location and move to next. "-" pressed will go back.
4022e192b24SSimon Glass 			 */
4032e192b24SSimon Glass 			if (incrflag)
4042e192b24SSimon Glass 				addr += nbytes ? -size : size;
4052e192b24SSimon Glass 			nbytes = 1;
4062e192b24SSimon Glass 			/* good enough to not time out */
4072e192b24SSimon Glass 			bootretry_reset_cmd_timeout();
4082e192b24SSimon Glass 		}
4092e192b24SSimon Glass #ifdef CONFIG_BOOT_RETRY_TIME
4102e192b24SSimon Glass 		else if (nbytes == -2) {
4112e192b24SSimon Glass 			break;	/* timed out, exit the command	*/
4122e192b24SSimon Glass 		}
4132e192b24SSimon Glass #endif
4142e192b24SSimon Glass 		else {
4152e192b24SSimon Glass 			char *endp;
4162e192b24SSimon Glass 			i = simple_strtoul(console_buffer, &endp, 16);
4172e192b24SSimon Glass 			nbytes = endp - console_buffer;
4182e192b24SSimon Glass 			if (nbytes) {
4192e192b24SSimon Glass 				/* good enough to not time out
4202e192b24SSimon Glass 				 */
4212e192b24SSimon Glass 				bootretry_reset_cmd_timeout();
4222e192b24SSimon Glass 				dm_pci_write_config(dev, addr, i, size);
4232e192b24SSimon Glass 				if (incrflag)
4242e192b24SSimon Glass 					addr += size;
4252e192b24SSimon Glass 			}
4262e192b24SSimon Glass 		}
4272e192b24SSimon Glass 	} while (nbytes);
4282e192b24SSimon Glass 
4292e192b24SSimon Glass 	return 0;
4302e192b24SSimon Glass }
4312e192b24SSimon Glass 
432b997a73eSSimon Glass static const struct pci_flag_info {
433b997a73eSSimon Glass 	uint flag;
434b997a73eSSimon Glass 	const char *name;
435b997a73eSSimon Glass } pci_flag_info[] = {
436b997a73eSSimon Glass 	{ PCI_REGION_IO, "io" },
437b997a73eSSimon Glass 	{ PCI_REGION_PREFETCH, "prefetch" },
438b997a73eSSimon Glass 	{ PCI_REGION_SYS_MEMORY, "sysmem" },
439b997a73eSSimon Glass 	{ PCI_REGION_RO, "readonly" },
440b997a73eSSimon Glass 	{ PCI_REGION_IO, "io" },
441b997a73eSSimon Glass };
442b997a73eSSimon Glass 
443b997a73eSSimon Glass static void pci_show_regions(struct udevice *bus)
444b997a73eSSimon Glass {
445b997a73eSSimon Glass 	struct pci_controller *hose = dev_get_uclass_priv(bus);
446b997a73eSSimon Glass 	const struct pci_region *reg;
447b997a73eSSimon Glass 	int i, j;
448b997a73eSSimon Glass 
449b997a73eSSimon Glass 	if (!hose) {
450b997a73eSSimon Glass 		printf("Bus '%s' is not a PCI controller\n", bus->name);
451b997a73eSSimon Glass 		return;
452b997a73eSSimon Glass 	}
453b997a73eSSimon Glass 
454002ab4c6SKunihiko Hayashi 	printf("#   %-18s %-18s %-18s  %s\n", "Bus start", "Phys start", "Size",
455b997a73eSSimon Glass 	       "Flags");
456b997a73eSSimon Glass 	for (i = 0, reg = hose->regions; i < hose->region_count; i++, reg++) {
457002ab4c6SKunihiko Hayashi 		printf("%d   %#018llx %#018llx %#018llx  ", i,
458b997a73eSSimon Glass 		       (unsigned long long)reg->bus_start,
459b997a73eSSimon Glass 		       (unsigned long long)reg->phys_start,
460b997a73eSSimon Glass 		       (unsigned long long)reg->size);
461b997a73eSSimon Glass 		if (!(reg->flags & PCI_REGION_TYPE))
462b997a73eSSimon Glass 			printf("mem ");
463b997a73eSSimon Glass 		for (j = 0; j < ARRAY_SIZE(pci_flag_info); j++) {
464b997a73eSSimon Glass 			if (reg->flags & pci_flag_info[j].flag)
465b997a73eSSimon Glass 				printf("%s ", pci_flag_info[j].name);
466b997a73eSSimon Glass 		}
467b997a73eSSimon Glass 		printf("\n");
468b997a73eSSimon Glass 	}
469b997a73eSSimon Glass }
470b997a73eSSimon Glass 
4712e192b24SSimon Glass /* PCI Configuration Space access commands
4722e192b24SSimon Glass  *
4732e192b24SSimon Glass  * Syntax:
4742e192b24SSimon Glass  *	pci display[.b, .w, .l] bus.device.function} [addr] [len]
4752e192b24SSimon Glass  *	pci next[.b, .w, .l] bus.device.function [addr]
4762e192b24SSimon Glass  *      pci modify[.b, .w, .l] bus.device.function [addr]
4772e192b24SSimon Glass  *      pci write[.b, .w, .l] bus.device.function addr value
4782e192b24SSimon Glass  */
4792e192b24SSimon Glass static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
4802e192b24SSimon Glass {
4812e192b24SSimon Glass 	ulong addr = 0, value = 0, cmd_size = 0;
4822e192b24SSimon Glass 	enum pci_size_t size = PCI_SIZE_32;
4832e192b24SSimon Glass 	struct udevice *dev, *bus;
4842e192b24SSimon Glass 	int busnum = 0;
4852e192b24SSimon Glass 	pci_dev_t bdf = 0;
4862e192b24SSimon Glass 	char cmd = 's';
4872e192b24SSimon Glass 	int ret = 0;
4882e192b24SSimon Glass 
4892e192b24SSimon Glass 	if (argc > 1)
4902e192b24SSimon Glass 		cmd = argv[1][0];
4912e192b24SSimon Glass 
4922e192b24SSimon Glass 	switch (cmd) {
4932e192b24SSimon Glass 	case 'd':		/* display */
4942e192b24SSimon Glass 	case 'n':		/* next */
4952e192b24SSimon Glass 	case 'm':		/* modify */
4962e192b24SSimon Glass 	case 'w':		/* write */
4972e192b24SSimon Glass 		/* Check for a size specification. */
4982e192b24SSimon Glass 		cmd_size = cmd_get_data_size(argv[1], 4);
4992e192b24SSimon Glass 		size = (cmd_size == 4) ? PCI_SIZE_32 : cmd_size - 1;
5002e192b24SSimon Glass 		if (argc > 3)
5012e192b24SSimon Glass 			addr = simple_strtoul(argv[3], NULL, 16);
5022e192b24SSimon Glass 		if (argc > 4)
5032e192b24SSimon Glass 			value = simple_strtoul(argv[4], NULL, 16);
5042e192b24SSimon Glass 	case 'h':		/* header */
505e5f96a87SYehuda Yitschak 	case 'b':		/* bars */
5062e192b24SSimon Glass 		if (argc < 3)
5072e192b24SSimon Glass 			goto usage;
5082e192b24SSimon Glass 		if ((bdf = get_pci_dev(argv[2])) == -1)
5092e192b24SSimon Glass 			return 1;
5102e192b24SSimon Glass 		break;
5112e192b24SSimon Glass 	case 'e':
512e578b92cSStephen Warren 		pci_init();
513e578b92cSStephen Warren 		return 0;
514b997a73eSSimon Glass 	case 'r': /* no break */
5152e192b24SSimon Glass 	default:		/* scan bus */
5162e192b24SSimon Glass 		value = 1; /* short listing */
5172e192b24SSimon Glass 		if (argc > 1) {
518b997a73eSSimon Glass 			if (cmd != 'r' && argv[argc-1][0] == 'l') {
5192e192b24SSimon Glass 				value = 0;
5202e192b24SSimon Glass 				argc--;
5212e192b24SSimon Glass 			}
5222e192b24SSimon Glass 			if (argc > 1)
5232e192b24SSimon Glass 				busnum = simple_strtoul(argv[1], NULL, 16);
5242e192b24SSimon Glass 		}
5252e192b24SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
5262e192b24SSimon Glass 		if (ret) {
5272e192b24SSimon Glass 			printf("No such bus\n");
5282e192b24SSimon Glass 			return CMD_RET_FAILURE;
5292e192b24SSimon Glass 		}
530b997a73eSSimon Glass 		if (cmd == 'r')
531b997a73eSSimon Glass 			pci_show_regions(bus);
532b997a73eSSimon Glass 		else
5332e192b24SSimon Glass 			pciinfo(bus, value);
5342e192b24SSimon Glass 		return 0;
5352e192b24SSimon Glass 	}
5362e192b24SSimon Glass 
5372e192b24SSimon Glass 	ret = dm_pci_bus_find_bdf(bdf, &dev);
5382e192b24SSimon Glass 	if (ret) {
5392e192b24SSimon Glass 		printf("No such device\n");
5402e192b24SSimon Glass 		return CMD_RET_FAILURE;
5412e192b24SSimon Glass 	}
5422e192b24SSimon Glass 
5432e192b24SSimon Glass 	switch (argv[1][0]) {
5442e192b24SSimon Glass 	case 'h':		/* header */
5452e192b24SSimon Glass 		pci_header_show(dev);
5462e192b24SSimon Glass 		break;
5472e192b24SSimon Glass 	case 'd':		/* display */
5482e192b24SSimon Glass 		return pci_cfg_display(dev, addr, size, value);
5492e192b24SSimon Glass 	case 'n':		/* next */
5502e192b24SSimon Glass 		if (argc < 4)
5512e192b24SSimon Glass 			goto usage;
5522e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 0);
5532e192b24SSimon Glass 		break;
5542e192b24SSimon Glass 	case 'm':		/* modify */
5552e192b24SSimon Glass 		if (argc < 4)
5562e192b24SSimon Glass 			goto usage;
5572e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 1);
5582e192b24SSimon Glass 		break;
5592e192b24SSimon Glass 	case 'w':		/* write */
5602e192b24SSimon Glass 		if (argc < 5)
5612e192b24SSimon Glass 			goto usage;
5622e192b24SSimon Glass 		ret = dm_pci_write_config(dev, addr, value, size);
5632e192b24SSimon Glass 		break;
564e5f96a87SYehuda Yitschak 	case 'b':		/* bars */
565e5f96a87SYehuda Yitschak 		return pci_bar_show(dev);
5662e192b24SSimon Glass 	default:
5672e192b24SSimon Glass 		ret = CMD_RET_USAGE;
5682e192b24SSimon Glass 		break;
5692e192b24SSimon Glass 	}
5702e192b24SSimon Glass 
5712e192b24SSimon Glass 	return ret;
5722e192b24SSimon Glass  usage:
5732e192b24SSimon Glass 	return CMD_RET_USAGE;
5742e192b24SSimon Glass }
5752e192b24SSimon Glass 
5762e192b24SSimon Glass /***************************************************/
5772e192b24SSimon Glass 
5782e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP
5792e192b24SSimon Glass static char pci_help_text[] =
5802e192b24SSimon Glass 	"[bus] [long]\n"
5812e192b24SSimon Glass 	"    - short or long list of PCI devices on bus 'bus'\n"
5822e192b24SSimon Glass 	"pci enum\n"
583e578b92cSStephen Warren 	"    - Enumerate PCI buses\n"
5842e192b24SSimon Glass 	"pci header b.d.f\n"
5852e192b24SSimon Glass 	"    - show header of PCI device 'bus.device.function'\n"
586e5f96a87SYehuda Yitschak 	"pci bar b.d.f\n"
587e5f96a87SYehuda Yitschak 	"    - show BARs base and size for device b.d.f'\n"
588b997a73eSSimon Glass 	"pci regions\n"
589b997a73eSSimon Glass 	"    - show PCI regions\n"
5902e192b24SSimon Glass 	"pci display[.b, .w, .l] b.d.f [address] [# of objects]\n"
5912e192b24SSimon Glass 	"    - display PCI configuration space (CFG)\n"
5922e192b24SSimon Glass 	"pci next[.b, .w, .l] b.d.f address\n"
5932e192b24SSimon Glass 	"    - modify, read and keep CFG address\n"
5942e192b24SSimon Glass 	"pci modify[.b, .w, .l] b.d.f address\n"
5952e192b24SSimon Glass 	"    -  modify, auto increment CFG address\n"
5962e192b24SSimon Glass 	"pci write[.b, .w, .l] b.d.f address value\n"
5972e192b24SSimon Glass 	"    - write to CFG address";
5982e192b24SSimon Glass #endif
5992e192b24SSimon Glass 
6002e192b24SSimon Glass U_BOOT_CMD(
6012e192b24SSimon Glass 	pci,	5,	1,	do_pci,
6022e192b24SSimon Glass 	"list and access PCI Configuration Space", pci_help_text
6032e192b24SSimon Glass );
604