1 /*
2 * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "pld_usb.h"
21 #include "pld_internal.h"
22
23 #include <linux/atomic.h>
24 #include <linux/usb.h>
25 #include <linux/slab.h>
26 #include <linux/platform_device.h>
27 #include <linux/err.h>
28 #include <linux/list.h>
29 #include "osif_psoc_sync.h"
30
31 #ifdef CONFIG_PLD_USB_CNSS
32 #include <net/cnss2.h>
33 #endif
34
35
36 #define VENDOR_ATHR 0x0CF3
37 static struct usb_device_id pld_usb_id_table[] = {
38 {USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9378, 0xFF, 0xFF, 0xFF)},
39 {USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9379, 0xFF, 0xFF, 0xFF)},
40 {} /* Terminating entry */
41 };
42
43 atomic_t pld_usb_reg_done;
44
45 /**
46 * pld_usb_probe() - pld_usb_probe
47 * @interface: pointer to usb_interface structure
48 * @id: pointer to usb_device_id obtained from the enumerated device
49 *
50 * Return: int 0 on success and errno on failure.
51 */
pld_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)52 static int pld_usb_probe(struct usb_interface *interface,
53 const struct usb_device_id *id)
54 {
55 struct usb_device *pdev = interface_to_usbdev(interface);
56 struct pld_context *pld_context;
57 int ret = 0;
58
59 pld_context = pld_get_global_context();
60 if (!pld_context) {
61 ret = -ENODEV;
62 goto out;
63 }
64
65 ret = pld_add_dev(pld_context, &pdev->dev, &interface->dev,
66 PLD_BUS_TYPE_USB);
67 if (ret)
68 goto out;
69
70 ret = pld_context->ops->probe(&pdev->dev,
71 PLD_BUS_TYPE_USB, interface, (void *)id);
72 if (ret != 0) {
73 pr_err("%s, probe returned %d", __func__, ret);
74 atomic_set(&pld_usb_reg_done, false);
75 } else {
76 atomic_set(&pld_usb_reg_done, true);
77 }
78
79 out:
80 return ret;
81 }
82
83 /**
84 * pld_usb_remove() - Remove function for USB device
85 * @interface: pointer to usb_interface for the usb device being removed
86 *
87 * Return: void
88 */
pld_usb_remove(struct usb_interface * interface)89 static void pld_usb_remove(struct usb_interface *interface)
90 {
91 struct usb_device *pdev = interface_to_usbdev(interface);
92 struct pld_context *pld_context;
93 int errno;
94 struct osif_psoc_sync *psoc_sync;
95
96 errno = osif_psoc_sync_trans_start_wait(&pdev->dev, &psoc_sync);
97 if (errno)
98 return;
99
100 osif_psoc_sync_unregister(&pdev->dev);
101 osif_psoc_sync_wait_for_ops(psoc_sync);
102
103 pld_context = pld_get_global_context();
104
105 if (!pld_context)
106 goto out;
107
108 if (atomic_read(&pld_usb_reg_done) != true) {
109 pr_info("%s: already de-registered!\n", __func__);
110 goto out;
111 }
112
113 pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_USB);
114
115 pld_del_dev(pld_context, &pdev->dev);
116
117 atomic_set(&pld_usb_reg_done, false);
118 out:
119 osif_psoc_sync_trans_stop(psoc_sync);
120 osif_psoc_sync_destroy(psoc_sync);
121
122 pr_info("%s: done!\n", __func__);
123 }
124
125 /**
126 * pld_usb_suspend() - Suspend callback function for power management
127 * @interface: pointer to usb_interface for the usb device
128 * @state: power state
129 *
130 * This function is to suspend the PCIE device when power management is
131 * enabled.
132 *
133 * Return: void
134 */
pld_usb_suspend(struct usb_interface * interface,pm_message_t state)135 static int pld_usb_suspend(struct usb_interface *interface,
136 pm_message_t state)
137 {
138 struct usb_device *pdev = interface_to_usbdev(interface);
139 struct pld_context *pld_context;
140
141 pld_context = pld_get_global_context();
142 return pld_context->ops->suspend(&pdev->dev, PLD_BUS_TYPE_USB, state);
143 }
144
145 /**
146 * pld_usb_resume() - Resume callback function for power management
147 * @interface: pointer to usb_interface for the usb device
148 *
149 * This function is to resume the USB device when power management is
150 * enabled.
151 *
152 * Return: void
153 */
pld_usb_resume(struct usb_interface * interface)154 static int pld_usb_resume(struct usb_interface *interface)
155 {
156 struct pld_context *pld_context;
157 struct usb_device *pdev = interface_to_usbdev(interface);
158
159 pld_context = pld_get_global_context();
160 return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_USB);
161 }
162
163 /**
164 * pld_usb_reset_resume() - pld_usb_reset_resume
165 * @interface: pointer to usb_interface for the usb device
166 *
167 * Return: void
168 */
pld_usb_reset_resume(struct usb_interface * interface)169 static int pld_usb_reset_resume(struct usb_interface *interface)
170 {
171 struct pld_context *pld_context;
172 struct usb_device *pdev = interface_to_usbdev(interface);
173
174 pld_context = pld_get_global_context();
175 return pld_context->ops->reset_resume(&pdev->dev, PLD_BUS_TYPE_USB);
176 }
177
178 #ifdef CONFIG_PLD_USB_CNSS
179 /**
180 * pld_usb_reinit() - SSR re-initialize function for USB device
181 * @interface: Pointer to struct usb_interface
182 * @id: Pointer to USB device ID
183 *
184 * Return: int
185 */
pld_usb_reinit(struct usb_interface * interface,const struct usb_device_id * id)186 static int pld_usb_reinit(struct usb_interface *interface,
187 const struct usb_device_id *id)
188 {
189 struct pld_context *pld_context;
190 struct usb_device *pdev = interface_to_usbdev(interface);
191
192 pld_context = pld_get_global_context();
193 if (pld_context->ops->reinit)
194 return pld_context->ops->reinit(&pdev->dev, PLD_BUS_TYPE_USB,
195 interface, (void *)id);
196
197 return -ENODEV;
198 }
199
200 /**
201 * pld_usb_shutdown() - SSR shutdown function for USB device
202 * @interface: Pointer to struct usb_interface
203 *
204 * Return: void
205 */
pld_usb_shutdown(struct usb_interface * interface)206 static void pld_usb_shutdown(struct usb_interface *interface)
207 {
208 struct pld_context *pld_context;
209 struct usb_device *pdev = interface_to_usbdev(interface);
210
211 pld_context = pld_get_global_context();
212 if (pld_context->ops->shutdown)
213 pld_context->ops->shutdown(&pdev->dev, PLD_BUS_TYPE_USB);
214 }
215
216 /**
217 * pld_usb_uevent() - update wlan driver status callback function
218 * @interface: USB interface
219 * @status: driver uevent status
220 *
221 * This function will be called when platform driver wants to update wlan
222 * driver's status.
223 *
224 * Return: void
225 */
pld_usb_uevent(struct usb_interface * interface,uint32_t status)226 static void pld_usb_uevent(struct usb_interface *interface, uint32_t status)
227 {
228 struct pld_context *pld_context;
229 struct pld_uevent_data data = {0};
230 struct usb_device *pdev = interface_to_usbdev(interface);
231
232 pld_context = pld_get_global_context();
233 if (!pld_context)
234 return;
235
236 switch (status) {
237 case CNSS_RECOVERY:
238 data.uevent = PLD_FW_RECOVERY_START;
239 break;
240 case CNSS_FW_DOWN:
241 data.uevent = PLD_FW_DOWN;
242 break;
243 default:
244 goto out;
245 }
246
247 if (pld_context->ops->uevent)
248 pld_context->ops->uevent(&pdev->dev, &data);
249
250 out:
251 return;
252 }
253 struct cnss_usb_wlan_driver pld_usb_ops = {
254 .name = "pld_usb_cnss",
255 .id_table = pld_usb_id_table,
256 .probe = pld_usb_probe,
257 .remove = pld_usb_remove,
258 .shutdown = pld_usb_shutdown,
259 .reinit = pld_usb_reinit,
260 .update_status = pld_usb_uevent,
261 #ifdef CONFIG_PM
262 .suspend = pld_usb_suspend,
263 .resume = pld_usb_resume,
264 .reset_resume = pld_usb_reset_resume,
265 #endif
266 };
267
pld_usb_register_driver(void)268 int pld_usb_register_driver(void)
269 {
270 pr_info("%s usb_register\n", __func__);
271 return cnss_usb_wlan_register_driver(&pld_usb_ops);
272 }
273
pld_usb_unregister_driver(void)274 void pld_usb_unregister_driver(void)
275 {
276 cnss_usb_wlan_unregister_driver(&pld_usb_ops);
277 pr_info("%s usb_deregister done!\n", __func__);
278 }
279
pld_usb_wlan_enable(struct device * dev,struct pld_wlan_enable_cfg * config,enum pld_driver_mode mode,const char * host_version)280 int pld_usb_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config,
281 enum pld_driver_mode mode, const char *host_version)
282 {
283 struct cnss_wlan_enable_cfg cfg;
284 enum cnss_driver_mode cnss_mode;
285
286 switch (mode) {
287 case PLD_FTM:
288 cnss_mode = CNSS_FTM;
289 break;
290 case PLD_EPPING:
291 cnss_mode = CNSS_EPPING;
292 break;
293 default:
294 cnss_mode = CNSS_MISSION;
295 break;
296 }
297 return cnss_wlan_enable(dev, &cfg, cnss_mode, host_version);
298 }
299
pld_usb_is_fw_down(struct device * dev)300 int pld_usb_is_fw_down(struct device *dev)
301 {
302 return cnss_usb_is_device_down(dev);
303 }
304
pld_usb_athdiag_read(struct device * dev,uint32_t offset,uint32_t memtype,uint32_t datalen,uint8_t * output)305 int pld_usb_athdiag_read(struct device *dev, uint32_t offset,
306 uint32_t memtype, uint32_t datalen,
307 uint8_t *output)
308 {
309 return cnss_athdiag_read(dev, offset, memtype, datalen, output);
310 }
311
pld_usb_athdiag_write(struct device * dev,uint32_t offset,uint32_t memtype,uint32_t datalen,uint8_t * input)312 int pld_usb_athdiag_write(struct device *dev, uint32_t offset,
313 uint32_t memtype, uint32_t datalen,
314 uint8_t *input)
315 {
316 return cnss_athdiag_write(dev, offset, memtype, datalen, input);
317 }
318
319 #else /* CONFIG_PLD_USB_CNSS */
320
321 struct usb_driver pld_usb_ops = {
322 .name = "pld_usb",
323 .id_table = pld_usb_id_table,
324 .probe = pld_usb_probe,
325 .disconnect = pld_usb_remove,
326 #ifdef CONFIG_PM
327 .suspend = pld_usb_suspend,
328 .resume = pld_usb_resume,
329 .reset_resume = pld_usb_reset_resume,
330 #endif
331 .supports_autosuspend = true,
332 };
333
pld_usb_register_driver(void)334 int pld_usb_register_driver(void)
335 {
336 int status;
337
338 usb_register(&pld_usb_ops);
339
340 if (atomic_read(&pld_usb_reg_done) == true) {
341 status = 0;
342 } else {
343 usb_deregister(&pld_usb_ops);
344 status = -1;
345 }
346
347 pr_info("%s usb_register %s, status %d\n", __func__,
348 (status == 0) ? "done" : "failed", status);
349
350 return status;
351 }
352
pld_usb_unregister_driver(void)353 void pld_usb_unregister_driver(void)
354 {
355 struct pld_context *pld_context;
356
357 pld_context = pld_get_global_context();
358 if (atomic_read(&pld_usb_reg_done) == false)
359 return;
360
361 pld_context->ops->remove(NULL, PLD_BUS_TYPE_USB);
362
363 atomic_set(&pld_usb_reg_done, false);
364 usb_deregister(&pld_usb_ops);
365 pr_info("%s usb_deregister done!\n", __func__);
366 }
367
pld_usb_wlan_enable(struct device * dev,struct pld_wlan_enable_cfg * config,enum pld_driver_mode mode,const char * host_version)368 int pld_usb_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config,
369 enum pld_driver_mode mode, const char *host_version)
370 {
371 return 0;
372 }
373
pld_usb_is_fw_down(struct device * dev)374 int pld_usb_is_fw_down(struct device *dev)
375 {
376 return 0;
377 }
378
pld_usb_athdiag_read(struct device * dev,uint32_t offset,uint32_t memtype,uint32_t datalen,uint8_t * output)379 int pld_usb_athdiag_read(struct device *dev, uint32_t offset,
380 uint32_t memtype, uint32_t datalen,
381 uint8_t *output)
382 {
383 return 0;
384 }
385
pld_usb_athdiag_write(struct device * dev,uint32_t offset,uint32_t memtype,uint32_t datalen,uint8_t * input)386 int pld_usb_athdiag_write(struct device *dev, uint32_t offset,
387 uint32_t memtype, uint32_t datalen,
388 uint8_t *input)
389 {
390 return 0;
391 }
392
393 #endif /* CONFIG_PLD_USB_CNSS */
394