xref: /rk3399_rockchip-uboot/cmd/pci.c (revision 2e192b245ed36a63bab0ef576999a95e23f60ecd)
1*2e192b24SSimon Glass /*
2*2e192b24SSimon Glass  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
3*2e192b24SSimon Glass  * Andreas Heppel <aheppel@sysgo.de>
4*2e192b24SSimon Glass  *
5*2e192b24SSimon Glass  * (C) Copyright 2002
6*2e192b24SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7*2e192b24SSimon Glass  * Wolfgang Grandegger, DENX Software Engineering, wg@denx.de.
8*2e192b24SSimon Glass  *
9*2e192b24SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
10*2e192b24SSimon Glass  */
11*2e192b24SSimon Glass 
12*2e192b24SSimon Glass /*
13*2e192b24SSimon Glass  * PCI routines
14*2e192b24SSimon Glass  */
15*2e192b24SSimon Glass 
16*2e192b24SSimon Glass #include <common.h>
17*2e192b24SSimon Glass #include <bootretry.h>
18*2e192b24SSimon Glass #include <cli.h>
19*2e192b24SSimon Glass #include <command.h>
20*2e192b24SSimon Glass #include <console.h>
21*2e192b24SSimon Glass #include <dm.h>
22*2e192b24SSimon Glass #include <asm/processor.h>
23*2e192b24SSimon Glass #include <asm/io.h>
24*2e192b24SSimon Glass #include <pci.h>
25*2e192b24SSimon Glass 
26*2e192b24SSimon Glass struct pci_reg_info {
27*2e192b24SSimon Glass 	const char *name;
28*2e192b24SSimon Glass 	enum pci_size_t size;
29*2e192b24SSimon Glass 	u8 offset;
30*2e192b24SSimon Glass };
31*2e192b24SSimon Glass 
32*2e192b24SSimon Glass static int pci_byte_size(enum pci_size_t size)
33*2e192b24SSimon Glass {
34*2e192b24SSimon Glass 	switch (size) {
35*2e192b24SSimon Glass 	case PCI_SIZE_8:
36*2e192b24SSimon Glass 		return 1;
37*2e192b24SSimon Glass 	case PCI_SIZE_16:
38*2e192b24SSimon Glass 		return 2;
39*2e192b24SSimon Glass 	case PCI_SIZE_32:
40*2e192b24SSimon Glass 	default:
41*2e192b24SSimon Glass 		return 4;
42*2e192b24SSimon Glass 	}
43*2e192b24SSimon Glass }
44*2e192b24SSimon Glass 
45*2e192b24SSimon Glass static int pci_field_width(enum pci_size_t size)
46*2e192b24SSimon Glass {
47*2e192b24SSimon Glass 	return pci_byte_size(size) * 2;
48*2e192b24SSimon Glass }
49*2e192b24SSimon Glass 
50*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
51*2e192b24SSimon Glass static void pci_show_regs(struct udevice *dev, struct pci_reg_info *regs)
52*2e192b24SSimon Glass {
53*2e192b24SSimon Glass 	for (; regs->name; regs++) {
54*2e192b24SSimon Glass 		unsigned long val;
55*2e192b24SSimon Glass 
56*2e192b24SSimon Glass 		dm_pci_read_config(dev, regs->offset, &val, regs->size);
57*2e192b24SSimon Glass 		printf("  %s =%*s%#.*lx\n", regs->name,
58*2e192b24SSimon Glass 		       (int)(28 - strlen(regs->name)), "",
59*2e192b24SSimon Glass 		       pci_field_width(regs->size), val);
60*2e192b24SSimon Glass 	}
61*2e192b24SSimon Glass }
62*2e192b24SSimon Glass #else
63*2e192b24SSimon Glass static unsigned long pci_read_config(pci_dev_t dev, int offset,
64*2e192b24SSimon Glass 				     enum pci_size_t size)
65*2e192b24SSimon Glass {
66*2e192b24SSimon Glass 	u32 val32;
67*2e192b24SSimon Glass 	u16 val16;
68*2e192b24SSimon Glass 	u8 val8;
69*2e192b24SSimon Glass 
70*2e192b24SSimon Glass 	switch (size) {
71*2e192b24SSimon Glass 	case PCI_SIZE_8:
72*2e192b24SSimon Glass 		pci_read_config_byte(dev, offset, &val8);
73*2e192b24SSimon Glass 		return val8;
74*2e192b24SSimon Glass 	case PCI_SIZE_16:
75*2e192b24SSimon Glass 		pci_read_config_word(dev, offset, &val16);
76*2e192b24SSimon Glass 		return val16;
77*2e192b24SSimon Glass 	case PCI_SIZE_32:
78*2e192b24SSimon Glass 	default:
79*2e192b24SSimon Glass 		pci_read_config_dword(dev, offset, &val32);
80*2e192b24SSimon Glass 		return val32;
81*2e192b24SSimon Glass 	}
82*2e192b24SSimon Glass }
83*2e192b24SSimon Glass 
84*2e192b24SSimon Glass static void pci_show_regs(pci_dev_t dev, struct pci_reg_info *regs)
85*2e192b24SSimon Glass {
86*2e192b24SSimon Glass 	for (; regs->name; regs++) {
87*2e192b24SSimon Glass 		printf("  %s =%*s%#.*lx\n", regs->name,
88*2e192b24SSimon Glass 		       (int)(28 - strlen(regs->name)), "",
89*2e192b24SSimon Glass 		       pci_field_width(regs->size),
90*2e192b24SSimon Glass 		       pci_read_config(dev, regs->offset, regs->size));
91*2e192b24SSimon Glass 	}
92*2e192b24SSimon Glass }
93*2e192b24SSimon Glass #endif
94*2e192b24SSimon Glass 
95*2e192b24SSimon Glass static struct pci_reg_info regs_start[] = {
96*2e192b24SSimon Glass 	{ "vendor ID", PCI_SIZE_16, PCI_VENDOR_ID },
97*2e192b24SSimon Glass 	{ "device ID", PCI_SIZE_16, PCI_DEVICE_ID },
98*2e192b24SSimon Glass 	{ "command register ID", PCI_SIZE_16, PCI_COMMAND },
99*2e192b24SSimon Glass 	{ "status register", PCI_SIZE_16, PCI_STATUS },
100*2e192b24SSimon Glass 	{ "revision ID", PCI_SIZE_8, PCI_REVISION_ID },
101*2e192b24SSimon Glass 	{},
102*2e192b24SSimon Glass };
103*2e192b24SSimon Glass 
104*2e192b24SSimon Glass static struct pci_reg_info regs_rest[] = {
105*2e192b24SSimon Glass 	{ "sub class code", PCI_SIZE_8, PCI_CLASS_SUB_CODE },
106*2e192b24SSimon Glass 	{ "programming interface", PCI_SIZE_8, PCI_CLASS_PROG },
107*2e192b24SSimon Glass 	{ "cache line", PCI_SIZE_8, PCI_CACHE_LINE_SIZE },
108*2e192b24SSimon Glass 	{ "latency time", PCI_SIZE_8, PCI_LATENCY_TIMER },
109*2e192b24SSimon Glass 	{ "header type", PCI_SIZE_8, PCI_HEADER_TYPE },
110*2e192b24SSimon Glass 	{ "BIST", PCI_SIZE_8, PCI_BIST },
111*2e192b24SSimon Glass 	{ "base address 0", PCI_SIZE_32, PCI_BASE_ADDRESS_0 },
112*2e192b24SSimon Glass 	{},
113*2e192b24SSimon Glass };
114*2e192b24SSimon Glass 
115*2e192b24SSimon Glass static struct pci_reg_info regs_normal[] = {
116*2e192b24SSimon Glass 	{ "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
117*2e192b24SSimon Glass 	{ "base address 2", PCI_SIZE_32, PCI_BASE_ADDRESS_2 },
118*2e192b24SSimon Glass 	{ "base address 3", PCI_SIZE_32, PCI_BASE_ADDRESS_3 },
119*2e192b24SSimon Glass 	{ "base address 4", PCI_SIZE_32, PCI_BASE_ADDRESS_4 },
120*2e192b24SSimon Glass 	{ "base address 5", PCI_SIZE_32, PCI_BASE_ADDRESS_5 },
121*2e192b24SSimon Glass 	{ "cardBus CIS pointer", PCI_SIZE_32, PCI_CARDBUS_CIS },
122*2e192b24SSimon Glass 	{ "sub system vendor ID", PCI_SIZE_16, PCI_SUBSYSTEM_VENDOR_ID },
123*2e192b24SSimon Glass 	{ "sub system ID", PCI_SIZE_16, PCI_SUBSYSTEM_ID },
124*2e192b24SSimon Glass 	{ "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS },
125*2e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
126*2e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
127*2e192b24SSimon Glass 	{ "min Grant", PCI_SIZE_8, PCI_MIN_GNT },
128*2e192b24SSimon Glass 	{ "max Latency", PCI_SIZE_8, PCI_MAX_LAT },
129*2e192b24SSimon Glass 	{},
130*2e192b24SSimon Glass };
131*2e192b24SSimon Glass 
132*2e192b24SSimon Glass static struct pci_reg_info regs_bridge[] = {
133*2e192b24SSimon Glass 	{ "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
134*2e192b24SSimon Glass 	{ "primary bus number", PCI_SIZE_8, PCI_PRIMARY_BUS },
135*2e192b24SSimon Glass 	{ "secondary bus number", PCI_SIZE_8, PCI_SECONDARY_BUS },
136*2e192b24SSimon Glass 	{ "subordinate bus number", PCI_SIZE_8, PCI_SUBORDINATE_BUS },
137*2e192b24SSimon Glass 	{ "secondary latency timer", PCI_SIZE_8, PCI_SEC_LATENCY_TIMER },
138*2e192b24SSimon Glass 	{ "IO base", PCI_SIZE_8, PCI_IO_BASE },
139*2e192b24SSimon Glass 	{ "IO limit", PCI_SIZE_8, PCI_IO_LIMIT },
140*2e192b24SSimon Glass 	{ "secondary status", PCI_SIZE_16, PCI_SEC_STATUS },
141*2e192b24SSimon Glass 	{ "memory base", PCI_SIZE_16, PCI_MEMORY_BASE },
142*2e192b24SSimon Glass 	{ "memory limit", PCI_SIZE_16, PCI_MEMORY_LIMIT },
143*2e192b24SSimon Glass 	{ "prefetch memory base", PCI_SIZE_16, PCI_PREF_MEMORY_BASE },
144*2e192b24SSimon Glass 	{ "prefetch memory limit", PCI_SIZE_16, PCI_PREF_MEMORY_LIMIT },
145*2e192b24SSimon Glass 	{ "prefetch memory base upper", PCI_SIZE_32, PCI_PREF_BASE_UPPER32 },
146*2e192b24SSimon Glass 	{ "prefetch memory limit upper", PCI_SIZE_32, PCI_PREF_LIMIT_UPPER32 },
147*2e192b24SSimon Glass 	{ "IO base upper 16 bits", PCI_SIZE_16, PCI_IO_BASE_UPPER16 },
148*2e192b24SSimon Glass 	{ "IO limit upper 16 bits", PCI_SIZE_16, PCI_IO_LIMIT_UPPER16 },
149*2e192b24SSimon Glass 	{ "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS1 },
150*2e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
151*2e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
152*2e192b24SSimon Glass 	{ "bridge control", PCI_SIZE_16, PCI_BRIDGE_CONTROL },
153*2e192b24SSimon Glass 	{},
154*2e192b24SSimon Glass };
155*2e192b24SSimon Glass 
156*2e192b24SSimon Glass static struct pci_reg_info regs_cardbus[] = {
157*2e192b24SSimon Glass 	{ "capabilities", PCI_SIZE_8, PCI_CB_CAPABILITY_LIST },
158*2e192b24SSimon Glass 	{ "secondary status", PCI_SIZE_16, PCI_CB_SEC_STATUS },
159*2e192b24SSimon Glass 	{ "primary bus number", PCI_SIZE_8, PCI_CB_PRIMARY_BUS },
160*2e192b24SSimon Glass 	{ "CardBus number", PCI_SIZE_8, PCI_CB_CARD_BUS },
161*2e192b24SSimon Glass 	{ "subordinate bus number", PCI_SIZE_8, PCI_CB_SUBORDINATE_BUS },
162*2e192b24SSimon Glass 	{ "CardBus latency timer", PCI_SIZE_8, PCI_CB_LATENCY_TIMER },
163*2e192b24SSimon Glass 	{ "CardBus memory base 0", PCI_SIZE_32, PCI_CB_MEMORY_BASE_0 },
164*2e192b24SSimon Glass 	{ "CardBus memory limit 0", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_0 },
165*2e192b24SSimon Glass 	{ "CardBus memory base 1", PCI_SIZE_32, PCI_CB_MEMORY_BASE_1 },
166*2e192b24SSimon Glass 	{ "CardBus memory limit 1", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_1 },
167*2e192b24SSimon Glass 	{ "CardBus IO base 0", PCI_SIZE_16, PCI_CB_IO_BASE_0 },
168*2e192b24SSimon Glass 	{ "CardBus IO base high 0", PCI_SIZE_16, PCI_CB_IO_BASE_0_HI },
169*2e192b24SSimon Glass 	{ "CardBus IO limit 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0 },
170*2e192b24SSimon Glass 	{ "CardBus IO limit high 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0_HI },
171*2e192b24SSimon Glass 	{ "CardBus IO base 1", PCI_SIZE_16, PCI_CB_IO_BASE_1 },
172*2e192b24SSimon Glass 	{ "CardBus IO base high 1", PCI_SIZE_16, PCI_CB_IO_BASE_1_HI },
173*2e192b24SSimon Glass 	{ "CardBus IO limit 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1 },
174*2e192b24SSimon Glass 	{ "CardBus IO limit high 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1_HI },
175*2e192b24SSimon Glass 	{ "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
176*2e192b24SSimon Glass 	{ "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
177*2e192b24SSimon Glass 	{ "bridge control", PCI_SIZE_16, PCI_CB_BRIDGE_CONTROL },
178*2e192b24SSimon Glass 	{ "subvendor ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_VENDOR_ID },
179*2e192b24SSimon Glass 	{ "subdevice ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_ID },
180*2e192b24SSimon Glass 	{ "PC Card 16bit base address", PCI_SIZE_32, PCI_CB_LEGACY_MODE_BASE },
181*2e192b24SSimon Glass 	{},
182*2e192b24SSimon Glass };
183*2e192b24SSimon Glass 
184*2e192b24SSimon Glass /**
185*2e192b24SSimon Glass  * pci_header_show() - Show the header of the specified PCI device.
186*2e192b24SSimon Glass  *
187*2e192b24SSimon Glass  * @dev: Bus+Device+Function number
188*2e192b24SSimon Glass  */
189*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
190*2e192b24SSimon Glass void pci_header_show(struct udevice *dev)
191*2e192b24SSimon Glass #else
192*2e192b24SSimon Glass void pci_header_show(pci_dev_t dev)
193*2e192b24SSimon Glass #endif
194*2e192b24SSimon Glass {
195*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
196*2e192b24SSimon Glass 	unsigned long class, header_type;
197*2e192b24SSimon Glass 
198*2e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
199*2e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_HEADER_TYPE, &header_type, PCI_SIZE_8);
200*2e192b24SSimon Glass #else
201*2e192b24SSimon Glass 	u8 class, header_type;
202*2e192b24SSimon Glass 
203*2e192b24SSimon Glass 	pci_read_config_byte(dev, PCI_CLASS_CODE, &class);
204*2e192b24SSimon Glass 	pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type);
205*2e192b24SSimon Glass #endif
206*2e192b24SSimon Glass 	pci_show_regs(dev, regs_start);
207*2e192b24SSimon Glass 	printf("  class code =                  0x%.2x (%s)\n", (int)class,
208*2e192b24SSimon Glass 	       pci_class_str(class));
209*2e192b24SSimon Glass 	pci_show_regs(dev, regs_rest);
210*2e192b24SSimon Glass 
211*2e192b24SSimon Glass 	switch (header_type & 0x03) {
212*2e192b24SSimon Glass 	case PCI_HEADER_TYPE_NORMAL:	/* "normal" PCI device */
213*2e192b24SSimon Glass 		pci_show_regs(dev, regs_normal);
214*2e192b24SSimon Glass 		break;
215*2e192b24SSimon Glass 	case PCI_HEADER_TYPE_BRIDGE:	/* PCI-to-PCI bridge */
216*2e192b24SSimon Glass 		pci_show_regs(dev, regs_bridge);
217*2e192b24SSimon Glass 		break;
218*2e192b24SSimon Glass 	case PCI_HEADER_TYPE_CARDBUS:	/* PCI-to-CardBus bridge */
219*2e192b24SSimon Glass 		pci_show_regs(dev, regs_cardbus);
220*2e192b24SSimon Glass 		break;
221*2e192b24SSimon Glass 
222*2e192b24SSimon Glass 	default:
223*2e192b24SSimon Glass 		printf("unknown header\n");
224*2e192b24SSimon Glass 		break;
225*2e192b24SSimon Glass     }
226*2e192b24SSimon Glass }
227*2e192b24SSimon Glass 
228*2e192b24SSimon Glass void pciinfo_header(int busnum, bool short_listing)
229*2e192b24SSimon Glass {
230*2e192b24SSimon Glass 	printf("Scanning PCI devices on bus %d\n", busnum);
231*2e192b24SSimon Glass 
232*2e192b24SSimon Glass 	if (short_listing) {
233*2e192b24SSimon Glass 		printf("BusDevFun  VendorId   DeviceId   Device Class       Sub-Class\n");
234*2e192b24SSimon Glass 		printf("_____________________________________________________________\n");
235*2e192b24SSimon Glass 	}
236*2e192b24SSimon Glass }
237*2e192b24SSimon Glass 
238*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
239*2e192b24SSimon Glass /**
240*2e192b24SSimon Glass  * pci_header_show_brief() - Show the short-form PCI device header
241*2e192b24SSimon Glass  *
242*2e192b24SSimon Glass  * Reads and prints the header of the specified PCI device in short form.
243*2e192b24SSimon Glass  *
244*2e192b24SSimon Glass  * @dev: PCI device to show
245*2e192b24SSimon Glass  */
246*2e192b24SSimon Glass static void pci_header_show_brief(struct udevice *dev)
247*2e192b24SSimon Glass {
248*2e192b24SSimon Glass 	ulong vendor, device;
249*2e192b24SSimon Glass 	ulong class, subclass;
250*2e192b24SSimon Glass 
251*2e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_VENDOR_ID, &vendor, PCI_SIZE_16);
252*2e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_DEVICE_ID, &device, PCI_SIZE_16);
253*2e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
254*2e192b24SSimon Glass 	dm_pci_read_config(dev, PCI_CLASS_SUB_CODE, &subclass, PCI_SIZE_8);
255*2e192b24SSimon Glass 
256*2e192b24SSimon Glass 	printf("0x%.4lx     0x%.4lx     %-23s 0x%.2lx\n",
257*2e192b24SSimon Glass 	       vendor, device,
258*2e192b24SSimon Glass 	       pci_class_str(class), subclass);
259*2e192b24SSimon Glass }
260*2e192b24SSimon Glass 
261*2e192b24SSimon Glass static void pciinfo(struct udevice *bus, bool short_listing)
262*2e192b24SSimon Glass {
263*2e192b24SSimon Glass 	struct udevice *dev;
264*2e192b24SSimon Glass 
265*2e192b24SSimon Glass 	pciinfo_header(bus->seq, short_listing);
266*2e192b24SSimon Glass 
267*2e192b24SSimon Glass 	for (device_find_first_child(bus, &dev);
268*2e192b24SSimon Glass 	     dev;
269*2e192b24SSimon Glass 	     device_find_next_child(&dev)) {
270*2e192b24SSimon Glass 		struct pci_child_platdata *pplat;
271*2e192b24SSimon Glass 
272*2e192b24SSimon Glass 		pplat = dev_get_parent_platdata(dev);
273*2e192b24SSimon Glass 		if (short_listing) {
274*2e192b24SSimon Glass 			printf("%02x.%02x.%02x   ", bus->seq,
275*2e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
276*2e192b24SSimon Glass 			pci_header_show_brief(dev);
277*2e192b24SSimon Glass 		} else {
278*2e192b24SSimon Glass 			printf("\nFound PCI device %02x.%02x.%02x:\n", bus->seq,
279*2e192b24SSimon Glass 			       PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
280*2e192b24SSimon Glass 			pci_header_show(dev);
281*2e192b24SSimon Glass 		}
282*2e192b24SSimon Glass 	}
283*2e192b24SSimon Glass }
284*2e192b24SSimon Glass 
285*2e192b24SSimon Glass #else
286*2e192b24SSimon Glass 
287*2e192b24SSimon Glass /**
288*2e192b24SSimon Glass  * pci_header_show_brief() - Show the short-form PCI device header
289*2e192b24SSimon Glass  *
290*2e192b24SSimon Glass  * Reads and prints the header of the specified PCI device in short form.
291*2e192b24SSimon Glass  *
292*2e192b24SSimon Glass  * @dev: Bus+Device+Function number
293*2e192b24SSimon Glass  */
294*2e192b24SSimon Glass void pci_header_show_brief(pci_dev_t dev)
295*2e192b24SSimon Glass {
296*2e192b24SSimon Glass 	u16 vendor, device;
297*2e192b24SSimon Glass 	u8 class, subclass;
298*2e192b24SSimon Glass 
299*2e192b24SSimon Glass 	pci_read_config_word(dev, PCI_VENDOR_ID, &vendor);
300*2e192b24SSimon Glass 	pci_read_config_word(dev, PCI_DEVICE_ID, &device);
301*2e192b24SSimon Glass 	pci_read_config_byte(dev, PCI_CLASS_CODE, &class);
302*2e192b24SSimon Glass 	pci_read_config_byte(dev, PCI_CLASS_SUB_CODE, &subclass);
303*2e192b24SSimon Glass 
304*2e192b24SSimon Glass 	printf("0x%.4x     0x%.4x     %-23s 0x%.2x\n",
305*2e192b24SSimon Glass 	       vendor, device,
306*2e192b24SSimon Glass 	       pci_class_str(class), subclass);
307*2e192b24SSimon Glass }
308*2e192b24SSimon Glass 
309*2e192b24SSimon Glass /**
310*2e192b24SSimon Glass  * pciinfo() - Show a list of devices on the PCI bus
311*2e192b24SSimon Glass  *
312*2e192b24SSimon Glass  * Show information about devices on PCI bus. Depending on @short_pci_listing
313*2e192b24SSimon Glass  * the output will be more or less exhaustive.
314*2e192b24SSimon Glass  *
315*2e192b24SSimon Glass  * @bus_num: The number of the bus to be scanned
316*2e192b24SSimon Glass  * @short_pci_listing: true to use short form, showing only a brief header
317*2e192b24SSimon Glass  * for each device
318*2e192b24SSimon Glass  */
319*2e192b24SSimon Glass void pciinfo(int bus_num, int short_pci_listing)
320*2e192b24SSimon Glass {
321*2e192b24SSimon Glass 	struct pci_controller *hose = pci_bus_to_hose(bus_num);
322*2e192b24SSimon Glass 	int device;
323*2e192b24SSimon Glass 	int function;
324*2e192b24SSimon Glass 	unsigned char header_type;
325*2e192b24SSimon Glass 	unsigned short vendor_id;
326*2e192b24SSimon Glass 	pci_dev_t dev;
327*2e192b24SSimon Glass 	int ret;
328*2e192b24SSimon Glass 
329*2e192b24SSimon Glass 	if (!hose)
330*2e192b24SSimon Glass 		return;
331*2e192b24SSimon Glass 
332*2e192b24SSimon Glass 	pciinfo_header(bus_num, short_pci_listing);
333*2e192b24SSimon Glass 
334*2e192b24SSimon Glass 	for (device = 0; device < PCI_MAX_PCI_DEVICES; device++) {
335*2e192b24SSimon Glass 		header_type = 0;
336*2e192b24SSimon Glass 		vendor_id = 0;
337*2e192b24SSimon Glass 		for (function = 0; function < PCI_MAX_PCI_FUNCTIONS;
338*2e192b24SSimon Glass 		     function++) {
339*2e192b24SSimon Glass 			/*
340*2e192b24SSimon Glass 			 * If this is not a multi-function device, we skip
341*2e192b24SSimon Glass 			 * the rest.
342*2e192b24SSimon Glass 			 */
343*2e192b24SSimon Glass 			if (function && !(header_type & 0x80))
344*2e192b24SSimon Glass 				break;
345*2e192b24SSimon Glass 
346*2e192b24SSimon Glass 			dev = PCI_BDF(bus_num, device, function);
347*2e192b24SSimon Glass 
348*2e192b24SSimon Glass 			if (pci_skip_dev(hose, dev))
349*2e192b24SSimon Glass 				continue;
350*2e192b24SSimon Glass 
351*2e192b24SSimon Glass 			ret = pci_read_config_word(dev, PCI_VENDOR_ID,
352*2e192b24SSimon Glass 						   &vendor_id);
353*2e192b24SSimon Glass 			if (ret)
354*2e192b24SSimon Glass 				goto error;
355*2e192b24SSimon Glass 			if ((vendor_id == 0xFFFF) || (vendor_id == 0x0000))
356*2e192b24SSimon Glass 				continue;
357*2e192b24SSimon Glass 
358*2e192b24SSimon Glass 			if (!function) {
359*2e192b24SSimon Glass 				pci_read_config_byte(dev, PCI_HEADER_TYPE,
360*2e192b24SSimon Glass 						     &header_type);
361*2e192b24SSimon Glass 			}
362*2e192b24SSimon Glass 
363*2e192b24SSimon Glass 			if (short_pci_listing) {
364*2e192b24SSimon Glass 				printf("%02x.%02x.%02x   ", bus_num, device,
365*2e192b24SSimon Glass 				       function);
366*2e192b24SSimon Glass 				pci_header_show_brief(dev);
367*2e192b24SSimon Glass 			} else {
368*2e192b24SSimon Glass 				printf("\nFound PCI device %02x.%02x.%02x:\n",
369*2e192b24SSimon Glass 				       bus_num, device, function);
370*2e192b24SSimon Glass 				pci_header_show(dev);
371*2e192b24SSimon Glass 			}
372*2e192b24SSimon Glass 		}
373*2e192b24SSimon Glass 	}
374*2e192b24SSimon Glass 
375*2e192b24SSimon Glass 	return;
376*2e192b24SSimon Glass error:
377*2e192b24SSimon Glass 	printf("Cannot read bus configuration: %d\n", ret);
378*2e192b24SSimon Glass }
379*2e192b24SSimon Glass #endif
380*2e192b24SSimon Glass 
381*2e192b24SSimon Glass /**
382*2e192b24SSimon Glass  * get_pci_dev() - Convert the "bus.device.function" identifier into a number
383*2e192b24SSimon Glass  *
384*2e192b24SSimon Glass  * @name: Device string in the form "bus.device.function" where each is in hex
385*2e192b24SSimon Glass  * @return encoded pci_dev_t or -1 if the string was invalid
386*2e192b24SSimon Glass  */
387*2e192b24SSimon Glass static pci_dev_t get_pci_dev(char *name)
388*2e192b24SSimon Glass {
389*2e192b24SSimon Glass 	char cnum[12];
390*2e192b24SSimon Glass 	int len, i, iold, n;
391*2e192b24SSimon Glass 	int bdfs[3] = {0,0,0};
392*2e192b24SSimon Glass 
393*2e192b24SSimon Glass 	len = strlen(name);
394*2e192b24SSimon Glass 	if (len > 8)
395*2e192b24SSimon Glass 		return -1;
396*2e192b24SSimon Glass 	for (i = 0, iold = 0, n = 0; i < len; i++) {
397*2e192b24SSimon Glass 		if (name[i] == '.') {
398*2e192b24SSimon Glass 			memcpy(cnum, &name[iold], i - iold);
399*2e192b24SSimon Glass 			cnum[i - iold] = '\0';
400*2e192b24SSimon Glass 			bdfs[n++] = simple_strtoul(cnum, NULL, 16);
401*2e192b24SSimon Glass 			iold = i + 1;
402*2e192b24SSimon Glass 		}
403*2e192b24SSimon Glass 	}
404*2e192b24SSimon Glass 	strcpy(cnum, &name[iold]);
405*2e192b24SSimon Glass 	if (n == 0)
406*2e192b24SSimon Glass 		n = 1;
407*2e192b24SSimon Glass 	bdfs[n] = simple_strtoul(cnum, NULL, 16);
408*2e192b24SSimon Glass 
409*2e192b24SSimon Glass 	return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]);
410*2e192b24SSimon Glass }
411*2e192b24SSimon Glass 
412*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
413*2e192b24SSimon Glass static int pci_cfg_display(struct udevice *dev, ulong addr,
414*2e192b24SSimon Glass 			   enum pci_size_t size, ulong length)
415*2e192b24SSimon Glass #else
416*2e192b24SSimon Glass static int pci_cfg_display(pci_dev_t bdf, ulong addr, enum pci_size_t size,
417*2e192b24SSimon Glass 			   ulong length)
418*2e192b24SSimon Glass #endif
419*2e192b24SSimon Glass {
420*2e192b24SSimon Glass #define DISP_LINE_LEN	16
421*2e192b24SSimon Glass 	ulong i, nbytes, linebytes;
422*2e192b24SSimon Glass 	int byte_size;
423*2e192b24SSimon Glass 	int rc = 0;
424*2e192b24SSimon Glass 
425*2e192b24SSimon Glass 	byte_size = pci_byte_size(size);
426*2e192b24SSimon Glass 	if (length == 0)
427*2e192b24SSimon Glass 		length = 0x40 / byte_size; /* Standard PCI config space */
428*2e192b24SSimon Glass 
429*2e192b24SSimon Glass 	/* Print the lines.
430*2e192b24SSimon Glass 	 * once, and all accesses are with the specified bus width.
431*2e192b24SSimon Glass 	 */
432*2e192b24SSimon Glass 	nbytes = length * byte_size;
433*2e192b24SSimon Glass 	do {
434*2e192b24SSimon Glass 		printf("%08lx:", addr);
435*2e192b24SSimon Glass 		linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
436*2e192b24SSimon Glass 		for (i = 0; i < linebytes; i += byte_size) {
437*2e192b24SSimon Glass 			unsigned long val;
438*2e192b24SSimon Glass 
439*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
440*2e192b24SSimon Glass 			dm_pci_read_config(dev, addr, &val, size);
441*2e192b24SSimon Glass #else
442*2e192b24SSimon Glass 			val = pci_read_config(bdf, addr, size);
443*2e192b24SSimon Glass #endif
444*2e192b24SSimon Glass 			printf(" %0*lx", pci_field_width(size), val);
445*2e192b24SSimon Glass 			addr += byte_size;
446*2e192b24SSimon Glass 		}
447*2e192b24SSimon Glass 		printf("\n");
448*2e192b24SSimon Glass 		nbytes -= linebytes;
449*2e192b24SSimon Glass 		if (ctrlc()) {
450*2e192b24SSimon Glass 			rc = 1;
451*2e192b24SSimon Glass 			break;
452*2e192b24SSimon Glass 		}
453*2e192b24SSimon Glass 	} while (nbytes > 0);
454*2e192b24SSimon Glass 
455*2e192b24SSimon Glass 	return (rc);
456*2e192b24SSimon Glass }
457*2e192b24SSimon Glass 
458*2e192b24SSimon Glass #ifndef CONFIG_DM_PCI
459*2e192b24SSimon Glass static int pci_cfg_write (pci_dev_t bdf, ulong addr, ulong size, ulong value)
460*2e192b24SSimon Glass {
461*2e192b24SSimon Glass 	if (size == 4) {
462*2e192b24SSimon Glass 		pci_write_config_dword(bdf, addr, value);
463*2e192b24SSimon Glass 	}
464*2e192b24SSimon Glass 	else if (size == 2) {
465*2e192b24SSimon Glass 		ushort val = value & 0xffff;
466*2e192b24SSimon Glass 		pci_write_config_word(bdf, addr, val);
467*2e192b24SSimon Glass 	}
468*2e192b24SSimon Glass 	else {
469*2e192b24SSimon Glass 		u_char val = value & 0xff;
470*2e192b24SSimon Glass 		pci_write_config_byte(bdf, addr, val);
471*2e192b24SSimon Glass 	}
472*2e192b24SSimon Glass 	return 0;
473*2e192b24SSimon Glass }
474*2e192b24SSimon Glass #endif
475*2e192b24SSimon Glass 
476*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
477*2e192b24SSimon Glass static int pci_cfg_modify(struct udevice *dev, ulong addr, ulong size,
478*2e192b24SSimon Glass 			  ulong value, int incrflag)
479*2e192b24SSimon Glass #else
480*2e192b24SSimon Glass static int pci_cfg_modify(pci_dev_t bdf, ulong addr, ulong size, ulong value,
481*2e192b24SSimon Glass 			  int incrflag)
482*2e192b24SSimon Glass #endif
483*2e192b24SSimon Glass {
484*2e192b24SSimon Glass 	ulong	i;
485*2e192b24SSimon Glass 	int	nbytes;
486*2e192b24SSimon Glass 	ulong val;
487*2e192b24SSimon Glass 
488*2e192b24SSimon Glass 	/* Print the address, followed by value.  Then accept input for
489*2e192b24SSimon Glass 	 * the next value.  A non-converted value exits.
490*2e192b24SSimon Glass 	 */
491*2e192b24SSimon Glass 	do {
492*2e192b24SSimon Glass 		printf("%08lx:", addr);
493*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
494*2e192b24SSimon Glass 		dm_pci_read_config(dev, addr, &val, size);
495*2e192b24SSimon Glass #else
496*2e192b24SSimon Glass 		val = pci_read_config(bdf, addr, size);
497*2e192b24SSimon Glass #endif
498*2e192b24SSimon Glass 		printf(" %0*lx", pci_field_width(size), val);
499*2e192b24SSimon Glass 
500*2e192b24SSimon Glass 		nbytes = cli_readline(" ? ");
501*2e192b24SSimon Glass 		if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
502*2e192b24SSimon Glass 			/* <CR> pressed as only input, don't modify current
503*2e192b24SSimon Glass 			 * location and move to next. "-" pressed will go back.
504*2e192b24SSimon Glass 			 */
505*2e192b24SSimon Glass 			if (incrflag)
506*2e192b24SSimon Glass 				addr += nbytes ? -size : size;
507*2e192b24SSimon Glass 			nbytes = 1;
508*2e192b24SSimon Glass 			/* good enough to not time out */
509*2e192b24SSimon Glass 			bootretry_reset_cmd_timeout();
510*2e192b24SSimon Glass 		}
511*2e192b24SSimon Glass #ifdef CONFIG_BOOT_RETRY_TIME
512*2e192b24SSimon Glass 		else if (nbytes == -2) {
513*2e192b24SSimon Glass 			break;	/* timed out, exit the command	*/
514*2e192b24SSimon Glass 		}
515*2e192b24SSimon Glass #endif
516*2e192b24SSimon Glass 		else {
517*2e192b24SSimon Glass 			char *endp;
518*2e192b24SSimon Glass 			i = simple_strtoul(console_buffer, &endp, 16);
519*2e192b24SSimon Glass 			nbytes = endp - console_buffer;
520*2e192b24SSimon Glass 			if (nbytes) {
521*2e192b24SSimon Glass 				/* good enough to not time out
522*2e192b24SSimon Glass 				 */
523*2e192b24SSimon Glass 				bootretry_reset_cmd_timeout();
524*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
525*2e192b24SSimon Glass 				dm_pci_write_config(dev, addr, i, size);
526*2e192b24SSimon Glass #else
527*2e192b24SSimon Glass 				pci_cfg_write(bdf, addr, size, i);
528*2e192b24SSimon Glass #endif
529*2e192b24SSimon Glass 				if (incrflag)
530*2e192b24SSimon Glass 					addr += size;
531*2e192b24SSimon Glass 			}
532*2e192b24SSimon Glass 		}
533*2e192b24SSimon Glass 	} while (nbytes);
534*2e192b24SSimon Glass 
535*2e192b24SSimon Glass 	return 0;
536*2e192b24SSimon Glass }
537*2e192b24SSimon Glass 
538*2e192b24SSimon Glass /* PCI Configuration Space access commands
539*2e192b24SSimon Glass  *
540*2e192b24SSimon Glass  * Syntax:
541*2e192b24SSimon Glass  *	pci display[.b, .w, .l] bus.device.function} [addr] [len]
542*2e192b24SSimon Glass  *	pci next[.b, .w, .l] bus.device.function [addr]
543*2e192b24SSimon Glass  *      pci modify[.b, .w, .l] bus.device.function [addr]
544*2e192b24SSimon Glass  *      pci write[.b, .w, .l] bus.device.function addr value
545*2e192b24SSimon Glass  */
546*2e192b24SSimon Glass static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
547*2e192b24SSimon Glass {
548*2e192b24SSimon Glass 	ulong addr = 0, value = 0, cmd_size = 0;
549*2e192b24SSimon Glass 	enum pci_size_t size = PCI_SIZE_32;
550*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
551*2e192b24SSimon Glass 	struct udevice *dev, *bus;
552*2e192b24SSimon Glass #else
553*2e192b24SSimon Glass 	pci_dev_t dev;
554*2e192b24SSimon Glass #endif
555*2e192b24SSimon Glass 	int busnum = 0;
556*2e192b24SSimon Glass 	pci_dev_t bdf = 0;
557*2e192b24SSimon Glass 	char cmd = 's';
558*2e192b24SSimon Glass 	int ret = 0;
559*2e192b24SSimon Glass 
560*2e192b24SSimon Glass 	if (argc > 1)
561*2e192b24SSimon Glass 		cmd = argv[1][0];
562*2e192b24SSimon Glass 
563*2e192b24SSimon Glass 	switch (cmd) {
564*2e192b24SSimon Glass 	case 'd':		/* display */
565*2e192b24SSimon Glass 	case 'n':		/* next */
566*2e192b24SSimon Glass 	case 'm':		/* modify */
567*2e192b24SSimon Glass 	case 'w':		/* write */
568*2e192b24SSimon Glass 		/* Check for a size specification. */
569*2e192b24SSimon Glass 		cmd_size = cmd_get_data_size(argv[1], 4);
570*2e192b24SSimon Glass 		size = (cmd_size == 4) ? PCI_SIZE_32 : cmd_size - 1;
571*2e192b24SSimon Glass 		if (argc > 3)
572*2e192b24SSimon Glass 			addr = simple_strtoul(argv[3], NULL, 16);
573*2e192b24SSimon Glass 		if (argc > 4)
574*2e192b24SSimon Glass 			value = simple_strtoul(argv[4], NULL, 16);
575*2e192b24SSimon Glass 	case 'h':		/* header */
576*2e192b24SSimon Glass 		if (argc < 3)
577*2e192b24SSimon Glass 			goto usage;
578*2e192b24SSimon Glass 		if ((bdf = get_pci_dev(argv[2])) == -1)
579*2e192b24SSimon Glass 			return 1;
580*2e192b24SSimon Glass 		break;
581*2e192b24SSimon Glass #ifdef CONFIG_CMD_PCI_ENUM
582*2e192b24SSimon Glass 	case 'e':
583*2e192b24SSimon Glass 		break;
584*2e192b24SSimon Glass #endif
585*2e192b24SSimon Glass 	default:		/* scan bus */
586*2e192b24SSimon Glass 		value = 1; /* short listing */
587*2e192b24SSimon Glass 		if (argc > 1) {
588*2e192b24SSimon Glass 			if (argv[argc-1][0] == 'l') {
589*2e192b24SSimon Glass 				value = 0;
590*2e192b24SSimon Glass 				argc--;
591*2e192b24SSimon Glass 			}
592*2e192b24SSimon Glass 			if (argc > 1)
593*2e192b24SSimon Glass 				busnum = simple_strtoul(argv[1], NULL, 16);
594*2e192b24SSimon Glass 		}
595*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
596*2e192b24SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
597*2e192b24SSimon Glass 		if (ret) {
598*2e192b24SSimon Glass 			printf("No such bus\n");
599*2e192b24SSimon Glass 			return CMD_RET_FAILURE;
600*2e192b24SSimon Glass 		}
601*2e192b24SSimon Glass 		pciinfo(bus, value);
602*2e192b24SSimon Glass #else
603*2e192b24SSimon Glass 		pciinfo(busnum, value);
604*2e192b24SSimon Glass #endif
605*2e192b24SSimon Glass 		return 0;
606*2e192b24SSimon Glass 	}
607*2e192b24SSimon Glass 
608*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
609*2e192b24SSimon Glass 	ret = dm_pci_bus_find_bdf(bdf, &dev);
610*2e192b24SSimon Glass 	if (ret) {
611*2e192b24SSimon Glass 		printf("No such device\n");
612*2e192b24SSimon Glass 		return CMD_RET_FAILURE;
613*2e192b24SSimon Glass 	}
614*2e192b24SSimon Glass #else
615*2e192b24SSimon Glass 	dev = bdf;
616*2e192b24SSimon Glass #endif
617*2e192b24SSimon Glass 
618*2e192b24SSimon Glass 	switch (argv[1][0]) {
619*2e192b24SSimon Glass 	case 'h':		/* header */
620*2e192b24SSimon Glass 		pci_header_show(dev);
621*2e192b24SSimon Glass 		break;
622*2e192b24SSimon Glass 	case 'd':		/* display */
623*2e192b24SSimon Glass 		return pci_cfg_display(dev, addr, size, value);
624*2e192b24SSimon Glass #ifdef CONFIG_CMD_PCI_ENUM
625*2e192b24SSimon Glass 	case 'e':
626*2e192b24SSimon Glass # ifdef CONFIG_DM_PCI
627*2e192b24SSimon Glass 		printf("This command is not yet supported with driver model\n");
628*2e192b24SSimon Glass # else
629*2e192b24SSimon Glass 		pci_init();
630*2e192b24SSimon Glass # endif
631*2e192b24SSimon Glass 		break;
632*2e192b24SSimon Glass #endif
633*2e192b24SSimon Glass 	case 'n':		/* next */
634*2e192b24SSimon Glass 		if (argc < 4)
635*2e192b24SSimon Glass 			goto usage;
636*2e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 0);
637*2e192b24SSimon Glass 		break;
638*2e192b24SSimon Glass 	case 'm':		/* modify */
639*2e192b24SSimon Glass 		if (argc < 4)
640*2e192b24SSimon Glass 			goto usage;
641*2e192b24SSimon Glass 		ret = pci_cfg_modify(dev, addr, size, value, 1);
642*2e192b24SSimon Glass 		break;
643*2e192b24SSimon Glass 	case 'w':		/* write */
644*2e192b24SSimon Glass 		if (argc < 5)
645*2e192b24SSimon Glass 			goto usage;
646*2e192b24SSimon Glass #ifdef CONFIG_DM_PCI
647*2e192b24SSimon Glass 		ret = dm_pci_write_config(dev, addr, value, size);
648*2e192b24SSimon Glass #else
649*2e192b24SSimon Glass 		ret = pci_cfg_write(dev, addr, size, value);
650*2e192b24SSimon Glass #endif
651*2e192b24SSimon Glass 		break;
652*2e192b24SSimon Glass 	default:
653*2e192b24SSimon Glass 		ret = CMD_RET_USAGE;
654*2e192b24SSimon Glass 		break;
655*2e192b24SSimon Glass 	}
656*2e192b24SSimon Glass 
657*2e192b24SSimon Glass 	return ret;
658*2e192b24SSimon Glass  usage:
659*2e192b24SSimon Glass 	return CMD_RET_USAGE;
660*2e192b24SSimon Glass }
661*2e192b24SSimon Glass 
662*2e192b24SSimon Glass /***************************************************/
663*2e192b24SSimon Glass 
664*2e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP
665*2e192b24SSimon Glass static char pci_help_text[] =
666*2e192b24SSimon Glass 	"[bus] [long]\n"
667*2e192b24SSimon Glass 	"    - short or long list of PCI devices on bus 'bus'\n"
668*2e192b24SSimon Glass #ifdef CONFIG_CMD_PCI_ENUM
669*2e192b24SSimon Glass 	"pci enum\n"
670*2e192b24SSimon Glass 	"    - re-enumerate PCI buses\n"
671*2e192b24SSimon Glass #endif
672*2e192b24SSimon Glass 	"pci header b.d.f\n"
673*2e192b24SSimon Glass 	"    - show header of PCI device 'bus.device.function'\n"
674*2e192b24SSimon Glass 	"pci display[.b, .w, .l] b.d.f [address] [# of objects]\n"
675*2e192b24SSimon Glass 	"    - display PCI configuration space (CFG)\n"
676*2e192b24SSimon Glass 	"pci next[.b, .w, .l] b.d.f address\n"
677*2e192b24SSimon Glass 	"    - modify, read and keep CFG address\n"
678*2e192b24SSimon Glass 	"pci modify[.b, .w, .l] b.d.f address\n"
679*2e192b24SSimon Glass 	"    -  modify, auto increment CFG address\n"
680*2e192b24SSimon Glass 	"pci write[.b, .w, .l] b.d.f address value\n"
681*2e192b24SSimon Glass 	"    - write to CFG address";
682*2e192b24SSimon Glass #endif
683*2e192b24SSimon Glass 
684*2e192b24SSimon Glass U_BOOT_CMD(
685*2e192b24SSimon Glass 	pci,	5,	1,	do_pci,
686*2e192b24SSimon Glass 	"list and access PCI Configuration Space", pci_help_text
687*2e192b24SSimon Glass );
688