xref: /dpdk/drivers/common/nfp/nfp_common_pci.c (revision b6de43530dfa30cbf6b70857e3835099701063d4)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2023 Corigine, Inc.
3  * All rights reserved.
4  */
5 
6 #include "nfp_common_pci.h"
7 
8 #include <string.h>
9 
10 #include <rte_class.h>
11 #include <rte_devargs.h>
12 #include <rte_kvargs.h>
13 
14 #include "nfp_common_log.h"
15 
16 /* Reported driver name. */
17 #define NFP_PCI_DRIVER_NAME "nfp_common_pci"
18 
19 static struct rte_pci_driver nfp_common_pci_driver;
20 
21 /* PCI ID table is build dynamically based on registered nfp drivers. */
22 static struct rte_pci_id *nfp_pci_id_table;
23 
24 /* Head of list of drivers. */
25 static TAILQ_HEAD(nfp_drivers, nfp_class_driver) nfp_drivers_list =
26 		TAILQ_HEAD_INITIALIZER(nfp_drivers_list);
27 
28 static bool nfp_common_initialized;
29 
30 static const struct {
31 	const char *name;
32 	enum nfp_class drv_class;
33 } nfp_classes[] = {
34 	{ .name = "eth",      .drv_class = NFP_CLASS_ETH },
35 	{ .name = "vdpa",     .drv_class = NFP_CLASS_VDPA },
36 };
37 
38 static enum nfp_class
39 nfp_class_name_to_value(const char *class_name)
40 {
41 	uint32_t i;
42 
43 	for (i = 0; i < RTE_DIM(nfp_classes); i++) {
44 		if (strcmp(class_name, nfp_classes[i].name) == 0)
45 			return nfp_classes[i].drv_class;
46 	}
47 
48 	return NFP_CLASS_INVALID;
49 }
50 
51 static uint32_t
52 nfp_pci_id_table_size_get(const struct rte_pci_id *id_table)
53 {
54 	uint32_t table_size;
55 
56 	if (id_table == NULL)
57 		return 0;
58 
59 	for (table_size = 0; id_table->vendor_id != 0; id_table++)
60 		table_size++;
61 
62 	return table_size;
63 }
64 
65 static bool
66 nfp_pci_id_exists(const struct rte_pci_id *id,
67 		const struct rte_pci_id *table,
68 		uint32_t next_idx)
69 {
70 	uint32_t i;
71 
72 	if (next_idx == 0)
73 		return false;
74 
75 	for (i = 0; i < next_idx; i++) {
76 		if (id->device_id == table[i].device_id &&
77 				id->vendor_id == table[i].vendor_id &&
78 				id->subsystem_vendor_id == table[i].subsystem_vendor_id &&
79 				id->subsystem_device_id == table[i].subsystem_device_id)
80 			return true;
81 	}
82 
83 	return false;
84 }
85 
86 static void
87 nfp_pci_id_insert(struct rte_pci_id *new_table,
88 		uint32_t *next_idx,
89 		const struct rte_pci_id *id_table)
90 {
91 	if (id_table == NULL)
92 		return;
93 
94 	/* Add non duplicate entries to new table. */
95 	for (; id_table->vendor_id != 0; id_table++) {
96 		if (!nfp_pci_id_exists(id_table, new_table, *next_idx)) {
97 			new_table[*next_idx] = *id_table;
98 			(*next_idx)++;
99 		}
100 	}
101 }
102 
103 static int
104 nfp_pci_id_table_update(const struct rte_pci_id *driver_id_table)
105 {
106 	uint32_t i = 0;
107 	uint32_t num_ids = 0;
108 	struct rte_pci_id *old_table;
109 	const struct rte_pci_id *id_iter;
110 	struct rte_pci_id *updated_table;
111 
112 	old_table = nfp_pci_id_table;
113 	if (old_table != NULL)
114 		num_ids = nfp_pci_id_table_size_get(old_table);
115 	num_ids += nfp_pci_id_table_size_get(driver_id_table);
116 
117 	/* Increase size by one for the termination entry of vendor_id = 0. */
118 	num_ids += 1;
119 	updated_table = calloc(num_ids, sizeof(struct rte_pci_id));
120 	if (updated_table == NULL)
121 		return -ENOMEM;
122 
123 	if (old_table == NULL) {
124 		/* Copy the first driver's ID table. */
125 		for (id_iter = driver_id_table; id_iter[i].vendor_id != 0; i++)
126 			updated_table[i] = id_iter[i];
127 	} else {
128 		/* First copy existing table entries. */
129 		for (id_iter = old_table; id_iter[i].vendor_id != 0; i++)
130 			updated_table[i] = id_iter[i];
131 		/* New id to be added at the end of current ID table. */
132 		nfp_pci_id_insert(updated_table, &i, driver_id_table);
133 
134 		free(old_table);
135 	}
136 
137 	/* Terminate table with empty entry. */
138 	updated_table[i].vendor_id = 0;
139 	nfp_pci_id_table = updated_table;
140 	nfp_common_pci_driver.id_table = nfp_pci_id_table;
141 
142 	return 0;
143 }
144 
145 static int
146 nfp_kvarg_dev_class_handler(__rte_unused const char *key,
147 		const char *class_str,
148 		void *opaque)
149 {
150 	enum nfp_class *dev_class = opaque;
151 
152 	if (class_str == NULL)
153 		return *dev_class;
154 
155 	*dev_class = nfp_class_name_to_value(class_str);
156 
157 	return 0;
158 }
159 
160 static enum nfp_class
161 nfp_parse_class_options(const struct rte_devargs *devargs)
162 {
163 	struct rte_kvargs *kvargs;
164 	enum nfp_class dev_class = NFP_CLASS_ETH;
165 
166 	if (devargs == NULL)
167 		return dev_class;
168 
169 	kvargs = rte_kvargs_parse(devargs->args, NULL);
170 	if (kvargs == NULL)
171 		return dev_class;
172 
173 	rte_kvargs_process_opt(kvargs, RTE_DEVARGS_KEY_CLASS,
174 			       nfp_kvarg_dev_class_handler, &dev_class);
175 
176 	rte_kvargs_free(kvargs);
177 
178 	return dev_class;
179 }
180 
181 static int
182 nfp_drivers_probe(struct rte_pci_device *pci_dev,
183 		enum nfp_class class)
184 {
185 	int32_t ret = 0;
186 	struct nfp_class_driver *driver;
187 
188 	TAILQ_FOREACH(driver, &nfp_drivers_list, next) {
189 		if (driver->drv_class != class)
190 			continue;
191 
192 		ret = driver->probe(pci_dev);
193 		if (ret < 0) {
194 			PMD_DRV_LOG(ERR, "Failed to load driver %s.", driver->name);
195 			return ret;
196 		}
197 	}
198 
199 	return 0;
200 }
201 
202 static int
203 nfp_common_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
204 		struct rte_pci_device *pci_dev)
205 {
206 	enum nfp_class class;
207 	struct rte_device *eal_dev = &pci_dev->device;
208 
209 	PMD_DRV_LOG(INFO, "Probe device %s.", eal_dev->name);
210 
211 	class = nfp_parse_class_options(eal_dev->devargs);
212 	if (class == NFP_CLASS_INVALID) {
213 		PMD_DRV_LOG(ERR, "Unsupported nfp class type: %s.",
214 				eal_dev->devargs->args);
215 		return -ENOTSUP;
216 	}
217 
218 	return nfp_drivers_probe(pci_dev, class);
219 }
220 
221 static int
222 nfp_common_pci_remove(__rte_unused struct rte_pci_device *pci_dev)
223 {
224 	return 0;
225 }
226 
227 static struct rte_pci_driver nfp_common_pci_driver = {
228 	.driver = {
229 		.name = NFP_PCI_DRIVER_NAME,
230 	},
231 	.probe = nfp_common_pci_probe,
232 	.remove = nfp_common_pci_remove,
233 };
234 
235 static void
236 nfp_common_init(void)
237 {
238 	const struct rte_pci_id empty_table[] = {
239 		{
240 			.vendor_id = 0
241 		},
242 	};
243 
244 	if (nfp_common_initialized)
245 		return;
246 
247 	/*
248 	 * All the constructor of NFP PMDs run at same priority. So any of the PMD
249 	 * including this one can register the PCI table first. If any other
250 	 * PMD(s) have registered the PCI ID table, no need to register an empty
251 	 * default one.
252 	 */
253 	if (nfp_pci_id_table == NULL && nfp_pci_id_table_update(empty_table) != 0)
254 		return;
255 
256 	rte_pci_register(&nfp_common_pci_driver);
257 	nfp_common_initialized = true;
258 }
259 
260 void
261 nfp_class_driver_register(struct nfp_class_driver *driver)
262 {
263 	nfp_common_init();
264 
265 	if (driver->id_table != NULL) {
266 		if (nfp_pci_id_table_update(driver->id_table) != 0)
267 			return;
268 	}
269 
270 	nfp_common_pci_driver.drv_flags |= driver->drv_flags;
271 
272 	TAILQ_INSERT_TAIL(&nfp_drivers_list, driver, next);
273 }
274