1 // SPDX-License-Identifier: GPL-2.0
3 * USB host driver for the Greybus "generic" USB module.
5 * Copyright 2014 Google Inc.
6 * Copyright 2014 Linaro Ltd.
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>
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
22 struct gb_usb_hub_control_request
{
29 struct gb_usb_hub_control_response
{
33 struct gb_usb_device
{
34 struct gb_connection
*connection
;
35 struct gbphy_device
*gbphy_dev
;
38 static inline struct gb_usb_device
*to_gb_usb_device(struct usb_hcd
*hcd
)
40 return (struct gb_usb_device
*)hcd
->hcd_priv
;
43 static inline struct usb_hcd
*gb_usb_device_to_hcd(struct gb_usb_device
*dev
)
45 return container_of((void *)dev
, struct usb_hcd
, hcd_priv
);
48 static void hcd_stop(struct usb_hcd
*hcd
)
50 struct gb_usb_device
*dev
= to_gb_usb_device(hcd
);
53 ret
= gb_operation_sync(dev
->connection
, GB_USB_TYPE_HCD_STOP
,
56 dev_err(&dev
->gbphy_dev
->dev
, "HCD stop failed '%d'\n", ret
);
59 static int hcd_start(struct usb_hcd
*hcd
)
61 struct usb_bus
*bus
= hcd_to_bus(hcd
);
62 struct gb_usb_device
*dev
= to_gb_usb_device(hcd
);
65 ret
= gb_operation_sync(dev
->connection
, GB_USB_TYPE_HCD_START
,
68 dev_err(&dev
->gbphy_dev
->dev
, "HCD start failed '%d'\n", ret
);
72 hcd
->state
= HC_STATE_RUNNING
;
74 usb_hcd_resume_root_hub(hcd
);
78 static int urb_enqueue(struct usb_hcd
*hcd
, struct urb
*urb
, gfp_t mem_flags
)
83 static int urb_dequeue(struct usb_hcd
*hcd
, struct urb
*urb
, int status
)
88 static int get_frame_number(struct usb_hcd
*hcd
)
93 static int hub_status_data(struct usb_hcd
*hcd
, char *buf
)
98 static int hub_control(struct usb_hcd
*hcd
, u16 typeReq
, u16 wValue
, u16 wIndex
,
99 char *buf
, u16 wLength
)
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
;
108 /* FIXME: handle unspecified lengths */
109 response_size
= sizeof(*response
) + wLength
;
111 operation
= gb_operation_create(dev
->connection
,
112 GB_USB_TYPE_HUB_CONTROL
,
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
);
125 ret
= gb_operation_request_send_sync(operation
);
130 /* Greybus core has verified response size */
131 response
= operation
->response
->payload
;
132 memcpy(buf
, response
->buf
, wLength
);
135 gb_operation_put(operation
);
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
),
150 .urb_enqueue
= urb_enqueue
,
151 .urb_dequeue
= urb_dequeue
,
153 .get_frame_number
= get_frame_number
,
154 .hub_status_data
= hub_status_data
,
155 .hub_control
= hub_control
,
158 static int gb_usb_probe(struct gbphy_device
*gbphy_dev
,
159 const struct gbphy_device_id
*id
)
161 struct gb_connection
*connection
;
162 struct device
*dev
= &gbphy_dev
->dev
;
163 struct gb_usb_device
*gb_usb_dev
;
167 hcd
= usb_create_hcd(&usb_gb_hc_driver
, dev
, dev_name(dev
));
171 connection
= gb_connection_create(gbphy_dev
->bundle
,
172 le16_to_cpu(gbphy_dev
->cport_desc
->id
),
174 if (IS_ERR(connection
)) {
175 retval
= PTR_ERR(connection
);
179 gb_usb_dev
= to_gb_usb_device(hcd
);
180 gb_usb_dev
->connection
= connection
;
181 gb_connection_set_data(connection
, gb_usb_dev
);
182 gb_usb_dev
->gbphy_dev
= gbphy_dev
;
183 gb_gbphy_set_data(gbphy_dev
, gb_usb_dev
);
187 retval
= gb_connection_enable(connection
);
189 goto exit_connection_destroy
;
192 * FIXME: The USB bridged-PHY protocol driver depends on changes to
193 * USB core which are not yet upstream.
198 dev_warn(dev
, "USB protocol disabled\n");
199 retval
= -EPROTONOSUPPORT
;
200 goto exit_connection_disable
;
203 retval
= usb_add_hcd(hcd
, 0, 0);
205 goto exit_connection_disable
;
209 exit_connection_disable
:
210 gb_connection_disable(connection
);
211 exit_connection_destroy
:
212 gb_connection_destroy(connection
);
219 static void gb_usb_remove(struct gbphy_device
*gbphy_dev
)
221 struct gb_usb_device
*gb_usb_dev
= gb_gbphy_get_data(gbphy_dev
);
222 struct gb_connection
*connection
= gb_usb_dev
->connection
;
223 struct usb_hcd
*hcd
= gb_usb_device_to_hcd(gb_usb_dev
);
226 gb_connection_disable(connection
);
227 gb_connection_destroy(connection
);
231 static const struct gbphy_device_id gb_usb_id_table
[] = {
232 { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB
) },
235 MODULE_DEVICE_TABLE(gbphy
, gb_usb_id_table
);
237 static struct gbphy_driver usb_driver
= {
239 .probe
= gb_usb_probe
,
240 .remove
= gb_usb_remove
,
241 .id_table
= gb_usb_id_table
,
244 module_gbphy_driver(usb_driver
);
245 MODULE_LICENSE("GPL v2");