blob: 770f4e727522ee5e7f75e8a2af64f0821adfac86 [file] [log] [blame]
Mohit Khanna1d531c42016-05-12 21:35:50 -07001/*
Ajit Pal Singh6c6e81c2018-06-13 15:26:37 +05302 * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
Mohit Khanna1d531c42016-05-12 21:35:50 -07003 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
Mohit Khannae7a43572016-10-10 17:34:21 -070019#include "pld_usb.h"
Mohit Khanna1d531c42016-05-12 21:35:50 -070020#include "pld_internal.h"
21
22#include <linux/atomic.h>
23#include <linux/usb.h>
24#include <linux/slab.h>
25#include <linux/platform_device.h>
26#include <linux/err.h>
27#include <linux/list.h>
Ajit Pal Singh6c6e81c2018-06-13 15:26:37 +053028#ifdef CONFIG_PLD_USB_CNSS
29#include <net/cnss2.h>
30#endif
Mohit Khanna1d531c42016-05-12 21:35:50 -070031
32
33#define VENDOR_ATHR 0x0CF3
34static struct usb_device_id pld_usb_id_table[] = {
35 {USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9378, 0xFF, 0xFF, 0xFF)},
Yingying Tang2fb14382016-09-29 14:47:56 +080036 {USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9379, 0xFF, 0xFF, 0xFF)},
Mohit Khanna1d531c42016-05-12 21:35:50 -070037 {} /* Terminating entry */
38};
39
40atomic_t pld_usb_reg_done;
41
42/**
43 * pld_usb_probe() - pld_usb_probe
44 * @interface: pointer to usb_interface structure
45 * @id: pointer to usb_device_id obtained from the enumerated device
46
47 * Return: int 0 on success and errno on failure.
48 */
49static int pld_usb_probe(struct usb_interface *interface,
50 const struct usb_device_id *id)
51{
52 struct usb_device *pdev = interface_to_usbdev(interface);
53 struct pld_context *pld_context;
54 int ret = 0;
55
56 pld_context = pld_get_global_context();
57 if (!pld_context) {
58 ret = -ENODEV;
59 goto out;
60 }
61
62 ret = pld_add_dev(pld_context, &pdev->dev, PLD_BUS_TYPE_USB);
63 if (ret)
64 goto out;
65
66 ret = pld_context->ops->probe(&pdev->dev,
67 PLD_BUS_TYPE_USB, interface, (void *)id);
68 if (ret != 0) {
69 pr_err("%s, probe returned %d", __func__, ret);
70 atomic_set(&pld_usb_reg_done, false);
71 } else {
72 atomic_set(&pld_usb_reg_done, true);
73 }
74
75out:
76 return ret;
77}
78
79/**
80 * pld_usb_remove() - Remove function for USB device
81 * @interface: pointer to usb_interface for the usb device being removed
82 *
83 * Return: void
84 */
85static void pld_usb_remove(struct usb_interface *interface)
86{
87 struct usb_device *pdev = interface_to_usbdev(interface);
88 struct pld_context *pld_context;
89
90 pld_context = pld_get_global_context();
91
92 if (!pld_context)
93 return;
94
95 if (atomic_read(&pld_usb_reg_done) != true) {
96 pr_info("%s: already de-registered!\n", __func__);
97 return;
98 }
99
100 pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_USB);
101
102 pld_del_dev(pld_context, &pdev->dev);
103
104 atomic_set(&pld_usb_reg_done, false);
105 pr_info("%s: done!\n", __func__);
106}
107
108/**
109 * pld_usb_suspend() - Suspend callback function for power management
110 * @interface: pointer to usb_interface for the usb device
111 * @state: power state
112 *
113 * This function is to suspend the PCIE device when power management is
114 * enabled.
115 *
116 * Return: void
117 */
118static int pld_usb_suspend(struct usb_interface *interface,
119 pm_message_t state)
120{
121 struct usb_device *pdev = interface_to_usbdev(interface);
122 struct pld_context *pld_context;
123
124 pld_context = pld_get_global_context();
125 return pld_context->ops->suspend(&pdev->dev, PLD_BUS_TYPE_USB, state);
126}
127
128/**
129 * pld_usb_resume() - Resume callback function for power management
130 * @interface: pointer to usb_interface for the usb device
131 *
132 * This function is to resume the USB device when power management is
133 * enabled.
134 *
135 * Return: void
136 */
137static int pld_usb_resume(struct usb_interface *interface)
138{
139 struct pld_context *pld_context;
140 struct usb_device *pdev = interface_to_usbdev(interface);
141
142 pld_context = pld_get_global_context();
143 return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_USB);
144}
145
146/**
147 * pld_usb_reset_resume() - pld_usb_reset_resume
148 * @interface: pointer to usb_interface for the usb device
149 *
150 * Return: void
151 */
152static int pld_usb_reset_resume(struct usb_interface *interface)
153{
154 struct pld_context *pld_context;
155 struct usb_device *pdev = interface_to_usbdev(interface);
156
157 pld_context = pld_get_global_context();
158 return pld_context->ops->reset_resume(&pdev->dev, PLD_BUS_TYPE_USB);
159}
160
Ajit Pal Singh6c6e81c2018-06-13 15:26:37 +0530161#ifdef CONFIG_PLD_USB_CNSS
162struct cnss_usb_wlan_driver pld_usb_ops = {
163 .name = "pld_usb_cnss",
164 .id_table = pld_usb_id_table,
165 .probe = pld_usb_probe,
166 .disconnect = pld_usb_remove,
167#ifdef CONFIG_PM
168 .suspend = pld_usb_suspend,
169 .resume = pld_usb_resume,
170 .reset_resume = pld_usb_reset_resume,
171#endif
172
173/**
174 * pld_usb_register_driver() - registration routine for wlan usb drive
175 *
176 * Return: int negative error code on failure and 0 on success
177 */
178int pld_usb_register_driver(void)
179{
180 pr_info("%s usb_register\n", __func__);
181 return cnss_wlan_register_driver(&pld_usb_ops);
182}
183
184/**
185 * pld_usb_unregister_driver() - de-registration routine for wlan usb driver
186 *
187 * Return: void
188 */
189void pld_usb_unregister_driver(void)
190{
191 cnss_wlan_register_driver(&pld_usb_ops);
192 pr_info("%s usb_deregister done!\n", __func__);
193}
194
195#else /* CONFIG_PLD_USB_CNSS */
196
Mohit Khanna1d531c42016-05-12 21:35:50 -0700197struct usb_driver pld_usb_ops = {
198 .name = "pld_usb",
199 .id_table = pld_usb_id_table,
200 .probe = pld_usb_probe,
201 .disconnect = pld_usb_remove,
202#ifdef CONFIG_PM
203 .suspend = pld_usb_suspend,
204 .resume = pld_usb_resume,
205 .reset_resume = pld_usb_reset_resume,
206#endif
207 .supports_autosuspend = true,
208};
209
210/**
211 * pld_usb_register_driver() - registration routine for wlan usb driver
212 *
213 * Return: int negative error code on failure and 0 on success
214 */
215int pld_usb_register_driver(void)
216{
217 int status;
218
219 usb_register(&pld_usb_ops);
220
221 if (atomic_read(&pld_usb_reg_done) == true)
222 status = 0;
223 else
224 status = -1;
225
226 pr_info("%s usb_register %s, status %d\n", __func__,
227 (status == 0) ? "done" : "failed", status);
228
229 return status;
230}
231
232/**
233 * pld_usb_unregister_driver() - de-registration routine for wlan usb driver
234 *
235 * Return: void
236 */
237void pld_usb_unregister_driver(void)
238{
239 struct pld_context *pld_context;
Mohit Khanna1d531c42016-05-12 21:35:50 -0700240
Manikandan Mohan8cf50612017-04-10 13:23:32 -0700241 pld_context = pld_get_global_context();
Mohit Khanna1d531c42016-05-12 21:35:50 -0700242 if (atomic_read(&pld_usb_reg_done) == false)
243 return;
244
245 pld_context->ops->remove(NULL, PLD_BUS_TYPE_USB);
246
247 atomic_set(&pld_usb_reg_done, false);
248 usb_deregister(&pld_usb_ops);
249 pr_info("%s usb_deregister done!\n", __func__);
250}
Ajit Pal Singh6c6e81c2018-06-13 15:26:37 +0530251#endif /* CONFIG_PLD_USB_CNSS */