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