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