129cb31c9SChaoyong He /* SPDX-License-Identifier: BSD-3-Clause 229cb31c9SChaoyong He * Copyright (c) 2023 Corigine, Inc. 329cb31c9SChaoyong He * All rights reserved. 429cb31c9SChaoyong He */ 529cb31c9SChaoyong He 629cb31c9SChaoyong He #include "nfp_common_pci.h" 729cb31c9SChaoyong He 829cb31c9SChaoyong He #include <string.h> 929cb31c9SChaoyong He 1029cb31c9SChaoyong He #include <rte_class.h> 1129cb31c9SChaoyong He #include <rte_devargs.h> 1229cb31c9SChaoyong He #include <rte_kvargs.h> 1329cb31c9SChaoyong He 1429cb31c9SChaoyong He #include "nfp_common_log.h" 1529cb31c9SChaoyong He 1629cb31c9SChaoyong He /* Reported driver name. */ 1729cb31c9SChaoyong He #define NFP_PCI_DRIVER_NAME "nfp_common_pci" 1829cb31c9SChaoyong He 1929cb31c9SChaoyong He static struct rte_pci_driver nfp_common_pci_driver; 2029cb31c9SChaoyong He 2129cb31c9SChaoyong He /* PCI ID table is build dynamically based on registered nfp drivers. */ 2229cb31c9SChaoyong He static struct rte_pci_id *nfp_pci_id_table; 2329cb31c9SChaoyong He 2429cb31c9SChaoyong He /* Head of list of drivers. */ 2529cb31c9SChaoyong He static TAILQ_HEAD(nfp_drivers, nfp_class_driver) nfp_drivers_list = 2629cb31c9SChaoyong He TAILQ_HEAD_INITIALIZER(nfp_drivers_list); 2729cb31c9SChaoyong He 2829cb31c9SChaoyong He static bool nfp_common_initialized; 2929cb31c9SChaoyong He 3029cb31c9SChaoyong He static const struct { 3129cb31c9SChaoyong He const char *name; 3229cb31c9SChaoyong He enum nfp_class drv_class; 3329cb31c9SChaoyong He } nfp_classes[] = { 3429cb31c9SChaoyong He { .name = "eth", .drv_class = NFP_CLASS_ETH }, 357f11d166SChaoyong He { .name = "vdpa", .drv_class = NFP_CLASS_VDPA }, 3629cb31c9SChaoyong He }; 3729cb31c9SChaoyong He 3829cb31c9SChaoyong He static enum nfp_class 3929cb31c9SChaoyong He nfp_class_name_to_value(const char *class_name) 4029cb31c9SChaoyong He { 4129cb31c9SChaoyong He uint32_t i; 4229cb31c9SChaoyong He 4329cb31c9SChaoyong He for (i = 0; i < RTE_DIM(nfp_classes); i++) { 4429cb31c9SChaoyong He if (strcmp(class_name, nfp_classes[i].name) == 0) 4529cb31c9SChaoyong He return nfp_classes[i].drv_class; 4629cb31c9SChaoyong He } 4729cb31c9SChaoyong He 4829cb31c9SChaoyong He return NFP_CLASS_INVALID; 4929cb31c9SChaoyong He } 5029cb31c9SChaoyong He 5129cb31c9SChaoyong He static uint32_t 5229cb31c9SChaoyong He nfp_pci_id_table_size_get(const struct rte_pci_id *id_table) 5329cb31c9SChaoyong He { 5429cb31c9SChaoyong He uint32_t table_size; 5529cb31c9SChaoyong He 5629cb31c9SChaoyong He if (id_table == NULL) 5729cb31c9SChaoyong He return 0; 5829cb31c9SChaoyong He 5929cb31c9SChaoyong He for (table_size = 0; id_table->vendor_id != 0; id_table++) 6029cb31c9SChaoyong He table_size++; 6129cb31c9SChaoyong He 6229cb31c9SChaoyong He return table_size; 6329cb31c9SChaoyong He } 6429cb31c9SChaoyong He 6529cb31c9SChaoyong He static bool 6629cb31c9SChaoyong He nfp_pci_id_exists(const struct rte_pci_id *id, 6729cb31c9SChaoyong He const struct rte_pci_id *table, 6829cb31c9SChaoyong He uint32_t next_idx) 6929cb31c9SChaoyong He { 7029cb31c9SChaoyong He uint32_t i; 7129cb31c9SChaoyong He 7229cb31c9SChaoyong He if (next_idx == 0) 7329cb31c9SChaoyong He return false; 7429cb31c9SChaoyong He 7529cb31c9SChaoyong He for (i = 0; i < next_idx; i++) { 7629cb31c9SChaoyong He if (id->device_id == table[i].device_id && 7729cb31c9SChaoyong He id->vendor_id == table[i].vendor_id && 7829cb31c9SChaoyong He id->subsystem_vendor_id == table[i].subsystem_vendor_id && 7929cb31c9SChaoyong He id->subsystem_device_id == table[i].subsystem_device_id) 8029cb31c9SChaoyong He return true; 8129cb31c9SChaoyong He } 8229cb31c9SChaoyong He 8329cb31c9SChaoyong He return false; 8429cb31c9SChaoyong He } 8529cb31c9SChaoyong He 8629cb31c9SChaoyong He static void 8729cb31c9SChaoyong He nfp_pci_id_insert(struct rte_pci_id *new_table, 8829cb31c9SChaoyong He uint32_t *next_idx, 8929cb31c9SChaoyong He const struct rte_pci_id *id_table) 9029cb31c9SChaoyong He { 9129cb31c9SChaoyong He if (id_table == NULL) 9229cb31c9SChaoyong He return; 9329cb31c9SChaoyong He 9429cb31c9SChaoyong He /* Add non duplicate entries to new table. */ 9529cb31c9SChaoyong He for (; id_table->vendor_id != 0; id_table++) { 9629cb31c9SChaoyong He if (!nfp_pci_id_exists(id_table, new_table, *next_idx)) { 9729cb31c9SChaoyong He new_table[*next_idx] = *id_table; 9829cb31c9SChaoyong He (*next_idx)++; 9929cb31c9SChaoyong He } 10029cb31c9SChaoyong He } 10129cb31c9SChaoyong He } 10229cb31c9SChaoyong He 10329cb31c9SChaoyong He static int 10429cb31c9SChaoyong He nfp_pci_id_table_update(const struct rte_pci_id *driver_id_table) 10529cb31c9SChaoyong He { 10629cb31c9SChaoyong He uint32_t i = 0; 10729cb31c9SChaoyong He uint32_t num_ids = 0; 10829cb31c9SChaoyong He struct rte_pci_id *old_table; 10929cb31c9SChaoyong He const struct rte_pci_id *id_iter; 11029cb31c9SChaoyong He struct rte_pci_id *updated_table; 11129cb31c9SChaoyong He 11229cb31c9SChaoyong He old_table = nfp_pci_id_table; 11329cb31c9SChaoyong He if (old_table != NULL) 11429cb31c9SChaoyong He num_ids = nfp_pci_id_table_size_get(old_table); 11529cb31c9SChaoyong He num_ids += nfp_pci_id_table_size_get(driver_id_table); 11629cb31c9SChaoyong He 11729cb31c9SChaoyong He /* Increase size by one for the termination entry of vendor_id = 0. */ 11829cb31c9SChaoyong He num_ids += 1; 11929cb31c9SChaoyong He updated_table = calloc(num_ids, sizeof(struct rte_pci_id)); 12029cb31c9SChaoyong He if (updated_table == NULL) 12129cb31c9SChaoyong He return -ENOMEM; 12229cb31c9SChaoyong He 12329cb31c9SChaoyong He if (old_table == NULL) { 12429cb31c9SChaoyong He /* Copy the first driver's ID table. */ 12529cb31c9SChaoyong He for (id_iter = driver_id_table; id_iter[i].vendor_id != 0; i++) 12629cb31c9SChaoyong He updated_table[i] = id_iter[i]; 12729cb31c9SChaoyong He } else { 12829cb31c9SChaoyong He /* First copy existing table entries. */ 12929cb31c9SChaoyong He for (id_iter = old_table; id_iter[i].vendor_id != 0; i++) 13029cb31c9SChaoyong He updated_table[i] = id_iter[i]; 13129cb31c9SChaoyong He /* New id to be added at the end of current ID table. */ 13229cb31c9SChaoyong He nfp_pci_id_insert(updated_table, &i, driver_id_table); 13329cb31c9SChaoyong He 13429cb31c9SChaoyong He free(old_table); 13529cb31c9SChaoyong He } 13629cb31c9SChaoyong He 13729cb31c9SChaoyong He /* Terminate table with empty entry. */ 13829cb31c9SChaoyong He updated_table[i].vendor_id = 0; 13929cb31c9SChaoyong He nfp_pci_id_table = updated_table; 14029cb31c9SChaoyong He nfp_common_pci_driver.id_table = nfp_pci_id_table; 14129cb31c9SChaoyong He 14229cb31c9SChaoyong He return 0; 14329cb31c9SChaoyong He } 14429cb31c9SChaoyong He 14529cb31c9SChaoyong He static int 14629cb31c9SChaoyong He nfp_kvarg_dev_class_handler(__rte_unused const char *key, 14729cb31c9SChaoyong He const char *class_str, 14829cb31c9SChaoyong He void *opaque) 14929cb31c9SChaoyong He { 15029cb31c9SChaoyong He enum nfp_class *dev_class = opaque; 15129cb31c9SChaoyong He 15229cb31c9SChaoyong He if (class_str == NULL) 15329cb31c9SChaoyong He return *dev_class; 15429cb31c9SChaoyong He 15529cb31c9SChaoyong He *dev_class = nfp_class_name_to_value(class_str); 15629cb31c9SChaoyong He 15729cb31c9SChaoyong He return 0; 15829cb31c9SChaoyong He } 15929cb31c9SChaoyong He 16029cb31c9SChaoyong He static enum nfp_class 16129cb31c9SChaoyong He nfp_parse_class_options(const struct rte_devargs *devargs) 16229cb31c9SChaoyong He { 16329cb31c9SChaoyong He struct rte_kvargs *kvargs; 16429cb31c9SChaoyong He enum nfp_class dev_class = NFP_CLASS_ETH; 16529cb31c9SChaoyong He 16629cb31c9SChaoyong He if (devargs == NULL) 16729cb31c9SChaoyong He return dev_class; 16829cb31c9SChaoyong He 16929cb31c9SChaoyong He kvargs = rte_kvargs_parse(devargs->args, NULL); 17029cb31c9SChaoyong He if (kvargs == NULL) 17129cb31c9SChaoyong He return dev_class; 17229cb31c9SChaoyong He 1733977d07dSChengwen Feng rte_kvargs_process_opt(kvargs, RTE_DEVARGS_KEY_CLASS, 17429cb31c9SChaoyong He nfp_kvarg_dev_class_handler, &dev_class); 17529cb31c9SChaoyong He 17629cb31c9SChaoyong He rte_kvargs_free(kvargs); 17729cb31c9SChaoyong He 17829cb31c9SChaoyong He return dev_class; 17929cb31c9SChaoyong He } 18029cb31c9SChaoyong He 18129cb31c9SChaoyong He static int 18229cb31c9SChaoyong He nfp_drivers_probe(struct rte_pci_device *pci_dev, 18329cb31c9SChaoyong He enum nfp_class class) 18429cb31c9SChaoyong He { 18529cb31c9SChaoyong He int32_t ret = 0; 18629cb31c9SChaoyong He struct nfp_class_driver *driver; 18729cb31c9SChaoyong He 18829cb31c9SChaoyong He TAILQ_FOREACH(driver, &nfp_drivers_list, next) { 18929cb31c9SChaoyong He if (driver->drv_class != class) 19029cb31c9SChaoyong He continue; 19129cb31c9SChaoyong He 19229cb31c9SChaoyong He ret = driver->probe(pci_dev); 19329cb31c9SChaoyong He if (ret < 0) { 194*b6de4353SZerun Fu PMD_DRV_LOG(ERR, "Failed to load driver %s.", driver->name); 19529cb31c9SChaoyong He return ret; 19629cb31c9SChaoyong He } 19729cb31c9SChaoyong He } 19829cb31c9SChaoyong He 19929cb31c9SChaoyong He return 0; 20029cb31c9SChaoyong He } 20129cb31c9SChaoyong He 20229cb31c9SChaoyong He static int 20329cb31c9SChaoyong He nfp_common_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, 20429cb31c9SChaoyong He struct rte_pci_device *pci_dev) 20529cb31c9SChaoyong He { 20629cb31c9SChaoyong He enum nfp_class class; 20729cb31c9SChaoyong He struct rte_device *eal_dev = &pci_dev->device; 20829cb31c9SChaoyong He 209f6272c7aSZerun Fu PMD_DRV_LOG(INFO, "Probe device %s.", eal_dev->name); 21029cb31c9SChaoyong He 21129cb31c9SChaoyong He class = nfp_parse_class_options(eal_dev->devargs); 21229cb31c9SChaoyong He if (class == NFP_CLASS_INVALID) { 213*b6de4353SZerun Fu PMD_DRV_LOG(ERR, "Unsupported nfp class type: %s.", 21429cb31c9SChaoyong He eal_dev->devargs->args); 21529cb31c9SChaoyong He return -ENOTSUP; 21629cb31c9SChaoyong He } 21729cb31c9SChaoyong He 21829cb31c9SChaoyong He return nfp_drivers_probe(pci_dev, class); 21929cb31c9SChaoyong He } 22029cb31c9SChaoyong He 22129cb31c9SChaoyong He static int 22229cb31c9SChaoyong He nfp_common_pci_remove(__rte_unused struct rte_pci_device *pci_dev) 22329cb31c9SChaoyong He { 22429cb31c9SChaoyong He return 0; 22529cb31c9SChaoyong He } 22629cb31c9SChaoyong He 22729cb31c9SChaoyong He static struct rte_pci_driver nfp_common_pci_driver = { 22829cb31c9SChaoyong He .driver = { 22929cb31c9SChaoyong He .name = NFP_PCI_DRIVER_NAME, 23029cb31c9SChaoyong He }, 23129cb31c9SChaoyong He .probe = nfp_common_pci_probe, 23229cb31c9SChaoyong He .remove = nfp_common_pci_remove, 23329cb31c9SChaoyong He }; 23429cb31c9SChaoyong He 23529cb31c9SChaoyong He static void 23629cb31c9SChaoyong He nfp_common_init(void) 23729cb31c9SChaoyong He { 23829cb31c9SChaoyong He const struct rte_pci_id empty_table[] = { 23929cb31c9SChaoyong He { 24029cb31c9SChaoyong He .vendor_id = 0 24129cb31c9SChaoyong He }, 24229cb31c9SChaoyong He }; 24329cb31c9SChaoyong He 24429cb31c9SChaoyong He if (nfp_common_initialized) 24529cb31c9SChaoyong He return; 24629cb31c9SChaoyong He 24729cb31c9SChaoyong He /* 24829cb31c9SChaoyong He * All the constructor of NFP PMDs run at same priority. So any of the PMD 24929cb31c9SChaoyong He * including this one can register the PCI table first. If any other 25029cb31c9SChaoyong He * PMD(s) have registered the PCI ID table, no need to register an empty 25129cb31c9SChaoyong He * default one. 25229cb31c9SChaoyong He */ 25329cb31c9SChaoyong He if (nfp_pci_id_table == NULL && nfp_pci_id_table_update(empty_table) != 0) 25429cb31c9SChaoyong He return; 25529cb31c9SChaoyong He 25629cb31c9SChaoyong He rte_pci_register(&nfp_common_pci_driver); 25729cb31c9SChaoyong He nfp_common_initialized = true; 25829cb31c9SChaoyong He } 25929cb31c9SChaoyong He 26029cb31c9SChaoyong He void 26129cb31c9SChaoyong He nfp_class_driver_register(struct nfp_class_driver *driver) 26229cb31c9SChaoyong He { 26329cb31c9SChaoyong He nfp_common_init(); 26429cb31c9SChaoyong He 26529cb31c9SChaoyong He if (driver->id_table != NULL) { 26629cb31c9SChaoyong He if (nfp_pci_id_table_update(driver->id_table) != 0) 26729cb31c9SChaoyong He return; 26829cb31c9SChaoyong He } 26929cb31c9SChaoyong He 27029cb31c9SChaoyong He nfp_common_pci_driver.drv_flags |= driver->drv_flags; 27129cb31c9SChaoyong He 27229cb31c9SChaoyong He TAILQ_INSERT_TAIL(&nfp_drivers_list, driver, next); 27329cb31c9SChaoyong He } 274