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

Attachment: signature.asc
Description: Digital signature

Reply via email to