xref: /rk3399_rockchip-uboot/cmd/pci.c (revision efe4ea7cc24665b65821fc603c505bdffc01c9ef)
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);
74e5f96a87SYehuda Yitschak 
75e5f96a87SYehuda Yitschak 	if (header_type == PCI_HEADER_TYPE_CARDBUS) {
76e5f96a87SYehuda Yitschak 		printf("CardBus doesn't support BARs\n");
77e5f96a87SYehuda Yitschak 		return -ENOSYS;
78e5f96a87SYehuda Yitschak 	}
79e5f96a87SYehuda Yitschak 
80e5f96a87SYehuda Yitschak 	bar_cnt = (header_type == PCI_HEADER_TYPE_NORMAL) ? 6 : 2;
81e5f96a87SYehuda Yitschak 
82e5f96a87SYehuda Yitschak 	printf("ID   Base                Size                Width  Type\n");
83e5f96a87SYehuda Yitschak 	printf("----------------------------------------------------------\n");
84e5f96a87SYehuda Yitschak 
85e5f96a87SYehuda Yitschak 	bar_id = 0;
86e5f96a87SYehuda Yitschak 	reg_addr = PCI_BASE_ADDRESS_0;
87e5f96a87SYehuda Yitschak 	while (bar_cnt) {
88e5f96a87SYehuda Yitschak 		dm_pci_read_config32(dev, reg_addr, &base_low);
89e5f96a87SYehuda Yitschak 		dm_pci_write_config32(dev, reg_addr, 0xffffffff);
90e5f96a87SYehuda Yitschak 		dm_pci_read_config32(dev, reg_addr, &size_low);
91e5f96a87SYehuda Yitschak 		dm_pci_write_config32(dev, reg_addr, base_low);
92e5f96a87SYehuda Yitschak 		reg_addr += 4;
93e5f96a87SYehuda Yitschak 
94e5f96a87SYehuda Yitschak 		base = base_low & ~0xf;
95e5f96a87SYehuda Yitschak 		size = size_low & ~0xf;
96e5f96a87SYehuda Yitschak 		base_high = 0x0;
97e5f96a87SYehuda Yitschak 		size_high = 0xffffffff;
98e5f96a87SYehuda Yitschak 		is_64 = 0;
99e5f96a87SYehuda Yitschak 		prefetchable = base_low & PCI_BASE_ADDRESS_MEM_PREFETCH;
100e5f96a87SYehuda Yitschak 		is_io = base_low & PCI_BASE_ADDRESS_SPACE_IO;
101e5f96a87SYehuda Yitschak 		mem_type = base_low & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
102e5f96a87SYehuda Yitschak 
103e5f96a87SYehuda Yitschak 		if (mem_type == PCI_BASE_ADDRESS_MEM_TYPE_64) {
104e5f96a87SYehuda Yitschak 			dm_pci_read_config32(dev, reg_addr, &base_high);
105e5f96a87SYehuda Yitschak 			dm_pci_write_config32(dev, reg_addr, 0xffffffff);
106e5f96a87SYehuda Yitschak 			dm_pci_read_config32(dev, reg_addr, &size_high);
107e5f96a87SYehuda Yitschak 			dm_pci_write_config32(dev, reg_addr, base_high);
108e5f96a87SYehuda Yitschak 			bar_cnt--;
109e5f96a87SYehuda Yitschak 			reg_addr += 4;
110e5f96a87SYehuda Yitschak 			is_64 = 1;
111e5f96a87SYehuda Yitschak 		}
112e5f96a87SYehuda Yitschak 
113e5f96a87SYehuda Yitschak 		base = base | ((u64)base_high << 32);
114e5f96a87SYehuda Yitschak 		size = size | ((u64)size_high << 32);
115e5f96a87SYehuda Yitschak 
116e5f96a87SYehuda Yitschak 		if ((!is_64 && size_low) || (is_64 && size)) {
117e5f96a87SYehuda Yitschak 			size = ~size + 1;
118002ab4c6SKunihiko Hayashi 			printf(" %d   %#018llx  %#018llx  %d     %s   %s\n",
11984d7f916SSimon Glass 			       bar_id, (unsigned long long)base,
12084d7f916SSimon Glass 			       (unsigned long long)size, is_64 ? 64 : 32,
121e5f96a87SYehuda Yitschak 			       is_io ? "I/O" : "MEM",
122e5f96a87SYehuda Yitschak 			       prefetchable ? "Prefetchable" : "");
123e5f96a87SYehuda Yitschak 		}
124e5f96a87SYehuda Yitschak 
125e5f96a87SYehuda Yitschak 		bar_id++;
126e5f96a87SYehuda Yitschak 		bar_cnt--;
127e5f96a87SYehuda Yitschak 	}
128e5f96a87SYehuda Yitschak 
129e5f96a87SYehuda Yitschak 	return 0;
130e5f96a87SYehuda Yitschak }
131e5f96a87SYehuda Yitschak 
1322e192b24SSimon Glass static struct pci_reg_info regs_start[] = {
1332e192b24SSimon Glass 	{ "vendor ID", PCI_SIZE_16, PCI_VENDOR_ID },
1342e192b24SSimon Glass 	{ "device ID", PCI_SIZE_16, PCI_DEVICE_ID },
1352e192b24SSimon Glass 	{ "command register ID", PCI_SIZE_16, PCI_COMMAND },
1362e192b24SSimon Glass 	{ "status register", PCI_SIZE_16, PCI_STATUS },
1372e192b24SSimon Glass 	{ "revision ID", PCI_SIZE_8, PCI_REVISION_ID },
1382e192b24SSimon Glass 	{},
1392e192b24SSimon Glass };
1402e192b24SSimon Glass 
1412e192b24SSimon Glass static struct pci_reg_info regs_rest[] = {
1422e192b24SSimon Glass 	{ "sub class code", PCI_SIZE_8, PCI_CLASS_SUB_CODE },
1432e192b24SSimon Glass 	{ "programming interface", PCI_SIZE_8, PCI_CLASS_PROG },
1442e192b24SSimon Glass 	{ "cache line", PCI_SIZE_8, PCI_CACHE_LINE_SIZE },
1452e192b24SSimon Glass 	{ "latency time", PCI_SIZE_8, PCI_LATENCY_TIMER },
1462e192b24SSimon Glass 	{ "header type", PCI_SIZE_8, PCI_HEADER_TYPE },
1472e192b24SSimon Glass 	{ "BIST", PCI_SIZE_8, PCI_BIST },
1482e192b24SSimon Glass 	{ "base address 0", PCI_SIZE_32, PCI_BASE_ADDRESS_0 },
1492e192b24SSimon Glass 	{},
1502e192b24SSimon Glass };
1512e192b24SSimon Glass 
1522e192b24SSimon Glass static struct pci_reg_info regs_normal[] = {
1532e192b24SSimon Glass 	{ "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
1542e192b24SSimon Glass 	{ "base address 2", PCI_SIZE_32, PCI_BASE_ADDRESS_2 },
1552e192b24SSimon Glass 	{ "base address 3", PCI_SIZE_32, PCI_BASE_ADDRESS_3 },
1562e192b24SSimon Glass 	{ "base address 4", PCI_SIZE_32, PCI_BASE_ADDRESS_4 },
1572e192b24SSimon Glass 	{ "base address 5", PCI_SIZE_32, PCI_BASE_ADDRESS_5 },
1582e192b24SSimon Glass 	{ "cardBus CIS pointer", PCI_SIZE_32, PCI_CARDBUS_CIS },
1592e192b24SSimon Glass 	{ "sub system vendor ID", PCI_SIZE_16, PCI_SUBSYSTEM_VENDOR_ID },
1602e192b24SSimon Glass 	{ "sub system ID", PCI_SIZE_16, PCI_SUBSYSTEM_ID },
1612e192b24SSimon Glass 	{ "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS },
1622e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
1632e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
1642e192b24SSimon Glass 	{ "min Grant", PCI_SIZE_8, PCI_MIN_GNT },
1652e192b24SSimon Glass 	{ "max Latency", PCI_SIZE_8, PCI_MAX_LAT },
1662e192b24SSimon Glass 	{},
1672e192b24SSimon Glass };
1682e192b24SSimon Glass 
1692e192b24SSimon Glass static struct pci_reg_info regs_bridge[] = {
1702e192b24SSimon Glass 	{ "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
1712e192b24SSimon Glass 	{ "primary bus number", PCI_SIZE_8, PCI_PRIMARY_BUS },
1722e192b24SSimon Glass 	{ "secondary bus number", PCI_SIZE_8, PCI_SECONDARY_BUS },
1732e192b24SSimon Glass 	{ "subordinate bus number", PCI_SIZE_8, PCI_SUBORDINATE_BUS },
1742e192b24SSimon Glass 	{ "secondary latency timer", PCI_SIZE_8, PCI_SEC_LATENCY_TIMER },
1752e192b24SSimon Glass 	{ "IO base", PCI_SIZE_8, PCI_IO_BASE },
1762e192b24SSimon Glass 	{ "IO limit", PCI_SIZE_8, PCI_IO_LIMIT },
1772e192b24SSimon Glass 	{ "secondary status", PCI_SIZE_16, PCI_SEC_STATUS },
1782e192b24SSimon Glass 	{ "memory base", PCI_SIZE_16, PCI_MEMORY_BASE },
1792e192b24SSimon Glass 	{ "memory limit", PCI_SIZE_16, PCI_MEMORY_LIMIT },
1802e192b24SSimon Glass 	{ "prefetch memory base", PCI_SIZE_16, PCI_PREF_MEMORY_BASE },
1812e192b24SSimon Glass 	{ "prefetch memory limit", PCI_SIZE_16, PCI_PREF_MEMORY_LIMIT },
1822e192b24SSimon Glass 	{ "prefetch memory base upper", PCI_SIZE_32, PCI_PREF_BASE_UPPER32 },
1832e192b24SSimon Glass 	{ "prefetch memory limit upper", PCI_SIZE_32, PCI_PREF_LIMIT_UPPER32 },
1842e192b24SSimon Glass 	{ "IO base upper 16 bits", PCI_SIZE_16, PCI_IO_BASE_UPPER16 },
1852e192b24SSimon Glass 	{ "IO limit upper 16 bits", PCI_SIZE_16, PCI_IO_LIMIT_UPPER16 },
1862e192b24SSimon Glass 	{ "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS1 },
1872e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
1882e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
1892e192b24SSimon Glass 	{ "bridge control", PCI_SIZE_16, PCI_BRIDGE_CONTROL },
1902e192b24SSimon Glass 	{},
1912e192b24SSimon Glass };
1922e192b24SSimon Glass 
1932e192b24SSimon Glass static struct pci_reg_info regs_cardbus[] = {
1942e192b24SSimon Glass 	{ "capabilities", PCI_SIZE_8, PCI_CB_CAPABILITY_LIST },
1952e192b24SSimon Glass 	{ "secondary status", PCI_SIZE_16, PCI_CB_SEC_STATUS },
1962e192b24SSimon Glass 	{ "primary bus number", PCI_SIZE_8, PCI_CB_PRIMARY_BUS },
1972e192b24SSimon Glass 	{ "CardBus number", PCI_SIZE_8, PCI_CB_CARD_BUS },
1982e192b24SSimon Glass 	{ "subordinate bus number", PCI_SIZE_8, PCI_CB_SUBORDINATE_BUS },
1992e192b24SSimon Glass 	{ "CardBus latency timer", PCI_SIZE_8, PCI_CB_LATENCY_TIMER },
2002e192b24SSimon Glass 	{ "CardBus memory base 0", PCI_SIZE_32, PCI_CB_MEMORY_BASE_0 },
2012e192b24SSimon Glass 	{ "CardBus memory limit 0", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_0 },
2022e192b24SSimon Glass 	{ "CardBus memory base 1", PCI_SIZE_32, PCI_CB_MEMORY_BASE_1 },
2032e192b24SSimon Glass 	{ "CardBus memory limit 1", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_1 },
2042e192b24SSimon Glass 	{ "CardBus IO base 0", PCI_SIZE_16, PCI_CB_IO_BASE_0 },
2052e192b24SSimon Glass 	{ "CardBus IO base high 0", PCI_SIZE_16, PCI_CB_IO_BASE_0_HI },
2062e192b24SSimon Glass 	{ "CardBus IO limit 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0 },
2072e192b24SSimon Glass 	{ "CardBus IO limit high 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0_HI },
2082e192b24SSimon Glass 	{ "CardBus IO base 1", PCI_SIZE_16, PCI_CB_IO_BASE_1 },
2092e192b24SSimon Glass 	{ "CardBus IO base high 1", PCI_SIZE_16, PCI_CB_IO_BASE_1_HI },
2102e192b24SSimon Glass 	{ "CardBus IO limit 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1 },
2112e192b24SSimon Glass 	{ "CardBus IO limit high 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1_HI },
2122e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
2132e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
2142e192b24SSimon Glass 	{ "bridge control", PCI_SIZE_16, PCI_CB_BRIDGE_CONTROL },
2152e192b24SSimon Glass 	{ "subvendor ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_VENDOR_ID },
2162e192b24SSimon Glass 	{ "subdevice ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_ID },
2172e192b24SSimon Glass 	{ "PC Card 16bit base address", PCI_SIZE_32, PCI_CB_LEGACY_MODE_BASE },
2182e192b24SSimon Glass 	{},
2192e192b24SSimon Glass };
2202e192b24SSimon Glass 
2212e192b24SSimon Glass /**
2222e192b24SSimon Glass  * pci_header_show() - Show the header of the specified PCI device.
2232e192b24SSimon Glass  *
2242e192b24SSimon Glass  * @dev: Bus+Device+Function number
2252e192b24SSimon Glass  */
226853d3013SVladimir Oltean static void pci_header_show(struct udevice *dev)
2272e192b24SSimon Glass {
2282e192b24SSimon Glass 	unsigned long class, header_type;
2292e192b24SSimon Glass 
2302e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
2312e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_HEADER_TYPE, &header_type, PCI_SIZE_8);
2322e192b24SSimon Glass 	pci_show_regs(dev, regs_start);
2332e192b24SSimon Glass 	printf("  class code =                  0x%.2x (%s)\n", (int)class,
2342e192b24SSimon Glass 	       pci_class_str(class));
2352e192b24SSimon Glass 	pci_show_regs(dev, regs_rest);
2362e192b24SSimon Glass 
2372e192b24SSimon Glass 	switch (header_type & 0x03) {
2382e192b24SSimon Glass 	case PCI_HEADER_TYPE_NORMAL:	/* "normal" PCI device */
2392e192b24SSimon Glass 		pci_show_regs(dev, regs_normal);
2402e192b24SSimon Glass 		break;
2412e192b24SSimon Glass 	case PCI_HEADER_TYPE_BRIDGE:	/* PCI-to-PCI bridge */
2422e192b24SSimon Glass 		pci_show_regs(dev, regs_bridge);
2432e192b24SSimon Glass 		break;
2442e192b24SSimon Glass 	case PCI_HEADER_TYPE_CARDBUS:	/* PCI-to-CardBus bridge */
2452e192b24SSimon Glass 		pci_show_regs(dev, regs_cardbus);
2462e192b24SSimon Glass 		break;
2472e192b24SSimon Glass 
2482e192b24SSimon Glass 	default:
2492e192b24SSimon Glass 		printf("unknown header\n");
2502e192b24SSimon Glass 		break;
2512e192b24SSimon Glass     }
2522e192b24SSimon Glass }
2532e192b24SSimon Glass 
254*efe4ea7cSVladimir Oltean static void pciinfo_header(int busnum, bool short_listing)
2552e192b24SSimon Glass {
2562e192b24SSimon Glass 	printf("Scanning PCI devices on bus %d\n", busnum);
2572e192b24SSimon Glass 
2582e192b24SSimon Glass 	if (short_listing) {
2592e192b24SSimon Glass 		printf("BusDevFun  VendorId   DeviceId   Device Class       Sub-Class\n");
2602e192b24SSimon Glass 		printf("_____________________________________________________________\n");
2612e192b24SSimon Glass 	}
2622e192b24SSimon Glass }
2632e192b24SSimon Glass 
2642e192b24SSimon Glass /**
2652e192b24SSimon Glass  * pci_header_show_brief() - Show the short-form PCI device header
2662e192b24SSimon Glass  *
2672e192b24SSimon Glass  * Reads and prints the header of the specified PCI device in short form.
2682e192b24SSimon Glass  *
2692e192b24SSimon Glass  * @dev: PCI device to show
2702e192b24SSimon Glass  */
2712e192b24SSimon Glass static void pci_header_show_brief(struct udevice *dev)
2722e192b24SSimon Glass {
2732e192b24SSimon Glass 	ulong vendor, device;
2742e192b24SSimon Glass 	ulong class, subclass;
2752e192b24SSimon Glass 
2762e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_VENDOR_ID, &vendor, PCI_SIZE_16);
2772e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_DEVICE_ID, &device, PCI_SIZE_16);
2782e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
2792e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_SUB_CODE, &subclass, PCI_SIZE_8);
2802e192b24SSimon Glass 
2812e192b24SSimon Glass 	printf("0x%.4lx     0x%.4lx     %-23s 0x%.2lx\n",
2822e192b24SSimon Glass 	       vendor, device,
2832e192b24SSimon Glass 	       pci_class_str(class), subclass);
2842e192b24SSimon Glass }
2852e192b24SSimon Glass 
2862e192b24SSimon Glass static void pciinfo(struct udevice *bus, bool short_listing)
2872e192b24SSimon Glass {
2882e192b24SSimon Glass 	struct udevice *dev;
2892e192b24SSimon Glass 
2902e192b24SSimon Glass 	pciinfo_header(bus->seq, short_listing);
2912e192b24SSimon Glass 
2922e192b24SSimon Glass 	for (device_find_first_child(bus, &dev);
2932e192b24SSimon Glass 	     dev;
2942e192b24SSimon Glass 	     device_find_next_child(&dev)) {
2952e192b24SSimon Glass 		struct pci_child_platdata *pplat;
2962e192b24SSimon Glass 
2972e192b24SSimon Glass 		pplat = dev_get_parent_platdata(dev);
2982e192b24SSimon Glass 		if (short_listing) {
2992e192b24SSimon Glass 			printf("%02x.%02x.%02x   ", bus->seq,
3002e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
3012e192b24SSimon Glass 			pci_header_show_brief(dev);
3022e192b24SSimon Glass 		} else {
3032e192b24SSimon Glass 			printf("\nFound PCI device %02x.%02x.%02x:\n", bus->seq,
3042e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
3052e192b24SSimon Glass 			pci_header_show(dev);
3062e192b24SSimon Glass 		}
3072e192b24SSimon Glass 	}
3082e192b24SSimon Glass }
3092e192b24SSimon Glass 
3102e192b24SSimon Glass /**
3112e192b24SSimon Glass  * get_pci_dev() - Convert the "bus.device.function" identifier into a number
3122e192b24SSimon Glass  *
3132e192b24SSimon Glass  * @name: Device string in the form "bus.device.function" where each is in hex
3142e192b24SSimon Glass  * @return encoded pci_dev_t or -1 if the string was invalid
3152e192b24SSimon Glass  */
3162e192b24SSimon Glass static pci_dev_t get_pci_dev(char *name)
3172e192b24SSimon Glass {
3182e192b24SSimon Glass 	char cnum[12];
3192e192b24SSimon Glass 	int len, i, iold, n;
3202e192b24SSimon Glass 	int bdfs[3] = {0,0,0};
3212e192b24SSimon Glass 
3222e192b24SSimon Glass 	len = strlen(name);
3232e192b24SSimon Glass 	if (len > 8)
3242e192b24SSimon Glass 		return -1;
3252e192b24SSimon Glass 	for (i = 0, iold = 0, n = 0; i < len; i++) {
3262e192b24SSimon Glass 		if (name[i] == '.') {
3272e192b24SSimon Glass 			memcpy(cnum, &name[iold], i - iold);
3282e192b24SSimon Glass 			cnum[i - iold] = '\0';
3292e192b24SSimon Glass 			bdfs[n++] = simple_strtoul(cnum, NULL, 16);
3302e192b24SSimon Glass 			iold = i + 1;
3312e192b24SSimon Glass 		}
3322e192b24SSimon Glass 	}
3332e192b24SSimon Glass 	strcpy(cnum, &name[iold]);
3342e192b24SSimon Glass 	if (n == 0)
3352e192b24SSimon Glass 		n = 1;
3362e192b24SSimon Glass 	bdfs[n] = simple_strtoul(cnum, NULL, 16);
3372e192b24SSimon Glass 
3382e192b24SSimon Glass 	return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]);
3392e192b24SSimon Glass }
3402e192b24SSimon Glass 
3412e192b24SSimon Glass static int pci_cfg_display(struct udevice *dev, ulong addr,
3422e192b24SSimon Glass 			   enum pci_size_t size, ulong length)
3432e192b24SSimon Glass {
3442e192b24SSimon Glass #define DISP_LINE_LEN	16
3452e192b24SSimon Glass 	ulong i, nbytes, linebytes;
3462e192b24SSimon Glass 	int byte_size;
3472e192b24SSimon Glass 	int rc = 0;
3482e192b24SSimon Glass 
3492e192b24SSimon Glass 	byte_size = pci_byte_size(size);
3502e192b24SSimon Glass 	if (length == 0)
3512e192b24SSimon Glass 		length = 0x40 / byte_size; /* Standard PCI config space */
3522e192b24SSimon Glass 
3532e192b24SSimon Glass 	/* Print the lines.
3542e192b24SSimon Glass 	 * once, and all accesses are with the specified bus width.
3552e192b24SSimon Glass 	 */
3562e192b24SSimon Glass 	nbytes = length * byte_size;
3572e192b24SSimon Glass 	do {
3582e192b24SSimon Glass 		printf("%08lx:", addr);
3592e192b24SSimon Glass 		linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
3602e192b24SSimon Glass 		for (i = 0; i < linebytes; i += byte_size) {
3612e192b24SSimon Glass 			unsigned long val;
3622e192b24SSimon Glass 
3632e192b24SSimon Glass 			dm_pci_read_config(dev, addr, &val, size);
3642e192b24SSimon Glass 			printf(" %0*lx", pci_field_width(size), val);
3652e192b24SSimon Glass 			addr += byte_size;
3662e192b24SSimon Glass 		}
3672e192b24SSimon Glass 		printf("\n");
3682e192b24SSimon Glass 		nbytes -= linebytes;
3692e192b24SSimon Glass 		if (ctrlc()) {
3702e192b24SSimon Glass 			rc = 1;
3712e192b24SSimon Glass 			break;
3722e192b24SSimon Glass 		}
3732e192b24SSimon Glass 	} while (nbytes > 0);
3742e192b24SSimon Glass 
3752e192b24SSimon Glass 	return (rc);
3762e192b24SSimon Glass }
3772e192b24SSimon Glass 
3782e192b24SSimon Glass static int pci_cfg_modify(struct udevice *dev, ulong addr, ulong size,
3792e192b24SSimon Glass 			  ulong value, int incrflag)
3802e192b24SSimon Glass {
3812e192b24SSimon Glass 	ulong	i;
3822e192b24SSimon Glass 	int	nbytes;
3832e192b24SSimon Glass 	ulong val;
3842e192b24SSimon Glass 
3852e192b24SSimon Glass 	/* Print the address, followed by value.  Then accept input for
3862e192b24SSimon Glass 	 * the next value.  A non-converted value exits.
3872e192b24SSimon Glass 	 */
3882e192b24SSimon Glass 	do {
3892e192b24SSimon Glass 		printf("%08lx:", addr);
3902e192b24SSimon Glass 		dm_pci_read_config(dev, addr, &val, size);
3912e192b24SSimon Glass 		printf(" %0*lx", pci_field_width(size), val);
3922e192b24SSimon Glass 
3932e192b24SSimon Glass 		nbytes = cli_readline(" ? ");
3942e192b24SSimon Glass 		if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
3952e192b24SSimon Glass 			/* <CR> pressed as only input, don't modify current
3962e192b24SSimon Glass 			 * location and move to next. "-" pressed will go back.
3972e192b24SSimon Glass 			 */
3982e192b24SSimon Glass 			if (incrflag)
3992e192b24SSimon Glass 				addr += nbytes ? -size : size;
4002e192b24SSimon Glass 			nbytes = 1;
4012e192b24SSimon Glass 			/* good enough to not time out */
4022e192b24SSimon Glass 			bootretry_reset_cmd_timeout();
4032e192b24SSimon Glass 		}
4042e192b24SSimon Glass #ifdef CONFIG_BOOT_RETRY_TIME
4052e192b24SSimon Glass 		else if (nbytes == -2) {
4062e192b24SSimon Glass 			break;	/* timed out, exit the command	*/
4072e192b24SSimon Glass 		}
4082e192b24SSimon Glass #endif
4092e192b24SSimon Glass 		else {
4102e192b24SSimon Glass 			char *endp;
4112e192b24SSimon Glass 			i = simple_strtoul(console_buffer, &endp, 16);
4122e192b24SSimon Glass 			nbytes = endp - console_buffer;
4132e192b24SSimon Glass 			if (nbytes) {
4142e192b24SSimon Glass 				/* good enough to not time out
4152e192b24SSimon Glass 				 */
4162e192b24SSimon Glass 				bootretry_reset_cmd_timeout();
4172e192b24SSimon Glass 				dm_pci_write_config(dev, addr, i, size);
4182e192b24SSimon Glass 				if (incrflag)
4192e192b24SSimon Glass 					addr += size;
4202e192b24SSimon Glass 			}
4212e192b24SSimon Glass 		}
4222e192b24SSimon Glass 	} while (nbytes);
4232e192b24SSimon Glass 
4242e192b24SSimon Glass 	return 0;
4252e192b24SSimon Glass }
4262e192b24SSimon Glass 
427b997a73eSSimon Glass static const struct pci_flag_info {
428b997a73eSSimon Glass 	uint flag;
429b997a73eSSimon Glass 	const char *name;
430b997a73eSSimon Glass } pci_flag_info[] = {
431b997a73eSSimon Glass 	{ PCI_REGION_IO, "io" },
432b997a73eSSimon Glass 	{ PCI_REGION_PREFETCH, "prefetch" },
433b997a73eSSimon Glass 	{ PCI_REGION_SYS_MEMORY, "sysmem" },
434b997a73eSSimon Glass 	{ PCI_REGION_RO, "readonly" },
435b997a73eSSimon Glass 	{ PCI_REGION_IO, "io" },
436b997a73eSSimon Glass };
437b997a73eSSimon Glass 
438b997a73eSSimon Glass static void pci_show_regions(struct udevice *bus)
439b997a73eSSimon Glass {
440b997a73eSSimon Glass 	struct pci_controller *hose = dev_get_uclass_priv(bus);
441b997a73eSSimon Glass 	const struct pci_region *reg;
442b997a73eSSimon Glass 	int i, j;
443b997a73eSSimon Glass 
444b997a73eSSimon Glass 	if (!hose) {
445b997a73eSSimon Glass 		printf("Bus '%s' is not a PCI controller\n", bus->name);
446b997a73eSSimon Glass 		return;
447b997a73eSSimon Glass 	}
448b997a73eSSimon Glass 
449002ab4c6SKunihiko Hayashi 	printf("#   %-18s %-18s %-18s  %s\n", "Bus start", "Phys start", "Size",
450b997a73eSSimon Glass 	       "Flags");
451b997a73eSSimon Glass 	for (i = 0, reg = hose->regions; i < hose->region_count; i++, reg++) {
452002ab4c6SKunihiko Hayashi 		printf("%d   %#018llx %#018llx %#018llx  ", i,
453b997a73eSSimon Glass 		       (unsigned long long)reg->bus_start,
454b997a73eSSimon Glass 		       (unsigned long long)reg->phys_start,
455b997a73eSSimon Glass 		       (unsigned long long)reg->size);
456b997a73eSSimon Glass 		if (!(reg->flags & PCI_REGION_TYPE))
457b997a73eSSimon Glass 			printf("mem ");
458b997a73eSSimon Glass 		for (j = 0; j < ARRAY_SIZE(pci_flag_info); j++) {
459b997a73eSSimon Glass 			if (reg->flags & pci_flag_info[j].flag)
460b997a73eSSimon Glass 				printf("%s ", pci_flag_info[j].name);
461b997a73eSSimon Glass 		}
462b997a73eSSimon Glass 		printf("\n");
463b997a73eSSimon Glass 	}
464b997a73eSSimon Glass }
465b997a73eSSimon Glass 
4662e192b24SSimon Glass /* PCI Configuration Space access commands
4672e192b24SSimon Glass  *
4682e192b24SSimon Glass  * Syntax:
4692e192b24SSimon Glass  *	pci display[.b, .w, .l] bus.device.function} [addr] [len]
4702e192b24SSimon Glass  *	pci next[.b, .w, .l] bus.device.function [addr]
4712e192b24SSimon Glass  *      pci modify[.b, .w, .l] bus.device.function [addr]
4722e192b24SSimon Glass  *      pci write[.b, .w, .l] bus.device.function addr value
4732e192b24SSimon Glass  */
4742e192b24SSimon Glass static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
4752e192b24SSimon Glass {
4762e192b24SSimon Glass 	ulong addr = 0, value = 0, cmd_size = 0;
4772e192b24SSimon Glass 	enum pci_size_t size = PCI_SIZE_32;
4782e192b24SSimon Glass 	struct udevice *dev, *bus;
4792e192b24SSimon Glass 	int busnum = 0;
4802e192b24SSimon Glass 	pci_dev_t bdf = 0;
4812e192b24SSimon Glass 	char cmd = 's';
4822e192b24SSimon Glass 	int ret = 0;
4832e192b24SSimon Glass 
4842e192b24SSimon Glass 	if (argc > 1)
4852e192b24SSimon Glass 		cmd = argv[1][0];
4862e192b24SSimon Glass 
4872e192b24SSimon Glass 	switch (cmd) {
4882e192b24SSimon Glass 	case 'd':		/* display */
4892e192b24SSimon Glass 	case 'n':		/* next */
4902e192b24SSimon Glass 	case 'm':		/* modify */
4912e192b24SSimon Glass 	case 'w':		/* write */
4922e192b24SSimon Glass 		/* Check for a size specification. */
4932e192b24SSimon Glass 		cmd_size = cmd_get_data_size(argv[1], 4);
4942e192b24SSimon Glass 		size = (cmd_size == 4) ? PCI_SIZE_32 : cmd_size - 1;
4952e192b24SSimon Glass 		if (argc > 3)
4962e192b24SSimon Glass 			addr = simple_strtoul(argv[3], NULL, 16);
4972e192b24SSimon Glass 		if (argc > 4)
4982e192b24SSimon Glass 			value = simple_strtoul(argv[4], NULL, 16);
4992e192b24SSimon Glass 	case 'h':		/* header */
500e5f96a87SYehuda Yitschak 	case 'b':		/* bars */
5012e192b24SSimon Glass 		if (argc < 3)
5022e192b24SSimon Glass 			goto usage;
5032e192b24SSimon Glass 		if ((bdf = get_pci_dev(argv[2])) == -1)
5042e192b24SSimon Glass 			return 1;
5052e192b24SSimon Glass 		break;
5062e192b24SSimon Glass 	case 'e':
507e578b92cSStephen Warren 		pci_init();
508e578b92cSStephen Warren 		return 0;
509b997a73eSSimon Glass 	case 'r': /* no break */
5102e192b24SSimon Glass 	default:		/* scan bus */
5112e192b24SSimon Glass 		value = 1; /* short listing */
5122e192b24SSimon Glass 		if (argc > 1) {
513b997a73eSSimon Glass 			if (cmd != 'r' && argv[argc-1][0] == 'l') {
5142e192b24SSimon Glass 				value = 0;
5152e192b24SSimon Glass 				argc--;
5162e192b24SSimon Glass 			}
5172e192b24SSimon Glass 			if (argc > 1)
5182e192b24SSimon Glass 				busnum = simple_strtoul(argv[1], NULL, 16);
5192e192b24SSimon Glass 		}
5202e192b24SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
5212e192b24SSimon Glass 		if (ret) {
5222e192b24SSimon Glass 			printf("No such bus\n");
5232e192b24SSimon Glass 			return CMD_RET_FAILURE;
5242e192b24SSimon Glass 		}
525b997a73eSSimon Glass 		if (cmd == 'r')
526b997a73eSSimon Glass 			pci_show_regions(bus);
527b997a73eSSimon Glass 		else
5282e192b24SSimon Glass 			pciinfo(bus, value);
5292e192b24SSimon Glass 		return 0;
5302e192b24SSimon Glass 	}
5312e192b24SSimon Glass 
5322e192b24SSimon Glass 	ret = dm_pci_bus_find_bdf(bdf, &dev);
5332e192b24SSimon Glass 	if (ret) {
5342e192b24SSimon Glass 		printf("No such device\n");
5352e192b24SSimon Glass 		return CMD_RET_FAILURE;
5362e192b24SSimon Glass 	}
5372e192b24SSimon Glass 
5382e192b24SSimon Glass 	switch (argv[1][0]) {
5392e192b24SSimon Glass 	case 'h':		/* header */
5402e192b24SSimon Glass 		pci_header_show(dev);
5412e192b24SSimon Glass 		break;
5422e192b24SSimon Glass 	case 'd':		/* display */
5432e192b24SSimon Glass 		return pci_cfg_display(dev, addr, size, value);
5442e192b24SSimon Glass 	case 'n':		/* next */
5452e192b24SSimon Glass 		if (argc < 4)
5462e192b24SSimon Glass 			goto usage;
5472e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 0);
5482e192b24SSimon Glass 		break;
5492e192b24SSimon Glass 	case 'm':		/* modify */
5502e192b24SSimon Glass 		if (argc < 4)
5512e192b24SSimon Glass 			goto usage;
5522e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 1);
5532e192b24SSimon Glass 		break;
5542e192b24SSimon Glass 	case 'w':		/* write */
5552e192b24SSimon Glass 		if (argc < 5)
5562e192b24SSimon Glass 			goto usage;
5572e192b24SSimon Glass 		ret = dm_pci_write_config(dev, addr, value, size);
5582e192b24SSimon Glass 		break;
559e5f96a87SYehuda Yitschak 	case 'b':		/* bars */
560e5f96a87SYehuda Yitschak 		return pci_bar_show(dev);
5612e192b24SSimon Glass 	default:
5622e192b24SSimon Glass 		ret = CMD_RET_USAGE;
5632e192b24SSimon Glass 		break;
5642e192b24SSimon Glass 	}
5652e192b24SSimon Glass 
5662e192b24SSimon Glass 	return ret;
5672e192b24SSimon Glass  usage:
5682e192b24SSimon Glass 	return CMD_RET_USAGE;
5692e192b24SSimon Glass }
5702e192b24SSimon Glass 
5712e192b24SSimon Glass /***************************************************/
5722e192b24SSimon Glass 
5732e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP
5742e192b24SSimon Glass static char pci_help_text[] =
5752e192b24SSimon Glass 	"[bus] [long]\n"
5762e192b24SSimon Glass 	"    - short or long list of PCI devices on bus 'bus'\n"
5772e192b24SSimon Glass 	"pci enum\n"
578e578b92cSStephen Warren 	"    - Enumerate PCI buses\n"
5792e192b24SSimon Glass 	"pci header b.d.f\n"
5802e192b24SSimon Glass 	"    - show header of PCI device 'bus.device.function'\n"
581e5f96a87SYehuda Yitschak 	"pci bar b.d.f\n"
582e5f96a87SYehuda Yitschak 	"    - show BARs base and size for device b.d.f'\n"
583b997a73eSSimon Glass 	"pci regions\n"
584b997a73eSSimon Glass 	"    - show PCI regions\n"
5852e192b24SSimon Glass 	"pci display[.b, .w, .l] b.d.f [address] [# of objects]\n"
5862e192b24SSimon Glass 	"    - display PCI configuration space (CFG)\n"
5872e192b24SSimon Glass 	"pci next[.b, .w, .l] b.d.f address\n"
5882e192b24SSimon Glass 	"    - modify, read and keep CFG address\n"
5892e192b24SSimon Glass 	"pci modify[.b, .w, .l] b.d.f address\n"
5902e192b24SSimon Glass 	"    -  modify, auto increment CFG address\n"
5912e192b24SSimon Glass 	"pci write[.b, .w, .l] b.d.f address value\n"
5922e192b24SSimon Glass 	"    - write to CFG address";
5932e192b24SSimon Glass #endif
5942e192b24SSimon Glass 
5952e192b24SSimon Glass U_BOOT_CMD(
5962e192b24SSimon Glass 	pci,	5,	1,	do_pci,
5972e192b24SSimon Glass 	"list and access PCI Configuration Space", pci_help_text
5982e192b24SSimon Glass );
599