cap.c revision 173576
1193323Sed/*-
2193323Sed * Copyright (c) 2007 Yahoo!, Inc.
3193323Sed * All rights reserved.
4193323Sed * Written by: John Baldwin <jhb@FreeBSD.org>
5193323Sed *
6193323Sed * Redistribution and use in source and binary forms, with or without
7193323Sed * modification, are permitted provided that the following conditions
8193323Sed * are met:
9193323Sed * 1. Redistributions of source code must retain the above copyright
10193323Sed *    notice, this list of conditions and the following disclaimer.
11193323Sed * 2. Redistributions in binary form must reproduce the above copyright
12193323Sed *    notice, this list of conditions and the following disclaimer in the
13193323Sed *    documentation and/or other materials provided with the distribution.
14193323Sed * 3. Neither the name of the author nor the names of any co-contributors
15193323Sed *    may be used to endorse or promote products derived from this software
16193323Sed *    without specific prior written permission.
17193323Sed *
18193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28193323Sed * SUCH DAMAGE.
29193323Sed */
30193323Sed
31193323Sed#ifndef lint
32193323Sedstatic const char rcsid[] =
33193323Sed  "$FreeBSD: head/usr.sbin/pciconf/cap.c 173576 2007-11-13 01:30:40Z jb $";
34193323Sed#endif /* not lint */
35193323Sed
36193323Sed#include <sys/types.h>
37193323Sed
38193323Sed#include <err.h>
39193323Sed#include <stdio.h>
40193323Sed#include <sys/agpio.h>
41193323Sed#include <sys/pciio.h>
42193323Sed
43193323Sed#include <dev/agp/agpreg.h>
44193323Sed#include <dev/pci/pcireg.h>
45193323Sed
46193323Sed#include "pciconf.h"
47193323Sed
48193323Sedstatic void
49193323Sedcap_power(int fd, struct pci_conf *p, uint8_t ptr)
50193323Sed{
51193323Sed	uint16_t cap, status;
52193323Sed
53193323Sed	cap = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_CAP, 2);
54193323Sed	status = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_STATUS, 2);
55193323Sed	printf("powerspec %d  supports D0%s%s D3  current D%d",
56193323Sed	    cap & PCIM_PCAP_SPEC,
57193323Sed	    cap & PCIM_PCAP_D1SUPP ? " D1" : "",
58193323Sed	    cap & PCIM_PCAP_D2SUPP ? " D2" : "",
59193323Sed	    status & PCIM_PSTAT_DMASK);
60193323Sed}
61193323Sed
62193323Sedstatic void
63193323Sedcap_agp(int fd, struct pci_conf *p, uint8_t ptr)
64193323Sed{
65193323Sed	uint32_t status, command;
66193323Sed
67193323Sed	status = read_config(fd, &p->pc_sel, ptr + AGP_STATUS, 4);
68193323Sed	command = read_config(fd, &p->pc_sel, ptr + AGP_CAPID, 4);
69193323Sed	printf("AGP ");
70193323Sed	if (AGP_MODE_GET_MODE_3(status)) {
71193323Sed		printf("v3 ");
72193323Sed		if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_8x)
73193323Sed			printf("8x ");
74193323Sed		if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_4x)
75193323Sed			printf("4x ");
76193323Sed	} else {
77193323Sed		if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_4x)
78193323Sed			printf("4x ");
79193323Sed		if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_2x)
80193323Sed			printf("2x ");
81193323Sed		if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_1x)
82193323Sed			printf("1x ");
83193323Sed	}
84193323Sed	if (AGP_MODE_GET_SBA(status))
85193323Sed		printf("SBA ");
86193323Sed	if (AGP_MODE_GET_AGP(command)) {
87193323Sed		printf("enabled at ");
88193323Sed		if (AGP_MODE_GET_MODE_3(command)) {
89193323Sed			printf("v3 ");
90193323Sed			switch (AGP_MODE_GET_RATE(command)) {
91193323Sed			case AGP_MODE_V3_RATE_8x:
92193323Sed				printf("8x ");
93193323Sed				break;
94193323Sed			case AGP_MODE_V3_RATE_4x:
95193323Sed				printf("4x ");
96193323Sed				break;
97193323Sed			}
98193323Sed		} else
99193323Sed			switch (AGP_MODE_GET_RATE(command)) {
100193323Sed			case AGP_MODE_V2_RATE_4x:
101193323Sed				printf("4x ");
102193323Sed				break;
103193323Sed			case AGP_MODE_V2_RATE_2x:
104193323Sed				printf("2x ");
105193323Sed				break;
106193323Sed			case AGP_MODE_V2_RATE_1x:
107193323Sed				printf("1x ");
108193323Sed				break;
109193323Sed			}
110193323Sed		if (AGP_MODE_GET_SBA(command))
111193323Sed			printf("SBA ");
112193323Sed	} else
113193323Sed		printf("disabled");
114193323Sed}
115193323Sed
116193323Sedstatic void
117193323Sedcap_vpd(int fd, struct pci_conf *p, uint8_t ptr)
118193323Sed{
119193323Sed
120193323Sed	printf("VPD");
121193323Sed}
122193323Sed
123193323Sedstatic void
124193323Sedcap_msi(int fd, struct pci_conf *p, uint8_t ptr)
125193323Sed{
126193323Sed	uint16_t ctrl;
127193323Sed	int msgnum;
128193323Sed
129193323Sed	ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSI_CTRL, 2);
130193323Sed	msgnum = 1 << ((ctrl & PCIM_MSICTRL_MMC_MASK) >> 1);
131193323Sed	printf("MSI supports %d message%s%s%s ", msgnum,
132193323Sed	    (msgnum == 1) ? "" : "s",
133193323Sed	    (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "",
134193323Sed	    (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks" : "");
135193323Sed	if (ctrl & PCIM_MSICTRL_MSI_ENABLE) {
136193323Sed		msgnum = 1 << ((ctrl & PCIM_MSICTRL_MME_MASK) >> 4);
137193323Sed		printf("enabled with %d message%s", msgnum,
138193323Sed		    (msgnum == 1) ? "" : "s");
139193323Sed	}
140193323Sed}
141193323Sed
142193323Sedstatic void
143193323Sedcap_pcix(int fd, struct pci_conf *p, uint8_t ptr)
144193323Sed{
145193323Sed	uint32_t status;
146193323Sed	int comma, max_splits, max_burst_read;
147193323Sed
148193323Sed	status = read_config(fd, &p->pc_sel, ptr + PCIXR_STATUS, 4);
149193323Sed	printf("PCI-X ");
150193323Sed	if (status & PCIXM_STATUS_64BIT)
151193323Sed		printf("64-bit ");
152193323Sed	if ((p->pc_hdr & PCIM_HDRTYPE) == 1)
153193323Sed		printf("bridge ");
154193323Sed	printf("supports");
155193323Sed	comma = 0;
156193323Sed	if (status & PCIXM_STATUS_133CAP) {
157193323Sed		printf("%s 133MHz", comma ? "," : "");
158193323Sed		comma = 1;
159193323Sed	}
160193323Sed	if (status & PCIXM_STATUS_266CAP) {
161193323Sed		printf("%s 266MHz", comma ? "," : "");
162193323Sed		comma = 1;
163193323Sed	}
164193323Sed	if (status & PCIXM_STATUS_533CAP) {
165193323Sed		printf("%s 533MHz", comma ? "," : "");
166193323Sed		comma = 1;
167193323Sed	}
168193323Sed	if ((p->pc_hdr & PCIM_HDRTYPE) == 1)
169193323Sed		return;
170193323Sed	switch (status & PCIXM_STATUS_MAX_READ) {
171193323Sed	case PCIXM_STATUS_MAX_READ_512:
172193323Sed		max_burst_read = 512;
173193323Sed		break;
174193323Sed	case PCIXM_STATUS_MAX_READ_1024:
175193323Sed		max_burst_read = 1024;
176193323Sed		break;
177193323Sed	case PCIXM_STATUS_MAX_READ_2048:
178193323Sed		max_burst_read = 2048;
179193323Sed		break;
180193323Sed	case PCIXM_STATUS_MAX_READ_4096:
181193323Sed		max_burst_read = 4096;
182193323Sed		break;
183193323Sed	}
184193323Sed	switch (status & PCIXM_STATUS_MAX_SPLITS) {
185193323Sed	case PCIXM_STATUS_MAX_SPLITS_1:
186193323Sed		max_splits = 1;
187193323Sed		break;
188193323Sed	case PCIXM_STATUS_MAX_SPLITS_2:
189193323Sed		max_splits = 2;
190193323Sed		break;
191193323Sed	case PCIXM_STATUS_MAX_SPLITS_3:
192193323Sed		max_splits = 3;
193193323Sed		break;
194193323Sed	case PCIXM_STATUS_MAX_SPLITS_4:
195193323Sed		max_splits = 4;
196193323Sed		break;
197193323Sed	case PCIXM_STATUS_MAX_SPLITS_8:
198193323Sed		max_splits = 8;
199193323Sed		break;
200193323Sed	case PCIXM_STATUS_MAX_SPLITS_12:
201193323Sed		max_splits = 12;
202193323Sed		break;
203193323Sed	case PCIXM_STATUS_MAX_SPLITS_16:
204193323Sed		max_splits = 16;
205193323Sed		break;
206193323Sed	case PCIXM_STATUS_MAX_SPLITS_32:
207193323Sed		max_splits = 32;
208193323Sed		break;
209193323Sed	}
210193323Sed	printf("%s %d burst read, %d split transaction%s", comma ? "," : "",
211193323Sed	    max_burst_read, max_splits, max_splits == 1 ? "" : "s");
212193323Sed}
213193323Sed
214193323Sedstatic void
215193323Sedcap_ht(int fd, struct pci_conf *p, uint8_t ptr)
216193323Sed{
217193323Sed	uint32_t reg;
218193323Sed	uint16_t command;
219193323Sed
220193323Sed	command = read_config(fd, &p->pc_sel, ptr + PCIR_HT_COMMAND, 2);
221193323Sed	printf("HT ");
222193323Sed	if ((command & 0xe000) == PCIM_HTCAP_SLAVE)
223193323Sed		printf("slave");
224193323Sed	else if ((command & 0xe000) == PCIM_HTCAP_HOST)
225193323Sed		printf("host");
226193323Sed	else
227193323Sed		switch (command & PCIM_HTCMD_CAP_MASK) {
228193323Sed		case PCIM_HTCAP_SWITCH:
229193323Sed			printf("switch");
230193323Sed			break;
231193323Sed		case PCIM_HTCAP_INTERRUPT:
232193323Sed			printf("interrupt");
233193323Sed			break;
234193323Sed		case PCIM_HTCAP_REVISION_ID:
235193323Sed			printf("revision ID");
236193323Sed			break;
237193323Sed		case PCIM_HTCAP_UNITID_CLUMPING:
238193323Sed			printf("unit ID clumping");
239193323Sed			break;
240193323Sed		case PCIM_HTCAP_EXT_CONFIG_SPACE:
241193323Sed			printf("extended config space");
242193323Sed			break;
243193323Sed		case PCIM_HTCAP_ADDRESS_MAPPING:
244193323Sed			printf("address mapping");
245193323Sed			break;
246193323Sed		case PCIM_HTCAP_MSI_MAPPING:
247193323Sed			printf("MSI %saddress window %s at 0x",
248193323Sed			    command & PCIM_HTCMD_MSI_FIXED ? "fixed " : "",
249193323Sed			    command & PCIM_HTCMD_MSI_ENABLE ? "enabled" :
250193323Sed			    "disabled");
251193323Sed			if (command & PCIM_HTCMD_MSI_FIXED)
252193323Sed				printf("fee00000");
253193323Sed			else {
254193323Sed				reg = read_config(fd, &p->pc_sel,
255193323Sed				    ptr + PCIR_HTMSI_ADDRESS_HI, 4);
256193323Sed				if (reg != 0)
257193323Sed					printf("%08x", reg);
258193323Sed				reg = read_config(fd, &p->pc_sel,
259193323Sed				    ptr + PCIR_HTMSI_ADDRESS_LO, 4);
260193323Sed				printf("%08x", reg);
261193323Sed			}
262193323Sed			break;
263193323Sed		case PCIM_HTCAP_DIRECT_ROUTE:
264193323Sed			printf("direct route");
265193323Sed			break;
266193323Sed		case PCIM_HTCAP_VCSET:
267193323Sed			printf("VC set");
268193323Sed			break;
269193323Sed		case PCIM_HTCAP_RETRY_MODE:
270193323Sed			printf("retry mode");
271193323Sed			break;
272193323Sed		case PCIM_HTCAP_X86_ENCODING:
273193323Sed			printf("X86 encoding");
274193323Sed			break;
275193323Sed		default:
276193323Sed			printf("unknown %02x", command);
277193323Sed			break;
278193323Sed		}
279193323Sed}
280193323Sed
281193323Sedstatic void
282193323Sedcap_vendor(int fd, struct pci_conf *p, uint8_t ptr)
283193323Sed{
284193323Sed	uint8_t length;
285193323Sed
286193323Sed	length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1);
287193323Sed	printf("vendor (length %d)", length);
288193323Sed	if (p->pc_vendor == 0x8086) {
289193323Sed		/* Intel */
290193323Sed		uint8_t version;
291193323Sed
292193323Sed		version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA,
293193323Sed		    1);
294193323Sed		printf(" Intel cap %d version %d", version >> 4, version & 0xf);
295193323Sed		if (version >> 4 == 1 && length == 12) {
296193323Sed			/* Feature Detection */
297193323Sed			uint32_t fvec;
298193323Sed			int comma;
299193323Sed
300193323Sed			comma = 0;
301193323Sed			fvec = read_config(fd, &p->pc_sel, ptr +
302193323Sed			    PCIR_VENDOR_DATA + 5, 4);
303193323Sed			printf("\n\t\t features:");
304193323Sed			if (fvec & (1 << 0)) {
305193323Sed				printf(" AMT");
306193323Sed				comma = 1;
307193323Sed			}
308193323Sed			fvec = read_config(fd, &p->pc_sel, ptr +
309193323Sed			    PCIR_VENDOR_DATA + 1, 4);
310193323Sed			if (fvec & (1 << 21)) {
311193323Sed				printf("%s Quick Resume", comma ? "," : "");
312193323Sed				comma = 1;
313193323Sed			}
314193323Sed			if (fvec & (1 << 18)) {
315193323Sed				printf("%s SATA RAID-5", comma ? "," : "");
316193323Sed				comma = 1;
317193323Sed			}
318193323Sed			if (fvec & (1 << 9)) {
319193323Sed				printf("%s Mobile", comma ? "," : "");
320193323Sed				comma = 1;
321193323Sed			}
322193323Sed			if (fvec & (1 << 7)) {
323193323Sed				printf("%s 6 PCI-e x1 slots", comma ? "," : "");
324193323Sed				comma = 1;
325193323Sed			} else {
326193323Sed				printf("%s 4 PCI-e x1 slots", comma ? "," : "");
327193323Sed				comma = 1;
328193323Sed			}
329193323Sed			if (fvec & (1 << 5)) {
330193323Sed				printf("%s SATA RAID-0/1/10", comma ? "," : "");
331193323Sed				comma = 1;
332193323Sed			}
333193323Sed			if (fvec & (1 << 3)) {
334193323Sed				printf("%s SATA AHCI", comma ? "," : "");
335193323Sed				comma = 1;
336193323Sed			}
337193323Sed		}
338193323Sed	}
339193323Sed}
340193323Sed
341193323Sedstatic void
342193323Sedcap_debug(int fd, struct pci_conf *p, uint8_t ptr)
343193323Sed{
344193323Sed	uint16_t debug_port;
345193323Sed
346193323Sed	debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2);
347193323Sed	printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port &
348193323Sed	    PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13));
349193323Sed}
350193323Sed
351193323Sedstatic void
352193323Sedcap_subvendor(int fd, struct pci_conf *p, uint8_t ptr)
353193323Sed{
354193323Sed	uint32_t id;
355193323Sed
356193323Sed	id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4);
357193323Sed	printf("PCI Bridge card=0x%08x", id);
358193323Sed}
359193323Sed
360193323Sedstatic void
361193323Sedcap_express(int fd, struct pci_conf *p, uint8_t ptr)
362193323Sed{
363193323Sed	uint16_t flags;
364193323Sed
365193323Sed	flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_FLAGS, 2);
366193323Sed	printf("PCI-Express %d ", flags & PCIM_EXP_FLAGS_VERSION);
367193323Sed	switch (flags & PCIM_EXP_FLAGS_TYPE) {
368193323Sed	case PCIM_EXP_TYPE_ENDPOINT:
369193323Sed		printf("endpoint");
370193323Sed		break;
371193323Sed	case PCIM_EXP_TYPE_LEGACY_ENDPOINT:
372193323Sed		printf("legacy endpoint");
373193323Sed		break;
374193323Sed	case PCIM_EXP_TYPE_ROOT_PORT:
375193323Sed		printf("root port");
376193323Sed		break;
377193323Sed	case PCIM_EXP_TYPE_UPSTREAM_PORT:
378193323Sed		printf("upstream port");
379193323Sed		break;
380193323Sed	case PCIM_EXP_TYPE_DOWNSTREAM_PORT:
381193323Sed		printf("downstream port");
382193323Sed		break;
383193323Sed	case PCIM_EXP_TYPE_PCI_BRIDGE:
384193323Sed		printf("PCI bridge");
385193323Sed		break;
386193323Sed	default:
387193323Sed		printf("type %d", (flags & PCIM_EXP_FLAGS_TYPE) >> 8);
388193323Sed		break;
389193323Sed	}
390193323Sed	if (flags & PCIM_EXP_FLAGS_IRQ)
391193323Sed		printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 17);
392193323Sed}
393193323Sed
394193323Sedstatic void
395193323Sedcap_msix(int fd, struct pci_conf *p, uint8_t ptr)
396193323Sed{
397193323Sed	uint32_t val;
398193323Sed	uint16_t ctrl;
399193323Sed	int msgnum, table_bar, pba_bar;
400193323Sed
401193323Sed	ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2);
402193323Sed	msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1;
403193323Sed	val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4);
404193323Sed	table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
405193323Sed	val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4);
406193323Sed	pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
407193323Sed	printf("MSI-X supports %d message%s ", msgnum,
408193323Sed	    (msgnum == 1) ? "" : "s");
409193323Sed	if (table_bar == pba_bar)
410193323Sed		printf("in map 0x%x", table_bar);
411193323Sed	else
412193323Sed		printf("in maps 0x%x and 0x%x", table_bar, pba_bar);
413193323Sed	if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE)
414193323Sed		printf(" enabled");
415193323Sed}
416193323Sed
417193323Sedvoid
418193323Sedlist_caps(int fd, struct pci_conf *p)
419193323Sed{
420193323Sed	uint16_t cmd;
421193323Sed	uint8_t ptr, cap;
422193323Sed
423193323Sed	/* Are capabilities present for this device? */
424193323Sed	cmd = read_config(fd, &p->pc_sel, PCIR_STATUS, 2);
425193323Sed	if (!(cmd & PCIM_STATUS_CAPPRESENT))
426193323Sed		return;
427193323Sed
428193323Sed	switch (p->pc_hdr & PCIM_HDRTYPE) {
429193323Sed	case 0:
430193323Sed	case 1:
431193323Sed		ptr = PCIR_CAP_PTR;
432193323Sed		break;
433193323Sed	case 2:
434193323Sed		ptr = PCIR_CAP_PTR_2;
435193323Sed		break;
436193323Sed	default:
437193323Sed		errx(1, "list_caps: bad header type");
438193323Sed	}
439193323Sed
440193323Sed	/* Walk the capability list. */
441193323Sed	ptr = read_config(fd, &p->pc_sel, ptr, 1);
442193323Sed	while (ptr != 0 && ptr != 0xff) {
443193323Sed		cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1);
444193323Sed		printf("    cap %02x[%02x] = ", cap, ptr);
445193323Sed		switch (cap) {
446193323Sed		case PCIY_PMG:
447193323Sed			cap_power(fd, p, ptr);
448193323Sed			break;
449193323Sed		case PCIY_AGP:
450193323Sed			cap_agp(fd, p, ptr);
451193323Sed			break;
452193323Sed		case PCIY_VPD:
453193323Sed			cap_vpd(fd, p, ptr);
454193323Sed			break;
455193323Sed		case PCIY_MSI:
456193323Sed			cap_msi(fd, p, ptr);
457193323Sed			break;
458193323Sed		case PCIY_PCIX:
459193323Sed			cap_pcix(fd, p, ptr);
460193323Sed			break;
461193323Sed		case PCIY_HT:
462			cap_ht(fd, p, ptr);
463			break;
464		case PCIY_VENDOR:
465			cap_vendor(fd, p, ptr);
466			break;
467		case PCIY_DEBUG:
468			cap_debug(fd, p, ptr);
469			break;
470		case PCIY_SUBVENDOR:
471			cap_subvendor(fd, p, ptr);
472			break;
473		case PCIY_EXPRESS:
474			cap_express(fd, p, ptr);
475			break;
476		case PCIY_MSIX:
477			cap_msix(fd, p, ptr);
478			break;
479		default:
480			printf("unknown");
481			break;
482		}
483		printf("\n");
484		ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1);
485	}
486}
487