linux/drivers/staging/greybus/usb.c
<<
>>
Prefs
   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");
 246