usb.c (5623B)
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 22struct gb_usb_hub_control_request { 23 __le16 typeReq; 24 __le16 wValue; 25 __le16 wIndex; 26 __le16 wLength; 27}; 28 29struct gb_usb_hub_control_response { 30 u8 buf[0]; 31}; 32 33struct gb_usb_device { 34 struct gb_connection *connection; 35 struct gbphy_device *gbphy_dev; 36}; 37 38static 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 43static 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 48static 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(dev->connection, GB_USB_TYPE_HCD_STOP, 54 NULL, 0, NULL, 0); 55 if (ret) 56 dev_err(&dev->gbphy_dev->dev, "HCD stop failed '%d'\n", ret); 57} 58 59static 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(dev->connection, GB_USB_TYPE_HCD_START, 66 NULL, 0, NULL, 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 78static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) 79{ 80 return -ENXIO; 81} 82 83static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 84{ 85 return -ENXIO; 86} 87 88static int get_frame_number(struct usb_hcd *hcd) 89{ 90 return 0; 91} 92 93static int hub_status_data(struct usb_hcd *hcd, char *buf) 94{ 95 return 0; 96} 97 98static 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(dev->connection, 112 GB_USB_TYPE_HUB_CONTROL, 113 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 } 134out: 135 gb_operation_put(operation); 136 137 return ret; 138} 139 140static 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 158static 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(&usb_gb_hc_driver, dev, dev_name(dev)); 168 if (!hcd) 169 return -ENOMEM; 170 171 connection = gb_connection_create(gbphy_dev->bundle, 172 le16_to_cpu(gbphy_dev->cport_desc->id), 173 NULL); 174 if (IS_ERR(connection)) { 175 retval = PTR_ERR(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, gb_usb_dev); 182 gb_usb_dev->gbphy_dev = gbphy_dev; 183 gb_gbphy_set_data(gbphy_dev, 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, 0, 0); 204 if (retval) 205 goto exit_connection_disable; 206 207 return 0; 208 209exit_connection_disable: 210 gb_connection_disable(connection); 211exit_connection_destroy: 212 gb_connection_destroy(connection); 213exit_usb_put: 214 usb_put_hcd(hcd); 215 216 return retval; 217} 218 219static void gb_usb_remove(struct gbphy_device *gbphy_dev) 220{ 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); 224 225 usb_remove_hcd(hcd); 226 gb_connection_disable(connection); 227 gb_connection_destroy(connection); 228 usb_put_hcd(hcd); 229} 230 231static const struct gbphy_device_id gb_usb_id_table[] = { 232 { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) }, 233 { }, 234}; 235MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table); 236 237static 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 244module_gbphy_driver(usb_driver); 245MODULE_LICENSE("GPL v2");