1. 30 7月, 2018 1 次提交
  2. 19 6月, 2018 2 次提交
    • W
      usb: dwc2: alloc dma aligned buffer for isoc split in · af424a41
      William Wu 提交于
      The commit 3bc04e28 ("usb: dwc2: host: Get aligned DMA in
      a more supported way") rips out a lot of code to simply the
      allocation of aligned DMA. However, it also introduces a new
      issue when use isoc split in transfer.
      
      In my test case, I connect the dwc2 controller with an usb hs
      Hub (GL852G-12), and plug an usb fs audio device (Plantronics
      headset) into the downstream port of Hub. Then use the usb mic
      to record, we can find noise when playback.
      
      It's because that the usb Hub uses an MDATA for the first
      transaction and a DATA0 for the second transaction for the isoc
      split in transaction. An typical isoc split in transaction sequence
      like this:
      
      - SSPLIT IN transaction
      - CSPLIT IN transaction
        - MDATA packet
      - CSPLIT IN transaction
        - DATA0 packet
      
      The DMA address of MDATA (urb->dma) is always DWORD-aligned, but
      the DMA address of DATA0 (urb->dma + qtd->isoc_split_offset) may
      not be DWORD-aligned, it depends on the qtd->isoc_split_offset (the
      length of MDATA). In my test case, the length of MDATA is usually
      unaligned, this cause DATA0 packet transmission error.
      
      This patch use kmem_cache to allocate aligned DMA buf for isoc
      split in transaction. Note that according to usb 2.0 spec, the
      maximum data payload size is 1023 bytes for each fs isoc ep,
      and the maximum allowable interrupt data payload size is 64 bytes
      or less for fs interrupt ep. So we set the size of object to be
      1024 bytes in the kmem cache.
      Tested-by: NGevorg Sahakyan <sahakyan@synopsys.com>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Acked-by: Minas Harutyunyan hminas@synopsys.com>
      Signed-off-by: NWilliam Wu <william.wu@rock-chips.com>
      Reviewed-by: NDouglas Anderson <dianders@chromium.org>
      Signed-off-by: NFelipe Balbi <felipe.balbi@linux.intel.com>
      af424a41
    • W
      usb: dwc2: fix the incorrect bitmaps for the ports of multi_tt hub · 87606759
      William Wu 提交于
      The dwc2_get_ls_map() use ttport to reference into the
      bitmap if we're on a multi_tt hub. But the bitmaps index
      from 0 to (hub->maxchild - 1), while the ttport index from
      1 to hub->maxchild. This will cause invalid memory access
      when the number of ttport is hub->maxchild.
      
      Without this patch, I can easily meet a Kernel panic issue
      if connect a low-speed USB mouse with the max port of FE2.1
      multi-tt hub (1a40:0201) on rk3288 platform.
      
      Fixes: 9f9f09b0 ("usb: dwc2: host: Totally redo the microframe scheduler")
      Cc: <stable@vger.kernel.org>
      Reviewed-by: NDouglas Anderson <dianders@chromium.org>
      Acked-by: Minas Harutyunyan hminas@synopsys.com>
      Signed-off-by: NWilliam Wu <william.wu@rock-chips.com>
      Signed-off-by: NFelipe Balbi <felipe.balbi@linux.intel.com>
      87606759
  3. 21 5月, 2018 1 次提交
  4. 13 12月, 2017 1 次提交
    • D
      usb: dwc2: host: Don't retry NAKed transactions right away · 38d2b5fb
      Douglas Anderson 提交于
      On rk3288-veyron devices on Chrome OS it was found that plugging in an
      Arduino-based USB device could cause the system to lockup, especially
      if the CPU Frequency was at one of the slower operating points (like
      100 MHz / 200 MHz).
      
      Upon tracing, I found that the following was happening:
      * The USB device (full speed) was connected to a high speed hub and
        then to the rk3288.  Thus, we were dealing with split transactions,
        which is all handled in software on dwc2.
      * Userspace was initiating a BULK IN transfer
      * When we sent the SSPLIT (to start the split transaction), we got an
        ACK.  Good.  Then we issued the CSPLIT.
      * When we sent the CSPLIT, we got back a NAK.  We immediately (from
        the interrupt handler) started to retry and sent another SSPLIT.
      * The device kept NAKing our CSPLIT, so we kept ping-ponging between
        sending a SSPLIT and a CSPLIT, each time sending from the interrupt
        handler.
      * The handling of the interrupts was (because of the low CPU speed and
        the inefficiency of the dwc2 interrupt handler) was actually taking
        _longer_ than it took the other side to send the ACK/NAK.  Thus we
        were _always_ in the USB interrupt routine.
      * The fact that USB interrupts were always going off was preventing
        other things from happening in the system.  This included preventing
        the system from being able to transition to a higher CPU frequency.
      
      As I understand it, there is no requirement to retry super quickly
      after a NAK, we just have to retry sometime in the future.  Thus one
      solution to the above is to just add a delay between getting a NAK and
      retrying the transmission.  If this delay is sufficiently long to get
      out of the interrupt routine then the rest of the system will be able
      to make forward progress.  Even a 25 us delay would probably be
      enough, but we'll be extra conservative and try to delay 1 ms (the
      exact amount depends on HZ and the accuracy of the jiffy and how close
      the current jiffy is to ticking, but could be as much as 20 ms or as
      little as 1 ms).
      
      Presumably adding a delay like this could impact the USB throughput,
      so we only add the delay with repeated NAKs.
      
      NOTE: Upon further testing of a pl2303 serial adapter, I found that
      this fix may help with problems there.  Specifically I found that the
      pl2303 serial adapters tend to respond with a NAK when they have
      nothing to say and thus we end with this same sequence.
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Reviewed-by: NJulius Werner <jwerner@chromium.org>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NFelipe Balbi <felipe.balbi@linux.intel.com>
      38d2b5fb
  5. 22 11月, 2017 1 次提交
    • K
      treewide: setup_timer() -> timer_setup() · e99e88a9
      Kees Cook 提交于
      This converts all remaining cases of the old setup_timer() API into using
      timer_setup(), where the callback argument is the structure already
      holding the struct timer_list. These should have no behavioral changes,
      since they just change which pointer is passed into the callback with
      the same available pointers after conversion. It handles the following
      examples, in addition to some other variations.
      
      Casting from unsigned long:
      
          void my_callback(unsigned long data)
          {
              struct something *ptr = (struct something *)data;
          ...
          }
          ...
          setup_timer(&ptr->my_timer, my_callback, ptr);
      
      and forced object casts:
      
          void my_callback(struct something *ptr)
          {
          ...
          }
          ...
          setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
      
      become:
      
          void my_callback(struct timer_list *t)
          {
              struct something *ptr = from_timer(ptr, t, my_timer);
          ...
          }
          ...
          timer_setup(&ptr->my_timer, my_callback, 0);
      
      Direct function assignments:
      
          void my_callback(unsigned long data)
          {
              struct something *ptr = (struct something *)data;
          ...
          }
          ...
          ptr->my_timer.function = my_callback;
      
      have a temporary cast added, along with converting the args:
      
          void my_callback(struct timer_list *t)
          {
              struct something *ptr = from_timer(ptr, t, my_timer);
          ...
          }
          ...
          ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
      
      And finally, callbacks without a data assignment:
      
          void my_callback(unsigned long data)
          {
          ...
          }
          ...
          setup_timer(&ptr->my_timer, my_callback, 0);
      
      have their argument renamed to verify they're unused during conversion:
      
          void my_callback(struct timer_list *unused)
          {
          ...
          }
          ...
          timer_setup(&ptr->my_timer, my_callback, 0);
      
      The conversion is done with the following Coccinelle script:
      
      spatch --very-quiet --all-includes --include-headers \
      	-I ./arch/x86/include -I ./arch/x86/include/generated \
      	-I ./include -I ./arch/x86/include/uapi \
      	-I ./arch/x86/include/generated/uapi -I ./include/uapi \
      	-I ./include/generated/uapi --include ./include/linux/kconfig.h \
      	--dir . \
      	--cocci-file ~/src/data/timer_setup.cocci
      
      @fix_address_of@
      expression e;
      @@
      
       setup_timer(
      -&(e)
      +&e
       , ...)
      
      // Update any raw setup_timer() usages that have a NULL callback, but
      // would otherwise match change_timer_function_usage, since the latter
      // will update all function assignments done in the face of a NULL
      // function initialization in setup_timer().
      @change_timer_function_usage_NULL@
      expression _E;
      identifier _timer;
      type _cast_data;
      @@
      
      (
      -setup_timer(&_E->_timer, NULL, _E);
      +timer_setup(&_E->_timer, NULL, 0);
      |
      -setup_timer(&_E->_timer, NULL, (_cast_data)_E);
      +timer_setup(&_E->_timer, NULL, 0);
      |
      -setup_timer(&_E._timer, NULL, &_E);
      +timer_setup(&_E._timer, NULL, 0);
      |
      -setup_timer(&_E._timer, NULL, (_cast_data)&_E);
      +timer_setup(&_E._timer, NULL, 0);
      )
      
      @change_timer_function_usage@
      expression _E;
      identifier _timer;
      struct timer_list _stl;
      identifier _callback;
      type _cast_func, _cast_data;
      @@
      
      (
      -setup_timer(&_E->_timer, _callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, &_callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, _callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)_callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, &_callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
       _E->_timer@_stl.function = _callback;
      |
       _E->_timer@_stl.function = &_callback;
      |
       _E->_timer@_stl.function = (_cast_func)_callback;
      |
       _E->_timer@_stl.function = (_cast_func)&_callback;
      |
       _E._timer@_stl.function = _callback;
      |
       _E._timer@_stl.function = &_callback;
      |
       _E._timer@_stl.function = (_cast_func)_callback;
      |
       _E._timer@_stl.function = (_cast_func)&_callback;
      )
      
      // callback(unsigned long arg)
      @change_callback_handle_cast
       depends on change_timer_function_usage@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _origtype;
      identifier _origarg;
      type _handletype;
      identifier _handle;
      @@
      
       void _callback(
      -_origtype _origarg
      +struct timer_list *t
       )
       {
      (
      	... when != _origarg
      	_handletype *_handle =
      -(_handletype *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      |
      	... when != _origarg
      	_handletype *_handle =
      -(void *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      |
      	... when != _origarg
      	_handletype *_handle;
      	... when != _handle
      	_handle =
      -(_handletype *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      |
      	... when != _origarg
      	_handletype *_handle;
      	... when != _handle
      	_handle =
      -(void *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      )
       }
      
      // callback(unsigned long arg) without existing variable
      @change_callback_handle_cast_no_arg
       depends on change_timer_function_usage &&
                           !change_callback_handle_cast@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _origtype;
      identifier _origarg;
      type _handletype;
      @@
      
       void _callback(
      -_origtype _origarg
      +struct timer_list *t
       )
       {
      +	_handletype *_origarg = from_timer(_origarg, t, _timer);
      +
      	... when != _origarg
      -	(_handletype *)_origarg
      +	_origarg
      	... when != _origarg
       }
      
      // Avoid already converted callbacks.
      @match_callback_converted
       depends on change_timer_function_usage &&
                  !change_callback_handle_cast &&
      	    !change_callback_handle_cast_no_arg@
      identifier change_timer_function_usage._callback;
      identifier t;
      @@
      
       void _callback(struct timer_list *t)
       { ... }
      
      // callback(struct something *handle)
      @change_callback_handle_arg
       depends on change_timer_function_usage &&
      	    !match_callback_converted &&
                  !change_callback_handle_cast &&
                  !change_callback_handle_cast_no_arg@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _handletype;
      identifier _handle;
      @@
      
       void _callback(
      -_handletype *_handle
      +struct timer_list *t
       )
       {
      +	_handletype *_handle = from_timer(_handle, t, _timer);
      	...
       }
      
      // If change_callback_handle_arg ran on an empty function, remove
      // the added handler.
      @unchange_callback_handle_arg
       depends on change_timer_function_usage &&
      	    change_callback_handle_arg@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _handletype;
      identifier _handle;
      identifier t;
      @@
      
       void _callback(struct timer_list *t)
       {
      -	_handletype *_handle = from_timer(_handle, t, _timer);
       }
      
      // We only want to refactor the setup_timer() data argument if we've found
      // the matching callback. This undoes changes in change_timer_function_usage.
      @unchange_timer_function_usage
       depends on change_timer_function_usage &&
                  !change_callback_handle_cast &&
                  !change_callback_handle_cast_no_arg &&
      	    !change_callback_handle_arg@
      expression change_timer_function_usage._E;
      identifier change_timer_function_usage._timer;
      identifier change_timer_function_usage._callback;
      type change_timer_function_usage._cast_data;
      @@
      
      (
      -timer_setup(&_E->_timer, _callback, 0);
      +setup_timer(&_E->_timer, _callback, (_cast_data)_E);
      |
      -timer_setup(&_E._timer, _callback, 0);
      +setup_timer(&_E._timer, _callback, (_cast_data)&_E);
      )
      
      // If we fixed a callback from a .function assignment, fix the
      // assignment cast now.
      @change_timer_function_assignment
       depends on change_timer_function_usage &&
                  (change_callback_handle_cast ||
                   change_callback_handle_cast_no_arg ||
                   change_callback_handle_arg)@
      expression change_timer_function_usage._E;
      identifier change_timer_function_usage._timer;
      identifier change_timer_function_usage._callback;
      type _cast_func;
      typedef TIMER_FUNC_TYPE;
      @@
      
      (
       _E->_timer.function =
      -_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E->_timer.function =
      -&_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E->_timer.function =
      -(_cast_func)_callback;
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E->_timer.function =
      -(_cast_func)&_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -&_callback;
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -(_cast_func)_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -(_cast_func)&_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      )
      
      // Sometimes timer functions are called directly. Replace matched args.
      @change_timer_function_calls
       depends on change_timer_function_usage &&
                  (change_callback_handle_cast ||
                   change_callback_handle_cast_no_arg ||
                   change_callback_handle_arg)@
      expression _E;
      identifier change_timer_function_usage._timer;
      identifier change_timer_function_usage._callback;
      type _cast_data;
      @@
      
       _callback(
      (
      -(_cast_data)_E
      +&_E->_timer
      |
      -(_cast_data)&_E
      +&_E._timer
      |
      -_E
      +&_E->_timer
      )
       )
      
      // If a timer has been configured without a data argument, it can be
      // converted without regard to the callback argument, since it is unused.
      @match_timer_function_unused_data@
      expression _E;
      identifier _timer;
      identifier _callback;
      @@
      
      (
      -setup_timer(&_E->_timer, _callback, 0);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, _callback, 0L);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, _callback, 0UL);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, 0);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, 0L);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, 0UL);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_timer, _callback, 0);
      +timer_setup(&_timer, _callback, 0);
      |
      -setup_timer(&_timer, _callback, 0L);
      +timer_setup(&_timer, _callback, 0);
      |
      -setup_timer(&_timer, _callback, 0UL);
      +timer_setup(&_timer, _callback, 0);
      |
      -setup_timer(_timer, _callback, 0);
      +timer_setup(_timer, _callback, 0);
      |
      -setup_timer(_timer, _callback, 0L);
      +timer_setup(_timer, _callback, 0);
      |
      -setup_timer(_timer, _callback, 0UL);
      +timer_setup(_timer, _callback, 0);
      )
      
      @change_callback_unused_data
       depends on match_timer_function_unused_data@
      identifier match_timer_function_unused_data._callback;
      type _origtype;
      identifier _origarg;
      @@
      
       void _callback(
      -_origtype _origarg
      +struct timer_list *unused
       )
       {
      	... when != _origarg
       }
      Signed-off-by: NKees Cook <keescook@chromium.org>
      e99e88a9
  6. 04 11月, 2017 1 次提交
  7. 24 1月, 2017 4 次提交
  8. 18 11月, 2016 2 次提交
  9. 29 6月, 2016 1 次提交
  10. 28 4月, 2016 1 次提交
  11. 04 3月, 2016 10 次提交
    • D
      usb: dwc2: host: Totally redo the microframe scheduler · 9f9f09b0
      Douglas Anderson 提交于
      This totally reimplements the microframe scheduler in dwc2 to attempt to
      handle periodic splits properly.  The old code didn't even try, so this
      was a significant effort since periodic splits are one of the most
      complicated things in USB.
      
      I've attempted to keep the old "don't use the microframe" schduler
      around for now, but not sure it's needed.  It has also only been lightly
      tested.
      
      I think it's pretty certain that this scheduler isn't perfect and might
      have some bugs, but it seems much better than what was there before.
      With this change my stressful USB test (USB webcam + USB audio + some
      keyboards) crackles less.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      9f9f09b0
    • D
      usb: dwc2: host: Properly set even/odd frame · 9cf1a601
      Douglas Anderson 提交于
      When setting up ISO and INT transfers dwc2 needs to specify whether the
      transfer is for an even or an odd frame (or microframe if the controller
      is running in high speed mode).
      
      The controller appears to use this as a simple way to figure out if a
      transfer should happen right away (in the current microframe) or should
      happen at the start of the next microframe.  Said another way:
      
      - If you set "odd" and the current frame number is odd it appears that
        the controller will try to transfer right away.  Same thing if you set
        "even" and the current frame number is even.
      - If the oddness you set and the oddness of the frame number are
        _different_, the transfer will be delayed until the frame number
        changes.
      
      As I understand it, the above technique allows you to plan ahead of time
      where possible by always working on the next frame.  ...but it still
      allows you to properly respond immediately to things that happened in
      the previous frame.
      
      The old dwc2_hc_set_even_odd_frame() didn't really handle this concept.
      It always looked at the frame number and setup the transfer to happen in
      the next frame.  In some cases that meant that certain transactions
      would be transferred in the wrong frame.
      
      We'll try our best to set the even / odd to do the transfer in the
      scheduled frame.  If that fails then we'll do an ugly "schedule ASAP".
      We'll also modify the scheduler code to handle this and not try to
      schedule a second transfer for the same frame.
      
      Note that this change relies on the work to redo the microframe
      scheduler.  It can work atop ("usb: dwc2: host: Manage frame nums better
      in scheduler") but it works even better after ("usb: dwc2: host: Totally
      redo the microframe scheduler").
      
      With this change my stressful USB test (USB webcam + USB audio +
      keyboards) has less audio crackling than before.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      9cf1a601
    • D
      usb: dwc2: host: Manage frame nums better in scheduler · fb616e3f
      Douglas Anderson 提交于
      The dwc2 scheduler (contained in hcd_queue.c) was a bit confusing in the
      way it initted / kept track of which frames a QH was going to be active
      in.  Let's clean things up a little bit in preparation for a rewrite of
      the microframe scheduler.
      
      Specifically:
      * Old code would pick a frame number in dwc2_qh_init() and would try to
        pick it "in a slightly future (micro)frame".  As far as I can tell the
        reason for this was that there was a delay between dwc2_qh_init() and
        when we actually wanted to dwc2_hcd_qh_add().  ...but apparently this
        attempt to be slightly in the future wasn't enough because
        dwc2_hcd_qh_add() then had code to reset things if the frame _wasn't_
        in the future.  There's no reason not to just pick the frame later.
        For non-periodic QH we now pick the frame in dwc2_hcd_qh_add().  For
        periodic QH we pick the frame at dwc2_schedule_periodic() time.
      * The old "dwc2_qh_init() actually assigned to "hsotg->frame_number".
        This doesn't seem like a great idea since that variable is supposed to
        be used to keep track of which SOF the interrupt handler has seen.
        Let's be clean: anyone who wants the current frame number (instead of
        the one as of the last interrupt) should ask for it.
      * The old code wasn't terribly consistent about trying to use the frame
        that the microframe scheduler assigned to it.  In
        dwc2_sched_periodic_split() when it was scheduling the first frame it
        always "ORed" in 0x7 (!).  Since the frame goes on the wire 1 uFrame
        after next_active_frame it meant that the SSPLIT would always try for
        uFrame 0 and the transaction would happen on the low speed bus during
        uFrame 1.  This is irregardless of what the microframe scheduler
        said.
      * The old code assumed it would get called to schedule the next in a
        periodic split very quickly.  That is if next_active_frame was
        0 (transfer on wire in uFrame 1) it assumed it was getting called to
        schedule the next uFrame during uFrame 1 too (so it could queue
        something up for uFrame 2).  It should be possible to actually queue
        something up for uFrame 2 while in uFrame 2 (AKA queue up ASAP).  To
        do this, code needs to look at the previously scheduled frame when
        deciding when to next be active, not look at the current frame number.
      * If there was no microframe scheduler, the old code would check for
        whether we should be active using "qh->next_active_frame ==
        frame_number".  This seemed like a race waiting to happen.  ...plus
        there's no way that you wouldn't want to schedule if next_active_frame
        was actually less than frame number.
      
      Note that this change doesn't make 100% sense on its own since it's
      expecting some sanity in the frame numbers assigned by the microframe
      scheduler and (as per the future patch which rewries it) I think that
      the current microframe scheduler is quite insane.  However, it seems
      like splitting this up from the microframe scheduler patch makes things
      into smaller chunks and hopefully adds to clarity rather than reduces
      it.  The two patches could certainly be squashed.  Not that in the very
      least, I don't see any obvious bad behavior introduced with just this
      patch.
      
      I've attempted to keep the config parameter to disable the microframe
      scheduler in tact in this change, though I'm not sure it's worth it.
      Obviously the code is touched a lot so it's possible I regressed
      something when the microframe scheduler is disabled, though I did some
      basic testing and it seemed to work OK.  I'm still not 100% sure why you
      wouldn't want the microframe scheduler (presuming it works), so maybe a
      future patch (or a future version of this patch?) could remove that
      parameter.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      fb616e3f
    • D
      usb: dwc2: host: Split code out to make dwc2_do_reserve() · 2d3f1398
      Douglas Anderson 提交于
      This no-op change splits code out of dwc2_schedule_periodic() into a
      dwc2_do_reserve() function.  This makes it a little easier to follow the
      logic.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      2d3f1398
    • D
      usb: dwc2: host: Reorder things in hcd_queue.c · b951c6c7
      Douglas Anderson 提交于
      This no-op change just reorders a few functions in hcd_queue.c in order
      to prepare for future changes.  Motivations here:
      
      The functions dwc2_hcd_qh_free() and dwc2_hcd_qh_create() are exported
      functions.  They are not called within the file.  That means that they
      should be near the bottom so that they can easily call static helpers.
      
      The function dwc2_qh_init() is only called by dwc2_hcd_qh_create() and
      should move near the bottom with it.
      
      The only reason that the dwc2_unreserve_timer_fn() timer function (and
      its subroutine dwc2_do_unreserve()) were so high in the file was that
      they needed to be above dwc2_qh_init().  Now that dwc2_qh_init() has
      been moved down it can be moved down a bit.  A later patch will split
      the reserve code out of dwc2_schedule_periodic() and the reserve
      function should be near the unreserve function.  The reserve function
      needs to be below dwc2_find_uframe() since it calls that.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      b951c6c7
    • D
      usb: dwc2: host: Rename some fields in struct dwc2_qh · ced9eee1
      Douglas Anderson 提交于
      This no-op change just does some renames to simplify a future patch.
      
      1. The "interval" field is renamed to "host_interval" to make it more
         obvious that this interval may be 8 times the interval that the
         device sees (if we're doing split transactions).  A future patch will
         also add the "device_interval" field.
      2. The "usecs" field is renamed to "host_us" again to make it more
         obvious that this is the time for the transaction as seen by the
         host.  For split transactions the device may see a much longer
         transaction time.  A future patch will also add "device_us".
      3. The "sched_frame" field is renamed to "next_active_frame".  The name
         "sched_frame" kept confusing me because it felt like something more
         permament (the QH's reservation or something).  The name
         "next_active_frame" makes it more obvious that this field is
         constantly changing.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      ced9eee1
    • D
      usb: dwc2: host: Add a delay before releasing periodic bandwidth · 17dd5b64
      Douglas Anderson 提交于
      We'd like to be able to use HCD_BH in order to speed up the dwc2 host
      interrupt handler quite a bit.  However, according to the kernel doc for
      usb_submit_urb() (specifically the part about "Reserved Bandwidth
      Transfers"), we need to keep a reservation active as long as a device
      driver keeps submitting.  That was easy to do when we gave back the URB
      in the interrupt context: we just looked at when our queue was empty and
      released the reserved bandwidth then.  ...but now we need a little more
      complexity.
      
      We'll follow EHCI's lead in commit 9118f9eb ("USB: EHCI: improve
      interrupt qh unlink") and add a 5ms delay.  Since we don't have a whole
      timer infrastructure in dwc2, we'll just add a timer per QH.  The
      overhead for this is very small.
      
      Note that the dwc2 scheduler is pretty broken (see future patches to fix
      it).  This patch attempts to replicate all old behavior and just add the
      proper delay.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      17dd5b64
    • D
      usb: dwc2: host: Add scheduler tracing · 74fc4a75
      Douglas Anderson 提交于
      In preparation for future changes to the scheduler let's add some
      tracing that makes it easy for us to see what's happening.  By default
      this tracing will be off.
      
      By changing "core.h" you can easily trace to ftrace, the console, or
      nowhere.
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Reviewed-by: NKever Yang <kever.yang@rock-chips.com>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      74fc4a75
    • D
      usb: dwc2: host: Always add to the tail of queues · 94ef7aee
      Douglas Anderson 提交于
      The queues the the dwc2 host controller used are truly queues.  That
      means FIFO or first in first out.
      
      Unfortunately though the code was iterating through these queues
      starting from the head, some places in the code was adding things to the
      queue by adding at the head instead of the tail.  That means last in
      first out.  Doh.
      
      Go through and just always add to the tail.
      
      Doing this makes things much happier when I've got:
      * 7-port USB 2.0 Single-TT hub
      * - Microsoft 2.4 GHz Transceiver v7.0 dongle
      * - Jabra speakerphone playing music
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Reviewed-by: NKever Yang <kever.yang@rock-chips.com>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      94ef7aee
    • D
      usb: dwc2: host: Get aligned DMA in a more supported way · 3bc04e28
      Douglas Anderson 提交于
      All other host controllers who want aligned buffers for DMA do it a
      certain way.  Let's do that too instead of working behind the USB core's
      back.  This makes our interrupt handler not take forever and also rips
      out a lot of code, simplifying things a bunch.
      
      This also has the side effect of removing the 65535 max transfer size
      limit.
      
      NOTE: The actual code to allocate the aligned buffers is ripped almost
      completely from the tegra EHCI driver.  At some point in the future we
      may want to add this functionality to the USB core to share more code
      everywhere.
      Signed-off-by: NDouglas Anderson <dianders@chromium.org>
      Acked-by: NJohn Youn <johnyoun@synopsys.com>
      Tested-by: NHeiko Stuebner <heiko@sntech.de>
      Tested-by: NJohn Youn <johnyoun@synopsys.com>
      Tested-by: NStefan Wahren <stefan.wahren@i2se.com>
      Signed-off-by: NFelipe Balbi <balbi@kernel.org>
      3bc04e28
  12. 15 12月, 2015 1 次提交
  13. 02 10月, 2015 2 次提交
  14. 27 9月, 2015 1 次提交
  15. 07 7月, 2015 1 次提交
  16. 30 4月, 2015 2 次提交
  17. 20 9月, 2014 1 次提交
    • P
      usb: dwc2: handle DMA buffer unmapping sanely · 5dce9555
      Paul Zimmerman 提交于
      The driver's handling of DMA buffers for non-aligned transfers
      was kind of nuts. For IN transfers, it left the URB DMA buffer
      mapped until the transfer completed, then synced it, copied the
      data from the bounce buffer, then synced it again.
      
      Instead of that, just call usb_hcd_unmap_urb_for_dma() to unmap
      the buffer before starting the transfer. Then no syncing is
      required when doing the copy. This should also allow handling of
      other types of mappings besides just dma_map_single() ones.
      
      Also reduce the size of the bounce buffer allocation for Isoc
      endpoints to 3K, since that's the largest possible transfer size.
      
      Tested on Raspberry Pi and Altera SOCFPGA.
      Signed-off-by: NPaul Zimmerman <paulz@synopsys.com>
      Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
      5dce9555
  18. 14 1月, 2014 1 次提交
  19. 26 11月, 2013 3 次提交
  20. 15 11月, 2013 1 次提交
  21. 26 9月, 2013 1 次提交
    • D
      staging: dwc2: add microframe scheduler from downstream Pi kernel · 20f2eb9c
      Dom Cobley 提交于
      The transfer scheduler in the dwc2 driver is pretty basic, not to
      mention buggy. It works fairly well with just a couple of devices
      plugged in, but if you add, say, multiple devices with periodic
      endpoints, the scheduler breaks down and can't even enumerate all
      the devices.
      
      To improve this, import the "microframe scheduler" patch from the
      driver in the downstream Raspberry Pi kernel, which is based on
      the Synopsys vendor driver. The original patch came from Denx
      (http://git.denx.de/?p=linux-denx.git) and was commited to the
      raspberrypi.org git tree by "popcornmix" (Dom Cobley).
      
      I have added a driver parameter for this, enabled by default, in
      case anyone has problems with it and needs to disable it. I don't
      think we should add a DT binding for that, though, since I plan
      to remove the option once any bugs are fixed.
      
      [raspberrypi.org patch from Dom Cobley]
      Signed-off-by: NDom Cobley <popcornmix@gmail.com>
      [adapted to dwc2 driver by Paul Zimmerman]
      Signed-off-by: NPaul Zimmerman <paulz@synopsys.com>
      Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
      20f2eb9c
  22. 17 9月, 2013 1 次提交