16d5e39acSMartin Mares /*
26d5e39acSMartin Mares * The PCI Library -- Capabilities
36d5e39acSMartin Mares *
46d5e39acSMartin Mares * Copyright (c) 2008 Martin Mares <[email protected]>
56d5e39acSMartin Mares *
661829219SMartin Mares * Can be freely distributed and used under the terms of the GNU GPL v2+.
761829219SMartin Mares *
861829219SMartin Mares * SPDX-License-Identifier: GPL-2.0-or-later
96d5e39acSMartin Mares */
106d5e39acSMartin Mares
116d5e39acSMartin Mares #include <string.h>
126d5e39acSMartin Mares
136d5e39acSMartin Mares #include "internal.h"
146d5e39acSMartin Mares
156d5e39acSMartin Mares static void
pci_add_cap(struct pci_dev * d,unsigned int addr,unsigned int id,unsigned int type)166d5e39acSMartin Mares pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type)
176d5e39acSMartin Mares {
186d5e39acSMartin Mares struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap));
196d5e39acSMartin Mares
209c48ed23SMartin Mares if (d->last_cap)
219c48ed23SMartin Mares d->last_cap->next = cap;
229c48ed23SMartin Mares else
236d5e39acSMartin Mares d->first_cap = cap;
249c48ed23SMartin Mares d->last_cap = cap;
25ae94d494SMartin Mares cap->next = NULL;
266d5e39acSMartin Mares cap->addr = addr;
276d5e39acSMartin Mares cap->id = id;
286d5e39acSMartin Mares cap->type = type;
296d5e39acSMartin Mares d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n",
306d5e39acSMartin Mares d->domain, d->bus, d->dev, d->func, id, type, addr);
316d5e39acSMartin Mares }
326d5e39acSMartin Mares
336d5e39acSMartin Mares static void
pci_scan_trad_caps(struct pci_dev * d)346d5e39acSMartin Mares pci_scan_trad_caps(struct pci_dev *d)
356d5e39acSMartin Mares {
366d5e39acSMartin Mares word status = pci_read_word(d, PCI_STATUS);
376d5e39acSMartin Mares byte been_there[256];
386d5e39acSMartin Mares int where;
396d5e39acSMartin Mares
406d5e39acSMartin Mares if (!(status & PCI_STATUS_CAP_LIST))
416d5e39acSMartin Mares return;
426d5e39acSMartin Mares
436d5e39acSMartin Mares memset(been_there, 0, 256);
446d5e39acSMartin Mares where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3;
456d5e39acSMartin Mares while (where)
466d5e39acSMartin Mares {
476d5e39acSMartin Mares byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID);
486d5e39acSMartin Mares byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3;
496d5e39acSMartin Mares if (been_there[where]++)
506d5e39acSMartin Mares break;
516d5e39acSMartin Mares if (id == 0xff)
526d5e39acSMartin Mares break;
536d5e39acSMartin Mares pci_add_cap(d, where, id, PCI_CAP_NORMAL);
546d5e39acSMartin Mares where = next;
556d5e39acSMartin Mares }
566d5e39acSMartin Mares }
576d5e39acSMartin Mares
586d5e39acSMartin Mares static void
pci_scan_ext_caps(struct pci_dev * d)596d5e39acSMartin Mares pci_scan_ext_caps(struct pci_dev *d)
606d5e39acSMartin Mares {
616d5e39acSMartin Mares byte been_there[0x1000];
626d5e39acSMartin Mares int where = 0x100;
636d5e39acSMartin Mares
646d5e39acSMartin Mares if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
656d5e39acSMartin Mares return;
666d5e39acSMartin Mares
676d5e39acSMartin Mares memset(been_there, 0, 0x1000);
686d5e39acSMartin Mares do
696d5e39acSMartin Mares {
706d5e39acSMartin Mares u32 header;
716d5e39acSMartin Mares int id;
726d5e39acSMartin Mares
736d5e39acSMartin Mares header = pci_read_long(d, where);
746d5e39acSMartin Mares if (!header || header == 0xffffffff)
756d5e39acSMartin Mares break;
766d5e39acSMartin Mares id = header & 0xffff;
776d5e39acSMartin Mares if (been_there[where]++)
786d5e39acSMartin Mares break;
796d5e39acSMartin Mares pci_add_cap(d, where, id, PCI_CAP_EXTENDED);
80d61c4772SMartin Mares where = (header >> 20) & ~3;
816d5e39acSMartin Mares }
826d5e39acSMartin Mares while (where);
836d5e39acSMartin Mares }
846d5e39acSMartin Mares
858e9299e4SMartin Mares void
pci_scan_caps(struct pci_dev * d,unsigned int want_fields)866d5e39acSMartin Mares pci_scan_caps(struct pci_dev *d, unsigned int want_fields)
876d5e39acSMartin Mares {
888e9299e4SMartin Mares if (want_fields & PCI_FILL_EXT_CAPS)
896d5e39acSMartin Mares want_fields |= PCI_FILL_CAPS;
906d5e39acSMartin Mares
918e9299e4SMartin Mares if (want_fill(d, want_fields, PCI_FILL_CAPS))
926d5e39acSMartin Mares pci_scan_trad_caps(d);
938e9299e4SMartin Mares if (want_fill(d, want_fields, PCI_FILL_EXT_CAPS))
946d5e39acSMartin Mares pci_scan_ext_caps(d);
956d5e39acSMartin Mares }
966d5e39acSMartin Mares
976d5e39acSMartin Mares void
pci_free_caps(struct pci_dev * d)986d5e39acSMartin Mares pci_free_caps(struct pci_dev *d)
996d5e39acSMartin Mares {
1006d5e39acSMartin Mares struct pci_cap *cap;
1016d5e39acSMartin Mares
1026d5e39acSMartin Mares while (cap = d->first_cap)
1036d5e39acSMartin Mares {
104adb9abcfSMartin Mares d->first_cap = cap->next;
105adb9abcfSMartin Mares pci_mfree(cap);
1066d5e39acSMartin Mares }
1076d5e39acSMartin Mares }
1086d5e39acSMartin Mares
1096d5e39acSMartin Mares struct pci_cap *
pci_find_cap(struct pci_dev * d,unsigned int id,unsigned int type)1106d5e39acSMartin Mares pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type)
1116d5e39acSMartin Mares {
112c22c314aSDaniel Schaefer return pci_find_cap_nr(d, id, type, NULL);
113c22c314aSDaniel Schaefer }
1146d5e39acSMartin Mares
115c22c314aSDaniel Schaefer /**
116c22c314aSDaniel Schaefer * Finds a particular capability of a device
117c22c314aSDaniel Schaefer *
118d7d9e305SMartin Mares * To select one capability if there are more than one with the same id, you
119c22c314aSDaniel Schaefer * can provide a pointer to an unsigned int that contains the index which you
120c22c314aSDaniel Schaefer * want as cap_number. If you don't care and are fine with the first one you
1218f33a693SGuillem Jover * can supply NULL. The cap_number will be replaced by the actual number
1228f33a693SGuillem Jover * of capabilities with that id.
123c22c314aSDaniel Schaefer */
124c22c314aSDaniel Schaefer struct pci_cap *
pci_find_cap_nr(struct pci_dev * d,unsigned int id,unsigned int type,unsigned int * cap_number)125c22c314aSDaniel Schaefer pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type,
126c22c314aSDaniel Schaefer unsigned int *cap_number)
127c22c314aSDaniel Schaefer {
128c22c314aSDaniel Schaefer struct pci_cap *c;
129d7d9e305SMartin Mares struct pci_cap *found = NULL;
130d7d9e305SMartin Mares unsigned int target = (cap_number ? *cap_number : 0);
131d7d9e305SMartin Mares unsigned int index = 0;
132d7d9e305SMartin Mares
133*ef78f397SMartin Mares pci_fill_info_v313(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
134d7d9e305SMartin Mares
135d7d9e305SMartin Mares for (c=d->first_cap; c; c=c->next)
136c22c314aSDaniel Schaefer {
137d7d9e305SMartin Mares if (c->type == type && c->id == id)
138d7d9e305SMartin Mares {
139d7d9e305SMartin Mares if (target == index)
140d7d9e305SMartin Mares found = c;
141d7d9e305SMartin Mares index++;
142c22c314aSDaniel Schaefer }
143bc68c6cfSMartin Mares }
144c22c314aSDaniel Schaefer
145d7d9e305SMartin Mares if (cap_number)
146d7d9e305SMartin Mares *cap_number = index;
147d7d9e305SMartin Mares return found;
1486d5e39acSMartin Mares }
149