Re: [PATCH v4 3/5] HCD descriptor DMA support for the DWC2 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux