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