Hi, On Tue, Feb 19, 2013 at 06:50:05PM -0800, Paul Zimmerman wrote: > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/spinlock.h> > +#include <linux/interrupt.h> > +#include <linux/dma-mapping.h> > +#include <linux/debugfs.h> > +#include <linux/seq_file.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/usb.h> > + > +#include <linux/usb/hcd.h> > +#include <linux/usb/ch11.h> > +#include <linux/usb/gadget.h> > +#include <linux/usb/ch9.h>
the usual set of unused headers ;-)
> +#include "core.h"
> +#include "hcd.h"
> +
> +#ifdef DWC2_TRACK_MISSED_SOFS
> +#warning Compiling code to track missed SOFs
> +#define FRAME_NUM_ARRAY_SIZE 1000
> +
> +/* This function is for debug only */
> +static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
> +{
> + static u16 frame_num_array[FRAME_NUM_ARRAY_SIZE];
> + static u16 last_frame_num_array[FRAME_NUM_ARRAY_SIZE];
you could turn this into a list_head and everytime you find a missed
sof, you allocate a e.g. struct dwc2_missec_isoc_data and add that to a
list of missed isocs. The only "problem" is that you would have to
allocate with GFP_ATOMIC.
> + static int frame_num_idx;
> + static u16 last_frame_num = HFNUM_MAX_FRNUM;
> + static int dumped_frame_num_array;
> + u16 curr_frame_number = hsotg->frame_number;
> +
> + if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
> + if ((last_frame_num + 1 & HFNUM_MAX_FRNUM) !=
> + curr_frame_number) {
> + frame_num_array[frame_num_idx] = curr_frame_number;
> + last_frame_num_array[frame_num_idx++] = last_frame_num;
> + }
> + } else if (!dumped_frame_num_array) {
> + int i;
> +
> + dev_info(hsotg->dev, "Frame Last Frame\n");
> + dev_info(hsotg->dev, "----- ----------\n");
> + for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) {
> + dev_info(hsotg->dev, "0x%04x 0x%04x\n",
> + frame_num_array[i], last_frame_num_array[i]);
> + }
> + dumped_frame_num_array = 1;
> + }
> + last_frame_num = curr_frame_number;
> +}
> +#endif
do you really need to dump this information at the time it happens ? I
ask this because this looks like a very nice feature to have in
production. You could just pack it under a debugfs interface and provide
a Kconfig choice to enable (or not) debugfs support. Then driver can
constantly track this information and let user see by "cat
/sys/kernel/debug/dwc2/missed_sofs" or something similar.
> +static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
> +{
> + struct list_head *qh_entry;
> + struct dwc2_qh *qh;
> + u32 hfnum;
> + enum dwc2_transaction_type tr_type;
> +
> +#ifdef DEBUG_SOF
> + dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
> +#endif
> +
> + hfnum = readl(hsotg->regs + HFNUM);
> + hsotg->frame_number = hfnum >> HFNUM_FRNUM_SHIFT &
> + HFNUM_FRNUM_MASK >> HFNUM_FRNUM_SHIFT;
> +
> +#ifdef DWC2_TRACK_MISSED_SOFS
> + dwc2_track_missed_sofs(hsotg);
> +#endif
another suggestion, would be to move the ifdefs inside the function
body, so that it's a no-op when DWC2_TRACK_MISSED_SOFS isn't set, which
means you don't need to ifdef the call here.
> +static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
> + struct dwc2_host_chan *chan, int chnum,
> + struct dwc2_qtd *qtd)
> +{
> + u32 hcintmsk;
> + int out_nak_enh = 0;
> +
> + dev_vdbg(hsotg->dev,
> + "--Host Channel %d Interrupt: DMA Channel Halted--\n", chnum);
> +
> + /*
> + * For core with OUT NAK enhancement, the flow for high-speed
> + * CONTROL/BULK OUT is handled a little differently
> + */
> + if (hsotg->snpsid >= DWC2_CORE_REV_2_71a) {
> + if (chan->speed == USB_SPEED_HIGH && !chan->ep_is_in &&
> + (chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
> + chan->ep_type == USB_ENDPOINT_XFER_BULK)) {
> + out_nak_enh = 1;
> + }
> + }
> +
> + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE ||
> + (chan->halt_status == DWC2_HC_XFER_AHB_ERR &&
> + hsotg->core_params->dma_desc_enable <= 0)) {
> + if (hsotg->core_params->dma_desc_enable > 0)
> + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
> + chan->halt_status);
> + else
> + /*
> + * Just release the channel. A dequeue can happen on a
> + * transfer timeout. In the case of an AHB Error, the
> + * channel was forced to halt because there's no way to
> + * gracefully recover.
> + */
> + dwc2_release_channel(hsotg, chan, qtd,
> + chan->halt_status);
> + return;
> + }
> +
> + hcintmsk = readl(hsotg->regs + HCINTMSK(chnum));
> +
> + if (chan->hcint & HCINTMSK_XFERCOMPL) {
> + /*
> + * Todo: This is here because of a possible hardware bug. Spec
> + * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
> + * interrupt w/ACK bit set should occur, but I only see the
> + * XFERCOMP bit, even with it masked out. This is a workaround
> + * for that behavior. Should fix this when hardware is fixed.
> + */
> + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && !chan->ep_is_in)
> + dwc2_hc_ack_intr(hsotg, chan, chnum, qtd);
> + dwc2_hc_xfercomp_intr(hsotg, chan, chnum, qtd);
> + } else if (chan->hcint & HCINTMSK_STALL) {
> + dwc2_hc_stall_intr(hsotg, chan, chnum, qtd);
> + } else if ((chan->hcint & HCINTMSK_XACTERR) &&
> + hsotg->core_params->dma_desc_enable <= 0) {
> + if (out_nak_enh) {
> + if (chan->hcint &
> + (HCINTMSK_NYET | HCINTMSK_NAK | HCINTMSK_ACK)) {
> + dev_vdbg(hsotg->dev,
> + "XactErr with NYET/NAK/ACK\n");
> + qtd->error_count = 0;
> + } else {
> + dev_vdbg(hsotg->dev,
> + "XactErr without NYET/NAK/ACK\n");
> + }
> + }
> +
> + /*
> + * Must handle xacterr before nak or ack. Could get a xacterr
> + * at the same time as either of these on a BULK/CONTROL OUT
> + * that started with a PING. The xacterr takes precedence.
> + */
> + dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
> + } else if ((chan->hcint & HCINTMSK_XCS_XACT) &&
> + hsotg->core_params->dma_desc_enable > 0) {
are all these really mutually exclusive ? I mean, can you get XACTERR
and AHBERR simultaneously ?
--
balbi
signature.asc
Description: Digital signature
