HI, On Tue, Feb 19, 2013 at 06:50:06PM -0800, Paul Zimmerman wrote: > +#include <linux/kernel.h> > +#include <linux/module.h> apparently not used. > +#include <linux/moduleparam.h> not used. > +#include <linux/spinlock.h> not used > +#include <linux/interrupt.h> not used either > +#include <linux/dma-mapping.h> > +#include <linux/debugfs.h> not used > +#include <linux/seq_file.h> not used > +#include <linux/delay.h> not used. > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/usb.h> > + > +#include <linux/usb/hcd.h> > +#include <linux/usb/ch11.h> this doesn't exist anymore (moved to uapi/linux/usb/ch11.h) have you build tested ? In any case, I think it makes sense to have a small <linux/usb/ch11.h> header simply including uapi/linux/usb/ch11.h. Greg ? > +#include <linux/usb/gadget.h> not used > +static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u16 fr_list_en) > +{ > + u32 hcfg = readl(hsotg->regs + HCFG); > + u32 frlisten; > + > + if (hcfg & HCFG_PERSCHEDENA) > + /* already enabled */ > + return; > + > + writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR); > + > + switch (fr_list_en) { > + case 64: > + frlisten = 3; > + break; > + case 32: > + frlisten = 2; > + break; > + case 16: > + frlisten = 1; > + break; > + case 8: what are these cases ? Frame list size in bits ? Care to provide macros? Or perhaps something like below would be faster and have the same outcome: frlisten = __ffs(fr_list_en >> 3); Just make sure to add a comment stating what the code is doing ;-) > +int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, > + gfp_t mem_flags) > +{ > + int retval = 0; > + > + if (qh->do_split) { > + dev_err(hsotg->dev, > + "SPLIT Transfers are not supported in Descriptor DMA.\n"); > + return -1; > + } > + > + retval = dwc2_desc_list_alloc(hsotg, qh, mem_flags); > + > + if (retval == 0 && (qh->ep_type == USB_ENDPOINT_XFER_ISOC || I think it's best to make the retval check explict: if (retval) goto out; if (qh->ep_type == USB_ENDPOINT_XFER_ISOC || qh->ep_type == USB_ENDPOINT_XFER_INT) { if (hsotg->frame_list) goto out; retval = dwc2_frame_list_alloc(hsotg, mem_flags); if (retval) goto out; dwc2_per_sched_enable(hsotg, MAX_FRLIST_EN_NUM); out: > +void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) > +{ > + dwc2_desc_list_free(hsotg, qh); > + > + /* > + * Channel still assigned due to some reasons. > + * Seen on Isoc URB dequeue. Channel halted but no subsequent > + * ChHalted interrupt to release the channel. Afterwards > + * when it comes here from endpoint disable routine > + * channel remains assigned. > + */ > + if (qh->channel) > + dwc2_release_channel_ddma(hsotg, qh); would be cool to debug this properly at some point. Could be pointing to a race condition somewhere. > +static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg, > + struct dwc2_host_chan *chan, > + struct dwc2_qtd *qtd, struct dwc2_qh *qh, > + int n_desc) > +{ > + struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[n_desc]; > + int len = chan->xfer_len; > + > + if (len > MAX_DMA_DESC_SIZE) > + len = MAX_DMA_DESC_SIZE - chan->max_packet + 1; > + > + if (chan->ep_is_in) { > + int num_packets; > + > + if (len > 0 && chan->max_packet) > + num_packets = (len + chan->max_packet - 1) > + / chan->max_packet; > + else > + /* Need 1 packet for transfer length of 0 */ > + num_packets = 1; > + > + /* Always program an integral # of packets for IN transfers */ > + len = num_packets * chan->max_packet; > + } > + > + dma_desc->status = len << HOST_DMA_NBYTES_SHIFT & HOST_DMA_NBYTES_MASK; > + qh->n_bytes[n_desc] = len; > + > + if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL && > + qtd->control_phase == DWC2_CONTROL_SETUP) > + dma_desc->status |= HOST_DMA_SUP; > + > + dma_desc->buf = (unsigned long)chan->xfer_buff & 0xffffffff; is this really enough ? I wonder if you shouldn't be mapping the buffer to dma with dma_map_single() ? > + list_for_each(qtd_item, &qh->qtd_list) { > + qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); these two can be combined into list_for_each_entry() > + if (n_desc) { > + /* SG request - more than 1 QTD */ > + chan->xfer_buff = (u8 *)qtd->urb->dma + > + qtd->urb->actual_length; hmm... this casting looks weird. Seems like you need some re-factoring here. You shouldn't be casting dma addresses to u8 pointers and casting it back to unsigned long later... Just fix your data structures to use proper data types. > +static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, > + struct dwc2_host_chan *chan, > + enum dwc2_halt_status halt_status) > +{ > + struct dwc2_hcd_iso_packet_desc *frame_desc; > + struct list_head *qtd_item, *qtd_tmp; > + struct dwc2_qtd *qtd; > + struct dwc2_qh *qh; > + u16 idx; > + int rc; > + > + qh = chan->qh; > + idx = qh->td_first; > + > + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { > + list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { > + qtd = list_entry(qtd_item, struct dwc2_qtd, > + qtd_list_entry); list_for_each_entry_safe() > + qtd->in_process = 0; > + } > + return; > + } else if (halt_status == DWC2_HC_XFER_AHB_ERR || else is unnecessary here. > + halt_status == DWC2_HC_XFER_BABBLE_ERR) { > + /* > + * Channel is halted in these error cases, considered as serious > + * issues. > + * Complete all URBs marking all frames as failed, irrespective > + * whether some of the descriptors (frames) succeeded or not. > + * Pass error code to completion routine as well, to update > + * urb->status, some of class drivers might use it to stop > + * queing transfer requests. > + */ > + int err = halt_status == DWC2_HC_XFER_AHB_ERR ? > + -EIO : -EOVERFLOW; > + > + list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { > + qtd = list_entry(qtd_item, struct dwc2_qtd, > + qtd_list_entry); list_for_each_entry_safe(). > + for (idx = 0; idx < qtd->urb->packet_count; idx++) { > + frame_desc = &qtd->urb->iso_descs[idx]; > + frame_desc->status = err; > + } > + dwc2_host_complete(hsotg, qtd->urb->priv, qtd->urb, > + err); > + dwc2_hcd_qtd_remove_and_free(hsotg, qtd, qh); > + } > + return; > + } > + > + list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { > + qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); list_for_each_entry_safe(). > +static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg, > + struct dwc2_host_chan *chan, > + int chnum, struct dwc2_qtd *qtd, > + int desc_num, > + enum dwc2_halt_status halt_status, > + int *xfer_done) > +{ > + struct dwc2_qh *qh = chan->qh; > + struct dwc2_hcd_urb *urb = qtd->urb; > + struct dwc2_hcd_dma_desc *dma_desc; > + u32 n_bytes; > + int failed; > + > + dev_vdbg(hsotg->dev, "%s()\n", __func__); > + > + dma_desc = &qh->desc_list[desc_num]; > + n_bytes = qh->n_bytes[desc_num]; > + dev_vdbg(hsotg->dev, > + "qtd=%p dwc2_urb=%p desc_num=%d desc=%p n_bytes=%d\n", > + qtd, urb, desc_num, dma_desc, n_bytes); > + failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc, > + halt_status, n_bytes, > + xfer_done); > + if (failed || (*xfer_done && urb->status != -EINPROGRESS)) { > + dwc2_host_complete(hsotg, urb->priv, urb, urb->status); > + dwc2_hcd_qtd_remove_and_free(hsotg, qtd, qh); > + dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n", > + failed, *xfer_done, urb->status); > + return failed; > + > + } else if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL) { else is unnecessary here. > + if (qtd->control_phase == DWC2_CONTROL_SETUP) { > + if (urb->length > 0) > + qtd->control_phase = DWC2_CONTROL_DATA; > + else > + qtd->control_phase = DWC2_CONTROL_STATUS; > + dev_vdbg(hsotg->dev, > + " Control setup transaction done\n"); > + } else if (qtd->control_phase == DWC2_CONTROL_DATA) { this might look nicer with a switch. > +static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, > + struct dwc2_host_chan *chan, > + int chnum, > + enum dwc2_halt_status halt_status) > +{ > + struct list_head *qtd_item, *qtd_tmp; > + struct dwc2_qh *qh = chan->qh; > + struct dwc2_qtd *qtd = NULL; > + int xfer_done; > + int desc_num = 0; > + > + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { > + list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { > + qtd = list_entry(qtd_item, struct dwc2_qtd, list_for_each_entry_safe() > + qtd_list_entry); > + qtd->in_process = 0; > + } > + return; > + } > + > + list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { > + int i; > + > + qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); list_for_each_entry_safe() -- balbi
Attachment:
signature.asc
Description: Digital signature