forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2563 lines
65 KiB
2563 lines
65 KiB
// SPDX-License-Identifier: GPL-2.0+ |
|
/* |
|
* Handles the Intel 27x USB Device Controller (UDC) |
|
* |
|
* Inspired by original driver by Frank Becker, David Brownell, and others. |
|
* Copyright (C) 2008 Robert Jarzmik |
|
*/ |
|
#include <linux/module.h> |
|
#include <linux/kernel.h> |
|
#include <linux/types.h> |
|
#include <linux/errno.h> |
|
#include <linux/err.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/delay.h> |
|
#include <linux/list.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/proc_fs.h> |
|
#include <linux/clk.h> |
|
#include <linux/irq.h> |
|
#include <linux/gpio.h> |
|
#include <linux/gpio/consumer.h> |
|
#include <linux/slab.h> |
|
#include <linux/prefetch.h> |
|
#include <linux/byteorder/generic.h> |
|
#include <linux/platform_data/pxa2xx_udc.h> |
|
#include <linux/of_device.h> |
|
#include <linux/of_gpio.h> |
|
|
|
#include <linux/usb.h> |
|
#include <linux/usb/ch9.h> |
|
#include <linux/usb/gadget.h> |
|
#include <linux/usb/phy.h> |
|
|
|
#include "pxa27x_udc.h" |
|
|
|
/* |
|
* This driver handles the USB Device Controller (UDC) in Intel's PXA 27x |
|
* series processors. |
|
* |
|
* Such controller drivers work with a gadget driver. The gadget driver |
|
* returns descriptors, implements configuration and data protocols used |
|
* by the host to interact with this device, and allocates endpoints to |
|
* the different protocol interfaces. The controller driver virtualizes |
|
* usb hardware so that the gadget drivers will be more portable. |
|
* |
|
* This UDC hardware wants to implement a bit too much USB protocol. The |
|
* biggest issues are: that the endpoints have to be set up before the |
|
* controller can be enabled (minor, and not uncommon); and each endpoint |
|
* can only have one configuration, interface and alternative interface |
|
* number (major, and very unusual). Once set up, these cannot be changed |
|
* without a controller reset. |
|
* |
|
* The workaround is to setup all combinations necessary for the gadgets which |
|
* will work with this driver. This is done in pxa_udc structure, statically. |
|
* See pxa_udc, udc_usb_ep versus pxa_ep, and matching function find_pxa_ep. |
|
* (You could modify this if needed. Some drivers have a "fifo_mode" module |
|
* parameter to facilitate such changes.) |
|
* |
|
* The combinations have been tested with these gadgets : |
|
* - zero gadget |
|
* - file storage gadget |
|
* - ether gadget |
|
* |
|
* The driver doesn't use DMA, only IO access and IRQ callbacks. No use is |
|
* made of UDC's double buffering either. USB "On-The-Go" is not implemented. |
|
* |
|
* All the requests are handled the same way : |
|
* - the drivers tries to handle the request directly to the IO |
|
* - if the IO fifo is not big enough, the remaining is send/received in |
|
* interrupt handling. |
|
*/ |
|
|
|
#define DRIVER_VERSION "2008-04-18" |
|
#define DRIVER_DESC "PXA 27x USB Device Controller driver" |
|
|
|
static const char driver_name[] = "pxa27x_udc"; |
|
static struct pxa_udc *the_controller; |
|
|
|
static void handle_ep(struct pxa_ep *ep); |
|
|
|
/* |
|
* Debug filesystem |
|
*/ |
|
#ifdef CONFIG_USB_GADGET_DEBUG_FS |
|
|
|
#include <linux/debugfs.h> |
|
#include <linux/uaccess.h> |
|
#include <linux/seq_file.h> |
|
|
|
static int state_dbg_show(struct seq_file *s, void *p) |
|
{ |
|
struct pxa_udc *udc = s->private; |
|
u32 tmp; |
|
|
|
if (!udc->driver) |
|
return -ENODEV; |
|
|
|
/* basic device status */ |
|
seq_printf(s, DRIVER_DESC "\n" |
|
"%s version: %s\n" |
|
"Gadget driver: %s\n", |
|
driver_name, DRIVER_VERSION, |
|
udc->driver ? udc->driver->driver.name : "(none)"); |
|
|
|
tmp = udc_readl(udc, UDCCR); |
|
seq_printf(s, |
|
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n", |
|
tmp, |
|
(tmp & UDCCR_OEN) ? " oen":"", |
|
(tmp & UDCCR_AALTHNP) ? " aalthnp":"", |
|
(tmp & UDCCR_AHNP) ? " rem" : "", |
|
(tmp & UDCCR_BHNP) ? " rstir" : "", |
|
(tmp & UDCCR_DWRE) ? " dwre" : "", |
|
(tmp & UDCCR_SMAC) ? " smac" : "", |
|
(tmp & UDCCR_EMCE) ? " emce" : "", |
|
(tmp & UDCCR_UDR) ? " udr" : "", |
|
(tmp & UDCCR_UDA) ? " uda" : "", |
|
(tmp & UDCCR_UDE) ? " ude" : "", |
|
(tmp & UDCCR_ACN) >> UDCCR_ACN_S, |
|
(tmp & UDCCR_AIN) >> UDCCR_AIN_S, |
|
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S); |
|
/* registers for device and ep0 */ |
|
seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n", |
|
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1)); |
|
seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n", |
|
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1)); |
|
seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR)); |
|
seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n", |
|
udc->stats.irqs_reset, udc->stats.irqs_suspend, |
|
udc->stats.irqs_resume, udc->stats.irqs_reconfig); |
|
|
|
return 0; |
|
} |
|
DEFINE_SHOW_ATTRIBUTE(state_dbg); |
|
|
|
static int queues_dbg_show(struct seq_file *s, void *p) |
|
{ |
|
struct pxa_udc *udc = s->private; |
|
struct pxa_ep *ep; |
|
struct pxa27x_request *req; |
|
int i, maxpkt; |
|
|
|
if (!udc->driver) |
|
return -ENODEV; |
|
|
|
/* dump endpoint queues */ |
|
for (i = 0; i < NR_PXA_ENDPOINTS; i++) { |
|
ep = &udc->pxa_ep[i]; |
|
maxpkt = ep->fifo_size; |
|
seq_printf(s, "%-12s max_pkt=%d %s\n", |
|
EPNAME(ep), maxpkt, "pio"); |
|
|
|
if (list_empty(&ep->queue)) { |
|
seq_puts(s, "\t(nothing queued)\n"); |
|
continue; |
|
} |
|
|
|
list_for_each_entry(req, &ep->queue, queue) { |
|
seq_printf(s, "\treq %p len %d/%d buf %p\n", |
|
&req->req, req->req.actual, |
|
req->req.length, req->req.buf); |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
DEFINE_SHOW_ATTRIBUTE(queues_dbg); |
|
|
|
static int eps_dbg_show(struct seq_file *s, void *p) |
|
{ |
|
struct pxa_udc *udc = s->private; |
|
struct pxa_ep *ep; |
|
int i; |
|
u32 tmp; |
|
|
|
if (!udc->driver) |
|
return -ENODEV; |
|
|
|
ep = &udc->pxa_ep[0]; |
|
tmp = udc_ep_readl(ep, UDCCSR); |
|
seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", |
|
tmp, |
|
(tmp & UDCCSR0_SA) ? " sa" : "", |
|
(tmp & UDCCSR0_RNE) ? " rne" : "", |
|
(tmp & UDCCSR0_FST) ? " fst" : "", |
|
(tmp & UDCCSR0_SST) ? " sst" : "", |
|
(tmp & UDCCSR0_DME) ? " dme" : "", |
|
(tmp & UDCCSR0_IPR) ? " ipr" : "", |
|
(tmp & UDCCSR0_OPC) ? " opc" : ""); |
|
for (i = 0; i < NR_PXA_ENDPOINTS; i++) { |
|
ep = &udc->pxa_ep[i]; |
|
tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR); |
|
seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n", |
|
EPNAME(ep), |
|
ep->stats.in_bytes, ep->stats.in_ops, |
|
ep->stats.out_bytes, ep->stats.out_ops, |
|
ep->stats.irqs, |
|
tmp, udc_ep_readl(ep, UDCCSR), |
|
udc_ep_readl(ep, UDCBCR)); |
|
} |
|
|
|
return 0; |
|
} |
|
DEFINE_SHOW_ATTRIBUTE(eps_dbg); |
|
|
|
static void pxa_init_debugfs(struct pxa_udc *udc) |
|
{ |
|
struct dentry *root; |
|
|
|
root = debugfs_create_dir(udc->gadget.name, usb_debug_root); |
|
udc->debugfs_root = root; |
|
|
|
debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops); |
|
debugfs_create_file("queues", 0400, root, udc, &queues_dbg_fops); |
|
debugfs_create_file("epstate", 0400, root, udc, &eps_dbg_fops); |
|
} |
|
|
|
static void pxa_cleanup_debugfs(struct pxa_udc *udc) |
|
{ |
|
debugfs_remove_recursive(udc->debugfs_root); |
|
} |
|
|
|
#else |
|
static inline void pxa_init_debugfs(struct pxa_udc *udc) |
|
{ |
|
} |
|
|
|
static inline void pxa_cleanup_debugfs(struct pxa_udc *udc) |
|
{ |
|
} |
|
#endif |
|
|
|
/** |
|
* is_match_usb_pxa - check if usb_ep and pxa_ep match |
|
* @udc_usb_ep: usb endpoint |
|
* @ep: pxa endpoint |
|
* @config: configuration required in pxa_ep |
|
* @interface: interface required in pxa_ep |
|
* @altsetting: altsetting required in pxa_ep |
|
* |
|
* Returns 1 if all criteria match between pxa and usb endpoint, 0 otherwise |
|
*/ |
|
static int is_match_usb_pxa(struct udc_usb_ep *udc_usb_ep, struct pxa_ep *ep, |
|
int config, int interface, int altsetting) |
|
{ |
|
if (usb_endpoint_num(&udc_usb_ep->desc) != ep->addr) |
|
return 0; |
|
if (usb_endpoint_dir_in(&udc_usb_ep->desc) != ep->dir_in) |
|
return 0; |
|
if (usb_endpoint_type(&udc_usb_ep->desc) != ep->type) |
|
return 0; |
|
if ((ep->config != config) || (ep->interface != interface) |
|
|| (ep->alternate != altsetting)) |
|
return 0; |
|
return 1; |
|
} |
|
|
|
/** |
|
* find_pxa_ep - find pxa_ep structure matching udc_usb_ep |
|
* @udc: pxa udc |
|
* @udc_usb_ep: udc_usb_ep structure |
|
* |
|
* Match udc_usb_ep and all pxa_ep available, to see if one matches. |
|
* This is necessary because of the strong pxa hardware restriction requiring |
|
* that once pxa endpoints are initialized, their configuration is freezed, and |
|
* no change can be made to their address, direction, or in which configuration, |
|
* interface or altsetting they are active ... which differs from more usual |
|
* models which have endpoints be roughly just addressable fifos, and leave |
|
* configuration events up to gadget drivers (like all control messages). |
|
* |
|
* Note that there is still a blurred point here : |
|
* - we rely on UDCCR register "active interface" and "active altsetting". |
|
* This is a nonsense in regard of USB spec, where multiple interfaces are |
|
* active at the same time. |
|
* - if we knew for sure that the pxa can handle multiple interface at the |
|
* same time, assuming Intel's Developer Guide is wrong, this function |
|
* should be reviewed, and a cache of couples (iface, altsetting) should |
|
* be kept in the pxa_udc structure. In this case this function would match |
|
* against the cache of couples instead of the "last altsetting" set up. |
|
* |
|
* Returns the matched pxa_ep structure or NULL if none found |
|
*/ |
|
static struct pxa_ep *find_pxa_ep(struct pxa_udc *udc, |
|
struct udc_usb_ep *udc_usb_ep) |
|
{ |
|
int i; |
|
struct pxa_ep *ep; |
|
int cfg = udc->config; |
|
int iface = udc->last_interface; |
|
int alt = udc->last_alternate; |
|
|
|
if (udc_usb_ep == &udc->udc_usb_ep[0]) |
|
return &udc->pxa_ep[0]; |
|
|
|
for (i = 1; i < NR_PXA_ENDPOINTS; i++) { |
|
ep = &udc->pxa_ep[i]; |
|
if (is_match_usb_pxa(udc_usb_ep, ep, cfg, iface, alt)) |
|
return ep; |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* update_pxa_ep_matches - update pxa_ep cached values in all udc_usb_ep |
|
* @udc: pxa udc |
|
* |
|
* Context: interrupt handler |
|
* |
|
* Updates all pxa_ep fields in udc_usb_ep structures, if this field was |
|
* previously set up (and is not NULL). The update is necessary is a |
|
* configuration change or altsetting change was issued by the USB host. |
|
*/ |
|
static void update_pxa_ep_matches(struct pxa_udc *udc) |
|
{ |
|
int i; |
|
struct udc_usb_ep *udc_usb_ep; |
|
|
|
for (i = 1; i < NR_USB_ENDPOINTS; i++) { |
|
udc_usb_ep = &udc->udc_usb_ep[i]; |
|
if (udc_usb_ep->pxa_ep) |
|
udc_usb_ep->pxa_ep = find_pxa_ep(udc, udc_usb_ep); |
|
} |
|
} |
|
|
|
/** |
|
* pio_irq_enable - Enables irq generation for one endpoint |
|
* @ep: udc endpoint |
|
*/ |
|
static void pio_irq_enable(struct pxa_ep *ep) |
|
{ |
|
struct pxa_udc *udc = ep->dev; |
|
int index = EPIDX(ep); |
|
u32 udcicr0 = udc_readl(udc, UDCICR0); |
|
u32 udcicr1 = udc_readl(udc, UDCICR1); |
|
|
|
if (index < 16) |
|
udc_writel(udc, UDCICR0, udcicr0 | (3 << (index * 2))); |
|
else |
|
udc_writel(udc, UDCICR1, udcicr1 | (3 << ((index - 16) * 2))); |
|
} |
|
|
|
/** |
|
* pio_irq_disable - Disables irq generation for one endpoint |
|
* @ep: udc endpoint |
|
*/ |
|
static void pio_irq_disable(struct pxa_ep *ep) |
|
{ |
|
struct pxa_udc *udc = ep->dev; |
|
int index = EPIDX(ep); |
|
u32 udcicr0 = udc_readl(udc, UDCICR0); |
|
u32 udcicr1 = udc_readl(udc, UDCICR1); |
|
|
|
if (index < 16) |
|
udc_writel(udc, UDCICR0, udcicr0 & ~(3 << (index * 2))); |
|
else |
|
udc_writel(udc, UDCICR1, udcicr1 & ~(3 << ((index - 16) * 2))); |
|
} |
|
|
|
/** |
|
* udc_set_mask_UDCCR - set bits in UDCCR |
|
* @udc: udc device |
|
* @mask: bits to set in UDCCR |
|
* |
|
* Sets bits in UDCCR, leaving DME and FST bits as they were. |
|
*/ |
|
static inline void udc_set_mask_UDCCR(struct pxa_udc *udc, int mask) |
|
{ |
|
u32 udccr = udc_readl(udc, UDCCR); |
|
udc_writel(udc, UDCCR, |
|
(udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS)); |
|
} |
|
|
|
/** |
|
* udc_clear_mask_UDCCR - clears bits in UDCCR |
|
* @udc: udc device |
|
* @mask: bit to clear in UDCCR |
|
* |
|
* Clears bits in UDCCR, leaving DME and FST bits as they were. |
|
*/ |
|
static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask) |
|
{ |
|
u32 udccr = udc_readl(udc, UDCCR); |
|
udc_writel(udc, UDCCR, |
|
(udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS)); |
|
} |
|
|
|
/** |
|
* ep_write_UDCCSR - set bits in UDCCSR |
|
* @ep: udc endpoint |
|
* @mask: bits to set in UDCCR |
|
* |
|
* Sets bits in UDCCSR (UDCCSR0 and UDCCSR*). |
|
* |
|
* A specific case is applied to ep0 : the ACM bit is always set to 1, for |
|
* SET_INTERFACE and SET_CONFIGURATION. |
|
*/ |
|
static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask) |
|
{ |
|
if (is_ep0(ep)) |
|
mask |= UDCCSR0_ACM; |
|
udc_ep_writel(ep, UDCCSR, mask); |
|
} |
|
|
|
/** |
|
* ep_count_bytes_remain - get how many bytes in udc endpoint |
|
* @ep: udc endpoint |
|
* |
|
* Returns number of bytes in OUT fifos. Broken for IN fifos (-EOPNOTSUPP) |
|
*/ |
|
static int ep_count_bytes_remain(struct pxa_ep *ep) |
|
{ |
|
if (ep->dir_in) |
|
return -EOPNOTSUPP; |
|
return udc_ep_readl(ep, UDCBCR) & 0x3ff; |
|
} |
|
|
|
/** |
|
* ep_is_empty - checks if ep has byte ready for reading |
|
* @ep: udc endpoint |
|
* |
|
* If endpoint is the control endpoint, checks if there are bytes in the |
|
* control endpoint fifo. If endpoint is a data endpoint, checks if bytes |
|
* are ready for reading on OUT endpoint. |
|
* |
|
* Returns 0 if ep not empty, 1 if ep empty, -EOPNOTSUPP if IN endpoint |
|
*/ |
|
static int ep_is_empty(struct pxa_ep *ep) |
|
{ |
|
int ret; |
|
|
|
if (!is_ep0(ep) && ep->dir_in) |
|
return -EOPNOTSUPP; |
|
if (is_ep0(ep)) |
|
ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR0_RNE); |
|
else |
|
ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNE); |
|
return ret; |
|
} |
|
|
|
/** |
|
* ep_is_full - checks if ep has place to write bytes |
|
* @ep: udc endpoint |
|
* |
|
* If endpoint is not the control endpoint and is an IN endpoint, checks if |
|
* there is place to write bytes into the endpoint. |
|
* |
|
* Returns 0 if ep not full, 1 if ep full, -EOPNOTSUPP if OUT endpoint |
|
*/ |
|
static int ep_is_full(struct pxa_ep *ep) |
|
{ |
|
if (is_ep0(ep)) |
|
return (udc_ep_readl(ep, UDCCSR) & UDCCSR0_IPR); |
|
if (!ep->dir_in) |
|
return -EOPNOTSUPP; |
|
return (!(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNF)); |
|
} |
|
|
|
/** |
|
* epout_has_pkt - checks if OUT endpoint fifo has a packet available |
|
* @ep: pxa endpoint |
|
* |
|
* Returns 1 if a complete packet is available, 0 if not, -EOPNOTSUPP for IN ep. |
|
*/ |
|
static int epout_has_pkt(struct pxa_ep *ep) |
|
{ |
|
if (!is_ep0(ep) && ep->dir_in) |
|
return -EOPNOTSUPP; |
|
if (is_ep0(ep)) |
|
return (udc_ep_readl(ep, UDCCSR) & UDCCSR0_OPC); |
|
return (udc_ep_readl(ep, UDCCSR) & UDCCSR_PC); |
|
} |
|
|
|
/** |
|
* set_ep0state - Set ep0 automata state |
|
* @udc: udc device |
|
* @state: state |
|
*/ |
|
static void set_ep0state(struct pxa_udc *udc, int state) |
|
{ |
|
struct pxa_ep *ep = &udc->pxa_ep[0]; |
|
char *old_stname = EP0_STNAME(udc); |
|
|
|
udc->ep0state = state; |
|
ep_dbg(ep, "state=%s->%s, udccsr0=0x%03x, udcbcr=%d\n", old_stname, |
|
EP0_STNAME(udc), udc_ep_readl(ep, UDCCSR), |
|
udc_ep_readl(ep, UDCBCR)); |
|
} |
|
|
|
/** |
|
* ep0_idle - Put control endpoint into idle state |
|
* @dev: udc device |
|
*/ |
|
static void ep0_idle(struct pxa_udc *dev) |
|
{ |
|
set_ep0state(dev, WAIT_FOR_SETUP); |
|
} |
|
|
|
/** |
|
* inc_ep_stats_reqs - Update ep stats counts |
|
* @ep: physical endpoint |
|
* @is_in: ep direction (USB_DIR_IN or 0) |
|
* |
|
*/ |
|
static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in) |
|
{ |
|
if (is_in) |
|
ep->stats.in_ops++; |
|
else |
|
ep->stats.out_ops++; |
|
} |
|
|
|
/** |
|
* inc_ep_stats_bytes - Update ep stats counts |
|
* @ep: physical endpoint |
|
* @count: bytes transferred on endpoint |
|
* @is_in: ep direction (USB_DIR_IN or 0) |
|
*/ |
|
static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in) |
|
{ |
|
if (is_in) |
|
ep->stats.in_bytes += count; |
|
else |
|
ep->stats.out_bytes += count; |
|
} |
|
|
|
/** |
|
* pxa_ep_setup - Sets up an usb physical endpoint |
|
* @ep: pxa27x physical endpoint |
|
* |
|
* Find the physical pxa27x ep, and setup its UDCCR |
|
*/ |
|
static void pxa_ep_setup(struct pxa_ep *ep) |
|
{ |
|
u32 new_udccr; |
|
|
|
new_udccr = ((ep->config << UDCCONR_CN_S) & UDCCONR_CN) |
|
| ((ep->interface << UDCCONR_IN_S) & UDCCONR_IN) |
|
| ((ep->alternate << UDCCONR_AISN_S) & UDCCONR_AISN) |
|
| ((EPADDR(ep) << UDCCONR_EN_S) & UDCCONR_EN) |
|
| ((EPXFERTYPE(ep) << UDCCONR_ET_S) & UDCCONR_ET) |
|
| ((ep->dir_in) ? UDCCONR_ED : 0) |
|
| ((ep->fifo_size << UDCCONR_MPS_S) & UDCCONR_MPS) |
|
| UDCCONR_EE; |
|
|
|
udc_ep_writel(ep, UDCCR, new_udccr); |
|
} |
|
|
|
/** |
|
* pxa_eps_setup - Sets up all usb physical endpoints |
|
* @dev: udc device |
|
* |
|
* Setup all pxa physical endpoints, except ep0 |
|
*/ |
|
static void pxa_eps_setup(struct pxa_udc *dev) |
|
{ |
|
unsigned int i; |
|
|
|
dev_dbg(dev->dev, "%s: dev=%p\n", __func__, dev); |
|
|
|
for (i = 1; i < NR_PXA_ENDPOINTS; i++) |
|
pxa_ep_setup(&dev->pxa_ep[i]); |
|
} |
|
|
|
/** |
|
* pxa_ep_alloc_request - Allocate usb request |
|
* @_ep: usb endpoint |
|
* @gfp_flags: |
|
* |
|
* For the pxa27x, these can just wrap kmalloc/kfree. gadget drivers |
|
* must still pass correctly initialized endpoints, since other controller |
|
* drivers may care about how it's currently set up (dma issues etc). |
|
*/ |
|
static struct usb_request * |
|
pxa_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) |
|
{ |
|
struct pxa27x_request *req; |
|
|
|
req = kzalloc(sizeof *req, gfp_flags); |
|
if (!req) |
|
return NULL; |
|
|
|
INIT_LIST_HEAD(&req->queue); |
|
req->in_use = 0; |
|
req->udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
|
|
return &req->req; |
|
} |
|
|
|
/** |
|
* pxa_ep_free_request - Free usb request |
|
* @_ep: usb endpoint |
|
* @_req: usb request |
|
* |
|
* Wrapper around kfree to free _req |
|
*/ |
|
static void pxa_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) |
|
{ |
|
struct pxa27x_request *req; |
|
|
|
req = container_of(_req, struct pxa27x_request, req); |
|
WARN_ON(!list_empty(&req->queue)); |
|
kfree(req); |
|
} |
|
|
|
/** |
|
* ep_add_request - add a request to the endpoint's queue |
|
* @ep: usb endpoint |
|
* @req: usb request |
|
* |
|
* Context: ep->lock held |
|
* |
|
* Queues the request in the endpoint's queue, and enables the interrupts |
|
* on the endpoint. |
|
*/ |
|
static void ep_add_request(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
if (unlikely(!req)) |
|
return; |
|
ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req, |
|
req->req.length, udc_ep_readl(ep, UDCCSR)); |
|
|
|
req->in_use = 1; |
|
list_add_tail(&req->queue, &ep->queue); |
|
pio_irq_enable(ep); |
|
} |
|
|
|
/** |
|
* ep_del_request - removes a request from the endpoint's queue |
|
* @ep: usb endpoint |
|
* @req: usb request |
|
* |
|
* Context: ep->lock held |
|
* |
|
* Unqueue the request from the endpoint's queue. If there are no more requests |
|
* on the endpoint, and if it's not the control endpoint, interrupts are |
|
* disabled on the endpoint. |
|
*/ |
|
static void ep_del_request(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
if (unlikely(!req)) |
|
return; |
|
ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req, |
|
req->req.length, udc_ep_readl(ep, UDCCSR)); |
|
|
|
list_del_init(&req->queue); |
|
req->in_use = 0; |
|
if (!is_ep0(ep) && list_empty(&ep->queue)) |
|
pio_irq_disable(ep); |
|
} |
|
|
|
/** |
|
* req_done - Complete an usb request |
|
* @ep: pxa physical endpoint |
|
* @req: pxa request |
|
* @status: usb request status sent to gadget API |
|
* @pflags: flags of previous spinlock_irq_save() or NULL if no lock held |
|
* |
|
* Context: ep->lock held if flags not NULL, else ep->lock released |
|
* |
|
* Retire a pxa27x usb request. Endpoint must be locked. |
|
*/ |
|
static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status, |
|
unsigned long *pflags) |
|
{ |
|
unsigned long flags; |
|
|
|
ep_del_request(ep, req); |
|
if (likely(req->req.status == -EINPROGRESS)) |
|
req->req.status = status; |
|
else |
|
status = req->req.status; |
|
|
|
if (status && status != -ESHUTDOWN) |
|
ep_dbg(ep, "complete req %p stat %d len %u/%u\n", |
|
&req->req, status, |
|
req->req.actual, req->req.length); |
|
|
|
if (pflags) |
|
spin_unlock_irqrestore(&ep->lock, *pflags); |
|
local_irq_save(flags); |
|
usb_gadget_giveback_request(&req->udc_usb_ep->usb_ep, &req->req); |
|
local_irq_restore(flags); |
|
if (pflags) |
|
spin_lock_irqsave(&ep->lock, *pflags); |
|
} |
|
|
|
/** |
|
* ep_end_out_req - Ends endpoint OUT request |
|
* @ep: physical endpoint |
|
* @req: pxa request |
|
* @pflags: flags of previous spinlock_irq_save() or NULL if no lock held |
|
* |
|
* Context: ep->lock held or released (see req_done()) |
|
* |
|
* Ends endpoint OUT request (completes usb request). |
|
*/ |
|
static void ep_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req, |
|
unsigned long *pflags) |
|
{ |
|
inc_ep_stats_reqs(ep, !USB_DIR_IN); |
|
req_done(ep, req, 0, pflags); |
|
} |
|
|
|
/** |
|
* ep0_end_out_req - Ends control endpoint OUT request (ends data stage) |
|
* @ep: physical endpoint |
|
* @req: pxa request |
|
* @pflags: flags of previous spinlock_irq_save() or NULL if no lock held |
|
* |
|
* Context: ep->lock held or released (see req_done()) |
|
* |
|
* Ends control endpoint OUT request (completes usb request), and puts |
|
* control endpoint into idle state |
|
*/ |
|
static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req, |
|
unsigned long *pflags) |
|
{ |
|
set_ep0state(ep->dev, OUT_STATUS_STAGE); |
|
ep_end_out_req(ep, req, pflags); |
|
ep0_idle(ep->dev); |
|
} |
|
|
|
/** |
|
* ep_end_in_req - Ends endpoint IN request |
|
* @ep: physical endpoint |
|
* @req: pxa request |
|
* @pflags: flags of previous spinlock_irq_save() or NULL if no lock held |
|
* |
|
* Context: ep->lock held or released (see req_done()) |
|
* |
|
* Ends endpoint IN request (completes usb request). |
|
*/ |
|
static void ep_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req, |
|
unsigned long *pflags) |
|
{ |
|
inc_ep_stats_reqs(ep, USB_DIR_IN); |
|
req_done(ep, req, 0, pflags); |
|
} |
|
|
|
/** |
|
* ep0_end_in_req - Ends control endpoint IN request (ends data stage) |
|
* @ep: physical endpoint |
|
* @req: pxa request |
|
* @pflags: flags of previous spinlock_irq_save() or NULL if no lock held |
|
* |
|
* Context: ep->lock held or released (see req_done()) |
|
* |
|
* Ends control endpoint IN request (completes usb request), and puts |
|
* control endpoint into status state |
|
*/ |
|
static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req, |
|
unsigned long *pflags) |
|
{ |
|
set_ep0state(ep->dev, IN_STATUS_STAGE); |
|
ep_end_in_req(ep, req, pflags); |
|
} |
|
|
|
/** |
|
* nuke - Dequeue all requests |
|
* @ep: pxa endpoint |
|
* @status: usb request status |
|
* |
|
* Context: ep->lock released |
|
* |
|
* Dequeues all requests on an endpoint. As a side effect, interrupts will be |
|
* disabled on that endpoint (because no more requests). |
|
*/ |
|
static void nuke(struct pxa_ep *ep, int status) |
|
{ |
|
struct pxa27x_request *req; |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&ep->lock, flags); |
|
while (!list_empty(&ep->queue)) { |
|
req = list_entry(ep->queue.next, struct pxa27x_request, queue); |
|
req_done(ep, req, status, &flags); |
|
} |
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
} |
|
|
|
/** |
|
* read_packet - transfer 1 packet from an OUT endpoint into request |
|
* @ep: pxa physical endpoint |
|
* @req: usb request |
|
* |
|
* Takes bytes from OUT endpoint and transfers them info the usb request. |
|
* If there is less space in request than bytes received in OUT endpoint, |
|
* bytes are left in the OUT endpoint. |
|
* |
|
* Returns how many bytes were actually transferred |
|
*/ |
|
static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
u32 *buf; |
|
int bytes_ep, bufferspace, count, i; |
|
|
|
bytes_ep = ep_count_bytes_remain(ep); |
|
bufferspace = req->req.length - req->req.actual; |
|
|
|
buf = (u32 *)(req->req.buf + req->req.actual); |
|
prefetchw(buf); |
|
|
|
if (likely(!ep_is_empty(ep))) |
|
count = min(bytes_ep, bufferspace); |
|
else /* zlp */ |
|
count = 0; |
|
|
|
for (i = count; i > 0; i -= 4) |
|
*buf++ = udc_ep_readl(ep, UDCDR); |
|
req->req.actual += count; |
|
|
|
ep_write_UDCCSR(ep, UDCCSR_PC); |
|
|
|
return count; |
|
} |
|
|
|
/** |
|
* write_packet - transfer 1 packet from request into an IN endpoint |
|
* @ep: pxa physical endpoint |
|
* @req: usb request |
|
* @max: max bytes that fit into endpoint |
|
* |
|
* Takes bytes from usb request, and transfers them into the physical |
|
* endpoint. If there are no bytes to transfer, doesn't write anything |
|
* to physical endpoint. |
|
* |
|
* Returns how many bytes were actually transferred. |
|
*/ |
|
static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req, |
|
unsigned int max) |
|
{ |
|
int length, count, remain, i; |
|
u32 *buf; |
|
u8 *buf_8; |
|
|
|
buf = (u32 *)(req->req.buf + req->req.actual); |
|
prefetch(buf); |
|
|
|
length = min(req->req.length - req->req.actual, max); |
|
req->req.actual += length; |
|
|
|
remain = length & 0x3; |
|
count = length & ~(0x3); |
|
for (i = count; i > 0 ; i -= 4) |
|
udc_ep_writel(ep, UDCDR, *buf++); |
|
|
|
buf_8 = (u8 *)buf; |
|
for (i = remain; i > 0; i--) |
|
udc_ep_writeb(ep, UDCDR, *buf_8++); |
|
|
|
ep_vdbg(ep, "length=%d+%d, udccsr=0x%03x\n", count, remain, |
|
udc_ep_readl(ep, UDCCSR)); |
|
|
|
return length; |
|
} |
|
|
|
/** |
|
* read_fifo - Transfer packets from OUT endpoint into usb request |
|
* @ep: pxa physical endpoint |
|
* @req: usb request |
|
* |
|
* Context: interrupt handler |
|
* |
|
* Unload as many packets as possible from the fifo we use for usb OUT |
|
* transfers and put them into the request. Caller should have made sure |
|
* there's at least one packet ready. |
|
* Doesn't complete the request, that's the caller's job |
|
* |
|
* Returns 1 if the request completed, 0 otherwise |
|
*/ |
|
static int read_fifo(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
int count, is_short, completed = 0; |
|
|
|
while (epout_has_pkt(ep)) { |
|
count = read_packet(ep, req); |
|
inc_ep_stats_bytes(ep, count, !USB_DIR_IN); |
|
|
|
is_short = (count < ep->fifo_size); |
|
ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n", |
|
udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "", |
|
&req->req, req->req.actual, req->req.length); |
|
|
|
/* completion */ |
|
if (is_short || req->req.actual == req->req.length) { |
|
completed = 1; |
|
break; |
|
} |
|
/* finished that packet. the next one may be waiting... */ |
|
} |
|
return completed; |
|
} |
|
|
|
/** |
|
* write_fifo - transfer packets from usb request into an IN endpoint |
|
* @ep: pxa physical endpoint |
|
* @req: pxa usb request |
|
* |
|
* Write to an IN endpoint fifo, as many packets as possible. |
|
* irqs will use this to write the rest later. |
|
* caller guarantees at least one packet buffer is ready (or a zlp). |
|
* Doesn't complete the request, that's the caller's job |
|
* |
|
* Returns 1 if request fully transferred, 0 if partial transfer |
|
*/ |
|
static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
unsigned max; |
|
int count, is_short, is_last = 0, completed = 0, totcount = 0; |
|
u32 udccsr; |
|
|
|
max = ep->fifo_size; |
|
do { |
|
udccsr = udc_ep_readl(ep, UDCCSR); |
|
if (udccsr & UDCCSR_PC) { |
|
ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n", |
|
udccsr); |
|
ep_write_UDCCSR(ep, UDCCSR_PC); |
|
} |
|
if (udccsr & UDCCSR_TRN) { |
|
ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n", |
|
udccsr); |
|
ep_write_UDCCSR(ep, UDCCSR_TRN); |
|
} |
|
|
|
count = write_packet(ep, req, max); |
|
inc_ep_stats_bytes(ep, count, USB_DIR_IN); |
|
totcount += count; |
|
|
|
/* last packet is usually short (or a zlp) */ |
|
if (unlikely(count < max)) { |
|
is_last = 1; |
|
is_short = 1; |
|
} else { |
|
if (likely(req->req.length > req->req.actual) |
|
|| req->req.zero) |
|
is_last = 0; |
|
else |
|
is_last = 1; |
|
/* interrupt/iso maxpacket may not fill the fifo */ |
|
is_short = unlikely(max < ep->fifo_size); |
|
} |
|
|
|
if (is_short) |
|
ep_write_UDCCSR(ep, UDCCSR_SP); |
|
|
|
/* requests complete when all IN data is in the FIFO */ |
|
if (is_last) { |
|
completed = 1; |
|
break; |
|
} |
|
} while (!ep_is_full(ep)); |
|
|
|
ep_dbg(ep, "wrote count:%d bytes%s%s, left:%d req=%p\n", |
|
totcount, is_last ? "/L" : "", is_short ? "/S" : "", |
|
req->req.length - req->req.actual, &req->req); |
|
|
|
return completed; |
|
} |
|
|
|
/** |
|
* read_ep0_fifo - Transfer packets from control endpoint into usb request |
|
* @ep: control endpoint |
|
* @req: pxa usb request |
|
* |
|
* Special ep0 version of the above read_fifo. Reads as many bytes from control |
|
* endpoint as can be read, and stores them into usb request (limited by request |
|
* maximum length). |
|
* |
|
* Returns 0 if usb request only partially filled, 1 if fully filled |
|
*/ |
|
static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
int count, is_short, completed = 0; |
|
|
|
while (epout_has_pkt(ep)) { |
|
count = read_packet(ep, req); |
|
ep_write_UDCCSR(ep, UDCCSR0_OPC); |
|
inc_ep_stats_bytes(ep, count, !USB_DIR_IN); |
|
|
|
is_short = (count < ep->fifo_size); |
|
ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n", |
|
udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "", |
|
&req->req, req->req.actual, req->req.length); |
|
|
|
if (is_short || req->req.actual >= req->req.length) { |
|
completed = 1; |
|
break; |
|
} |
|
} |
|
|
|
return completed; |
|
} |
|
|
|
/** |
|
* write_ep0_fifo - Send a request to control endpoint (ep0 in) |
|
* @ep: control endpoint |
|
* @req: request |
|
* |
|
* Context: interrupt handler |
|
* |
|
* Sends a request (or a part of the request) to the control endpoint (ep0 in). |
|
* If the request doesn't fit, the remaining part will be sent from irq. |
|
* The request is considered fully written only if either : |
|
* - last write transferred all remaining bytes, but fifo was not fully filled |
|
* - last write was a 0 length write |
|
* |
|
* Returns 1 if request fully written, 0 if request only partially sent |
|
*/ |
|
static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) |
|
{ |
|
unsigned count; |
|
int is_last, is_short; |
|
|
|
count = write_packet(ep, req, EP0_FIFO_SIZE); |
|
inc_ep_stats_bytes(ep, count, USB_DIR_IN); |
|
|
|
is_short = (count < EP0_FIFO_SIZE); |
|
is_last = ((count == 0) || (count < EP0_FIFO_SIZE)); |
|
|
|
/* Sends either a short packet or a 0 length packet */ |
|
if (unlikely(is_short)) |
|
ep_write_UDCCSR(ep, UDCCSR0_IPR); |
|
|
|
ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n", |
|
count, is_short ? "/S" : "", is_last ? "/L" : "", |
|
req->req.length - req->req.actual, |
|
&req->req, udc_ep_readl(ep, UDCCSR)); |
|
|
|
return is_last; |
|
} |
|
|
|
/** |
|
* pxa_ep_queue - Queue a request into an IN endpoint |
|
* @_ep: usb endpoint |
|
* @_req: usb request |
|
* @gfp_flags: flags |
|
* |
|
* Context: thread context or from the interrupt handler in the |
|
* special case of ep0 setup : |
|
* (irq->handle_ep0_ctrl_req->gadget_setup->pxa_ep_queue) |
|
* |
|
* Returns 0 if succedeed, error otherwise |
|
*/ |
|
static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, |
|
gfp_t gfp_flags) |
|
{ |
|
struct udc_usb_ep *udc_usb_ep; |
|
struct pxa_ep *ep; |
|
struct pxa27x_request *req; |
|
struct pxa_udc *dev; |
|
unsigned long flags; |
|
int rc = 0; |
|
int is_first_req; |
|
unsigned length; |
|
int recursion_detected; |
|
|
|
req = container_of(_req, struct pxa27x_request, req); |
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
|
|
if (unlikely(!_req || !_req->complete || !_req->buf)) |
|
return -EINVAL; |
|
|
|
if (unlikely(!_ep)) |
|
return -EINVAL; |
|
|
|
ep = udc_usb_ep->pxa_ep; |
|
if (unlikely(!ep)) |
|
return -EINVAL; |
|
|
|
dev = ep->dev; |
|
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { |
|
ep_dbg(ep, "bogus device state\n"); |
|
return -ESHUTDOWN; |
|
} |
|
|
|
/* iso is always one packet per request, that's the only way |
|
* we can report per-packet status. that also helps with dma. |
|
*/ |
|
if (unlikely(EPXFERTYPE_is_ISO(ep) |
|
&& req->req.length > ep->fifo_size)) |
|
return -EMSGSIZE; |
|
|
|
spin_lock_irqsave(&ep->lock, flags); |
|
recursion_detected = ep->in_handle_ep; |
|
|
|
is_first_req = list_empty(&ep->queue); |
|
ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n", |
|
_req, is_first_req ? "yes" : "no", |
|
_req->length, _req->buf); |
|
|
|
if (!ep->enabled) { |
|
_req->status = -ESHUTDOWN; |
|
rc = -ESHUTDOWN; |
|
goto out_locked; |
|
} |
|
|
|
if (req->in_use) { |
|
ep_err(ep, "refusing to queue req %p (already queued)\n", req); |
|
goto out_locked; |
|
} |
|
|
|
length = _req->length; |
|
_req->status = -EINPROGRESS; |
|
_req->actual = 0; |
|
|
|
ep_add_request(ep, req); |
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
|
|
if (is_ep0(ep)) { |
|
switch (dev->ep0state) { |
|
case WAIT_ACK_SET_CONF_INTERF: |
|
if (length == 0) { |
|
ep_end_in_req(ep, req, NULL); |
|
} else { |
|
ep_err(ep, "got a request of %d bytes while" |
|
"in state WAIT_ACK_SET_CONF_INTERF\n", |
|
length); |
|
ep_del_request(ep, req); |
|
rc = -EL2HLT; |
|
} |
|
ep0_idle(ep->dev); |
|
break; |
|
case IN_DATA_STAGE: |
|
if (!ep_is_full(ep)) |
|
if (write_ep0_fifo(ep, req)) |
|
ep0_end_in_req(ep, req, NULL); |
|
break; |
|
case OUT_DATA_STAGE: |
|
if ((length == 0) || !epout_has_pkt(ep)) |
|
if (read_ep0_fifo(ep, req)) |
|
ep0_end_out_req(ep, req, NULL); |
|
break; |
|
default: |
|
ep_err(ep, "odd state %s to send me a request\n", |
|
EP0_STNAME(ep->dev)); |
|
ep_del_request(ep, req); |
|
rc = -EL2HLT; |
|
break; |
|
} |
|
} else { |
|
if (!recursion_detected) |
|
handle_ep(ep); |
|
} |
|
|
|
out: |
|
return rc; |
|
out_locked: |
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
goto out; |
|
} |
|
|
|
/** |
|
* pxa_ep_dequeue - Dequeue one request |
|
* @_ep: usb endpoint |
|
* @_req: usb request |
|
* |
|
* Return 0 if no error, -EINVAL or -ECONNRESET otherwise |
|
*/ |
|
static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) |
|
{ |
|
struct pxa_ep *ep; |
|
struct udc_usb_ep *udc_usb_ep; |
|
struct pxa27x_request *req; |
|
unsigned long flags; |
|
int rc = -EINVAL; |
|
|
|
if (!_ep) |
|
return rc; |
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
ep = udc_usb_ep->pxa_ep; |
|
if (!ep || is_ep0(ep)) |
|
return rc; |
|
|
|
spin_lock_irqsave(&ep->lock, flags); |
|
|
|
/* make sure it's actually queued on this endpoint */ |
|
list_for_each_entry(req, &ep->queue, queue) { |
|
if (&req->req == _req) { |
|
rc = 0; |
|
break; |
|
} |
|
} |
|
|
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
if (!rc) |
|
req_done(ep, req, -ECONNRESET, NULL); |
|
return rc; |
|
} |
|
|
|
/** |
|
* pxa_ep_set_halt - Halts operations on one endpoint |
|
* @_ep: usb endpoint |
|
* @value: |
|
* |
|
* Returns 0 if no error, -EINVAL, -EROFS, -EAGAIN otherwise |
|
*/ |
|
static int pxa_ep_set_halt(struct usb_ep *_ep, int value) |
|
{ |
|
struct pxa_ep *ep; |
|
struct udc_usb_ep *udc_usb_ep; |
|
unsigned long flags; |
|
int rc; |
|
|
|
|
|
if (!_ep) |
|
return -EINVAL; |
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
ep = udc_usb_ep->pxa_ep; |
|
if (!ep || is_ep0(ep)) |
|
return -EINVAL; |
|
|
|
if (value == 0) { |
|
/* |
|
* This path (reset toggle+halt) is needed to implement |
|
* SET_INTERFACE on normal hardware. but it can't be |
|
* done from software on the PXA UDC, and the hardware |
|
* forgets to do it as part of SET_INTERFACE automagic. |
|
*/ |
|
ep_dbg(ep, "only host can clear halt\n"); |
|
return -EROFS; |
|
} |
|
|
|
spin_lock_irqsave(&ep->lock, flags); |
|
|
|
rc = -EAGAIN; |
|
if (ep->dir_in && (ep_is_full(ep) || !list_empty(&ep->queue))) |
|
goto out; |
|
|
|
/* FST, FEF bits are the same for control and non control endpoints */ |
|
rc = 0; |
|
ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF); |
|
if (is_ep0(ep)) |
|
set_ep0state(ep->dev, STALL); |
|
|
|
out: |
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
return rc; |
|
} |
|
|
|
/** |
|
* pxa_ep_fifo_status - Get how many bytes in physical endpoint |
|
* @_ep: usb endpoint |
|
* |
|
* Returns number of bytes in OUT fifos. Broken for IN fifos. |
|
*/ |
|
static int pxa_ep_fifo_status(struct usb_ep *_ep) |
|
{ |
|
struct pxa_ep *ep; |
|
struct udc_usb_ep *udc_usb_ep; |
|
|
|
if (!_ep) |
|
return -ENODEV; |
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
ep = udc_usb_ep->pxa_ep; |
|
if (!ep || is_ep0(ep)) |
|
return -ENODEV; |
|
|
|
if (ep->dir_in) |
|
return -EOPNOTSUPP; |
|
if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN || ep_is_empty(ep)) |
|
return 0; |
|
else |
|
return ep_count_bytes_remain(ep) + 1; |
|
} |
|
|
|
/** |
|
* pxa_ep_fifo_flush - Flushes one endpoint |
|
* @_ep: usb endpoint |
|
* |
|
* Discards all data in one endpoint(IN or OUT), except control endpoint. |
|
*/ |
|
static void pxa_ep_fifo_flush(struct usb_ep *_ep) |
|
{ |
|
struct pxa_ep *ep; |
|
struct udc_usb_ep *udc_usb_ep; |
|
unsigned long flags; |
|
|
|
if (!_ep) |
|
return; |
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
ep = udc_usb_ep->pxa_ep; |
|
if (!ep || is_ep0(ep)) |
|
return; |
|
|
|
spin_lock_irqsave(&ep->lock, flags); |
|
|
|
if (unlikely(!list_empty(&ep->queue))) |
|
ep_dbg(ep, "called while queue list not empty\n"); |
|
ep_dbg(ep, "called\n"); |
|
|
|
/* for OUT, just read and discard the FIFO contents. */ |
|
if (!ep->dir_in) { |
|
while (!ep_is_empty(ep)) |
|
udc_ep_readl(ep, UDCDR); |
|
} else { |
|
/* most IN status is the same, but ISO can't stall */ |
|
ep_write_UDCCSR(ep, |
|
UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN |
|
| (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST)); |
|
} |
|
|
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
} |
|
|
|
/** |
|
* pxa_ep_enable - Enables usb endpoint |
|
* @_ep: usb endpoint |
|
* @desc: usb endpoint descriptor |
|
* |
|
* Nothing much to do here, as ep configuration is done once and for all |
|
* before udc is enabled. After udc enable, no physical endpoint configuration |
|
* can be changed. |
|
* Function makes sanity checks and flushes the endpoint. |
|
*/ |
|
static int pxa_ep_enable(struct usb_ep *_ep, |
|
const struct usb_endpoint_descriptor *desc) |
|
{ |
|
struct pxa_ep *ep; |
|
struct udc_usb_ep *udc_usb_ep; |
|
struct pxa_udc *udc; |
|
|
|
if (!_ep || !desc) |
|
return -EINVAL; |
|
|
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
if (udc_usb_ep->pxa_ep) { |
|
ep = udc_usb_ep->pxa_ep; |
|
ep_warn(ep, "usb_ep %s already enabled, doing nothing\n", |
|
_ep->name); |
|
} else { |
|
ep = find_pxa_ep(udc_usb_ep->dev, udc_usb_ep); |
|
} |
|
|
|
if (!ep || is_ep0(ep)) { |
|
dev_err(udc_usb_ep->dev->dev, |
|
"unable to match pxa_ep for ep %s\n", |
|
_ep->name); |
|
return -EINVAL; |
|
} |
|
|
|
if ((desc->bDescriptorType != USB_DT_ENDPOINT) |
|
|| (ep->type != usb_endpoint_type(desc))) { |
|
ep_err(ep, "type mismatch\n"); |
|
return -EINVAL; |
|
} |
|
|
|
if (ep->fifo_size < usb_endpoint_maxp(desc)) { |
|
ep_err(ep, "bad maxpacket\n"); |
|
return -ERANGE; |
|
} |
|
|
|
udc_usb_ep->pxa_ep = ep; |
|
udc = ep->dev; |
|
|
|
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { |
|
ep_err(ep, "bogus device state\n"); |
|
return -ESHUTDOWN; |
|
} |
|
|
|
ep->enabled = 1; |
|
|
|
/* flush fifo (mostly for OUT buffers) */ |
|
pxa_ep_fifo_flush(_ep); |
|
|
|
ep_dbg(ep, "enabled\n"); |
|
return 0; |
|
} |
|
|
|
/** |
|
* pxa_ep_disable - Disable usb endpoint |
|
* @_ep: usb endpoint |
|
* |
|
* Same as for pxa_ep_enable, no physical endpoint configuration can be |
|
* changed. |
|
* Function flushes the endpoint and related requests. |
|
*/ |
|
static int pxa_ep_disable(struct usb_ep *_ep) |
|
{ |
|
struct pxa_ep *ep; |
|
struct udc_usb_ep *udc_usb_ep; |
|
|
|
if (!_ep) |
|
return -EINVAL; |
|
|
|
udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); |
|
ep = udc_usb_ep->pxa_ep; |
|
if (!ep || is_ep0(ep) || !list_empty(&ep->queue)) |
|
return -EINVAL; |
|
|
|
ep->enabled = 0; |
|
nuke(ep, -ESHUTDOWN); |
|
|
|
pxa_ep_fifo_flush(_ep); |
|
udc_usb_ep->pxa_ep = NULL; |
|
|
|
ep_dbg(ep, "disabled\n"); |
|
return 0; |
|
} |
|
|
|
static const struct usb_ep_ops pxa_ep_ops = { |
|
.enable = pxa_ep_enable, |
|
.disable = pxa_ep_disable, |
|
|
|
.alloc_request = pxa_ep_alloc_request, |
|
.free_request = pxa_ep_free_request, |
|
|
|
.queue = pxa_ep_queue, |
|
.dequeue = pxa_ep_dequeue, |
|
|
|
.set_halt = pxa_ep_set_halt, |
|
.fifo_status = pxa_ep_fifo_status, |
|
.fifo_flush = pxa_ep_fifo_flush, |
|
}; |
|
|
|
/** |
|
* dplus_pullup - Connect or disconnect pullup resistor to D+ pin |
|
* @udc: udc device |
|
* @on: 0 if disconnect pullup resistor, 1 otherwise |
|
* Context: any |
|
* |
|
* Handle D+ pullup resistor, make the device visible to the usb bus, and |
|
* declare it as a full speed usb device |
|
*/ |
|
static void dplus_pullup(struct pxa_udc *udc, int on) |
|
{ |
|
if (udc->gpiod) { |
|
gpiod_set_value(udc->gpiod, on); |
|
} else if (udc->udc_command) { |
|
if (on) |
|
udc->udc_command(PXA2XX_UDC_CMD_CONNECT); |
|
else |
|
udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT); |
|
} |
|
udc->pullup_on = on; |
|
} |
|
|
|
/** |
|
* pxa_udc_get_frame - Returns usb frame number |
|
* @_gadget: usb gadget |
|
*/ |
|
static int pxa_udc_get_frame(struct usb_gadget *_gadget) |
|
{ |
|
struct pxa_udc *udc = to_gadget_udc(_gadget); |
|
|
|
return (udc_readl(udc, UDCFNR) & 0x7ff); |
|
} |
|
|
|
/** |
|
* pxa_udc_wakeup - Force udc device out of suspend |
|
* @_gadget: usb gadget |
|
* |
|
* Returns 0 if successful, error code otherwise |
|
*/ |
|
static int pxa_udc_wakeup(struct usb_gadget *_gadget) |
|
{ |
|
struct pxa_udc *udc = to_gadget_udc(_gadget); |
|
|
|
/* host may not have enabled remote wakeup */ |
|
if ((udc_readl(udc, UDCCR) & UDCCR_DWRE) == 0) |
|
return -EHOSTUNREACH; |
|
udc_set_mask_UDCCR(udc, UDCCR_UDR); |
|
return 0; |
|
} |
|
|
|
static void udc_enable(struct pxa_udc *udc); |
|
static void udc_disable(struct pxa_udc *udc); |
|
|
|
/** |
|
* should_enable_udc - Tells if UDC should be enabled |
|
* @udc: udc device |
|
* Context: any |
|
* |
|
* The UDC should be enabled if : |
|
* - the pullup resistor is connected |
|
* - and a gadget driver is bound |
|
* - and vbus is sensed (or no vbus sense is available) |
|
* |
|
* Returns 1 if UDC should be enabled, 0 otherwise |
|
*/ |
|
static int should_enable_udc(struct pxa_udc *udc) |
|
{ |
|
int put_on; |
|
|
|
put_on = ((udc->pullup_on) && (udc->driver)); |
|
put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver))); |
|
return put_on; |
|
} |
|
|
|
/** |
|
* should_disable_udc - Tells if UDC should be disabled |
|
* @udc: udc device |
|
* Context: any |
|
* |
|
* The UDC should be disabled if : |
|
* - the pullup resistor is not connected |
|
* - or no gadget driver is bound |
|
* - or no vbus is sensed (when vbus sesing is available) |
|
* |
|
* Returns 1 if UDC should be disabled |
|
*/ |
|
static int should_disable_udc(struct pxa_udc *udc) |
|
{ |
|
int put_off; |
|
|
|
put_off = ((!udc->pullup_on) || (!udc->driver)); |
|
put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver))); |
|
return put_off; |
|
} |
|
|
|
/** |
|
* pxa_udc_pullup - Offer manual D+ pullup control |
|
* @_gadget: usb gadget using the control |
|
* @is_active: 0 if disconnect, else connect D+ pullup resistor |
|
* |
|
* Context: task context, might sleep |
|
* |
|
* Returns 0 if OK, -EOPNOTSUPP if udc driver doesn't handle D+ pullup |
|
*/ |
|
static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) |
|
{ |
|
struct pxa_udc *udc = to_gadget_udc(_gadget); |
|
|
|
if (!udc->gpiod && !udc->udc_command) |
|
return -EOPNOTSUPP; |
|
|
|
dplus_pullup(udc, is_active); |
|
|
|
if (should_enable_udc(udc)) |
|
udc_enable(udc); |
|
if (should_disable_udc(udc)) |
|
udc_disable(udc); |
|
return 0; |
|
} |
|
|
|
/** |
|
* pxa_udc_vbus_session - Called by external transceiver to enable/disable udc |
|
* @_gadget: usb gadget |
|
* @is_active: 0 if should disable the udc, 1 if should enable |
|
* |
|
* Enables the udc, and optionnaly activates D+ pullup resistor. Or disables the |
|
* udc, and deactivates D+ pullup resistor. |
|
* |
|
* Returns 0 |
|
*/ |
|
static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) |
|
{ |
|
struct pxa_udc *udc = to_gadget_udc(_gadget); |
|
|
|
udc->vbus_sensed = is_active; |
|
if (should_enable_udc(udc)) |
|
udc_enable(udc); |
|
if (should_disable_udc(udc)) |
|
udc_disable(udc); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* pxa_udc_vbus_draw - Called by gadget driver after SET_CONFIGURATION completed |
|
* @_gadget: usb gadget |
|
* @mA: current drawn |
|
* |
|
* Context: task context, might sleep |
|
* |
|
* Called after a configuration was chosen by a USB host, to inform how much |
|
* current can be drawn by the device from VBus line. |
|
* |
|
* Returns 0 or -EOPNOTSUPP if no transceiver is handling the udc |
|
*/ |
|
static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) |
|
{ |
|
struct pxa_udc *udc; |
|
|
|
udc = to_gadget_udc(_gadget); |
|
if (!IS_ERR_OR_NULL(udc->transceiver)) |
|
return usb_phy_set_power(udc->transceiver, mA); |
|
return -EOPNOTSUPP; |
|
} |
|
|
|
/** |
|
* pxa_udc_phy_event - Called by phy upon VBus event |
|
* @nb: notifier block |
|
* @action: phy action, is vbus connect or disconnect |
|
* @data: the usb_gadget structure in pxa_udc |
|
* |
|
* Called by the USB Phy when a cable connect or disconnect is sensed. |
|
* |
|
* Returns 0 |
|
*/ |
|
static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action, |
|
void *data) |
|
{ |
|
struct usb_gadget *gadget = data; |
|
|
|
switch (action) { |
|
case USB_EVENT_VBUS: |
|
usb_gadget_vbus_connect(gadget); |
|
return NOTIFY_OK; |
|
case USB_EVENT_NONE: |
|
usb_gadget_vbus_disconnect(gadget); |
|
return NOTIFY_OK; |
|
default: |
|
return NOTIFY_DONE; |
|
} |
|
} |
|
|
|
static struct notifier_block pxa27x_udc_phy = { |
|
.notifier_call = pxa_udc_phy_event, |
|
}; |
|
|
|
static int pxa27x_udc_start(struct usb_gadget *g, |
|
struct usb_gadget_driver *driver); |
|
static int pxa27x_udc_stop(struct usb_gadget *g); |
|
|
|
static const struct usb_gadget_ops pxa_udc_ops = { |
|
.get_frame = pxa_udc_get_frame, |
|
.wakeup = pxa_udc_wakeup, |
|
.pullup = pxa_udc_pullup, |
|
.vbus_session = pxa_udc_vbus_session, |
|
.vbus_draw = pxa_udc_vbus_draw, |
|
.udc_start = pxa27x_udc_start, |
|
.udc_stop = pxa27x_udc_stop, |
|
}; |
|
|
|
/** |
|
* udc_disable - disable udc device controller |
|
* @udc: udc device |
|
* Context: any |
|
* |
|
* Disables the udc device : disables clocks, udc interrupts, control endpoint |
|
* interrupts. |
|
*/ |
|
static void udc_disable(struct pxa_udc *udc) |
|
{ |
|
if (!udc->enabled) |
|
return; |
|
|
|
udc_writel(udc, UDCICR0, 0); |
|
udc_writel(udc, UDCICR1, 0); |
|
|
|
udc_clear_mask_UDCCR(udc, UDCCR_UDE); |
|
|
|
ep0_idle(udc); |
|
udc->gadget.speed = USB_SPEED_UNKNOWN; |
|
clk_disable(udc->clk); |
|
|
|
udc->enabled = 0; |
|
} |
|
|
|
/** |
|
* udc_init_data - Initialize udc device data structures |
|
* @dev: udc device |
|
* |
|
* Initializes gadget endpoint list, endpoints locks. No action is taken |
|
* on the hardware. |
|
*/ |
|
static void udc_init_data(struct pxa_udc *dev) |
|
{ |
|
int i; |
|
struct pxa_ep *ep; |
|
|
|
/* device/ep0 records init */ |
|
INIT_LIST_HEAD(&dev->gadget.ep_list); |
|
INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); |
|
dev->udc_usb_ep[0].pxa_ep = &dev->pxa_ep[0]; |
|
dev->gadget.quirk_altset_not_supp = 1; |
|
ep0_idle(dev); |
|
|
|
/* PXA endpoints init */ |
|
for (i = 0; i < NR_PXA_ENDPOINTS; i++) { |
|
ep = &dev->pxa_ep[i]; |
|
|
|
ep->enabled = is_ep0(ep); |
|
INIT_LIST_HEAD(&ep->queue); |
|
spin_lock_init(&ep->lock); |
|
} |
|
|
|
/* USB endpoints init */ |
|
for (i = 1; i < NR_USB_ENDPOINTS; i++) { |
|
list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list, |
|
&dev->gadget.ep_list); |
|
usb_ep_set_maxpacket_limit(&dev->udc_usb_ep[i].usb_ep, |
|
dev->udc_usb_ep[i].usb_ep.maxpacket); |
|
} |
|
} |
|
|
|
/** |
|
* udc_enable - Enables the udc device |
|
* @udc: udc device |
|
* |
|
* Enables the udc device : enables clocks, udc interrupts, control endpoint |
|
* interrupts, sets usb as UDC client and setups endpoints. |
|
*/ |
|
static void udc_enable(struct pxa_udc *udc) |
|
{ |
|
if (udc->enabled) |
|
return; |
|
|
|
clk_enable(udc->clk); |
|
udc_writel(udc, UDCICR0, 0); |
|
udc_writel(udc, UDCICR1, 0); |
|
udc_clear_mask_UDCCR(udc, UDCCR_UDE); |
|
|
|
ep0_idle(udc); |
|
udc->gadget.speed = USB_SPEED_FULL; |
|
memset(&udc->stats, 0, sizeof(udc->stats)); |
|
|
|
pxa_eps_setup(udc); |
|
udc_set_mask_UDCCR(udc, UDCCR_UDE); |
|
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM); |
|
udelay(2); |
|
if (udc_readl(udc, UDCCR) & UDCCR_EMCE) |
|
dev_err(udc->dev, "Configuration errors, udc disabled\n"); |
|
|
|
/* |
|
* Caller must be able to sleep in order to cope with startup transients |
|
*/ |
|
msleep(100); |
|
|
|
/* enable suspend/resume and reset irqs */ |
|
udc_writel(udc, UDCICR1, |
|
UDCICR1_IECC | UDCICR1_IERU |
|
| UDCICR1_IESU | UDCICR1_IERS); |
|
|
|
/* enable ep0 irqs */ |
|
pio_irq_enable(&udc->pxa_ep[0]); |
|
|
|
udc->enabled = 1; |
|
} |
|
|
|
/** |
|
* pxa27x_start - Register gadget driver |
|
* @g: gadget |
|
* @driver: gadget driver |
|
* |
|
* When a driver is successfully registered, it will receive control requests |
|
* including set_configuration(), which enables non-control requests. Then |
|
* usb traffic follows until a disconnect is reported. Then a host may connect |
|
* again, or the driver might get unbound. |
|
* |
|
* Note that the udc is not automatically enabled. Check function |
|
* should_enable_udc(). |
|
* |
|
* Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise |
|
*/ |
|
static int pxa27x_udc_start(struct usb_gadget *g, |
|
struct usb_gadget_driver *driver) |
|
{ |
|
struct pxa_udc *udc = to_pxa(g); |
|
int retval; |
|
|
|
/* first hook up the driver ... */ |
|
udc->driver = driver; |
|
|
|
if (!IS_ERR_OR_NULL(udc->transceiver)) { |
|
retval = otg_set_peripheral(udc->transceiver->otg, |
|
&udc->gadget); |
|
if (retval) { |
|
dev_err(udc->dev, "can't bind to transceiver\n"); |
|
goto fail; |
|
} |
|
} |
|
|
|
if (should_enable_udc(udc)) |
|
udc_enable(udc); |
|
return 0; |
|
|
|
fail: |
|
udc->driver = NULL; |
|
return retval; |
|
} |
|
|
|
/** |
|
* stop_activity - Stops udc endpoints |
|
* @udc: udc device |
|
* |
|
* Disables all udc endpoints (even control endpoint), report disconnect to |
|
* the gadget user. |
|
*/ |
|
static void stop_activity(struct pxa_udc *udc) |
|
{ |
|
int i; |
|
|
|
udc->gadget.speed = USB_SPEED_UNKNOWN; |
|
|
|
for (i = 0; i < NR_USB_ENDPOINTS; i++) |
|
pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep); |
|
} |
|
|
|
/** |
|
* pxa27x_udc_stop - Unregister the gadget driver |
|
* @g: gadget |
|
* |
|
* Returns 0 if no error, -ENODEV, -EINVAL otherwise |
|
*/ |
|
static int pxa27x_udc_stop(struct usb_gadget *g) |
|
{ |
|
struct pxa_udc *udc = to_pxa(g); |
|
|
|
stop_activity(udc); |
|
udc_disable(udc); |
|
|
|
udc->driver = NULL; |
|
|
|
if (!IS_ERR_OR_NULL(udc->transceiver)) |
|
return otg_set_peripheral(udc->transceiver->otg, NULL); |
|
return 0; |
|
} |
|
|
|
/** |
|
* handle_ep0_ctrl_req - handle control endpoint control request |
|
* @udc: udc device |
|
* @req: control request |
|
*/ |
|
static void handle_ep0_ctrl_req(struct pxa_udc *udc, |
|
struct pxa27x_request *req) |
|
{ |
|
struct pxa_ep *ep = &udc->pxa_ep[0]; |
|
union { |
|
struct usb_ctrlrequest r; |
|
u32 word[2]; |
|
} u; |
|
int i; |
|
int have_extrabytes = 0; |
|
unsigned long flags; |
|
|
|
nuke(ep, -EPROTO); |
|
spin_lock_irqsave(&ep->lock, flags); |
|
|
|
/* |
|
* In the PXA320 manual, in the section about Back-to-Back setup |
|
* packets, it describes this situation. The solution is to set OPC to |
|
* get rid of the status packet, and then continue with the setup |
|
* packet. Generalize to pxa27x CPUs. |
|
*/ |
|
if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0)) |
|
ep_write_UDCCSR(ep, UDCCSR0_OPC); |
|
|
|
/* read SETUP packet */ |
|
for (i = 0; i < 2; i++) { |
|
if (unlikely(ep_is_empty(ep))) |
|
goto stall; |
|
u.word[i] = udc_ep_readl(ep, UDCDR); |
|
} |
|
|
|
have_extrabytes = !ep_is_empty(ep); |
|
while (!ep_is_empty(ep)) { |
|
i = udc_ep_readl(ep, UDCDR); |
|
ep_err(ep, "wrong to have extra bytes for setup : 0x%08x\n", i); |
|
} |
|
|
|
ep_dbg(ep, "SETUP %02x.%02x v%04x i%04x l%04x\n", |
|
u.r.bRequestType, u.r.bRequest, |
|
le16_to_cpu(u.r.wValue), le16_to_cpu(u.r.wIndex), |
|
le16_to_cpu(u.r.wLength)); |
|
if (unlikely(have_extrabytes)) |
|
goto stall; |
|
|
|
if (u.r.bRequestType & USB_DIR_IN) |
|
set_ep0state(udc, IN_DATA_STAGE); |
|
else |
|
set_ep0state(udc, OUT_DATA_STAGE); |
|
|
|
/* Tell UDC to enter Data Stage */ |
|
ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC); |
|
|
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
i = udc->driver->setup(&udc->gadget, &u.r); |
|
spin_lock_irqsave(&ep->lock, flags); |
|
if (i < 0) |
|
goto stall; |
|
out: |
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
return; |
|
stall: |
|
ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n", |
|
udc_ep_readl(ep, UDCCSR), i); |
|
ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF); |
|
set_ep0state(udc, STALL); |
|
goto out; |
|
} |
|
|
|
/** |
|
* handle_ep0 - Handle control endpoint data transfers |
|
* @udc: udc device |
|
* @fifo_irq: 1 if triggered by fifo service type irq |
|
* @opc_irq: 1 if triggered by output packet complete type irq |
|
* |
|
* Context : interrupt handler |
|
* |
|
* Tries to transfer all pending request data into the endpoint and/or |
|
* transfer all pending data in the endpoint into usb requests. |
|
* Handles states of ep0 automata. |
|
* |
|
* PXA27x hardware handles several standard usb control requests without |
|
* driver notification. The requests fully handled by hardware are : |
|
* SET_ADDRESS, SET_FEATURE, CLEAR_FEATURE, GET_CONFIGURATION, GET_INTERFACE, |
|
* GET_STATUS |
|
* The requests handled by hardware, but with irq notification are : |
|
* SYNCH_FRAME, SET_CONFIGURATION, SET_INTERFACE |
|
* The remaining standard requests really handled by handle_ep0 are : |
|
* GET_DESCRIPTOR, SET_DESCRIPTOR, specific requests. |
|
* Requests standardized outside of USB 2.0 chapter 9 are handled more |
|
* uniformly, by gadget drivers. |
|
* |
|
* The control endpoint state machine is _not_ USB spec compliant, it's even |
|
* hardly compliant with Intel PXA270 developers guide. |
|
* The key points which inferred this state machine are : |
|
* - on every setup token, bit UDCCSR0_SA is raised and held until cleared by |
|
* software. |
|
* - on every OUT packet received, UDCCSR0_OPC is raised and held until |
|
* cleared by software. |
|
* - clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it |
|
* before reading ep0. |
|
* This is true only for PXA27x. This is not true anymore for PXA3xx family |
|
* (check Back-to-Back setup packet in developers guide). |
|
* - irq can be called on a "packet complete" event (opc_irq=1), while |
|
* UDCCSR0_OPC is not yet raised (delta can be as big as 100ms |
|
* from experimentation). |
|
* - as UDCCSR0_SA can be activated while in irq handling, and clearing |
|
* UDCCSR0_OPC would flush the setup data, we almost never clear UDCCSR0_OPC |
|
* => we never actually read the "status stage" packet of an IN data stage |
|
* => this is not documented in Intel documentation |
|
* - hardware as no idea of STATUS STAGE, it only handle SETUP STAGE and DATA |
|
* STAGE. The driver add STATUS STAGE to send last zero length packet in |
|
* OUT_STATUS_STAGE. |
|
* - special attention was needed for IN_STATUS_STAGE. If a packet complete |
|
* event is detected, we terminate the status stage without ackowledging the |
|
* packet (not to risk to loose a potential SETUP packet) |
|
*/ |
|
static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) |
|
{ |
|
u32 udccsr0; |
|
struct pxa_ep *ep = &udc->pxa_ep[0]; |
|
struct pxa27x_request *req = NULL; |
|
int completed = 0; |
|
|
|
if (!list_empty(&ep->queue)) |
|
req = list_entry(ep->queue.next, struct pxa27x_request, queue); |
|
|
|
udccsr0 = udc_ep_readl(ep, UDCCSR); |
|
ep_dbg(ep, "state=%s, req=%p, udccsr0=0x%03x, udcbcr=%d, irq_msk=%x\n", |
|
EP0_STNAME(udc), req, udccsr0, udc_ep_readl(ep, UDCBCR), |
|
(fifo_irq << 1 | opc_irq)); |
|
|
|
if (udccsr0 & UDCCSR0_SST) { |
|
ep_dbg(ep, "clearing stall status\n"); |
|
nuke(ep, -EPIPE); |
|
ep_write_UDCCSR(ep, UDCCSR0_SST); |
|
ep0_idle(udc); |
|
} |
|
|
|
if (udccsr0 & UDCCSR0_SA) { |
|
nuke(ep, 0); |
|
set_ep0state(udc, SETUP_STAGE); |
|
} |
|
|
|
switch (udc->ep0state) { |
|
case WAIT_FOR_SETUP: |
|
/* |
|
* Hardware bug : beware, we cannot clear OPC, since we would |
|
* miss a potential OPC irq for a setup packet. |
|
* So, we only do ... nothing, and hope for a next irq with |
|
* UDCCSR0_SA set. |
|
*/ |
|
break; |
|
case SETUP_STAGE: |
|
udccsr0 &= UDCCSR0_CTRL_REQ_MASK; |
|
if (likely(udccsr0 == UDCCSR0_CTRL_REQ_MASK)) |
|
handle_ep0_ctrl_req(udc, req); |
|
break; |
|
case IN_DATA_STAGE: /* GET_DESCRIPTOR */ |
|
if (epout_has_pkt(ep)) |
|
ep_write_UDCCSR(ep, UDCCSR0_OPC); |
|
if (req && !ep_is_full(ep)) |
|
completed = write_ep0_fifo(ep, req); |
|
if (completed) |
|
ep0_end_in_req(ep, req, NULL); |
|
break; |
|
case OUT_DATA_STAGE: /* SET_DESCRIPTOR */ |
|
if (epout_has_pkt(ep) && req) |
|
completed = read_ep0_fifo(ep, req); |
|
if (completed) |
|
ep0_end_out_req(ep, req, NULL); |
|
break; |
|
case STALL: |
|
ep_write_UDCCSR(ep, UDCCSR0_FST); |
|
break; |
|
case IN_STATUS_STAGE: |
|
/* |
|
* Hardware bug : beware, we cannot clear OPC, since we would |
|
* miss a potential PC irq for a setup packet. |
|
* So, we only put the ep0 into WAIT_FOR_SETUP state. |
|
*/ |
|
if (opc_irq) |
|
ep0_idle(udc); |
|
break; |
|
case OUT_STATUS_STAGE: |
|
case WAIT_ACK_SET_CONF_INTERF: |
|
ep_warn(ep, "should never get in %s state here!!!\n", |
|
EP0_STNAME(ep->dev)); |
|
ep0_idle(udc); |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* handle_ep - Handle endpoint data tranfers |
|
* @ep: pxa physical endpoint |
|
* |
|
* Tries to transfer all pending request data into the endpoint and/or |
|
* transfer all pending data in the endpoint into usb requests. |
|
* |
|
* Is always called from the interrupt handler. ep->lock must not be held. |
|
*/ |
|
static void handle_ep(struct pxa_ep *ep) |
|
{ |
|
struct pxa27x_request *req; |
|
int completed; |
|
u32 udccsr; |
|
int is_in = ep->dir_in; |
|
int loop = 0; |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&ep->lock, flags); |
|
if (ep->in_handle_ep) |
|
goto recursion_detected; |
|
ep->in_handle_ep = 1; |
|
|
|
do { |
|
completed = 0; |
|
udccsr = udc_ep_readl(ep, UDCCSR); |
|
|
|
if (likely(!list_empty(&ep->queue))) |
|
req = list_entry(ep->queue.next, |
|
struct pxa27x_request, queue); |
|
else |
|
req = NULL; |
|
|
|
ep_dbg(ep, "req:%p, udccsr 0x%03x loop=%d\n", |
|
req, udccsr, loop++); |
|
|
|
if (unlikely(udccsr & (UDCCSR_SST | UDCCSR_TRN))) |
|
udc_ep_writel(ep, UDCCSR, |
|
udccsr & (UDCCSR_SST | UDCCSR_TRN)); |
|
if (!req) |
|
break; |
|
|
|
if (unlikely(is_in)) { |
|
if (likely(!ep_is_full(ep))) |
|
completed = write_fifo(ep, req); |
|
} else { |
|
if (likely(epout_has_pkt(ep))) |
|
completed = read_fifo(ep, req); |
|
} |
|
|
|
if (completed) { |
|
if (is_in) |
|
ep_end_in_req(ep, req, &flags); |
|
else |
|
ep_end_out_req(ep, req, &flags); |
|
} |
|
} while (completed); |
|
|
|
ep->in_handle_ep = 0; |
|
recursion_detected: |
|
spin_unlock_irqrestore(&ep->lock, flags); |
|
} |
|
|
|
/** |
|
* pxa27x_change_configuration - Handle SET_CONF usb request notification |
|
* @udc: udc device |
|
* @config: usb configuration |
|
* |
|
* Post the request to upper level. |
|
* Don't use any pxa specific harware configuration capabilities |
|
*/ |
|
static void pxa27x_change_configuration(struct pxa_udc *udc, int config) |
|
{ |
|
struct usb_ctrlrequest req ; |
|
|
|
dev_dbg(udc->dev, "config=%d\n", config); |
|
|
|
udc->config = config; |
|
udc->last_interface = 0; |
|
udc->last_alternate = 0; |
|
|
|
req.bRequestType = 0; |
|
req.bRequest = USB_REQ_SET_CONFIGURATION; |
|
req.wValue = config; |
|
req.wIndex = 0; |
|
req.wLength = 0; |
|
|
|
set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF); |
|
udc->driver->setup(&udc->gadget, &req); |
|
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN); |
|
} |
|
|
|
/** |
|
* pxa27x_change_interface - Handle SET_INTERF usb request notification |
|
* @udc: udc device |
|
* @iface: interface number |
|
* @alt: alternate setting number |
|
* |
|
* Post the request to upper level. |
|
* Don't use any pxa specific harware configuration capabilities |
|
*/ |
|
static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt) |
|
{ |
|
struct usb_ctrlrequest req; |
|
|
|
dev_dbg(udc->dev, "interface=%d, alternate setting=%d\n", iface, alt); |
|
|
|
udc->last_interface = iface; |
|
udc->last_alternate = alt; |
|
|
|
req.bRequestType = USB_RECIP_INTERFACE; |
|
req.bRequest = USB_REQ_SET_INTERFACE; |
|
req.wValue = alt; |
|
req.wIndex = iface; |
|
req.wLength = 0; |
|
|
|
set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF); |
|
udc->driver->setup(&udc->gadget, &req); |
|
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN); |
|
} |
|
|
|
/* |
|
* irq_handle_data - Handle data transfer |
|
* @irq: irq IRQ number |
|
* @udc: dev pxa_udc device structure |
|
* |
|
* Called from irq handler, transferts data to or from endpoint to queue |
|
*/ |
|
static void irq_handle_data(int irq, struct pxa_udc *udc) |
|
{ |
|
int i; |
|
struct pxa_ep *ep; |
|
u32 udcisr0 = udc_readl(udc, UDCISR0) & UDCCISR0_EP_MASK; |
|
u32 udcisr1 = udc_readl(udc, UDCISR1) & UDCCISR1_EP_MASK; |
|
|
|
if (udcisr0 & UDCISR_INT_MASK) { |
|
udc->pxa_ep[0].stats.irqs++; |
|
udc_writel(udc, UDCISR0, UDCISR_INT(0, UDCISR_INT_MASK)); |
|
handle_ep0(udc, !!(udcisr0 & UDCICR_FIFOERR), |
|
!!(udcisr0 & UDCICR_PKTCOMPL)); |
|
} |
|
|
|
udcisr0 >>= 2; |
|
for (i = 1; udcisr0 != 0 && i < 16; udcisr0 >>= 2, i++) { |
|
if (!(udcisr0 & UDCISR_INT_MASK)) |
|
continue; |
|
|
|
udc_writel(udc, UDCISR0, UDCISR_INT(i, UDCISR_INT_MASK)); |
|
|
|
WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep)); |
|
if (i < ARRAY_SIZE(udc->pxa_ep)) { |
|
ep = &udc->pxa_ep[i]; |
|
ep->stats.irqs++; |
|
handle_ep(ep); |
|
} |
|
} |
|
|
|
for (i = 16; udcisr1 != 0 && i < 24; udcisr1 >>= 2, i++) { |
|
udc_writel(udc, UDCISR1, UDCISR_INT(i - 16, UDCISR_INT_MASK)); |
|
if (!(udcisr1 & UDCISR_INT_MASK)) |
|
continue; |
|
|
|
WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep)); |
|
if (i < ARRAY_SIZE(udc->pxa_ep)) { |
|
ep = &udc->pxa_ep[i]; |
|
ep->stats.irqs++; |
|
handle_ep(ep); |
|
} |
|
} |
|
|
|
} |
|
|
|
/** |
|
* irq_udc_suspend - Handle IRQ "UDC Suspend" |
|
* @udc: udc device |
|
*/ |
|
static void irq_udc_suspend(struct pxa_udc *udc) |
|
{ |
|
udc_writel(udc, UDCISR1, UDCISR1_IRSU); |
|
udc->stats.irqs_suspend++; |
|
|
|
if (udc->gadget.speed != USB_SPEED_UNKNOWN |
|
&& udc->driver && udc->driver->suspend) |
|
udc->driver->suspend(&udc->gadget); |
|
ep0_idle(udc); |
|
} |
|
|
|
/** |
|
* irq_udc_resume - Handle IRQ "UDC Resume" |
|
* @udc: udc device |
|
*/ |
|
static void irq_udc_resume(struct pxa_udc *udc) |
|
{ |
|
udc_writel(udc, UDCISR1, UDCISR1_IRRU); |
|
udc->stats.irqs_resume++; |
|
|
|
if (udc->gadget.speed != USB_SPEED_UNKNOWN |
|
&& udc->driver && udc->driver->resume) |
|
udc->driver->resume(&udc->gadget); |
|
} |
|
|
|
/** |
|
* irq_udc_reconfig - Handle IRQ "UDC Change Configuration" |
|
* @udc: udc device |
|
*/ |
|
static void irq_udc_reconfig(struct pxa_udc *udc) |
|
{ |
|
unsigned config, interface, alternate, config_change; |
|
u32 udccr = udc_readl(udc, UDCCR); |
|
|
|
udc_writel(udc, UDCISR1, UDCISR1_IRCC); |
|
udc->stats.irqs_reconfig++; |
|
|
|
config = (udccr & UDCCR_ACN) >> UDCCR_ACN_S; |
|
config_change = (config != udc->config); |
|
pxa27x_change_configuration(udc, config); |
|
|
|
interface = (udccr & UDCCR_AIN) >> UDCCR_AIN_S; |
|
alternate = (udccr & UDCCR_AAISN) >> UDCCR_AAISN_S; |
|
pxa27x_change_interface(udc, interface, alternate); |
|
|
|
if (config_change) |
|
update_pxa_ep_matches(udc); |
|
udc_set_mask_UDCCR(udc, UDCCR_SMAC); |
|
} |
|
|
|
/** |
|
* irq_udc_reset - Handle IRQ "UDC Reset" |
|
* @udc: udc device |
|
*/ |
|
static void irq_udc_reset(struct pxa_udc *udc) |
|
{ |
|
u32 udccr = udc_readl(udc, UDCCR); |
|
struct pxa_ep *ep = &udc->pxa_ep[0]; |
|
|
|
dev_info(udc->dev, "USB reset\n"); |
|
udc_writel(udc, UDCISR1, UDCISR1_IRRS); |
|
udc->stats.irqs_reset++; |
|
|
|
if ((udccr & UDCCR_UDA) == 0) { |
|
dev_dbg(udc->dev, "USB reset start\n"); |
|
stop_activity(udc); |
|
} |
|
udc->gadget.speed = USB_SPEED_FULL; |
|
memset(&udc->stats, 0, sizeof udc->stats); |
|
|
|
nuke(ep, -EPROTO); |
|
ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC); |
|
ep0_idle(udc); |
|
} |
|
|
|
/** |
|
* pxa_udc_irq - Main irq handler |
|
* @irq: irq number |
|
* @_dev: udc device |
|
* |
|
* Handles all udc interrupts |
|
*/ |
|
static irqreturn_t pxa_udc_irq(int irq, void *_dev) |
|
{ |
|
struct pxa_udc *udc = _dev; |
|
u32 udcisr0 = udc_readl(udc, UDCISR0); |
|
u32 udcisr1 = udc_readl(udc, UDCISR1); |
|
u32 udccr = udc_readl(udc, UDCCR); |
|
u32 udcisr1_spec; |
|
|
|
dev_vdbg(udc->dev, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, " |
|
"UDCCR:0x%08x\n", udcisr0, udcisr1, udccr); |
|
|
|
udcisr1_spec = udcisr1 & 0xf8000000; |
|
if (unlikely(udcisr1_spec & UDCISR1_IRSU)) |
|
irq_udc_suspend(udc); |
|
if (unlikely(udcisr1_spec & UDCISR1_IRRU)) |
|
irq_udc_resume(udc); |
|
if (unlikely(udcisr1_spec & UDCISR1_IRCC)) |
|
irq_udc_reconfig(udc); |
|
if (unlikely(udcisr1_spec & UDCISR1_IRRS)) |
|
irq_udc_reset(udc); |
|
|
|
if ((udcisr0 & UDCCISR0_EP_MASK) | (udcisr1 & UDCCISR1_EP_MASK)) |
|
irq_handle_data(irq, udc); |
|
|
|
return IRQ_HANDLED; |
|
} |
|
|
|
static struct pxa_udc memory = { |
|
.gadget = { |
|
.ops = &pxa_udc_ops, |
|
.ep0 = &memory.udc_usb_ep[0].usb_ep, |
|
.name = driver_name, |
|
.dev = { |
|
.init_name = "gadget", |
|
}, |
|
}, |
|
|
|
.udc_usb_ep = { |
|
USB_EP_CTRL, |
|
USB_EP_OUT_BULK(1), |
|
USB_EP_IN_BULK(2), |
|
USB_EP_IN_ISO(3), |
|
USB_EP_OUT_ISO(4), |
|
USB_EP_IN_INT(5), |
|
}, |
|
|
|
.pxa_ep = { |
|
PXA_EP_CTRL, |
|
/* Endpoints for gadget zero */ |
|
PXA_EP_OUT_BULK(1, 1, 3, 0, 0), |
|
PXA_EP_IN_BULK(2, 2, 3, 0, 0), |
|
/* Endpoints for ether gadget, file storage gadget */ |
|
PXA_EP_OUT_BULK(3, 1, 1, 0, 0), |
|
PXA_EP_IN_BULK(4, 2, 1, 0, 0), |
|
PXA_EP_IN_ISO(5, 3, 1, 0, 0), |
|
PXA_EP_OUT_ISO(6, 4, 1, 0, 0), |
|
PXA_EP_IN_INT(7, 5, 1, 0, 0), |
|
/* Endpoints for RNDIS, serial */ |
|
PXA_EP_OUT_BULK(8, 1, 2, 0, 0), |
|
PXA_EP_IN_BULK(9, 2, 2, 0, 0), |
|
PXA_EP_IN_INT(10, 5, 2, 0, 0), |
|
/* |
|
* All the following endpoints are only for completion. They |
|
* won't never work, as multiple interfaces are really broken on |
|
* the pxa. |
|
*/ |
|
PXA_EP_OUT_BULK(11, 1, 2, 1, 0), |
|
PXA_EP_IN_BULK(12, 2, 2, 1, 0), |
|
/* Endpoint for CDC Ether */ |
|
PXA_EP_OUT_BULK(13, 1, 1, 1, 1), |
|
PXA_EP_IN_BULK(14, 2, 1, 1, 1), |
|
} |
|
}; |
|
|
|
#if defined(CONFIG_OF) |
|
static const struct of_device_id udc_pxa_dt_ids[] = { |
|
{ .compatible = "marvell,pxa270-udc" }, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids); |
|
#endif |
|
|
|
/** |
|
* pxa_udc_probe - probes the udc device |
|
* @pdev: platform device |
|
* |
|
* Perform basic init : allocates udc clock, creates sysfs files, requests |
|
* irq. |
|
*/ |
|
static int pxa_udc_probe(struct platform_device *pdev) |
|
{ |
|
struct pxa_udc *udc = &memory; |
|
int retval = 0, gpio; |
|
struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); |
|
unsigned long gpio_flags; |
|
|
|
if (mach) { |
|
gpio_flags = mach->gpio_pullup_inverted ? GPIOF_ACTIVE_LOW : 0; |
|
gpio = mach->gpio_pullup; |
|
if (gpio_is_valid(gpio)) { |
|
retval = devm_gpio_request_one(&pdev->dev, gpio, |
|
gpio_flags, |
|
"USB D+ pullup"); |
|
if (retval) |
|
return retval; |
|
udc->gpiod = gpio_to_desc(mach->gpio_pullup); |
|
} |
|
udc->udc_command = mach->udc_command; |
|
} else { |
|
udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); |
|
} |
|
|
|
udc->regs = devm_platform_ioremap_resource(pdev, 0); |
|
if (IS_ERR(udc->regs)) |
|
return PTR_ERR(udc->regs); |
|
udc->irq = platform_get_irq(pdev, 0); |
|
if (udc->irq < 0) |
|
return udc->irq; |
|
|
|
udc->dev = &pdev->dev; |
|
if (of_have_populated_dt()) { |
|
udc->transceiver = |
|
devm_usb_get_phy_by_phandle(udc->dev, "phys", 0); |
|
if (IS_ERR(udc->transceiver)) |
|
return PTR_ERR(udc->transceiver); |
|
} else { |
|
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); |
|
} |
|
|
|
if (IS_ERR(udc->gpiod)) { |
|
dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n", |
|
PTR_ERR(udc->gpiod)); |
|
return PTR_ERR(udc->gpiod); |
|
} |
|
if (udc->gpiod) |
|
gpiod_direction_output(udc->gpiod, 0); |
|
|
|
udc->clk = devm_clk_get(&pdev->dev, NULL); |
|
if (IS_ERR(udc->clk)) |
|
return PTR_ERR(udc->clk); |
|
|
|
retval = clk_prepare(udc->clk); |
|
if (retval) |
|
return retval; |
|
|
|
udc->vbus_sensed = 0; |
|
|
|
the_controller = udc; |
|
platform_set_drvdata(pdev, udc); |
|
udc_init_data(udc); |
|
|
|
/* irq setup after old hardware state is cleaned up */ |
|
retval = devm_request_irq(&pdev->dev, udc->irq, pxa_udc_irq, |
|
IRQF_SHARED, driver_name, udc); |
|
if (retval != 0) { |
|
dev_err(udc->dev, "%s: can't get irq %i, err %d\n", |
|
driver_name, udc->irq, retval); |
|
goto err; |
|
} |
|
|
|
if (!IS_ERR_OR_NULL(udc->transceiver)) |
|
usb_register_notifier(udc->transceiver, &pxa27x_udc_phy); |
|
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); |
|
if (retval) |
|
goto err_add_gadget; |
|
|
|
pxa_init_debugfs(udc); |
|
if (should_enable_udc(udc)) |
|
udc_enable(udc); |
|
return 0; |
|
|
|
err_add_gadget: |
|
if (!IS_ERR_OR_NULL(udc->transceiver)) |
|
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); |
|
err: |
|
clk_unprepare(udc->clk); |
|
return retval; |
|
} |
|
|
|
/** |
|
* pxa_udc_remove - removes the udc device driver |
|
* @_dev: platform device |
|
*/ |
|
static int pxa_udc_remove(struct platform_device *_dev) |
|
{ |
|
struct pxa_udc *udc = platform_get_drvdata(_dev); |
|
|
|
usb_del_gadget_udc(&udc->gadget); |
|
pxa_cleanup_debugfs(udc); |
|
|
|
if (!IS_ERR_OR_NULL(udc->transceiver)) { |
|
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); |
|
usb_put_phy(udc->transceiver); |
|
} |
|
|
|
udc->transceiver = NULL; |
|
the_controller = NULL; |
|
clk_unprepare(udc->clk); |
|
|
|
return 0; |
|
} |
|
|
|
static void pxa_udc_shutdown(struct platform_device *_dev) |
|
{ |
|
struct pxa_udc *udc = platform_get_drvdata(_dev); |
|
|
|
if (udc_readl(udc, UDCCR) & UDCCR_UDE) |
|
udc_disable(udc); |
|
} |
|
|
|
#ifdef CONFIG_PXA27x |
|
extern void pxa27x_clear_otgph(void); |
|
#else |
|
#define pxa27x_clear_otgph() do {} while (0) |
|
#endif |
|
|
|
#ifdef CONFIG_PM |
|
/** |
|
* pxa_udc_suspend - Suspend udc device |
|
* @_dev: platform device |
|
* @state: suspend state |
|
* |
|
* Suspends udc : saves configuration registers (UDCCR*), then disables the udc |
|
* device. |
|
*/ |
|
static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state) |
|
{ |
|
struct pxa_udc *udc = platform_get_drvdata(_dev); |
|
struct pxa_ep *ep; |
|
|
|
ep = &udc->pxa_ep[0]; |
|
udc->udccsr0 = udc_ep_readl(ep, UDCCSR); |
|
|
|
udc_disable(udc); |
|
udc->pullup_resume = udc->pullup_on; |
|
dplus_pullup(udc, 0); |
|
|
|
if (udc->driver) |
|
udc->driver->disconnect(&udc->gadget); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* pxa_udc_resume - Resume udc device |
|
* @_dev: platform device |
|
* |
|
* Resumes udc : restores configuration registers (UDCCR*), then enables the udc |
|
* device. |
|
*/ |
|
static int pxa_udc_resume(struct platform_device *_dev) |
|
{ |
|
struct pxa_udc *udc = platform_get_drvdata(_dev); |
|
struct pxa_ep *ep; |
|
|
|
ep = &udc->pxa_ep[0]; |
|
udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME)); |
|
|
|
dplus_pullup(udc, udc->pullup_resume); |
|
if (should_enable_udc(udc)) |
|
udc_enable(udc); |
|
/* |
|
* We do not handle OTG yet. |
|
* |
|
* OTGPH bit is set when sleep mode is entered. |
|
* it indicates that OTG pad is retaining its state. |
|
* Upon exit from sleep mode and before clearing OTGPH, |
|
* Software must configure the USB OTG pad, UDC, and UHC |
|
* to the state they were in before entering sleep mode. |
|
*/ |
|
pxa27x_clear_otgph(); |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
/* work with hotplug and coldplug */ |
|
MODULE_ALIAS("platform:pxa27x-udc"); |
|
|
|
static struct platform_driver udc_driver = { |
|
.driver = { |
|
.name = "pxa27x-udc", |
|
.of_match_table = of_match_ptr(udc_pxa_dt_ids), |
|
}, |
|
.probe = pxa_udc_probe, |
|
.remove = pxa_udc_remove, |
|
.shutdown = pxa_udc_shutdown, |
|
#ifdef CONFIG_PM |
|
.suspend = pxa_udc_suspend, |
|
.resume = pxa_udc_resume |
|
#endif |
|
}; |
|
|
|
module_platform_driver(udc_driver); |
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC); |
|
MODULE_AUTHOR("Robert Jarzmik"); |
|
MODULE_LICENSE("GPL");
|
|
|