// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc. * All rights reserved. * * Author: Li Yang <leoli@freescale.com> * Jiang Bo <tanya.jiang@freescale.com> * * Description: * Freescale high-speed USB SOC DR module device controller driver. * This can be found on MPC8349E/MPC8313E/MPC5121E cpus. * The driver is previously named as mpc_udc. Based on bare board * code from Dave Liu and Shlomi Gridish.
*/
#ifdef CONFIG_PPC32 /* * On some SoCs, the USB controller registers can be big or little endian, * depending on the version of the chip. In order to be able to run the * same kernel binary on 2 different versions of an SoC, the BE/LE decision * must be made at run time. _fsl_readl and fsl_writel are pointers to the * BE or LE readl() and writel() functions, and fsl_readl() and fsl_writel() * call through those pointers. Platform code for SoCs that have BE USB * registers should set pdata->big_endian_mmio flag. * * This also applies to controller-to-cpu accessors for the USB descriptors, * since their endianness is also SoC dependant. Platform code for SoCs that * have BE USB descriptors should set pdata->big_endian_desc flag.
*/ static u32 _fsl_readl_be(constunsigned __iomem *p)
{ return in_be32(p);
}
/******************************************************************** * Internal Used Function
********************************************************************/ /*----------------------------------------------------------------- * done() - retire a request; caller blocked irqs * @status : request status to be set, only works when * request is still in progress.
*--------------------------------------------------------------*/ staticvoid done(struct fsl_ep *ep, struct fsl_req *req, int status)
__releases(ep->udc->lock)
__acquires(ep->udc->lock)
{ struct fsl_udc *udc = NULL; unsignedchar stopped = ep->stopped; struct ep_td_struct *curr_td, *next_td; int j;
udc = (struct fsl_udc *)ep->udc; /* Removed the req from fsl_ep->queue */
list_del_init(&req->queue);
/* req.status should be set as -EINPROGRESS in ep_queue() */ if (req->req.status == -EINPROGRESS)
req->req.status = status; else
status = req->req.status;
/* Free dtd for the request */
next_td = req->head; for (j = 0; j < req->dtd_count; j++) {
curr_td = next_td; if (j != req->dtd_count - 1) {
next_td = curr_td->next_td_virt;
}
dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
}
/*----------------------------------------------------------------- * nuke(): delete all requests related to this ep * called with spinlock held
*--------------------------------------------------------------*/ staticvoid nuke(struct fsl_ep *ep, int status)
{
ep->stopped = 1;
/* Flush fifo */
fsl_ep_fifo_flush(&ep->ep);
/* Whether this eq has request linked */ while (!list_empty(&ep->queue)) { struct fsl_req *req = NULL;
/*------------------------------------------------------------------ Internal Hardware related function
------------------------------------------------------------------*/
#ifdefined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) /* Turn on cache snooping hardware, since some PowerPC platforms
* wholly rely on hardware to deal with cache coherent. */
if (udc->pdata->have_sysif_regs) { /* Setup Snooping for all the 4GB space */
tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
__raw_writel(tmp, &usb_sys_regs->snoop1);
tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
__raw_writel(tmp, &usb_sys_regs->snoop2);
} #endif
return 0;
}
/* Enable DR irq and set controller to run state */ staticvoid dr_controller_run(struct fsl_udc *udc)
{
u32 temp;
/* if we're in OTG mode, and the Host is currently using the port, * stop now and don't rip the controller out from under the * ehci driver
*/ if (udc->gadget.is_otg) { if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
pr_debug("udc: Leaving early\n"); return;
}
}
/* disable all INTR */
fsl_writel(0, &dr_regs->usbintr);
if (value) { /* set the stall bit */ if (dir)
tmp_epctrl |= EPCTRL_TX_EP_STALL; else
tmp_epctrl |= EPCTRL_RX_EP_STALL;
} else { /* clear the stall bit and reset data toggle */ if (dir) {
tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
} else {
tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
}
}
fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
}
/* Get stall status of a specific ep
Return: 0: not stalled; 1:stalled */ staticint dr_ep_get_stall(unsignedchar ep_num, unsignedchar dir)
{
u32 epctrl;
/******************************************************************** Internal Structure Build up functions
********************************************************************/
/*------------------------------------------------------------------ * struct_ep_qh_setup(): set the Endpoint Capabilites field of QH * @zlt: Zero Length Termination Select (1: disable; 0: enable) * @mult: Mult field
------------------------------------------------------------------*/ staticvoid struct_ep_qh_setup(struct fsl_udc *udc, unsignedchar ep_num, unsignedchar dir, unsignedchar ep_type, unsignedint max_pkt_len, unsignedint zlt, unsignedchar mult)
{ struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir]; unsignedint tmp = 0;
/* set the Endpoint Capabilites in QH */ switch (ep_type) { case USB_ENDPOINT_XFER_CONTROL: /* Interrupt On Setup (IOS). for control ep */
tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
| EP_QUEUE_HEAD_IOS; break; case USB_ENDPOINT_XFER_ISOC:
tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
| (mult << EP_QUEUE_HEAD_MULT_POS); break; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT:
tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; break; default:
dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type); return;
} if (zlt)
tmp |= EP_QUEUE_HEAD_ZLT_SEL;
/*------------------------------------------------------------------------- * when configurations are set, or when interface settings change * for example the do_set_interface() in gadget layer, * the driver will enable or disable the relevant endpoints * ep0 doesn't use this routine. It is always enabled.
-------------------------------------------------------------------------*/ staticint fsl_ep_enable(struct usb_ep *_ep, conststruct usb_endpoint_descriptor *desc)
{ struct fsl_udc *udc = NULL; struct fsl_ep *ep = NULL; unsignedshort max = 0; unsignedchar mult = 0, zlt; int retval = -EINVAL; unsignedlong flags;
ep = container_of(_ep, struct fsl_ep, ep);
/* catch various bogus parameters */ if (!_ep || !desc
|| (desc->bDescriptorType != USB_DT_ENDPOINT)) return -EINVAL;
udc = ep->udc;
if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN;
max = usb_endpoint_maxp(desc);
/* Disable automatic zlp generation. Driver is responsible to indicate * explicitly through req->req.zero. This is needed to enable multi-td
* request. */
zlt = 1;
/* Assume the max packet size from gadget is always correct */ switch (desc->bmAttributes & 0x03) { case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: /* mult = 0. Execute N Transactions as demonstrated by * the USB variable length packet protocol where N is * computed using the Maximum Packet Length (dQH) and
* the Total Bytes field (dTD) */
mult = 0; break; case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */
mult = usb_endpoint_maxp_mult(desc); /* 3 transactions at most */ if (mult > 3) goto en_done; break; default: goto en_done;
}
/*--------------------------------------------------------------------- * @ep : the ep being unconfigured. May not be ep0 * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
*---------------------------------------------------------------------*/ staticint fsl_ep_disable(struct usb_ep *_ep)
{ struct fsl_udc *udc = NULL; struct fsl_ep *ep = NULL; unsignedlong flags;
u32 epctrl; int ep_num;
ep = container_of(_ep, struct fsl_ep, ep); if (!_ep || !ep->ep.desc) { /* * dev_vdbg(&udc->gadget.dev, "%s not enabled\n", * _ep ? ep->ep.name : NULL);
*/ return -EINVAL;
}
/*--------------------------------------------------------------------- * allocate a request object used by this endpoint * the main operation is to insert the req->queue to the eq->queue * Returns the request, or null if one could not be allocated
*---------------------------------------------------------------------*/ staticstruct usb_request *
fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{ struct fsl_req *req;
req = kzalloc(sizeof *req, gfp_flags); if (!req) return NULL;
/* Actually add a dTD chain to an empty dQH and let go */ staticvoid fsl_prime_ep(struct fsl_ep *ep, struct ep_td_struct *td)
{ struct ep_queue_head *qh = get_qh_by_ep(ep);
/* Write dQH next pointer and terminate bit to 0 */
qh->next_dtd_ptr = cpu_to_hc32(td->td_dma
& EP_QUEUE_HEAD_NEXT_POINTER_MASK);
/* Clear active and halt bit */
qh->size_ioc_int_sts &= cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
| EP_QUEUE_HEAD_STATUS_HALT));
/* Ensure that updates to the QH will occur before priming. */
wmb();
/* Prime endpoint by writing correct bit to ENDPTPRIME */
fsl_writel(ep_is_in(ep) ? (1 << (ep_index(ep) + 16))
: (1 << (ep_index(ep))), &dr_regs->endpointprime);
}
/* Add dTD chain to the dQH of an EP */ staticvoid fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
{
u32 temp, bitmask, tmp_stat;
/* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr);
dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */
/* check if the pipe is empty */ if (!(list_empty(&ep->queue)) && !(ep_index(ep) == 0)) { /* Add td to the end */ struct fsl_req *lastreq;
lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
lastreq->tail->next_td_ptr =
cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); /* Ensure dTD's next dtd pointer to be updated */
wmb(); /* Read prime bit, if 1 goto done */ if (fsl_readl(&dr_regs->endpointprime) & bitmask) return;
do { /* Set ATDTW bit in USBCMD */
temp = fsl_readl(&dr_regs->usbcmd);
fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
/* Read correct status bit */
tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
/* Write ATDTW bit to 0 */
temp = fsl_readl(&dr_regs->usbcmd);
fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
if (tmp_stat) return;
}
fsl_prime_ep(ep, req->head);
}
/* Fill in the dTD structure * @req: request that the transfer belongs to * @length: return actually data length of the dTD * @dma: return dma address of the dTD * @is_last: return flag if it is the last dTD of the request
* return: pointer to the built dTD */ staticstruct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
dma_addr_t *dma, int *is_last, gfp_t gfp_flags)
{
u32 swap_temp; struct ep_td_struct *dtd;
/* how big will this transfer be? */
*length = min(req->req.length - req->req.actual,
(unsigned)EP_MAX_LENGTH_TRANSFER);
/* zlp is needed if req->req.zero is set */ if (req->req.zero) { if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
*is_last = 1; else
*is_last = 0;
} elseif (req->req.length == req->req.actual)
*is_last = 1; else
*is_last = 0;
if ((*is_last) == 0)
dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n"); /* Fill in the transfer size; set active bit */
swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
/* Enable interrupt for the last dtd of a request */ if (*is_last && !req->req.no_interrupt)
swap_temp |= DTD_IOC;
/* Stop the ep before we deal with the queue */
ep->stopped = 1;
ep_num = ep_index(ep);
epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); if (ep_is_in(ep))
epctrl &= ~EPCTRL_TX_ENABLE; else
epctrl &= ~EPCTRL_RX_ENABLE;
fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(iter, &ep->queue, queue) { if (&iter->req != _req) continue;
req = iter; break;
} if (!req) {
ret = -EINVAL; goto out;
}
/* The request is in progress, or completed but not dequeued */ if (ep->queue.next == &req->queue) {
_req->status = -ECONNRESET;
fsl_ep_fifo_flush(_ep); /* flush current transfer */
/* The request isn't the last request in this ep queue */ if (req->queue.next != &ep->queue) { struct fsl_req *next_req;
/* prime with dTD of next request */
fsl_prime_ep(ep, next_req->head);
} /* The request hasn't been processed, patch up the TD chain */
} else { struct fsl_req *prev_req;
/*----------------------------------------------------------------- * modify the endpoint halt feature * @ep: the non-isochronous endpoint being stalled * @value: 1--set halt 0--clear halt * Returns zero, or a negative error code.
*----------------------------------------------------------------*/ staticint fsl_ep_set_halt(struct usb_ep *_ep, int value)
{ struct fsl_ep *ep = NULL; unsignedlong flags; int status = -EOPNOTSUPP; /* operation not supported */ unsignedchar ep_dir = 0, ep_num = 0; struct fsl_udc *udc = NULL;
ep = container_of(_ep, struct fsl_ep, ep);
udc = ep->udc; if (!_ep || !ep->ep.desc) {
status = -EINVAL; goto out;
}
if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
status = -EOPNOTSUPP; goto out;
}
/* Attempt to halt IN ep will fail if any transfer requests
* are still queue */ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
status = -EAGAIN; goto out;
}
timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT; do {
fsl_writel(bits, &dr_regs->endptflush);
/* Wait until flush complete */ while (fsl_readl(&dr_regs->endptflush)) { if (time_after(jiffies, timeout)) {
dev_err(&udc_controller->gadget.dev, "ep flush timeout\n"); return;
}
cpu_relax();
} /* See if we need to flush again */
} while (fsl_readl(&dr_regs->endptstatus) & bits);
}
/*---------------------------------------------------------------------- * Get the current frame number (from DR frame_index Reg )
*----------------------------------------------------------------------*/ staticint fsl_get_frame(struct usb_gadget *gadget)
{ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
}
/*----------------------------------------------------------------------- * Tries to wake up the host connected to this gadget
-----------------------------------------------------------------------*/ staticint fsl_wakeup(struct usb_gadget *gadget)
{ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
u32 portsc;
/* Remote wakeup feature not enabled by host */ if (!udc->remote_wakeup) return -ENOTSUPP;
portsc = fsl_readl(&dr_regs->portsc1); /* not suspended? */ if (!(portsc & PORTSCX_PORT_SUSPEND)) return 0; /* trigger force resume */
portsc |= PORTSCX_PORT_FORCE_RESUME;
fsl_writel(portsc, &dr_regs->portsc1); return 0;
}
/* constrain controller's VBUS power usage * This call is used by gadget drivers during SET_CONFIGURATION calls, * reporting how much power the device may consume. For example, this * could affect how quickly batteries are recharged. * * Returns zero on success, else negative errno.
*/ staticint fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
{ struct fsl_udc *udc;
/* Change Data+ pullup status * this func is used by usb_gadget_connect/disconnect
*/ staticint fsl_pullup(struct usb_gadget *gadget, int is_on)
{ struct fsl_udc *udc;
/* * Empty complete function used by this driver to fill in the req->complete * field when creating a request since the complete field is mandatory.
*/ staticvoid fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { }
/* Set protocol stall on ep0, protocol stall will automatically be cleared
on new transaction */ staticvoid ep0stall(struct fsl_udc *udc)
{
u32 tmp;
/* must set tx and rx to stall at the same time */
tmp = fsl_readl(&dr_regs->endptctrl[0]);
tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
fsl_writel(tmp, &dr_regs->endptctrl[0]);
udc->ep0_state = WAIT_FOR_SETUP;
udc->ep0_dir = 0;
}
/* Prime a status phase for ep0 */ staticint ep0_prime_status(struct fsl_udc *udc, int direction)
{ struct fsl_req *req = udc->status_req; struct fsl_ep *ep; int ret;
/* stall if endpoint doesn't exist */ if (!target_ep->ep.desc) goto stall;
tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
<< USB_ENDPOINT_HALT;
}
udc->ep0_dir = USB_DIR_IN; /* Borrow the per device status_req */
req = udc->status_req; /* Fill in the request structure */
*((u16 *) req->req.buf) = cpu_to_le16(tmp);
} elseif ((setup->bRequestType & (USB_RECIP_MASK
| USB_TYPE_MASK)) == (USB_RECIP_DEVICE
| USB_TYPE_STANDARD)) { /* Note: The driver has not include OTG support yet.
* This will be set when OTG support is added */ if (wValue == USB_DEVICE_TEST_MODE)
ptc = wIndex >> 8; elseif (gadget_is_otg(&udc->gadget)) { if (setup->bRequest ==
USB_DEVICE_B_HNP_ENABLE)
udc->gadget.b_hnp_enable = 1; elseif (setup->bRequest ==
USB_DEVICE_A_HNP_SUPPORT)
udc->gadget.a_hnp_support = 1; elseif (setup->bRequest ==
USB_DEVICE_A_ALT_HNP_SUPPORT)
udc->gadget.a_alt_hnp_support = 1;
}
rc = 0;
} else break;
if (rc == 0) { if (ep0_prime_status(udc, EP_DIR_IN))
ep0stall(udc);
} if (ptc) {
u32 tmp;
mdelay(10);
tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16);
fsl_writel(tmp, &dr_regs->portsc1);
printk(KERN_INFO "udc: switch to test mode %d.\n", ptc);
}
return;
}
default: break;
}
/* Requests handled by gadget */ if (wLength) { /* Data phase from gadget, status phase from udc */
udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
? USB_DIR_IN : USB_DIR_OUT;
spin_unlock(&udc->lock); if (udc->driver->setup(&udc->gadget,
&udc->local_setup_buff) < 0)
ep0stall(udc);
spin_lock(&udc->lock);
udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
? DATA_STATE_XMIT : DATA_STATE_RECV; /* * If the data stage is IN, send status prime immediately. * See 2.0 Spec chapter 8.5.3.3 for detail.
*/ if (udc->ep0_state == DATA_STATE_XMIT) if (ep0_prime_status(udc, EP_DIR_OUT))
ep0stall(udc);
} else { /* No data phase, IN status from gadget */
udc->ep0_dir = USB_DIR_IN;
spin_unlock(&udc->lock); if (udc->driver->setup(&udc->gadget,
&udc->local_setup_buff) < 0)
ep0stall(udc);
spin_lock(&udc->lock);
udc->ep0_state = WAIT_FOR_OUT_STATUS;
}
}
/* Process request for Data or Status phase of ep0
* prime status phase if needed */ staticvoid ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, struct fsl_req *req)
{ if (udc->usb_state == USB_STATE_ADDRESS) { /* Set the new address */
u32 new_address = (u32) udc->device_address;
fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
&dr_regs->deviceaddr);
}
done(ep0, req, 0);
switch (udc->ep0_state) { case DATA_STATE_XMIT: /* already primed at setup_received_irq */
udc->ep0_state = WAIT_FOR_OUT_STATUS; break; case DATA_STATE_RECV: /* send status phase */ if (ep0_prime_status(udc, EP_DIR_IN))
ep0stall(udc); break; case WAIT_FOR_OUT_STATUS:
udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP:
dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n"); break; default:
ep0stall(udc); break;
}
}
/* Tripwire mechanism to ensure a setup packet payload is extracted without
* being corrupted by another incoming setup packet */ staticvoid tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
{
u32 temp; struct ep_queue_head *qh; struct fsl_usb2_platform_data *pdata = udc->pdata;
qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
/* Clear bit in ENDPTSETUPSTAT */
temp = fsl_readl(&dr_regs->endptsetupstat);
fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
/* while a hazard exists when setup package arrives */ do { /* Set Setup Tripwire */
temp = fsl_readl(&dr_regs->usbcmd);
fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
/* Copy the setup packet to local buffer */ if (pdata->le_setup_buf) {
u32 *p = (u32 *)buffer_ptr;
u32 *s = (u32 *)qh->setup_buffer;
/* Convert little endian setup buffer to CPU endian */
*p++ = le32_to_cpu(*s++);
*p = le32_to_cpu(*s);
} else {
memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
}
} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
/* process-ep_req(): free the completed Tds for this req */ staticint process_ep_req(struct fsl_udc *udc, int pipe, struct fsl_req *curr_req)
{ struct ep_td_struct *curr_td; int actual, remaining_length, j, tmp; int status = 0; int errors = 0; struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; int direction = pipe % 2;
curr_td = curr_req->head;
actual = curr_req->req.length;
for (j = 0; j < curr_req->dtd_count; j++) {
remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts)
& DTD_PACKET_SIZE)
>> DTD_LENGTH_BIT_POS;
actual -= remaining_length;
errors = hc32_to_cpu(curr_td->size_ioc_sts); if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) {
dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */
tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
tmp &= ~errors;
curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp);
status = -EPIPE; /* FIXME: continue with next queued TD? */
break;
} if (errors & DTD_STATUS_DATA_BUFF_ERR) {
dev_vdbg(&udc->gadget.dev, "Transfer overflow\n");
status = -EPROTO; break;
} elseif (errors & DTD_STATUS_TRANSACTION_ERR) {
dev_vdbg(&udc->gadget.dev, "ISO error\n");
status = -EILSEQ; break;
} else
dev_err(&udc->gadget.dev, "Unknown error has occurred (0x%x)!\n",
errors);
} elseif (hc32_to_cpu(curr_td->size_ioc_sts)
& DTD_STATUS_ACTIVE) {
dev_vdbg(&udc->gadget.dev, "Request not complete\n");
status = REQ_UNCOMPLETE; return status;
} elseif (remaining_length) { if (direction) {
dev_vdbg(&udc->gadget.dev, "Transmit dTD remaining length not zero\n");
status = -EPROTO; break;
} else { break;
}
} else {
dev_vdbg(&udc->gadget.dev, "dTD transmitted successful\n");
}
/* Process a DTD completion interrupt */ staticvoid dtd_complete_irq(struct fsl_udc *udc)
{
u32 bit_pos; int i, ep_num, direction, bit_mask, status; struct fsl_ep *curr_ep; struct fsl_req *curr_req, *temp_req;
/* Clear the bits in the register */
bit_pos = fsl_readl(&dr_regs->endptcomplete);
fsl_writel(bit_pos, &dr_regs->endptcomplete);
if (!bit_pos) return;
for (i = 0; i < udc->max_ep; i++) {
ep_num = i >> 1;
direction = i % 2;
bit_mask = 1 << (ep_num + 16 * direction);
if (!(bit_pos & bit_mask)) continue;
curr_ep = get_ep_by_pipe(udc, i);
/* If the ep is configured */ if (!curr_ep->ep.name) {
dev_warn(&udc->gadget.dev, "Invalid EP?\n"); continue;
}
/* process the req queue until an uncomplete request */
list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
queue) {
status = process_ep_req(udc, i, curr_req);
dev_vdbg(&udc->gadget.dev, "status of process_ep_req= %d, ep = %d\n",
status, ep_num); if (status == REQ_UNCOMPLETE) break; /* write back status to req */
curr_req->req.status = status;
staticinlineenum usb_device_speed portscx_device_speed(u32 reg)
{ switch (reg & PORTSCX_PORT_SPEED_MASK) { case PORTSCX_PORT_SPEED_HIGH: return USB_SPEED_HIGH; case PORTSCX_PORT_SPEED_FULL: return USB_SPEED_FULL; case PORTSCX_PORT_SPEED_LOW: return USB_SPEED_LOW; default: return USB_SPEED_UNKNOWN;
}
}
/* Process a port change interrupt */ staticvoid port_change_irq(struct fsl_udc *udc)
{ if (udc->bus_reset)
udc->bus_reset = 0;
/* Bus resetting is finished */ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) /* Get the speed */
udc->gadget.speed =
portscx_device_speed(fsl_readl(&dr_regs->portsc1));
/* Update USB state */ if (!udc->resume_state)
udc->usb_state = USB_STATE_DEFAULT;
}
/* Clear usb state */
udc->resume_state = 0;
udc->ep0_dir = 0;
udc->ep0_state = WAIT_FOR_SETUP;
udc->remote_wakeup = 0; /* default to 0 on reset */
udc->gadget.b_hnp_enable = 0;
udc->gadget.a_hnp_support = 0;
udc->gadget.a_alt_hnp_support = 0;
/* Clear all the setup token semaphores */
temp = fsl_readl(&dr_regs->endptsetupstat);
fsl_writel(temp, &dr_regs->endptsetupstat);
/* Clear all the endpoint complete status bits */
temp = fsl_readl(&dr_regs->endptcomplete);
fsl_writel(temp, &dr_regs->endptcomplete);
timeout = jiffies + 100; while (fsl_readl(&dr_regs->endpointprime)) { /* Wait until all endptprime bits cleared */ if (time_after(jiffies, timeout)) {
dev_err(&udc->gadget.dev, "Timeout for reset\n"); break;
}
cpu_relax();
}
/* Write 1s to the flush register */
fsl_writel(0xffffffff, &dr_regs->endptflush);
if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
dev_vdbg(&udc->gadget.dev, "Bus reset\n"); /* Bus is reseting */
udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue
* head and TR Queue */
reset_queues(udc, true);
udc->usb_state = USB_STATE_DEFAULT;
} else {
dev_vdbg(&udc->gadget.dev, "Controller reset\n"); /* initialize usb hw reg except for regs for EP, not
* touch usbintr reg */
dr_controller_setup(udc);
/* Reset all internal used Queues */
reset_queues(udc, false);
ep0_setup(udc);
/* Enable DR IRQ reg, Set Run bit, change udc state */
dr_controller_run(udc);
udc->usb_state = USB_STATE_ATTACHED;
}
}
/* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
bus_resume(udc);
/* USB Interrupt */ if (irq_src & USB_STS_INT) {
dev_vdbg(&udc->gadget.dev, "Packet int\n"); /* Setup package, we only support ep0 as control ep */ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
tripwire_handler(udc, 0,
(u8 *) (&udc->local_setup_buff));
setup_received_irq(udc, &udc->local_setup_buff);
status = IRQ_HANDLED;
}
/* completion of dtd */ if (fsl_readl(&dr_regs->endptcomplete)) {
dtd_complete_irq(udc);
status = IRQ_HANDLED;
}
}
/* SOF (for ISO transfer) */ if (irq_src & USB_STS_SOF) {
status = IRQ_HANDLED;
}
/* Port Change */ if (irq_src & USB_STS_PORT_CHANGE) {
port_change_irq(udc);
status = IRQ_HANDLED;
}
/* Reset Received */ if (irq_src & USB_STS_RESET) {
dev_vdbg(&udc->gadget.dev, "reset int\n");
reset_irq(udc);
status = IRQ_HANDLED;
}
/* Sleep Enable (Suspend) */ if (irq_src & USB_STS_SUSPEND) {
suspend_irq(udc);
status = IRQ_HANDLED;
}
/*----------------------------------------------------------------* * Hook to gadget drivers * Called by initialization code of gadget drivers
*----------------------------------------------------------------*/ staticint fsl_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver)
{ int retval = 0; unsignedlong flags;
/* lock is needed but whether should use this lock or another */
spin_lock_irqsave(&udc_controller->lock, flags);
/* hook up the driver */
udc_controller->driver = driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
g->is_selfpowered = 1;
if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { /* Suspend the controller until OTG enable it */
udc_controller->stopped = 1;
printk(KERN_INFO "Suspend udc for OTG auto detect\n");
/* connect to bus through transceiver */ if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
retval = otg_set_peripheral(
udc_controller->transceiver->otg,
&udc_controller->gadget); if (retval < 0) {
dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n");
udc_controller->driver = NULL; return retval;
}
}
} else { /* Enable DR IRQ reg and set USBCMD reg Run bit */
dr_controller_run(udc_controller);
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_state = WAIT_FOR_SETUP;
udc_controller->ep0_dir = 0;
}
/*------------------------------------------------------------------------- PROC File System Support
-------------------------------------------------------------------------*/ #ifdef CONFIG_USB_GADGET_DEBUG_FILES
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
ep = &udc->eps[0];
seq_printf(m, "For %s Maxpkt is 0x%x index is 0x%x\n",
ep->ep.name, ep_maxpacket(ep), ep_index(ep));
if (list_empty(&ep->queue)) {
seq_puts(m, "its req queue is empty\n\n");
} else {
list_for_each_entry(req, &ep->queue, queue) {
seq_printf(m, "req %p actual 0x%x length 0x%x buf %p\n",
&req->req, req->req.actual,
req->req.length, req->req.buf);
}
} /* other gadget->eplist ep */
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { if (ep->ep.desc) {
seq_printf(m, "\nFor %s Maxpkt is 0x%x " "index is 0x%x\n",
ep->ep.name, ep_maxpacket(ep),
ep_index(ep));
if (list_empty(&ep->queue)) {
seq_puts(m, "its req queue is empty\n\n");
} else {
list_for_each_entry(req, &ep->queue, queue) {
seq_printf(m, "req %p actual 0x%x length " "0x%x buf %p\n",
&req->req, req->req.actual,
req->req.length, req->req.buf);
} /* end for each_entry of ep req */
} /* end for else */
} /* end for if(ep->queue) */
} /* end (ep->desc) */
/****************************************************************** Internal structure setup functions
*******************************************************************/ /*------------------------------------------------------------------ * init resource for global controller called by fsl_udc_probe() * On success the udc handle is initialized, on failure it is * unchanged (reset). * Return 0 on success and -1 on allocation failure
------------------------------------------------------------------*/ staticint struct_udc_setup(struct fsl_udc *udc, struct platform_device *pdev)
{ struct fsl_usb2_platform_data *pdata;
size_t size;
udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL); if (!udc->eps) {
dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n"); goto eps_alloc_failed;
}
/* initialized QHs, take care of alignment */
size = udc->max_ep * sizeof(struct ep_queue_head); if (size < QH_ALIGNMENT)
size = QH_ALIGNMENT; elseif ((size % QH_ALIGNMENT) != 0) {
size += QH_ALIGNMENT + 1;
size &= ~(QH_ALIGNMENT - 1);
}
udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
&udc->ep_qh_dma, GFP_KERNEL); if (!udc->ep_qh) {
dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n"); goto ep_queue_alloc_failed;
}
udc->ep_qh_size = size;
/* Initialize ep0 status request structure */ /* FIXME: fsl_alloc_request() ignores ep argument */
udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), struct fsl_req, req); if (!udc->status_req) {
dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n"); goto udc_status_alloc_failed;
}
/* allocate a small amount of memory to get valid address */
udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); if (!udc->status_req->req.buf) {
dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n"); goto udc_req_buf_alloc_failed;
}
udc->resume_state = USB_STATE_NOTATTACHED;
udc->usb_state = USB_STATE_POWERED;
udc->ep0_dir = 0;
udc->remote_wakeup = 0; /* default to 0 on reset */
/*---------------------------------------------------------------- * Setup the fsl_ep struct for eps * Link fsl_ep->ep to gadget->ep_list * ep0out is not used so do nothing here * ep0in should be taken care
*--------------------------------------------------------------*/ staticint struct_ep_setup(struct fsl_udc *udc, unsignedchar index, char *name, int link)
{ struct fsl_ep *ep = &udc->eps[index];
/* for ep0: maxP defined in desc * for other eps, maxP is set by epautoconfig() called by gadget layer
*/
usb_ep_set_maxpacket_limit(&ep->ep, (unsignedshort) ~0);
/* the queue lists any req for this ep */
INIT_LIST_HEAD(&ep->queue);
/* gagdet.ep_list used for ep_autoconfig so no ep0 */ if (link)
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
ep->gadget = &udc->gadget;
ep->qh = &udc->ep_qh[index];
return 0;
}
/* Driver probe function * all initialization operations implemented here except enabling usb_intr reg * board setup should have been done in the platform code
*/ staticint fsl_udc_probe(struct platform_device *pdev)
{ struct fsl_usb2_platform_data *pdata; struct resource *res; int ret = -ENODEV; unsignedint i;
u32 dccparams;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.