diff options
Diffstat (limited to 'greybus_usb.patch')
| -rw-r--r-- | greybus_usb.patch | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/greybus_usb.patch b/greybus_usb.patch new file mode 100644 index 00000000000000..c2719d11714126 --- /dev/null +++ b/greybus_usb.patch @@ -0,0 +1,254 @@ +--- + drivers/greybus/usb.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 247 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/usb.c +@@ -0,0 +1,247 @@ ++/* ++ * USB host driver for the Greybus "generic" USB module. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/usb.h> ++#include <linux/usb/hcd.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++/* Greybus USB request types */ ++#define GB_USB_TYPE_HCD_START 0x02 ++#define GB_USB_TYPE_HCD_STOP 0x03 ++#define GB_USB_TYPE_HUB_CONTROL 0x04 ++ ++struct gb_usb_hub_control_request { ++ __le16 typeReq; ++ __le16 wValue; ++ __le16 wIndex; ++ __le16 wLength; ++}; ++ ++struct gb_usb_hub_control_response { ++ u8 buf[0]; ++}; ++ ++struct gb_usb_device { ++ struct gb_connection *connection; ++ struct gbphy_device *gbphy_dev; ++}; ++ ++static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd) ++{ ++ return (struct gb_usb_device *)hcd->hcd_priv; ++} ++ ++static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev) ++{ ++ return container_of((void *)dev, struct usb_hcd, hcd_priv); ++} ++ ++static void hcd_stop(struct usb_hcd *hcd) ++{ ++ struct gb_usb_device *dev = to_gb_usb_device(hcd); ++ int ret; ++ ++ ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, ++ NULL, 0, NULL, 0); ++ if (ret) ++ dev_err(&dev->gbphy_dev->dev, "HCD stop failed '%d'\n", ret); ++} ++ ++static int hcd_start(struct usb_hcd *hcd) ++{ ++ struct usb_bus *bus = hcd_to_bus(hcd); ++ struct gb_usb_device *dev = to_gb_usb_device(hcd); ++ int ret; ++ ++ ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, ++ NULL, 0, NULL, 0); ++ if (ret) { ++ dev_err(&dev->gbphy_dev->dev, "HCD start failed '%d'\n", ret); ++ return ret; ++ } ++ ++ hcd->state = HC_STATE_RUNNING; ++ if (bus->root_hub) ++ usb_hcd_resume_root_hub(hcd); ++ return 0; ++} ++ ++static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ++{ ++ return -ENXIO; ++} ++ ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ return -ENXIO; ++} ++ ++static int get_frame_number(struct usb_hcd *hcd) ++{ ++ return 0; ++} ++ ++static int hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ return 0; ++} ++ ++static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, ++ char *buf, u16 wLength) ++{ ++ struct gb_usb_device *dev = to_gb_usb_device(hcd); ++ struct gb_operation *operation; ++ struct gb_usb_hub_control_request *request; ++ struct gb_usb_hub_control_response *response; ++ size_t response_size; ++ int ret; ++ ++ /* FIXME: handle unspecified lengths */ ++ response_size = sizeof(*response) + wLength; ++ ++ operation = gb_operation_create(dev->connection, ++ GB_USB_TYPE_HUB_CONTROL, ++ sizeof(*request), ++ response_size, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ request = operation->request->payload; ++ request->typeReq = cpu_to_le16(typeReq); ++ request->wValue = cpu_to_le16(wValue); ++ request->wIndex = cpu_to_le16(wIndex); ++ request->wLength = cpu_to_le16(wLength); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) ++ goto out; ++ ++ if (wLength) { ++ /* Greybus core has verified response size */ ++ response = operation->response->payload; ++ memcpy(buf, response->buf, wLength); ++ } ++out: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static struct hc_driver usb_gb_hc_driver = { ++ .description = "greybus-hcd", ++ .product_desc = "Greybus USB Host Controller", ++ .hcd_priv_size = sizeof(struct gb_usb_device), ++ ++ .flags = HCD_USB2, ++ ++ .start = hcd_start, ++ .stop = hcd_stop, ++ ++ .urb_enqueue = urb_enqueue, ++ .urb_dequeue = urb_dequeue, ++ ++ .get_frame_number = get_frame_number, ++ .hub_status_data = hub_status_data, ++ .hub_control = hub_control, ++}; ++ ++static int gb_usb_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct device *dev = &gbphy_dev->dev; ++ struct gb_usb_device *gb_usb_dev; ++ struct usb_hcd *hcd; ++ int retval; ++ ++ hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto exit_usb_put; ++ } ++ ++ gb_usb_dev = to_gb_usb_device(hcd); ++ gb_usb_dev->connection = connection; ++ gb_connection_set_data(connection, gb_usb_dev); ++ gb_usb_dev->gbphy_dev = gbphy_dev; ++ gb_gbphy_set_data(gbphy_dev, gb_usb_dev); ++ ++ hcd->has_tt = 1; ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto exit_connection_destroy; ++ ++ /* ++ * FIXME: The USB bridged-PHY protocol driver depends on changes to ++ * USB core which are not yet upstream. ++ * ++ * Disable for now. ++ */ ++ if (1) { ++ dev_warn(dev, "USB protocol disabled\n"); ++ retval = -EPROTONOSUPPORT; ++ goto exit_connection_disable; ++ } ++ ++ retval = usb_add_hcd(hcd, 0, 0); ++ if (retval) ++ goto exit_connection_disable; ++ ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_usb_put: ++ usb_put_hcd(hcd); ++ ++ return retval; ++} ++ ++static void gb_usb_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_usb_device *gb_usb_dev = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = gb_usb_dev->connection; ++ struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev); ++ ++ usb_remove_hcd(hcd); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ usb_put_hcd(hcd); ++} ++ ++static const struct gbphy_device_id gb_usb_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table); ++ ++static struct gbphy_driver usb_driver = { ++ .name = "usb", ++ .probe = gb_usb_probe, ++ .remove = gb_usb_remove, ++ .id_table = gb_usb_id_table, ++}; ++ ++module_gbphy_driver(usb_driver); ++MODULE_LICENSE("GPL v2"); |
