xref: /pciutils/lib/generic.c (revision 61829219)
1727ce158SMartin Mares /*
2727ce158SMartin Mares  *	The PCI Library -- Generic Direct Access Functions
3727ce158SMartin Mares  *
48e9299e4SMartin Mares  *	Copyright (c) 1997--2022 Martin Mares <[email protected]>
5727ce158SMartin Mares  *
6*61829219SMartin Mares  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
7*61829219SMartin Mares  *
8*61829219SMartin Mares  *	SPDX-License-Identifier: GPL-2.0-or-later
9727ce158SMartin Mares  */
10727ce158SMartin Mares 
11727ce158SMartin Mares #include <string.h>
12727ce158SMartin Mares 
13727ce158SMartin Mares #include "internal.h"
14727ce158SMartin Mares 
1514d6c0a3SMartin Mares void
pci_generic_scan_bus(struct pci_access * a,byte * busmap,int domain,int bus)168d750a8dSPali Rohár pci_generic_scan_bus(struct pci_access *a, byte *busmap, int domain, int bus)
17727ce158SMartin Mares {
18727ce158SMartin Mares   int dev, multi, ht;
1905bb10a2SMartin Mares   struct pci_dev *t;
20727ce158SMartin Mares 
21727ce158SMartin Mares   a->debug("Scanning bus %02x for devices...\n", bus);
22727ce158SMartin Mares   if (busmap[bus])
23727ce158SMartin Mares     {
24727ce158SMartin Mares       a->warning("Bus %02x seen twice (firmware bug). Ignored.", bus);
25727ce158SMartin Mares       return;
26727ce158SMartin Mares     }
27727ce158SMartin Mares   busmap[bus] = 1;
2805bb10a2SMartin Mares   t = pci_alloc_dev(a);
298d750a8dSPali Rohár   t->domain = domain;
30727ce158SMartin Mares   t->bus = bus;
31727ce158SMartin Mares   for (dev=0; dev<32; dev++)
32727ce158SMartin Mares     {
33727ce158SMartin Mares       t->dev = dev;
34727ce158SMartin Mares       multi = 0;
35eb620239SMartin Mares       for (t->func=0; !t->func || multi && t->func<8; t->func++)
36727ce158SMartin Mares 	{
37727ce158SMartin Mares 	  u32 vd = pci_read_long(t, PCI_VENDOR_ID);
38727ce158SMartin Mares 	  struct pci_dev *d;
39727ce158SMartin Mares 
40727ce158SMartin Mares 	  if (!vd || vd == 0xffffffff)
41eb620239SMartin Mares 	    continue;
42727ce158SMartin Mares 	  ht = pci_read_byte(t, PCI_HEADER_TYPE);
43727ce158SMartin Mares 	  if (!t->func)
44727ce158SMartin Mares 	    multi = ht & 0x80;
45727ce158SMartin Mares 	  ht &= 0x7f;
46727ce158SMartin Mares 	  d = pci_alloc_dev(a);
478d750a8dSPali Rohár 	  d->domain = t->domain;
48727ce158SMartin Mares 	  d->bus = t->bus;
49727ce158SMartin Mares 	  d->dev = t->dev;
50727ce158SMartin Mares 	  d->func = t->func;
51727ce158SMartin Mares 	  d->vendor_id = vd & 0xffff;
52727ce158SMartin Mares 	  d->device_id = vd >> 16U;
53727ce158SMartin Mares 	  d->known_fields = PCI_FILL_IDENT;
54727ce158SMartin Mares 	  d->hdrtype = ht;
55727ce158SMartin Mares 	  pci_link_dev(a, d);
56727ce158SMartin Mares 	  switch (ht)
57727ce158SMartin Mares 	    {
58727ce158SMartin Mares 	    case PCI_HEADER_TYPE_NORMAL:
59727ce158SMartin Mares 	      break;
60727ce158SMartin Mares 	    case PCI_HEADER_TYPE_BRIDGE:
61727ce158SMartin Mares 	    case PCI_HEADER_TYPE_CARDBUS:
628d750a8dSPali Rohár 	      pci_generic_scan_bus(a, busmap, domain, pci_read_byte(t, PCI_SECONDARY_BUS));
63727ce158SMartin Mares 	      break;
64727ce158SMartin Mares 	    default:
6584c8d1bbSMartin Mares 	      a->debug("Device %04x:%02x:%02x.%d has unknown header type %02x.\n", d->domain, d->bus, d->dev, d->func, ht);
66727ce158SMartin Mares 	    }
67727ce158SMartin Mares 	}
68727ce158SMartin Mares     }
6905bb10a2SMartin Mares   pci_free_dev(t);
70727ce158SMartin Mares }
71727ce158SMartin Mares 
72727ce158SMartin Mares void
pci_generic_scan_domain(struct pci_access * a,int domain)738d750a8dSPali Rohár pci_generic_scan_domain(struct pci_access *a, int domain)
74727ce158SMartin Mares {
75727ce158SMartin Mares   byte busmap[256];
76727ce158SMartin Mares 
771ac3a99dSMartin Mares   memset(busmap, 0, sizeof(busmap));
788d750a8dSPali Rohár   pci_generic_scan_bus(a, busmap, domain, 0);
798d750a8dSPali Rohár }
808d750a8dSPali Rohár 
818d750a8dSPali Rohár void
pci_generic_scan(struct pci_access * a)828d750a8dSPali Rohár pci_generic_scan(struct pci_access *a)
838d750a8dSPali Rohár {
848d750a8dSPali Rohár   pci_generic_scan_domain(a, 0);
85727ce158SMartin Mares }
86727ce158SMartin Mares 
878e9299e4SMartin Mares static int
get_hdr_type(struct pci_dev * d)888e9299e4SMartin Mares get_hdr_type(struct pci_dev *d)
898e9299e4SMartin Mares {
908e9299e4SMartin Mares   if (d->hdrtype < 0)
918e9299e4SMartin Mares     d->hdrtype = pci_read_byte(d, PCI_HEADER_TYPE) & 0x7f;
928e9299e4SMartin Mares   return d->hdrtype;
938e9299e4SMartin Mares }
948e9299e4SMartin Mares 
958e9299e4SMartin Mares void
pci_generic_fill_info(struct pci_dev * d,unsigned int flags)9682c06b47SMartin Mares pci_generic_fill_info(struct pci_dev *d, unsigned int flags)
97727ce158SMartin Mares {
98727ce158SMartin Mares   struct pci_access *a = d->access;
994fd10eb9SPali Rohár   struct pci_cap *cap;
100727ce158SMartin Mares 
1018e9299e4SMartin Mares   if (want_fill(d, flags, PCI_FILL_IDENT))
102727ce158SMartin Mares     {
103727ce158SMartin Mares       d->vendor_id = pci_read_word(d, PCI_VENDOR_ID);
104727ce158SMartin Mares       d->device_id = pci_read_word(d, PCI_DEVICE_ID);
105727ce158SMartin Mares     }
10682c06b47SMartin Mares 
1078e9299e4SMartin Mares   if (want_fill(d, flags, PCI_FILL_CLASS))
108c2b144efSMartin Mares     d->device_class = pci_read_word(d, PCI_CLASS_DEVICE);
10982c06b47SMartin Mares 
1102dc7b53bSPali Rohár   if (want_fill(d, flags, PCI_FILL_CLASS_EXT))
1112dc7b53bSPali Rohár     {
1122dc7b53bSPali Rohár       d->prog_if = pci_read_byte(d, PCI_CLASS_PROG);
1132dc7b53bSPali Rohár       d->rev_id = pci_read_byte(d, PCI_REVISION_ID);
1142dc7b53bSPali Rohár     }
1152dc7b53bSPali Rohár 
1162dc7b53bSPali Rohár   if (want_fill(d, flags, PCI_FILL_SUBSYS))
1172dc7b53bSPali Rohár     {
1182dc7b53bSPali Rohár       switch (get_hdr_type(d))
1192dc7b53bSPali Rohár         {
1202dc7b53bSPali Rohár         case PCI_HEADER_TYPE_NORMAL:
1212dc7b53bSPali Rohár           d->subsys_vendor_id = pci_read_word(d, PCI_SUBSYSTEM_VENDOR_ID);
1222dc7b53bSPali Rohár           d->subsys_id = pci_read_word(d, PCI_SUBSYSTEM_ID);
1232dc7b53bSPali Rohár           break;
1244fd10eb9SPali Rohár         case PCI_HEADER_TYPE_BRIDGE:
1254fd10eb9SPali Rohár           cap = pci_find_cap(d, PCI_CAP_ID_SSVID, PCI_CAP_NORMAL);
1264fd10eb9SPali Rohár           if (cap)
1274fd10eb9SPali Rohár             {
1284fd10eb9SPali Rohár               d->subsys_vendor_id = pci_read_word(d, cap->addr + PCI_SSVID_VENDOR);
1294fd10eb9SPali Rohár               d->subsys_id = pci_read_word(d, cap->addr + PCI_SSVID_DEVICE);
1304fd10eb9SPali Rohár             }
1314fd10eb9SPali Rohár           break;
1322dc7b53bSPali Rohár         case PCI_HEADER_TYPE_CARDBUS:
1332dc7b53bSPali Rohár           d->subsys_vendor_id = pci_read_word(d, PCI_CB_SUBSYSTEM_VENDOR_ID);
1342dc7b53bSPali Rohár           d->subsys_id = pci_read_word(d, PCI_CB_SUBSYSTEM_ID);
1352dc7b53bSPali Rohár           break;
1362dc7b53bSPali Rohár         default:
1372dc7b53bSPali Rohár           clear_fill(d, PCI_FILL_SUBSYS);
1382dc7b53bSPali Rohár         }
1392dc7b53bSPali Rohár     }
1402dc7b53bSPali Rohár 
1418e9299e4SMartin Mares   if (want_fill(d, flags, PCI_FILL_IRQ))
142727ce158SMartin Mares     d->irq = pci_read_byte(d, PCI_INTERRUPT_LINE);
14382c06b47SMartin Mares 
1448e9299e4SMartin Mares   if (want_fill(d, flags, PCI_FILL_BASES))
145727ce158SMartin Mares     {
146727ce158SMartin Mares       int cnt = 0, i;
1471ac3a99dSMartin Mares       memset(d->base_addr, 0, sizeof(d->base_addr));
1488e9299e4SMartin Mares       switch (get_hdr_type(d))
149727ce158SMartin Mares 	{
150727ce158SMartin Mares 	case PCI_HEADER_TYPE_NORMAL:
151727ce158SMartin Mares 	  cnt = 6;
152727ce158SMartin Mares 	  break;
153727ce158SMartin Mares 	case PCI_HEADER_TYPE_BRIDGE:
154727ce158SMartin Mares 	  cnt = 2;
155727ce158SMartin Mares 	  break;
156727ce158SMartin Mares 	case PCI_HEADER_TYPE_CARDBUS:
157727ce158SMartin Mares 	  cnt = 1;
158727ce158SMartin Mares 	  break;
159727ce158SMartin Mares 	}
160727ce158SMartin Mares       if (cnt)
161727ce158SMartin Mares 	{
162727ce158SMartin Mares 	  for (i=0; i<cnt; i++)
163727ce158SMartin Mares 	    {
164727ce158SMartin Mares 	      u32 x = pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4);
165727ce158SMartin Mares 	      if (!x || x == (u32) ~0)
166727ce158SMartin Mares 		continue;
1676aa54f1bSMartin Mares 	      if ((x & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
168727ce158SMartin Mares 		d->base_addr[i] = x;
1696aa54f1bSMartin Mares 	      else
170727ce158SMartin Mares 		{
1716aa54f1bSMartin Mares 		  if ((x & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64)
1726aa54f1bSMartin Mares 		    d->base_addr[i] = x;
1736aa54f1bSMartin Mares 		  else if (i >= cnt-1)
1746aa54f1bSMartin Mares 		    a->warning("%04x:%02x:%02x.%d: Invalid 64-bit address seen for BAR %d.", d->domain, d->bus, d->dev, d->func, i);
175727ce158SMartin Mares 		  else
176727ce158SMartin Mares 		    {
177727ce158SMartin Mares 		      u32 y = pci_read_long(d, PCI_BASE_ADDRESS_0 + (++i)*4);
178489233b4SMartin Mares #ifdef PCI_HAVE_64BIT_ADDRESS
1796aa54f1bSMartin Mares 		      d->base_addr[i-1] = x | (((pciaddr_t) y) << 32);
180727ce158SMartin Mares #else
181727ce158SMartin Mares 		      if (y)
18284c8d1bbSMartin Mares 			a->warning("%04x:%02x:%02x.%d 64-bit device address ignored.", d->domain, d->bus, d->dev, d->func);
1836aa54f1bSMartin Mares 		      else
1846aa54f1bSMartin Mares 			d->base_addr[i-1] = x;
185727ce158SMartin Mares #endif
186727ce158SMartin Mares 		    }
187727ce158SMartin Mares 		}
188727ce158SMartin Mares 	    }
189727ce158SMartin Mares 	}
190727ce158SMartin Mares     }
19182c06b47SMartin Mares 
1928e9299e4SMartin Mares   if (want_fill(d, flags, PCI_FILL_ROM_BASE))
193727ce158SMartin Mares     {
194727ce158SMartin Mares       int reg = 0;
195727ce158SMartin Mares       d->rom_base_addr = 0;
1968e9299e4SMartin Mares       switch (get_hdr_type(d))
197727ce158SMartin Mares 	{
198727ce158SMartin Mares 	case PCI_HEADER_TYPE_NORMAL:
199727ce158SMartin Mares 	  reg = PCI_ROM_ADDRESS;
200727ce158SMartin Mares 	  break;
201727ce158SMartin Mares 	case PCI_HEADER_TYPE_BRIDGE:
202727ce158SMartin Mares 	  reg = PCI_ROM_ADDRESS1;
203727ce158SMartin Mares 	  break;
204727ce158SMartin Mares 	}
205727ce158SMartin Mares       if (reg)
206727ce158SMartin Mares 	{
2076aa54f1bSMartin Mares 	  u32 u = pci_read_long(d, reg);
2086aa54f1bSMartin Mares 	  if (u != 0xffffffff)
2096aa54f1bSMartin Mares 	    d->rom_base_addr = u;
210727ce158SMartin Mares 	}
211727ce158SMartin Mares     }
21282c06b47SMartin Mares 
2138e9299e4SMartin Mares   pci_scan_caps(d, flags);
214727ce158SMartin Mares }
215727ce158SMartin Mares 
216727ce158SMartin Mares static int
pci_generic_block_op(struct pci_dev * d,int pos,byte * buf,int len,int (* r)(struct pci_dev * d,int pos,byte * buf,int len))217727ce158SMartin Mares pci_generic_block_op(struct pci_dev *d, int pos, byte *buf, int len,
218727ce158SMartin Mares 		 int (*r)(struct pci_dev *d, int pos, byte *buf, int len))
219727ce158SMartin Mares {
220727ce158SMartin Mares   if ((pos & 1) && len >= 1)
221727ce158SMartin Mares     {
222727ce158SMartin Mares       if (!r(d, pos, buf, 1))
223727ce158SMartin Mares 	return 0;
224727ce158SMartin Mares       pos++; buf++; len--;
225727ce158SMartin Mares     }
226727ce158SMartin Mares   if ((pos & 3) && len >= 2)
227727ce158SMartin Mares     {
228727ce158SMartin Mares       if (!r(d, pos, buf, 2))
229727ce158SMartin Mares 	return 0;
230727ce158SMartin Mares       pos += 2; buf += 2; len -= 2;
231727ce158SMartin Mares     }
232727ce158SMartin Mares   while (len >= 4)
233727ce158SMartin Mares     {
234727ce158SMartin Mares       if (!r(d, pos, buf, 4))
235727ce158SMartin Mares 	return 0;
236727ce158SMartin Mares       pos += 4; buf += 4; len -= 4;
237727ce158SMartin Mares     }
238727ce158SMartin Mares   if (len >= 2)
239727ce158SMartin Mares     {
240727ce158SMartin Mares       if (!r(d, pos, buf, 2))
241727ce158SMartin Mares 	return 0;
242727ce158SMartin Mares       pos += 2; buf += 2; len -= 2;
243727ce158SMartin Mares     }
244727ce158SMartin Mares   if (len && !r(d, pos, buf, 1))
245727ce158SMartin Mares     return 0;
246727ce158SMartin Mares   return 1;
247727ce158SMartin Mares }
248727ce158SMartin Mares 
249727ce158SMartin Mares int
pci_generic_block_read(struct pci_dev * d,int pos,byte * buf,int len)250727ce158SMartin Mares pci_generic_block_read(struct pci_dev *d, int pos, byte *buf, int len)
251727ce158SMartin Mares {
252727ce158SMartin Mares   return pci_generic_block_op(d, pos, buf, len, d->access->methods->read);
253727ce158SMartin Mares }
254727ce158SMartin Mares 
255727ce158SMartin Mares int
pci_generic_block_write(struct pci_dev * d,int pos,byte * buf,int len)256727ce158SMartin Mares pci_generic_block_write(struct pci_dev *d, int pos, byte *buf, int len)
257727ce158SMartin Mares {
258727ce158SMartin Mares   return pci_generic_block_op(d, pos, buf, len, d->access->methods->write);
259727ce158SMartin Mares }
260