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
signature.asc
Description: Digital signature
