xref: /freebsd/sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.c (revision 050d28e13cdede7528944c0abf8d0455729c63fd)
1 /*-
2  * Broadcom NetXtreme-C/E network driver.
3  *
4  * Copyright (c) 2024 Broadcom, All Rights Reserved.
5  * The term Broadcom refers to Broadcom Limited and/or its subsidiaries
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS'
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <linux/device.h>
30 #include <linux/slab.h>
31 #include <linux/string.h>
32 #include <linux/types.h>
33 #include <linux/list.h>
34 #include <linux/delay.h>
35 
36 #include "bnxt_auxbus_compat.h"
37 
38 static struct list_head bnxt_aux_bus_dev_list = LINUX_LIST_HEAD_INIT(bnxt_aux_bus_dev_list);
39 static struct list_head bnxt_aux_bus_drv_list = LINUX_LIST_HEAD_INIT(bnxt_aux_bus_drv_list);
40 static DEFINE_MUTEX(bnxt_auxbus_lock);
41 
auxiliary_match_id(const struct auxiliary_device_id * id,const struct auxiliary_device * auxdev)42 static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
43 							    const struct auxiliary_device *auxdev)
44 {
45 	for (; id->name[0]; id++) {
46 		const char *p = strrchr(dev_name(&auxdev->dev), '.');
47 		int match_size;
48 
49 		if (!p)
50 			continue;
51 		match_size = p - dev_name(&auxdev->dev);
52 
53 		if (strlen(id->name) == match_size &&
54 		    !strncmp(dev_name(&auxdev->dev), id->name, match_size))
55 			return id;
56 	}
57 	return NULL;
58 }
59 
auxiliary_device_init(struct auxiliary_device * auxdev)60 int auxiliary_device_init(struct auxiliary_device *auxdev)
61 {
62 	struct device *dev = &auxdev->dev;
63 	char *modname = KBUILD_MODNAME;
64 	int ret;
65 
66 	if (!dev->parent) {
67 		pr_err("auxiliary_device has a NULL dev->parent\n");
68 		return -EINVAL;
69 	}
70 
71 	if (!auxdev->name) {
72 		pr_err("auxiliary_device has a NULL name\n");
73 		return -EINVAL;
74 	}
75 
76 	ret = dev_set_name(dev, "%s.%s.%d", modname, auxdev->name, auxdev->id);
77 	if (ret) {
78 		dev_err(dev, "auxiliary device dev_set_name failed: %d\n", ret);
79 		return ret;
80 	}
81 
82 	return 0;
83 }
84 
auxiliary_device_add(struct auxiliary_device * auxdev)85 int auxiliary_device_add(struct auxiliary_device *auxdev)
86 {
87 	const struct auxiliary_device_id *id;
88 	struct auxiliary_driver *auxdrv = NULL;
89 	bool found = true;
90 	int ret = 0;
91 
92 	mutex_lock(&bnxt_auxbus_lock);
93 	list_for_each_entry(auxdrv, &bnxt_aux_bus_drv_list, list) {
94 		if (auxdrv) {
95 			msleep(2 * 1000);
96 
97 			id = auxiliary_match_id(auxdrv->id_table, auxdev);
98 			if (id) {
99 				ret = auxdrv->probe(auxdev, id);
100 				if (!ret)
101 					auxdev->dev.driver = &auxdrv->driver;
102 				else
103 					found = false;
104 				break;
105 			}
106 		}
107 	}
108 
109 	if (found)
110 		list_add_tail(&auxdev->list, &bnxt_aux_bus_dev_list);
111 	mutex_unlock(&bnxt_auxbus_lock);
112 
113 	return ret;
114 }
115 
auxiliary_device_uninit(struct auxiliary_device * auxdev)116 void auxiliary_device_uninit(struct auxiliary_device *auxdev)
117 {
118 	return;
119 }
120 
auxiliary_device_delete(struct auxiliary_device * auxdev)121 void auxiliary_device_delete(struct auxiliary_device *auxdev)
122 {
123 	struct auxiliary_driver *auxdrv;
124 
125 	mutex_lock(&bnxt_auxbus_lock);
126 	list_for_each_entry(auxdrv, &bnxt_aux_bus_drv_list, list) {
127 		if (auxdev->dev.driver != &auxdrv->driver)
128 			continue;
129 		if (auxdrv->remove)
130 			auxdrv->remove(auxdev);
131 		auxdev->dev.driver = NULL;
132 	}
133 	list_del(&auxdev->list);
134 	mutex_unlock(&bnxt_auxbus_lock);
135 }
136 
auxiliary_driver_register(struct auxiliary_driver * auxdrv)137 int auxiliary_driver_register(struct auxiliary_driver *auxdrv)
138 {
139 	const struct auxiliary_device_id *id;
140 	struct auxiliary_device *auxdev;
141 	int ret = 0;
142 
143 	if (WARN_ON(!auxdrv->probe) || WARN_ON(!auxdrv->id_table))
144 		return -EINVAL;
145 
146 	if (auxdrv->name)
147 		auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s.%s", KBUILD_MODNAME,
148 						auxdrv->name);
149 	else
150 		auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s", KBUILD_MODNAME);
151 	if (!auxdrv->driver.name)
152 		return -ENOMEM;
153 
154 	mutex_lock(&bnxt_auxbus_lock);
155 	list_for_each_entry(auxdev, &bnxt_aux_bus_dev_list, list) {
156 		if (auxdev->dev.driver)
157 			continue;
158 
159 		id = auxiliary_match_id(auxdrv->id_table, auxdev);
160 		if (id) {
161 			ret = auxdrv->probe(auxdev, id);
162 			if (ret)
163 				continue;
164 			auxdev->dev.driver = &auxdrv->driver;
165 		}
166 	}
167 	list_add_tail(&auxdrv->list, &bnxt_aux_bus_drv_list);
168 	mutex_unlock(&bnxt_auxbus_lock);
169 	return 0;
170 }
171 EXPORT_SYMBOL(auxiliary_driver_register);
172 
auxiliary_driver_unregister(struct auxiliary_driver * auxdrv)173 void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
174 {
175 	struct auxiliary_device *auxdev;
176 
177 	/* PF auxiliary devices are added to the list first and then VF devices.
178 	 * If we remove PF aux device driver first, it causes failures while
179 	 * removing VF driver.
180 	 * We need to remove VF auxiliary drivers first, so walk backwards.
181 	 */
182 	mutex_lock(&bnxt_auxbus_lock);
183 	list_for_each_entry_reverse(auxdev, &bnxt_aux_bus_dev_list, list) {
184 		if (auxdev->dev.driver != &auxdrv->driver)
185 			continue;
186 		if (auxdrv->remove)
187 			auxdrv->remove(auxdev);
188 		auxdev->dev.driver = NULL;
189 	}
190 	kfree(auxdrv->driver.name);
191 	list_del(&auxdrv->list);
192 	mutex_unlock(&bnxt_auxbus_lock);
193 }
194 EXPORT_SYMBOL(auxiliary_driver_unregister);
195