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