Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.12 247 lines 5.6 kB view raw
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 24struct gb_usb_hub_control_request { 25 __le16 typeReq; 26 __le16 wValue; 27 __le16 wIndex; 28 __le16 wLength; 29}; 30 31struct gb_usb_hub_control_response { 32 u8 buf[0]; 33}; 34 35struct gb_usb_device { 36 struct gb_connection *connection; 37 struct gbphy_device *gbphy_dev; 38}; 39 40static 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 45static 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 50static 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 61static 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 80static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) 81{ 82 return -ENXIO; 83} 84 85static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 86{ 87 return -ENXIO; 88} 89 90static int get_frame_number(struct usb_hcd *hcd) 91{ 92 return 0; 93} 94 95static int hub_status_data(struct usb_hcd *hcd, char *buf) 96{ 97 return 0; 98} 99 100static 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 } 136out: 137 gb_operation_put(operation); 138 139 return ret; 140} 141 142static 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 160static 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 211exit_connection_disable: 212 gb_connection_disable(connection); 213exit_connection_destroy: 214 gb_connection_destroy(connection); 215exit_usb_put: 216 usb_put_hcd(hcd); 217 218 return retval; 219} 220 221static 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 233static const struct gbphy_device_id gb_usb_id_table[] = { 234 { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) }, 235 { }, 236}; 237MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table); 238 239static 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 246module_gbphy_driver(usb_driver); 247MODULE_LICENSE("GPL v2");