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