mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-13 23:02:59 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
353 lines
8.7 KiB
C
353 lines
8.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/**
|
|
* udc-core.c - Core UDC Framework
|
|
*
|
|
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
|
*
|
|
* Author: Felipe Balbi <balbi@ti.com>
|
|
*
|
|
* Taken from Linux Kernel v3.19-rc1 (drivers/usb/gadget/udc-core.c) and ported
|
|
* to uboot.
|
|
*
|
|
* commit 02e8c96627 : usb: gadget: udc: core: prepend udc_attach_driver with
|
|
* usb_
|
|
*/
|
|
|
|
#include <linux/compat.h>
|
|
#include <malloc.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/dma-mapping.h>
|
|
#include <common.h>
|
|
|
|
#include <linux/usb/ch9.h>
|
|
#include <linux/usb/gadget.h>
|
|
|
|
/**
|
|
* struct usb_udc - describes one usb device controller
|
|
* @driver - the gadget driver pointer. For use by the class code
|
|
* @dev - the child device to the actual controller
|
|
* @gadget - the gadget. For use by the class code
|
|
* @list - for use by the udc class driver
|
|
*
|
|
* This represents the internal data structure which is used by the UDC-class
|
|
* to hold information about udc driver and gadget together.
|
|
*/
|
|
struct usb_udc {
|
|
struct usb_gadget_driver *driver;
|
|
struct usb_gadget *gadget;
|
|
struct device dev;
|
|
struct list_head list;
|
|
};
|
|
|
|
static struct class *udc_class;
|
|
static LIST_HEAD(udc_list);
|
|
DEFINE_MUTEX(udc_lock);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
int usb_gadget_map_request(struct usb_gadget *gadget,
|
|
struct usb_request *req, int is_in)
|
|
{
|
|
if (req->length == 0)
|
|
return 0;
|
|
|
|
req->dma = dma_map_single(req->buf, req->length,
|
|
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
|
|
|
|
void usb_gadget_unmap_request(struct usb_gadget *gadget,
|
|
struct usb_request *req, int is_in)
|
|
{
|
|
if (req->length == 0)
|
|
return;
|
|
|
|
dma_unmap_single((void *)(uintptr_t)req->dma, req->length,
|
|
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* usb_gadget_giveback_request - give the request back to the gadget layer
|
|
* Context: in_interrupt()
|
|
*
|
|
* This is called by device controller drivers in order to return the
|
|
* completed request back to the gadget layer.
|
|
*/
|
|
void usb_gadget_giveback_request(struct usb_ep *ep,
|
|
struct usb_request *req)
|
|
{
|
|
req->complete(ep, req);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
void usb_gadget_set_state(struct usb_gadget *gadget,
|
|
enum usb_device_state state)
|
|
{
|
|
gadget->state = state;
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
|
|
* @gadget: The gadget which bus reset occurs
|
|
* @driver: The gadget driver we want to notify
|
|
*
|
|
* If the udc driver has bus reset handler, it needs to call this when the bus
|
|
* reset occurs, it notifies the gadget driver that the bus reset occurs as
|
|
* well as updates gadget state.
|
|
*/
|
|
void usb_gadget_udc_reset(struct usb_gadget *gadget,
|
|
struct usb_gadget_driver *driver)
|
|
{
|
|
driver->reset(gadget);
|
|
usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
|
|
|
|
/**
|
|
* usb_gadget_udc_start - tells usb device controller to start up
|
|
* @udc: The UDC to be started
|
|
*
|
|
* This call is issued by the UDC Class driver when it's about
|
|
* to register a gadget driver to the device controller, before
|
|
* calling gadget driver's bind() method.
|
|
*
|
|
* It allows the controller to be powered off until strictly
|
|
* necessary to have it powered on.
|
|
*
|
|
* Returns zero on success, else negative errno.
|
|
*/
|
|
static inline int usb_gadget_udc_start(struct usb_udc *udc)
|
|
{
|
|
return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
|
|
}
|
|
|
|
/**
|
|
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
|
|
* @gadget: The device we want to stop activity
|
|
* @driver: The driver to unbind from @gadget
|
|
*
|
|
* This call is issued by the UDC Class driver after calling
|
|
* gadget driver's unbind() method.
|
|
*
|
|
* The details are implementation specific, but it can go as
|
|
* far as powering off UDC completely and disable its data
|
|
* line pullups.
|
|
*/
|
|
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
|
|
{
|
|
udc->gadget->ops->udc_stop(udc->gadget);
|
|
}
|
|
|
|
/**
|
|
* usb_udc_release - release the usb_udc struct
|
|
* @dev: the dev member within usb_udc
|
|
*
|
|
* This is called by driver's core in order to free memory once the last
|
|
* reference is released.
|
|
*/
|
|
static void usb_udc_release(struct device *dev)
|
|
{
|
|
struct usb_udc *udc;
|
|
|
|
udc = container_of(dev, struct usb_udc, dev);
|
|
kfree(udc);
|
|
}
|
|
|
|
/**
|
|
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
|
|
* @parent: the parent device to this udc. Usually the controller driver's
|
|
* device.
|
|
* @gadget: the gadget to be added to the list.
|
|
* @release: a gadget release function.
|
|
*
|
|
* Returns zero on success, negative errno otherwise.
|
|
*/
|
|
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
|
void (*release)(struct device *dev))
|
|
{
|
|
struct usb_udc *udc;
|
|
int ret = -ENOMEM;
|
|
|
|
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
|
|
if (!udc)
|
|
goto err1;
|
|
|
|
dev_set_name(&gadget->dev, "gadget");
|
|
gadget->dev.parent = parent;
|
|
|
|
udc->dev.release = usb_udc_release;
|
|
udc->dev.class = udc_class;
|
|
udc->dev.parent = parent;
|
|
|
|
udc->gadget = gadget;
|
|
|
|
mutex_lock(&udc_lock);
|
|
list_add_tail(&udc->list, &udc_list);
|
|
|
|
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
|
|
|
|
mutex_unlock(&udc_lock);
|
|
|
|
return 0;
|
|
|
|
err1:
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
|
|
|
|
/**
|
|
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
|
|
* @parent: the parent device to this udc. Usually the controller
|
|
* driver's device.
|
|
* @gadget: the gadget to be added to the list
|
|
*
|
|
* Returns zero on success, negative errno otherwise.
|
|
*/
|
|
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
|
|
{
|
|
return usb_add_gadget_udc_release(parent, gadget, NULL);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
|
|
|
|
static void usb_gadget_remove_driver(struct usb_udc *udc)
|
|
{
|
|
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
|
|
udc->driver->function);
|
|
|
|
usb_gadget_disconnect(udc->gadget);
|
|
udc->driver->disconnect(udc->gadget);
|
|
udc->driver->unbind(udc->gadget);
|
|
usb_gadget_udc_stop(udc);
|
|
|
|
udc->driver = NULL;
|
|
}
|
|
|
|
/**
|
|
* usb_del_gadget_udc - deletes @udc from udc_list
|
|
* @gadget: the gadget to be removed.
|
|
*
|
|
* This, will call usb_gadget_unregister_driver() if
|
|
* the @udc is still busy.
|
|
*/
|
|
void usb_del_gadget_udc(struct usb_gadget *gadget)
|
|
{
|
|
struct usb_udc *udc = NULL;
|
|
|
|
mutex_lock(&udc_lock);
|
|
list_for_each_entry(udc, &udc_list, list)
|
|
if (udc->gadget == gadget)
|
|
goto found;
|
|
|
|
dev_err(gadget->dev.parent, "gadget not registered.\n");
|
|
mutex_unlock(&udc_lock);
|
|
|
|
return;
|
|
|
|
found:
|
|
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
|
|
|
|
list_del(&udc->list);
|
|
mutex_unlock(&udc_lock);
|
|
|
|
if (udc->driver)
|
|
usb_gadget_remove_driver(udc);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
|
|
{
|
|
int ret;
|
|
|
|
dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
|
|
driver->function);
|
|
|
|
udc->driver = driver;
|
|
|
|
ret = driver->bind(udc->gadget);
|
|
if (ret)
|
|
goto err1;
|
|
ret = usb_gadget_udc_start(udc);
|
|
if (ret) {
|
|
driver->unbind(udc->gadget);
|
|
goto err1;
|
|
}
|
|
usb_gadget_connect(udc->gadget);
|
|
|
|
return 0;
|
|
err1:
|
|
if (ret != -EISNAM)
|
|
dev_err(&udc->dev, "failed to start %s: %d\n",
|
|
udc->driver->function, ret);
|
|
udc->driver = NULL;
|
|
return ret;
|
|
}
|
|
|
|
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
|
{
|
|
struct usb_udc *udc = NULL;
|
|
int ret;
|
|
|
|
if (!driver || !driver->bind || !driver->setup)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&udc_lock);
|
|
list_for_each_entry(udc, &udc_list, list) {
|
|
/* For now we take the first one */
|
|
if (!udc->driver)
|
|
goto found;
|
|
}
|
|
|
|
printf("couldn't find an available UDC\n");
|
|
mutex_unlock(&udc_lock);
|
|
return -ENODEV;
|
|
found:
|
|
ret = udc_bind_to_driver(udc, driver);
|
|
mutex_unlock(&udc_lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
|
|
|
|
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
|
{
|
|
return usb_gadget_probe_driver(driver);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
|
|
|
|
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
|
{
|
|
struct usb_udc *udc = NULL;
|
|
int ret = -ENODEV;
|
|
|
|
if (!driver || !driver->unbind)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&udc_lock);
|
|
list_for_each_entry(udc, &udc_list, list)
|
|
if (udc->driver == driver) {
|
|
usb_gadget_remove_driver(udc);
|
|
usb_gadget_set_state(udc->gadget,
|
|
USB_STATE_NOTATTACHED);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
mutex_unlock(&udc_lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
|
|
|
|
MODULE_DESCRIPTION("UDC Framework");
|
|
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
|
MODULE_LICENSE("GPL v2");
|