xref: /aosp_15_r20/external/coreboot/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /***********************license start***********************************
2 * Copyright (c) 2003-2017  Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17 *
18 *   * Neither the name of Cavium Inc. nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include "libbdk-arch/bdk-csrs-ap.h"
44 #include "libbdk-arch/bdk-csrs-pccpf.h"
45 #include "libbdk-hal/bdk-ecam.h"
46 #include "libbdk-hal/device/bdk-device.h"
47 #include "libbdk-hal/bdk-config.h"
48 #include "libbdk-driver/bdk-driver.h"
49 #include "libbdk-hal/bdk-utils.h"
50 
51 static struct bdk_driver_s *driver_list = NULL;
52 
53 #define DEVICE_GROW 64
54 static bdk_device_t *device_list = NULL;
55 static int device_list_count = 0;
56 static int device_list_max = 0;
57 
58 /**
59  * Called to register a new driver with the bdk-device system. Drivers are probed
60  * and initialized as device are found for them. If devices have already been
61  * added before the driver was registered, the driver will be probed and
62  * initialized before this function returns.
63  *
64  * @param driver Driver functions
65  *
66  * @return Zero on success, negative on failure
67  */
bdk_device_add_driver(struct bdk_driver_s * driver)68 int bdk_device_add_driver(struct bdk_driver_s *driver)
69 {
70     driver->next = driver_list;
71     driver_list = driver;
72     BDK_TRACE(DEVICE, "Added driver for %08x\n", driver->id);
73     return 0;
74 }
75 
76 /**
77  * Lookup the correct driver for a device
78  *
79  * @param device Device to lookup
80  *
81  * @return Driver, or NULL on failure
82  */
lookup_driver(const bdk_device_t * device)83 static const bdk_driver_t *lookup_driver(const bdk_device_t *device)
84 {
85     const bdk_driver_t *drv = driver_list;
86     while (drv)
87     {
88         if (drv->id == device->id)
89             return drv;
90         drv = drv->next;
91     }
92     return NULL;
93 }
94 
95 /**
96  * Populate the fields of a new device from the ECAM
97  *
98  * @param device Device to populate
99  */
populate_device(bdk_device_t * device)100 static void populate_device(bdk_device_t *device)
101 {
102     /* The default name may be replaced by the driver with something easier to read */
103     snprintf(device->name, sizeof(device->name), "N%d.E%d:%d:%d.%d",
104         device->node, device->ecam, device->bus, device->dev, device->func);
105 
106     BDK_TRACE(DEVICE_SCAN, "%s: Populating device\n", device->name);
107 
108     /* Get the current chip ID and pass. We'll need this to fill in version
109        information for the device */
110     bdk_ap_midr_el1_t midr_el1;
111     BDK_MRS(MIDR_EL1, midr_el1.u);
112 
113     /* PCCPF_XXX_VSEC_SCTL[RID] with the revision of the chip,
114        read from fuses */
115     BDK_CSR_DEFINE(sctl, BDK_PCCPF_XXX_VSEC_SCTL);
116     sctl.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_VSEC_SCTL);
117     sctl.s.rid = midr_el1.s.revision | (midr_el1.s.variant<<3);
118     sctl.s.node = device->node; /* Program node bits */
119     sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
120     if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
121         sctl.s.ea = 0; /* EA is not supported on CN88XX pass 1.x */
122     else
123         sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
124     bdk_ecam_write32(device, BDK_PCCPF_XXX_VSEC_SCTL, sctl.u);
125 
126     /* Read the Device ID */
127     device->id = bdk_ecam_read32(device, BDK_PCCPF_XXX_ID);
128 
129     /* Read the Device Type so we know how to handle BARs */
130     bdk_pccpf_xxx_clsize_t clsize;
131     clsize.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CLSIZE);
132     int isbridge = (clsize.s.hdrtype & 0x7f) == 1;
133 
134     BDK_TRACE(DEVICE_SCAN, "%s: Device ID: 0x%08x%s\n", device->name, device->id,
135         (isbridge) ? " (Bridge)" : "");
136 
137     /* Loop through all the BARs */
138     int max_bar = (isbridge) ? BDK_PCCPF_XXX_BAR0U : BDK_PCCPF_XXX_BAR4U;
139     int bar = BDK_PCCPF_XXX_BAR0L;
140     unsigned guess_instance = 0;
141     while (bar <= max_bar)
142     {
143         int bar_index = (bar - BDK_PCCPF_XXX_BAR0L) / 8;
144         /* Read the BAR address and config bits [3:0] */
145         uint64_t address = bdk_ecam_read32(device, bar);
146         int ismem = !(address & 1);         /* Bit 0: 0 = mem, 1 = io */
147         int is64 = ismem && (address & 4);  /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
148         /* Bit 3: 1 = Is prefetchable. We on't care for now */
149 
150         /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
151            it is using Enhanced Allocation (EA) */
152         if (!is64)
153         {
154             BDK_TRACE(DEVICE_SCAN, "%s: BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
155             bar += 8;
156             continue;
157         }
158 
159         /* Get the upper part of 64bit BARs */
160         address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
161 
162         /* Write the bits to determine the size */
163         bdk_ecam_write32(device, bar, -1);
164         bdk_ecam_write32(device, bar + 4, -1);
165         uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
166         size_mask |= bdk_ecam_read32(device, bar);
167         /* Make sure the node bits are correct in the address */
168         address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
169         /* Restore address value */
170         bdk_ecam_write32(device, bar, address);
171         bdk_ecam_write32(device, bar + 4, address >> 32);
172 
173         /* Convert the size into a power of 2 bits */
174         int size_bits = bdk_dpop(~size_mask | 0xf);
175         if (size_bits <= 4)
176             size_bits = 0;
177 
178         /* Store the BAR info */
179         device->bar[bar_index].address = address & ~0xfull;
180         device->bar[bar_index].size2 = size_bits;
181         device->bar[bar_index].flags = address & 0xf;
182         BDK_TRACE(DEVICE_SCAN, "%s: BAR%d 0x%llx/%d flags=0x%x\n",
183             device->name, bar_index, device->bar[bar_index].address,
184             device->bar[bar_index].size2, device->bar[bar_index].flags);
185         /* Move to the next BAR */
186         bar += 8;
187     }
188 
189     /* Walk the PCI capabilities looking for PCIe support and EA headers */
190     BDK_TRACE(DEVICE_SCAN, "%s: Walking PCI capabilites\n", device->name);
191     int has_pcie = 0;
192     bdk_pccpf_xxx_cap_ptr_t cap_ptr;
193     cap_ptr.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CAP_PTR);
194     int cap_loc = cap_ptr.s.cp;
195     while (cap_loc)
196     {
197         uint32_t cap = bdk_ecam_read32(device, cap_loc);
198         int cap_id = cap & 0xff;
199         int cap_next = (cap >> 8) & 0xff;
200 
201         BDK_TRACE(DEVICE_SCAN, "%s:    PCI Capability 0x%02x ID:0x%02x Next:0x%02x\n",
202             device->name, cap_loc, cap_id, cap_next);
203 
204         if (cap_id == 0x10)
205         {
206             BDK_TRACE(DEVICE_SCAN, "%s:      PCIe\n", device->name);
207             has_pcie = 1;
208         }
209         else if (cap_id == 0x01)
210         {
211             BDK_TRACE(DEVICE_SCAN, "%s:      PCI Power Management Interface\n", device->name);
212             /* Do nothing for now */
213         }
214         else if (cap_id == 0x11)
215         {
216             bdk_pccpf_xxx_msix_cap_hdr_t msix_cap_hdr;
217             bdk_pccpf_xxx_msix_table_t msix_table;
218             bdk_pccpf_xxx_msix_pba_t msix_pba;
219             msix_cap_hdr.u = cap;
220             msix_table.u = bdk_ecam_read32(device, cap_loc + 4);
221             msix_pba.u = bdk_ecam_read32(device, cap_loc + 8);
222             BDK_TRACE(DEVICE_SCAN, "%s:      MSI-X Entries:%d, Func Mask:%d, Enable:%d\n",
223                 device->name, msix_cap_hdr.s.msixts + 1, msix_cap_hdr.s.funm, msix_cap_hdr.s.msixen);
224             BDK_TRACE(DEVICE_SCAN, "%s:          Table BAR%d, Offset:0x%x\n",
225                 device->name, msix_table.s.msixtbir, msix_table.s.msixtoffs * 8);
226             BDK_TRACE(DEVICE_SCAN, "%s:          PBA BAR%d, Offset:0x%x\n",
227                 device->name, msix_pba.s.msixpbir, msix_pba.s.msixpoffs * 8);
228         }
229         else if (cap_id == 0x05)
230         {
231             BDK_TRACE(DEVICE_SCAN, "%s:      MSI\n", device->name);
232             /* Do nothing for now */
233         }
234         else if (cap_id == 0x14)
235         {
236             bdk_pccpf_xxx_ea_cap_hdr_t ea_cap_hdr;
237             ea_cap_hdr.u = cap;
238             cap_loc += 4;
239             BDK_TRACE(DEVICE_SCAN, "%s:      Enhanced Allocation, %d entries\n",
240                 device->name, ea_cap_hdr.s.num_entries);
241             if (isbridge)
242             {
243                 cap = bdk_ecam_read32(device, cap_loc);
244                 cap_loc += 4;
245                 int fixed_secondary_bus = cap & 0xff;
246                 int fixed_subordinate_bus = cap & 0xff;
247                 BDK_TRACE(DEVICE_SCAN, "%s:      Fixed Secondary Bus:0x%02x Fixed Subordinate Bus:0x%02x\n",
248                     device->name, fixed_secondary_bus, fixed_subordinate_bus);
249             }
250             for (int entry = 0; entry < ea_cap_hdr.s.num_entries; entry++)
251             {
252                 union bdk_pcc_ea_entry_s ea_entry;
253                 memset(&ea_entry, 0, sizeof(ea_entry));
254                 uint32_t *ptr = (uint32_t *)&ea_entry;
255                 *ptr++ = bdk_ecam_read32(device, cap_loc);
256 #if __BYTE_ORDER == __BIG_ENDIAN
257                 /* For big endian we actually need the previous data
258                    shifted 32 bits */
259                 *ptr = ptr[-1];
260 #endif
261                 asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
262                 int entry_size = ea_entry.s.entry_size;
263                 for (int i = 0; i < entry_size; i++)
264                 {
265                     *ptr++ = bdk_ecam_read32(device, cap_loc + 4*i + 4);
266                 }
267 #if __BYTE_ORDER == __BIG_ENDIAN
268                 /* The upper and lower 32bits need to be swapped */
269                 ea_entry.u[0] = (ea_entry.u[0] >> 32) | (ea_entry.u[0] << 32);
270                 ea_entry.u[1] = (ea_entry.u[1] >> 32) | (ea_entry.u[1] << 32);
271                 ea_entry.u[2] = (ea_entry.u[2] >> 32) | (ea_entry.u[2] << 32);
272 #endif
273                 asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
274                 BDK_TRACE(DEVICE_SCAN, "%s:      Enable:%d Writeable:%d Secondary Prop:0x%02x Primary Prop:0x%02x BEI:%d Size:%d\n",
275                     device->name, ea_entry.s.enable, ea_entry.s.w, ea_entry.s.sec_prop, ea_entry.s.pri_prop, ea_entry.s.bei, ea_entry.s.entry_size);
276                 if (ea_entry.s.entry_size > 0)
277                 {
278                     BDK_TRACE(DEVICE_SCAN, "%s:        Base:0x%08x 64bit:%d\n",
279                         device->name, ea_entry.s.basel << 2, ea_entry.s.base64);
280                 }
281                 if (ea_entry.s.entry_size > 1)
282                 {
283                     BDK_TRACE(DEVICE_SCAN, "%s:        MaxOffset:0x%08x 64bit:%d\n",
284                         device->name, (ea_entry.s.offsetl << 2) | 3, ea_entry.s.offset64);
285                 }
286                 if (ea_entry.s.entry_size > 2)
287                 {
288                     BDK_TRACE(DEVICE_SCAN, "%s:        BaseUpper:0x%08x\n",
289                         device->name, ea_entry.s.baseh);
290                 }
291                 if (ea_entry.s.entry_size > 3)
292                 {
293                     BDK_TRACE(DEVICE_SCAN, "%s:        MaxOffsetUpper:0x%08x\n",
294                         device->name, ea_entry.s.offseth);
295                 }
296                 if (ea_entry.s.enable)
297                 {
298                     uint64_t base = (uint64_t)ea_entry.s.baseh << 32;
299                     base |= (uint64_t)ea_entry.s.basel << 2;
300                     /* Make sure the node bits are correct in the address */
301                     base = (base & ~(3UL << 44)) | ((uint64_t)device->node << 44);
302                     uint64_t offset = (uint64_t)ea_entry.s.offseth << 32;
303                     offset |= ((uint64_t)ea_entry.s.offsetl << 2) | 3;
304                     switch (ea_entry.s.bei)
305                     {
306                         case 0: /* BAR 0 */
307                         case 2: /* BAR 1 */
308                         case 4: /* BAR 2 */
309                         {
310                             int bar_index = ea_entry.s.bei/2;
311                             device->bar[bar_index].address = base;
312                             device->bar[bar_index].size2 = bdk_dpop(offset);
313                             device->bar[bar_index].flags = ea_entry.s.base64 << 2;
314                             BDK_TRACE(DEVICE_SCAN, "%s:        Updated BAR%d 0x%llx/%d flags=0x%x\n",
315                                 device->name, bar_index, device->bar[bar_index].address,
316                                 device->bar[bar_index].size2, device->bar[bar_index].flags);
317                             if (0 == ea_entry.s.bei) {
318                                 /* PEMs eg PCIEEP and PCIERC do not have instance id
319                                 ** We can calculate it for PCIERC based on BAR0 allocation.
320                                 ** PCIEEP will be dropped by probe
321                                 */
322                                 guess_instance = (device->bar[bar_index].address >> 24) & 7;
323                             }
324                             break;
325                         }
326                         case 9: /* SR-IOV BAR 0 */
327                         case 11: /* SR-IOV BAR 1 */
328                         case 13: /* SR-IOV BAR 2 */
329                             // FIXME
330                             break;
331                     }
332                 }
333                 cap_loc += ea_entry.s.entry_size * 4 + 4;
334             }
335         }
336         else
337         {
338             /* Unknown PCI capability */
339             bdk_warn("%s: ECAM device unknown PCI capability 0x%x\n", device->name, cap_id);
340         }
341         cap_loc = cap_next;
342     }
343 
344     /* Walk the PCIe capabilities looking for instance header */
345     if (has_pcie)
346     {
347         BDK_TRACE(DEVICE_SCAN, "%s: Walking PCIe capabilites\n", device->name);
348         cap_loc = 0x100;
349         while (cap_loc)
350         {
351             uint32_t cap = bdk_ecam_read32(device, cap_loc);
352             int cap_id = cap & 0xffff;
353             int cap_ver = (cap >> 16) & 0xf;
354             int cap_next = cap >> 20;
355             BDK_TRACE(DEVICE_SCAN, "%s:    PCIe Capability 0x%03x ID:0x%04x Version:0x%x Next:0x%03x\n",
356                 device->name, cap_loc, cap_id, cap_ver, cap_next);
357             if (cap_id == 0xe)
358             {
359                 /* ARI. Do nothing for now */
360                 BDK_TRACE(DEVICE_SCAN, "%s:      ARI\n", device->name);
361             }
362             else if (cap_id == 0xb)
363             {
364                 /* Vendor specific*/
365                 int vsec_id = bdk_ecam_read32(device, cap_loc + 4);
366                 int vsec_id_id = vsec_id & 0xffff;
367                 int vsec_id_rev = (vsec_id >> 16) & 0xf;
368                 int vsec_id_len = vsec_id >> 20;
369                 BDK_TRACE(DEVICE_SCAN, "%s:      Vendor ID: 0x%04x Rev: 0x%x Size 0x%03x\n",
370                     device->name, vsec_id_id, vsec_id_rev, vsec_id_len);
371                 switch (vsec_id_id)
372                 {
373                     case 0x0001: /* RAS Data Path */
374                         BDK_TRACE(DEVICE_SCAN, "%s:      Vendor RAS Data Path\n", device->name);
375                         break;
376 
377                     case 0x0002: /* RAS DES */
378                         BDK_TRACE(DEVICE_SCAN, "%s:      Vendor RAS DES\n", device->name);
379                         break;
380 
381                     case 0x00a0: /* Cavium common */
382                     case 0x00a1: /* Cavium CN88XX */
383                     case 0x00a2: /* Cavium CN81XX */
384                     case 0x00a3: /* Cavium CN83XX */
385                         if ((vsec_id_rev == 1) || (vsec_id_rev == 2))
386                         {
387                             int vsec_ctl = bdk_ecam_read32(device, cap_loc + 8);
388                             int vsec_ctl_inst_num = vsec_ctl & 0xff;
389                             int vsec_ctl_subnum = (vsec_ctl >> 8) & 0xff;
390                             BDK_TRACE(DEVICE_SCAN, "%s:        Cavium Instance: 0x%02x Static Bus: 0x%02x\n",
391                                 device->name, vsec_ctl_inst_num, vsec_ctl_subnum);
392                             int vsec_sctl = bdk_ecam_read32(device, cap_loc + 12);
393                             int vsec_sctl_rid = (vsec_sctl >> 16) & 0xff;
394                             if (vsec_id_rev == 2)
395                             {
396                                 int vsec_sctl_pi = (vsec_sctl >> 24) & 0xff; /* Only in Rev 2 */
397                                 BDK_TRACE(DEVICE_SCAN, "%s:        Revision ID: 0x%02x Programming Interface: 0x%02x\n",
398                                     device->name, vsec_sctl_rid, vsec_sctl_pi);
399                             }
400                             else
401                             {
402                                 BDK_TRACE(DEVICE_SCAN, "%s:        Revision ID: 0x%02x\n",
403                                     device->name, vsec_sctl_rid);
404                             }
405                             /* Record the device instance */
406                             device->instance = vsec_ctl_inst_num;
407                         }
408                         else
409                         {
410                             bdk_warn("%s: ECAM device Unknown Cavium extension revision\n", device->name);
411                         }
412                         break;
413 
414                     default: /* Unknown Vendor extension */
415                         bdk_warn("%s: ECAM device unknown vendor extension ID 0x%x\n", device->name, vsec_id_id);
416                         break;
417                 }
418             }
419             else if (cap_id == 0x10)
420             {
421                 /* Single Root I/O Virtualization (SR-IOV) */
422                 BDK_TRACE(DEVICE_SCAN, "%s:      SR-IOV\n", device->name);
423                 /* Loop through all the SR-IOV BARs */
424                 bar = cap_loc + 0x24;
425                 while (bar <= (cap_loc + 0x3c))
426                 {
427                     int bar_index = (bar - 0x24 - cap_loc) / 8;
428                     /* Read the BAR address and config bits [3:0] */
429                     uint64_t address = bdk_ecam_read32(device, bar);
430                     int ismem = !(address & 1);         /* Bit 0: 0 = mem, 1 = io */
431                     int is64 = ismem && (address & 4);  /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
432                     /* Bit 3: 1 = Is prefetchable. We don't care for now */
433 
434                     /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
435                        it is using Enhanced Allocation (EA) */
436                     if (!is64)
437                     {
438                         BDK_TRACE(DEVICE_SCAN, "%s:        SR-IOV BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
439                         bar += 8;
440                         continue;
441                     }
442 
443                     /* Get the upper part of 64bit BARs */
444                     address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
445 
446                     /* Write the bits to determine the size */
447                     bdk_ecam_write32(device, bar, -1);
448                     bdk_ecam_write32(device, bar + 4, -1);
449                     uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
450                     size_mask |= bdk_ecam_read32(device, bar);
451                     /* Make sure the node bits are correct in the address */
452                     address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
453                     /* Restore address value */
454                     bdk_ecam_write32(device, bar, address);
455                     bdk_ecam_write32(device, bar + 4, address >> 32);
456 
457                     /* Convert the size into a power of 2 bits */
458                     int size_bits = bdk_dpop(size_mask | 0xf);
459                     if (size_bits <= 4)
460                         size_bits = 0;
461 
462                     BDK_TRACE(DEVICE_SCAN, "%s:        SR-IOV BAR%d 0x%llx/%d flags=0x%llx\n",
463                         device->name, bar_index, address & ~0xfull,
464                         size_bits, address & 0xf);
465                     /* Move to the next BAR */
466                     bar += 8;
467                 }
468             }
469             else if (cap_id == 0x01)
470             {
471                 /* Advanced Error Reporting Capability */
472                 BDK_TRACE(DEVICE_SCAN, "%s:      Advanced Error Reporting\n", device->name);
473             }
474             else if (cap_id == 0x19)
475             {
476                 /* Secondary PCI Express Extended Capability */
477                 BDK_TRACE(DEVICE_SCAN, "%s:      Secondary PCI Express Extended\n", device->name);
478             }
479             else if (cap_id == 0x15)
480             {
481                 /* PCI Express Resizable BAR (RBAR) Capability */
482                 BDK_TRACE(DEVICE_SCAN, "%s:      PCI Express Resizable BAR (RBAR)\n", device->name);
483             }
484             else if (cap_id == 0x0d)
485             {
486                 /* Extended access control := ACS Extended Capability */
487                 BDK_TRACE(DEVICE_SCAN, "%s:      ACS\n", device->name);
488             }
489             else
490             {
491                 /* Unknown PCIe capability */
492                 bdk_warn("%s: ECAM device unknown PCIe capability 0x%x\n", device->name, cap_id);
493             }
494             cap_loc = cap_next;
495         }
496     }
497     else
498     {
499         bdk_error("%s: ECAM device didn't have a PCIe capability\n", device->name);
500     }
501     if (BDK_NO_DEVICE_INSTANCE == device->instance) {
502         device->instance = guess_instance;
503     }
504     BDK_TRACE(DEVICE_SCAN, "%s: Device populated\n", device->name);
505 }
506 
507 /**
508  * Called by the ECAM code whan a new device is detected in the system
509  *
510  * @param node   Node the ECAM is on
511  * @param ecam   ECAM the device is on
512  * @param bus    Bus number for the device
513  * @param dev    Device number
514  * @param func   Function number
515  *
516  * @return Zero on success, negative on failure
517  */
bdk_device_add(bdk_node_t node,int ecam,int bus,int dev,int func)518 int bdk_device_add(bdk_node_t node, int ecam, int bus, int dev, int func)
519 {
520     if (device_list_count == device_list_max)
521     {
522         int grow = device_list_max + DEVICE_GROW;
523         bdk_device_t *tmp = malloc(grow * sizeof(bdk_device_t));
524         if (tmp)
525             memcpy(tmp, device_list, device_list_max * sizeof(bdk_device_t));
526         free(device_list);
527         if (tmp == NULL)
528         {
529             bdk_error("bdk-device: Failed to allocate space for device\n");
530             return -1;
531         }
532         device_list = tmp;
533         device_list_max = grow;
534     }
535 
536     bdk_device_t *device = &device_list[device_list_count++];
537     memset(device, 0, sizeof(*device));
538 
539     device->state = BDK_DEVICE_STATE_NOT_PROBED;
540     device->node = node;
541     device->ecam = ecam;
542     device->bus = bus;
543     device->dev = dev;
544     device->func = func;
545     device->instance = BDK_NO_DEVICE_INSTANCE;
546     populate_device(device);
547 
548     const bdk_driver_t *drv = lookup_driver(device);
549     if (drv)
550         BDK_TRACE(DEVICE, "%s: Added device\n", device->name);
551     else
552         BDK_TRACE(DEVICE, "%s: Added device without driver (0x%08x)\n", device->name, device->id);
553     return 0;
554 }
555 
556 /**
557  * Rename a device. Called by driver to give devices friendly names
558  *
559  * @param device Device to rename
560  * @param format Printf style format string
561  */
bdk_device_rename(bdk_device_t * device,const char * format,...)562 void bdk_device_rename(bdk_device_t *device, const char *format, ...)
563 {
564     char tmp[sizeof(device->name)];
565     va_list args;
566     va_start(args, format);
567     vsnprintf(tmp, sizeof(tmp), format, args);
568     va_end(args);
569     tmp[sizeof(tmp) - 1] = 0;
570     BDK_TRACE(DEVICE, "%s: Renamed to %s\n", device->name, tmp);
571     strcpy(device->name, tmp);
572 }
573 
574 /**
575  * Called by the ECAM code once all devices have been added
576  *
577  * @return Zero on success, negative on failure
578  */
bdk_device_init(void)579 int bdk_device_init(void)
580 {
581     /* Probe all devices first */
582     for (int i = 0; i < device_list_count; i++)
583     {
584         bdk_device_t *dev = &device_list[i];
585         const bdk_driver_t *drv = lookup_driver(dev);
586         if (drv == NULL)
587             continue;
588         if (dev->state == BDK_DEVICE_STATE_NOT_PROBED)
589         {
590             BDK_TRACE(DEVICE, "%s: Probing\n", dev->name);
591             if (drv->probe(dev))
592             {
593                 BDK_TRACE(DEVICE, "%s: Probe failed\n", dev->name);
594                 dev->state = BDK_DEVICE_STATE_PROBE_FAIL;
595             }
596             else
597             {
598                 BDK_TRACE(DEVICE, "%s: Probe complete\n", dev->name);
599                 dev->state = BDK_DEVICE_STATE_PROBED;
600             }
601         }
602     }
603 
604     /* Do init() after all the probes. See comments in top of bdk-device.h */
605     for (int i = 0; i < device_list_count; i++)
606     {
607         bdk_device_t *dev = &device_list[i];
608         const bdk_driver_t *drv = lookup_driver(dev);
609         if (drv == NULL)
610             continue;
611         if (dev->state == BDK_DEVICE_STATE_PROBED)
612         {
613             BDK_TRACE(DEVICE, "%s: Initializing\n", dev->name);
614             if (drv->init(dev))
615             {
616                 BDK_TRACE(DEVICE, "%s: Init failed\n", dev->name);
617                 dev->state = BDK_DEVICE_STATE_INIT_FAIL;
618             }
619             else
620             {
621                 BDK_TRACE(DEVICE, "%s: Init complete\n", dev->name);
622                 dev->state = BDK_DEVICE_STATE_READY;
623             }
624         }
625     }
626     return 0;
627 }
628 
629 /**
630  * Lookup a device by ECAM ID and internal instance number. This can be used by
631  * one device to find a handle to an associated device. For example, PKI would
632  * use this function to get a handle to the FPA.
633  *
634  * @param node     Node to lookup for
635  * @param id       ECAM ID
636  * @param instance Cavium internal instance number
637  *
638  * @return Device pointer, or NULL if the device isn't found
639  */
bdk_device_lookup(bdk_node_t node,uint32_t id,int instance)640 const bdk_device_t *bdk_device_lookup(bdk_node_t node, uint32_t id, int instance)
641 {
642     for (int i = 0; i < device_list_count; i++)
643     {
644         bdk_device_t *dev = &device_list[i];
645         if ((dev->node == node) && (dev->id == id) && (dev->instance == instance))
646             return dev;
647     }
648     BDK_TRACE(DEVICE, "No device found for node %d, ID %08x, instance %d\n", node, id, instance);
649     return NULL;
650 }
651 
652 /**
653  * Read from a device BAR
654  *
655  * @param device Device to read from
656  * @param bar    Which BAR to read from (0-3)
657  * @param size   Size of the read
658  * @param offset Offset into the BAR
659  *
660  * @return Value read
661  */
bdk_bar_read(const bdk_device_t * device,int bar,int size,uint64_t offset)662 uint64_t bdk_bar_read(const bdk_device_t *device, int bar, int size, uint64_t offset)
663 {
664     uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
665     address += device->bar[bar/2].address;
666     if (offset+size > (1ULL << device->bar[bar/2].size2)) {
667         /* The CSR address passed in offset doesn't contain the node number. Copy it
668            from the BAR address */
669         offset |= address & (0x3ull << 44);
670         if (address != offset)
671             bdk_fatal("BAR read address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
672     }
673     switch (size)
674     {
675         case 1:
676             return bdk_read64_uint8(address);
677         case 2:
678             return bdk_le16_to_cpu(bdk_read64_uint16(address));
679         case 4:
680             return bdk_le32_to_cpu(bdk_read64_uint32(address));
681         case 8:
682             return bdk_le64_to_cpu(bdk_read64_uint64(address));
683     }
684     bdk_fatal("%s: Unexpected read size %d\n", device->name, size);
685 }
686 
687 /**
688  * Write to a device BAR
689  *
690  * @param device Device to write to
691  * @param bar    Which BAR to read from (0-3)
692  * @param size   Size of the write
693  * @param offset Offset into the BAR
694  * @param value  Value to write
695  */
bdk_bar_write(const bdk_device_t * device,int bar,int size,uint64_t offset,uint64_t value)696 void bdk_bar_write(const bdk_device_t *device, int bar, int size, uint64_t offset, uint64_t value)
697 {
698     uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
699     address += device->bar[bar/2].address;
700     if (offset+size > (1ULL << device->bar[bar/2].size2)) {
701         /* The CSR address passed in offset doesn't contain the node number. Copy it
702            from the BAR address */
703         offset |= address & (0x3ull << 44);
704         if (address != offset)
705             bdk_fatal("BAR write address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
706     }
707     switch (size)
708     {
709         case 1:
710             bdk_write64_uint8(address, value);
711             return;
712         case 2:
713             bdk_write64_uint16(address, bdk_cpu_to_le16(value));
714             return;
715         case 4:
716             bdk_write64_uint32(address, bdk_cpu_to_le32(value));
717             return;
718         case 8:
719             bdk_write64_uint64(address, bdk_cpu_to_le64(value));
720             return;
721     }
722     bdk_fatal("%s: Unexpected write size %d\n", device->name, size);
723 }
724