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