09b51e5133f8faac18bc783b3e7bdac7690230b8
[coreboot.git] / payloads / libpayload / libpci / libpci.c
1 /*
2  * This file is part of the libpayload project.
3  *
4  * Copyright (C) 2010 coresystems GmbH
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <libpayload.h>
31 #include <pci.h>
32 #include <pci/pci.h>
33
34 /* libpci shim */
35 static pcidev_t libpci_to_lb(struct pci_dev *dev)
36 {
37         return PCI_DEV(dev->bus, dev->dev, dev->func);
38 }
39
40 /* libpci interface */
41 u8 pci_read_byte(struct pci_dev *dev, int pos)
42 {
43         return pci_read_config8(libpci_to_lb(dev), pos);
44 }
45
46 u16 pci_read_word(struct pci_dev *dev, int pos)
47 {
48         return pci_read_config16(libpci_to_lb(dev), pos);
49 }
50
51 u32 pci_read_long(struct pci_dev *dev, int pos)
52 {
53         return pci_read_config32(libpci_to_lb(dev), pos);
54 }
55
56 int pci_write_byte(struct pci_dev *dev, int pos, u8 data)
57 {
58         pci_write_config8(libpci_to_lb(dev), pos, data);
59         return 1; /* success */
60 }
61
62 int pci_write_word(struct pci_dev *dev, int pos, u16 data)
63 {
64         pci_write_config16(libpci_to_lb(dev), pos, data);
65         return 1; /* success */
66 }
67
68 int pci_write_long(struct pci_dev *dev, int pos, u32 data)
69 {
70         pci_write_config32(libpci_to_lb(dev), pos, data);
71         return 1; /* success */
72 }
73
74 struct pci_access *pci_alloc(void)
75 {
76         struct pci_access *pacc = malloc(sizeof(*pacc));
77         return pacc;
78 }
79
80 void pci_init(struct pci_access* pacc)
81 {
82         memset(pacc, 0, sizeof(*pacc));
83 }
84
85 void pci_filter_init(struct pci_access* pacc, struct pci_filter* pf)
86 {
87         pf->domain = -1;
88         pf->bus = -1;
89         pf->dev = -1;
90         pf->func = -1;
91         pf->vendor = -1;
92         pf->device = -1;
93 }
94
95 /* parse domain:bus:dev.func (with all components but "dev" optional)
96  * into filter.
97  * Returns NULL on success, a string pointer to the error message otherwise.
98  */
99 char *pci_filter_parse_slot(struct pci_filter* filter, const char* id)
100 {
101         char *endptr;
102
103         filter->func = filter->dev = filter->bus = filter->domain = -1;
104
105         char *funcp = strrchr(id, '.');
106         if (funcp) {
107                 filter->func = strtoul(funcp+1, &endptr, 0);
108                 if (endptr[0] != '\0') return "invalid pci device string";
109         }
110
111         char *devp = strrchr(id, ':');
112         if (!devp) {
113                 filter->dev = strtoul(id, &endptr, 0);
114         } else {
115                 filter->dev = strtoul(devp+1, &endptr, 0);
116         }
117         if (endptr != funcp) return "invalid pci device string";
118         if (!devp) return NULL;
119
120         char *busp = strchr(id, ':');
121         if (busp == devp) {
122                 filter->bus = strtoul(id, &endptr, 0);
123         } else {
124                 filter->bus = strtoul(busp+1, &endptr, 0);
125         }
126         if (endptr != funcp) return "invalid pci device string";
127         if (busp == devp) return NULL;
128
129         filter->domain = strtoul(id, &endptr, 0);
130         if (endptr != busp) return "invalid pci device string";
131
132         return NULL;
133 }
134
135 int pci_filter_match(struct pci_filter* pf, struct pci_dev* dev)
136 {
137         if ((pf->domain > -1) && (pf->domain != dev->domain))
138                 return 0;
139         if ((pf->bus > -1) && (pf->bus != dev->bus))
140                 return 0;
141         if ((pf->dev > -1) && (pf->dev != dev->dev))
142                 return 0;
143         if ((pf->func > -1) && (pf->func != dev->func))
144                 return 0;
145         if ((pf->vendor > -1) && (pf->vendor != dev->vendor_id))
146                 return 0;
147         if ((pf->device > -1) && (pf->device != dev->device_id))
148                 return 0;
149         return 1;
150 }
151
152 static struct pci_dev *pci_scan_single_bus(struct pci_dev *dev, int bus)
153 {
154         int devfn;
155         u32 val;
156         unsigned char hdr;
157
158         for (devfn = 0; devfn < 0x100; devfn++) {
159                 int func = devfn & 0x7;
160                 int slot = (devfn >> 3) & 0x1f;
161
162                 val = pci_read_config32(PCI_DEV(bus, slot, func),
163                                         REG_VENDOR_ID);
164
165                 if (val == 0xffffffff || val == 0x00000000 ||
166                     val == 0x0000ffff || val == 0xffff0000)
167                         continue;
168
169                 dev->next = malloc(sizeof(struct pci_dev));
170                 dev = dev->next;
171                 dev->domain = 0;
172                 dev->bus = bus;
173                 dev->dev = slot;
174                 dev->func = func;
175                 dev->vendor_id = val & 0xffff;
176                 dev->device_id = val >> 16;
177                 dev->next = 0;
178                 
179                 hdr = pci_read_config8(PCI_DEV(bus, slot, func),
180                                        REG_HEADER_TYPE);
181                 hdr &= 0x7F;
182
183                 if (hdr == HEADER_TYPE_BRIDGE || hdr == HEADER_TYPE_CARDBUS) {
184                         unsigned int busses;
185                         busses = pci_read_config32(PCI_DEV(bus, slot, func),
186                                                    REG_PRIMARY_BUS);
187                         busses = (busses >> 8) & 0xFF;
188
189                         /* Avoid recursion if the new bus is the same as
190                          * the old bus (insert lame The Who joke here) */
191
192                         if (busses != bus)
193                                 dev = pci_scan_single_bus(dev, busses);
194                 }
195         }
196
197         return dev;
198 }
199
200 void pci_scan_bus(struct pci_access* pacc)
201 {
202         struct pci_dev rootdev;
203         pci_scan_single_bus(&rootdev, 0);
204         pacc->devices = rootdev.next;
205 }
206
207 struct pci_dev *pci_get_dev(struct pci_access* pacc, u16 domain, u8 bus, u8 dev, u8 func)
208 {
209         struct pci_dev *cur = malloc(sizeof(*cur));
210         cur->domain = domain;
211         cur->bus = bus;
212         cur->dev = dev;
213         cur->func = func;
214         return cur;
215 }
216