Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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");