GNU Linux-libre 4.14.302-gnu1
[releases.git] / drivers / staging / greybus / usb.c
1 /*
2  * USB host driver for the Greybus "generic" USB module.
3  *
4  * Copyright 2014 Google Inc.
5  * Copyright 2014 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  *
9  */
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/usb.h>
14 #include <linux/usb/hcd.h>
15
16 #include "greybus.h"
17 #include "gbphy.h"
18
19 /* Greybus USB request types */
20 #define GB_USB_TYPE_HCD_START           0x02
21 #define GB_USB_TYPE_HCD_STOP            0x03
22 #define GB_USB_TYPE_HUB_CONTROL         0x04
23
24 struct gb_usb_hub_control_request {
25         __le16 typeReq;
26         __le16 wValue;
27         __le16 wIndex;
28         __le16 wLength;
29 };
30
31 struct gb_usb_hub_control_response {
32         u8 buf[0];
33 };
34
35 struct gb_usb_device {
36         struct gb_connection *connection;
37         struct gbphy_device *gbphy_dev;
38 };
39
40 static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd)
41 {
42         return (struct gb_usb_device *)hcd->hcd_priv;
43 }
44
45 static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev)
46 {
47         return container_of((void *)dev, struct usb_hcd, hcd_priv);
48 }
49
50 static void hcd_stop(struct usb_hcd *hcd)
51 {
52         struct gb_usb_device *dev = to_gb_usb_device(hcd);
53         int ret;
54
55         ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP,
56                                 NULL, 0, NULL, 0);
57         if (ret)
58                 dev_err(&dev->gbphy_dev->dev, "HCD stop failed '%d'\n", ret);
59 }
60
61 static int hcd_start(struct usb_hcd *hcd)
62 {
63         struct usb_bus *bus = hcd_to_bus(hcd);
64         struct gb_usb_device *dev = to_gb_usb_device(hcd);
65         int ret;
66
67         ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START,
68                                 NULL, 0, NULL, 0);
69         if (ret) {
70                 dev_err(&dev->gbphy_dev->dev, "HCD start failed '%d'\n", ret);
71                 return ret;
72         }
73
74         hcd->state = HC_STATE_RUNNING;
75         if (bus->root_hub)
76                 usb_hcd_resume_root_hub(hcd);
77         return 0;
78 }
79
80 static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
81 {
82         return -ENXIO;
83 }
84
85 static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
86 {
87         return -ENXIO;
88 }
89
90 static int get_frame_number(struct usb_hcd *hcd)
91 {
92         return 0;
93 }
94
95 static int hub_status_data(struct usb_hcd *hcd, char *buf)
96 {
97         return 0;
98 }
99
100 static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
101                        char *buf, u16 wLength)
102 {
103         struct gb_usb_device *dev = to_gb_usb_device(hcd);
104         struct gb_operation *operation;
105         struct gb_usb_hub_control_request *request;
106         struct gb_usb_hub_control_response *response;
107         size_t response_size;
108         int ret;
109
110         /* FIXME: handle unspecified lengths */
111         response_size = sizeof(*response) + wLength;
112
113         operation = gb_operation_create(dev->connection,
114                                         GB_USB_TYPE_HUB_CONTROL,
115                                         sizeof(*request),
116                                         response_size,
117                                         GFP_KERNEL);
118         if (!operation)
119                 return -ENOMEM;
120
121         request = operation->request->payload;
122         request->typeReq = cpu_to_le16(typeReq);
123         request->wValue = cpu_to_le16(wValue);
124         request->wIndex = cpu_to_le16(wIndex);
125         request->wLength = cpu_to_le16(wLength);
126
127         ret = gb_operation_request_send_sync(operation);
128         if (ret)
129                 goto out;
130
131         if (wLength) {
132                 /* Greybus core has verified response size */
133                 response = operation->response->payload;
134                 memcpy(buf, response->buf, wLength);
135         }
136 out:
137         gb_operation_put(operation);
138
139         return ret;
140 }
141
142 static const struct hc_driver usb_gb_hc_driver = {
143         .description = "greybus-hcd",
144         .product_desc = "Greybus USB Host Controller",
145         .hcd_priv_size = sizeof(struct gb_usb_device),
146
147         .flags = HCD_USB2,
148
149         .start = hcd_start,
150         .stop = hcd_stop,
151
152         .urb_enqueue = urb_enqueue,
153         .urb_dequeue = urb_dequeue,
154
155         .get_frame_number = get_frame_number,
156         .hub_status_data = hub_status_data,
157         .hub_control = hub_control,
158 };
159
160 static int gb_usb_probe(struct gbphy_device *gbphy_dev,
161                         const struct gbphy_device_id *id)
162 {
163         struct gb_connection *connection;
164         struct device *dev = &gbphy_dev->dev;
165         struct gb_usb_device *gb_usb_dev;
166         struct usb_hcd *hcd;
167         int retval;
168
169         hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev));
170         if (!hcd)
171                 return -ENOMEM;
172
173         connection = gb_connection_create(gbphy_dev->bundle,
174                                           le16_to_cpu(gbphy_dev->cport_desc->id),
175                                           NULL);
176         if (IS_ERR(connection)) {
177                 retval = PTR_ERR(connection);
178                 goto exit_usb_put;
179         }
180
181         gb_usb_dev = to_gb_usb_device(hcd);
182         gb_usb_dev->connection = connection;
183         gb_connection_set_data(connection, gb_usb_dev);
184         gb_usb_dev->gbphy_dev = gbphy_dev;
185         gb_gbphy_set_data(gbphy_dev, gb_usb_dev);
186
187         hcd->has_tt = 1;
188
189         retval = gb_connection_enable(connection);
190         if (retval)
191                 goto exit_connection_destroy;
192
193         /*
194          * FIXME: The USB bridged-PHY protocol driver depends on changes to
195          *        USB core which are not yet upstream.
196          *
197          *        Disable for now.
198          */
199         if (1) {
200                 dev_warn(dev, "USB protocol disabled\n");
201                 retval = -EPROTONOSUPPORT;
202                 goto exit_connection_disable;
203         }
204
205         retval = usb_add_hcd(hcd, 0, 0);
206         if (retval)
207                 goto exit_connection_disable;
208
209         return 0;
210
211 exit_connection_disable:
212         gb_connection_disable(connection);
213 exit_connection_destroy:
214         gb_connection_destroy(connection);
215 exit_usb_put:
216         usb_put_hcd(hcd);
217
218         return retval;
219 }
220
221 static void gb_usb_remove(struct gbphy_device *gbphy_dev)
222 {
223         struct gb_usb_device *gb_usb_dev = gb_gbphy_get_data(gbphy_dev);
224         struct gb_connection *connection = gb_usb_dev->connection;
225         struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev);
226
227         usb_remove_hcd(hcd);
228         gb_connection_disable(connection);
229         gb_connection_destroy(connection);
230         usb_put_hcd(hcd);
231 }
232
233 static const struct gbphy_device_id gb_usb_id_table[] = {
234         { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) },
235         { },
236 };
237 MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table);
238
239 static struct gbphy_driver usb_driver = {
240         .name           = "usb",
241         .probe          = gb_usb_probe,
242         .remove         = gb_usb_remove,
243         .id_table       = gb_usb_id_table,
244 };
245
246 module_gbphy_driver(usb_driver);
247 MODULE_LICENSE("GPL v2");