diff options
Diffstat (limited to 'drivers')
191 files changed, 3851 insertions, 4884 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index c0cd1b9075e3..576228037718 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_BCMA) += bcma/ obj-$(CONFIG_VHOST_RING) += vhost/ +obj-$(CONFIG_VHOST_IOTLB) += vhost/ obj-$(CONFIG_VHOST) += vhost/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_GREYBUS) += greybus/ diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c index 669392f31d4e..6284aff434a1 100644 --- a/drivers/accessibility/speakup/spk_ttyio.c +++ b/drivers/accessibility/speakup/spk_ttyio.c @@ -47,27 +47,20 @@ static int spk_ttyio_ldisc_open(struct tty_struct *tty) { struct spk_ldisc_data *ldisc_data; + if (tty != speakup_tty) + /* Somebody tried to use this line discipline outside speakup */ + return -ENODEV; + if (!tty->ops->write) return -EOPNOTSUPP; - mutex_lock(&speakup_tty_mutex); - if (speakup_tty) { - mutex_unlock(&speakup_tty_mutex); - return -EBUSY; - } - speakup_tty = tty; - ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL); - if (!ldisc_data) { - speakup_tty = NULL; - mutex_unlock(&speakup_tty_mutex); + if (!ldisc_data) return -ENOMEM; - } init_completion(&ldisc_data->completion); ldisc_data->buf_free = true; - speakup_tty->disc_data = ldisc_data; - mutex_unlock(&speakup_tty_mutex); + tty->disc_data = ldisc_data; return 0; } @@ -191,9 +184,25 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) tty_unlock(tty); + mutex_lock(&speakup_tty_mutex); + speakup_tty = tty; ret = tty_set_ldisc(tty, N_SPEAKUP); if (ret) - pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); + speakup_tty = NULL; + mutex_unlock(&speakup_tty_mutex); + + if (!ret) + /* Success */ + return 0; + + pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); + + tty_lock(tty); + if (tty->ops->close) + tty->ops->close(tty, NULL); + tty_unlock(tty); + + tty_kclose(tty); return ret; } diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 7cd5a29fc437..5645226ca3ce 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -142,6 +142,7 @@ config FPGA_DFL tristate "FPGA Device Feature List (DFL) support" select FPGA_BRIDGE select FPGA_REGION + depends on HAS_IOMEM help Device Feature List (DFL) defines a feature list structure that creates a linked list of feature headers within the MMIO space diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index e074f7ed388c..b5f8f3d731cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -1011,6 +1011,11 @@ static int vcn_v3_0_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, boo tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1); WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_CNTL, tmp); + /* Stall DPG before WPTR/RPTR reset */ + WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS), + UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK, + ~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK); + /* set the write pointer delay */ WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR_CNTL, 0); @@ -1033,6 +1038,10 @@ static int vcn_v3_0_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, boo WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR, lower_32_bits(ring->wptr)); + /* Unstall DPG */ + WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS), + 0, ~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK); + return 0; } @@ -1556,8 +1565,14 @@ static int vcn_v3_0_pause_dpg_mode(struct amdgpu_device *adev, UVD_DPG_PAUSE__NJ_PAUSE_DPG_ACK_MASK, UVD_DPG_PAUSE__NJ_PAUSE_DPG_ACK_MASK); + /* Stall DPG before WPTR/RPTR reset */ + WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS), + UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK, + ~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK); + /* Restore */ ring = &adev->vcn.inst[inst_idx].ring_enc[0]; + ring->wptr = 0; WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_LO, ring->gpu_addr); WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_HI, upper_32_bits(ring->gpu_addr)); WREG32_SOC15(VCN, inst_idx, mmUVD_RB_SIZE, ring->ring_size / 4); @@ -1565,14 +1580,16 @@ static int vcn_v3_0_pause_dpg_mode(struct amdgpu_device *adev, WREG32_SOC15(VCN, inst_idx, mmUVD_RB_WPTR, lower_32_bits(ring->wptr)); ring = &adev->vcn.inst[inst_idx].ring_enc[1]; + ring->wptr = 0; WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_LO2, ring->gpu_addr); WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_HI2, upper_32_bits(ring->gpu_addr)); WREG32_SOC15(VCN, inst_idx, mmUVD_RB_SIZE2, ring->ring_size / 4); WREG32_SOC15(VCN, inst_idx, mmUVD_RB_RPTR2, lower_32_bits(ring->wptr)); WREG32_SOC15(VCN, inst_idx, mmUVD_RB_WPTR2, lower_32_bits(ring->wptr)); - WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR, - RREG32_SOC15(VCN, inst_idx, mmUVD_SCRATCH2) & 0x7FFFFFFF); + /* Unstall DPG */ + WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS), + 0, ~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK); SOC15_WAIT_ON_RREG(VCN, inst_idx, mmUVD_POWER_STATUS, UVD_PGFSM_CONFIG__UVDM_UVDU_PWR_ON, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK); @@ -1630,10 +1647,6 @@ static void vcn_v3_0_dec_ring_set_wptr(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) - WREG32_SOC15(VCN, ring->me, mmUVD_SCRATCH2, - lower_32_bits(ring->wptr) | 0x80000000); - if (ring->use_doorbell) { adev->wb.wb[ring->wptr_offs] = lower_32_bits(ring->wptr); WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr)); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c index 2f8fee05547a..c001307b0a59 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c @@ -163,8 +163,17 @@ void rn_update_clocks(struct clk_mgr *clk_mgr_base, new_clocks->dppclk_khz = 100000; } - if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->base.clks.dppclk_khz)) { - if (clk_mgr->base.clks.dppclk_khz > new_clocks->dppclk_khz) + /* + * Temporally ignore thew 0 cases for disp and dpp clks. + * We may have a new feature that requires 0 clks in the future. + */ + if (new_clocks->dppclk_khz == 0 || new_clocks->dispclk_khz == 0) { + new_clocks->dppclk_khz = clk_mgr_base->clks.dppclk_khz; + new_clocks->dispclk_khz = clk_mgr_base->clks.dispclk_khz; + } + + if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr_base->clks.dppclk_khz)) { + if (clk_mgr_base->clks.dppclk_khz > new_clocks->dppclk_khz) dpp_clock_lowered = true; clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz; update_dppclk = true; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index 2380759ddf48..6db96fa1df09 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -1164,7 +1164,12 @@ int smu_v11_0_set_fan_speed_rpm(struct smu_context *smu, if (ret) return ret; - crystal_clock_freq = amdgpu_asic_get_xclk(adev); + /* + * crystal_clock_freq div by 4 is required since the fan control + * module refers to 25MHz + */ + + crystal_clock_freq = amdgpu_asic_get_xclk(adev) / 4; tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); WREG32_SOC15(THM, 0, mmCG_TACH_CTRL, REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL), diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 99e682563d47..3bfe6ed67da1 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -18021,16 +18021,6 @@ int intel_modeset_init_nogem(struct drm_i915_private *i915) if (!HAS_GMCH(i915)) sanitize_watermarks(i915); - /* - * Force all active planes to recompute their states. So that on - * mode_setcrtc after probe, all the intel_plane_state variables - * are already calculated and there is no assert_plane warnings - * during bootup. - */ - ret = intel_initial_commit(dev); - if (ret) - drm_dbg_kms(&i915->drm, "Initial commit in probe failed.\n"); - return 0; } @@ -18039,11 +18029,21 @@ int intel_modeset_init(struct drm_i915_private *i915) { int ret; - intel_overlay_setup(i915); - if (!HAS_DISPLAY(i915)) return 0; + /* + * Force all active planes to recompute their states. So that on + * mode_setcrtc after probe, all the intel_plane_state variables + * are already calculated and there is no assert_plane warnings + * during bootup. + */ + ret = intel_initial_commit(&i915->drm); + if (ret) + return ret; + + intel_overlay_setup(i915); + ret = intel_fbdev_init(&i915->drm); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c index cf6e05ea4d8f..a24cc1ff08a0 100644 --- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c @@ -101,18 +101,37 @@ static void __intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b) intel_gt_pm_put_async(b->irq_engine->gt); } +static void intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b) +{ + spin_lock(&b->irq_lock); + if (b->irq_armed) + __intel_breadcrumbs_disarm_irq(b); + spin_unlock(&b->irq_lock); +} + static void add_signaling_context(struct intel_breadcrumbs *b, struct intel_context *ce) { - intel_context_get(ce); - list_add_tail(&ce->signal_link, &b->signalers); + lockdep_assert_held(&ce->signal_lock); + + spin_lock(&b->signalers_lock); + list_add_rcu(&ce->signal_link, &b->signalers); + spin_unlock(&b->signalers_lock); } -static void remove_signaling_context(struct intel_breadcrumbs *b, +static bool remove_signaling_context(struct intel_breadcrumbs *b, struct intel_context *ce) { - list_del(&ce->signal_link); - intel_context_put(ce); + lockdep_assert_held(&ce->signal_lock); + + if (!list_empty(&ce->signals)) + return false; + + spin_lock(&b->signalers_lock); + list_del_rcu(&ce->signal_link); + spin_unlock(&b->signalers_lock); + + return true; } static inline bool __request_completed(const struct i915_request *rq) @@ -175,6 +194,8 @@ static void add_retire(struct intel_breadcrumbs *b, struct intel_timeline *tl) static bool __signal_request(struct i915_request *rq) { + GEM_BUG_ON(test_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags)); + if (!__dma_fence_signal(&rq->fence)) { i915_request_put(rq); return false; @@ -195,15 +216,12 @@ static void signal_irq_work(struct irq_work *work) struct intel_breadcrumbs *b = container_of(work, typeof(*b), irq_work); const ktime_t timestamp = ktime_get(); struct llist_node *signal, *sn; - struct intel_context *ce, *cn; - struct list_head *pos, *next; + struct intel_context *ce; signal = NULL; if (unlikely(!llist_empty(&b->signaled_requests))) signal = llist_del_all(&b->signaled_requests); - spin_lock(&b->irq_lock); - /* * Keep the irq armed until the interrupt after all listeners are gone. * @@ -229,47 +247,44 @@ static void signal_irq_work(struct irq_work *work) * interrupt draw less ire from other users of the system and tools * like powertop. */ - if (!signal && b->irq_armed && list_empty(&b->signalers)) - __intel_breadcrumbs_disarm_irq(b); + if (!signal && READ_ONCE(b->irq_armed) && list_empty(&b->signalers)) + intel_breadcrumbs_disarm_irq(b); - list_for_each_entry_safe(ce, cn, &b->signalers, signal_link) { - GEM_BUG_ON(list_empty(&ce->signals)); + rcu_read_lock(); + list_for_each_entry_rcu(ce, &b->signalers, signal_link) { + struct i915_request *rq; - list_for_each_safe(pos, next, &ce->signals) { - struct i915_request *rq = - list_entry(pos, typeof(*rq), signal_link); + list_for_each_entry_rcu(rq, &ce->signals, signal_link) { + bool release; - GEM_BUG_ON(!check_signal_order(ce, rq)); if (!__request_completed(rq)) break; + if (!test_and_clear_bit(I915_FENCE_FLAG_SIGNAL, + &rq->fence.flags)) + break; + /* * Queue for execution after dropping the signaling * spinlock as the callback chain may end up adding * more signalers to the same context or engine. */ - clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags); + spin_lock(&ce->signal_lock); + list_del_rcu(&rq->signal_link); + release = remove_signaling_context(b, ce); + spin_unlock(&ce->signal_lock); + if (__signal_request(rq)) /* We own signal_node now, xfer to local list */ signal = slist_add(&rq->signal_node, signal); - } - /* - * We process the list deletion in bulk, only using a list_add - * (not list_move) above but keeping the status of - * rq->signal_link known with the I915_FENCE_FLAG_SIGNAL bit. - */ - if (!list_is_first(pos, &ce->signals)) { - /* Advance the list to the first incomplete request */ - __list_del_many(&ce->signals, pos); - if (&ce->signals == pos) { /* now empty */ + if (release) { add_retire(b, ce->timeline); - remove_signaling_context(b, ce); + intel_context_put(ce); } } } - - spin_unlock(&b->irq_lock); + rcu_read_unlock(); llist_for_each_safe(signal, sn, signal) { struct i915_request *rq = @@ -298,14 +313,15 @@ intel_breadcrumbs_create(struct intel_engine_cs *irq_engine) if (!b) return NULL; - spin_lock_init(&b->irq_lock); + b->irq_engine = irq_engine; + + spin_lock_init(&b->signalers_lock); INIT_LIST_HEAD(&b->signalers); init_llist_head(&b->signaled_requests); + spin_lock_init(&b->irq_lock); init_irq_work(&b->irq_work, signal_irq_work); - b->irq_engine = irq_engine; - return b; } @@ -347,9 +363,9 @@ void intel_breadcrumbs_free(struct intel_breadcrumbs *b) kfree(b); } -static void insert_breadcrumb(struct i915_request *rq, - struct intel_breadcrumbs *b) +static void insert_breadcrumb(struct i915_request *rq) { + struct intel_breadcrumbs *b = READ_ONCE(rq->engine)->breadcrumbs; struct intel_context *ce = rq->context; struct list_head *pos; @@ -371,6 +387,7 @@ static void insert_breadcrumb(struct i915_request *rq, } if (list_empty(&ce->signals)) { + intel_context_get(ce); add_signaling_context(b, ce); pos = &ce->signals; } else { @@ -396,8 +413,9 @@ static void insert_breadcrumb(struct i915_request *rq, break; } } - list_add(&rq->signal_link, pos); + list_add_rcu(&rq->signal_link, pos); GEM_BUG_ON(!check_signal_order(ce, rq)); + GEM_BUG_ON(test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &rq->fence.flags)); set_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags); /* @@ -410,7 +428,7 @@ static void insert_breadcrumb(struct i915_request *rq, bool i915_request_enable_breadcrumb(struct i915_request *rq) { - struct intel_breadcrumbs *b; + struct intel_context *ce = rq->context; /* Serialises with i915_request_retire() using rq->lock */ if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &rq->fence.flags)) @@ -425,67 +443,30 @@ bool i915_request_enable_breadcrumb(struct i915_request *rq) if (!test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags)) return true; - /* - * rq->engine is locked by rq->engine->active.lock. That however - * is not known until after rq->engine has been dereferenced and - * the lock acquired. Hence we acquire the lock and then validate - * that rq->engine still matches the lock we hold for it. - * - * Here, we are using the breadcrumb lock as a proxy for the - * rq->engine->active.lock, and we know that since the breadcrumb - * will be serialised within i915_request_submit/i915_request_unsubmit, - * the engine cannot change while active as long as we hold the - * breadcrumb lock on that engine. - * - * From the dma_fence_enable_signaling() path, we are outside of the - * request submit/unsubmit path, and so we must be more careful to - * acquire the right lock. - */ - b = READ_ONCE(rq->engine)->breadcrumbs; - spin_lock(&b->irq_lock); - while (unlikely(b != READ_ONCE(rq->engine)->breadcrumbs)) { - spin_unlock(&b->irq_lock); - b = READ_ONCE(rq->engine)->breadcrumbs; - spin_lock(&b->irq_lock); - } - - /* - * Now that we are finally serialised with request submit/unsubmit, - * [with b->irq_lock] and with i915_request_retire() [via checking - * SIGNALED with rq->lock] confirm the request is indeed active. If - * it is no longer active, the breadcrumb will be attached upon - * i915_request_submit(). - */ + spin_lock(&ce->signal_lock); if (test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags)) - insert_breadcrumb(rq, b); - - spin_unlock(&b->irq_lock); + insert_breadcrumb(rq); + spin_unlock(&ce->signal_lock); return true; } void i915_request_cancel_breadcrumb(struct i915_request *rq) { - struct intel_breadcrumbs *b = rq->engine->breadcrumbs; + struct intel_context *ce = rq->context; + bool release; - /* - * We must wait for b->irq_lock so that we know the interrupt handler - * has released its reference to the intel_context and has completed - * the DMA_FENCE_FLAG_SIGNALED_BIT/I915_FENCE_FLAG_SIGNAL dance (if - * required). - */ - spin_lock(&b->irq_lock); - if (test_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags)) { - struct intel_context *ce = rq->context; + if (!test_and_clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags)) + return; - list_del(&rq->signal_link); - if (list_empty(&ce->signals)) - remove_signaling_context(b, ce); + spin_lock(&ce->signal_lock); + list_del_rcu(&rq->signal_link); + release = remove_signaling_context(rq->engine->breadcrumbs, ce); + spin_unlock(&ce->signal_lock); + if (release) + intel_context_put(ce); - clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags); - i915_request_put(rq); - } - spin_unlock(&b->irq_lock); + i915_request_put(rq); } static void print_signals(struct intel_breadcrumbs *b, struct drm_printer *p) @@ -495,18 +476,17 @@ static void print_signals(struct intel_breadcrumbs *b, struct drm_printer *p) drm_printf(p, "Signals:\n"); - spin_lock_irq(&b->irq_lock); - list_for_each_entry(ce, &b->signalers, signal_link) { - list_for_each_entry(rq, &ce->signals, signal_link) { + rcu_read_lock(); + list_for_each_entry_rcu(ce, &b->signalers, signal_link) { + list_for_each_entry_rcu(rq, &ce->signals, signal_link) drm_printf(p, "\t[%llx:%llx%s] @ %dms\n", rq->fence.context, rq->fence.seqno, i915_request_completed(rq) ? "!" : i915_request_started(rq) ? "*" : "", jiffies_to_msecs(jiffies - rq->emitted_jiffies)); - } } - spin_unlock_irq(&b->irq_lock); + rcu_read_unlock(); } void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine, diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs_types.h b/drivers/gpu/drm/i915/gt/intel_breadcrumbs_types.h index 3fa19820b37a..a74bb3062bd8 100644 --- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs_types.h +++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs_types.h @@ -29,18 +29,16 @@ * the overhead of waking that client is much preferred. */ struct intel_breadcrumbs { - spinlock_t irq_lock; /* protects the lists used in hardirq context */ - /* Not all breadcrumbs are attached to physical HW */ struct intel_engine_cs *irq_engine; + spinlock_t signalers_lock; /* protects the list of signalers */ struct list_head signalers; struct llist_head signaled_requests; + spinlock_t irq_lock; /* protects the interrupt from hardirq context */ struct irq_work irq_work; /* for use from inside irq_lock */ - unsigned int irq_enabled; - bool irq_armed; }; diff --git a/drivers/gpu/drm/i915/gt/intel_context.c b/drivers/gpu/drm/i915/gt/intel_context.c index 92a3f25c4006..349e7fa1488d 100644 --- a/drivers/gpu/drm/i915/gt/intel_context.c +++ b/drivers/gpu/drm/i915/gt/intel_context.c @@ -25,11 +25,18 @@ static struct intel_context *intel_context_alloc(void) return kmem_cache_zalloc(global.slab_ce, GFP_KERNEL); } -void intel_context_free(struct intel_context *ce) +static void rcu_context_free(struct rcu_head *rcu) { + struct intel_context *ce = container_of(rcu, typeof(*ce), rcu); + kmem_cache_free(global.slab_ce, ce); } +void intel_context_free(struct intel_context *ce) +{ + call_rcu(&ce->rcu, rcu_context_free); +} + struct intel_context * intel_context_create(struct intel_engine_cs *engine) { @@ -356,8 +363,7 @@ static int __intel_context_active(struct i915_active *active) } void -intel_context_init(struct intel_context *ce, - struct intel_engine_cs *engine) +intel_context_init(struct intel_context *ce, struct intel_engine_cs *engine) { GEM_BUG_ON(!engine->cops); GEM_BUG_ON(!engine->gt->vm); @@ -373,7 +379,8 @@ intel_context_init(struct intel_context *ce, ce->vm = i915_vm_get(engine->gt->vm); - INIT_LIST_HEAD(&ce->signal_link); + /* NB ce->signal_link/lock is used under RCU */ + spin_lock_init(&ce->signal_lock); INIT_LIST_HEAD(&ce->signals); mutex_init(&ce->pin_mutex); diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h b/drivers/gpu/drm/i915/gt/intel_context_types.h index 552cb57a2e8c..52fa9c132746 100644 --- a/drivers/gpu/drm/i915/gt/intel_context_types.h +++ b/drivers/gpu/drm/i915/gt/intel_context_types.h @@ -25,6 +25,7 @@ DECLARE_EWMA(runtime, 3, 8); struct i915_gem_context; struct i915_gem_ww_ctx; struct i915_vma; +struct intel_breadcrumbs; struct intel_context; struct intel_ring; @@ -44,7 +45,16 @@ struct intel_context_ops { }; struct intel_context { - struct kref ref; + /* + * Note: Some fields may be accessed under RCU. + * + * Unless otherwise noted a field can safely be assumed to be protected + * by strong reference counting. + */ + union { + struct kref ref; /* no kref_get_unless_zero()! */ + struct rcu_head rcu; + }; struct intel_engine_cs *engine; struct intel_engine_cs *inflight; @@ -54,8 +64,15 @@ struct intel_context { struct i915_address_space *vm; struct i915_gem_context __rcu *gem_context; - struct list_head signal_link; - struct list_head signals; + /* + * @signal_lock protects the list of requests that need signaling, + * @signals. While there are any requests that need signaling, + * we add the context to the breadcrumbs worker, and remove it + * upon completion/cancellation of the last request. + */ + struct list_head signal_link; /* Accessed under RCU */ + struct list_head signals; /* Guarded by signal_lock */ + spinlock_t signal_lock; /* protects signals, the list of requests */ struct i915_vma *state; struct intel_ring *ring; diff --git a/drivers/gpu/drm/i915/gt/intel_mocs.c b/drivers/gpu/drm/i915/gt/intel_mocs.c index 313e51e7d4f7..4f74706967fd 100644 --- a/drivers/gpu/drm/i915/gt/intel_mocs.c +++ b/drivers/gpu/drm/i915/gt/intel_mocs.c @@ -131,7 +131,19 @@ static const struct drm_i915_mocs_entry skl_mocs_table[] = { GEN9_MOCS_ENTRIES, MOCS_ENTRY(I915_MOCS_CACHED, LE_3_WB | LE_TC_2_LLC_ELLC | LE_LRUM(3), - L3_3_WB) + L3_3_WB), + + /* + * mocs:63 + * - used by the L3 for all of its evictions. + * Thus it is expected to allow LLC cacheability to enable coherent + * flows to be maintained. + * - used to force L3 uncachable cycles. + * Thus it is expected to make the surface L3 uncacheable. + */ + MOCS_ENTRY(63, + LE_3_WB | LE_TC_1_LLC | LE_LRUM(3), + L3_1_UC) }; /* NOTE: the LE_TGT_CACHE is not used on Broxton */ diff --git a/drivers/gpu/drm/i915/gt/intel_rps.c b/drivers/gpu/drm/i915/gt/intel_rps.c index e6a00eea0631..c1c9cc0ad3b9 100644 --- a/drivers/gpu/drm/i915/gt/intel_rps.c +++ b/drivers/gpu/drm/i915/gt/intel_rps.c @@ -883,6 +883,10 @@ void intel_rps_park(struct intel_rps *rps) adj = -2; rps->last_adj = adj; rps->cur_freq = max_t(int, rps->cur_freq + adj, rps->min_freq); + if (rps->cur_freq < rps->efficient_freq) { + rps->cur_freq = rps->efficient_freq; + rps->last_adj = 0; + } GT_TRACE(rps_to_gt(rps), "park:%x\n", rps->cur_freq); } diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.c b/drivers/gpu/drm/i915/gt/shmem_utils.c index f011ea42487e..463af675fadd 100644 --- a/drivers/gpu/drm/i915/gt/shmem_utils.c +++ b/drivers/gpu/drm/i915/gt/shmem_utils.c @@ -103,10 +103,13 @@ static int __shmem_rw(struct file *file, loff_t off, return PTR_ERR(page); vaddr = kmap(page); - if (write) + if (write) { memcpy(vaddr + offset_in_page(off), ptr, this); - else + set_page_dirty(page); + } else { memcpy(ptr, vaddr + offset_in_page(off), this); + } + mark_page_accessed(page); kunmap(page); put_page(page); diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h index 874af6db6103..620b6fab2c5c 100644 --- a/drivers/gpu/drm/i915/i915_request.h +++ b/drivers/gpu/drm/i915/i915_request.h @@ -177,10 +177,8 @@ struct i915_request { struct intel_ring *ring; struct intel_timeline __rcu *timeline; - union { - struct list_head signal_link; - struct llist_node signal_node; - }; + struct list_head signal_link; + struct llist_node signal_node; /* * The rcu epoch of when this request was allocated. Used to judiciously diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index b721b8b262ce..9e1224d54729 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -22,6 +22,7 @@ #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane.h> #include <drm/drm_plane_helper.h> #include <drm/drm_vblank.h> @@ -484,17 +485,27 @@ static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane, writel(ctrl, mxsfb->base + LCDC_AS_CTRL); } +static bool mxsfb_format_mod_supported(struct drm_plane *plane, + uint32_t format, + uint64_t modifier) +{ + return modifier == DRM_FORMAT_MOD_LINEAR; +} + static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, .atomic_check = mxsfb_plane_atomic_check, .atomic_update = mxsfb_plane_primary_atomic_update, }; static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, .atomic_check = mxsfb_plane_atomic_check, .atomic_update = mxsfb_plane_overlay_atomic_update, }; static const struct drm_plane_funcs mxsfb_plane_funcs = { + .format_mod_supported = mxsfb_format_mod_supported, .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 56b335a55966..7daa12eec01b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1214,8 +1214,8 @@ retry: } reg->bus.offset = handle; - ret = 0; } + ret = 0; break; default: ret = -EINVAL; diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 033fd30074b0..282e4c837cd9 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -195,8 +195,7 @@ static void sdi_bridge_mode_set(struct drm_bridge *bridge, sdi->pixelclock = adjusted_mode->clock * 1000; } -static void sdi_bridge_enable(struct drm_bridge *bridge, - struct drm_bridge_state *bridge_state) +static void sdi_bridge_enable(struct drm_bridge *bridge) { struct sdi_device *sdi = drm_bridge_to_sdi(bridge); struct dispc_clock_info dispc_cinfo; @@ -259,8 +258,7 @@ err_get_dispc: regulator_disable(sdi->vdds_sdi_reg); } -static void sdi_bridge_disable(struct drm_bridge *bridge, - struct drm_bridge_state *bridge_state) +static void sdi_bridge_disable(struct drm_bridge *bridge) { struct sdi_device *sdi = drm_bridge_to_sdi(bridge); @@ -278,8 +276,8 @@ static const struct drm_bridge_funcs sdi_bridge_funcs = { .mode_valid = sdi_bridge_mode_valid, .mode_fixup = sdi_bridge_mode_fixup, .mode_set = sdi_bridge_mode_set, - .atomic_enable = sdi_bridge_enable, - .atomic_disable = sdi_bridge_disable, + .enable = sdi_bridge_enable, + .disable = sdi_bridge_disable, }; static void sdi_bridge_init(struct sdi_device *sdi) diff --git a/drivers/gpu/drm/panel/panel-sony-acx565akm.c b/drivers/gpu/drm/panel/panel-sony-acx565akm.c index e95fdfb16b6c..ba0b3ead150f 100644 --- a/drivers/gpu/drm/panel/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/panel/panel-sony-acx565akm.c @@ -629,7 +629,7 @@ static int acx565akm_probe(struct spi_device *spi) lcd->spi = spi; mutex_init(&lcd->mutex); - lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); + lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(lcd->reset_gpio)) { dev_err(&spi->dev, "failed to get reset GPIO\n"); return PTR_ERR(lcd->reset_gpio); diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index f292c6a6e20f..41edd0a421b2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -544,7 +544,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, struct device_node *port, *endpoint; int ret = 0, child_count = 0; const char *name; - u32 endpoint_id; + u32 endpoint_id = 0; lvds->drm_dev = drm_dev; port = of_graph_get_port_by_id(dev->of_node, 1); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index ba9d1c3e7cac..e4baf07992a4 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -90,7 +90,7 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) if (!fpriv) return -ENOMEM; - idr_init(&fpriv->contexts); + idr_init_base(&fpriv->contexts, 1); mutex_init(&fpriv->lock); filp->driver_priv = fpriv; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 5a4fd0dbf4cf..47d26b5d9945 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -129,7 +129,6 @@ int tegra_output_probe(struct tegra_output *output) if (!output->ddc) { err = -EPROBE_DEFER; - of_node_put(ddc); return err; } } diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index e88a17c2937f..cc2aa2308a51 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -397,7 +397,6 @@ struct tegra_sor; struct tegra_sor_ops { const char *name; int (*probe)(struct tegra_sor *sor); - int (*remove)(struct tegra_sor *sor); void (*audio_enable)(struct tegra_sor *sor); void (*audio_disable)(struct tegra_sor *sor); }; @@ -2942,6 +2941,24 @@ static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = { .atomic_check = tegra_sor_encoder_atomic_check, }; +static void tegra_sor_disable_regulator(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static int tegra_sor_enable_regulator(struct tegra_sor *sor, struct regulator *reg) +{ + int err; + + err = regulator_enable(reg); + if (err) + return err; + + return devm_add_action_or_reset(sor->dev, tegra_sor_disable_regulator, reg); +} + static int tegra_sor_hdmi_probe(struct tegra_sor *sor) { int err; @@ -2953,7 +2970,7 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor) return PTR_ERR(sor->avdd_io_supply); } - err = regulator_enable(sor->avdd_io_supply); + err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply); if (err < 0) { dev_err(sor->dev, "failed to enable AVDD I/O supply: %d\n", err); @@ -2967,7 +2984,7 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor) return PTR_ERR(sor->vdd_pll_supply); } - err = regulator_enable(sor->vdd_pll_supply); + err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply); if (err < 0) { dev_err(sor->dev, "failed to enable VDD PLL supply: %d\n", err); @@ -2981,7 +2998,7 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor) return PTR_ERR(sor->hdmi_supply); } - err = regulator_enable(sor->hdmi_supply); + err = tegra_sor_enable_regulator(sor, sor->hdmi_supply); if (err < 0) { dev_err(sor->dev, "failed to enable HDMI supply: %d\n", err); return err; @@ -2992,19 +3009,9 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor) return 0; } -static int tegra_sor_hdmi_remove(struct tegra_sor *sor) -{ - regulator_disable(sor->hdmi_supply); - regulator_disable(sor->vdd_pll_supply); - regulator_disable(sor->avdd_io_supply); - - return 0; -} - static const struct tegra_sor_ops tegra_sor_hdmi_ops = { .name = "HDMI", .probe = tegra_sor_hdmi_probe, - .remove = tegra_sor_hdmi_remove, .audio_enable = tegra_sor_hdmi_audio_enable, .audio_disable = tegra_sor_hdmi_audio_disable, }; @@ -3017,7 +3024,7 @@ static int tegra_sor_dp_probe(struct tegra_sor *sor) if (IS_ERR(sor->avdd_io_supply)) return PTR_ERR(sor->avdd_io_supply); - err = regulator_enable(sor->avdd_io_supply); + err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply); if (err < 0) return err; @@ -3025,25 +3032,16 @@ static int tegra_sor_dp_probe(struct tegra_sor *sor) if (IS_ERR(sor->vdd_pll_supply)) return PTR_ERR(sor->vdd_pll_supply); - err = regulator_enable(sor->vdd_pll_supply); + err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply); if (err < 0) return err; return 0; } -static int tegra_sor_dp_remove(struct tegra_sor *sor) -{ - regulator_disable(sor->vdd_pll_supply); - regulator_disable(sor->avdd_io_supply); - - return 0; -} - static const struct tegra_sor_ops tegra_sor_dp_ops = { .name = "DP", .probe = tegra_sor_dp_probe, - .remove = tegra_sor_dp_remove, }; static int tegra_sor_init(struct host1x_client *client) @@ -3145,6 +3143,7 @@ static int tegra_sor_init(struct host1x_client *client) if (err < 0) { dev_err(sor->dev, "failed to deassert SOR reset: %d\n", err); + clk_disable_unprepare(sor->clk); return err; } @@ -3152,12 +3151,17 @@ static int tegra_sor_init(struct host1x_client *client) } err = clk_prepare_enable(sor->clk_safe); - if (err < 0) + if (err < 0) { + clk_disable_unprepare(sor->clk); return err; + } err = clk_prepare_enable(sor->clk_dp); - if (err < 0) + if (err < 0) { + clk_disable_unprepare(sor->clk_safe); + clk_disable_unprepare(sor->clk); return err; + } return 0; } @@ -3764,17 +3768,16 @@ static int tegra_sor_probe(struct platform_device *pdev) return err; err = tegra_output_probe(&sor->output); - if (err < 0) { - dev_err(&pdev->dev, "failed to probe output: %d\n", err); - return err; - } + if (err < 0) + return dev_err_probe(&pdev->dev, err, + "failed to probe output\n"); if (sor->ops && sor->ops->probe) { err = sor->ops->probe(sor); if (err < 0) { dev_err(&pdev->dev, "failed to probe %s: %d\n", sor->ops->name, err); - goto output; + goto remove; } } @@ -3955,9 +3958,6 @@ unregister: rpm_disable: pm_runtime_disable(&pdev->dev); remove: - if (sor->ops && sor->ops->remove) - sor->ops->remove(sor); -output: tegra_output_remove(&sor->output); return err; } @@ -3976,12 +3976,6 @@ static int tegra_sor_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); - if (sor->ops && sor->ops->remove) { - err = sor->ops->remove(sor); - if (err < 0) - dev_err(&pdev->dev, "failed to remove SOR: %d\n", err); - } - tegra_output_remove(&sor->output); return 0; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a97a9d058198..a49e0ed4a599 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -734,6 +734,7 @@ config I2C_LPC2K config I2C_MLXBF tristate "Mellanox BlueField I2C controller" depends on MELLANOX_PLATFORM && ARM64 + select I2C_SLAVE help Enabling this option will add I2C SMBus support for Mellanox BlueField system. diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index c98529c76348..e6f8d6e45a15 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -412,6 +412,19 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) dma->chan_using = NULL; } +static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits) +{ + unsigned int temp; + + /* + * i2sr_clr_opcode is the value to clear all interrupts. Here we want to + * clear only <bits>, so we write ~i2sr_clr_opcode with just <bits> + * toggled. This is required because i.MX needs W0C and Vybrid uses W1C. + */ + temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); +} + static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic) { unsigned long orig_jiffies = jiffies; @@ -424,8 +437,7 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool a /* check for arbitration lost */ if (temp & I2SR_IAL) { - temp &= ~I2SR_IAL; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); + i2c_imx_clear_irq(i2c_imx, I2SR_IAL); return -EAGAIN; } @@ -469,7 +481,7 @@ static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic) */ readb_poll_timeout_atomic(addr, regval, regval & I2SR_IIF, 5, 1000 + 100); i2c_imx->i2csr = regval; - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR); + i2c_imx_clear_irq(i2c_imx, I2SR_IIF | I2SR_IAL); } else { wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10); } @@ -478,6 +490,16 @@ static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic) dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__); return -ETIMEDOUT; } + + /* check for arbitration lost */ + if (i2c_imx->i2csr & I2SR_IAL) { + dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__); + i2c_imx_clear_irq(i2c_imx, I2SR_IAL); + + i2c_imx->i2csr = 0; + return -EAGAIN; + } + dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__); i2c_imx->i2csr = 0; return 0; @@ -593,6 +615,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic) /* Stop I2C transaction */ dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + if (!(temp & I2CR_MSTA)) + i2c_imx->stopped = 1; temp &= ~(I2CR_MSTA | I2CR_MTX); if (i2c_imx->dma) temp &= ~I2CR_DMAEN; @@ -623,9 +647,7 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id) if (temp & I2SR_IIF) { /* save status register */ i2c_imx->i2csr = temp; - temp &= ~I2SR_IIF; - temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF); - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); + i2c_imx_clear_irq(i2c_imx, I2SR_IIF); wake_up(&i2c_imx->queue); return IRQ_HANDLED; } @@ -758,9 +780,12 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, */ dev_dbg(dev, "<%s> clear MSTA\n", __func__); temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + if (!(temp & I2CR_MSTA)) + i2c_imx->stopped = 1; temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - i2c_imx_bus_busy(i2c_imx, 0, false); + if (!i2c_imx->stopped) + i2c_imx_bus_busy(i2c_imx, 0, false); } else { /* * For i2c master receiver repeat restart operation like: @@ -885,9 +910,12 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, dev_dbg(&i2c_imx->adapter.dev, "<%s> clear MSTA\n", __func__); temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + if (!(temp & I2CR_MSTA)) + i2c_imx->stopped = 1; temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - i2c_imx_bus_busy(i2c_imx, 0, atomic); + if (!i2c_imx->stopped) + i2c_imx_bus_busy(i2c_imx, 0, atomic); } else { /* * For i2c master receiver repeat restart operation like: diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 33574d40ea9c..2fb0532d8a16 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -1258,9 +1258,9 @@ static int mlxbf_i2c_get_gpio(struct platform_device *pdev, return -EFAULT; gpio_res->io = devm_ioremap(dev, params->start, size); - if (IS_ERR(gpio_res->io)) { + if (!gpio_res->io) { devm_release_mem_region(dev, params->start, size); - return PTR_ERR(gpio_res->io); + return -ENOMEM; } return 0; @@ -1323,9 +1323,9 @@ static int mlxbf_i2c_get_corepll(struct platform_device *pdev, return -EFAULT; corepll_res->io = devm_ioremap(dev, params->start, size); - if (IS_ERR(corepll_res->io)) { + if (!corepll_res->io) { devm_release_mem_region(dev, params->start, size); - return PTR_ERR(corepll_res->io); + return -ENOMEM; } return 0; @@ -1717,9 +1717,9 @@ static int mlxbf_i2c_init_coalesce(struct platform_device *pdev, return -EFAULT; coalesce_res->io = ioremap(params->start, size); - if (IS_ERR(coalesce_res->io)) { + if (!coalesce_res->io) { release_mem_region(params->start, size); - return PTR_ERR(coalesce_res->io); + return -ENOMEM; } priv->coalesce = coalesce_res; diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index f13735beca58..1c259b5188de 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -194,9 +194,9 @@ static irqreturn_t cci_isr(int irq, void *dev) if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_ERROR)) { if (val & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR || val & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR) - cci->master[0].status = -ENXIO; + cci->master[1].status = -ENXIO; else - cci->master[0].status = -EIO; + cci->master[1].status = -EIO; writel(CCI_HALT_REQ_I2C_M1_Q0Q1, cci->base + CCI_HALT_REQ); ret = IRQ_HANDLED; diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index fbc04b60cfd1..5a47915869ae 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -801,7 +801,8 @@ static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup) if (ret || qup->bus_err || qup->qup_err) { reinit_completion(&qup->xfer); - if (qup_i2c_change_state(qup, QUP_RUN_STATE)) { + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) { dev_err(qup->dev, "change to run state timed out"); goto desc_err; } diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 7ee7ffe22ae3..d79335506ecd 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1140,6 +1140,20 @@ static bool __init intel_idle_max_cstate_reached(int cstate) return false; } +static bool __init intel_idle_state_needs_timer_stop(struct cpuidle_state *state) +{ + unsigned long eax = flg2MWAIT(state->flags); + + if (boot_cpu_has(X86_FEATURE_ARAT)) + return false; + + /* + * Switch over to one-shot tick broadcast if the target C-state + * is deeper than C1. + */ + return !!((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK); +} + #ifdef CONFIG_ACPI_PROCESSOR_CSTATE #include <acpi/processor.h> @@ -1210,20 +1224,6 @@ static bool __init intel_idle_acpi_cst_extract(void) return false; } -static bool __init intel_idle_state_needs_timer_stop(struct cpuidle_state *state) -{ - unsigned long eax = flg2MWAIT(state->flags); - - if (boot_cpu_has(X86_FEATURE_ARAT)) - return false; - - /* - * Switch over to one-shot tick broadcast if the target C-state - * is deeper than C1. - */ - return !!((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK); -} - static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index c77cdb3b62b5..8c73377ac82c 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -241,6 +241,7 @@ static const struct xpad_device { { 0x1038, 0x1430, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 }, { 0x1038, 0x1431, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 }, { 0x11c9, 0x55f0, "Nacon GC-100XF", 0, XTYPE_XBOX360 }, + { 0x1209, 0x2882, "Ardwiino Controller", 0, XTYPE_XBOX360 }, { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 }, { 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, @@ -418,6 +419,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */ XPAD_XBOX360_VENDOR(0x1038), /* SteelSeries Controllers */ XPAD_XBOX360_VENDOR(0x11c9), /* Nacon GC100XF */ + XPAD_XBOX360_VENDOR(0x1209), /* Ardwiino Controllers */ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index cae1a3fae83a..d14a65683c5e 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/input.h> #include <linux/init.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/acpi.h> #include <linux/dmi.h> diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a4c9b9652560..7ecb65176c1a 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -219,6 +219,10 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), }, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), + DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), + }, }, { } }; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 944cbb519c6d..abae23af0791 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1471,7 +1471,8 @@ static int __init i8042_setup_aux(void) if (error) goto err_free_ports; - if (aux_enable()) + error = aux_enable(); + if (error) goto err_free_irq; i8042_aux_irq_registered = true; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 98f17fa3a892..b6f75367a284 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2183,11 +2183,11 @@ static int mxt_initialize(struct mxt_data *data) msleep(MXT_FW_RESET_TIME); } - error = mxt_acquire_irq(data); + error = mxt_check_retrigen(data); if (error) return error; - error = mxt_check_retrigen(data); + error = mxt_acquire_irq(data); if (error) return error; diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 9644424591da..4bc453f5bbaa 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -712,10 +712,6 @@ static bool block_size_is_power_of_two(struct cache *cache) return cache->sectors_per_block_shift >= 0; } -/* gcc on ARM generates spurious references to __udivdi3 and __umoddi3 */ -#if defined(CONFIG_ARM) && __GNUC__ == 4 && __GNUC_MINOR__ <= 6 -__always_inline -#endif static dm_block_t block_div(dm_block_t b, uint32_t n) { do_div(b, n); diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 3fc3757def55..5a7a1b90e671 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -3462,7 +3462,7 @@ static int get_mac(struct crypto_shash **hash, struct alg_spec *a, char **error, int r; if (a->alg_string) { - *hash = crypto_alloc_shash(a->alg_string, 0, 0); + *hash = crypto_alloc_shash(a->alg_string, 0, CRYPTO_ALG_ALLOCATES_MEMORY); if (IS_ERR(*hash)) { *error = error_alg; r = PTR_ERR(*hash); @@ -3519,7 +3519,7 @@ static int create_journal(struct dm_integrity_c *ic, char **error) struct journal_completion comp; comp.ic = ic; - ic->journal_crypt = crypto_alloc_skcipher(ic->journal_crypt_alg.alg_string, 0, 0); + ic->journal_crypt = crypto_alloc_skcipher(ic->journal_crypt_alg.alg_string, 0, CRYPTO_ALG_ALLOCATES_MEMORY); if (IS_ERR(ic->journal_crypt)) { *error = "Invalid journal cipher"; r = PTR_ERR(ic->journal_crypt); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index ce543b761be7..7eeb7c4169c9 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -18,7 +18,6 @@ #include <linux/mutex.h> #include <linux/delay.h> #include <linux/atomic.h> -#include <linux/lcm.h> #include <linux/blk-mq.h> #include <linux/mount.h> #include <linux/dax.h> @@ -1247,12 +1246,6 @@ void dm_table_event_callback(struct dm_table *t, void dm_table_event(struct dm_table *t) { - /* - * You can no longer call dm_table_event() from interrupt - * context, use a bottom half instead. - */ - BUG_ON(in_interrupt()); - mutex_lock(&_event_lock); if (t->event_fn) t->event_fn(t->event_context); @@ -1455,10 +1448,6 @@ int dm_calculate_queue_limits(struct dm_table *table, zone_sectors = ti_limits.chunk_sectors; } - /* Stack chunk_sectors if target-specific splitting is required */ - if (ti->max_io_len) - ti_limits.chunk_sectors = lcm_not_zero(ti->max_io_len, - ti_limits.chunk_sectors); /* Set I/O hints portion of queue limits */ if (ti->type->io_hints) ti->type->io_hints(ti, &ti_limits); diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 9ae4ce7df95c..d5223a0e5cc5 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -319,7 +319,7 @@ err1: #else static int persistent_memory_claim(struct dm_writecache *wc) { - BUG(); + return -EOPNOTSUPP; } #endif @@ -2041,7 +2041,7 @@ static int writecache_ctr(struct dm_target *ti, unsigned argc, char **argv) struct wc_memory_superblock s; static struct dm_arg _args[] = { - {0, 10, "Invalid number of feature args"}, + {0, 16, "Invalid number of feature args"}, }; as.argc = argc; @@ -2479,6 +2479,8 @@ static void writecache_status(struct dm_target *ti, status_type_t type, extra_args += 2; if (wc->autocommit_time_set) extra_args += 2; + if (wc->max_age != MAX_AGE_UNSPECIFIED) + extra_args += 2; if (wc->cleaner) extra_args++; if (wc->writeback_fua_set) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c18fc2548518..4e0cbfe3f14d 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -476,8 +476,10 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector, return -EAGAIN; map = dm_get_live_table(md, &srcu_idx); - if (!map) - return -EIO; + if (!map) { + ret = -EIO; + goto out; + } do { struct dm_target *tgt; @@ -507,7 +509,6 @@ out: static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx, struct block_device **bdev) - __acquires(md->io_barrier) { struct dm_target *tgt; struct dm_table *map; @@ -541,7 +542,6 @@ retry: } static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx) - __releases(md->io_barrier) { dm_put_live_table(md, srcu_idx); } @@ -1037,15 +1037,18 @@ static sector_t max_io_len(struct dm_target *ti, sector_t sector) sector_t max_len; /* - * Does the target need to split even further? - * - q->limits.chunk_sectors reflects ti->max_io_len so - * blk_max_size_offset() provides required splitting. - * - blk_max_size_offset() also respects q->limits.max_sectors + * Does the target need to split IO even further? + * - varied (per target) IO splitting is a tenet of DM; this + * explains why stacked chunk_sectors based splitting via + * blk_max_size_offset() isn't possible here. So pass in + * ti->max_io_len to override stacked chunk_sectors. */ - max_len = blk_max_size_offset(ti->table->md->queue, - target_offset); - if (len > max_len) - len = max_len; + if (ti->max_io_len) { + max_len = blk_max_size_offset(ti->table->md->queue, + target_offset, ti->max_io_len); + if (len > max_len) + len = max_len; + } return len; } @@ -1196,11 +1199,9 @@ static int dm_dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff, * ->zero_page_range() is mandatory dax operation. If we are * here, something is wrong. */ - dm_put_live_table(md, srcu_idx); goto out; } ret = ti->type->dax_zero_page_range(ti, pgoff, nr_pages); - out: dm_put_live_table(md, srcu_idx); diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 20572224099a..783bbdcb1e61 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -231,16 +231,16 @@ delete_cdev_device: static void device_cdev_sysfs_del(struct hl_device *hdev) { - /* device_release() won't be called so must free devices explicitly */ - if (!hdev->cdev_sysfs_created) { - kfree(hdev->dev_ctrl); - kfree(hdev->dev); - return; - } + if (!hdev->cdev_sysfs_created) + goto put_devices; hl_sysfs_fini(hdev); cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl); cdev_device_del(&hdev->cdev, hdev->dev); + +put_devices: + put_device(hdev->dev); + put_device(hdev->dev_ctrl); } /* @@ -1371,9 +1371,9 @@ sw_fini: early_fini: device_early_fini(hdev); free_dev_ctrl: - kfree(hdev->dev_ctrl); + put_device(hdev->dev_ctrl); free_dev: - kfree(hdev->dev); + put_device(hdev->dev); out_disabled: hdev->disabled = true; if (add_cdev_sysfs_on_err) diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 84227819e4d1..bfe223abf142 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1626,6 +1626,7 @@ static int vm_ctx_init_with_ranges(struct hl_ctx *ctx, goto host_hpage_range_err; } } else { + kfree(ctx->host_huge_va_range); ctx->host_huge_va_range = ctx->host_va_range; } diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index c06581ffa7bd..f5fd5b786607 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -46,14 +46,4 @@ config INTEL_MEI_TXE Supported SoCs: Intel Bay Trail -config INTEL_MEI_VIRTIO - tristate "Intel MEI interface emulation with virtio framework" - select INTEL_MEI - depends on X86 && PCI && VIRTIO_PCI - help - This module implements mei hw emulation over virtio transport. - The module will be called mei_virtio. - Enable this if your virtual machine supports virtual mei - device over virtio. - source "drivers/misc/mei/hdcp/Kconfig" diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 52aefaab5c1b..f1c76f7ee804 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -22,9 +22,6 @@ obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o mei-txe-objs := pci-txe.o mei-txe-objs += hw-txe.o -obj-$(CONFIG_INTEL_MEI_VIRTIO) += mei-virtio.o -mei-virtio-objs := hw-virtio.o - mei-$(CONFIG_EVENT_TRACING) += mei-trace.o CFLAGS_mei-trace.o = -I$(src) diff --git a/drivers/misc/mei/hw-virtio.c b/drivers/misc/mei/hw-virtio.c deleted file mode 100644 index 899dc1c5e7ca..000000000000 --- a/drivers/misc/mei/hw-virtio.c +++ /dev/null @@ -1,874 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2018-2020, Intel Corporation. - */ -#include <linux/err.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/scatterlist.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/virtio.h> -#include <linux/virtio_config.h> -#include <linux/virtio_ids.h> -#include <linux/atomic.h> - -#include "mei_dev.h" -#include "hbm.h" -#include "client.h" - -#define MEI_VIRTIO_RPM_TIMEOUT 500 -/* ACRN virtio device types */ -#ifndef VIRTIO_ID_MEI -#define VIRTIO_ID_MEI 0xFFFE /* virtio mei */ -#endif - -/** - * struct mei_virtio_cfg - settings passed from the virtio backend - * @buf_depth: read buffer depth in slots (4bytes) - * @hw_ready: hw is ready for operation - * @host_reset: synchronize reset with virtio backend - * @reserved: reserved for alignment - * @fw_status: FW status - */ -struct mei_virtio_cfg { - u32 buf_depth; - u8 hw_ready; - u8 host_reset; - u8 reserved[2]; - u32 fw_status[MEI_FW_STATUS_MAX]; -} __packed; - -struct mei_virtio_hw { - struct mei_device mdev; - char name[32]; - - struct virtqueue *in; - struct virtqueue *out; - - bool host_ready; - struct work_struct intr_handler; - - u32 *recv_buf; - u8 recv_rdy; - size_t recv_sz; - u32 recv_idx; - u32 recv_len; - - /* send buffer */ - atomic_t hbuf_ready; - const void *send_hdr; - const void *send_buf; - - struct mei_virtio_cfg cfg; -}; - -#define to_virtio_hw(_dev) container_of(_dev, struct mei_virtio_hw, mdev) - -/** - * mei_virtio_fw_status() - read status register of mei - * @dev: mei device - * @fw_status: fw status register values - * - * Return: always 0 - */ -static int mei_virtio_fw_status(struct mei_device *dev, - struct mei_fw_status *fw_status) -{ - struct virtio_device *vdev = dev_to_virtio(dev->dev); - - fw_status->count = MEI_FW_STATUS_MAX; - virtio_cread_bytes(vdev, offsetof(struct mei_virtio_cfg, fw_status), - fw_status->status, sizeof(fw_status->status)); - return 0; -} - -/** - * mei_virtio_pg_state() - translate internal pg state - * to the mei power gating state - * There is no power management in ACRN mode always return OFF - * @dev: mei device - * - * Return: - * * MEI_PG_OFF - if aliveness is on (always) - * * MEI_PG_ON - (never) - */ -static inline enum mei_pg_state mei_virtio_pg_state(struct mei_device *dev) -{ - return MEI_PG_OFF; -} - -/** - * mei_virtio_hw_config() - configure hw dependent settings - * - * @dev: mei device - * - * Return: always 0 - */ -static int mei_virtio_hw_config(struct mei_device *dev) -{ - return 0; -} - -/** - * mei_virtio_hbuf_empty_slots() - counts write empty slots. - * @dev: the device structure - * - * Return: always return frontend buf size if buffer is ready, 0 otherwise - */ -static int mei_virtio_hbuf_empty_slots(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - return (atomic_read(&hw->hbuf_ready) == 1) ? hw->cfg.buf_depth : 0; -} - -/** - * mei_virtio_hbuf_is_ready() - checks if write buffer is ready - * @dev: the device structure - * - * Return: true if hbuf is ready - */ -static bool mei_virtio_hbuf_is_ready(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - return atomic_read(&hw->hbuf_ready) == 1; -} - -/** - * mei_virtio_hbuf_max_depth() - returns depth of FE write buffer. - * @dev: the device structure - * - * Return: size of frontend write buffer in bytes - */ -static u32 mei_virtio_hbuf_depth(const struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - return hw->cfg.buf_depth; -} - -/** - * mei_virtio_intr_clear() - clear and stop interrupts - * @dev: the device structure - */ -static void mei_virtio_intr_clear(struct mei_device *dev) -{ - /* - * In our virtio solution, there are two types of interrupts, - * vq interrupt and config change interrupt. - * 1) start/reset rely on virtio config changed interrupt; - * 2) send/recv rely on virtio virtqueue interrupts. - * They are all virtual interrupts. So, we don't have corresponding - * operation to do here. - */ -} - -/** - * mei_virtio_intr_enable() - enables mei BE virtqueues callbacks - * @dev: the device structure - */ -static void mei_virtio_intr_enable(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - struct virtio_device *vdev = dev_to_virtio(dev->dev); - - virtio_config_enable(vdev); - - virtqueue_enable_cb(hw->in); - virtqueue_enable_cb(hw->out); -} - -/** - * mei_virtio_intr_disable() - disables mei BE virtqueues callbacks - * - * @dev: the device structure - */ -static void mei_virtio_intr_disable(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - struct virtio_device *vdev = dev_to_virtio(dev->dev); - - virtio_config_disable(vdev); - - virtqueue_disable_cb(hw->in); - virtqueue_disable_cb(hw->out); -} - -/** - * mei_virtio_synchronize_irq() - wait for pending IRQ handlers for all - * virtqueue - * @dev: the device structure - */ -static void mei_virtio_synchronize_irq(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - /* - * Now, all IRQ handlers are converted to workqueue. - * Change synchronize irq to flush this work. - */ - flush_work(&hw->intr_handler); -} - -static void mei_virtio_free_outbufs(struct mei_virtio_hw *hw) -{ - kfree(hw->send_hdr); - kfree(hw->send_buf); - hw->send_hdr = NULL; - hw->send_buf = NULL; -} - -/** - * mei_virtio_write_message() - writes a message to mei virtio back-end service. - * @dev: the device structure - * @hdr: mei header of message - * @hdr_len: header length - * @data: message payload will be written - * @data_len: message payload length - * - * Return: - * * 0: on success - * * -EIO: if write has failed - * * -ENOMEM: on memory allocation failure - */ -static int mei_virtio_write_message(struct mei_device *dev, - const void *hdr, size_t hdr_len, - const void *data, size_t data_len) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - struct scatterlist sg[2]; - const void *hbuf, *dbuf; - int ret; - - if (WARN_ON(!atomic_add_unless(&hw->hbuf_ready, -1, 0))) - return -EIO; - - hbuf = kmemdup(hdr, hdr_len, GFP_KERNEL); - hw->send_hdr = hbuf; - - dbuf = kmemdup(data, data_len, GFP_KERNEL); - hw->send_buf = dbuf; - - if (!hbuf || !dbuf) { - ret = -ENOMEM; - goto fail; - } - - sg_init_table(sg, 2); - sg_set_buf(&sg[0], hbuf, hdr_len); - sg_set_buf(&sg[1], dbuf, data_len); - - ret = virtqueue_add_outbuf(hw->out, sg, 2, hw, GFP_KERNEL); - if (ret) { - dev_err(dev->dev, "failed to add outbuf\n"); - goto fail; - } - - virtqueue_kick(hw->out); - return 0; -fail: - - mei_virtio_free_outbufs(hw); - - return ret; -} - -/** - * mei_virtio_count_full_read_slots() - counts read full slots. - * @dev: the device structure - * - * Return: -EOVERFLOW if overflow, otherwise filled slots count - */ -static int mei_virtio_count_full_read_slots(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - if (hw->recv_idx > hw->recv_len) - return -EOVERFLOW; - - return hw->recv_len - hw->recv_idx; -} - -/** - * mei_virtio_read_hdr() - Reads 32bit dword from mei virtio receive buffer - * - * @dev: the device structure - * - * Return: 32bit dword of receive buffer (u32) - */ -static inline u32 mei_virtio_read_hdr(const struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - WARN_ON(hw->cfg.buf_depth < hw->recv_idx + 1); - - return hw->recv_buf[hw->recv_idx++]; -} - -static int mei_virtio_read(struct mei_device *dev, unsigned char *buffer, - unsigned long len) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - u32 slots = mei_data2slots(len); - - if (WARN_ON(hw->cfg.buf_depth < hw->recv_idx + slots)) - return -EOVERFLOW; - - /* - * Assumption: There is only one MEI message in recv_buf each time. - * Backend service need follow this rule too. - */ - memcpy(buffer, hw->recv_buf + hw->recv_idx, len); - hw->recv_idx += slots; - - return 0; -} - -static bool mei_virtio_pg_is_enabled(struct mei_device *dev) -{ - return false; -} - -static bool mei_virtio_pg_in_transition(struct mei_device *dev) -{ - return false; -} - -static void mei_virtio_add_recv_buf(struct mei_virtio_hw *hw) -{ - struct scatterlist sg; - - if (hw->recv_rdy) /* not needed */ - return; - - /* refill the recv_buf to IN virtqueue to get next message */ - sg_init_one(&sg, hw->recv_buf, mei_slots2data(hw->cfg.buf_depth)); - hw->recv_len = 0; - hw->recv_idx = 0; - hw->recv_rdy = 1; - virtqueue_add_inbuf(hw->in, &sg, 1, hw->recv_buf, GFP_KERNEL); - virtqueue_kick(hw->in); -} - -/** - * mei_virtio_hw_is_ready() - check whether the BE(hw) has turned ready - * @dev: mei device - * Return: bool - */ -static bool mei_virtio_hw_is_ready(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - struct virtio_device *vdev = dev_to_virtio(dev->dev); - - virtio_cread(vdev, struct mei_virtio_cfg, - hw_ready, &hw->cfg.hw_ready); - - dev_dbg(dev->dev, "hw ready %d\n", hw->cfg.hw_ready); - - return hw->cfg.hw_ready; -} - -/** - * mei_virtio_hw_reset - resets virtio hw. - * - * @dev: the device structure - * @intr_enable: virtio use data/config callbacks - * - * Return: 0 on success an error code otherwise - */ -static int mei_virtio_hw_reset(struct mei_device *dev, bool intr_enable) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - struct virtio_device *vdev = dev_to_virtio(dev->dev); - - dev_dbg(dev->dev, "hw reset\n"); - - dev->recvd_hw_ready = false; - hw->host_ready = false; - atomic_set(&hw->hbuf_ready, 0); - hw->recv_len = 0; - hw->recv_idx = 0; - - hw->cfg.host_reset = 1; - virtio_cwrite(vdev, struct mei_virtio_cfg, - host_reset, &hw->cfg.host_reset); - - mei_virtio_hw_is_ready(dev); - - if (intr_enable) - mei_virtio_intr_enable(dev); - - return 0; -} - -/** - * mei_virtio_hw_reset_release() - release device from the reset - * @dev: the device structure - */ -static void mei_virtio_hw_reset_release(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - struct virtio_device *vdev = dev_to_virtio(dev->dev); - - dev_dbg(dev->dev, "hw reset release\n"); - hw->cfg.host_reset = 0; - virtio_cwrite(vdev, struct mei_virtio_cfg, - host_reset, &hw->cfg.host_reset); -} - -/** - * mei_virtio_hw_ready_wait() - wait until the virtio(hw) has turned ready - * or timeout is reached - * @dev: mei device - * - * Return: 0 on success, error otherwise - */ -static int mei_virtio_hw_ready_wait(struct mei_device *dev) -{ - mutex_unlock(&dev->device_lock); - wait_event_timeout(dev->wait_hw_ready, - dev->recvd_hw_ready, - mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); - mutex_lock(&dev->device_lock); - if (!dev->recvd_hw_ready) { - dev_err(dev->dev, "wait hw ready failed\n"); - return -ETIMEDOUT; - } - - dev->recvd_hw_ready = false; - return 0; -} - -/** - * mei_virtio_hw_start() - hw start routine - * @dev: mei device - * - * Return: 0 on success, error otherwise - */ -static int mei_virtio_hw_start(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - int ret; - - dev_dbg(dev->dev, "hw start\n"); - mei_virtio_hw_reset_release(dev); - - ret = mei_virtio_hw_ready_wait(dev); - if (ret) - return ret; - - mei_virtio_add_recv_buf(hw); - atomic_set(&hw->hbuf_ready, 1); - dev_dbg(dev->dev, "hw is ready\n"); - hw->host_ready = true; - - return 0; -} - -/** - * mei_virtio_host_is_ready() - check whether the FE has turned ready - * @dev: mei device - * - * Return: bool - */ -static bool mei_virtio_host_is_ready(struct mei_device *dev) -{ - struct mei_virtio_hw *hw = to_virtio_hw(dev); - - dev_dbg(dev->dev, "host ready %d\n", hw->host_ready); - - return hw->host_ready; -} - -/** - * mei_virtio_data_in() - The callback of recv virtqueue of virtio mei - * @vq: receiving virtqueue - */ -static void mei_virtio_data_in(struct virtqueue *vq) -{ - struct mei_virtio_hw *hw = vq->vdev->priv; - - /* disable interrupts (enabled again from in the interrupt worker) */ - virtqueue_disable_cb(hw->in); - - schedule_work(&hw->intr_handler); -} - -/** - * mei_virtio_data_out() - The callback of send virtqueue of virtio mei - * @vq: transmitting virtqueue - */ -static void mei_virtio_data_out(struct virtqueue *vq) -{ - struct mei_virtio_hw *hw = vq->vdev->priv; - - schedule_work(&hw->intr_handler); -} - -static void mei_virtio_intr_handler(struct work_struct *work) -{ - struct mei_virtio_hw *hw = - container_of(work, struct mei_virtio_hw, intr_handler); - struct mei_device *dev = &hw->mdev; - LIST_HEAD(complete_list); - s32 slots; - int rets = 0; - void *data; - unsigned int len; - - mutex_lock(&dev->device_lock); - - if (dev->dev_state == MEI_DEV_DISABLED) { - dev_warn(dev->dev, "Interrupt in disabled state.\n"); - mei_virtio_intr_disable(dev); - goto end; - } - - /* check if ME wants a reset */ - if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { - dev_warn(dev->dev, "BE service not ready: resetting.\n"); - schedule_work(&dev->reset_work); - goto end; - } - - /* check if we need to start the dev */ - if (!mei_host_is_ready(dev)) { - if (mei_hw_is_ready(dev)) { - dev_dbg(dev->dev, "we need to start the dev.\n"); - dev->recvd_hw_ready = true; - wake_up(&dev->wait_hw_ready); - } else { - dev_warn(dev->dev, "Spurious Interrupt\n"); - } - goto end; - } - - /* read */ - if (hw->recv_rdy) { - data = virtqueue_get_buf(hw->in, &len); - if (!data || !len) { - dev_dbg(dev->dev, "No data %d", len); - } else { - dev_dbg(dev->dev, "data_in %d\n", len); - WARN_ON(data != hw->recv_buf); - hw->recv_len = mei_data2slots(len); - hw->recv_rdy = 0; - } - } - - /* write */ - if (!atomic_read(&hw->hbuf_ready)) { - if (!virtqueue_get_buf(hw->out, &len)) { - dev_warn(dev->dev, "Failed to getbuf\n"); - } else { - mei_virtio_free_outbufs(hw); - atomic_inc(&hw->hbuf_ready); - } - } - - /* check slots available for reading */ - slots = mei_count_full_read_slots(dev); - while (slots > 0) { - dev_dbg(dev->dev, "slots to read = %08x\n", slots); - rets = mei_irq_read_handler(dev, &complete_list, &slots); - - if (rets && - (dev->dev_state != MEI_DEV_RESETTING && - dev->dev_state != MEI_DEV_POWER_DOWN)) { - dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", - rets); - schedule_work(&dev->reset_work); - goto end; - } - } - - dev->hbuf_is_ready = mei_hbuf_is_ready(dev); - - mei_irq_write_handler(dev, &complete_list); - - dev->hbuf_is_ready = mei_hbuf_is_ready(dev); - - mei_irq_compl_handler(dev, &complete_list); - - mei_virtio_add_recv_buf(hw); - -end: - if (dev->dev_state != MEI_DEV_DISABLED) { - if (!virtqueue_enable_cb(hw->in)) - schedule_work(&hw->intr_handler); - } - - mutex_unlock(&dev->device_lock); -} - -static void mei_virtio_config_changed(struct virtio_device *vdev) -{ - struct mei_virtio_hw *hw = vdev->priv; - struct mei_device *dev = &hw->mdev; - - virtio_cread(vdev, struct mei_virtio_cfg, - hw_ready, &hw->cfg.hw_ready); - - if (dev->dev_state == MEI_DEV_DISABLED) { - dev_dbg(dev->dev, "disabled state don't start\n"); - return; - } - - /* Run intr handler once to handle reset notify */ - schedule_work(&hw->intr_handler); -} - -static void mei_virtio_remove_vqs(struct virtio_device *vdev) -{ - struct mei_virtio_hw *hw = vdev->priv; - - virtqueue_detach_unused_buf(hw->in); - hw->recv_len = 0; - hw->recv_idx = 0; - hw->recv_rdy = 0; - - virtqueue_detach_unused_buf(hw->out); - - mei_virtio_free_outbufs(hw); - - vdev->config->del_vqs(vdev); -} - -/* - * There are two virtqueues, one is for send and another is for recv. - */ -static int mei_virtio_init_vqs(struct mei_virtio_hw *hw, - struct virtio_device *vdev) -{ - struct virtqueue *vqs[2]; - - vq_callback_t *cbs[] = { - mei_virtio_data_in, - mei_virtio_data_out, - }; - static const char * const names[] = { - "in", - "out", - }; - int ret; - - ret = virtio_find_vqs(vdev, 2, vqs, cbs, names, NULL); - if (ret) - return ret; - - hw->in = vqs[0]; - hw->out = vqs[1]; - - return 0; -} - -static const struct mei_hw_ops mei_virtio_ops = { - .fw_status = mei_virtio_fw_status, - .pg_state = mei_virtio_pg_state, - - .host_is_ready = mei_virtio_host_is_ready, - - .hw_is_ready = mei_virtio_hw_is_ready, - .hw_reset = mei_virtio_hw_reset, - .hw_config = mei_virtio_hw_config, - .hw_start = mei_virtio_hw_start, - - .pg_in_transition = mei_virtio_pg_in_transition, - .pg_is_enabled = mei_virtio_pg_is_enabled, - - .intr_clear = mei_virtio_intr_clear, - .intr_enable = mei_virtio_intr_enable, - .intr_disable = mei_virtio_intr_disable, - .synchronize_irq = mei_virtio_synchronize_irq, - - .hbuf_free_slots = mei_virtio_hbuf_empty_slots, - .hbuf_is_ready = mei_virtio_hbuf_is_ready, - .hbuf_depth = mei_virtio_hbuf_depth, - - .write = mei_virtio_write_message, - - .rdbuf_full_slots = mei_virtio_count_full_read_slots, - .read_hdr = mei_virtio_read_hdr, - .read = mei_virtio_read, -}; - -static int mei_virtio_probe(struct virtio_device *vdev) -{ - struct mei_virtio_hw *hw; - int ret; - - hw = devm_kzalloc(&vdev->dev, sizeof(*hw), GFP_KERNEL); - if (!hw) - return -ENOMEM; - - vdev->priv = hw; - - INIT_WORK(&hw->intr_handler, mei_virtio_intr_handler); - - ret = mei_virtio_init_vqs(hw, vdev); - if (ret) - goto vqs_failed; - - virtio_cread(vdev, struct mei_virtio_cfg, - buf_depth, &hw->cfg.buf_depth); - - hw->recv_buf = kzalloc(mei_slots2data(hw->cfg.buf_depth), GFP_KERNEL); - if (!hw->recv_buf) { - ret = -ENOMEM; - goto hbuf_failed; - } - atomic_set(&hw->hbuf_ready, 0); - - virtio_device_ready(vdev); - - mei_device_init(&hw->mdev, &vdev->dev, &mei_virtio_ops); - - pm_runtime_get_noresume(&vdev->dev); - pm_runtime_set_active(&vdev->dev); - pm_runtime_enable(&vdev->dev); - - ret = mei_start(&hw->mdev); - if (ret) - goto mei_start_failed; - - pm_runtime_set_autosuspend_delay(&vdev->dev, MEI_VIRTIO_RPM_TIMEOUT); - pm_runtime_use_autosuspend(&vdev->dev); - - ret = mei_register(&hw->mdev, &vdev->dev); - if (ret) - goto mei_failed; - - pm_runtime_put(&vdev->dev); - - return 0; - -mei_failed: - mei_stop(&hw->mdev); -mei_start_failed: - mei_cancel_work(&hw->mdev); - mei_disable_interrupts(&hw->mdev); - kfree(hw->recv_buf); -hbuf_failed: - vdev->config->del_vqs(vdev); -vqs_failed: - return ret; -} - -static int __maybe_unused mei_virtio_pm_runtime_idle(struct device *device) -{ - struct virtio_device *vdev = dev_to_virtio(device); - struct mei_virtio_hw *hw = vdev->priv; - - dev_dbg(&vdev->dev, "rpm: mei_virtio : runtime_idle\n"); - - if (!hw) - return -ENODEV; - - if (mei_write_is_idle(&hw->mdev)) - pm_runtime_autosuspend(device); - - return -EBUSY; -} - -static int __maybe_unused mei_virtio_pm_runtime_suspend(struct device *device) -{ - return 0; -} - -static int __maybe_unused mei_virtio_pm_runtime_resume(struct device *device) -{ - return 0; -} - -static int __maybe_unused mei_virtio_freeze(struct virtio_device *vdev) -{ - struct mei_virtio_hw *hw = vdev->priv; - - dev_dbg(&vdev->dev, "freeze\n"); - - if (!hw) - return -ENODEV; - - mei_stop(&hw->mdev); - mei_disable_interrupts(&hw->mdev); - cancel_work_sync(&hw->intr_handler); - vdev->config->reset(vdev); - mei_virtio_remove_vqs(vdev); - - return 0; -} - -static int __maybe_unused mei_virtio_restore(struct virtio_device *vdev) -{ - struct mei_virtio_hw *hw = vdev->priv; - int ret; - - dev_dbg(&vdev->dev, "restore\n"); - - if (!hw) - return -ENODEV; - - ret = mei_virtio_init_vqs(hw, vdev); - if (ret) - return ret; - - virtio_device_ready(vdev); - - ret = mei_restart(&hw->mdev); - if (ret) - return ret; - - /* Start timer if stopped in suspend */ - schedule_delayed_work(&hw->mdev.timer_work, HZ); - - return 0; -} - -static const struct dev_pm_ops mei_virtio_pm_ops = { - SET_RUNTIME_PM_OPS(mei_virtio_pm_runtime_suspend, - mei_virtio_pm_runtime_resume, - mei_virtio_pm_runtime_idle) -}; - -static void mei_virtio_remove(struct virtio_device *vdev) -{ - struct mei_virtio_hw *hw = vdev->priv; - - mei_stop(&hw->mdev); - mei_disable_interrupts(&hw->mdev); - cancel_work_sync(&hw->intr_handler); - mei_deregister(&hw->mdev); - vdev->config->reset(vdev); - mei_virtio_remove_vqs(vdev); - kfree(hw->recv_buf); - pm_runtime_disable(&vdev->dev); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_MEI, VIRTIO_DEV_ANY_ID }, - { } -}; - -static struct virtio_driver mei_virtio_driver = { - .id_table = id_table, - .probe = mei_virtio_probe, - .remove = mei_virtio_remove, - .config_changed = mei_virtio_config_changed, - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - .pm = &mei_virtio_pm_ops, - }, -#ifdef CONFIG_PM_SLEEP - .freeze = mei_virtio_freeze, - .restore = mei_virtio_restore, -#endif -}; - -module_virtio_driver(mei_virtio_driver); -MODULE_DEVICE_TABLE(virtio, id_table); -MODULE_DESCRIPTION("Virtio MEI frontend driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 1ccdbe89585b..1a9e9b9a4bf6 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -1295,12 +1295,22 @@ int c_can_power_up(struct net_device *dev) time_after(time_out, jiffies)) cpu_relax(); - if (time_after(jiffies, time_out)) - return -ETIMEDOUT; + if (time_after(jiffies, time_out)) { + ret = -ETIMEDOUT; + goto err_out; + } ret = c_can_start(dev); - if (!ret) - c_can_irq_control(priv, true); + if (ret) + goto err_out; + + c_can_irq_control(priv, true); + + return 0; + +err_out: + c_can_reset_ram(priv, false); + c_can_pm_runtime_put_sync(priv); return ret; } diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 72acd1ba162d..43151dd6cb1c 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -692,8 +692,10 @@ static int kvaser_pciefd_open(struct net_device *netdev) return err; err = kvaser_pciefd_bus_on(can); - if (err) + if (err) { + close_candev(netdev); return err; + } return 0; } diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c index e5d7d85e0b6d..7347ab39c5b6 100644 --- a/drivers/net/can/m_can/tcan4x5x.c +++ b/drivers/net/can/m_can/tcan4x5x.c @@ -489,18 +489,18 @@ static int tcan4x5x_can_probe(struct spi_device *spi) spi->bits_per_word = 32; ret = spi_setup(spi); if (ret) - goto out_clk; + goto out_m_can_class_free_dev; priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus, &spi->dev, &tcan4x5x_regmap); if (IS_ERR(priv->regmap)) { ret = PTR_ERR(priv->regmap); - goto out_clk; + goto out_m_can_class_free_dev; } ret = tcan4x5x_power_enable(priv->power, 1); if (ret) - goto out_clk; + goto out_m_can_class_free_dev; ret = tcan4x5x_parse_config(mcan_class); if (ret) @@ -519,11 +519,6 @@ static int tcan4x5x_can_probe(struct spi_device *spi) out_power: tcan4x5x_power_enable(priv->power, 0); -out_clk: - if (!IS_ERR(mcan_class->cclk)) { - clk_disable_unprepare(mcan_class->cclk); - clk_disable_unprepare(mcan_class->hclk); - } out_m_can_class_free_dev: m_can_class_free_dev(mcan_class->net); dev_err(&spi->dev, "Probe failed, err=%d\n", ret); diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 9f107798f904..25a4d7d0b349 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -474,7 +474,6 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) netdev_dbg(dev, "arbitration lost interrupt\n"); alc = priv->read_reg(priv, SJA1000_ALC); priv->can.can_stats.arbitration_lost++; - stats->tx_errors++; cf->can_id |= CAN_ERR_LOSTARB; cf->data[0] = alc & 0x1f; } diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c index e2c6cf4b2228..b3f2f4fe5ee0 100644 --- a/drivers/net/can/sun4i_can.c +++ b/drivers/net/can/sun4i_can.c @@ -604,7 +604,6 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status) netdev_dbg(dev, "arbitration lost interrupt\n"); alc = readl(priv->base + SUN4I_REG_STA_ADDR); priv->can.can_stats.arbitration_lost++; - stats->tx_errors++; if (likely(skb)) { cf->can_id |= CAN_ERR_LOSTARB; cf->data[0] = (alc >> 8) & 0x1f; diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 7fb42f388d59..7b79528d6eed 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -88,6 +88,7 @@ config BNX2 config CNIC tristate "QLogic CNIC support" depends on PCI && (IPV6 || IPV6=n) + depends on MMU select BNX2 select UIO help diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index e18e9ce27f94..1cc3c51eff71 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -3175,6 +3175,7 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, GFP_KERNEL | __GFP_COMP); if (!avail) { CH_ALERT(adapter, "free list queue 0 initialization failed\n"); + ret = -ENOMEM; goto err; } if (avail < q->fl[0].size) diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c index 96d561653496..50e3a70e5a29 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c @@ -1206,6 +1206,7 @@ static struct sock *chtls_recv_sock(struct sock *lsk, sk_setup_caps(newsk, dst); ctx = tls_get_ctx(lsk); newsk->sk_destruct = ctx->sk_destruct; + newsk->sk_prot_creator = lsk->sk_prot_creator; csk->sk = newsk; csk->passive_reap_next = oreq; csk->tx_chan = cxgb4_port_chan(ndev); diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c index 62c829023da5..a4fb463af22a 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c @@ -391,6 +391,7 @@ int chtls_setkey(struct chtls_sock *csk, u32 keylen, csk->wr_unacked += DIV_ROUND_UP(len, 16); enqueue_wr(csk, skb); cxgb4_ofld_send(csk->egress_dev, skb); + skb = NULL; chtls_set_scmd(csk); /* Clear quiesce for Rx key */ diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index d9c285948fc2..cb7c028b1bf5 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -2120,6 +2120,15 @@ workaround: skb_copy_header(new_skb, skb); new_skb->dev = skb->dev; + /* Copy relevant timestamp info from the old skb to the new */ + if (priv->tx_tstamp) { + skb_shinfo(new_skb)->tx_flags = skb_shinfo(skb)->tx_flags; + skb_shinfo(new_skb)->hwtstamps = skb_shinfo(skb)->hwtstamps; + skb_shinfo(new_skb)->tskey = skb_shinfo(skb)->tskey; + if (skb->sk) + skb_set_owner_w(new_skb, skb->sk); + } + /* We move the headroom when we align it so we have to reset the * network and transport header offsets relative to the new data * pointer. The checksum offload relies on these offsets. @@ -2127,7 +2136,6 @@ workaround: skb_set_network_header(new_skb, skb_network_offset(skb)); skb_set_transport_header(new_skb, skb_transport_offset(skb)); - /* TODO: does timestamping need the result in the old skb? */ dev_kfree_skb(skb); *s = new_skb; diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 2491ebc97871..da9450f18717 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -834,7 +834,7 @@ static void release_napi(struct ibmvnic_adapter *adapter) static int ibmvnic_login(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); - unsigned long timeout = msecs_to_jiffies(30000); + unsigned long timeout = msecs_to_jiffies(20000); int retry_count = 0; int retries = 10; bool retry; @@ -850,10 +850,8 @@ static int ibmvnic_login(struct net_device *netdev) adapter->init_done_rc = 0; reinit_completion(&adapter->init_done); rc = send_login(adapter); - if (rc) { - netdev_warn(netdev, "Unable to login\n"); + if (rc) return rc; - } if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { @@ -940,7 +938,7 @@ static void release_resources(struct ibmvnic_adapter *adapter) static int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state) { struct net_device *netdev = adapter->netdev; - unsigned long timeout = msecs_to_jiffies(30000); + unsigned long timeout = msecs_to_jiffies(20000); union ibmvnic_crq crq; bool resend; int rc; @@ -1857,7 +1855,7 @@ static int do_change_param_reset(struct ibmvnic_adapter *adapter, if (reset_state == VNIC_OPEN) { rc = __ibmvnic_close(netdev); if (rc) - return rc; + goto out; } release_resources(adapter); @@ -1875,24 +1873,25 @@ static int do_change_param_reset(struct ibmvnic_adapter *adapter, } rc = ibmvnic_reset_init(adapter, true); - if (rc) - return IBMVNIC_INIT_FAILED; + if (rc) { + rc = IBMVNIC_INIT_FAILED; + goto out; + } /* If the adapter was in PROBE state prior to the reset, * exit here. */ if (reset_state == VNIC_PROBED) - return 0; + goto out; rc = ibmvnic_login(netdev); if (rc) { - adapter->state = reset_state; - return rc; + goto out; } rc = init_resources(adapter); if (rc) - return rc; + goto out; ibmvnic_disable_irqs(adapter); @@ -1902,8 +1901,10 @@ static int do_change_param_reset(struct ibmvnic_adapter *adapter, return 0; rc = __ibmvnic_open(netdev); - if (rc) - return IBMVNIC_OPEN_FAILED; + if (rc) { + rc = IBMVNIC_OPEN_FAILED; + goto out; + } /* refresh device's multicast list */ ibmvnic_set_multi(netdev); @@ -1912,7 +1913,10 @@ static int do_change_param_reset(struct ibmvnic_adapter *adapter, for (i = 0; i < adapter->req_rx_queues; i++) napi_schedule(&adapter->napi[i]); - return 0; +out: + if (rc) + adapter->state = reset_state; + return rc; } /** @@ -2015,7 +2019,6 @@ static int do_reset(struct ibmvnic_adapter *adapter, rc = ibmvnic_login(netdev); if (rc) { - adapter->state = reset_state; goto out; } @@ -2083,6 +2086,9 @@ static int do_reset(struct ibmvnic_adapter *adapter, rc = 0; out: + /* restore the adapter state if reset failed */ + if (rc) + adapter->state = reset_state; rtnl_unlock(); return rc; @@ -2115,43 +2121,46 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter, if (rc) { netdev_err(adapter->netdev, "Couldn't initialize crq. rc=%d\n", rc); - return rc; + goto out; } rc = ibmvnic_reset_init(adapter, false); if (rc) - return rc; + goto out; /* If the adapter was in PROBE state prior to the reset, * exit here. */ if (reset_state == VNIC_PROBED) - return 0; + goto out; rc = ibmvnic_login(netdev); - if (rc) { - adapter->state = VNIC_PROBED; - return 0; - } + if (rc) + goto out; rc = init_resources(adapter); if (rc) - return rc; + goto out; ibmvnic_disable_irqs(adapter); adapter->state = VNIC_CLOSED; if (reset_state == VNIC_CLOSED) - return 0; + goto out; rc = __ibmvnic_open(netdev); - if (rc) - return IBMVNIC_OPEN_FAILED; + if (rc) { + rc = IBMVNIC_OPEN_FAILED; + goto out; + } call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, netdev); call_netdevice_notifiers(NETDEV_RESEND_IGMP, netdev); - - return 0; +out: + /* restore adapter state if reset failed */ + if (rc) + adapter->state = reset_state; + return rc; } static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter) @@ -2173,17 +2182,6 @@ static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter) return rwi; } -static void free_all_rwi(struct ibmvnic_adapter *adapter) -{ - struct ibmvnic_rwi *rwi; - - rwi = get_next_rwi(adapter); - while (rwi) { - kfree(rwi); - rwi = get_next_rwi(adapter); - } -} - static void __ibmvnic_reset(struct work_struct *work) { struct ibmvnic_rwi *rwi; @@ -2241,20 +2239,23 @@ static void __ibmvnic_reset(struct work_struct *work) rc = do_hard_reset(adapter, rwi, reset_state); rtnl_unlock(); } + if (rc) { + /* give backing device time to settle down */ + netdev_dbg(adapter->netdev, + "[S:%d] Hard reset failed, waiting 60 secs\n", + adapter->state); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(60 * HZ); + } } else if (!(rwi->reset_reason == VNIC_RESET_FATAL && adapter->from_passive_init)) { rc = do_reset(adapter, rwi, reset_state); } kfree(rwi); - if (rc == IBMVNIC_OPEN_FAILED) { - if (list_empty(&adapter->rwi_list)) - adapter->state = VNIC_CLOSED; - else - adapter->state = reset_state; - rc = 0; - } else if (rc && rc != IBMVNIC_INIT_FAILED && - !adapter->force_reset_recovery) - break; + adapter->last_reset_time = jiffies; + + if (rc) + netdev_dbg(adapter->netdev, "Reset failed, rc=%d\n", rc); rwi = get_next_rwi(adapter); @@ -2268,11 +2269,6 @@ static void __ibmvnic_reset(struct work_struct *work) complete(&adapter->reset_done); } - if (rc) { - netdev_dbg(adapter->netdev, "Reset failed\n"); - free_all_rwi(adapter); - } - clear_bit_unlock(0, &adapter->resetting); } @@ -2360,7 +2356,13 @@ static void ibmvnic_tx_timeout(struct net_device *dev, unsigned int txqueue) "Adapter is resetting, skip timeout reset\n"); return; } - + /* No queuing up reset until at least 5 seconds (default watchdog val) + * after last reset + */ + if (time_before(jiffies, (adapter->last_reset_time + dev->watchdog_timeo))) { + netdev_dbg(dev, "Not yet time to tx timeout.\n"); + return; + } ibmvnic_reset(adapter, VNIC_RESET_TIMEOUT); } @@ -2402,6 +2404,12 @@ restart_poll: if (!pending_scrq(adapter, adapter->rx_scrq[scrq_num])) break; + /* The queue entry at the current index is peeked at above + * to determine that there is a valid descriptor awaiting + * processing. We want to be sure that the current slot + * holds a valid descriptor before reading its contents. + */ + dma_rmb(); next = ibmvnic_next_scrq(adapter, adapter->rx_scrq[scrq_num]); rx_buff = (struct ibmvnic_rx_buff *)be64_to_cpu(next-> @@ -2860,15 +2868,26 @@ static int reset_one_sub_crq_queue(struct ibmvnic_adapter *adapter, { int rc; + if (!scrq) { + netdev_dbg(adapter->netdev, + "Invalid scrq reset. irq (%d) or msgs (%p).\n", + scrq->irq, scrq->msgs); + return -EINVAL; + } + if (scrq->irq) { free_irq(scrq->irq, scrq); irq_dispose_mapping(scrq->irq); scrq->irq = 0; } - - memset(scrq->msgs, 0, 4 * PAGE_SIZE); - atomic_set(&scrq->used, 0); - scrq->cur = 0; + if (scrq->msgs) { + memset(scrq->msgs, 0, 4 * PAGE_SIZE); + atomic_set(&scrq->used, 0); + scrq->cur = 0; + } else { + netdev_dbg(adapter->netdev, "Invalid scrq reset\n"); + return -EINVAL; + } rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token, 4 * PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq); @@ -3100,13 +3119,18 @@ restart_loop: unsigned int pool = scrq->pool_index; int num_entries = 0; + /* The queue entry at the current index is peeked at above + * to determine that there is a valid descriptor awaiting + * processing. We want to be sure that the current slot + * holds a valid descriptor before reading its contents. + */ + dma_rmb(); + next = ibmvnic_next_scrq(adapter, scrq); for (i = 0; i < next->tx_comp.num_comps; i++) { - if (next->tx_comp.rcs[i]) { + if (next->tx_comp.rcs[i]) dev_err(dev, "tx error %x\n", next->tx_comp.rcs[i]); - continue; - } index = be32_to_cpu(next->tx_comp.correlators[i]); if (index & IBMVNIC_TSO_POOL_MASK) { tx_pool = &adapter->tso_pool[pool]; @@ -3500,6 +3524,11 @@ static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *adapter, } spin_unlock_irqrestore(&scrq->lock, flags); + /* Ensure that the entire buffer descriptor has been + * loaded before reading its contents + */ + dma_rmb(); + return entry; } @@ -3721,15 +3750,16 @@ static int send_login(struct ibmvnic_adapter *adapter) struct ibmvnic_login_rsp_buffer *login_rsp_buffer; struct ibmvnic_login_buffer *login_buffer; struct device *dev = &adapter->vdev->dev; + struct vnic_login_client_data *vlcd; dma_addr_t rsp_buffer_token; dma_addr_t buffer_token; size_t rsp_buffer_size; union ibmvnic_crq crq; + int client_data_len; size_t buffer_size; __be64 *tx_list_p; __be64 *rx_list_p; - int client_data_len; - struct vnic_login_client_data *vlcd; + int rc; int i; if (!adapter->tx_scrq || !adapter->rx_scrq) { @@ -3833,16 +3863,25 @@ static int send_login(struct ibmvnic_adapter *adapter) crq.login.cmd = LOGIN; crq.login.ioba = cpu_to_be32(buffer_token); crq.login.len = cpu_to_be32(buffer_size); - ibmvnic_send_crq(adapter, &crq); + + adapter->login_pending = true; + rc = ibmvnic_send_crq(adapter, &crq); + if (rc) { + adapter->login_pending = false; + netdev_err(adapter->netdev, "Failed to send login, rc=%d\n", rc); + goto buf_rsp_map_failed; + } return 0; buf_rsp_map_failed: kfree(login_rsp_buffer); + adapter->login_rsp_buf = NULL; buf_rsp_alloc_failed: dma_unmap_single(dev, buffer_token, buffer_size, DMA_TO_DEVICE); buf_map_failed: kfree(login_buffer); + adapter->login_buf = NULL; buf_alloc_failed: return -1; } @@ -4385,6 +4424,15 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, u64 *size_array; int i; + /* CHECK: Test/set of login_pending does not need to be atomic + * because only ibmvnic_tasklet tests/clears this. + */ + if (!adapter->login_pending) { + netdev_warn(netdev, "Ignoring unexpected login response\n"); + return 0; + } + adapter->login_pending = false; + dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz, DMA_TO_DEVICE); dma_unmap_single(dev, adapter->login_rsp_buf_token, @@ -4414,7 +4462,7 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, adapter->req_rx_add_queues != be32_to_cpu(login_rsp->num_rxadd_subcrqs))) { dev_err(dev, "FATAL: Inconsistent login and login rsp\n"); - ibmvnic_remove(adapter->vdev); + ibmvnic_reset(adapter, VNIC_RESET_FATAL); return -EIO; } size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + @@ -4756,6 +4804,11 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, case IBMVNIC_CRQ_INIT: dev_info(dev, "Partner initialized\n"); adapter->from_passive_init = true; + /* Discard any stale login responses from prev reset. + * CHECK: should we clear even on INIT_COMPLETE? + */ + adapter->login_pending = false; + if (!completion_done(&adapter->init_done)) { complete(&adapter->init_done); adapter->init_done_rc = -EIO; @@ -5093,7 +5146,7 @@ map_failed: static int ibmvnic_reset_init(struct ibmvnic_adapter *adapter, bool reset) { struct device *dev = &adapter->vdev->dev; - unsigned long timeout = msecs_to_jiffies(30000); + unsigned long timeout = msecs_to_jiffies(20000); u64 old_num_rx_queues, old_num_tx_queues; int rc; @@ -5188,6 +5241,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) dev_set_drvdata(&dev->dev, netdev); adapter->vdev = dev; adapter->netdev = netdev; + adapter->login_pending = false; ether_addr_copy(adapter->mac_addr, mac_addr_p); ether_addr_copy(netdev->dev_addr, adapter->mac_addr); @@ -5251,7 +5305,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) adapter->state = VNIC_PROBED; adapter->wait_for_reset = false; - + adapter->last_reset_time = jiffies; return 0; ibmvnic_register_fail: diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 47a3fd71c96f..21e7ea858cda 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -1086,6 +1086,9 @@ struct ibmvnic_adapter { struct delayed_work ibmvnic_delayed_reset; unsigned long resetting; bool napi_enabled, from_passive_init; + bool login_pending; + /* last device reset time */ + unsigned long last_reset_time; bool failover_pending; bool force_reset_recovery; diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index f6616c8933ca..cea886c5bcb5 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -4426,6 +4426,7 @@ static int mvpp2_open(struct net_device *dev) if (!valid) { netdev_err(port->dev, "invalid configuration: no dt or link IRQ"); + err = -ENOENT; goto err_free_irq; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c index 97f1594cee11..e51f60b55daa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c @@ -44,6 +44,7 @@ static void accel_fs_tcp_set_ipv4_flow(struct mlx5_flow_spec *spec, struct sock outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4); } +#if IS_ENABLED(CONFIG_IPV6) static void accel_fs_tcp_set_ipv6_flow(struct mlx5_flow_spec *spec, struct sock *sk) { MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_protocol); @@ -63,6 +64,7 @@ static void accel_fs_tcp_set_ipv6_flow(struct mlx5_flow_spec *spec, struct sock outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), 0xff, 16); } +#endif void mlx5e_accel_fs_del_sk(struct mlx5_flow_handle *rule) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 6dd3ea3cbbed..d97203cf6a00 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -161,7 +161,9 @@ ipsec_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb, } static inline void -mlx5e_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg) +mlx5e_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb, + struct mlx5e_accel_tx_state *accel, + struct mlx5_wqe_eth_seg *eseg) { if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { eseg->cs_flags = MLX5_ETH_WQE_L3_CSUM; @@ -173,6 +175,11 @@ mlx5e_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb, struct eseg->cs_flags |= MLX5_ETH_WQE_L4_CSUM; sq->stats->csum_partial++; } +#ifdef CONFIG_MLX5_EN_TLS + } else if (unlikely(accel && accel->tls.tls_tisn)) { + eseg->cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM; + sq->stats->csum_partial++; +#endif } else if (unlikely(eseg->flow_table_metadata & cpu_to_be32(MLX5_ETH_WQE_FT_META_IPSEC))) { ipsec_txwqe_build_eseg_csum(sq, skb, eseg); @@ -607,12 +614,13 @@ void mlx5e_tx_mpwqe_ensure_complete(struct mlx5e_txqsq *sq) } static bool mlx5e_txwqe_build_eseg(struct mlx5e_priv *priv, struct mlx5e_txqsq *sq, - struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg) + struct sk_buff *skb, struct mlx5e_accel_tx_state *accel, + struct mlx5_wqe_eth_seg *eseg) { if (unlikely(!mlx5e_accel_tx_eseg(priv, skb, eseg))) return false; - mlx5e_txwqe_build_eseg_csum(sq, skb, eseg); + mlx5e_txwqe_build_eseg_csum(sq, skb, accel, eseg); return true; } @@ -639,7 +647,7 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) if (mlx5e_tx_skb_supports_mpwqe(skb, &attr)) { struct mlx5_wqe_eth_seg eseg = {}; - if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &eseg))) + if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &accel, &eseg))) return NETDEV_TX_OK; mlx5e_sq_xmit_mpwqe(sq, skb, &eseg, netdev_xmit_more()); @@ -656,7 +664,7 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) /* May update the WQE, but may not post other WQEs. */ mlx5e_accel_tx_finish(sq, wqe, &accel, (struct mlx5_wqe_inline_seg *)(wqe->data + wqe_attr.ds_cnt_inl)); - if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &wqe->eth))) + if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &accel, &wqe->eth))) return NETDEV_TX_OK; mlx5e_sq_xmit_wqe(sq, skb, &attr, &wqe_attr, wqe, pi, netdev_xmit_more()); @@ -675,7 +683,7 @@ void mlx5e_sq_xmit_simple(struct mlx5e_txqsq *sq, struct sk_buff *skb, bool xmit mlx5e_sq_calc_wqe_attr(skb, &attr, &wqe_attr); pi = mlx5e_txqsq_get_next_pi(sq, wqe_attr.num_wqebbs); wqe = MLX5E_TX_FETCH_WQE(sq, pi); - mlx5e_txwqe_build_eseg_csum(sq, skb, &wqe->eth); + mlx5e_txwqe_build_eseg_csum(sq, skb, NULL, &wqe->eth); mlx5e_sq_xmit_wqe(sq, skb, &attr, &wqe_attr, wqe, pi, xmit_more); } @@ -944,7 +952,7 @@ void mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, mlx5i_txwqe_build_datagram(av, dqpn, dqkey, datagram); - mlx5e_txwqe_build_eseg_csum(sq, skb, eseg); + mlx5e_txwqe_build_eseg_csum(sq, skb, NULL, eseg); eseg->mss = attr.mss; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 150638814517..4d7f8a357df7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -422,6 +422,24 @@ static void release_all_pages(struct mlx5_core_dev *dev, u32 func_id, npages, ec_function, func_id); } +static u32 fwp_fill_manage_pages_out(struct fw_page *fwp, u32 *out, u32 index, + u32 npages) +{ + u32 pages_set = 0; + unsigned int n; + + for_each_clear_bit(n, &fwp->bitmask, MLX5_NUM_4K_IN_PAGE) { + MLX5_ARRAY_SET64(manage_pages_out, out, pas, index + pages_set, + fwp->addr + (n * MLX5_ADAPTER_PAGE_SIZE)); + pages_set++; + + if (!--npages) + break; + } + + return pages_set; +} + static int reclaim_pages_cmd(struct mlx5_core_dev *dev, u32 *in, int in_size, u32 *out, int out_size) { @@ -448,8 +466,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, fwp = rb_entry(p, struct fw_page, rb_node); p = rb_next(p); - MLX5_ARRAY_SET64(manage_pages_out, out, pas, i, fwp->addr); - i++; + i += fwp_fill_manage_pages_out(fwp, out, i, npages - i); } MLX5_SET(manage_pages_out, out, output_num_entries, i); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c index 6bd34b293007..51bbd88ff021 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c @@ -92,6 +92,7 @@ int mlx5dr_cmd_query_device(struct mlx5_core_dev *mdev, caps->eswitch_manager = MLX5_CAP_GEN(mdev, eswitch_manager); caps->gvmi = MLX5_CAP_GEN(mdev, vhca_id); caps->flex_protocols = MLX5_CAP_GEN(mdev, flex_parser_protocols); + caps->sw_format_ver = MLX5_CAP_GEN(mdev, steering_format_version); if (mlx5dr_matcher_supp_flex_parser_icmp_v4(caps)) { caps->flex_parser_id_icmp_dw0 = MLX5_CAP_GEN(mdev, flex_parser_id_icmp_dw0); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 890767a2a7cb..aa2c2d6c44e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -223,6 +223,11 @@ static int dr_domain_caps_init(struct mlx5_core_dev *mdev, if (ret) return ret; + if (dmn->info.caps.sw_format_ver != MLX5_STEERING_FORMAT_CONNECTX_5) { + mlx5dr_err(dmn, "SW steering is not supported on this device\n"); + return -EOPNOTSUPP; + } + ret = dr_domain_query_fdb_caps(mdev, dmn); if (ret) return ret; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index f50f3b107aa3..cf62ea4f882e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -625,6 +625,7 @@ struct mlx5dr_cmd_caps { u8 max_ft_level; u16 roce_min_src_udp; u8 num_esw_ports; + u8 sw_format_ver; bool eswitch_manager; bool rx_sw_owner; bool tx_sw_owner; diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index be6660128b55..040a15a828b4 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1078,16 +1078,20 @@ static int pasemi_mac_open(struct net_device *dev) mac->tx = pasemi_mac_setup_tx_resources(dev); - if (!mac->tx) + if (!mac->tx) { + ret = -ENOMEM; goto out_tx_ring; + } /* We might already have allocated rings in case mtu was changed * before interface was brought up. */ if (dev->mtu > 1500 && !mac->num_cs) { pasemi_mac_setup_csrings(mac); - if (!mac->num_cs) + if (!mac->num_cs) { + ret = -ENOMEM; goto out_tx_ring; + } } /* Zero out rmon counters */ diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 1426bfc009bc..8ae9ce2014a4 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -257,11 +257,21 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, skb_dst_set(skb, &tun_dst->dst); /* Ignore packet loops (and multicast echo) */ - if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr)) { - geneve->dev->stats.rx_errors++; - goto drop; - } + if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr)) + goto rx_error; + switch (skb_protocol(skb, true)) { + case htons(ETH_P_IP): + if (pskb_may_pull(skb, sizeof(struct iphdr))) + goto rx_error; + break; + case htons(ETH_P_IPV6): + if (pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto rx_error; + break; + default: + goto rx_error; + } oiph = skb_network_header(skb); skb_reset_network_header(skb); @@ -298,6 +308,8 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, dev_sw_netstats_rx_add(geneve->dev, len); return; +rx_error: + geneve->dev->stats.rx_errors++; drop: /* Consume bad packet */ kfree_skb(skb); diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index 3160443ef3b9..d7b5f87eaa15 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -866,7 +866,7 @@ static int tbnet_open(struct net_device *dev) eof_mask = BIT(TBIP_PDF_FRAME_END); ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, - RING_FLAG_FRAME, sof_mask, eof_mask, + RING_FLAG_FRAME, 0, sof_mask, eof_mask, tbnet_start_poll, net); if (!ring) { netdev_err(dev, "failed to allocate Rx ring\n"); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 1a557aeba32b..977f77e2c2ce 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -3798,6 +3798,9 @@ static void vxlan_config_apply(struct net_device *dev, dev->gso_max_segs = lowerdev->gso_max_segs; needed_headroom = lowerdev->hard_header_len; + needed_headroom += lowerdev->needed_headroom; + + dev->needed_tailroom = lowerdev->needed_tailroom; max_mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); @@ -3877,8 +3880,10 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev, if (dst->remote_ifindex) { remote_dev = __dev_get_by_index(net, dst->remote_ifindex); - if (!remote_dev) + if (!remote_dev) { + err = -ENODEV; goto errout; + } err = netdev_upper_dev_link(remote_dev, dev, extack); if (err) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index ca4967b81d01..580b07a43856 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -491,8 +491,8 @@ struct iwl_cfg { #define IWL_CFG_RF_ID_HR 0x7 #define IWL_CFG_RF_ID_HR1 0x4 -#define IWL_CFG_NO_160 0x0 -#define IWL_CFG_160 0x1 +#define IWL_CFG_NO_160 0x1 +#define IWL_CFG_160 0x0 #define IWL_CFG_CORES_BT 0x0 #define IWL_CFG_CORES_BT_GNSS 0x5 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 129021f26791..7b5ece380fbf 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -536,9 +536,15 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x2725, 0x0090, iwlax211_2ax_cfg_so_gf_a0)}, {IWL_PCI_DEVICE(0x2725, 0x0020, iwlax210_2ax_cfg_ty_gf_a0)}, + {IWL_PCI_DEVICE(0x2725, 0x0024, iwlax210_2ax_cfg_ty_gf_a0)}, {IWL_PCI_DEVICE(0x2725, 0x0310, iwlax210_2ax_cfg_ty_gf_a0)}, {IWL_PCI_DEVICE(0x2725, 0x0510, iwlax210_2ax_cfg_ty_gf_a0)}, {IWL_PCI_DEVICE(0x2725, 0x0A10, iwlax210_2ax_cfg_ty_gf_a0)}, + {IWL_PCI_DEVICE(0x2725, 0xE020, iwlax210_2ax_cfg_ty_gf_a0)}, + {IWL_PCI_DEVICE(0x2725, 0xE024, iwlax210_2ax_cfg_ty_gf_a0)}, + {IWL_PCI_DEVICE(0x2725, 0x4020, iwlax210_2ax_cfg_ty_gf_a0)}, + {IWL_PCI_DEVICE(0x2725, 0x6020, iwlax210_2ax_cfg_ty_gf_a0)}, + {IWL_PCI_DEVICE(0x2725, 0x6024, iwlax210_2ax_cfg_ty_gf_a0)}, {IWL_PCI_DEVICE(0x2725, 0x00B0, iwlax411_2ax_cfg_sosnj_gf4_a0)}, {IWL_PCI_DEVICE(0x2726, 0x0070, iwlax201_cfg_snj_hr_b0)}, {IWL_PCI_DEVICE(0x2726, 0x0074, iwlax201_cfg_snj_hr_b0)}, diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 7d3f0a2e5fa0..f1ae9ff835b2 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -1020,8 +1020,6 @@ void mt76u_stop_tx(struct mt76_dev *dev) { int ret; - mt76_worker_disable(&dev->tx_worker); - ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy), HZ / 5); if (!ret) { @@ -1040,6 +1038,8 @@ void mt76u_stop_tx(struct mt76_dev *dev) usb_kill_urb(q->entry[j].urb); } + mt76_worker_disable(&dev->tx_worker); + /* On device removal we maight queue skb's, but mt76u_tx_kick() * will fail to submit urb, cleanup those skb's manually. */ @@ -1048,18 +1048,19 @@ void mt76u_stop_tx(struct mt76_dev *dev) if (!q) continue; - entry = q->entry[q->tail]; - q->entry[q->tail].done = false; - - mt76_queue_tx_complete(dev, q, &entry); + while (q->queued > 0) { + entry = q->entry[q->tail]; + q->entry[q->tail].done = false; + mt76_queue_tx_complete(dev, q, &entry); + } } + + mt76_worker_enable(&dev->tx_worker); } cancel_work_sync(&dev->usb.stat_work); clear_bit(MT76_READING_STATS, &dev->phy.state); - mt76_worker_enable(&dev->tx_worker); - mt76_tx_status_check(dev, NULL, true); } EXPORT_SYMBOL_GPL(mt76u_stop_tx); diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index 3852c4f0ac0b..efbba9caef3b 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -147,6 +147,8 @@ static int rtw_debugfs_copy_from_user(char tmp[], int size, { int tmp_len; + memset(tmp, 0, size); + if (count < num) return -EFAULT; diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 31be31161350..8111ed1fc574 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -438,8 +438,7 @@ static int cros_typec_enable_tbt(struct cros_typec_data *typec, if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_LINK_UNIDIR) data.cable_mode |= TBT_CABLE_LINK_TRAINING; - if (pd_ctrl->cable_gen) - data.cable_mode |= TBT_CABLE_ROUNDED; + data.cable_mode |= TBT_SET_CABLE_ROUNDED(pd_ctrl->cable_gen); /* Enter Mode VDO */ data.enter_vdo = TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed); diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c index 5046b6b7fd35..b4c651fc749c 100644 --- a/drivers/pwm/pwm-sl28cpld.c +++ b/drivers/pwm/pwm-sl28cpld.c @@ -84,12 +84,14 @@ struct sl28cpld_pwm { struct regmap *regmap; u32 offset; }; +#define sl28cpld_pwm_from_chip(_chip) \ + container_of(_chip, struct sl28cpld_pwm, pwm_chip) static void sl28cpld_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { - struct sl28cpld_pwm *priv = dev_get_drvdata(chip->dev); + struct sl28cpld_pwm *priv = sl28cpld_pwm_from_chip(chip); unsigned int reg; int prescaler; @@ -118,7 +120,7 @@ static void sl28cpld_pwm_get_state(struct pwm_chip *chip, static int sl28cpld_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct sl28cpld_pwm *priv = dev_get_drvdata(chip->dev); + struct sl28cpld_pwm *priv = sl28cpld_pwm_from_chip(chip); unsigned int cycle, prescaler; bool write_duty_cycle_first; int ret; diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index e4cc92bc4d94..bb940cbcbb5d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -6459,7 +6459,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) r = _base_handshake_req_reply_wait(ioc, sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, - sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10); + sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 30); if (r != 0) { ioc_err(ioc, "%s: handshake failed (r=%d)\n", __func__, r); diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 0f2b681449e6..edd26a2570fa 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -664,7 +664,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL; struct _pcie_device *pcie_device = NULL; u16 smid; - u8 timeout; + unsigned long timeout; u8 issue_reset; u32 sz, sz_arg; void *psge; diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 0c65fbd41035..99c8ff81de74 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1246,6 +1246,11 @@ static void storvsc_on_channel_callback(void *context) request = (struct storvsc_cmd_request *) ((unsigned long)desc->trans_id); + if (hv_pkt_datalen(desc) < sizeof(struct vstor_packet) - vmscsi_size_delta) { + dev_err(&device->device, "Invalid packet len\n"); + continue; + } + if (request == &stor_device->init_request || request == &stor_device->reset_request) { memcpy(&request->vstor_packet, packet, @@ -1994,8 +1999,10 @@ static int storvsc_probe(struct hv_device *device, alloc_ordered_workqueue("storvsc_error_wq_%d", WQ_MEM_RECLAIM, host->host_no); - if (!host_dev->handle_error_wq) + if (!host_dev->handle_error_wq) { + ret = -ENOMEM; goto err_out2; + } INIT_WORK(&host_dev->host_scan_work, storvsc_host_scan); /* Register the HBA and start the scsi bus scan */ ret = scsi_add_host(host, &device->device); diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig index 7fc058f81d00..4bfec8a28064 100644 --- a/drivers/thunderbolt/Kconfig +++ b/drivers/thunderbolt/Kconfig @@ -31,4 +31,17 @@ config USB4_KUNIT_TEST bool "KUnit tests" depends on KUNIT=y +config USB4_DMA_TEST + tristate "DMA traffic test driver" + depends on DEBUG_FS + help + This allows sending and receiving DMA traffic through loopback + connection. Loopback connection can be done by either special + dongle that has TX/RX lines crossed, or by simply connecting a + cable back to the host. Only enable this if you know what you + are doing. Normal users and distro kernels should say N here. + + To compile this driver a module, choose M here. The module will be + called thunderbolt_dma_test. + endif # USB4 diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 571537371072..7aa48f6c41d9 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -7,3 +7,6 @@ thunderbolt-objs += nvm.o retimer.o quirks.o thunderbolt-${CONFIG_ACPI} += acpi.o thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o thunderbolt-${CONFIG_USB4_KUNIT_TEST} += test.o + +thunderbolt_dma_test-${CONFIG_USB4_DMA_TEST} += dma_test.o +obj-$(CONFIG_USB4_DMA_TEST) += thunderbolt_dma_test.o diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 9894b8f63064..bac08b820015 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -628,8 +628,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) if (!ctl->tx) goto err; - ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, - 0xffff, NULL, NULL); + ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff, + 0xffff, NULL, NULL); if (!ctl->rx) goto err; @@ -962,6 +962,9 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space, if (res->tb_error == TB_CFG_ERROR_LOCK) return -EACCES; + else if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED) + return -ENOTCONN; + return -EIO; } diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index ed65d2b13964..9541d7409ab1 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -691,6 +691,30 @@ void tb_switch_debugfs_remove(struct tb_switch *sw) debugfs_remove_recursive(sw->debugfs_dir); } +/** + * tb_service_debugfs_init() - Add debugfs directory for service + * @svc: Thunderbolt service pointer + * + * Adds debugfs directory for service. + */ +void tb_service_debugfs_init(struct tb_service *svc) +{ + svc->debugfs_dir = debugfs_create_dir(dev_name(&svc->dev), + tb_debugfs_root); +} + +/** + * tb_service_debugfs_remove() - Remove service debugfs directory + * @svc: Thunderbolt service pointer + * + * Removes the previously created debugfs directory for @svc. + */ +void tb_service_debugfs_remove(struct tb_service *svc) +{ + debugfs_remove_recursive(svc->debugfs_dir); + svc->debugfs_dir = NULL; +} + void tb_debugfs_init(void) { tb_debugfs_root = debugfs_create_dir("thunderbolt", NULL); diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c new file mode 100644 index 000000000000..f924423fa180 --- /dev/null +++ b/drivers/thunderbolt/dma_test.c @@ -0,0 +1,736 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMA traffic test driver + * + * Copyright (C) 2020, Intel Corporation + * Authors: Isaac Hazan <isaac.hazan@intel.com> + * Mika Westerberg <mika.westerberg@linux.intel.com> + */ + +#include <linux/acpi.h> +#include <linux/completion.h> +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/sizes.h> +#include <linux/thunderbolt.h> + +#define DMA_TEST_HOPID 8 +#define DMA_TEST_TX_RING_SIZE 64 +#define DMA_TEST_RX_RING_SIZE 256 +#define DMA_TEST_FRAME_SIZE SZ_4K +#define DMA_TEST_DATA_PATTERN 0x0123456789abcdefLL +#define DMA_TEST_MAX_PACKETS 1000 + +enum dma_test_frame_pdf { + DMA_TEST_PDF_FRAME_START = 1, + DMA_TEST_PDF_FRAME_END, +}; + +struct dma_test_frame { + struct dma_test *dma_test; + void *data; + struct ring_frame frame; +}; + +enum dma_test_test_error { + DMA_TEST_NO_ERROR, + DMA_TEST_INTERRUPTED, + DMA_TEST_BUFFER_ERROR, + DMA_TEST_DMA_ERROR, + DMA_TEST_CONFIG_ERROR, + DMA_TEST_SPEED_ERROR, + DMA_TEST_WIDTH_ERROR, + DMA_TEST_BONDING_ERROR, + DMA_TEST_PACKET_ERROR, +}; + +static const char * const dma_test_error_names[] = { + [DMA_TEST_NO_ERROR] = "no errors", + [DMA_TEST_INTERRUPTED] = "interrupted by signal", + [DMA_TEST_BUFFER_ERROR] = "no memory for packet buffers", + [DMA_TEST_DMA_ERROR] = "DMA ring setup failed", + [DMA_TEST_CONFIG_ERROR] = "configuration is not valid", + [DMA_TEST_SPEED_ERROR] = "unexpected link speed", + [DMA_TEST_WIDTH_ERROR] = "unexpected link width", + [DMA_TEST_BONDING_ERROR] = "lane bonding configuration error", + [DMA_TEST_PACKET_ERROR] = "packet check failed", +}; + +enum dma_test_result { + DMA_TEST_NOT_RUN, + DMA_TEST_SUCCESS, + DMA_TEST_FAIL, +}; + +static const char * const dma_test_result_names[] = { + [DMA_TEST_NOT_RUN] = "not run", + [DMA_TEST_SUCCESS] = "success", + [DMA_TEST_FAIL] = "failed", +}; + +/** + * struct dma_test - DMA test device driver private data + * @svc: XDomain service the driver is bound to + * @xd: XDomain the service belongs to + * @rx_ring: Software ring holding RX frames + * @tx_ring: Software ring holding TX frames + * @packets_to_send: Number of packets to send + * @packets_to_receive: Number of packets to receive + * @packets_sent: Actual number of packets sent + * @packets_received: Actual number of packets received + * @link_speed: Expected link speed (Gb/s), %0 to use whatever is negotiated + * @link_width: Expected link width (Gb/s), %0 to use whatever is negotiated + * @crc_errors: Number of CRC errors during the test run + * @buffer_overflow_errors: Number of buffer overflow errors during the test + * run + * @result: Result of the last run + * @error_code: Error code of the last run + * @complete: Used to wait for the Rx to complete + * @lock: Lock serializing access to this structure + * @debugfs_dir: dentry of this dma_test + */ +struct dma_test { + const struct tb_service *svc; + struct tb_xdomain *xd; + struct tb_ring *rx_ring; + struct tb_ring *tx_ring; + unsigned int packets_to_send; + unsigned int packets_to_receive; + unsigned int packets_sent; + unsigned int packets_received; + unsigned int link_speed; + unsigned int link_width; + unsigned int crc_errors; + unsigned int buffer_overflow_errors; + enum dma_test_result result; + enum dma_test_test_error error_code; + struct completion complete; + struct mutex lock; + struct dentry *debugfs_dir; +}; + +/* DMA test property directory UUID: 3188cd10-6523-4a5a-a682-fdca07a248d8 */ +static const uuid_t dma_test_dir_uuid = + UUID_INIT(0x3188cd10, 0x6523, 0x4a5a, + 0xa6, 0x82, 0xfd, 0xca, 0x07, 0xa2, 0x48, 0xd8); + +static struct tb_property_dir *dma_test_dir; +static void *dma_test_pattern; + +static void dma_test_free_rings(struct dma_test *dt) +{ + if (dt->rx_ring) { + tb_ring_free(dt->rx_ring); + dt->rx_ring = NULL; + } + if (dt->tx_ring) { + tb_ring_free(dt->tx_ring); + dt->tx_ring = NULL; + } +} + +static int dma_test_start_rings(struct dma_test *dt) +{ + unsigned int flags = RING_FLAG_FRAME; + struct tb_xdomain *xd = dt->xd; + int ret, e2e_tx_hop = 0; + struct tb_ring *ring; + + /* + * If we are both sender and receiver (traffic goes over a + * special loopback dongle) enable E2E flow control. This avoids + * losing packets. + */ + if (dt->packets_to_send && dt->packets_to_receive) + flags |= RING_FLAG_E2E; + + if (dt->packets_to_send) { + ring = tb_ring_alloc_tx(xd->tb->nhi, -1, DMA_TEST_TX_RING_SIZE, + flags); + if (!ring) + return -ENOMEM; + + dt->tx_ring = ring; + e2e_tx_hop = ring->hop; + } + + if (dt->packets_to_receive) { + u16 sof_mask, eof_mask; + + sof_mask = BIT(DMA_TEST_PDF_FRAME_START); + eof_mask = BIT(DMA_TEST_PDF_FRAME_END); + + ring = tb_ring_alloc_rx(xd->tb->nhi, -1, DMA_TEST_RX_RING_SIZE, + flags, e2e_tx_hop, sof_mask, eof_mask, + NULL, NULL); + if (!ring) { + dma_test_free_rings(dt); + return -ENOMEM; + } + + dt->rx_ring = ring; + } + + ret = tb_xdomain_enable_paths(dt->xd, DMA_TEST_HOPID, + dt->tx_ring ? dt->tx_ring->hop : 0, + DMA_TEST_HOPID, + dt->rx_ring ? dt->rx_ring->hop : 0); + if (ret) { + dma_test_free_rings(dt); + return ret; + } + + if (dt->tx_ring) + tb_ring_start(dt->tx_ring); + if (dt->rx_ring) + tb_ring_start(dt->rx_ring); + + return 0; +} + +static void dma_test_stop_rings(struct dma_test *dt) +{ + if (dt->rx_ring) + tb_ring_stop(dt->rx_ring); + if (dt->tx_ring) + tb_ring_stop(dt->tx_ring); + + if (tb_xdomain_disable_paths(dt->xd)) + dev_warn(&dt->svc->dev, "failed to disable DMA paths\n"); + + dma_test_free_rings(dt); +} + +static void dma_test_rx_callback(struct tb_ring *ring, struct ring_frame *frame, + bool canceled) +{ + struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame); + struct dma_test *dt = tf->dma_test; + struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); + + dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE, + DMA_FROM_DEVICE); + kfree(tf->data); + + if (canceled) { + kfree(tf); + return; + } + + dt->packets_received++; + dev_dbg(&dt->svc->dev, "packet %u/%u received\n", dt->packets_received, + dt->packets_to_receive); + + if (tf->frame.flags & RING_DESC_CRC_ERROR) + dt->crc_errors++; + if (tf->frame.flags & RING_DESC_BUFFER_OVERRUN) + dt->buffer_overflow_errors++; + + kfree(tf); + + if (dt->packets_received == dt->packets_to_receive) + complete(&dt->complete); +} + +static int dma_test_submit_rx(struct dma_test *dt, size_t npackets) +{ + struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); + int i; + + for (i = 0; i < npackets; i++) { + struct dma_test_frame *tf; + dma_addr_t dma_addr; + + tf = kzalloc(sizeof(*tf), GFP_KERNEL); + if (!tf) + return -ENOMEM; + + tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); + if (!tf->data) { + kfree(tf); + return -ENOMEM; + } + + dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dev, dma_addr)) { + kfree(tf->data); + kfree(tf); + return -ENOMEM; + } + + tf->frame.buffer_phy = dma_addr; + tf->frame.callback = dma_test_rx_callback; + tf->dma_test = dt; + INIT_LIST_HEAD(&tf->frame.list); + + tb_ring_rx(dt->rx_ring, &tf->frame); + } + + return 0; +} + +static void dma_test_tx_callback(struct tb_ring *ring, struct ring_frame *frame, + bool canceled) +{ + struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame); + struct dma_test *dt = tf->dma_test; + struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); + + dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE, + DMA_TO_DEVICE); + kfree(tf->data); + kfree(tf); +} + +static int dma_test_submit_tx(struct dma_test *dt, size_t npackets) +{ + struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); + int i; + + for (i = 0; i < npackets; i++) { + struct dma_test_frame *tf; + dma_addr_t dma_addr; + + tf = kzalloc(sizeof(*tf), GFP_KERNEL); + if (!tf) + return -ENOMEM; + + tf->frame.size = 0; /* means 4096 */ + tf->dma_test = dt; + + tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); + if (!tf->data) { + kfree(tf); + return -ENOMEM; + } + + memcpy(tf->data, dma_test_pattern, DMA_TEST_FRAME_SIZE); + + dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dma_dev, dma_addr)) { + kfree(tf->data); + kfree(tf); + return -ENOMEM; + } + + tf->frame.buffer_phy = dma_addr; + tf->frame.callback = dma_test_tx_callback; + tf->frame.sof = DMA_TEST_PDF_FRAME_START; + tf->frame.eof = DMA_TEST_PDF_FRAME_END; + INIT_LIST_HEAD(&tf->frame.list); + + dt->packets_sent++; + dev_dbg(&dt->svc->dev, "packet %u/%u sent\n", dt->packets_sent, + dt->packets_to_send); + + tb_ring_tx(dt->tx_ring, &tf->frame); + } + + return 0; +} + +#define DMA_TEST_DEBUGFS_ATTR(__fops, __get, __validate, __set) \ +static int __fops ## _show(void *data, u64 *val) \ +{ \ + struct tb_service *svc = data; \ + struct dma_test *dt = tb_service_get_drvdata(svc); \ + int ret; \ + \ + ret = mutex_lock_interruptible(&dt->lock); \ + if (ret) \ + return ret; \ + __get(dt, val); \ + mutex_unlock(&dt->lock); \ + return 0; \ +} \ +static int __fops ## _store(void *data, u64 val) \ +{ \ + struct tb_service *svc = data; \ + struct dma_test *dt = tb_service_get_drvdata(svc); \ + int ret; \ + \ + ret = __validate(val); \ + if (ret) \ + return ret; \ + ret = mutex_lock_interruptible(&dt->lock); \ + if (ret) \ + return ret; \ + __set(dt, val); \ + mutex_unlock(&dt->lock); \ + return 0; \ +} \ +DEFINE_DEBUGFS_ATTRIBUTE(__fops ## _fops, __fops ## _show, \ + __fops ## _store, "%llu\n") + +static void lanes_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->link_width; +} + +static int lanes_validate(u64 val) +{ + return val > 2 ? -EINVAL : 0; +} + +static void lanes_set(struct dma_test *dt, u64 val) +{ + dt->link_width = val; +} +DMA_TEST_DEBUGFS_ATTR(lanes, lanes_get, lanes_validate, lanes_set); + +static void speed_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->link_speed; +} + +static int speed_validate(u64 val) +{ + switch (val) { + case 20: + case 10: + case 0: + return 0; + default: + return -EINVAL; + } +} + +static void speed_set(struct dma_test *dt, u64 val) +{ + dt->link_speed = val; +} +DMA_TEST_DEBUGFS_ATTR(speed, speed_get, speed_validate, speed_set); + +static void packets_to_receive_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->packets_to_receive; +} + +static int packets_to_receive_validate(u64 val) +{ + return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0; +} + +static void packets_to_receive_set(struct dma_test *dt, u64 val) +{ + dt->packets_to_receive = val; +} +DMA_TEST_DEBUGFS_ATTR(packets_to_receive, packets_to_receive_get, + packets_to_receive_validate, packets_to_receive_set); + +static void packets_to_send_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->packets_to_send; +} + +static int packets_to_send_validate(u64 val) +{ + return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0; +} + +static void packets_to_send_set(struct dma_test *dt, u64 val) +{ + dt->packets_to_send = val; +} +DMA_TEST_DEBUGFS_ATTR(packets_to_send, packets_to_send_get, + packets_to_send_validate, packets_to_send_set); + +static int dma_test_set_bonding(struct dma_test *dt) +{ + switch (dt->link_width) { + case 2: + return tb_xdomain_lane_bonding_enable(dt->xd); + case 1: + tb_xdomain_lane_bonding_disable(dt->xd); + fallthrough; + default: + return 0; + } +} + +static bool dma_test_validate_config(struct dma_test *dt) +{ + if (!dt->packets_to_send && !dt->packets_to_receive) + return false; + if (dt->packets_to_send && dt->packets_to_receive && + dt->packets_to_send != dt->packets_to_receive) + return false; + return true; +} + +static void dma_test_check_errors(struct dma_test *dt, int ret) +{ + if (!dt->error_code) { + if (dt->link_speed && dt->xd->link_speed != dt->link_speed) { + dt->error_code = DMA_TEST_SPEED_ERROR; + } else if (dt->link_width && + dt->xd->link_width != dt->link_width) { + dt->error_code = DMA_TEST_WIDTH_ERROR; + } else if (dt->packets_to_send != dt->packets_sent || + dt->packets_to_receive != dt->packets_received || + dt->crc_errors || dt->buffer_overflow_errors) { + dt->error_code = DMA_TEST_PACKET_ERROR; + } else { + return; + } + } + + dt->result = DMA_TEST_FAIL; +} + +static int test_store(void *data, u64 val) +{ + struct tb_service *svc = data; + struct dma_test *dt = tb_service_get_drvdata(svc); + int ret; + + if (val != 1) + return -EINVAL; + + ret = mutex_lock_interruptible(&dt->lock); + if (ret) + return ret; + + dt->packets_sent = 0; + dt->packets_received = 0; + dt->crc_errors = 0; + dt->buffer_overflow_errors = 0; + dt->result = DMA_TEST_SUCCESS; + dt->error_code = DMA_TEST_NO_ERROR; + + dev_dbg(&svc->dev, "DMA test starting\n"); + if (dt->link_speed) + dev_dbg(&svc->dev, "link_speed: %u Gb/s\n", dt->link_speed); + if (dt->link_width) + dev_dbg(&svc->dev, "link_width: %u\n", dt->link_width); + dev_dbg(&svc->dev, "packets_to_send: %u\n", dt->packets_to_send); + dev_dbg(&svc->dev, "packets_to_receive: %u\n", dt->packets_to_receive); + + if (!dma_test_validate_config(dt)) { + dev_err(&svc->dev, "invalid test configuration\n"); + dt->error_code = DMA_TEST_CONFIG_ERROR; + goto out_unlock; + } + + ret = dma_test_set_bonding(dt); + if (ret) { + dev_err(&svc->dev, "failed to set lanes\n"); + dt->error_code = DMA_TEST_BONDING_ERROR; + goto out_unlock; + } + + ret = dma_test_start_rings(dt); + if (ret) { + dev_err(&svc->dev, "failed to enable DMA rings\n"); + dt->error_code = DMA_TEST_DMA_ERROR; + goto out_unlock; + } + + if (dt->packets_to_receive) { + reinit_completion(&dt->complete); + ret = dma_test_submit_rx(dt, dt->packets_to_receive); + if (ret) { + dev_err(&svc->dev, "failed to submit receive buffers\n"); + dt->error_code = DMA_TEST_BUFFER_ERROR; + goto out_stop; + } + } + + if (dt->packets_to_send) { + ret = dma_test_submit_tx(dt, dt->packets_to_send); + if (ret) { + dev_err(&svc->dev, "failed to submit transmit buffers\n"); + dt->error_code = DMA_TEST_BUFFER_ERROR; + goto out_stop; + } + } + + if (dt->packets_to_receive) { + ret = wait_for_completion_interruptible(&dt->complete); + if (ret) { + dt->error_code = DMA_TEST_INTERRUPTED; + goto out_stop; + } + } + +out_stop: + dma_test_stop_rings(dt); +out_unlock: + dma_test_check_errors(dt, ret); + mutex_unlock(&dt->lock); + + dev_dbg(&svc->dev, "DMA test %s\n", dma_test_result_names[dt->result]); + return ret; +} +DEFINE_DEBUGFS_ATTRIBUTE(test_fops, NULL, test_store, "%llu\n"); + +static int status_show(struct seq_file *s, void *not_used) +{ + struct tb_service *svc = s->private; + struct dma_test *dt = tb_service_get_drvdata(svc); + int ret; + + ret = mutex_lock_interruptible(&dt->lock); + if (ret) + return ret; + + seq_printf(s, "result: %s\n", dma_test_result_names[dt->result]); + if (dt->result == DMA_TEST_NOT_RUN) + goto out_unlock; + + seq_printf(s, "packets received: %u\n", dt->packets_received); + seq_printf(s, "packets sent: %u\n", dt->packets_sent); + seq_printf(s, "CRC errors: %u\n", dt->crc_errors); + seq_printf(s, "buffer overflow errors: %u\n", + dt->buffer_overflow_errors); + seq_printf(s, "error: %s\n", dma_test_error_names[dt->error_code]); + +out_unlock: + mutex_unlock(&dt->lock); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(status); + +static void dma_test_debugfs_init(struct tb_service *svc) +{ + struct dma_test *dt = tb_service_get_drvdata(svc); + + dt->debugfs_dir = debugfs_create_dir("dma_test", svc->debugfs_dir); + + debugfs_create_file("lanes", 0600, dt->debugfs_dir, svc, &lanes_fops); + debugfs_create_file("speed", 0600, dt->debugfs_dir, svc, &speed_fops); + debugfs_create_file("packets_to_receive", 0600, dt->debugfs_dir, svc, + &packets_to_receive_fops); + debugfs_create_file("packets_to_send", 0600, dt->debugfs_dir, svc, + &packets_to_send_fops); + debugfs_create_file("status", 0400, dt->debugfs_dir, svc, &status_fops); + debugfs_create_file("test", 0200, dt->debugfs_dir, svc, &test_fops); +} + +static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id) +{ + struct tb_xdomain *xd = tb_service_parent(svc); + struct dma_test *dt; + + dt = devm_kzalloc(&svc->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + + dt->svc = svc; + dt->xd = xd; + mutex_init(&dt->lock); + init_completion(&dt->complete); + + tb_service_set_drvdata(svc, dt); + dma_test_debugfs_init(svc); + + return 0; +} + +static void dma_test_remove(struct tb_service *svc) +{ + struct dma_test *dt = tb_service_get_drvdata(svc); + + mutex_lock(&dt->lock); + debugfs_remove_recursive(dt->debugfs_dir); + mutex_unlock(&dt->lock); +} + +static int __maybe_unused dma_test_suspend(struct device *dev) +{ + /* + * No need to do anything special here. If userspace is writing + * to the test attribute when suspend started, it comes out from + * wait_for_completion_interruptible() with -ERESTARTSYS and the + * DMA test fails tearing down the rings. Once userspace is + * thawed the kernel restarts the write syscall effectively + * re-running the test. + */ + return 0; +} + +static int __maybe_unused dma_test_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops dma_test_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dma_test_suspend, dma_test_resume) +}; + +static const struct tb_service_id dma_test_ids[] = { + { TB_SERVICE("dma_test", 1) }, + { }, +}; +MODULE_DEVICE_TABLE(tbsvc, dma_test_ids); + +static struct tb_service_driver dma_test_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "thunderbolt_dma_test", + .pm = &dma_test_pm_ops, + }, + .probe = dma_test_probe, + .remove = dma_test_remove, + .id_table = dma_test_ids, +}; + +static int __init dma_test_init(void) +{ + u64 data_value = DMA_TEST_DATA_PATTERN; + int i, ret; + + dma_test_pattern = kmalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); + if (!dma_test_pattern) + return -ENOMEM; + + for (i = 0; i < DMA_TEST_FRAME_SIZE / sizeof(data_value); i++) + ((u32 *)dma_test_pattern)[i] = data_value++; + + dma_test_dir = tb_property_create_dir(&dma_test_dir_uuid); + if (!dma_test_dir) { + ret = -ENOMEM; + goto err_free_pattern; + } + + tb_property_add_immediate(dma_test_dir, "prtcid", 1); + tb_property_add_immediate(dma_test_dir, "prtcvers", 1); + tb_property_add_immediate(dma_test_dir, "prtcrevs", 0); + tb_property_add_immediate(dma_test_dir, "prtcstns", 0); + + ret = tb_register_property_dir("dma_test", dma_test_dir); + if (ret) + goto err_free_dir; + + ret = tb_register_service_driver(&dma_test_driver); + if (ret) + goto err_unregister_dir; + + return 0; + +err_unregister_dir: + tb_unregister_property_dir("dma_test", dma_test_dir); +err_free_dir: + tb_property_free_dir(dma_test_dir); +err_free_pattern: + kfree(dma_test_pattern); + + return ret; +} +module_init(dma_test_init); + +static void __exit dma_test_exit(void) +{ + tb_unregister_service_driver(&dma_test_driver); + tb_unregister_property_dir("dma_test", dma_test_dir); + tb_property_free_dir(dma_test_dir); + kfree(dma_test_pattern); +} +module_exit(dma_test_exit); + +MODULE_AUTHOR("Isaac Hazan <isaac.hazan@intel.com>"); +MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); +MODULE_DESCRIPTION("DMA traffic test driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 977ba91f4d0e..8b7f941a9bb7 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -49,6 +49,18 @@ module_param(start_icm, bool, 0444); MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: false)"); /** + * struct usb4_switch_nvm_auth - Holds USB4 NVM_AUTH status + * @reply: Reply from ICM firmware is placed here + * @request: Request that is sent to ICM firmware + * @icm: Pointer to ICM private data + */ +struct usb4_switch_nvm_auth { + struct icm_usb4_switch_op_response reply; + struct icm_usb4_switch_op request; + struct icm *icm; +}; + +/** * struct icm - Internal connection manager private data * @request_lock: Makes sure only one message is send to ICM at time * @rescan_work: Work used to rescan the surviving switches after resume @@ -61,6 +73,8 @@ MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: f * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) * @rpm: Does the controller support runtime PM (RTD3) * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller + * @proto_version: Firmware protocol version + * @last_nvm_auth: Last USB4 router NVM_AUTH result (or %NULL if not set) * @veto: Is RTD3 veto in effect * @is_supported: Checks if we can support ICM on this controller * @cio_reset: Trigger CIO reset @@ -79,11 +93,13 @@ struct icm { struct mutex request_lock; struct delayed_work rescan_work; struct pci_dev *upstream_port; - size_t max_boot_acl; int vnd_cap; bool safe_mode; + size_t max_boot_acl; bool rpm; bool can_upgrade_nvm; + u8 proto_version; + struct usb4_switch_nvm_auth *last_nvm_auth; bool veto; bool (*is_supported)(struct tb *tb); int (*cio_reset)(struct tb *tb); @@ -92,7 +108,7 @@ struct icm { void (*save_devices)(struct tb *tb); int (*driver_ready)(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm); + u8 *proto_version, size_t *nboot_acl, bool *rpm); void (*set_uuid)(struct tb *tb); void (*device_connected)(struct tb *tb, const struct icm_pkg_header *hdr); @@ -437,7 +453,7 @@ static void icm_fr_save_devices(struct tb *tb) static int icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_fr_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -870,7 +886,13 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) return; } + pm_runtime_get_sync(sw->dev.parent); + remove_switch(sw); + + pm_runtime_mark_last_busy(sw->dev.parent); + pm_runtime_put_autosuspend(sw->dev.parent); + tb_switch_put(sw); } @@ -986,7 +1008,7 @@ static int icm_tr_cio_reset(struct tb *tb) static int icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_tr_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -1002,6 +1024,9 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, if (security_level) *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK; + if (proto_version) + *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> + ICM_TR_INFO_PROTO_VERSION_SHIFT; if (nboot_acl) *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >> ICM_TR_INFO_BOOT_ACL_SHIFT; @@ -1280,8 +1305,13 @@ icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) tb_warn(tb, "no switch exists at %llx, ignoring\n", route); return; } + pm_runtime_get_sync(sw->dev.parent); remove_switch(sw); + + pm_runtime_mark_last_busy(sw->dev.parent); + pm_runtime_put_autosuspend(sw->dev.parent); + tb_switch_put(sw); } @@ -1450,7 +1480,7 @@ static int icm_ar_get_mode(struct tb *tb) static int icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_ar_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -1580,7 +1610,7 @@ static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, static int icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_tr_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -1594,6 +1624,10 @@ icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, if (ret) return ret; + if (proto_version) + *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> + ICM_TR_INFO_PROTO_VERSION_SHIFT; + /* Ice Lake always supports RTD3 */ if (rpm) *rpm = true; @@ -1702,13 +1736,14 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, static int __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm *icm = tb_priv(tb); unsigned int retries = 50; int ret; - ret = icm->driver_ready(tb, security_level, nboot_acl, rpm); + ret = icm->driver_ready(tb, security_level, proto_version, nboot_acl, + rpm); if (ret) { tb_err(tb, "failed to send driver ready to ICM\n"); return ret; @@ -1918,8 +1953,8 @@ static int icm_driver_ready(struct tb *tb) return 0; } - ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl, - &icm->rpm); + ret = __icm_driver_ready(tb, &tb->security_level, &icm->proto_version, + &tb->nboot_acl, &icm->rpm); if (ret) return ret; @@ -1930,6 +1965,9 @@ static int icm_driver_ready(struct tb *tb) if (tb->nboot_acl > icm->max_boot_acl) tb->nboot_acl = 0; + if (icm->proto_version >= 3) + tb_dbg(tb, "USB4 proxy operations supported\n"); + return 0; } @@ -1976,7 +2014,9 @@ static int complete_rpm(struct device *dev, void *data) static void remove_unplugged_switch(struct tb_switch *sw) { - pm_runtime_get_sync(sw->dev.parent); + struct device *parent = get_device(sw->dev.parent); + + pm_runtime_get_sync(parent); /* * Signal this and switches below for rpm_complete because @@ -1987,8 +2027,10 @@ static void remove_unplugged_switch(struct tb_switch *sw) bus_for_each_dev(&tb_bus_type, &sw->dev, NULL, complete_rpm); tb_switch_remove(sw); - pm_runtime_mark_last_busy(sw->dev.parent); - pm_runtime_put_autosuspend(sw->dev.parent); + pm_runtime_mark_last_busy(parent); + pm_runtime_put_autosuspend(parent); + + put_device(parent); } static void icm_free_unplugged_children(struct tb_switch *sw) @@ -2041,7 +2083,7 @@ static void icm_complete(struct tb *tb) * Now all existing children should be resumed, start events * from ICM to get updated status. */ - __icm_driver_ready(tb, NULL, NULL, NULL); + __icm_driver_ready(tb, NULL, NULL, NULL, NULL); /* * We do not get notifications of devices that have been @@ -2120,6 +2162,8 @@ static void icm_stop(struct tb *tb) tb_switch_remove(tb->root_switch); tb->root_switch = NULL; nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); + kfree(icm->last_nvm_auth); + icm->last_nvm_auth = NULL; } static int icm_disconnect_pcie_paths(struct tb *tb) @@ -2127,6 +2171,165 @@ static int icm_disconnect_pcie_paths(struct tb *tb) return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); } +static void icm_usb4_switch_nvm_auth_complete(void *data) +{ + struct usb4_switch_nvm_auth *auth = data; + struct icm *icm = auth->icm; + struct tb *tb = icm_to_tb(icm); + + tb_dbg(tb, "NVM_AUTH response for %llx flags %#x status %#x\n", + get_route(auth->reply.route_hi, auth->reply.route_lo), + auth->reply.hdr.flags, auth->reply.status); + + mutex_lock(&tb->lock); + if (WARN_ON(icm->last_nvm_auth)) + kfree(icm->last_nvm_auth); + icm->last_nvm_auth = auth; + mutex_unlock(&tb->lock); +} + +static int icm_usb4_switch_nvm_authenticate(struct tb *tb, u64 route) +{ + struct usb4_switch_nvm_auth *auth; + struct icm *icm = tb_priv(tb); + struct tb_cfg_request *req; + int ret; + + auth = kzalloc(sizeof(*auth), GFP_KERNEL); + if (!auth) + return -ENOMEM; + + auth->icm = icm; + auth->request.hdr.code = ICM_USB4_SWITCH_OP; + auth->request.route_hi = upper_32_bits(route); + auth->request.route_lo = lower_32_bits(route); + auth->request.opcode = USB4_SWITCH_OP_NVM_AUTH; + + req = tb_cfg_request_alloc(); + if (!req) { + ret = -ENOMEM; + goto err_free_auth; + } + + req->match = icm_match; + req->copy = icm_copy; + req->request = &auth->request; + req->request_size = sizeof(auth->request); + req->request_type = TB_CFG_PKG_ICM_CMD; + req->response = &auth->reply; + req->npackets = 1; + req->response_size = sizeof(auth->reply); + req->response_type = TB_CFG_PKG_ICM_RESP; + + tb_dbg(tb, "NVM_AUTH request for %llx\n", route); + + mutex_lock(&icm->request_lock); + ret = tb_cfg_request(tb->ctl, req, icm_usb4_switch_nvm_auth_complete, + auth); + mutex_unlock(&icm->request_lock); + + tb_cfg_request_put(req); + if (ret) + goto err_free_auth; + return 0; + +err_free_auth: + kfree(auth); + return ret; +} + +static int icm_usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, + u8 *status, const void *tx_data, size_t tx_data_len, + void *rx_data, size_t rx_data_len) +{ + struct icm_usb4_switch_op_response reply; + struct icm_usb4_switch_op request; + struct tb *tb = sw->tb; + struct icm *icm = tb_priv(tb); + u64 route = tb_route(sw); + int ret; + + /* + * USB4 router operation proxy is supported in firmware if the + * protocol version is 3 or higher. + */ + if (icm->proto_version < 3) + return -EOPNOTSUPP; + + /* + * NVM_AUTH is a special USB4 proxy operation that does not + * return immediately so handle it separately. + */ + if (opcode == USB4_SWITCH_OP_NVM_AUTH) + return icm_usb4_switch_nvm_authenticate(tb, route); + + memset(&request, 0, sizeof(request)); + request.hdr.code = ICM_USB4_SWITCH_OP; + request.route_hi = upper_32_bits(route); + request.route_lo = lower_32_bits(route); + request.opcode = opcode; + if (metadata) + request.metadata = *metadata; + + if (tx_data_len) { + request.data_len_valid |= ICM_USB4_SWITCH_DATA_VALID; + if (tx_data_len < ARRAY_SIZE(request.data)) + request.data_len_valid = + tx_data_len & ICM_USB4_SWITCH_DATA_LEN_MASK; + memcpy(request.data, tx_data, tx_data_len * sizeof(u32)); + } + + memset(&reply, 0, sizeof(reply)); + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), + 1, ICM_TIMEOUT); + if (ret) + return ret; + + if (reply.hdr.flags & ICM_FLAGS_ERROR) + return -EIO; + + if (status) + *status = reply.status; + + if (metadata) + *metadata = reply.metadata; + + if (rx_data_len) + memcpy(rx_data, reply.data, rx_data_len * sizeof(u32)); + + return 0; +} + +static int icm_usb4_switch_nvm_authenticate_status(struct tb_switch *sw, + u32 *status) +{ + struct usb4_switch_nvm_auth *auth; + struct tb *tb = sw->tb; + struct icm *icm = tb_priv(tb); + int ret = 0; + + if (icm->proto_version < 3) + return -EOPNOTSUPP; + + auth = icm->last_nvm_auth; + icm->last_nvm_auth = NULL; + + if (auth && auth->reply.route_hi == sw->config.route_hi && + auth->reply.route_lo == sw->config.route_lo) { + tb_dbg(tb, "NVM_AUTH found for %llx flags 0x%#x status %#x\n", + tb_route(sw), auth->reply.hdr.flags, auth->reply.status); + if (auth->reply.hdr.flags & ICM_FLAGS_ERROR) + ret = -EIO; + else + *status = auth->reply.status; + } else { + *status = 0; + } + + kfree(auth); + return ret; +} + /* Falcon Ridge */ static const struct tb_cm_ops icm_fr_ops = { .driver_ready = icm_driver_ready, @@ -2185,6 +2388,9 @@ static const struct tb_cm_ops icm_tr_ops = { .disconnect_pcie_paths = icm_disconnect_pcie_paths, .approve_xdomain_paths = icm_tr_approve_xdomain_paths, .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, + .usb4_switch_op = icm_usb4_switch_op, + .usb4_switch_nvm_authenticate_status = + icm_usb4_switch_nvm_authenticate_status, }; /* Ice Lake */ @@ -2198,6 +2404,9 @@ static const struct tb_cm_ops icm_icl_ops = { .handle_event = icm_handle_event, .approve_xdomain_paths = icm_tr_approve_xdomain_paths, .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, + .usb4_switch_op = icm_usb4_switch_op, + .usb4_switch_nvm_authenticate_status = + icm_usb4_switch_nvm_authenticate_status, }; struct tb *icm_probe(struct tb_nhi *nhi) @@ -2296,6 +2505,17 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->rtd3_veto = icm_icl_rtd3_veto; tb->cm_ops = &icm_icl_ops; break; + + case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI: + icm->is_supported = icm_tgl_is_supported; + icm->get_mode = icm_ar_get_mode; + icm->driver_ready = icm_tr_driver_ready; + icm->device_connected = icm_tr_device_connected; + icm->device_disconnected = icm_tr_device_disconnected; + icm->xdomain_connected = icm_tr_xdomain_connected; + icm->xdomain_disconnected = icm_tr_xdomain_disconnected; + tb->cm_ops = &icm_tr_ops; + break; } if (!icm->is_supported || !icm->is_supported(tb)) { @@ -2304,5 +2524,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) return NULL; } + tb_dbg(tb, "using firmware connection manager\n"); + return tb; } diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index db80dc5dfeba..cfc622da4f83 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -494,7 +494,7 @@ err_unlock: static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, bool transmit, unsigned int flags, - u16 sof_mask, u16 eof_mask, + int e2e_tx_hop, u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data) { @@ -517,6 +517,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ring->is_tx = transmit; ring->size = size; ring->flags = flags; + ring->e2e_tx_hop = e2e_tx_hop; ring->sof_mask = sof_mask; ring->eof_mask = eof_mask; ring->head = 0; @@ -561,7 +562,7 @@ err_free_ring: struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags) { - return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL); + return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, 0, NULL, NULL); } EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); @@ -571,6 +572,7 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @hop: HopID (ring) to allocate. Pass %-1 for automatic allocation. * @size: Number of entries in the ring * @flags: Flags for the ring + * @e2e_tx_hop: Transmit HopID when E2E is enabled in @flags * @sof_mask: Mask of PDF values that start a frame * @eof_mask: Mask of PDF values that end a frame * @start_poll: If not %NULL the ring will call this function when an @@ -579,10 +581,11 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @poll_data: Optional data passed to @start_poll */ struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask, + unsigned int flags, int e2e_tx_hop, + u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data) { - return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask, + return tb_ring_alloc(nhi, hop, size, false, flags, e2e_tx_hop, sof_mask, eof_mask, start_poll, poll_data); } EXPORT_SYMBOL_GPL(tb_ring_alloc_rx); @@ -629,6 +632,31 @@ void tb_ring_start(struct tb_ring *ring) ring_iowrite32options(ring, sof_eof_mask, 4); ring_iowrite32options(ring, flags, 0); } + + /* + * Now that the ring valid bit is set we can configure E2E if + * enabled for the ring. + */ + if (ring->flags & RING_FLAG_E2E) { + if (!ring->is_tx) { + u32 hop; + + hop = ring->e2e_tx_hop << REG_RX_OPTIONS_E2E_HOP_SHIFT; + hop &= REG_RX_OPTIONS_E2E_HOP_MASK; + flags |= hop; + + dev_dbg(&ring->nhi->pdev->dev, + "enabling E2E for %s %d with TX HopID %d\n", + RING_TYPE(ring), ring->hop, ring->e2e_tx_hop); + } else { + dev_dbg(&ring->nhi->pdev->dev, "enabling E2E for %s %d\n", + RING_TYPE(ring), ring->hop); + } + + flags |= RING_FLAG_E2E_FLOW_CONTROL; + ring_iowrite32options(ring, flags, 0); + } + ring_interrupt_active(ring, true); ring->running = true; err: diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 4e0861d75072..69770beca792 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -55,6 +55,7 @@ extern const struct tb_nhi_ops icl_nhi_ops; * need for the PCI quirk anymore as we will use ICM also on Apple * hardware. */ +#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI 0x1137 #define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI 0x157d #define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE 0x157e #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI 0x15bf diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 03e7b714deab..ca7d738d66de 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -406,10 +406,17 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index, if (!hop.pending) { if (clear_fc) { - /* Clear flow control */ - hop.ingress_fc = 0; + /* + * Clear flow control. Protocol adapters + * IFC and ISE bits are vendor defined + * in the USB4 spec so we clear them + * only for pre-USB4 adapters. + */ + if (!tb_switch_is_usb4(port->sw)) { + hop.ingress_fc = 0; + hop.ingress_shared_buffer = 0; + } hop.egress_fc = 0; - hop.ingress_shared_buffer = 0; hop.egress_shared_buffer = 0; return tb_port_write(port, &hop, TB_CFG_HOPS, @@ -447,7 +454,7 @@ void tb_path_deactivate(struct tb_path *path) return; } tb_dbg(path->tb, - "deactivating %s path from %llx:%x to %llx:%x\n", + "deactivating %s path from %llx:%u to %llx:%u\n", path->name, tb_route(path->hops[0].in_port->sw), path->hops[0].in_port->port, tb_route(path->hops[path->path_length - 1].out_port->sw), @@ -475,7 +482,7 @@ int tb_path_activate(struct tb_path *path) } tb_dbg(path->tb, - "activating %s path from %llx:%x to %llx:%x\n", + "activating %s path from %llx:%u to %llx:%u\n", path->name, tb_route(path->hops[0].in_port->sw), path->hops[0].in_port->port, tb_route(path->hops[path->path_length - 1].out_port->sw), diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c73bbfe69ba1..a8572f49d3ad 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -503,12 +503,13 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) /** * tb_port_state() - get connectedness state of a port + * @port: the port to check * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: Returns an enum tb_port_state on success or an error code on failure. */ -static int tb_port_state(struct tb_port *port) +int tb_port_state(struct tb_port *port) { struct tb_cap_phy phy; int res; @@ -932,7 +933,14 @@ int tb_port_get_link_speed(struct tb_port *port) return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10; } -static int tb_port_get_link_width(struct tb_port *port) +/** + * tb_port_get_link_width() - Get current link width + * @port: Port to check (USB4 or CIO) + * + * Returns link width. Return values can be 1 (Single-Lane), 2 (Dual-Lane) + * or negative errno in case of failure. + */ +int tb_port_get_link_width(struct tb_port *port) { u32 val; int ret; @@ -1001,7 +1009,16 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width) port->cap_phy + LANE_ADP_CS_1, 1); } -static int tb_port_lane_bonding_enable(struct tb_port *port) +/** + * tb_port_lane_bonding_enable() - Enable bonding on port + * @port: port to enable + * + * Enable bonding by setting the link width of the port and the + * other port in case of dual link port. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_port_lane_bonding_enable(struct tb_port *port) { int ret; @@ -1031,7 +1048,15 @@ static int tb_port_lane_bonding_enable(struct tb_port *port) return 0; } -static void tb_port_lane_bonding_disable(struct tb_port *port) +/** + * tb_port_lane_bonding_disable() - Disable bonding on port + * @port: port to disable + * + * Disable bonding by setting the link width of the port and the + * other port in case of dual link port. + * + */ +void tb_port_lane_bonding_disable(struct tb_port *port) { port->dual_link_port->bonded = false; port->bonded = false; @@ -2135,6 +2160,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) fallthrough; case 3: + case 4: ret = tb_switch_set_uuid(sw); if (ret) return ret; @@ -2150,6 +2176,22 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) break; } + if (sw->no_nvm_upgrade) + return 0; + + if (tb_switch_is_usb4(sw)) { + ret = usb4_switch_nvm_authenticate_status(sw, &status); + if (ret) + return ret; + + if (status) { + tb_sw_info(sw, "switch flash authentication failed\n"); + nvm_set_auth_status(sw, status); + } + + return 0; + } + /* Root switch DMA port requires running firmware */ if (!tb_route(sw) && !tb_switch_is_icm(sw)) return 0; @@ -2158,9 +2200,6 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) if (!sw->dma_port) return 0; - if (sw->no_nvm_upgrade) - return 0; - /* * If there is status already set then authentication failed * when the dma_port_flash_update_auth() returned. Power cycling diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 214fbc92c1b7..51d5b031cada 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1534,5 +1534,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) INIT_LIST_HEAD(&tcm->dp_resources); INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work); + tb_dbg(tb, "using software connection manager\n"); + return tb; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8ea360b0ff77..554feda1e359 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -367,6 +367,14 @@ struct tb_path { * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update * @approve_xdomain_paths: Approve (establish) XDomain DMA paths * @disconnect_xdomain_paths: Disconnect XDomain DMA paths + * @usb4_switch_op: Optional proxy for USB4 router operations. If set + * this will be called whenever USB4 router operation is + * performed. If this returns %-EOPNOTSUPP then the + * native USB4 router operation is called. + * @usb4_switch_nvm_authenticate_status: Optional callback that the CM + * implementation can be used to + * return status of USB4 NVM_AUTH + * router operation. */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -393,6 +401,11 @@ struct tb_cm_ops { int (*disconnect_pcie_paths)(struct tb *tb); int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); + int (*usb4_switch_op)(struct tb_switch *sw, u16 opcode, u32 *metadata, + u8 *status, const void *tx_data, size_t tx_data_len, + void *rx_data, size_t rx_data_len); + int (*usb4_switch_nvm_authenticate_status)(struct tb_switch *sw, + u32 *status); }; static inline void *tb_priv(struct tb *tb) @@ -864,6 +877,10 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, (p) = tb_next_port_on_path((src), (dst), (p))) int tb_port_get_link_speed(struct tb_port *port); +int tb_port_get_link_width(struct tb_port *port); +int tb_port_state(struct tb_port *port); +int tb_port_lane_bonding_enable(struct tb_port *port); +void tb_port_lane_bonding_disable(struct tb_port *port); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); @@ -970,6 +987,7 @@ int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, const void *buf, size_t size); int usb4_switch_nvm_authenticate(struct tb_switch *sw); +int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status); bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in); int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in); @@ -1025,11 +1043,15 @@ void tb_debugfs_init(void); void tb_debugfs_exit(void); void tb_switch_debugfs_init(struct tb_switch *sw); void tb_switch_debugfs_remove(struct tb_switch *sw); +void tb_service_debugfs_init(struct tb_service *svc); +void tb_service_debugfs_remove(struct tb_service *svc); #else static inline void tb_debugfs_init(void) { } static inline void tb_debugfs_exit(void) { } static inline void tb_switch_debugfs_init(struct tb_switch *sw) { } static inline void tb_switch_debugfs_remove(struct tb_switch *sw) { } +static inline void tb_service_debugfs_init(struct tb_service *svc) { } +static inline void tb_service_debugfs_remove(struct tb_service *svc) { } #endif #ifdef CONFIG_USB4_KUNIT_TEST diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 0e01dbc63e72..bcabfcb2fd03 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -106,6 +106,7 @@ enum icm_pkg_code { ICM_APPROVE_XDOMAIN = 0x10, ICM_DISCONNECT_XDOMAIN = 0x11, ICM_PREBOOT_ACL = 0x18, + ICM_USB4_SWITCH_OP = 0x20, }; enum icm_event_code { @@ -343,6 +344,8 @@ struct icm_tr_pkg_driver_ready_response { #define ICM_TR_FLAGS_RTD3 BIT(6) #define ICM_TR_INFO_SLEVEL_MASK GENMASK(2, 0) +#define ICM_TR_INFO_PROTO_VERSION_MASK GENMASK(6, 4) +#define ICM_TR_INFO_PROTO_VERSION_SHIFT 4 #define ICM_TR_INFO_BOOT_ACL_SHIFT 7 #define ICM_TR_INFO_BOOT_ACL_MASK GENMASK(12, 7) @@ -478,6 +481,31 @@ struct icm_icl_event_rtd3_veto { u32 veto_reason; }; +/* USB4 ICM messages */ + +struct icm_usb4_switch_op { + struct icm_pkg_header hdr; + u32 route_hi; + u32 route_lo; + u32 metadata; + u16 opcode; + u16 data_len_valid; + u32 data[16]; +}; + +#define ICM_USB4_SWITCH_DATA_LEN_MASK GENMASK(3, 0) +#define ICM_USB4_SWITCH_DATA_VALID BIT(4) + +struct icm_usb4_switch_op_response { + struct icm_pkg_header hdr; + u32 route_hi; + u32 route_lo; + u32 metadata; + u16 opcode; + u16 status; + u32 data[16]; +}; + /* XDomain messages */ struct tb_xdomain_header { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index e7d9529822fa..ae427a953489 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -211,11 +211,25 @@ struct tb_regs_switch_header { #define ROUTER_CS_9 0x09 #define ROUTER_CS_25 0x19 #define ROUTER_CS_26 0x1a +#define ROUTER_CS_26_OPCODE_MASK GENMASK(15, 0) #define ROUTER_CS_26_STATUS_MASK GENMASK(29, 24) #define ROUTER_CS_26_STATUS_SHIFT 24 #define ROUTER_CS_26_ONS BIT(30) #define ROUTER_CS_26_OV BIT(31) +/* USB4 router operations opcodes */ +enum usb4_switch_op { + USB4_SWITCH_OP_QUERY_DP_RESOURCE = 0x10, + USB4_SWITCH_OP_ALLOC_DP_RESOURCE = 0x11, + USB4_SWITCH_OP_DEALLOC_DP_RESOURCE = 0x12, + USB4_SWITCH_OP_NVM_WRITE = 0x20, + USB4_SWITCH_OP_NVM_AUTH = 0x21, + USB4_SWITCH_OP_NVM_READ = 0x22, + USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, + USB4_SWITCH_OP_DROM_READ = 0x24, + USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, +}; + /* Router TMU configuration */ #define TMU_RTR_CS_0 0x00 #define TMU_RTR_CS_0_TD BIT(27) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 829b6ccdd5d4..dcdf9c7a9cae 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -34,9 +34,6 @@ #define TB_DP_AUX_PATH_OUT 1 #define TB_DP_AUX_PATH_IN 2 -#define TB_DMA_PATH_OUT 0 -#define TB_DMA_PATH_IN 1 - static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" }; #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ @@ -829,10 +826,10 @@ static void tb_dma_init_path(struct tb_path *path, unsigned int isb, * @nhi: Host controller port * @dst: Destination null port which the other domain is connected to * @transmit_ring: NHI ring number used to send packets towards the - * other domain + * other domain. Set to %0 if TX path is not needed. * @transmit_path: HopID used for transmitting packets * @receive_ring: NHI ring number used to receive packets from the - * other domain + * other domain. Set to %0 if RX path is not needed. * @reveive_path: HopID used for receiving packets * * Return: Returns a tb_tunnel on success or NULL on failure. @@ -843,10 +840,19 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, int receive_path) { struct tb_tunnel *tunnel; + size_t npaths = 0, i = 0; struct tb_path *path; u32 credits; - tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_DMA); + if (receive_ring) + npaths++; + if (transmit_ring) + npaths++; + + if (WARN_ON(!npaths)) + return NULL; + + tunnel = tb_tunnel_alloc(tb, npaths, TB_TUNNEL_DMA); if (!tunnel) return NULL; @@ -856,22 +862,28 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, credits = tb_dma_credits(nhi); - path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, "DMA RX"); - if (!path) { - tb_tunnel_free(tunnel); - return NULL; + if (receive_ring) { + path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, + "DMA RX"); + if (!path) { + tb_tunnel_free(tunnel); + return NULL; + } + tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL, + credits); + tunnel->paths[i++] = path; } - tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL, - credits); - tunnel->paths[TB_DMA_PATH_IN] = path; - path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, "DMA TX"); - if (!path) { - tb_tunnel_free(tunnel); - return NULL; + if (transmit_ring) { + path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, + "DMA TX"); + if (!path) { + tb_tunnel_free(tunnel); + return NULL; + } + tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits); + tunnel->paths[i++] = path; } - tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits); - tunnel->paths[TB_DMA_PATH_OUT] = path; return tunnel; } diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index f2583b4053e4..67a2867382ed 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -16,18 +16,6 @@ #define USB4_DATA_DWORDS 16 #define USB4_DATA_RETRIES 3 -enum usb4_switch_op { - USB4_SWITCH_OP_QUERY_DP_RESOURCE = 0x10, - USB4_SWITCH_OP_ALLOC_DP_RESOURCE = 0x11, - USB4_SWITCH_OP_DEALLOC_DP_RESOURCE = 0x12, - USB4_SWITCH_OP_NVM_WRITE = 0x20, - USB4_SWITCH_OP_NVM_AUTH = 0x21, - USB4_SWITCH_OP_NVM_READ = 0x22, - USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, - USB4_SWITCH_OP_DROM_READ = 0x24, - USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, -}; - enum usb4_sb_target { USB4_SB_TARGET_ROUTER, USB4_SB_TARGET_PARTNER, @@ -74,34 +62,6 @@ static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, return -ETIMEDOUT; } -static int usb4_switch_op_read_data(struct tb_switch *sw, void *data, - size_t dwords) -{ - if (dwords > USB4_DATA_DWORDS) - return -EINVAL; - - return tb_sw_read(sw, data, TB_CFG_SWITCH, ROUTER_CS_9, dwords); -} - -static int usb4_switch_op_write_data(struct tb_switch *sw, const void *data, - size_t dwords) -{ - if (dwords > USB4_DATA_DWORDS) - return -EINVAL; - - return tb_sw_write(sw, data, TB_CFG_SWITCH, ROUTER_CS_9, dwords); -} - -static int usb4_switch_op_read_metadata(struct tb_switch *sw, u32 *metadata) -{ - return tb_sw_read(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); -} - -static int usb4_switch_op_write_metadata(struct tb_switch *sw, u32 metadata) -{ - return tb_sw_write(sw, &metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); -} - static int usb4_do_read_data(u16 address, void *buf, size_t size, read_block_fn read_block, void *read_block_data) { @@ -171,11 +131,26 @@ static int usb4_do_write_data(unsigned int address, const void *buf, size_t size return 0; } -static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status) +static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status, + const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) { u32 val; int ret; + if (metadata) { + ret = tb_sw_write(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); + if (ret) + return ret; + } + if (tx_dwords) { + ret = tb_sw_write(sw, tx_data, TB_CFG_SWITCH, ROUTER_CS_9, + tx_dwords); + if (ret) + return ret; + } + val = opcode | ROUTER_CS_26_OV; ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); if (ret) @@ -192,10 +167,73 @@ static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status) if (val & ROUTER_CS_26_ONS) return -EOPNOTSUPP; - *status = (val & ROUTER_CS_26_STATUS_MASK) >> ROUTER_CS_26_STATUS_SHIFT; + if (status) + *status = (val & ROUTER_CS_26_STATUS_MASK) >> + ROUTER_CS_26_STATUS_SHIFT; + + if (metadata) { + ret = tb_sw_read(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); + if (ret) + return ret; + } + if (rx_dwords) { + ret = tb_sw_read(sw, rx_data, TB_CFG_SWITCH, ROUTER_CS_9, + rx_dwords); + if (ret) + return ret; + } + return 0; } +static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, + u8 *status, const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) +{ + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + + if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS) + return -EINVAL; + + /* + * If the connection manager implementation provides USB4 router + * operation proxy callback, call it here instead of running the + * operation natively. + */ + if (cm_ops->usb4_switch_op) { + int ret; + + ret = cm_ops->usb4_switch_op(sw, opcode, metadata, status, + tx_data, tx_dwords, rx_data, + rx_dwords); + if (ret != -EOPNOTSUPP) + return ret; + + /* + * If the proxy was not supported then run the native + * router operation instead. + */ + } + + return usb4_native_switch_op(sw, opcode, metadata, status, tx_data, + tx_dwords, rx_data, rx_dwords); +} + +static inline int usb4_switch_op(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status) +{ + return __usb4_switch_op(sw, opcode, metadata, status, NULL, 0, NULL, 0); +} + +static inline int usb4_switch_op_data(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status, + const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) +{ + return __usb4_switch_op(sw, opcode, metadata, status, tx_data, + tx_dwords, rx_data, rx_dwords); +} + static void usb4_switch_check_wakes(struct tb_switch *sw) { struct tb_port *port; @@ -348,18 +386,12 @@ static int usb4_switch_drom_read_block(void *data, metadata |= (dwaddress << USB4_DROM_ADDRESS_SHIFT) & USB4_DROM_ADDRESS_MASK; - ret = usb4_switch_op_write_metadata(sw, metadata); + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_DROM_READ, &metadata, + &status, NULL, 0, buf, dwords); if (ret) return ret; - ret = usb4_switch_op(sw, USB4_SWITCH_OP_DROM_READ, &status); - if (ret) - return ret; - - if (status) - return -EIO; - - return usb4_switch_op_read_data(sw, buf, dwords); + return status ? -EIO : 0; } /** @@ -512,17 +544,14 @@ int usb4_switch_nvm_sector_size(struct tb_switch *sw) u8 status; int ret; - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SECTOR_SIZE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SECTOR_SIZE, &metadata, + &status); if (ret) return ret; if (status) return status == 0x2 ? -EOPNOTSUPP : -EIO; - ret = usb4_switch_op_read_metadata(sw, &metadata); - if (ret) - return ret; - return metadata & USB4_NVM_SECTOR_SIZE_MASK; } @@ -539,18 +568,12 @@ static int usb4_switch_nvm_read_block(void *data, metadata |= (dwaddress << USB4_NVM_READ_OFFSET_SHIFT) & USB4_NVM_READ_OFFSET_MASK; - ret = usb4_switch_op_write_metadata(sw, metadata); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_READ, &status); + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_NVM_READ, &metadata, + &status, NULL, 0, buf, dwords); if (ret) return ret; - if (status) - return -EIO; - - return usb4_switch_op_read_data(sw, buf, dwords); + return status ? -EIO : 0; } /** @@ -581,11 +604,8 @@ static int usb4_switch_nvm_set_offset(struct tb_switch *sw, metadata = (dwaddress << USB4_NVM_SET_OFFSET_SHIFT) & USB4_NVM_SET_OFFSET_MASK; - ret = usb4_switch_op_write_metadata(sw, metadata); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SET_OFFSET, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SET_OFFSET, &metadata, + &status); if (ret) return ret; @@ -599,11 +619,8 @@ static int usb4_switch_nvm_write_next_block(void *data, const void *buf, u8 status; int ret; - ret = usb4_switch_op_write_data(sw, buf, dwords); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_WRITE, &status); + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_NVM_WRITE, NULL, &status, + buf, dwords, NULL, 0); if (ret) return ret; @@ -638,32 +655,78 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, * @sw: USB4 router * * After the new NVM has been written via usb4_switch_nvm_write(), this - * function triggers NVM authentication process. If the authentication - * is successful the router is power cycled and the new NVM starts + * function triggers NVM authentication process. The router gets power + * cycled and if the authentication is successful the new NVM starts * running. In case of failure returns negative errno. + * + * The caller should call usb4_switch_nvm_authenticate_status() to read + * the status of the authentication after power cycle. It should be the + * first router operation to avoid the status being lost. */ int usb4_switch_nvm_authenticate(struct tb_switch *sw) { - u8 status = 0; int ret; - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, NULL, NULL); + switch (ret) { + /* + * The router is power cycled once NVM_AUTH is started so it is + * expected to get any of the following errors back. + */ + case -EACCES: + case -ENOTCONN: + case -ETIMEDOUT: + return 0; + + default: + return ret; + } +} + +/** + * usb4_switch_nvm_authenticate_status() - Read status of last NVM authenticate + * @sw: USB4 router + * @status: Status code of the operation + * + * The function checks if there is status available from the last NVM + * authenticate router operation. If there is status then %0 is returned + * and the status code is placed in @status. Returns negative errno in case + * of failure. + * + * Must be called before any other router operation. + */ +int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status) +{ + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + u16 opcode; + u32 val; + int ret; + + if (cm_ops->usb4_switch_nvm_authenticate_status) { + ret = cm_ops->usb4_switch_nvm_authenticate_status(sw, status); + if (ret != -EOPNOTSUPP) + return ret; + } + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); if (ret) return ret; - switch (status) { - case 0x0: - tb_sw_dbg(sw, "NVM authentication successful\n"); - return 0; - case 0x1: - return -EINVAL; - case 0x2: - return -EAGAIN; - case 0x3: - return -EOPNOTSUPP; - default: - return -EIO; + /* Check that the opcode is correct */ + opcode = val & ROUTER_CS_26_OPCODE_MASK; + if (opcode == USB4_SWITCH_OP_NVM_AUTH) { + if (val & ROUTER_CS_26_OV) + return -EBUSY; + if (val & ROUTER_CS_26_ONS) + return -EOPNOTSUPP; + + *status = (val & ROUTER_CS_26_STATUS_MASK) >> + ROUTER_CS_26_STATUS_SHIFT; + } else { + *status = 0; } + + return 0; } /** @@ -677,14 +740,12 @@ int usb4_switch_nvm_authenticate(struct tb_switch *sw) */ bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) { + u32 metadata = in->port; u8 status; int ret; - ret = usb4_switch_op_write_metadata(sw, in->port); - if (ret) - return false; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_QUERY_DP_RESOURCE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_QUERY_DP_RESOURCE, &metadata, + &status); /* * If DP resource allocation is not supported assume it is * always available. @@ -709,14 +770,12 @@ bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) */ int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) { + u32 metadata = in->port; u8 status; int ret; - ret = usb4_switch_op_write_metadata(sw, in->port); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_ALLOC_DP_RESOURCE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_ALLOC_DP_RESOURCE, &metadata, + &status); if (ret == -EOPNOTSUPP) return 0; else if (ret) @@ -734,14 +793,12 @@ int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) */ int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in) { + u32 metadata = in->port; u8 status; int ret; - ret = usb4_switch_op_write_metadata(sw, in->port); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_DEALLOC_DP_RESOURCE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_DEALLOC_DP_RESOURCE, &metadata, + &status); if (ret == -EOPNOTSUPP) return 0; else if (ret) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index c00ad817042e..9b3a299a1202 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -8,6 +8,7 @@ */ #include <linux/device.h> +#include <linux/delay.h> #include <linux/kmod.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -21,6 +22,7 @@ #define XDOMAIN_UUID_RETRIES 10 #define XDOMAIN_PROPERTIES_RETRIES 60 #define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 +#define XDOMAIN_BONDING_WAIT 100 /* ms */ struct xdomain_request_work { struct work_struct work; @@ -587,8 +589,6 @@ static void tb_xdp_handle_request(struct work_struct *work) break; case PROPERTIES_CHANGED_REQUEST: { - const struct tb_xdp_properties_changed *xchg = - (const struct tb_xdp_properties_changed *)pkg; struct tb_xdomain *xd; ret = tb_xdp_properties_changed_response(ctl, route, sequence); @@ -598,10 +598,12 @@ static void tb_xdp_handle_request(struct work_struct *work) * the xdomain related to this connection as well in * case there is a change in services it offers. */ - xd = tb_xdomain_find_by_uuid_locked(tb, &xchg->src_uuid); + xd = tb_xdomain_find_by_route_locked(tb, route); if (xd) { - queue_delayed_work(tb->wq, &xd->get_properties_work, - msecs_to_jiffies(50)); + if (device_is_registered(&xd->dev)) { + queue_delayed_work(tb->wq, &xd->get_properties_work, + msecs_to_jiffies(50)); + } tb_xdomain_put(xd); } @@ -777,6 +779,7 @@ static void tb_service_release(struct device *dev) struct tb_service *svc = container_of(dev, struct tb_service, dev); struct tb_xdomain *xd = tb_service_parent(svc); + tb_service_debugfs_remove(svc); ida_simple_remove(&xd->service_ids, svc->id); kfree(svc->key); kfree(svc); @@ -891,6 +894,8 @@ static void enumerate_services(struct tb_xdomain *xd) svc->dev.parent = &xd->dev; dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); + tb_service_debugfs_init(svc); + if (device_register(&svc->dev)) { put_device(&svc->dev); break; @@ -943,6 +948,43 @@ static void tb_xdomain_restore_paths(struct tb_xdomain *xd) } } +static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd) +{ + return tb_to_switch(xd->dev.parent); +} + +static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd) +{ + bool change = false; + struct tb_port *port; + int ret; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + + ret = tb_port_get_link_speed(port); + if (ret < 0) + return ret; + + if (xd->link_speed != ret) + change = true; + + xd->link_speed = ret; + + ret = tb_port_get_link_width(port); + if (ret < 0) + return ret; + + if (xd->link_width != ret) + change = true; + + xd->link_width = ret; + + if (change) + kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); + + return 0; +} + static void tb_xdomain_get_uuid(struct work_struct *work) { struct tb_xdomain *xd = container_of(work, typeof(*xd), @@ -962,10 +1004,8 @@ static void tb_xdomain_get_uuid(struct work_struct *work) return; } - if (uuid_equal(&uuid, xd->local_uuid)) { + if (uuid_equal(&uuid, xd->local_uuid)) dev_dbg(&xd->dev, "intra-domain loop detected\n"); - return; - } /* * If the UUID is different, there is another domain connected @@ -1056,6 +1096,8 @@ static void tb_xdomain_get_properties(struct work_struct *work) xd->properties = dir; xd->property_block_gen = gen; + tb_xdomain_update_link_attributes(xd); + tb_xdomain_restore_paths(xd); mutex_unlock(&xd->lock); @@ -1162,9 +1204,35 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(unique_id); +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%u.0 Gb/s\n", xd->link_speed); +} + +static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL); +static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL); + +static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%u\n", xd->link_width); +} + +static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); +static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); + static struct attribute *xdomain_attrs[] = { &dev_attr_device.attr, &dev_attr_device_name.attr, + &dev_attr_rx_lanes.attr, + &dev_attr_rx_speed.attr, + &dev_attr_tx_lanes.attr, + &dev_attr_tx_speed.attr, &dev_attr_unique_id.attr, &dev_attr_vendor.attr, &dev_attr_vendor_name.attr, @@ -1382,6 +1450,70 @@ void tb_xdomain_remove(struct tb_xdomain *xd) } /** + * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain + * @xd: XDomain connection + * + * Lane bonding is disabled by default for XDomains. This function tries + * to enable bonding by first enabling the port and waiting for the CL0 + * state. + * + * Return: %0 in case of success and negative errno in case of error. + */ +int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) +{ + struct tb_port *port; + int ret; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (!port->dual_link_port) + return -ENODEV; + + ret = tb_port_enable(port->dual_link_port); + if (ret) + return ret; + + ret = tb_wait_for_port(port->dual_link_port, true); + if (ret < 0) + return ret; + if (!ret) + return -ENOTCONN; + + ret = tb_port_lane_bonding_enable(port); + if (ret) { + tb_port_warn(port, "failed to enable lane bonding\n"); + return ret; + } + + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding enabled\n"); + return 0; +} +EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_enable); + +/** + * tb_xdomain_lane_bonding_disable() - Disable lane bonding + * @xd: XDomain connection + * + * Lane bonding is disabled by default for XDomains. If bonding has been + * enabled, this function can be used to disable it. + */ +void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) +{ + struct tb_port *port; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (port->dual_link_port) { + tb_port_lane_bonding_disable(port); + tb_port_disable(port->dual_link_port); + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding disabled\n"); + } +} +EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_disable); + +/** * tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection * @xd: XDomain connection * @transmit_path: HopID of the transmit path the other end is using to diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 9f8b9a567b35..56ade99ef99f 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2897,10 +2897,14 @@ void __do_SAK(struct tty_struct *tty) struct task_struct *g, *p; struct pid *session; int i; + unsigned long flags; if (!tty) return; - session = tty->session; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + session = get_pid(tty->session); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); tty_ldisc_flush(tty); @@ -2932,6 +2936,7 @@ void __do_SAK(struct tty_struct *tty) task_unlock(p); } while_each_thread(g, p); read_unlock(&tasklist_lock); + put_pid(session); #endif } diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index 28a23a0fef21..aa6d0537b379 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -103,8 +103,8 @@ static void __proc_set_tty(struct tty_struct *tty) put_pid(tty->session); put_pid(tty->pgrp); tty->pgrp = get_pid(task_pgrp(current)); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); tty->session = get_pid(task_session(current)); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (current->signal->tty) { tty_debug(tty, "current tty %s not NULL!!\n", current->signal->tty->name); @@ -293,20 +293,23 @@ void disassociate_ctty(int on_exit) spin_lock_irq(¤t->sighand->siglock); put_pid(current->signal->tty_old_pgrp); current->signal->tty_old_pgrp = NULL; - tty = tty_kref_get(current->signal->tty); + spin_unlock_irq(¤t->sighand->siglock); + if (tty) { unsigned long flags; + + tty_lock(tty); spin_lock_irqsave(&tty->ctrl_lock, flags); put_pid(tty->session); put_pid(tty->pgrp); tty->session = NULL; tty->pgrp = NULL; spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty_unlock(tty); tty_kref_put(tty); } - spin_unlock_irq(¤t->sighand->siglock); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); session_clear_tty(task_session(current)); @@ -477,14 +480,19 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t return -ENOTTY; if (retval) return retval; - if (!current->signal->tty || - (current->signal->tty != real_tty) || - (real_tty->session != task_session(current))) - return -ENOTTY; + if (get_user(pgrp_nr, p)) return -EFAULT; if (pgrp_nr < 0) return -EINVAL; + + spin_lock_irq(&real_tty->ctrl_lock); + if (!current->signal->tty || + (current->signal->tty != real_tty) || + (real_tty->session != task_session(current))) { + retval = -ENOTTY; + goto out_unlock_ctrl; + } rcu_read_lock(); pgrp = find_vpid(pgrp_nr); retval = -ESRCH; @@ -494,12 +502,12 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t if (session_of_pgrp(pgrp) != task_session(current)) goto out_unlock; retval = 0; - spin_lock_irq(&tty->ctrl_lock); put_pid(real_tty->pgrp); real_tty->pgrp = get_pid(pgrp); - spin_unlock_irq(&tty->ctrl_lock); out_unlock: rcu_read_unlock(); +out_unlock_ctrl: + spin_unlock_irq(&real_tty->ctrl_lock); return retval; } @@ -511,20 +519,30 @@ out_unlock: * * Obtain the session id of the tty. If there is no session * return an error. - * - * Locking: none. Reference to current->signal->tty is safe. */ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) { + unsigned long flags; + pid_t sid; + /* * (tty == real_tty) is a cheap way of * testing if the tty is NOT a master pty. */ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; + + spin_lock_irqsave(&real_tty->ctrl_lock, flags); if (!real_tty->session) - return -ENOTTY; - return put_user(pid_vnr(real_tty->session), p); + goto err; + sid = pid_vnr(real_tty->session); + spin_unlock_irqrestore(&real_tty->ctrl_lock, flags); + + return put_user(sid, p); + +err: + spin_unlock_irqrestore(&real_tty->ctrl_lock, flags); + return -ENOTTY; } /* diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 1c1c1d659394..ba5706ccc188 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ -obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_USB2) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_MAX3421_HCD) += host/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index e62a770a5d3b..4d3947476f0e 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -810,9 +810,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, mutex_unlock(&instance->poll_state_serialize); mutex_unlock(&instance->adsl_state_serialize); - printk(KERN_INFO "%s%d: %s %pM\n", atm_dev->type, atm_dev->number, - usbatm_instance->description, atm_dev->esi); - if (start_polling) cxacru_poll_status(&instance->poll_work.work); return 0; @@ -852,15 +849,15 @@ static void cxacru_poll_status(struct work_struct *work) switch (instance->adsl_status) { case 0: - atm_printk(KERN_INFO, usbatm, "ADSL state: running\n"); + atm_info(usbatm, "ADSL state: running\n"); break; case 1: - atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n"); + atm_info(usbatm, "ADSL state: stopped\n"); break; default: - atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status); + atm_info(usbatm, "Unknown adsl status %02x\n", instance->adsl_status); break; } } diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 56fe30d247da..33ae03ac13a6 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -249,7 +249,7 @@ static void usbatm_complete(struct urb *urb) /* vdbg("%s: urb 0x%p, status %d, actual_length %d", __func__, urb, status, urb->actual_length); */ - /* usually in_interrupt(), but not always */ + /* Can be invoked from task context, protect against interrupts */ spin_lock_irqsave(&channel->lock, flags); /* must add to the back when receiving; doesn't matter when sending */ @@ -1278,7 +1278,7 @@ EXPORT_SYMBOL_GPL(usbatm_usb_disconnect); static int __init usbatm_usb_init(void) { if (sizeof(struct usbatm_control) > sizeof_field(struct sk_buff, cb)) { - printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name); + pr_err("%s unusable with this kernel!\n", usbatm_driver_name); return -EIO; } diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index ffc9810070a3..0befbf63d1cc 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -179,7 +179,7 @@ static int __init xusbatm_init(void) num_vendor != num_product || num_vendor != num_rx_endpoint || num_vendor != num_tx_endpoint) { - printk(KERN_WARNING "xusbatm: malformed module parameters\n"); + pr_warn("xusbatm: malformed module parameters\n"); return -EINVAL; } diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 0f08aebce86d..1991cb5cf6bf 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -427,7 +427,6 @@ static irqreturn_t cdns3_wakeup_irq(int irq, void *data) */ static int cdns3_probe(struct platform_device *pdev) { - struct usb_role_switch_desc sw_desc = { }; struct device *dev = &pdev->dev; struct resource *res; struct cdns3 *cdns; @@ -521,18 +520,21 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) goto err2; - sw_desc.set = cdns3_role_set; - sw_desc.get = cdns3_role_get; - sw_desc.allow_userspace_control = true; - sw_desc.driver_data = cdns; - if (device_property_read_bool(dev, "usb-role-switch")) + if (device_property_read_bool(dev, "usb-role-switch")) { + struct usb_role_switch_desc sw_desc = { }; + + sw_desc.set = cdns3_role_set; + sw_desc.get = cdns3_role_get; + sw_desc.allow_userspace_control = true; + sw_desc.driver_data = cdns; sw_desc.fwnode = dev->fwnode; - cdns->role_sw = usb_role_switch_register(dev, &sw_desc); - if (IS_ERR(cdns->role_sw)) { - ret = PTR_ERR(cdns->role_sw); - dev_warn(dev, "Unable to register Role Switch\n"); - goto err3; + cdns->role_sw = usb_role_switch_register(dev, &sw_desc); + if (IS_ERR(cdns->role_sw)) { + ret = PTR_ERR(cdns->role_sw); + dev_warn(dev, "Unable to register Role Switch\n"); + goto err3; + } } if (cdns->wakeup_irq) { @@ -543,7 +545,7 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) { dev_err(cdns->dev, "couldn't register wakeup irq handler\n"); - goto err3; + goto err4; } } @@ -575,7 +577,8 @@ static int cdns3_probe(struct platform_device *pdev) return 0; err4: cdns3_drd_exit(cdns); - usb_role_switch_unregister(cdns->role_sw); + if (cdns->role_sw) + usb_role_switch_unregister(cdns->role_sw); err3: set_phy_power_off(cdns); err2: diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index c62e09ba9231..08a4e693c470 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1260,6 +1260,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, priv_req->end_trb = priv_ep->enqueue; cdns3_ep_inc_enq(priv_ep); trb = priv_ep->trb_pool + priv_ep->enqueue; + trb->length = 0; } while (sg_iter < num_trb); trb = priv_req->trb; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 8fa712148e5d..9e12152ea46b 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -57,7 +57,8 @@ static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | - CI_HDRC_TURN_VBUS_EARLY_ON, + CI_HDRC_TURN_VBUS_EARLY_ON | + CI_HDRC_DISABLE_DEVICE_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index a18d7c4222dd..ce5e6f6711f7 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -118,7 +118,7 @@ static struct attribute *ulpi_dev_attrs[] = { NULL }; -static struct attribute_group ulpi_dev_attr_group = { +static const struct attribute_group ulpi_dev_attr_group = { .attrs = ulpi_dev_attrs, }; diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 6cf22c27f2d2..fbb087b728dc 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -51,7 +51,8 @@ void __init usb_init_pool_max(void) /** * hcd_buffer_create - initialize buffer pools * @hcd: the bus whose buffer pools are to be initialized - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Call this as part of initializing a host controller that uses the dma * memory allocators. It initializes some pools of dma-coherent memory that @@ -88,7 +89,8 @@ int hcd_buffer_create(struct usb_hcd *hcd) /** * hcd_buffer_destroy - deallocate buffer pools * @hcd: the bus whose buffer pools are to be destroyed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * This frees the buffer pools created by hcd_buffer_create(). */ diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 562a730befda..b199eb65f378 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1076,6 +1076,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) case USB_PTM_CAP_TYPE: dev->bos->ptm_cap = (struct usb_ptm_cap_descriptor *)buffer; + break; default: break; } diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 1c2c04079676..903426b6d305 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -153,7 +153,7 @@ static struct attribute *ep_dev_attrs[] = { &dev_attr_direction.attr, NULL, }; -static struct attribute_group ep_dev_attr_grp = { +static const struct attribute_group ep_dev_attr_grp = { .attrs = ep_dev_attrs, }; static const struct attribute_group *ep_dev_groups[] = { diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ec0d6c50610c..d630cccd2e6e 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -160,7 +160,8 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, * @dev: USB Host Controller being probed * @id: pci hotplug id connecting controller to HCD framework * @driver: USB HC driver handle - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Allocates basic PCI resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -304,7 +305,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_probe); /** * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Reverses the effect of usb_hcd_pci_probe(), first invoking * the HCD's stop() method. It is always called from a thread diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2c6b9578a7d3..60886a7464c3 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -747,8 +747,7 @@ error: * driver requests it; otherwise the driver is responsible for * calling usb_hcd_poll_rh_status() when an event occurs. * - * Completions are called in_interrupt(), but they may or may not - * be in_irq(). + * Completion handler may not sleep. See usb_hcd_giveback_urb() for details. */ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) { @@ -904,7 +903,8 @@ static void usb_bus_init (struct usb_bus *bus) /** * usb_register_bus - registers the USB host controller with the usb core * @bus: pointer to the bus to register - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Assigns a bus number, and links the controller into usbcore data * structures so that it can be seen by scanning the bus list. @@ -939,7 +939,8 @@ error_find_busnum: /** * usb_deregister_bus - deregisters the USB host controller * @bus: pointer to the bus to deregister - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Recycles the bus number, and unlinks the controller from usbcore data * structures so that it won't be seen by scanning the bus list. @@ -1646,9 +1647,16 @@ static void __usb_hcd_giveback_urb(struct urb *urb) /* pass ownership to the completion handler */ urb->status = status; - kcov_remote_start_usb((u64)urb->dev->bus->busnum); + /* + * This function can be called in task context inside another remote + * coverage collection section, but KCOV doesn't support that kind of + * recursion yet. Only collect coverage in softirq context for now. + */ + if (in_serving_softirq()) + kcov_remote_start_usb((u64)urb->dev->bus->busnum); urb->complete(urb); - kcov_remote_stop(); + if (in_serving_softirq()) + kcov_remote_stop(); usb_anchor_resume_wakeups(anchor); atomic_dec(&urb->use_count); @@ -1691,7 +1699,11 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) * @hcd: host controller returning the URB * @urb: urb being returned to the USB device driver. * @status: completion status code for the URB. - * Context: in_interrupt() + * + * Context: atomic. The completion callback is invoked in caller's context. + * For HCDs with HCD_BH flag set, the completion callback is invoked in tasklet + * context (except for URBs submitted to the root hub which always complete in + * caller's context). * * This hands the URB from HCD to its USB device driver, using its * completion function. The HCD has freed all per-urb resources @@ -2268,7 +2280,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); * usb_bus_start_enum - start immediate enumeration (for OTG) * @bus: the bus (must use hcd framework) * @port_num: 1-based number of port; usually bus->otg_port - * Context: in_interrupt() + * Context: atomic * * Starts enumeration, with an immediate reset followed later by * hub_wq identifying and possibly configuring the device. @@ -2474,7 +2486,8 @@ EXPORT_SYMBOL_GPL(__usb_create_hcd); * @bus_name: value to store in hcd->self.bus_name * @primary_hcd: a pointer to the usb_hcd structure that is sharing the * PCI device. Only allocate certain resources for the primary HCD - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Allocate a struct usb_hcd, with extra space at the end for the * HC driver's private data. Initialize the generic members of the @@ -2496,7 +2509,8 @@ EXPORT_SYMBOL_GPL(usb_create_shared_hcd); * @driver: HC driver that will use this hcd * @dev: device for this HC, stored in hcd->self.controller * @bus_name: value to store in hcd->self.bus_name - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Allocate a struct usb_hcd, with extra space at the end for the * HC driver's private data. Initialize the generic members of the @@ -2830,7 +2844,8 @@ EXPORT_SYMBOL_GPL(usb_add_hcd); /** * usb_remove_hcd - shutdown processing for generic HCDs * @hcd: the usb_hcd structure to remove - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Disconnects the root hub, then reverses the effects of usb_add_hcd(), * invoking the HCD's stop() method. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 17202b2ee063..7f71218cc1e5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2171,7 +2171,8 @@ static void hub_disconnect_children(struct usb_device *udev) /** * usb_disconnect - disconnect a device (usbcore-internal) * @pdev: pointer to device being disconnected - * Context: !in_interrupt () + * + * Context: task context, might sleep * * Something got disconnected. Get rid of it and all of its children. * diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 19ebb542befc..30e9e680c74c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -119,7 +119,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, * @timeout: time in msecs to wait for the message to complete before timing * out (if 0 the wait is forever) * - * Context: !in_interrupt () + * Context: task context, might sleep. * * This function sends a simple control message to a specified endpoint and * waits for the message to complete, or timeout. @@ -204,9 +204,6 @@ int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request, int ret; u8 *data = NULL; - if (usb_pipe_type_check(dev, pipe)) - return -EINVAL; - if (size) { data = kmemdup(driver_data, size, memflags); if (!data) @@ -219,9 +216,8 @@ int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request, if (ret < 0) return ret; - if (ret == size) - return 0; - return -EINVAL; + + return 0; } EXPORT_SYMBOL_GPL(usb_control_msg_send); @@ -273,7 +269,7 @@ int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, int ret; u8 *data; - if (!size || !driver_data || usb_pipe_type_check(dev, pipe)) + if (!size || !driver_data) return -EINVAL; data = kmalloc(size, memflags); @@ -290,7 +286,7 @@ int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, memcpy(driver_data, data, size); ret = 0; } else { - ret = -EINVAL; + ret = -EREMOTEIO; } exit: @@ -310,7 +306,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv); * @timeout: time in msecs to wait for the message to complete before * timing out (if 0 the wait is forever) * - * Context: !in_interrupt () + * Context: task context, might sleep. * * This function sends a simple interrupt message to a specified endpoint and * waits for the message to complete, or timeout. @@ -343,7 +339,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * @timeout: time in msecs to wait for the message to complete before * timing out (if 0 the wait is forever) * - * Context: !in_interrupt () + * Context: task context, might sleep. * * This function sends a simple bulk message to a specified endpoint * and waits for the message to complete, or timeout. @@ -610,7 +606,8 @@ EXPORT_SYMBOL_GPL(usb_sg_init); * usb_sg_wait - synchronously execute scatter/gather request * @io: request block handle, as initialized with usb_sg_init(). * some fields become accessible when this call returns. - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This function blocks until the specified I/O operation completes. It * leverages the grouping of the related I/O requests to get good transfer @@ -764,7 +761,8 @@ EXPORT_SYMBOL_GPL(usb_sg_cancel); * @index: the number of the descriptor * @buf: where to put the descriptor * @size: how big is "buf"? - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Gets a USB descriptor. Convenience functions exist to simplify * getting some types of descriptors. Use @@ -812,7 +810,8 @@ EXPORT_SYMBOL_GPL(usb_get_descriptor); * @index: the number of the descriptor * @buf: where to put the string * @size: how big is "buf"? - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character, * in little-endian byte order). @@ -947,7 +946,8 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) * @index: the number of the descriptor * @buf: where to put the string * @size: how big is "buf"? - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This converts the UTF-16LE encoded strings returned by devices, from * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones @@ -1036,7 +1036,8 @@ char *usb_cache_string(struct usb_device *udev, int index) * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) * @dev: the device whose device descriptor is being updated * @size: how much of the descriptor to read - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Updates the copy of the device descriptor stored in the device structure, * which dedicates space for this purpose. @@ -1071,7 +1072,7 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) /* * usb_set_isoch_delay - informs the device of the packet transmit delay * @dev: the device whose delay is to be informed - * Context: !in_interrupt() + * Context: task context, might sleep * * Since this is an optional request, we don't bother if it fails. */ @@ -1100,7 +1101,8 @@ int usb_set_isoch_delay(struct usb_device *dev) * @type: USB_STATUS_TYPE_*; for standard or PTM status types * @target: zero (for device), else interface or endpoint number * @data: pointer to two bytes of bitmap data - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Returns device, interface, or endpoint status. Normally only of * interest to see if the device is self powered, or has enabled the @@ -1177,7 +1179,8 @@ EXPORT_SYMBOL_GPL(usb_get_status); * usb_clear_halt - tells device to clear endpoint halt/stall condition * @dev: device whose endpoint is halted * @pipe: endpoint "pipe" being cleared - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This is used to clear halt conditions for bulk and interrupt endpoints, * as reported by URB completion status. Endpoints that are halted are @@ -1481,7 +1484,8 @@ void usb_enable_interface(struct usb_device *dev, * @dev: the device whose interface is being updated * @interface: the interface being updated * @alternate: the setting being chosen. - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This is used to enable data transfers on interfaces that may not * be enabled by default. Not all devices support such configurability. @@ -1902,7 +1906,8 @@ static void __usb_queue_reset_device(struct work_struct *ws) * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt(), caller owns the device lock + * + * Context: task context, might sleep. Caller holds device lock. * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 235a7c645503..dfcca9c876c7 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -155,7 +155,7 @@ static struct attribute *port_dev_attrs[] = { NULL, }; -static struct attribute_group port_dev_attr_grp = { +static const struct attribute_group port_dev_attr_grp = { .attrs = port_dev_attrs, }; @@ -169,7 +169,7 @@ static struct attribute *port_dev_usb3_attrs[] = { NULL, }; -static struct attribute_group port_dev_usb3_attr_grp = { +static const struct attribute_group port_dev_usb3_attr_grp = { .attrs = port_dev_usb3_attrs, }; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index fad31ccd1fa8..1b4eb7046b07 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -342,6 +342,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x06a3, 0x0006), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Agfa SNAPSCAN 1212U */ + { USB_DEVICE(0x06bd, 0x0001), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Guillemot Webcam Hercules Dualpix Exchange (2nd ID) */ { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 8d134193fa0c..d85699bee671 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -641,7 +641,7 @@ static struct attribute *usb2_hardware_lpm_attr[] = { &dev_attr_usb2_lpm_besl.attr, NULL, }; -static struct attribute_group usb2_hardware_lpm_attr_group = { +static const struct attribute_group usb2_hardware_lpm_attr_group = { .name = power_group_name, .attrs = usb2_hardware_lpm_attr, }; @@ -651,7 +651,7 @@ static struct attribute *usb3_hardware_lpm_attr[] = { &dev_attr_usb3_hardware_lpm_u2.attr, NULL, }; -static struct attribute_group usb3_hardware_lpm_attr_group = { +static const struct attribute_group usb3_hardware_lpm_attr_group = { .name = power_group_name, .attrs = usb3_hardware_lpm_attr, }; @@ -663,7 +663,7 @@ static struct attribute *power_attrs[] = { &dev_attr_active_duration.attr, NULL, }; -static struct attribute_group power_attr_group = { +static const struct attribute_group power_attr_group = { .name = power_group_name, .attrs = power_attrs, }; @@ -832,7 +832,7 @@ static struct attribute *dev_attrs[] = { #endif NULL, }; -static struct attribute_group dev_attr_grp = { +static const struct attribute_group dev_attr_grp = { .attrs = dev_attrs, }; @@ -865,7 +865,7 @@ static umode_t dev_string_attrs_are_visible(struct kobject *kobj, return a->mode; } -static struct attribute_group dev_string_attr_grp = { +static const struct attribute_group dev_string_attr_grp = { .attrs = dev_string_attrs, .is_visible = dev_string_attrs_are_visible, }; @@ -1222,7 +1222,7 @@ static struct attribute *intf_attrs[] = { &dev_attr_interface_authorized.attr, NULL, }; -static struct attribute_group intf_attr_grp = { +static const struct attribute_group intf_attr_grp = { .attrs = intf_attrs, }; @@ -1246,7 +1246,7 @@ static umode_t intf_assoc_attrs_are_visible(struct kobject *kobj, return a->mode; } -static struct attribute_group intf_assoc_attr_grp = { +static const struct attribute_group intf_assoc_attr_grp = { .attrs = intf_assoc_attrs, .is_visible = intf_assoc_attrs_are_visible, }; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 9b4ac4415f1a..8f07b0516100 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -28,7 +28,6 @@ #include <linux/string.h> #include <linux/bitops.h> #include <linux/slab.h> -#include <linux/interrupt.h> /* for in_interrupt() */ #include <linux/kmod.h> #include <linux/init.h> #include <linux/spinlock.h> @@ -561,7 +560,8 @@ static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd) * @parent: hub to which device is connected; null to allocate a root hub * @bus: bus used to access the device * @port1: one-based index of port; ignored for root hubs - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 046f770a76da..8f5ceacdc5f1 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -678,6 +678,8 @@ static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait) mask |= (EPOLLIN | EPOLLOUT); break; } + break; + case FFS_CLOSING: break; case FFS_DEACTIVATED: @@ -1324,7 +1326,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, case FUNCTIONFS_ENDPOINT_DESC: { int desc_idx; - struct usb_endpoint_descriptor *desc; + struct usb_endpoint_descriptor desc1, *desc; switch (epfile->ffs->gadget->speed) { case USB_SPEED_SUPER: @@ -1336,10 +1338,12 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, default: desc_idx = 0; } + desc = epfile->ep->descs[desc_idx]; + memcpy(&desc1, desc, desc->bLength); spin_unlock_irq(&epfile->ffs->eps_lock); - ret = copy_to_user((void __user *)value, desc, desc->bLength); + ret = copy_to_user((void __user *)value, &desc1, desc1.bLength); if (ret) ret = -EFAULT; return ret; diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 1803646b3678..b56ad7c3838b 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -274,7 +274,7 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) default: ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); - /* FALLTHROUGH */ + fallthrough; /* NOTE: since this driver doesn't maintain an explicit record * of requests it submitted (just maintains qlen count), we diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index ed68a4860b7d..5a201ba7b155 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -559,6 +559,7 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) #if 1 DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); + break; #endif case -EREMOTEIO: /* short read */ break; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index debf54205d22..5b5cfeb6c14a 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -897,8 +897,6 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); * @ep: the endpoint to be used with with the request * @req: the request being given back * - * Context: in_interrupt() - * * This is called by device controller drivers in order to return the * completed request back to the gadget layer. */ diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 53a227217f1c..ab5e978b5052 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -553,6 +553,7 @@ static int dummy_enable(struct usb_ep *_ep, /* we'll fake any legal size */ break; /* save a return statement */ + fallthrough; default: goto done; } @@ -595,6 +596,7 @@ static int dummy_enable(struct usb_ep *_ep, if (max <= 1023) break; /* save a return statement */ + fallthrough; default: goto done; } @@ -1754,8 +1756,10 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, return ret_val; } -/* drive both sides of the transfers; looks like irq handlers to - * both drivers except the callbacks aren't in_irq(). +/* + * Drive both sides of the transfers; looks like irq handlers to both + * drivers except that the callbacks are invoked from soft interrupt + * context. */ static void dummy_timer(struct timer_list *t) { @@ -2734,7 +2738,7 @@ static int __init init(void) { int retval = -ENOMEM; int i; - struct dummy *dum[MAX_NUM_UDC]; + struct dummy *dum[MAX_NUM_UDC] = {}; if (usb_disabled()) return -ENODEV; diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index cfaeca457fa7..ce57961dfd2d 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -304,7 +304,7 @@ static struct pxa_ep *find_pxa_ep(struct pxa_udc *udc, * update_pxa_ep_matches - update pxa_ep cached values in all udc_usb_ep * @udc: pxa udc * - * Context: in_interrupt() + * Context: interrupt handler * * Updates all pxa_ep fields in udc_usb_ep structures, if this field was * previously set up (and is not NULL). The update is necessary is a @@ -859,7 +859,7 @@ static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req, * @ep: pxa physical endpoint * @req: usb request * - * Context: callable when in_interrupt() + * Context: interrupt handler * * Unload as many packets as possible from the fifo we use for usb OUT * transfers and put them into the request. Caller should have made sure @@ -997,7 +997,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) * @ep: control endpoint * @req: request * - * Context: callable when in_interrupt() + * Context: interrupt handler * * Sends a request (or a part of the request) to the control endpoint (ep0 in). * If the request doesn't fit, the remaining part will be sent from irq. @@ -1036,8 +1036,8 @@ static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) * @_req: usb request * @gfp_flags: flags * - * Context: normally called when !in_interrupt, but callable when in_interrupt() - * in the special case of ep0 setup : + * Context: thread context or from the interrupt handler in the + * special case of ep0 setup : * (irq->handle_ep0_ctrl_req->gadget_setup->pxa_ep_queue) * * Returns 0 if succedeed, error otherwise @@ -1512,7 +1512,8 @@ static int should_disable_udc(struct pxa_udc *udc) * pxa_udc_pullup - Offer manual D+ pullup control * @_gadget: usb gadget using the control * @is_active: 0 if disconnect, else connect D+ pullup resistor - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Returns 0 if OK, -EOPNOTSUPP if udc driver doesn't handle D+ pullup */ @@ -1560,7 +1561,7 @@ static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) * @_gadget: usb gadget * @mA: current drawn * - * Context: !in_interrupt() + * Context: task context, might sleep * * Called after a configuration was chosen by a USB host, to inform how much * current can be drawn by the device from VBus line. @@ -1886,7 +1887,7 @@ stall: * @fifo_irq: 1 if triggered by fifo service type irq * @opc_irq: 1 if triggered by output packet complete type irq * - * Context : when in_interrupt() or with ep->lock held + * Context : interrupt handler * * Tries to transfer all pending request data into the endpoint and/or * transfer all pending data in the endpoint into usb requests. @@ -2011,7 +2012,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) * Tries to transfer all pending request data into the endpoint and/or * transfer all pending data in the endpoint into usb requests. * - * Is always called when in_interrupt() and with ep->lock released. + * Is always called from the interrupt handler. ep->lock must not be held. */ static void handle_ep(struct pxa_ep *ep) { diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab12c4bf0ef1..31e59309da1f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -213,13 +213,6 @@ config USB_EHCI_FSL help Variation of ARC USB block used in some Freescale chips. -config USB_EHCI_MXC - tristate "Support for Freescale i.MX on-chip EHCI USB controller" - depends on ARCH_MXC || COMPILE_TEST - select USB_EHCI_ROOT_HUB_TT - help - Variation of ARC USB block used in some Freescale chips. - config USB_EHCI_HCD_NPCM7XX tristate "Support for Nuvoton NPCM7XX on-chip EHCI USB controller" depends on (USB_EHCI_HCD && ARCH_NPCM7XX) || COMPILE_TEST @@ -741,16 +734,6 @@ config USB_RENESAS_USBHS_HCD To compile this driver as a module, choose M here: the module will be called renesas-usbhs. -config USB_IMX21_HCD - tristate "i.MX21 HCD support" - depends on ARM && ARCH_MXC - help - This driver enables support for the on-chip USB host in the - i.MX21 processor. - - To compile this driver as a module, choose M here: the - module will be called "imx21-hcd". - config USB_HCD_BCMA tristate "BCMA usb host driver" depends on BCMA diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index bc731332fed9..c1b08703af10 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o -obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_NPCM7XX) += ehci-npcm7xx.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o @@ -81,7 +80,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o -obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 1e8b59ab2272..6f7bd6641694 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -39,10 +39,10 @@ static struct hc_driver __read_mostly fsl_ehci_hc_driver; /* * fsl_ehci_drv_probe - initialize FSL-based HCDs * @pdev: USB Host Controller being probed - * Context: !in_interrupt() * - * Allocates basic resources for this USB host controller. + * Context: task context, might sleep * + * Allocates basic resources for this USB host controller. */ static int fsl_ehci_drv_probe(struct platform_device *pdev) { @@ -684,12 +684,11 @@ static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = { /** * fsl_ehci_drv_remove - shutdown processing for FSL-based HCDs * @pdev: USB Host Controller being removed - * Context: !in_interrupt() * - * Reverses the effect of usb_hcd_fsl_probe(). + * Context: task context, might sleep * + * Reverses the effect of usb_hcd_fsl_probe(). */ - static int fsl_ehci_drv_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 3575b7201881..e358ae17d51e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -867,7 +867,7 @@ static int ehci_urb_enqueue ( */ if (urb->transfer_buffer_length > (16 * 1024)) return -EMSGSIZE; - /* FALLTHROUGH */ + fallthrough; /* case PIPE_BULK: */ default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c deleted file mode 100644 index dc2676320527..000000000000 --- a/drivers/usb/host/ehci-mxc.c +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/usb/otg.h> -#include <linux/usb/ulpi.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/platform_data/usb-ehci-mxc.h> -#include "ehci.h" - -#define DRIVER_DESC "Freescale On-Chip EHCI Host driver" - -static const char hcd_name[] = "ehci-mxc"; - -#define ULPI_VIEWPORT_OFFSET 0x170 - -struct ehci_mxc_priv { - struct clk *usbclk, *ahbclk, *phyclk; -}; - -static struct hc_driver __read_mostly ehci_mxc_hc_driver; - -static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = { - .extra_priv_size = sizeof(struct ehci_mxc_priv), -}; - -static int ehci_mxc_drv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev); - struct usb_hcd *hcd; - struct resource *res; - int irq, ret; - struct ehci_mxc_priv *priv; - struct ehci_hcd *ehci; - - if (!pdata) { - dev_err(dev, "No platform data given, bailing out.\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev)); - if (!hcd) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(hcd->regs)) { - ret = PTR_ERR(hcd->regs); - goto err_alloc; - } - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - hcd->has_tt = 1; - ehci = hcd_to_ehci(hcd); - priv = (struct ehci_mxc_priv *) ehci->priv; - - /* enable clocks */ - priv->usbclk = devm_clk_get(dev, "ipg"); - if (IS_ERR(priv->usbclk)) { - ret = PTR_ERR(priv->usbclk); - goto err_alloc; - } - clk_prepare_enable(priv->usbclk); - - priv->ahbclk = devm_clk_get(dev, "ahb"); - if (IS_ERR(priv->ahbclk)) { - ret = PTR_ERR(priv->ahbclk); - goto err_clk_ahb; - } - clk_prepare_enable(priv->ahbclk); - - /* "dr" device has its own clock on i.MX51 */ - priv->phyclk = devm_clk_get(dev, "phy"); - if (IS_ERR(priv->phyclk)) - priv->phyclk = NULL; - if (priv->phyclk) - clk_prepare_enable(priv->phyclk); - - /* call platform specific init function */ - if (pdata->init) { - ret = pdata->init(pdev); - if (ret) { - dev_err(dev, "platform init failed\n"); - goto err_init; - } - /* platforms need some time to settle changed IO settings */ - mdelay(10); - } - - /* EHCI registers start at offset 0x100 */ - ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* set up the PORTSCx register */ - ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); - - /* is this really needed? */ - msleep(10); - - /* Initialize the transceiver */ - if (pdata->otg) { - pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET; - ret = usb_phy_init(pdata->otg); - if (ret) { - dev_err(dev, "unable to init transceiver, probably missing\n"); - ret = -ENODEV; - goto err_add; - } - ret = otg_set_vbus(pdata->otg->otg, 1); - if (ret) { - dev_err(dev, "unable to enable vbus on transceiver\n"); - goto err_add; - } - } - - platform_set_drvdata(pdev, hcd); - - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret) - goto err_add; - - device_wakeup_enable(hcd->self.controller); - return 0; - -err_add: - if (pdata && pdata->exit) - pdata->exit(pdev); -err_init: - if (priv->phyclk) - clk_disable_unprepare(priv->phyclk); - - clk_disable_unprepare(priv->ahbclk); -err_clk_ahb: - clk_disable_unprepare(priv->usbclk); -err_alloc: - usb_put_hcd(hcd); - return ret; -} - -static int ehci_mxc_drv_remove(struct platform_device *pdev) -{ - struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv; - - usb_remove_hcd(hcd); - - if (pdata && pdata->exit) - pdata->exit(pdev); - - if (pdata && pdata->otg) - usb_phy_shutdown(pdata->otg); - - clk_disable_unprepare(priv->usbclk); - clk_disable_unprepare(priv->ahbclk); - - if (priv->phyclk) - clk_disable_unprepare(priv->phyclk); - - usb_put_hcd(hcd); - return 0; -} - -MODULE_ALIAS("platform:mxc-ehci"); - -static struct platform_driver ehci_mxc_driver = { - .probe = ehci_mxc_drv_probe, - .remove = ehci_mxc_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .driver = { - .name = "mxc-ehci", - }, -}; - -static int __init ehci_mxc_init(void) -{ - if (usb_disabled()) - return -ENODEV; - - pr_info("%s: " DRIVER_DESC "\n", hcd_name); - - ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides); - return platform_driver_register(&ehci_mxc_driver); -} -module_init(ehci_mxc_init); - -static void __exit ehci_mxc_cleanup(void) -{ - platform_driver_unregister(&ehci_mxc_driver); -} -module_exit(ehci_mxc_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Sascha Hauer"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 8771a2ed6926..7f4a03e8647a 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -220,6 +220,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) err_pm_runtime: pm_runtime_put_sync(dev); + pm_runtime_disable(dev); err_phy: for (i = 0; i < omap->nports; i++) { diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 2d462fbbe0a6..5fb92b956cc7 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -147,12 +147,14 @@ err1: /** * usb_hcd_msp_probe - initialize PMC MSP-based HCDs - * Context: !in_interrupt() + * @driver: Pointer to hc driver instance + * @dev: USB controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * */ int usb_hcd_msp_probe(const struct hc_driver *driver, struct platform_device *dev) @@ -223,8 +225,9 @@ err1: /** * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * @hcd: USB Host Controller being removed + * + * Context: task context, might sleep * * Reverses the effect of usb_hcd_msp_probe(), first invoking * the HCD's stop() method. It is always called from a thread @@ -233,7 +236,7 @@ err1: * may be called without controller electrically present * may be called with controller, bus, and devices active */ -void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev) +static void usb_hcd_msp_remove(struct usb_hcd *hcd) { usb_remove_hcd(hcd); iounmap(hcd->regs); @@ -306,7 +309,7 @@ static int ehci_hcd_msp_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - usb_hcd_msp_remove(hcd, pdev); + usb_hcd_msp_remove(hcd); /* free TWI GPIO USB_HOST_DEV pin */ gpio_free(MSP_PIN_USB0_HOST_DEV); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6dfb242f9a4b..0f85aa9b2fb1 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -244,6 +244,12 @@ static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci, /* FS/LS bus bandwidth */ if (tt_usecs) { + /* + * find_tt() will not return any error here as we have + * already called find_tt() before calling this function + * and checked for any error return. The previous call + * would have created the data structure. + */ tt = find_tt(qh->ps.udev); if (sign > 0) list_add_tail(&qh->ps.ps_list, &tt->ps_list); @@ -1337,6 +1343,12 @@ static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci, } } + /* + * find_tt() will not return any error here as we have + * already called find_tt() before calling this function + * and checked for any error return. The previous call + * would have created the data structure. + */ tt = find_tt(stream->ps.udev); if (sign > 0) list_add_tail(&stream->ps.ps_list, &tt->ps_list); diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 1d94fcfac2c2..5617ef30530a 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1951,7 +1951,7 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) goto fail; /* Hardware periodic table */ - fotg210->periodic = (__le32 *) + fotg210->periodic = dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, fotg210->periodic_size * sizeof(__le32), &fotg210->periodic_dma, 0); @@ -5276,7 +5276,7 @@ static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, */ if (urb->transfer_buffer_length > (16 * 1024)) return -EMSGSIZE; - /* FALLTHROUGH */ + fallthrough; /* case PIPE_BULK: */ default: if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c deleted file mode 100644 index 02a1344fbd6a..000000000000 --- a/drivers/usb/host/imx21-dbg.c +++ /dev/null @@ -1,439 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2009 by Martin Fuzzey - */ - -/* this file is part of imx21-hcd.c */ - -#ifdef CONFIG_DYNAMIC_DEBUG -#define DEBUG -#endif - -#ifndef DEBUG - -static inline void create_debug_files(struct imx21 *imx21) { } -static inline void remove_debug_files(struct imx21 *imx21) { } -static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {} -static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb, - int status) {} -static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {} -static inline void debug_urb_queued_for_etd(struct imx21 *imx21, - struct urb *urb) {} -static inline void debug_urb_queued_for_dmem(struct imx21 *imx21, - struct urb *urb) {} -static inline void debug_etd_allocated(struct imx21 *imx21) {} -static inline void debug_etd_freed(struct imx21 *imx21) {} -static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {} -static inline void debug_dmem_freed(struct imx21 *imx21, int size) {} -static inline void debug_isoc_submitted(struct imx21 *imx21, - int frame, struct td *td) {} -static inline void debug_isoc_completed(struct imx21 *imx21, - int frame, struct td *td, int cc, int len) {} - -#else - -#include <linux/debugfs.h> -#include <linux/seq_file.h> - -static const char *dir_labels[] = { - "TD 0", - "OUT", - "IN", - "TD 1" -}; - -static const char *speed_labels[] = { - "Full", - "Low" -}; - -static const char *format_labels[] = { - "Control", - "ISO", - "Bulk", - "Interrupt" -}; - -static inline struct debug_stats *stats_for_urb(struct imx21 *imx21, - struct urb *urb) -{ - return usb_pipeisoc(urb->pipe) ? - &imx21->isoc_stats : &imx21->nonisoc_stats; -} - -static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->submitted++; -} - -static void debug_urb_completed(struct imx21 *imx21, struct urb *urb, int st) -{ - if (st) - stats_for_urb(imx21, urb)->completed_failed++; - else - stats_for_urb(imx21, urb)->completed_ok++; -} - -static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->unlinked++; -} - -static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->queue_etd++; -} - -static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->queue_dmem++; -} - -static inline void debug_etd_allocated(struct imx21 *imx21) -{ - imx21->etd_usage.maximum = max( - ++(imx21->etd_usage.value), - imx21->etd_usage.maximum); -} - -static inline void debug_etd_freed(struct imx21 *imx21) -{ - imx21->etd_usage.value--; -} - -static inline void debug_dmem_allocated(struct imx21 *imx21, int size) -{ - imx21->dmem_usage.value += size; - imx21->dmem_usage.maximum = max( - imx21->dmem_usage.value, - imx21->dmem_usage.maximum); -} - -static inline void debug_dmem_freed(struct imx21 *imx21, int size) -{ - imx21->dmem_usage.value -= size; -} - - -static void debug_isoc_submitted(struct imx21 *imx21, - int frame, struct td *td) -{ - struct debug_isoc_trace *trace = &imx21->isoc_trace[ - imx21->isoc_trace_index++]; - - imx21->isoc_trace_index %= ARRAY_SIZE(imx21->isoc_trace); - trace->schedule_frame = td->frame; - trace->submit_frame = frame; - trace->request_len = td->len; - trace->td = td; -} - -static inline void debug_isoc_completed(struct imx21 *imx21, - int frame, struct td *td, int cc, int len) -{ - struct debug_isoc_trace *trace, *trace_failed; - int i; - int found = 0; - - trace = imx21->isoc_trace; - for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) { - if (trace->td == td) { - trace->done_frame = frame; - trace->done_len = len; - trace->cc = cc; - trace->td = NULL; - found = 1; - break; - } - } - - if (found && cc) { - trace_failed = &imx21->isoc_trace_failed[ - imx21->isoc_trace_index_failed++]; - - imx21->isoc_trace_index_failed %= ARRAY_SIZE( - imx21->isoc_trace_failed); - *trace_failed = *trace; - } -} - - -static char *format_ep(struct usb_host_endpoint *ep, char *buf, int bufsize) -{ - if (ep) - snprintf(buf, bufsize, "ep_%02x (type:%02X kaddr:%p)", - ep->desc.bEndpointAddress, - usb_endpoint_type(&ep->desc), - ep); - else - snprintf(buf, bufsize, "none"); - return buf; -} - -static char *format_etd_dword0(u32 value, char *buf, int bufsize) -{ - snprintf(buf, bufsize, - "addr=%d ep=%d dir=%s speed=%s format=%s halted=%d", - value & 0x7F, - (value >> DW0_ENDPNT) & 0x0F, - dir_labels[(value >> DW0_DIRECT) & 0x03], - speed_labels[(value >> DW0_SPEED) & 0x01], - format_labels[(value >> DW0_FORMAT) & 0x03], - (value >> DW0_HALTED) & 0x01); - return buf; -} - -static int debug_status_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - int etds_allocated = 0; - int etds_sw_busy = 0; - int etds_hw_busy = 0; - int dmem_blocks = 0; - int queued_for_etd = 0; - int queued_for_dmem = 0; - unsigned int dmem_bytes = 0; - int i; - struct etd_priv *etd; - u32 etd_enable_mask; - unsigned long flags; - struct imx21_dmem_area *dmem; - struct ep_priv *ep_priv; - - spin_lock_irqsave(&imx21->lock, flags); - - etd_enable_mask = readl(imx21->regs + USBH_ETDENSET); - for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) { - if (etd->alloc) - etds_allocated++; - if (etd->urb) - etds_sw_busy++; - if (etd_enable_mask & (1<<i)) - etds_hw_busy++; - } - - list_for_each_entry(dmem, &imx21->dmem_list, list) { - dmem_bytes += dmem->size; - dmem_blocks++; - } - - list_for_each_entry(ep_priv, &imx21->queue_for_etd, queue) - queued_for_etd++; - - list_for_each_entry(etd, &imx21->queue_for_dmem, queue) - queued_for_dmem++; - - spin_unlock_irqrestore(&imx21->lock, flags); - - seq_printf(s, - "Frame: %d\n" - "ETDs allocated: %d/%d (max=%d)\n" - "ETDs in use sw: %d\n" - "ETDs in use hw: %d\n" - "DMEM allocated: %d/%d (max=%d)\n" - "DMEM blocks: %d\n" - "Queued waiting for ETD: %d\n" - "Queued waiting for DMEM: %d\n", - readl(imx21->regs + USBH_FRMNUB) & 0xFFFF, - etds_allocated, USB_NUM_ETD, imx21->etd_usage.maximum, - etds_sw_busy, - etds_hw_busy, - dmem_bytes, DMEM_SIZE, imx21->dmem_usage.maximum, - dmem_blocks, - queued_for_etd, - queued_for_dmem); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_status); - -static int debug_dmem_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - struct imx21_dmem_area *dmem; - unsigned long flags; - char ep_text[40]; - - spin_lock_irqsave(&imx21->lock, flags); - - list_for_each_entry(dmem, &imx21->dmem_list, list) - seq_printf(s, - "%04X: size=0x%X " - "ep=%s\n", - dmem->offset, dmem->size, - format_ep(dmem->ep, ep_text, sizeof(ep_text))); - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_dmem); - -static int debug_etd_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - struct etd_priv *etd; - char buf[60]; - u32 dword; - int i, j; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) { - int state = -1; - struct urb_priv *urb_priv; - if (etd->urb) { - urb_priv = etd->urb->hcpriv; - if (urb_priv) - state = urb_priv->state; - } - - seq_printf(s, - "etd_num: %d\n" - "ep: %s\n" - "alloc: %d\n" - "len: %d\n" - "busy sw: %d\n" - "busy hw: %d\n" - "urb state: %d\n" - "current urb: %p\n", - - i, - format_ep(etd->ep, buf, sizeof(buf)), - etd->alloc, - etd->len, - etd->urb != NULL, - (readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0, - state, - etd->urb); - - for (j = 0; j < 4; j++) { - dword = etd_readl(imx21, i, j); - switch (j) { - case 0: - format_etd_dword0(dword, buf, sizeof(buf)); - break; - case 2: - snprintf(buf, sizeof(buf), - "cc=0X%02X", dword >> DW2_COMPCODE); - break; - default: - *buf = 0; - break; - } - seq_printf(s, - "dword %d: submitted=%08X cur=%08X [%s]\n", - j, - etd->submitted_dwords[j], - dword, - buf); - } - seq_printf(s, "\n"); - } - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_etd); - -static void debug_statistics_show_one(struct seq_file *s, - const char *name, struct debug_stats *stats) -{ - seq_printf(s, "%s:\n" - "submitted URBs: %lu\n" - "completed OK: %lu\n" - "completed failed: %lu\n" - "unlinked: %lu\n" - "queued for ETD: %lu\n" - "queued for DMEM: %lu\n\n", - name, - stats->submitted, - stats->completed_ok, - stats->completed_failed, - stats->unlinked, - stats->queue_etd, - stats->queue_dmem); -} - -static int debug_statistics_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats); - debug_statistics_show_one(s, "isoc", &imx21->isoc_stats); - seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks); - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_statistics); - -static void debug_isoc_show_one(struct seq_file *s, - const char *name, int index, struct debug_isoc_trace *trace) -{ - seq_printf(s, "%s %d:\n" - "cc=0X%02X\n" - "scheduled frame %d (%d)\n" - "submitted frame %d (%d)\n" - "completed frame %d (%d)\n" - "requested length=%d\n" - "completed length=%d\n\n", - name, index, - trace->cc, - trace->schedule_frame, trace->schedule_frame & 0xFFFF, - trace->submit_frame, trace->submit_frame & 0xFFFF, - trace->done_frame, trace->done_frame & 0xFFFF, - trace->request_len, - trace->done_len); -} - -static int debug_isoc_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - struct debug_isoc_trace *trace; - unsigned long flags; - int i; - - spin_lock_irqsave(&imx21->lock, flags); - - trace = imx21->isoc_trace_failed; - for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++) - debug_isoc_show_one(s, "isoc failed", i, trace); - - trace = imx21->isoc_trace; - for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) - debug_isoc_show_one(s, "isoc", i, trace); - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_isoc); - -static void create_debug_files(struct imx21 *imx21) -{ - struct dentry *root; - - root = debugfs_create_dir(dev_name(imx21->dev), usb_debug_root); - imx21->debug_root = root; - - debugfs_create_file("status", S_IRUGO, root, imx21, &debug_status_fops); - debugfs_create_file("dmem", S_IRUGO, root, imx21, &debug_dmem_fops); - debugfs_create_file("etd", S_IRUGO, root, imx21, &debug_etd_fops); - debugfs_create_file("statistics", S_IRUGO, root, imx21, - &debug_statistics_fops); - debugfs_create_file("isoc", S_IRUGO, root, imx21, &debug_isoc_fops); -} - -static void remove_debug_files(struct imx21 *imx21) -{ - debugfs_remove_recursive(imx21->debug_root); -} - -#endif - diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c deleted file mode 100644 index b2716cb72471..000000000000 --- a/drivers/usb/host/imx21-hcd.c +++ /dev/null @@ -1,1933 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * USB Host Controller Driver for IMX21 - * - * Copyright (C) 2006 Loping Dog Embedded Systems - * Copyright (C) 2009 Martin Fuzzey - * Originally written by Jay Monkman <jtm@lopingdog.com> - * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey - */ - - - /* - * The i.MX21 USB hardware contains - * * 32 transfer descriptors (called ETDs) - * * 4Kb of Data memory - * - * The data memory is shared between the host and function controllers - * (but this driver only supports the host controller) - * - * So setting up a transfer involves: - * * Allocating a ETD - * * Fill in ETD with appropriate information - * * Allocating data memory (and putting the offset in the ETD) - * * Activate the ETD - * * Get interrupt when done. - * - * An ETD is assigned to each active endpoint. - * - * Low resource (ETD and Data memory) situations are handled differently for - * isochronous and non insosynchronous transactions : - * - * Non ISOC transfers are queued if either ETDs or Data memory are unavailable - * - * ISOC transfers use 2 ETDs per endpoint to achieve double buffering. - * They allocate both ETDs and Data memory during URB submission - * (and fail if unavailable). - */ - -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/dma-mapping.h> -#include <linux/module.h> - -#include "imx21-hcd.h" - -#ifdef CONFIG_DYNAMIC_DEBUG -#define DEBUG -#endif - -#ifdef DEBUG -#define DEBUG_LOG_FRAME(imx21, etd, event) \ - (etd)->event##_frame = readl((imx21)->regs + USBH_FRMNUB) -#else -#define DEBUG_LOG_FRAME(imx21, etd, event) do { } while (0) -#endif - -static const char hcd_name[] = "imx21-hcd"; - -static inline struct imx21 *hcd_to_imx21(struct usb_hcd *hcd) -{ - return (struct imx21 *)hcd->hcd_priv; -} - - -/* =========================================== */ -/* Hardware access helpers */ -/* =========================================== */ - -static inline void set_register_bits(struct imx21 *imx21, u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - writel(readl(reg) | mask, reg); -} - -static inline void clear_register_bits(struct imx21 *imx21, - u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - writel(readl(reg) & ~mask, reg); -} - -static inline void clear_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - - if (readl(reg) & mask) - writel(mask, reg); -} - -static inline void set_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - - if (!(readl(reg) & mask)) - writel(mask, reg); -} - -static void etd_writel(struct imx21 *imx21, int etd_num, int dword, u32 value) -{ - writel(value, imx21->regs + USB_ETD_DWORD(etd_num, dword)); -} - -static u32 etd_readl(struct imx21 *imx21, int etd_num, int dword) -{ - return readl(imx21->regs + USB_ETD_DWORD(etd_num, dword)); -} - -static inline int wrap_frame(int counter) -{ - return counter & 0xFFFF; -} - -static inline int frame_after(int frame, int after) -{ - /* handle wrapping like jiffies time_afer */ - return (s16)((s16)after - (s16)frame) < 0; -} - -static int imx21_hc_get_frame(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - - return wrap_frame(readl(imx21->regs + USBH_FRMNUB)); -} - -static inline bool unsuitable_for_dma(dma_addr_t addr) -{ - return (addr & 3) != 0; -} - -#include "imx21-dbg.c" - -static void nonisoc_urb_completed_for_etd( - struct imx21 *imx21, struct etd_priv *etd, int status); -static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb); -static void free_dmem(struct imx21 *imx21, struct etd_priv *etd); - -/* =========================================== */ -/* ETD management */ -/* =========================================== */ - -static int alloc_etd(struct imx21 *imx21) -{ - int i; - struct etd_priv *etd = imx21->etd; - - for (i = 0; i < USB_NUM_ETD; i++, etd++) { - if (etd->alloc == 0) { - memset(etd, 0, sizeof(imx21->etd[0])); - etd->alloc = 1; - debug_etd_allocated(imx21); - return i; - } - } - return -1; -} - -static void disactivate_etd(struct imx21 *imx21, int num) -{ - int etd_mask = (1 << num); - struct etd_priv *etd = &imx21->etd[num]; - - writel(etd_mask, imx21->regs + USBH_ETDENCLR); - clear_register_bits(imx21, USBH_ETDDONEEN, etd_mask); - writel(etd_mask, imx21->regs + USB_ETDDMACHANLCLR); - clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask); - - etd->active_count = 0; - - DEBUG_LOG_FRAME(imx21, etd, disactivated); -} - -static void reset_etd(struct imx21 *imx21, int num) -{ - struct etd_priv *etd = imx21->etd + num; - int i; - - disactivate_etd(imx21, num); - - for (i = 0; i < 4; i++) - etd_writel(imx21, num, i, 0); - etd->urb = NULL; - etd->ep = NULL; - etd->td = NULL; - etd->bounce_buffer = NULL; -} - -static void free_etd(struct imx21 *imx21, int num) -{ - if (num < 0) - return; - - if (num >= USB_NUM_ETD) { - dev_err(imx21->dev, "BAD etd=%d!\n", num); - return; - } - if (imx21->etd[num].alloc == 0) { - dev_err(imx21->dev, "ETD %d already free!\n", num); - return; - } - - debug_etd_freed(imx21); - reset_etd(imx21, num); - memset(&imx21->etd[num], 0, sizeof(imx21->etd[0])); -} - - -static void setup_etd_dword0(struct imx21 *imx21, - int etd_num, struct urb *urb, u8 dir, u16 maxpacket) -{ - etd_writel(imx21, etd_num, 0, - ((u32) usb_pipedevice(urb->pipe)) << DW0_ADDRESS | - ((u32) usb_pipeendpoint(urb->pipe) << DW0_ENDPNT) | - ((u32) dir << DW0_DIRECT) | - ((u32) ((urb->dev->speed == USB_SPEED_LOW) ? - 1 : 0) << DW0_SPEED) | - ((u32) fmt_urb_to_etd[usb_pipetype(urb->pipe)] << DW0_FORMAT) | - ((u32) maxpacket << DW0_MAXPKTSIZ)); -} - -/* - * Copy buffer to data controller data memory. - * We cannot use memcpy_toio() because the hardware requires 32bit writes - */ -static void copy_to_dmem( - struct imx21 *imx21, int dmem_offset, void *src, int count) -{ - void __iomem *dmem = imx21->regs + USBOTG_DMEM + dmem_offset; - u32 word = 0; - u8 *p = src; - int byte = 0; - int i; - - for (i = 0; i < count; i++) { - byte = i % 4; - word += (*p++ << (byte * 8)); - if (byte == 3) { - writel(word, dmem); - dmem += 4; - word = 0; - } - } - - if (count && byte != 3) - writel(word, dmem); -} - -static void activate_etd(struct imx21 *imx21, int etd_num, u8 dir) -{ - u32 etd_mask = 1 << etd_num; - struct etd_priv *etd = &imx21->etd[etd_num]; - - if (etd->dma_handle && unsuitable_for_dma(etd->dma_handle)) { - /* For non aligned isoc the condition below is always true */ - if (etd->len <= etd->dmem_size) { - /* Fits into data memory, use PIO */ - if (dir != TD_DIR_IN) { - copy_to_dmem(imx21, - etd->dmem_offset, - etd->cpu_buffer, etd->len); - } - etd->dma_handle = 0; - - } else { - /* Too big for data memory, use bounce buffer */ - enum dma_data_direction dmadir; - - if (dir == TD_DIR_IN) { - dmadir = DMA_FROM_DEVICE; - etd->bounce_buffer = kmalloc(etd->len, - GFP_ATOMIC); - } else { - dmadir = DMA_TO_DEVICE; - etd->bounce_buffer = kmemdup(etd->cpu_buffer, - etd->len, - GFP_ATOMIC); - } - if (!etd->bounce_buffer) { - dev_err(imx21->dev, "failed bounce alloc\n"); - goto err_bounce_alloc; - } - - etd->dma_handle = - dma_map_single(imx21->dev, - etd->bounce_buffer, - etd->len, - dmadir); - if (dma_mapping_error(imx21->dev, etd->dma_handle)) { - dev_err(imx21->dev, "failed bounce map\n"); - goto err_bounce_map; - } - } - } - - clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask); - set_register_bits(imx21, USBH_ETDDONEEN, etd_mask); - clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask); - - if (etd->dma_handle) { - set_register_bits(imx21, USB_ETDDMACHANLCLR, etd_mask); - clear_toggle_bit(imx21, USBH_XBUFSTAT, etd_mask); - clear_toggle_bit(imx21, USBH_YBUFSTAT, etd_mask); - writel(etd->dma_handle, imx21->regs + USB_ETDSMSA(etd_num)); - set_register_bits(imx21, USB_ETDDMAEN, etd_mask); - } else { - if (dir != TD_DIR_IN) { - /* need to set for ZLP and PIO */ - set_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - set_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask); - } - } - - DEBUG_LOG_FRAME(imx21, etd, activated); - -#ifdef DEBUG - if (!etd->active_count) { - int i; - etd->activated_frame = readl(imx21->regs + USBH_FRMNUB); - etd->disactivated_frame = -1; - etd->last_int_frame = -1; - etd->last_req_frame = -1; - - for (i = 0; i < 4; i++) - etd->submitted_dwords[i] = etd_readl(imx21, etd_num, i); - } -#endif - - etd->active_count = 1; - writel(etd_mask, imx21->regs + USBH_ETDENSET); - return; - -err_bounce_map: - kfree(etd->bounce_buffer); - -err_bounce_alloc: - free_dmem(imx21, etd); - nonisoc_urb_completed_for_etd(imx21, etd, -ENOMEM); -} - -/* =========================================== */ -/* Data memory management */ -/* =========================================== */ - -static int alloc_dmem(struct imx21 *imx21, unsigned int size, - struct usb_host_endpoint *ep) -{ - unsigned int offset = 0; - struct imx21_dmem_area *area; - struct imx21_dmem_area *tmp; - - size += (~size + 1) & 0x3; /* Round to 4 byte multiple */ - - if (size > DMEM_SIZE) { - dev_err(imx21->dev, "size=%d > DMEM_SIZE(%d)\n", - size, DMEM_SIZE); - return -EINVAL; - } - - list_for_each_entry(tmp, &imx21->dmem_list, list) { - if ((size + offset) < offset) - goto fail; - if ((size + offset) <= tmp->offset) - break; - offset = tmp->size + tmp->offset; - if ((offset + size) > DMEM_SIZE) - goto fail; - } - - area = kmalloc(sizeof(struct imx21_dmem_area), GFP_ATOMIC); - if (area == NULL) - return -ENOMEM; - - area->ep = ep; - area->offset = offset; - area->size = size; - list_add_tail(&area->list, &tmp->list); - debug_dmem_allocated(imx21, size); - return offset; - -fail: - return -ENOMEM; -} - -/* Memory now available for a queued ETD - activate it */ -static void activate_queued_etd(struct imx21 *imx21, - struct etd_priv *etd, u32 dmem_offset) -{ - struct urb_priv *urb_priv = etd->urb->hcpriv; - int etd_num = etd - &imx21->etd[0]; - u32 maxpacket = etd_readl(imx21, etd_num, 1) >> DW1_YBUFSRTAD; - u8 dir = (etd_readl(imx21, etd_num, 2) >> DW2_DIRPID) & 0x03; - - dev_dbg(imx21->dev, "activating queued ETD %d now DMEM available\n", - etd_num); - etd_writel(imx21, etd_num, 1, - ((dmem_offset + maxpacket) << DW1_YBUFSRTAD) | dmem_offset); - - etd->dmem_offset = dmem_offset; - urb_priv->active = 1; - activate_etd(imx21, etd_num, dir); -} - -static void free_dmem(struct imx21 *imx21, struct etd_priv *etd) -{ - struct imx21_dmem_area *area; - struct etd_priv *tmp; - int found = 0; - int offset; - - if (!etd->dmem_size) - return; - etd->dmem_size = 0; - - offset = etd->dmem_offset; - list_for_each_entry(area, &imx21->dmem_list, list) { - if (area->offset == offset) { - debug_dmem_freed(imx21, area->size); - list_del(&area->list); - kfree(area); - found = 1; - break; - } - } - - if (!found) { - dev_err(imx21->dev, - "Trying to free unallocated DMEM %d\n", offset); - return; - } - - /* Try again to allocate memory for anything we've queued */ - list_for_each_entry_safe(etd, tmp, &imx21->queue_for_dmem, queue) { - offset = alloc_dmem(imx21, etd->dmem_size, etd->ep); - if (offset >= 0) { - list_del(&etd->queue); - activate_queued_etd(imx21, etd, (u32)offset); - } - } -} - -static void free_epdmem(struct imx21 *imx21, struct usb_host_endpoint *ep) -{ - struct imx21_dmem_area *area, *tmp; - - list_for_each_entry_safe(area, tmp, &imx21->dmem_list, list) { - if (area->ep == ep) { - dev_err(imx21->dev, - "Active DMEM %d for disabled ep=%p\n", - area->offset, ep); - list_del(&area->list); - kfree(area); - } - } -} - - -/* =========================================== */ -/* End handling */ -/* =========================================== */ - -/* Endpoint now idle - release its ETD(s) or assign to queued request */ -static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv) -{ - int i; - - for (i = 0; i < NUM_ISO_ETDS; i++) { - int etd_num = ep_priv->etd[i]; - struct etd_priv *etd; - if (etd_num < 0) - continue; - - etd = &imx21->etd[etd_num]; - ep_priv->etd[i] = -1; - - free_dmem(imx21, etd); /* for isoc */ - - if (list_empty(&imx21->queue_for_etd)) { - free_etd(imx21, etd_num); - continue; - } - - dev_dbg(imx21->dev, - "assigning idle etd %d for queued request\n", etd_num); - ep_priv = list_first_entry(&imx21->queue_for_etd, - struct ep_priv, queue); - list_del(&ep_priv->queue); - reset_etd(imx21, etd_num); - ep_priv->waiting_etd = 0; - ep_priv->etd[i] = etd_num; - - if (list_empty(&ep_priv->ep->urb_list)) { - dev_err(imx21->dev, "No urb for queued ep!\n"); - continue; - } - schedule_nonisoc_etd(imx21, list_first_entry( - &ep_priv->ep->urb_list, struct urb, urb_list)); - } -} - -static void urb_done(struct usb_hcd *hcd, struct urb *urb, int status) -__releases(imx21->lock) -__acquires(imx21->lock) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct ep_priv *ep_priv = urb->ep->hcpriv; - struct urb_priv *urb_priv = urb->hcpriv; - - debug_urb_completed(imx21, urb, status); - dev_vdbg(imx21->dev, "urb %p done %d\n", urb, status); - - kfree(urb_priv->isoc_td); - kfree(urb->hcpriv); - urb->hcpriv = NULL; - usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&imx21->lock); - usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&imx21->lock); - if (list_empty(&ep_priv->ep->urb_list)) - ep_idle(imx21, ep_priv); -} - -static void nonisoc_urb_completed_for_etd( - struct imx21 *imx21, struct etd_priv *etd, int status) -{ - struct usb_host_endpoint *ep = etd->ep; - - urb_done(imx21->hcd, etd->urb, status); - etd->urb = NULL; - - if (!list_empty(&ep->urb_list)) { - struct urb *urb = list_first_entry( - &ep->urb_list, struct urb, urb_list); - - dev_vdbg(imx21->dev, "next URB %p\n", urb); - schedule_nonisoc_etd(imx21, urb); - } -} - - -/* =========================================== */ -/* ISOC Handling ... */ -/* =========================================== */ - -static void schedule_isoc_etds(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct ep_priv *ep_priv = ep->hcpriv; - struct etd_priv *etd; - struct urb_priv *urb_priv; - struct td *td; - int etd_num; - int i; - int cur_frame; - u8 dir; - - for (i = 0; i < NUM_ISO_ETDS; i++) { -too_late: - if (list_empty(&ep_priv->td_list)) - break; - - etd_num = ep_priv->etd[i]; - if (etd_num < 0) - break; - - etd = &imx21->etd[etd_num]; - if (etd->urb) - continue; - - td = list_entry(ep_priv->td_list.next, struct td, list); - list_del(&td->list); - urb_priv = td->urb->hcpriv; - - cur_frame = imx21_hc_get_frame(hcd); - if (frame_after(cur_frame, td->frame)) { - dev_dbg(imx21->dev, "isoc too late frame %d > %d\n", - cur_frame, td->frame); - urb_priv->isoc_status = -EXDEV; - td->urb->iso_frame_desc[ - td->isoc_index].actual_length = 0; - td->urb->iso_frame_desc[td->isoc_index].status = -EXDEV; - if (--urb_priv->isoc_remaining == 0) - urb_done(hcd, td->urb, urb_priv->isoc_status); - goto too_late; - } - - urb_priv->active = 1; - etd->td = td; - etd->ep = td->ep; - etd->urb = td->urb; - etd->len = td->len; - etd->dma_handle = td->dma_handle; - etd->cpu_buffer = td->cpu_buffer; - - debug_isoc_submitted(imx21, cur_frame, td); - - dir = usb_pipeout(td->urb->pipe) ? TD_DIR_OUT : TD_DIR_IN; - setup_etd_dword0(imx21, etd_num, td->urb, dir, etd->dmem_size); - etd_writel(imx21, etd_num, 1, etd->dmem_offset); - etd_writel(imx21, etd_num, 2, - (TD_NOTACCESSED << DW2_COMPCODE) | - ((td->frame & 0xFFFF) << DW2_STARTFRM)); - etd_writel(imx21, etd_num, 3, - (TD_NOTACCESSED << DW3_COMPCODE0) | - (td->len << DW3_PKTLEN0)); - - activate_etd(imx21, etd_num, dir); - } -} - -static void isoc_etd_done(struct usb_hcd *hcd, int etd_num) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - int etd_mask = 1 << etd_num; - struct etd_priv *etd = imx21->etd + etd_num; - struct urb *urb = etd->urb; - struct urb_priv *urb_priv = urb->hcpriv; - struct td *td = etd->td; - struct usb_host_endpoint *ep = etd->ep; - int isoc_index = td->isoc_index; - unsigned int pipe = urb->pipe; - int dir_in = usb_pipein(pipe); - int cc; - int bytes_xfrd; - - disactivate_etd(imx21, etd_num); - - cc = (etd_readl(imx21, etd_num, 3) >> DW3_COMPCODE0) & 0xf; - bytes_xfrd = etd_readl(imx21, etd_num, 3) & 0x3ff; - - /* Input doesn't always fill the buffer, don't generate an error - * when this happens. - */ - if (dir_in && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - if (cc == TD_NOTACCESSED) - bytes_xfrd = 0; - - debug_isoc_completed(imx21, - imx21_hc_get_frame(hcd), td, cc, bytes_xfrd); - if (cc) { - urb_priv->isoc_status = -EXDEV; - dev_dbg(imx21->dev, - "bad iso cc=0x%X frame=%d sched frame=%d " - "cnt=%d len=%d urb=%p etd=%d index=%d\n", - cc, imx21_hc_get_frame(hcd), td->frame, - bytes_xfrd, td->len, urb, etd_num, isoc_index); - } - - if (dir_in) { - clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - if (!etd->dma_handle) - memcpy_fromio(etd->cpu_buffer, - imx21->regs + USBOTG_DMEM + etd->dmem_offset, - bytes_xfrd); - } - - urb->actual_length += bytes_xfrd; - urb->iso_frame_desc[isoc_index].actual_length = bytes_xfrd; - urb->iso_frame_desc[isoc_index].status = cc_to_error[cc]; - - etd->td = NULL; - etd->urb = NULL; - etd->ep = NULL; - - if (--urb_priv->isoc_remaining == 0) - urb_done(hcd, urb, urb_priv->isoc_status); - - schedule_isoc_etds(hcd, ep); -} - -static struct ep_priv *alloc_isoc_ep( - struct imx21 *imx21, struct usb_host_endpoint *ep) -{ - struct ep_priv *ep_priv; - int i; - - ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC); - if (!ep_priv) - return NULL; - - for (i = 0; i < NUM_ISO_ETDS; i++) - ep_priv->etd[i] = -1; - - INIT_LIST_HEAD(&ep_priv->td_list); - ep_priv->ep = ep; - ep->hcpriv = ep_priv; - return ep_priv; -} - -static int alloc_isoc_etds(struct imx21 *imx21, struct ep_priv *ep_priv) -{ - int i, j; - int etd_num; - - /* Allocate the ETDs if required */ - for (i = 0; i < NUM_ISO_ETDS; i++) { - if (ep_priv->etd[i] < 0) { - etd_num = alloc_etd(imx21); - if (etd_num < 0) - goto alloc_etd_failed; - - ep_priv->etd[i] = etd_num; - imx21->etd[etd_num].ep = ep_priv->ep; - } - } - return 0; - -alloc_etd_failed: - dev_err(imx21->dev, "isoc: Couldn't allocate etd\n"); - for (j = 0; j < i; j++) { - free_etd(imx21, ep_priv->etd[j]); - ep_priv->etd[j] = -1; - } - return -ENOMEM; -} - -static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd, - struct usb_host_endpoint *ep, - struct urb *urb, gfp_t mem_flags) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct urb_priv *urb_priv; - unsigned long flags; - struct ep_priv *ep_priv; - struct td *td = NULL; - int i; - int ret; - int cur_frame; - u16 maxpacket; - - urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags); - if (urb_priv == NULL) - return -ENOMEM; - - urb_priv->isoc_td = kcalloc(urb->number_of_packets, sizeof(struct td), - mem_flags); - if (urb_priv->isoc_td == NULL) { - ret = -ENOMEM; - goto alloc_td_failed; - } - - spin_lock_irqsave(&imx21->lock, flags); - - if (ep->hcpriv == NULL) { - ep_priv = alloc_isoc_ep(imx21, ep); - if (ep_priv == NULL) { - ret = -ENOMEM; - goto alloc_ep_failed; - } - } else { - ep_priv = ep->hcpriv; - } - - ret = alloc_isoc_etds(imx21, ep_priv); - if (ret) - goto alloc_etd_failed; - - ret = usb_hcd_link_urb_to_ep(hcd, urb); - if (ret) - goto link_failed; - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - urb->hcpriv = urb_priv; - urb_priv->ep = ep; - - /* allocate data memory for largest packets if not already done */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - for (i = 0; i < NUM_ISO_ETDS; i++) { - struct etd_priv *etd = &imx21->etd[ep_priv->etd[i]]; - - if (etd->dmem_size > 0 && etd->dmem_size < maxpacket) { - /* not sure if this can really occur.... */ - dev_err(imx21->dev, "increasing isoc buffer %d->%d\n", - etd->dmem_size, maxpacket); - ret = -EMSGSIZE; - goto alloc_dmem_failed; - } - - if (etd->dmem_size == 0) { - etd->dmem_offset = alloc_dmem(imx21, maxpacket, ep); - if (etd->dmem_offset < 0) { - dev_dbg(imx21->dev, "failed alloc isoc dmem\n"); - ret = -EAGAIN; - goto alloc_dmem_failed; - } - etd->dmem_size = maxpacket; - } - } - - /* calculate frame */ - cur_frame = imx21_hc_get_frame(hcd); - i = 0; - if (list_empty(&ep_priv->td_list)) { - urb->start_frame = wrap_frame(cur_frame + 5); - } else { - urb->start_frame = wrap_frame(list_entry(ep_priv->td_list.prev, - struct td, list)->frame + urb->interval); - - if (frame_after(cur_frame, urb->start_frame)) { - dev_dbg(imx21->dev, - "enqueue: adjusting iso start %d (cur=%d) asap=%d\n", - urb->start_frame, cur_frame, - (urb->transfer_flags & URB_ISO_ASAP) != 0); - i = DIV_ROUND_UP(wrap_frame( - cur_frame - urb->start_frame), - urb->interval); - - /* Treat underruns as if URB_ISO_ASAP was set */ - if ((urb->transfer_flags & URB_ISO_ASAP) || - i >= urb->number_of_packets) { - urb->start_frame = wrap_frame(urb->start_frame - + i * urb->interval); - i = 0; - } - } - } - - /* set up transfers */ - urb_priv->isoc_remaining = urb->number_of_packets - i; - td = urb_priv->isoc_td; - for (; i < urb->number_of_packets; i++, td++) { - unsigned int offset = urb->iso_frame_desc[i].offset; - td->ep = ep; - td->urb = urb; - td->len = urb->iso_frame_desc[i].length; - td->isoc_index = i; - td->frame = wrap_frame(urb->start_frame + urb->interval * i); - td->dma_handle = urb->transfer_dma + offset; - td->cpu_buffer = urb->transfer_buffer + offset; - list_add_tail(&td->list, &ep_priv->td_list); - } - - dev_vdbg(imx21->dev, "setup %d packets for iso frame %d->%d\n", - urb->number_of_packets, urb->start_frame, td->frame); - - debug_urb_submitted(imx21, urb); - schedule_isoc_etds(hcd, ep); - - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; - -alloc_dmem_failed: - usb_hcd_unlink_urb_from_ep(hcd, urb); - -link_failed: -alloc_etd_failed: -alloc_ep_failed: - spin_unlock_irqrestore(&imx21->lock, flags); - kfree(urb_priv->isoc_td); - -alloc_td_failed: - kfree(urb_priv); - return ret; -} - -static void dequeue_isoc_urb(struct imx21 *imx21, - struct urb *urb, struct ep_priv *ep_priv) -{ - struct urb_priv *urb_priv = urb->hcpriv; - struct td *td, *tmp; - int i; - - if (urb_priv->active) { - for (i = 0; i < NUM_ISO_ETDS; i++) { - int etd_num = ep_priv->etd[i]; - if (etd_num != -1 && imx21->etd[etd_num].urb == urb) { - struct etd_priv *etd = imx21->etd + etd_num; - - reset_etd(imx21, etd_num); - free_dmem(imx21, etd); - } - } - } - - list_for_each_entry_safe(td, tmp, &ep_priv->td_list, list) { - if (td->urb == urb) { - dev_vdbg(imx21->dev, "removing td %p\n", td); - list_del(&td->list); - } - } -} - -/* =========================================== */ -/* NON ISOC Handling ... */ -/* =========================================== */ - -static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb) -{ - unsigned int pipe = urb->pipe; - struct urb_priv *urb_priv = urb->hcpriv; - struct ep_priv *ep_priv = urb_priv->ep->hcpriv; - int state = urb_priv->state; - int etd_num = ep_priv->etd[0]; - struct etd_priv *etd; - u32 count; - u16 etd_buf_size; - u16 maxpacket; - u8 dir; - u8 bufround; - u8 datatoggle; - u8 interval = 0; - u8 relpolpos = 0; - - if (etd_num < 0) { - dev_err(imx21->dev, "No valid ETD\n"); - return; - } - if (readl(imx21->regs + USBH_ETDENSET) & (1 << etd_num)) - dev_err(imx21->dev, "submitting to active ETD %d\n", etd_num); - - etd = &imx21->etd[etd_num]; - maxpacket = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)); - if (!maxpacket) - maxpacket = 8; - - if (usb_pipecontrol(pipe) && (state != US_CTRL_DATA)) { - if (state == US_CTRL_SETUP) { - dir = TD_DIR_SETUP; - if (unsuitable_for_dma(urb->setup_dma)) - usb_hcd_unmap_urb_setup_for_dma(imx21->hcd, - urb); - etd->dma_handle = urb->setup_dma; - etd->cpu_buffer = urb->setup_packet; - bufround = 0; - count = 8; - datatoggle = TD_TOGGLE_DATA0; - } else { /* US_CTRL_ACK */ - dir = usb_pipeout(pipe) ? TD_DIR_IN : TD_DIR_OUT; - bufround = 0; - count = 0; - datatoggle = TD_TOGGLE_DATA1; - } - } else { - dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN; - bufround = (dir == TD_DIR_IN) ? 1 : 0; - if (unsuitable_for_dma(urb->transfer_dma)) - usb_hcd_unmap_urb_for_dma(imx21->hcd, urb); - - etd->dma_handle = urb->transfer_dma; - etd->cpu_buffer = urb->transfer_buffer; - if (usb_pipebulk(pipe) && (state == US_BULK0)) - count = 0; - else - count = urb->transfer_buffer_length; - - if (usb_pipecontrol(pipe)) { - datatoggle = TD_TOGGLE_DATA1; - } else { - if (usb_gettoggle( - urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe))) - datatoggle = TD_TOGGLE_DATA1; - else - datatoggle = TD_TOGGLE_DATA0; - } - } - - etd->urb = urb; - etd->ep = urb_priv->ep; - etd->len = count; - - if (usb_pipeint(pipe)) { - interval = urb->interval; - relpolpos = (readl(imx21->regs + USBH_FRMNUB) + 1) & 0xff; - } - - /* Write ETD to device memory */ - setup_etd_dword0(imx21, etd_num, urb, dir, maxpacket); - - etd_writel(imx21, etd_num, 2, - (u32) interval << DW2_POLINTERV | - ((u32) relpolpos << DW2_RELPOLPOS) | - ((u32) dir << DW2_DIRPID) | - ((u32) bufround << DW2_BUFROUND) | - ((u32) datatoggle << DW2_DATATOG) | - ((u32) TD_NOTACCESSED << DW2_COMPCODE)); - - /* DMA will always transfer buffer size even if TOBYCNT in DWORD3 - is smaller. Make sure we don't overrun the buffer! - */ - if (count && count < maxpacket) - etd_buf_size = count; - else - etd_buf_size = maxpacket; - - etd_writel(imx21, etd_num, 3, - ((u32) (etd_buf_size - 1) << DW3_BUFSIZE) | (u32) count); - - if (!count) - etd->dma_handle = 0; - - /* allocate x and y buffer space at once */ - etd->dmem_size = (count > maxpacket) ? maxpacket * 2 : maxpacket; - etd->dmem_offset = alloc_dmem(imx21, etd->dmem_size, urb_priv->ep); - if (etd->dmem_offset < 0) { - /* Setup everything we can in HW and update when we get DMEM */ - etd_writel(imx21, etd_num, 1, (u32)maxpacket << 16); - - dev_dbg(imx21->dev, "Queuing etd %d for DMEM\n", etd_num); - debug_urb_queued_for_dmem(imx21, urb); - list_add_tail(&etd->queue, &imx21->queue_for_dmem); - return; - } - - etd_writel(imx21, etd_num, 1, - (((u32) etd->dmem_offset + (u32) maxpacket) << DW1_YBUFSRTAD) | - (u32) etd->dmem_offset); - - urb_priv->active = 1; - - /* enable the ETD to kick off transfer */ - dev_vdbg(imx21->dev, "Activating etd %d for %d bytes %s\n", - etd_num, count, dir != TD_DIR_IN ? "out" : "in"); - activate_etd(imx21, etd_num, dir); - -} - -static void nonisoc_etd_done(struct usb_hcd *hcd, int etd_num) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct etd_priv *etd = &imx21->etd[etd_num]; - struct urb *urb = etd->urb; - u32 etd_mask = 1 << etd_num; - struct urb_priv *urb_priv = urb->hcpriv; - int dir; - int cc; - u32 bytes_xfrd; - int etd_done; - - disactivate_etd(imx21, etd_num); - - dir = (etd_readl(imx21, etd_num, 0) >> DW0_DIRECT) & 0x3; - cc = (etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE) & 0xf; - bytes_xfrd = etd->len - (etd_readl(imx21, etd_num, 3) & 0x1fffff); - - /* save toggle carry */ - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), - (etd_readl(imx21, etd_num, 0) >> DW0_TOGCRY) & 0x1); - - if (dir == TD_DIR_IN) { - clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask); - - if (etd->bounce_buffer) { - memcpy(etd->cpu_buffer, etd->bounce_buffer, bytes_xfrd); - dma_unmap_single(imx21->dev, - etd->dma_handle, etd->len, DMA_FROM_DEVICE); - } else if (!etd->dma_handle && bytes_xfrd) {/* PIO */ - memcpy_fromio(etd->cpu_buffer, - imx21->regs + USBOTG_DMEM + etd->dmem_offset, - bytes_xfrd); - } - } - - kfree(etd->bounce_buffer); - etd->bounce_buffer = NULL; - free_dmem(imx21, etd); - - urb->error_count = 0; - if (!(urb->transfer_flags & URB_SHORT_NOT_OK) - && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - if (cc != 0) - dev_vdbg(imx21->dev, "cc is 0x%x\n", cc); - - etd_done = (cc_to_error[cc] != 0); /* stop if error */ - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - switch (urb_priv->state) { - case US_CTRL_SETUP: - if (urb->transfer_buffer_length > 0) - urb_priv->state = US_CTRL_DATA; - else - urb_priv->state = US_CTRL_ACK; - break; - case US_CTRL_DATA: - urb->actual_length += bytes_xfrd; - urb_priv->state = US_CTRL_ACK; - break; - case US_CTRL_ACK: - etd_done = 1; - break; - default: - dev_err(imx21->dev, - "Invalid pipe state %d\n", urb_priv->state); - etd_done = 1; - break; - } - break; - - case PIPE_BULK: - urb->actual_length += bytes_xfrd; - if ((urb_priv->state == US_BULK) - && (urb->transfer_flags & URB_ZERO_PACKET) - && urb->transfer_buffer_length > 0 - && ((urb->transfer_buffer_length % - usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe))) == 0)) { - /* need a 0-packet */ - urb_priv->state = US_BULK0; - } else { - etd_done = 1; - } - break; - - case PIPE_INTERRUPT: - urb->actual_length += bytes_xfrd; - etd_done = 1; - break; - } - - if (etd_done) - nonisoc_urb_completed_for_etd(imx21, etd, cc_to_error[cc]); - else { - dev_vdbg(imx21->dev, "next state=%d\n", urb_priv->state); - schedule_nonisoc_etd(imx21, urb); - } -} - - -static struct ep_priv *alloc_ep(void) -{ - int i; - struct ep_priv *ep_priv; - - ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC); - if (!ep_priv) - return NULL; - - for (i = 0; i < NUM_ISO_ETDS; ++i) - ep_priv->etd[i] = -1; - - return ep_priv; -} - -static int imx21_hc_urb_enqueue(struct usb_hcd *hcd, - struct urb *urb, gfp_t mem_flags) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct usb_host_endpoint *ep = urb->ep; - struct urb_priv *urb_priv; - struct ep_priv *ep_priv; - struct etd_priv *etd; - int ret; - unsigned long flags; - - dev_vdbg(imx21->dev, - "enqueue urb=%p ep=%p len=%d " - "buffer=%p dma=%pad setupBuf=%p setupDma=%pad\n", - urb, ep, - urb->transfer_buffer_length, - urb->transfer_buffer, &urb->transfer_dma, - urb->setup_packet, &urb->setup_dma); - - if (usb_pipeisoc(urb->pipe)) - return imx21_hc_urb_enqueue_isoc(hcd, ep, urb, mem_flags); - - urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags); - if (!urb_priv) - return -ENOMEM; - - spin_lock_irqsave(&imx21->lock, flags); - - ep_priv = ep->hcpriv; - if (ep_priv == NULL) { - ep_priv = alloc_ep(); - if (!ep_priv) { - ret = -ENOMEM; - goto failed_alloc_ep; - } - ep->hcpriv = ep_priv; - ep_priv->ep = ep; - } - - ret = usb_hcd_link_urb_to_ep(hcd, urb); - if (ret) - goto failed_link; - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - urb->hcpriv = urb_priv; - urb_priv->ep = ep; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - urb_priv->state = US_CTRL_SETUP; - break; - case PIPE_BULK: - urb_priv->state = US_BULK; - break; - } - - debug_urb_submitted(imx21, urb); - if (ep_priv->etd[0] < 0) { - if (ep_priv->waiting_etd) { - dev_dbg(imx21->dev, - "no ETD available already queued %p\n", - ep_priv); - debug_urb_queued_for_etd(imx21, urb); - goto out; - } - ep_priv->etd[0] = alloc_etd(imx21); - if (ep_priv->etd[0] < 0) { - dev_dbg(imx21->dev, - "no ETD available queueing %p\n", ep_priv); - debug_urb_queued_for_etd(imx21, urb); - list_add_tail(&ep_priv->queue, &imx21->queue_for_etd); - ep_priv->waiting_etd = 1; - goto out; - } - } - - /* Schedule if no URB already active for this endpoint */ - etd = &imx21->etd[ep_priv->etd[0]]; - if (etd->urb == NULL) { - DEBUG_LOG_FRAME(imx21, etd, last_req); - schedule_nonisoc_etd(imx21, urb); - } - -out: - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; - -failed_link: -failed_alloc_ep: - spin_unlock_irqrestore(&imx21->lock, flags); - kfree(urb_priv); - return ret; -} - -static int imx21_hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, - int status) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - struct usb_host_endpoint *ep; - struct ep_priv *ep_priv; - struct urb_priv *urb_priv = urb->hcpriv; - int ret = -EINVAL; - - dev_vdbg(imx21->dev, "dequeue urb=%p iso=%d status=%d\n", - urb, usb_pipeisoc(urb->pipe), status); - - spin_lock_irqsave(&imx21->lock, flags); - - ret = usb_hcd_check_unlink_urb(hcd, urb, status); - if (ret) - goto fail; - ep = urb_priv->ep; - ep_priv = ep->hcpriv; - - debug_urb_unlinked(imx21, urb); - - if (usb_pipeisoc(urb->pipe)) { - dequeue_isoc_urb(imx21, urb, ep_priv); - schedule_isoc_etds(hcd, ep); - } else if (urb_priv->active) { - int etd_num = ep_priv->etd[0]; - if (etd_num != -1) { - struct etd_priv *etd = &imx21->etd[etd_num]; - - disactivate_etd(imx21, etd_num); - free_dmem(imx21, etd); - etd->urb = NULL; - kfree(etd->bounce_buffer); - etd->bounce_buffer = NULL; - } - } - - urb_done(hcd, urb, status); - - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; - -fail: - spin_unlock_irqrestore(&imx21->lock, flags); - return ret; -} - -/* =========================================== */ -/* Interrupt dispatch */ -/* =========================================== */ - -static void process_etds(struct usb_hcd *hcd, struct imx21 *imx21, int sof) -{ - int etd_num; - int enable_sof_int = 0; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - for (etd_num = 0; etd_num < USB_NUM_ETD; etd_num++) { - u32 etd_mask = 1 << etd_num; - u32 enabled = readl(imx21->regs + USBH_ETDENSET) & etd_mask; - u32 done = readl(imx21->regs + USBH_ETDDONESTAT) & etd_mask; - struct etd_priv *etd = &imx21->etd[etd_num]; - - - if (done) { - DEBUG_LOG_FRAME(imx21, etd, last_int); - } else { -/* - * Kludge warning! - * - * When multiple transfers are using the bus we sometimes get into a state - * where the transfer has completed (the CC field of the ETD is != 0x0F), - * the ETD has self disabled but the ETDDONESTAT flag is not set - * (and hence no interrupt occurs). - * This causes the transfer in question to hang. - * The kludge below checks for this condition at each SOF and processes any - * blocked ETDs (after an arbitrary 10 frame wait) - * - * With a single active transfer the usbtest test suite will run for days - * without the kludge. - * With other bus activity (eg mass storage) even just test1 will hang without - * the kludge. - */ - u32 dword0; - int cc; - - if (etd->active_count && !enabled) /* suspicious... */ - enable_sof_int = 1; - - if (!sof || enabled || !etd->active_count) - continue; - - cc = etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE; - if (cc == TD_NOTACCESSED) - continue; - - if (++etd->active_count < 10) - continue; - - dword0 = etd_readl(imx21, etd_num, 0); - dev_dbg(imx21->dev, - "unblock ETD %d dev=0x%X ep=0x%X cc=0x%02X!\n", - etd_num, dword0 & 0x7F, - (dword0 >> DW0_ENDPNT) & 0x0F, - cc); - -#ifdef DEBUG - dev_dbg(imx21->dev, - "frame: act=%d disact=%d" - " int=%d req=%d cur=%d\n", - etd->activated_frame, - etd->disactivated_frame, - etd->last_int_frame, - etd->last_req_frame, - readl(imx21->regs + USBH_FRMNUB)); - imx21->debug_unblocks++; -#endif - etd->active_count = 0; -/* End of kludge */ - } - - if (etd->ep == NULL || etd->urb == NULL) { - dev_dbg(imx21->dev, - "Interrupt for unexpected etd %d" - " ep=%p urb=%p\n", - etd_num, etd->ep, etd->urb); - disactivate_etd(imx21, etd_num); - continue; - } - - if (usb_pipeisoc(etd->urb->pipe)) - isoc_etd_done(hcd, etd_num); - else - nonisoc_etd_done(hcd, etd_num); - } - - /* only enable SOF interrupt if it may be needed for the kludge */ - if (enable_sof_int) - set_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT); - else - clear_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT); - - - spin_unlock_irqrestore(&imx21->lock, flags); -} - -static irqreturn_t imx21_irq(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - u32 ints = readl(imx21->regs + USBH_SYSISR); - - if (ints & USBH_SYSIEN_HERRINT) - dev_dbg(imx21->dev, "Scheduling error\n"); - - if (ints & USBH_SYSIEN_SORINT) - dev_dbg(imx21->dev, "Scheduling overrun\n"); - - if (ints & (USBH_SYSISR_DONEINT | USBH_SYSISR_SOFINT)) - process_etds(hcd, imx21, ints & USBH_SYSISR_SOFINT); - - writel(ints, imx21->regs + USBH_SYSISR); - return IRQ_HANDLED; -} - -static void imx21_hc_endpoint_disable(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - struct ep_priv *ep_priv; - int i; - - if (ep == NULL) - return; - - spin_lock_irqsave(&imx21->lock, flags); - ep_priv = ep->hcpriv; - dev_vdbg(imx21->dev, "disable ep=%p, ep->hcpriv=%p\n", ep, ep_priv); - - if (!list_empty(&ep->urb_list)) - dev_dbg(imx21->dev, "ep's URB list is not empty\n"); - - if (ep_priv != NULL) { - for (i = 0; i < NUM_ISO_ETDS; i++) { - if (ep_priv->etd[i] > -1) - dev_dbg(imx21->dev, "free etd %d for disable\n", - ep_priv->etd[i]); - - free_etd(imx21, ep_priv->etd[i]); - } - kfree(ep_priv); - ep->hcpriv = NULL; - } - - for (i = 0; i < USB_NUM_ETD; i++) { - if (imx21->etd[i].alloc && imx21->etd[i].ep == ep) { - dev_err(imx21->dev, - "Active etd %d for disabled ep=%p!\n", i, ep); - free_etd(imx21, i); - } - } - free_epdmem(imx21, ep); - spin_unlock_irqrestore(&imx21->lock, flags); -} - -/* =========================================== */ -/* Hub handling */ -/* =========================================== */ - -static int get_hub_descriptor(struct usb_hcd *hcd, - struct usb_hub_descriptor *desc) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - desc->bDescriptorType = USB_DT_HUB; /* HUB descriptor */ - desc->bHubContrCurrent = 0; - - desc->bNbrPorts = readl(imx21->regs + USBH_ROOTHUBA) - & USBH_ROOTHUBA_NDNSTMPRT_MASK; - desc->bDescLength = 9; - desc->bPwrOn2PwrGood = 0; - desc->wHubCharacteristics = (__force __u16) cpu_to_le16( - HUB_CHAR_NO_LPSM | /* No power switching */ - HUB_CHAR_NO_OCPM); /* No over current protection */ - - desc->u.hs.DeviceRemovable[0] = 1 << 1; - desc->u.hs.DeviceRemovable[1] = ~0; - return 0; -} - -static int imx21_hc_hub_status_data(struct usb_hcd *hcd, char *buf) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - int ports; - int changed = 0; - int i; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - ports = readl(imx21->regs + USBH_ROOTHUBA) - & USBH_ROOTHUBA_NDNSTMPRT_MASK; - if (ports > 7) { - ports = 7; - dev_err(imx21->dev, "ports %d > 7\n", ports); - } - for (i = 0; i < ports; i++) { - if (readl(imx21->regs + USBH_PORTSTAT(i)) & - (USBH_PORTSTAT_CONNECTSC | - USBH_PORTSTAT_PRTENBLSC | - USBH_PORTSTAT_PRTSTATSC | - USBH_PORTSTAT_OVRCURIC | - USBH_PORTSTAT_PRTRSTSC)) { - - changed = 1; - buf[0] |= 1 << (i + 1); - } - } - spin_unlock_irqrestore(&imx21->lock, flags); - - if (changed) - dev_info(imx21->dev, "Hub status changed\n"); - return changed; -} - -static int imx21_hc_hub_control(struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, u16 wIndex, char *buf, u16 wLength) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - int rc = 0; - u32 status_write = 0; - - switch (typeReq) { - case ClearHubFeature: - dev_dbg(imx21->dev, "ClearHubFeature\n"); - switch (wValue) { - case C_HUB_OVER_CURRENT: - dev_dbg(imx21->dev, " OVER_CURRENT\n"); - break; - case C_HUB_LOCAL_POWER: - dev_dbg(imx21->dev, " LOCAL_POWER\n"); - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - break; - - case ClearPortFeature: - dev_dbg(imx21->dev, "ClearPortFeature\n"); - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - dev_dbg(imx21->dev, " ENABLE\n"); - status_write = USBH_PORTSTAT_CURCONST; - break; - case USB_PORT_FEAT_SUSPEND: - dev_dbg(imx21->dev, " SUSPEND\n"); - status_write = USBH_PORTSTAT_PRTOVRCURI; - break; - case USB_PORT_FEAT_POWER: - dev_dbg(imx21->dev, " POWER\n"); - status_write = USBH_PORTSTAT_LSDEVCON; - break; - case USB_PORT_FEAT_C_ENABLE: - dev_dbg(imx21->dev, " C_ENABLE\n"); - status_write = USBH_PORTSTAT_PRTENBLSC; - break; - case USB_PORT_FEAT_C_SUSPEND: - dev_dbg(imx21->dev, " C_SUSPEND\n"); - status_write = USBH_PORTSTAT_PRTSTATSC; - break; - case USB_PORT_FEAT_C_CONNECTION: - dev_dbg(imx21->dev, " C_CONNECTION\n"); - status_write = USBH_PORTSTAT_CONNECTSC; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - dev_dbg(imx21->dev, " C_OVER_CURRENT\n"); - status_write = USBH_PORTSTAT_OVRCURIC; - break; - case USB_PORT_FEAT_C_RESET: - dev_dbg(imx21->dev, " C_RESET\n"); - status_write = USBH_PORTSTAT_PRTRSTSC; - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - - break; - - case GetHubDescriptor: - dev_dbg(imx21->dev, "GetHubDescriptor\n"); - rc = get_hub_descriptor(hcd, (void *)buf); - break; - - case GetHubStatus: - dev_dbg(imx21->dev, " GetHubStatus\n"); - *(__le32 *) buf = 0; - break; - - case GetPortStatus: - dev_dbg(imx21->dev, "GetPortStatus: port: %d, 0x%x\n", - wIndex, USBH_PORTSTAT(wIndex - 1)); - *(__le32 *) buf = readl(imx21->regs + - USBH_PORTSTAT(wIndex - 1)); - break; - - case SetHubFeature: - dev_dbg(imx21->dev, "SetHubFeature\n"); - switch (wValue) { - case C_HUB_OVER_CURRENT: - dev_dbg(imx21->dev, " OVER_CURRENT\n"); - break; - - case C_HUB_LOCAL_POWER: - dev_dbg(imx21->dev, " LOCAL_POWER\n"); - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - - break; - - case SetPortFeature: - dev_dbg(imx21->dev, "SetPortFeature\n"); - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - dev_dbg(imx21->dev, " SUSPEND\n"); - status_write = USBH_PORTSTAT_PRTSUSPST; - break; - case USB_PORT_FEAT_POWER: - dev_dbg(imx21->dev, " POWER\n"); - status_write = USBH_PORTSTAT_PRTPWRST; - break; - case USB_PORT_FEAT_RESET: - dev_dbg(imx21->dev, " RESET\n"); - status_write = USBH_PORTSTAT_PRTRSTST; - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - break; - - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - - if (status_write) - writel(status_write, imx21->regs + USBH_PORTSTAT(wIndex - 1)); - return rc; -} - -/* =========================================== */ -/* Host controller management */ -/* =========================================== */ - -static int imx21_hc_reset(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long timeout; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - /* Reset the Host controller modules */ - writel(USBOTG_RST_RSTCTRL | USBOTG_RST_RSTRH | - USBOTG_RST_RSTHSIE | USBOTG_RST_RSTHC, - imx21->regs + USBOTG_RST_CTRL); - - /* Wait for reset to finish */ - timeout = jiffies + HZ; - while (readl(imx21->regs + USBOTG_RST_CTRL) != 0) { - if (time_after(jiffies, timeout)) { - spin_unlock_irqrestore(&imx21->lock, flags); - dev_err(imx21->dev, "timeout waiting for reset\n"); - return -ETIMEDOUT; - } - spin_unlock_irq(&imx21->lock); - schedule_timeout_uninterruptible(1); - spin_lock_irq(&imx21->lock); - } - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; -} - -static int imx21_hc_start(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - int i, j; - u32 hw_mode = USBOTG_HWMODE_CRECFG_HOST; - u32 usb_control = 0; - - hw_mode |= ((imx21->pdata->host_xcvr << USBOTG_HWMODE_HOSTXCVR_SHIFT) & - USBOTG_HWMODE_HOSTXCVR_MASK); - hw_mode |= ((imx21->pdata->otg_xcvr << USBOTG_HWMODE_OTGXCVR_SHIFT) & - USBOTG_HWMODE_OTGXCVR_MASK); - - if (imx21->pdata->host1_txenoe) - usb_control |= USBCTRL_HOST1_TXEN_OE; - - if (!imx21->pdata->host1_xcverless) - usb_control |= USBCTRL_HOST1_BYP_TLL; - - if (imx21->pdata->otg_ext_xcvr) - usb_control |= USBCTRL_OTC_RCV_RXDP; - - - spin_lock_irqsave(&imx21->lock, flags); - - writel((USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN), - imx21->regs + USBOTG_CLK_CTRL); - writel(hw_mode, imx21->regs + USBOTG_HWMODE); - writel(usb_control, imx21->regs + USBCTRL); - writel(USB_MISCCONTROL_SKPRTRY | USB_MISCCONTROL_ARBMODE, - imx21->regs + USB_MISCCONTROL); - - /* Clear the ETDs */ - for (i = 0; i < USB_NUM_ETD; i++) - for (j = 0; j < 4; j++) - etd_writel(imx21, i, j, 0); - - /* Take the HC out of reset */ - writel(USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL | USBH_HOST_CTRL_CTLBLKSR_1, - imx21->regs + USBH_HOST_CTRL); - - /* Enable ports */ - if (imx21->pdata->enable_otg_host) - writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST, - imx21->regs + USBH_PORTSTAT(0)); - - if (imx21->pdata->enable_host1) - writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST, - imx21->regs + USBH_PORTSTAT(1)); - - if (imx21->pdata->enable_host2) - writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST, - imx21->regs + USBH_PORTSTAT(2)); - - - hcd->state = HC_STATE_RUNNING; - - /* Enable host controller interrupts */ - set_register_bits(imx21, USBH_SYSIEN, - USBH_SYSIEN_HERRINT | - USBH_SYSIEN_DONEINT | USBH_SYSIEN_SORINT); - set_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT); - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} - -static void imx21_hc_stop(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - writel(0, imx21->regs + USBH_SYSIEN); - clear_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT); - clear_register_bits(imx21, USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN, - USBOTG_CLK_CTRL); - spin_unlock_irqrestore(&imx21->lock, flags); -} - -/* =========================================== */ -/* Driver glue */ -/* =========================================== */ - -static const struct hc_driver imx21_hc_driver = { - .description = hcd_name, - .product_desc = "IMX21 USB Host Controller", - .hcd_priv_size = sizeof(struct imx21), - - .flags = HCD_DMA | HCD_USB11, - .irq = imx21_irq, - - .reset = imx21_hc_reset, - .start = imx21_hc_start, - .stop = imx21_hc_stop, - - /* I/O requests */ - .urb_enqueue = imx21_hc_urb_enqueue, - .urb_dequeue = imx21_hc_urb_dequeue, - .endpoint_disable = imx21_hc_endpoint_disable, - - /* scheduling support */ - .get_frame_number = imx21_hc_get_frame, - - /* Root hub support */ - .hub_status_data = imx21_hc_hub_status_data, - .hub_control = imx21_hc_hub_control, - -}; - -static struct mx21_usbh_platform_data default_pdata = { - .host_xcvr = MX21_USBXCVR_TXDIF_RXDIF, - .otg_xcvr = MX21_USBXCVR_TXDIF_RXDIF, - .enable_host1 = 1, - .enable_host2 = 1, - .enable_otg_host = 1, - -}; - -static int imx21_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - remove_debug_files(imx21); - usb_remove_hcd(hcd); - - if (res != NULL) { - clk_disable_unprepare(imx21->clk); - clk_put(imx21->clk); - iounmap(imx21->regs); - release_mem_region(res->start, resource_size(res)); - } - - kfree(hcd); - return 0; -} - - -static int imx21_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct imx21 *imx21; - struct resource *res; - int ret; - int irq; - - printk(KERN_INFO "%s\n", imx21_hc_driver.product_desc); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - hcd = usb_create_hcd(&imx21_hc_driver, - &pdev->dev, dev_name(&pdev->dev)); - if (hcd == NULL) { - dev_err(&pdev->dev, "Cannot create hcd (%s)\n", - dev_name(&pdev->dev)); - return -ENOMEM; - } - - imx21 = hcd_to_imx21(hcd); - imx21->hcd = hcd; - imx21->dev = &pdev->dev; - imx21->pdata = dev_get_platdata(&pdev->dev); - if (!imx21->pdata) - imx21->pdata = &default_pdata; - - spin_lock_init(&imx21->lock); - INIT_LIST_HEAD(&imx21->dmem_list); - INIT_LIST_HEAD(&imx21->queue_for_etd); - INIT_LIST_HEAD(&imx21->queue_for_dmem); - create_debug_files(imx21); - - res = request_mem_region(res->start, resource_size(res), hcd_name); - if (!res) { - ret = -EBUSY; - goto failed_request_mem; - } - - imx21->regs = ioremap(res->start, resource_size(res)); - if (imx21->regs == NULL) { - dev_err(imx21->dev, "Cannot map registers\n"); - ret = -ENOMEM; - goto failed_ioremap; - } - - /* Enable clocks source */ - imx21->clk = clk_get(imx21->dev, NULL); - if (IS_ERR(imx21->clk)) { - dev_err(imx21->dev, "no clock found\n"); - ret = PTR_ERR(imx21->clk); - goto failed_clock_get; - } - - ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000)); - if (ret) - goto failed_clock_set; - ret = clk_prepare_enable(imx21->clk); - if (ret) - goto failed_clock_enable; - - dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n", - (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF); - - ret = usb_add_hcd(hcd, irq, 0); - if (ret != 0) { - dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret); - goto failed_add_hcd; - } - device_wakeup_enable(hcd->self.controller); - - return 0; - -failed_add_hcd: - clk_disable_unprepare(imx21->clk); -failed_clock_enable: -failed_clock_set: - clk_put(imx21->clk); -failed_clock_get: - iounmap(imx21->regs); -failed_ioremap: - release_mem_region(res->start, resource_size(res)); -failed_request_mem: - remove_debug_files(imx21); - usb_put_hcd(hcd); - return ret; -} - -static struct platform_driver imx21_hcd_driver = { - .driver = { - .name = hcd_name, - }, - .probe = imx21_probe, - .remove = imx21_remove, - .suspend = NULL, - .resume = NULL, -}; - -module_platform_driver(imx21_hcd_driver); - -MODULE_DESCRIPTION("i.MX21 USB Host controller"); -MODULE_AUTHOR("Martin Fuzzey"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx21-hcd"); diff --git a/drivers/usb/host/imx21-hcd.h b/drivers/usb/host/imx21-hcd.h deleted file mode 100644 index 96d16752a73e..000000000000 --- a/drivers/usb/host/imx21-hcd.h +++ /dev/null @@ -1,431 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Macros and prototypes for i.MX21 - * - * Copyright (C) 2006 Loping Dog Embedded Systems - * Copyright (C) 2009 Martin Fuzzey - * Originally written by Jay Monkman <jtm@lopingdog.com> - * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey - */ - -#ifndef __LINUX_IMX21_HCD_H__ -#define __LINUX_IMX21_HCD_H__ - -#ifdef CONFIG_DYNAMIC_DEBUG -#define DEBUG -#endif - -#include <linux/platform_data/usb-mx2.h> - -#define NUM_ISO_ETDS 2 -#define USB_NUM_ETD 32 -#define DMEM_SIZE 4096 - -/* Register definitions */ -#define USBOTG_HWMODE 0x00 -#define USBOTG_HWMODE_ANASDBEN (1 << 14) -#define USBOTG_HWMODE_OTGXCVR_SHIFT 6 -#define USBOTG_HWMODE_OTGXCVR_MASK (3 << 6) -#define USBOTG_HWMODE_OTGXCVR_TD_RD (0 << 6) -#define USBOTG_HWMODE_OTGXCVR_TS_RD (2 << 6) -#define USBOTG_HWMODE_OTGXCVR_TD_RS (1 << 6) -#define USBOTG_HWMODE_OTGXCVR_TS_RS (3 << 6) -#define USBOTG_HWMODE_HOSTXCVR_SHIFT 4 -#define USBOTG_HWMODE_HOSTXCVR_MASK (3 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TD_RD (0 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TS_RD (2 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TD_RS (1 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TS_RS (3 << 4) -#define USBOTG_HWMODE_CRECFG_MASK (3 << 0) -#define USBOTG_HWMODE_CRECFG_HOST (1 << 0) -#define USBOTG_HWMODE_CRECFG_FUNC (2 << 0) -#define USBOTG_HWMODE_CRECFG_HNP (3 << 0) - -#define USBOTG_CINT_STAT 0x04 -#define USBOTG_CINT_STEN 0x08 -#define USBOTG_ASHNPINT (1 << 5) -#define USBOTG_ASFCINT (1 << 4) -#define USBOTG_ASHCINT (1 << 3) -#define USBOTG_SHNPINT (1 << 2) -#define USBOTG_FCINT (1 << 1) -#define USBOTG_HCINT (1 << 0) - -#define USBOTG_CLK_CTRL 0x0c -#define USBOTG_CLK_CTRL_FUNC (1 << 2) -#define USBOTG_CLK_CTRL_HST (1 << 1) -#define USBOTG_CLK_CTRL_MAIN (1 << 0) - -#define USBOTG_RST_CTRL 0x10 -#define USBOTG_RST_RSTI2C (1 << 15) -#define USBOTG_RST_RSTCTRL (1 << 5) -#define USBOTG_RST_RSTFC (1 << 4) -#define USBOTG_RST_RSTFSKE (1 << 3) -#define USBOTG_RST_RSTRH (1 << 2) -#define USBOTG_RST_RSTHSIE (1 << 1) -#define USBOTG_RST_RSTHC (1 << 0) - -#define USBOTG_FRM_INTVL 0x14 -#define USBOTG_FRM_REMAIN 0x18 -#define USBOTG_HNP_CSR 0x1c -#define USBOTG_HNP_ISR 0x2c -#define USBOTG_HNP_IEN 0x30 - -#define USBOTG_I2C_TXCVR_REG(x) (0x100 + (x)) -#define USBOTG_I2C_XCVR_DEVAD 0x118 -#define USBOTG_I2C_SEQ_OP_REG 0x119 -#define USBOTG_I2C_SEQ_RD_STARTAD 0x11a -#define USBOTG_I2C_OP_CTRL_REG 0x11b -#define USBOTG_I2C_SCLK_TO_SCK_HPER 0x11e -#define USBOTG_I2C_MASTER_INT_REG 0x11f - -#define USBH_HOST_CTRL 0x80 -#define USBH_HOST_CTRL_HCRESET (1 << 31) -#define USBH_HOST_CTRL_SCHDOVR(x) ((x) << 16) -#define USBH_HOST_CTRL_RMTWUEN (1 << 4) -#define USBH_HOST_CTRL_HCUSBSTE_RESET (0 << 2) -#define USBH_HOST_CTRL_HCUSBSTE_RESUME (1 << 2) -#define USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL (2 << 2) -#define USBH_HOST_CTRL_HCUSBSTE_SUSPEND (3 << 2) -#define USBH_HOST_CTRL_CTLBLKSR_1 (0 << 0) -#define USBH_HOST_CTRL_CTLBLKSR_2 (1 << 0) -#define USBH_HOST_CTRL_CTLBLKSR_3 (2 << 0) -#define USBH_HOST_CTRL_CTLBLKSR_4 (3 << 0) - -#define USBH_SYSISR 0x88 -#define USBH_SYSISR_PSCINT (1 << 6) -#define USBH_SYSISR_FMOFINT (1 << 5) -#define USBH_SYSISR_HERRINT (1 << 4) -#define USBH_SYSISR_RESDETINT (1 << 3) -#define USBH_SYSISR_SOFINT (1 << 2) -#define USBH_SYSISR_DONEINT (1 << 1) -#define USBH_SYSISR_SORINT (1 << 0) - -#define USBH_SYSIEN 0x8c -#define USBH_SYSIEN_PSCINT (1 << 6) -#define USBH_SYSIEN_FMOFINT (1 << 5) -#define USBH_SYSIEN_HERRINT (1 << 4) -#define USBH_SYSIEN_RESDETINT (1 << 3) -#define USBH_SYSIEN_SOFINT (1 << 2) -#define USBH_SYSIEN_DONEINT (1 << 1) -#define USBH_SYSIEN_SORINT (1 << 0) - -#define USBH_XBUFSTAT 0x98 -#define USBH_YBUFSTAT 0x9c -#define USBH_XYINTEN 0xa0 -#define USBH_XFILLSTAT 0xa8 -#define USBH_YFILLSTAT 0xac -#define USBH_ETDENSET 0xc0 -#define USBH_ETDENCLR 0xc4 -#define USBH_IMMEDINT 0xcc -#define USBH_ETDDONESTAT 0xd0 -#define USBH_ETDDONEEN 0xd4 -#define USBH_FRMNUB 0xe0 -#define USBH_LSTHRESH 0xe4 - -#define USBH_ROOTHUBA 0xe8 -#define USBH_ROOTHUBA_PWRTOGOOD_MASK (0xff) -#define USBH_ROOTHUBA_PWRTOGOOD_SHIFT (24) -#define USBH_ROOTHUBA_NOOVRCURP (1 << 12) -#define USBH_ROOTHUBA_OVRCURPM (1 << 11) -#define USBH_ROOTHUBA_DEVTYPE (1 << 10) -#define USBH_ROOTHUBA_PWRSWTMD (1 << 9) -#define USBH_ROOTHUBA_NOPWRSWT (1 << 8) -#define USBH_ROOTHUBA_NDNSTMPRT_MASK (0xff) - -#define USBH_ROOTHUBB 0xec -#define USBH_ROOTHUBB_PRTPWRCM(x) (1 << ((x) + 16)) -#define USBH_ROOTHUBB_DEVREMOVE(x) (1 << (x)) - -#define USBH_ROOTSTAT 0xf0 -#define USBH_ROOTSTAT_CLRRMTWUE (1 << 31) -#define USBH_ROOTSTAT_OVRCURCHG (1 << 17) -#define USBH_ROOTSTAT_DEVCONWUE (1 << 15) -#define USBH_ROOTSTAT_OVRCURI (1 << 1) -#define USBH_ROOTSTAT_LOCPWRS (1 << 0) - -#define USBH_PORTSTAT(x) (0xf4 + ((x) * 4)) -#define USBH_PORTSTAT_PRTRSTSC (1 << 20) -#define USBH_PORTSTAT_OVRCURIC (1 << 19) -#define USBH_PORTSTAT_PRTSTATSC (1 << 18) -#define USBH_PORTSTAT_PRTENBLSC (1 << 17) -#define USBH_PORTSTAT_CONNECTSC (1 << 16) -#define USBH_PORTSTAT_LSDEVCON (1 << 9) -#define USBH_PORTSTAT_PRTPWRST (1 << 8) -#define USBH_PORTSTAT_PRTRSTST (1 << 4) -#define USBH_PORTSTAT_PRTOVRCURI (1 << 3) -#define USBH_PORTSTAT_PRTSUSPST (1 << 2) -#define USBH_PORTSTAT_PRTENABST (1 << 1) -#define USBH_PORTSTAT_CURCONST (1 << 0) - -#define USB_DMAREV 0x800 -#define USB_DMAINTSTAT 0x804 -#define USB_DMAINTSTAT_EPERR (1 << 1) -#define USB_DMAINTSTAT_ETDERR (1 << 0) - -#define USB_DMAINTEN 0x808 -#define USB_DMAINTEN_EPERRINTEN (1 << 1) -#define USB_DMAINTEN_ETDERRINTEN (1 << 0) - -#define USB_ETDDMAERSTAT 0x80c -#define USB_EPDMAERSTAT 0x810 -#define USB_ETDDMAEN 0x820 -#define USB_EPDMAEN 0x824 -#define USB_ETDDMAXTEN 0x828 -#define USB_EPDMAXTEN 0x82c -#define USB_ETDDMAENXYT 0x830 -#define USB_EPDMAENXYT 0x834 -#define USB_ETDDMABST4EN 0x838 -#define USB_EPDMABST4EN 0x83c - -#define USB_MISCCONTROL 0x840 -#define USB_MISCCONTROL_ISOPREVFRM (1 << 3) -#define USB_MISCCONTROL_SKPRTRY (1 << 2) -#define USB_MISCCONTROL_ARBMODE (1 << 1) -#define USB_MISCCONTROL_FILTCC (1 << 0) - -#define USB_ETDDMACHANLCLR 0x848 -#define USB_EPDMACHANLCLR 0x84c -#define USB_ETDSMSA(x) (0x900 + ((x) * 4)) -#define USB_EPSMSA(x) (0x980 + ((x) * 4)) -#define USB_ETDDMABUFPTR(x) (0xa00 + ((x) * 4)) -#define USB_EPDMABUFPTR(x) (0xa80 + ((x) * 4)) - -#define USB_ETD_DWORD(x, w) (0x200 + ((x) * 16) + ((w) * 4)) -#define DW0_ADDRESS 0 -#define DW0_ENDPNT 7 -#define DW0_DIRECT 11 -#define DW0_SPEED 13 -#define DW0_FORMAT 14 -#define DW0_MAXPKTSIZ 16 -#define DW0_HALTED 27 -#define DW0_TOGCRY 28 -#define DW0_SNDNAK 30 - -#define DW1_XBUFSRTAD 0 -#define DW1_YBUFSRTAD 16 - -#define DW2_RTRYDELAY 0 -#define DW2_POLINTERV 0 -#define DW2_STARTFRM 0 -#define DW2_RELPOLPOS 8 -#define DW2_DIRPID 16 -#define DW2_BUFROUND 18 -#define DW2_DELAYINT 19 -#define DW2_DATATOG 22 -#define DW2_ERRORCNT 24 -#define DW2_COMPCODE 28 - -#define DW3_TOTBYECNT 0 -#define DW3_PKTLEN0 0 -#define DW3_COMPCODE0 12 -#define DW3_PKTLEN1 16 -#define DW3_BUFSIZE 21 -#define DW3_COMPCODE1 28 - -#define USBCTRL 0x600 -#define USBCTRL_I2C_WU_INT_STAT (1 << 27) -#define USBCTRL_OTG_WU_INT_STAT (1 << 26) -#define USBCTRL_HOST_WU_INT_STAT (1 << 25) -#define USBCTRL_FNT_WU_INT_STAT (1 << 24) -#define USBCTRL_I2C_WU_INT_EN (1 << 19) -#define USBCTRL_OTG_WU_INT_EN (1 << 18) -#define USBCTRL_HOST_WU_INT_EN (1 << 17) -#define USBCTRL_FNT_WU_INT_EN (1 << 16) -#define USBCTRL_OTC_RCV_RXDP (1 << 13) -#define USBCTRL_HOST1_BYP_TLL (1 << 12) -#define USBCTRL_OTG_BYP_VAL(x) ((x) << 10) -#define USBCTRL_HOST1_BYP_VAL(x) ((x) << 8) -#define USBCTRL_OTG_PWR_MASK (1 << 6) -#define USBCTRL_HOST1_PWR_MASK (1 << 5) -#define USBCTRL_HOST2_PWR_MASK (1 << 4) -#define USBCTRL_USB_BYP (1 << 2) -#define USBCTRL_HOST1_TXEN_OE (1 << 1) - -#define USBOTG_DMEM 0x1000 - -/* Values in TD blocks */ -#define TD_DIR_SETUP 0 -#define TD_DIR_OUT 1 -#define TD_DIR_IN 2 -#define TD_FORMAT_CONTROL 0 -#define TD_FORMAT_ISO 1 -#define TD_FORMAT_BULK 2 -#define TD_FORMAT_INT 3 -#define TD_TOGGLE_CARRY 0 -#define TD_TOGGLE_DATA0 2 -#define TD_TOGGLE_DATA1 3 - -/* control transfer states */ -#define US_CTRL_SETUP 2 -#define US_CTRL_DATA 1 -#define US_CTRL_ACK 0 - -/* bulk transfer main state and 0-length packet */ -#define US_BULK 1 -#define US_BULK0 0 - -/*ETD format description*/ -#define IMX_FMT_CTRL 0x0 -#define IMX_FMT_ISO 0x1 -#define IMX_FMT_BULK 0x2 -#define IMX_FMT_INT 0x3 - -static char fmt_urb_to_etd[4] = { -/*PIPE_ISOCHRONOUS*/ IMX_FMT_ISO, -/*PIPE_INTERRUPT*/ IMX_FMT_INT, -/*PIPE_CONTROL*/ IMX_FMT_CTRL, -/*PIPE_BULK*/ IMX_FMT_BULK -}; - -/* condition (error) CC codes and mapping (OHCI like) */ - -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -/*#define TD_UNEXPECTEDPID 0x07 - reserved, not active on MX2*/ -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_SCHEDULEOVERRUN 0x0E -#define TD_NOTACCESSED 0x0F - -static const int cc_to_error[16] = { - /* No Error */ 0, - /* CRC Error */ -EILSEQ, - /* Bit Stuff */ -EPROTO, - /* Data Togg */ -EILSEQ, - /* Stall */ -EPIPE, - /* DevNotResp */ -ETIMEDOUT, - /* PIDCheck */ -EPROTO, - /* UnExpPID */ -EPROTO, - /* DataOver */ -EOVERFLOW, - /* DataUnder */ -EREMOTEIO, - /* (for hw) */ -EIO, - /* (for hw) */ -EIO, - /* BufferOver */ -ECOMM, - /* BuffUnder */ -ENOSR, - /* (for HCD) */ -ENOSPC, - /* (for HCD) */ -EALREADY -}; - -/* HCD data associated with a usb core URB */ -struct urb_priv { - struct urb *urb; - struct usb_host_endpoint *ep; - int active; - int state; - struct td *isoc_td; - int isoc_remaining; - int isoc_status; -}; - -/* HCD data associated with a usb core endpoint */ -struct ep_priv { - struct usb_host_endpoint *ep; - struct list_head td_list; - struct list_head queue; - int etd[NUM_ISO_ETDS]; - int waiting_etd; -}; - -/* isoc packet */ -struct td { - struct list_head list; - struct urb *urb; - struct usb_host_endpoint *ep; - dma_addr_t dma_handle; - void *cpu_buffer; - int len; - int frame; - int isoc_index; -}; - -/* HCD data associated with a hardware ETD */ -struct etd_priv { - struct usb_host_endpoint *ep; - struct urb *urb; - struct td *td; - struct list_head queue; - dma_addr_t dma_handle; - void *cpu_buffer; - void *bounce_buffer; - int alloc; - int len; - int dmem_size; - int dmem_offset; - int active_count; -#ifdef DEBUG - int activated_frame; - int disactivated_frame; - int last_int_frame; - int last_req_frame; - u32 submitted_dwords[4]; -#endif -}; - -/* Hardware data memory info */ -struct imx21_dmem_area { - struct usb_host_endpoint *ep; - unsigned int offset; - unsigned int size; - struct list_head list; -}; - -#ifdef DEBUG -struct debug_usage_stats { - unsigned int value; - unsigned int maximum; -}; - -struct debug_stats { - unsigned long submitted; - unsigned long completed_ok; - unsigned long completed_failed; - unsigned long unlinked; - unsigned long queue_etd; - unsigned long queue_dmem; -}; - -struct debug_isoc_trace { - int schedule_frame; - int submit_frame; - int request_len; - int done_frame; - int done_len; - int cc; - struct td *td; -}; -#endif - -/* HCD data structure */ -struct imx21 { - spinlock_t lock; - struct device *dev; - struct usb_hcd *hcd; - struct mx21_usbh_platform_data *pdata; - struct list_head dmem_list; - struct list_head queue_for_etd; /* eps queued due to etd shortage */ - struct list_head queue_for_dmem; /* etds queued due to dmem shortage */ - struct etd_priv etd[USB_NUM_ETD]; - struct clk *clk; - void __iomem *regs; -#ifdef DEBUG - struct dentry *debug_root; - struct debug_stats nonisoc_stats; - struct debug_stats isoc_stats; - struct debug_usage_stats etd_usage; - struct debug_usage_stats dmem_usage; - struct debug_isoc_trace isoc_trace[20]; - struct debug_isoc_trace isoc_trace_failed[20]; - unsigned long debug_unblocks; - int isoc_trace_index; - int isoc_trace_index_failed; -#endif -}; - -#endif diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 3055d9abfec3..8544a2a2c1e6 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1447,6 +1447,7 @@ static int isp116x_bus_resume(struct usb_hcd *hcd) val &= ~HCCONTROL_HCFS; val |= HCCONTROL_USB_RESUME; isp116x_write_reg32(isp116x, HCCONTROL, val); + break; case HCCONTROL_USB_RESUME: break; case HCCONTROL_USB_OPER: diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 9bbfcc3fdd3c..208705b08d37 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -793,60 +793,6 @@ static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 l ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ } -static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd) -{ - isp1362_show_reg(isp1362_hcd, HCREVISION); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - isp1362_show_reg(isp1362_hcd, HCCMDSTAT); - isp1362_show_reg(isp1362_hcd, HCINTSTAT); - isp1362_show_reg(isp1362_hcd, HCINTENB); - isp1362_show_reg(isp1362_hcd, HCFMINTVL); - isp1362_show_reg(isp1362_hcd, HCFMREM); - isp1362_show_reg(isp1362_hcd, HCFMNUM); - isp1362_show_reg(isp1362_hcd, HCLSTHRESH); - isp1362_show_reg(isp1362_hcd, HCRHDESCA); - isp1362_show_reg(isp1362_hcd, HCRHDESCB); - isp1362_show_reg(isp1362_hcd, HCRHSTATUS); - isp1362_show_reg(isp1362_hcd, HCRHPORT1); - isp1362_show_reg(isp1362_hcd, HCRHPORT2); - - isp1362_show_reg(isp1362_hcd, HCHWCFG); - isp1362_show_reg(isp1362_hcd, HCDMACFG); - isp1362_show_reg(isp1362_hcd, HCXFERCTR); - isp1362_show_reg(isp1362_hcd, HCuPINT); - - if (in_interrupt()) - DBG(0, "%-12s[%02x]: %04x\n", "HCuPINTENB", - ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), isp1362_hcd->irqenb); - else - isp1362_show_reg(isp1362_hcd, HCuPINTENB); - isp1362_show_reg(isp1362_hcd, HCCHIPID); - isp1362_show_reg(isp1362_hcd, HCSCRATCH); - isp1362_show_reg(isp1362_hcd, HCBUFSTAT); - isp1362_show_reg(isp1362_hcd, HCDIRADDR); - /* Access would advance fifo - * isp1362_show_reg(isp1362_hcd, HCDIRDATA); - */ - isp1362_show_reg(isp1362_hcd, HCISTLBUFSZ); - isp1362_show_reg(isp1362_hcd, HCISTLRATE); - isp1362_show_reg(isp1362_hcd, HCINTLBUFSZ); - isp1362_show_reg(isp1362_hcd, HCINTLBLKSZ); - isp1362_show_reg(isp1362_hcd, HCINTLDONE); - isp1362_show_reg(isp1362_hcd, HCINTLSKIP); - isp1362_show_reg(isp1362_hcd, HCINTLLAST); - isp1362_show_reg(isp1362_hcd, HCINTLCURR); - isp1362_show_reg(isp1362_hcd, HCATLBUFSZ); - isp1362_show_reg(isp1362_hcd, HCATLBLKSZ); - /* only valid after ATL_DONE interrupt - * isp1362_show_reg(isp1362_hcd, HCATLDONE); - */ - isp1362_show_reg(isp1362_hcd, HCATLSKIP); - isp1362_show_reg(isp1362_hcd, HCATLLAST); - isp1362_show_reg(isp1362_hcd, HCATLCURR); - isp1362_show_reg(isp1362_hcd, HCATLDTC); - isp1362_show_reg(isp1362_hcd, HCATLDTCTO); -} - static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) { len = (len + 1) & ~1; diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0894f6caccb2..afd9174d83b1 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1537,6 +1537,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) __func__, urb->interval); return -EINVAL; } + break; default: break; } @@ -1847,7 +1848,7 @@ max3421_probe(struct spi_device *spi) struct max3421_hcd *max3421_hcd; struct usb_hcd *hcd = NULL; struct max3421_hcd_platform_data *pdata = NULL; - int retval = -ENOMEM; + int retval; if (spi_setup(spi) < 0) { dev_err(&spi->dev, "Unable to setup SPI bus"); @@ -1889,6 +1890,7 @@ max3421_probe(struct spi_device *spi) goto error; } + retval = -ENOMEM; hcd = usb_create_hcd(&max3421_hcd_desc, &spi->dev, dev_name(&spi->dev)); if (!hcd) { diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 0487a4b0501e..9bbd7ddd0003 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -155,7 +155,10 @@ static struct regmap *at91_dt_syscon_sfr(void) /* * usb_hcd_at91_probe - initialize AT91-based HCDs - * Context: !in_interrupt() + * @driver: Pointer to hc driver instance + * @pdev: USB controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -246,12 +249,14 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* * usb_hcd_at91_remove - shutdown processing for AT91-based HCDs - * Context: !in_interrupt() + * @hcd: USB controller to remove + * @pdev: Platform device required for cleanup + * + * Context: task context, might sleep * * Reverses the effect of usb_hcd_at91_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, "rmmod" or something similar. - * */ static void usb_hcd_at91_remove(struct usb_hcd *hcd, struct platform_device *pdev) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 73e13e7c2b46..1f5e69314a17 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -171,7 +171,7 @@ static int ohci_urb_enqueue ( /* 1 TD for setup, 1 for ACK, plus ... */ size = 2; - /* FALLTHROUGH */ + fallthrough; // case PIPE_INTERRUPT: // case PIPE_BULK: default: diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 44504c1751e0..f474f2f9c1e4 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -692,6 +692,7 @@ int ohci_hub_control( case C_HUB_OVER_CURRENT: ohci_writel (ohci, RH_HS_OCIC, &ohci->regs->roothub.status); + break; case C_HUB_LOCAL_POWER: break; default: diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ccdf2c216b5..0b3722770760 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -91,14 +91,14 @@ static int omap_ohci_transceiver_power(struct ohci_omap_priv *priv, int on) | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); else if (priv->power) - gpiod_set_value(priv->power, 0); + gpiod_set_value_cansleep(priv->power, 0); } else { if (machine_is_omap_innovator() && cpu_is_omap1510()) __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); else if (priv->power) - gpiod_set_value(priv->power, 1); + gpiod_set_value_cansleep(priv->power, 1); } return 0; @@ -285,7 +285,9 @@ static int ohci_omap_reset(struct usb_hcd *hcd) /** * ohci_hcd_omap_probe - initialize OMAP-based HCDs - * Context: !in_interrupt() + * @pdev: USB controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -399,8 +401,9 @@ err_put_hcd: /** * ohci_hcd_omap_remove - shutdown processing for OMAP-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * @pdev: USB Host Controller being removed + * + * Context: task context, might sleep * * Reverses the effect of ohci_hcd_omap_probe(), first invoking * the HCD's stop() method. It is always called from a thread diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 7679fb583e41..54aa5c77e549 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -410,12 +410,13 @@ static int ohci_pxa_of_init(struct platform_device *pdev) /** * ohci_hcd_pxa27x_probe - initialize pxa27x-based HCDs - * Context: !in_interrupt() + * @pdev: USB Host controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * */ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev) { @@ -509,13 +510,13 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev) /** * ohci_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * @pdev: USB Host Controller being removed + * + * Context: task context, might sleep * * Reverses the effect of ohci_hcd_pxa27x_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. - * */ static int ohci_hcd_pxa27x_remove(struct platform_device *pdev) { diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index de5e570c58ba..1bec9b585e2d 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -324,14 +324,13 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) /* * ohci_hcd_s3c2410_remove - shutdown processing for HCD * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Reverses the effect of ohci_hcd_3c2410_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. - * -*/ - + */ static int ohci_hcd_s3c2410_remove(struct platform_device *dev) { @@ -345,12 +344,13 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev) /* * ohci_hcd_s3c2410_probe - initialize S3C2410-based HCDs - * Context: !in_interrupt() + * @dev: USB Host Controller to be probed + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * */ static int ohci_hcd_s3c2410_probe(struct platform_device *dev) { diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 27dbbe1b28b1..4300326b3730 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1365,6 +1365,7 @@ __acquires(oxu->lock) switch (urb->status) { case -EINPROGRESS: /* success */ urb->status = 0; + break; default: /* fault */ break; case -EREMOTEIO: /* fault or normal */ @@ -4151,8 +4152,10 @@ static struct usb_hcd *oxu_create(struct platform_device *pdev, oxu->is_otg = otg; ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret < 0) + if (ret < 0) { + usb_put_hcd(hcd); return ERR_PTR(ret); + } device_wakeup_enable(hcd->self.controller); return hcd; diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 995bc52d2d22..eb96e1e15b71 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -208,13 +208,13 @@ struct u132 { #define ftdi_read_pcimem(pdev, member, data) usb_ftdi_elan_read_pcimem(pdev, \ offsetof(struct ohci_regs, member), 0, data); #define ftdi_write_pcimem(pdev, member, data) usb_ftdi_elan_write_pcimem(pdev, \ - offsetof(struct ohci_regs, member), 0, data); + offsetof(struct ohci_regs, member), 0, data) #define u132_read_pcimem(u132, member, data) \ usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \ - ohci_regs, member), 0, data); + ohci_regs, member), 0, data) #define u132_write_pcimem(u132, member, data) \ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \ - ohci_regs, member), 0, data); + ohci_regs, member), 0, data) static inline struct u132 *udev_to_u132(struct u132_udev *udev) { u8 udev_number = udev->udev_number; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index c799ca5361d4..74c497fd3476 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1712,6 +1712,10 @@ retry: hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irqrestore(&xhci->lock, flags); + + if (bus_state->bus_suspended) + usleep_range(5000, 10000); + return 0; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 138ba4528dd3..3589b49b6c8b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1144,7 +1144,6 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); return -EINVAL; - break; default: /* Speed was set earlier, this shouldn't happen. */ return -EINVAL; @@ -2110,7 +2109,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, xhci->event_ring->dequeue); - if (deq == 0 && !in_interrupt()) + if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index bf89172c43ca..84da8406d5b4 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -47,6 +47,7 @@ #define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI 0x15b6 +#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_XHCI 0x15c1 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI 0x15db #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI 0x15d4 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9 @@ -55,6 +56,7 @@ #define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13 #define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af #define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13 +#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138 #define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba @@ -232,13 +234,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI)) + pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI)) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; if (pdev->vendor == PCI_VENDOR_ID_ETRON && diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index aa2d35f98200..4d34f6005381 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -333,6 +333,9 @@ static int xhci_plat_probe(struct platform_device *pdev) if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) hcd->skip_phy_initialization = 1; + if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK)) + xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK; + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_usb_phy; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 167dae117f73..5677b81c0915 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2418,6 +2418,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_warn_ratelimited(xhci, "WARN Successful completion on short TX for slot %u ep %u: needs XHCI_TRUST_TX_LENGTH quirk?\n", slot_id, ep_index); + break; case COMP_SHORT_PACKET: break; /* Completion codes for endpoint stopped state */ @@ -2962,6 +2963,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, return -EINVAL; case EP_STATE_HALTED: xhci_dbg(xhci, "WARN halted endpoint, queueing URB anyway.\n"); + break; case EP_STATE_STOPPED: case EP_STATE_RUNNING: break; @@ -3325,7 +3327,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, full_len = urb->transfer_buffer_length; /* If we have scatter/gather list, we use it. */ - if (urb->num_sgs) { + if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) { num_sgs = urb->num_mapped_sgs; sg = urb->sg; addr = (u64) sg_dma_address(sg); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d4a8d0efbbc4..91ab81c3fc79 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1259,6 +1259,108 @@ EXPORT_SYMBOL_GPL(xhci_resume); /*-------------------------------------------------------------------------*/ +static int xhci_map_temp_buffer(struct usb_hcd *hcd, struct urb *urb) +{ + void *temp; + int ret = 0; + unsigned int buf_len; + enum dma_data_direction dir; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + buf_len = urb->transfer_buffer_length; + + temp = kzalloc_node(buf_len, GFP_ATOMIC, + dev_to_node(hcd->self.sysdev)); + + if (usb_urb_dir_out(urb)) + sg_pcopy_to_buffer(urb->sg, urb->num_sgs, + temp, buf_len, 0); + + urb->transfer_buffer = temp; + urb->transfer_dma = dma_map_single(hcd->self.sysdev, + urb->transfer_buffer, + urb->transfer_buffer_length, + dir); + + if (dma_mapping_error(hcd->self.sysdev, + urb->transfer_dma)) { + ret = -EAGAIN; + kfree(temp); + } else { + urb->transfer_flags |= URB_DMA_MAP_SINGLE; + } + + return ret; +} + +static bool xhci_urb_temp_buffer_required(struct usb_hcd *hcd, + struct urb *urb) +{ + bool ret = false; + unsigned int i; + unsigned int len = 0; + unsigned int trb_size; + unsigned int max_pkt; + struct scatterlist *sg; + struct scatterlist *tail_sg; + + tail_sg = urb->sg; + max_pkt = usb_endpoint_maxp(&urb->ep->desc); + + if (!urb->num_sgs) + return ret; + + if (urb->dev->speed >= USB_SPEED_SUPER) + trb_size = TRB_CACHE_SIZE_SS; + else + trb_size = TRB_CACHE_SIZE_HS; + + if (urb->transfer_buffer_length != 0 && + !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { + for_each_sg(urb->sg, sg, urb->num_sgs, i) { + len = len + sg->length; + if (i > trb_size - 2) { + len = len - tail_sg->length; + if (len < max_pkt) { + ret = true; + break; + } + + tail_sg = sg_next(tail_sg); + } + } + } + return ret; +} + +static void xhci_unmap_temp_buf(struct usb_hcd *hcd, struct urb *urb) +{ + unsigned int len; + unsigned int buf_len; + enum dma_data_direction dir; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_len = urb->transfer_buffer_length; + + if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_SINGLE)) + dma_unmap_single(hcd->self.sysdev, + urb->transfer_dma, + urb->transfer_buffer_length, + dir); + + if (usb_urb_dir_in(urb)) + len = sg_pcopy_from_buffer(urb->sg, urb->num_sgs, + urb->transfer_buffer, + buf_len, + 0); + + urb->transfer_flags &= ~URB_DMA_MAP_SINGLE; + kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; +} + /* * Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT), * we'll copy the actual data into the TRB address register. This is limited to @@ -1268,13 +1370,37 @@ EXPORT_SYMBOL_GPL(xhci_resume); static int xhci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(hcd); + if (xhci_urb_suitable_for_idt(urb)) return 0; + if (xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) { + if (xhci_urb_temp_buffer_required(hcd, urb)) + return xhci_map_temp_buffer(hcd, urb); + } return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); } -/* +static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + struct xhci_hcd *xhci; + bool unmap_temp_buf = false; + + xhci = hcd_to_xhci(hcd); + + if (urb->num_sgs && (urb->transfer_flags & URB_DMA_MAP_SINGLE)) + unmap_temp_buf = true; + + if ((xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) && unmap_temp_buf) + xhci_unmap_temp_buf(hcd, urb); + else + usb_hcd_unmap_urb_for_dma(hcd, urb); +} + +/** * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and * HCDs. Find the index for an endpoint given its descriptor. Use the return * value to right shift 1 for the bitmask. @@ -1476,11 +1602,9 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; - if (!HCD_HW_ACCESSIBLE(hcd)) { - if (!in_interrupt()) - xhci_dbg(xhci, "urb submitted during PCI suspend\n"); + if (!HCD_HW_ACCESSIBLE(hcd)) return -ESHUTDOWN; - } + if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); return -ENODEV; @@ -5329,6 +5453,7 @@ static const struct hc_driver xhci_hc_driver = { * managing i/o requests and associated device resources */ .map_urb_for_dma = xhci_map_urb_for_dma, + .unmap_urb_for_dma = xhci_unmap_urb_for_dma, .urb_enqueue = xhci_urb_enqueue, .urb_dequeue = xhci_urb_dequeue, .alloc_dev = xhci_alloc_dev, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ebb359ebb261..25e57bc9c3cc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1330,6 +1330,10 @@ enum xhci_setup_dev { #define TRB_SIA (1<<31) #define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20) +/* TRB cache size for xHC with TRB cache */ +#define TRB_CACHE_SIZE_HS 8 +#define TRB_CACHE_SIZE_SS 16 + struct xhci_generic_trb { __le32 field[4]; }; @@ -1878,6 +1882,7 @@ struct xhci_hcd { #define XHCI_RENESAS_FW_QUIRK BIT_ULL(36) #define XHCI_SKIP_PHY_INIT BIT_ULL(37) #define XHCI_DISABLE_SPARSE BIT_ULL(38) +#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 6818ea689cd9..8f1144359012 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -275,3 +275,12 @@ config USB_CHAOSKEY To compile this driver as a module, choose M here: the module will be called chaoskey. + +config BRCM_USB_PINMAP + tristate "Broadcom pinmap driver support" + depends on (ARCH_BRCMSTB && PHY_BRCM_USB) || COMPILE_TEST + default ARCH_BRCMSTB && PHY_BRCM_USB + help + This option enables support for remapping some USB external + signals, which are typically on dedicated pins on the chip, + to any gpio. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index da39bddb0604..5f4e598573ab 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o +obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index 9de0171b5177..ac8695195c13 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -184,10 +184,8 @@ static int mfi_fc_probe(struct usb_device *udev) return -ENODEV; mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); - if (!mfi) { - err = -ENOMEM; - goto error; - } + if (!mfi) + return -ENOMEM; battery_cfg.drv_data = mfi; @@ -198,17 +196,14 @@ static int mfi_fc_probe(struct usb_device *udev) if (IS_ERR(mfi->battery)) { dev_err(&udev->dev, "Can't register battery\n"); err = PTR_ERR(mfi->battery); - goto error; + kfree(mfi); + return err; } mfi->udev = usb_get_dev(udev); dev_set_drvdata(&udev->dev, mfi); return 0; - -error: - kfree(mfi); - return err; } static void mfi_fc_disconnect(struct usb_device *udev) diff --git a/drivers/usb/misc/brcmstb-usb-pinmap.c b/drivers/usb/misc/brcmstb-usb-pinmap.c new file mode 100644 index 000000000000..b3cfe8666ea7 --- /dev/null +++ b/drivers/usb/misc/brcmstb-usb-pinmap.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020, Broadcom */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/kernel.h> +#include <linux/kdebug.h> +#include <linux/gpio/consumer.h> + +struct out_pin { + u32 enable_mask; + u32 value_mask; + u32 changed_mask; + u32 clr_changed_mask; + struct gpio_desc *gpiod; + const char *name; +}; + +struct in_pin { + u32 enable_mask; + u32 value_mask; + struct gpio_desc *gpiod; + const char *name; + struct brcmstb_usb_pinmap_data *pdata; +}; + +struct brcmstb_usb_pinmap_data { + void __iomem *regs; + int in_count; + struct in_pin *in_pins; + int out_count; + struct out_pin *out_pins; +}; + + +static void pinmap_set(void __iomem *reg, u32 mask) +{ + u32 val; + + val = readl(reg); + val |= mask; + writel(val, reg); +} + +static void pinmap_unset(void __iomem *reg, u32 mask) +{ + u32 val; + + val = readl(reg); + val &= ~mask; + writel(val, reg); +} + +static void sync_in_pin(struct in_pin *pin) +{ + u32 val; + + val = gpiod_get_value(pin->gpiod); + if (val) + pinmap_set(pin->pdata->regs, pin->value_mask); + else + pinmap_unset(pin->pdata->regs, pin->value_mask); +} + +/* + * Interrupt from override register, propagate from override bit + * to GPIO. + */ +static irqreturn_t brcmstb_usb_pinmap_ovr_isr(int irq, void *dev_id) +{ + struct brcmstb_usb_pinmap_data *pdata = dev_id; + struct out_pin *pout; + u32 val; + u32 bit; + int x; + + pr_debug("%s: reg: 0x%x\n", __func__, readl(pdata->regs)); + pout = pdata->out_pins; + for (x = 0; x < pdata->out_count; x++) { + val = readl(pdata->regs); + if (val & pout->changed_mask) { + pinmap_set(pdata->regs, pout->clr_changed_mask); + pinmap_unset(pdata->regs, pout->clr_changed_mask); + bit = val & pout->value_mask; + gpiod_set_value(pout->gpiod, bit ? 1 : 0); + pr_debug("%s: %s bit changed state to %d\n", + __func__, pout->name, bit ? 1 : 0); + } + } + return IRQ_HANDLED; +} + +/* + * Interrupt from GPIO, propagate from GPIO to override bit. + */ +static irqreturn_t brcmstb_usb_pinmap_gpio_isr(int irq, void *dev_id) +{ + struct in_pin *pin = dev_id; + + pr_debug("%s: %s pin changed state\n", __func__, pin->name); + sync_in_pin(pin); + return IRQ_HANDLED; +} + + +static void get_pin_counts(struct device_node *dn, int *in_count, + int *out_count) +{ + int in; + int out; + + *in_count = 0; + *out_count = 0; + in = of_property_count_strings(dn, "brcm,in-functions"); + if (in < 0) + return; + out = of_property_count_strings(dn, "brcm,out-functions"); + if (out < 0) + return; + *in_count = in; + *out_count = out; +} + +static int parse_pins(struct device *dev, struct device_node *dn, + struct brcmstb_usb_pinmap_data *pdata) +{ + struct out_pin *pout; + struct in_pin *pin; + int index; + int res; + int x; + + pin = pdata->in_pins; + for (x = 0, index = 0; x < pdata->in_count; x++) { + pin->gpiod = devm_gpiod_get_index(dev, "in", x, GPIOD_IN); + if (IS_ERR(pin->gpiod)) { + dev_err(dev, "Error getting gpio %s\n", pin->name); + return PTR_ERR(pin->gpiod); + + } + res = of_property_read_string_index(dn, "brcm,in-functions", x, + &pin->name); + if (res < 0) { + dev_err(dev, "Error getting brcm,in-functions for %s\n", + pin->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,in-masks", index++, + &pin->enable_mask); + if (res < 0) { + dev_err(dev, "Error getting 1st brcm,in-masks for %s\n", + pin->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,in-masks", index++, + &pin->value_mask); + if (res < 0) { + dev_err(dev, "Error getting 2nd brcm,in-masks for %s\n", + pin->name); + return res; + } + pin->pdata = pdata; + pin++; + } + pout = pdata->out_pins; + for (x = 0, index = 0; x < pdata->out_count; x++) { + pout->gpiod = devm_gpiod_get_index(dev, "out", x, + GPIOD_OUT_HIGH); + if (IS_ERR(pout->gpiod)) { + dev_err(dev, "Error getting gpio %s\n", pin->name); + return PTR_ERR(pout->gpiod); + } + res = of_property_read_string_index(dn, "brcm,out-functions", x, + &pout->name); + if (res < 0) { + dev_err(dev, "Error getting brcm,out-functions for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->enable_mask); + if (res < 0) { + dev_err(dev, "Error getting 1st brcm,out-masks for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->value_mask); + if (res < 0) { + dev_err(dev, "Error getting 2nd brcm,out-masks for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->changed_mask); + if (res < 0) { + dev_err(dev, "Error getting 3rd brcm,out-masks for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->clr_changed_mask); + if (res < 0) { + dev_err(dev, "Error getting 4th out-masks for %s\n", + pout->name); + return res; + } + pout++; + } + return 0; +} + +static void sync_all_pins(struct brcmstb_usb_pinmap_data *pdata) +{ + struct out_pin *pout; + struct in_pin *pin; + int val; + int x; + + /* + * Enable the override, clear any changed condition and + * propagate the state to the GPIO for all out pins. + */ + pout = pdata->out_pins; + for (x = 0; x < pdata->out_count; x++) { + pinmap_set(pdata->regs, pout->enable_mask); + pinmap_set(pdata->regs, pout->clr_changed_mask); + pinmap_unset(pdata->regs, pout->clr_changed_mask); + val = readl(pdata->regs) & pout->value_mask; + gpiod_set_value(pout->gpiod, val ? 1 : 0); + pout++; + } + + /* sync and enable all in pins. */ + pin = pdata->in_pins; + for (x = 0; x < pdata->in_count; x++) { + sync_in_pin(pin); + pinmap_set(pdata->regs, pin->enable_mask); + pin++; + } +} + +static int __init brcmstb_usb_pinmap_probe(struct platform_device *pdev) +{ + struct device_node *dn = pdev->dev.of_node; + struct brcmstb_usb_pinmap_data *pdata; + struct in_pin *pin; + struct resource *r; + int out_count; + int in_count; + int err; + int irq; + int x; + + get_pin_counts(dn, &in_count, &out_count); + if ((in_count + out_count) == 0) + return -EINVAL; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pdata = devm_kzalloc(&pdev->dev, + sizeof(*pdata) + + (sizeof(struct in_pin) * in_count) + + (sizeof(struct out_pin) * out_count), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->in_count = in_count; + pdata->out_count = out_count; + pdata->in_pins = (struct in_pin *)(pdata + 1); + pdata->out_pins = (struct out_pin *)(pdata->in_pins + in_count); + + pdata->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!pdata->regs) + return -ENOMEM; + platform_set_drvdata(pdev, pdata); + + err = parse_pins(&pdev->dev, dn, pdata); + if (err) + return err; + + sync_all_pins(pdata); + + if (out_count) { + + /* Enable interrupt for out pins */ + irq = platform_get_irq(pdev, 0); + err = devm_request_irq(&pdev->dev, irq, + brcmstb_usb_pinmap_ovr_isr, + IRQF_TRIGGER_RISING, + pdev->name, pdata); + if (err < 0) { + dev_err(&pdev->dev, "Error requesting IRQ\n"); + return err; + } + } + + for (x = 0, pin = pdata->in_pins; x < pdata->in_count; x++, pin++) { + irq = gpiod_to_irq(pin->gpiod); + if (irq < 0) { + dev_err(&pdev->dev, "Error getting IRQ for %s pin\n", + pin->name); + return irq; + } + err = devm_request_irq(&pdev->dev, irq, + brcmstb_usb_pinmap_gpio_isr, + IRQF_SHARED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + pdev->name, pin); + if (err < 0) { + dev_err(&pdev->dev, "Error requesting IRQ for %s pin\n", + pin->name); + return err; + } + } + + dev_dbg(&pdev->dev, "Driver probe succeeded\n"); + dev_dbg(&pdev->dev, "In pin count: %d, out pin count: %d\n", + pdata->in_count, pdata->out_count); + return 0; +} + + +static const struct of_device_id brcmstb_usb_pinmap_of_match[] = { + { .compatible = "brcm,usb-pinmap" }, + { }, +}; + +static struct platform_driver brcmstb_usb_pinmap_driver = { + .driver = { + .name = "brcm-usb-pinmap", + .of_match_table = brcmstb_usb_pinmap_of_match, + }, +}; + +static int __init brcmstb_usb_pinmap_init(void) +{ + return platform_driver_probe(&brcmstb_usb_pinmap_driver, + brcmstb_usb_pinmap_probe); +} + +module_init(brcmstb_usb_pinmap_init); +MODULE_AUTHOR("Al Cooper <alcooperx@gmail.com>"); +MODULE_DESCRIPTION("Broadcom USB Pinmap Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 70ec29681526..efbd317f2f25 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -384,7 +384,6 @@ static ssize_t iowarrior_write(struct file *file, retval = usb_set_report(dev->interface, 2, 0, buf, count); kfree(buf); goto exit; - break; case USB_DEVICE_ID_CODEMERCS_IOW56: case USB_DEVICE_ID_CODEMERCS_IOW56AM: case USB_DEVICE_ID_CODEMERCS_IOW28: @@ -454,14 +453,12 @@ static ssize_t iowarrior_write(struct file *file, retval = count; usb_free_urb(int_out_urb); goto exit; - break; default: /* what do we have here ? An unsupported Product-ID ? */ dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n", __func__, dev->product_id); retval = -EFAULT; goto exit; - break; } error: usb_free_coherent(dev->udev, dev->report_size, buf, diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index ba655b4af4fc..1c9e09138c10 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -797,7 +797,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ &get_version_reply, sizeof(get_version_reply), 1000, GFP_KERNEL); - if (!result) { + if (result) { dev_err(idev, "get version request failed: %d\n", result); retval = result; goto error; diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 655d9cb0651a..c12cdd015410 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -16,7 +16,7 @@ config USB_SISUSBVGA config USB_SISUSBVGA_CON bool "Text console and mode switching support" if USB_SISUSBVGA - depends on VT + depends on VT && BROKEN select FONT_8x16 help Say Y here if you want a VGA text console via the USB dongle or diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index e3165d79b5f6..73ebfa6e9715 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -137,6 +137,7 @@ static void yurex_interrupt(struct urb *urb) dev_err(&dev->interface->dev, "%s - overflow with length %d, actual length is %d\n", __func__, YUREX_BUF_SIZE, dev->urb->actual_length); + return; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: diff --git a/drivers/usb/mtu3/mtu3_debug.h b/drivers/usb/mtu3/mtu3_debug.h index 3084c46017c3..9a36134b322d 100644 --- a/drivers/usb/mtu3/mtu3_debug.h +++ b/drivers/usb/mtu3/mtu3_debug.h @@ -19,7 +19,6 @@ struct ssusb_mtk; struct mtu3_regset { char name[MTU3_DEBUGFS_NAME_LEN]; struct debugfs_regset32 regset; - size_t nregs; }; struct mtu3_file_map { diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c index fdeade6254ae..7537bfd651af 100644 --- a/drivers/usb/mtu3/mtu3_debugfs.c +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -127,7 +127,7 @@ static void mtu3_debugfs_regset(struct mtu3 *mtu, void __iomem *base, struct debugfs_regset32 *regset; struct mtu3_regset *mregs; - mregs = devm_kzalloc(mtu->dev, sizeof(*regset), GFP_KERNEL); + mregs = devm_kzalloc(mtu->dev, sizeof(*mregs), GFP_KERNEL); if (!mregs) return; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index c26683a2702b..c42937692207 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -467,6 +467,7 @@ static void musb_do_idle(struct timer_list *t) fallthrough; case OTG_STATE_A_IDLE: tusb_musb_set_vbus(musb, 0); + break; default: break; } diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index ef4787cd3d37..52eebcb88c1f 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -144,7 +144,7 @@ config USB_MV_OTG depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help - Say Y here if you want to build Marvell USB OTG transciever + Say Y here if you want to build Marvell USB OTG transceiver driver in kernel (including PXA and MMP series). This driver implements role switch between EHCI host driver and gadget driver. diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index 4a6462c92ef2..00506fb01bda 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1208,9 +1208,6 @@ static int isp1301_remove(struct i2c_client *i2c) #ifdef CONFIG_USB_OTG otg_unbind(isp); #endif - if (machine_is_omap_h2()) - gpio_free(2); - set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); flush_work(&isp->work); @@ -1480,6 +1477,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { int status; struct isp1301 *isp; + int irq; if (the_transceiver) return 0; @@ -1543,20 +1541,27 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) #endif if (machine_is_omap_h2()) { + struct gpio_desc *gpiod; + /* full speed signaling by default */ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SPEED); isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_SPD_SUSP_CTRL); - /* IRQ wired at M14 */ - omap_cfg_reg(M14_1510_GPIO2); - if (gpio_request(2, "isp1301") == 0) - gpio_direction_input(2); + gpiod = devm_gpiod_get(&i2c->dev, NULL, GPIOD_IN); + if (IS_ERR(gpiod)) { + dev_err(&i2c->dev, "cannot obtain H2 GPIO\n"); + goto fail; + } + gpiod_set_consumer_name(gpiod, "isp1301"); + irq = gpiod_to_irq(gpiod); isp->irq_type = IRQF_TRIGGER_FALLING; + } else { + irq = i2c->irq; } - status = request_irq(i2c->irq, isp1301_irq, + status = request_irq(irq, isp1301_irq, isp->irq_type, DRIVER_NAME, isp); if (status < 0) { dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index a2e2f56c88cd..28deaaec581f 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -81,10 +81,11 @@ #define CH341_QUIRK_SIMULATE_BREAK BIT(1) static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x4348, 0x5523) }, + { USB_DEVICE(0x1a86, 0x5512) }, + { USB_DEVICE(0x1a86, 0x5523) }, { USB_DEVICE(0x1a86, 0x7522) }, { USB_DEVICE(0x1a86, 0x7523) }, - { USB_DEVICE(0x1a86, 0x5523) }, + { USB_DEVICE(0x4348, 0x5523) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 5ee48b0650c4..5f6b82ebccc5 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -276,12 +276,12 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) priv->cfg.unknown2 = cfg->unknown2; spin_unlock_irqrestore(&priv->lock, flags); + kfree(cfg); + /* READ_ON and urb submission */ rc = usb_serial_generic_open(tty, port); - if (rc) { - retval = rc; - goto err_free_cfg; - } + if (rc) + return rc; rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), @@ -324,8 +324,6 @@ err_disable_read: KLSI_TIMEOUT); err_generic_close: usb_serial_generic_close(port); -err_free_cfg: - kfree(cfg); return retval; } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 54ca85cc920d..56d6f6d83bd7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -419,6 +419,7 @@ static void option_instat_callback(struct urb *urb); #define CINTERION_PRODUCT_PH8 0x0053 #define CINTERION_PRODUCT_AHXX 0x0055 #define CINTERION_PRODUCT_PLXX 0x0060 +#define CINTERION_PRODUCT_EXS82 0x006c #define CINTERION_PRODUCT_PH8_2RMNET 0x0082 #define CINTERION_PRODUCT_PH8_AUDIO 0x0083 #define CINTERION_PRODUCT_AHXX_2RMNET 0x0084 @@ -1105,9 +1106,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0xff, 0xff), .driver_info = NUMEP2 }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0, 0) }, - { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96, 0xff, 0xff, 0xff), - .driver_info = NUMEP2 }, - { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96, 0xff, 0, 0) }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff), .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) }, @@ -1902,6 +1902,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_CLS8, 0xff), .driver_info = RSVD(0) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EXS82, 0xff) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) }, { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) }, @@ -2046,12 +2047,13 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, { USB_DEVICE(0x0489, 0xe0b5), /* Foxconn T77W968 ESIM */ .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, - { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 */ + { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, { USB_DEVICE(0x2cb7, 0x0104), /* Fibocom NL678 series */ .driver_info = RSVD(4) | RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0105, 0xff), /* Fibocom NL678 series */ .driver_info = RSVD(6) }, + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) }, /* GosunCn GM500 ECM/NCM */ diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 98c1aa594e6c..5f7d678502be 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -861,6 +861,7 @@ static int ms_count_freeblock(struct us_data *us, u16 PhyBlock) case MS_LB_NOT_USED: case MS_LB_NOT_USED_ERASED: Count++; + break; default: break; } diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index 3d5f7d0ff0f1..2b098b55c4cb 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -431,7 +431,6 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) us->srb->sc_data_direction); /* Return fail, SCSI seems to handle this better. */ return USB_STOR_TRANSPORT_FAILED; - break; } return USB_STOR_TRANSPORT_GOOD; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 560efd1479ba..e5a971b83e3f 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -92,7 +92,7 @@ static int slave_alloc (struct scsi_device *sdev) static int slave_configure(struct scsi_device *sdev) { struct us_data *us = host_to_us(sdev->host); - struct device *dev = sdev->host->dma_dev; + struct device *dev = us->pusb_dev->bus->sysdev; /* * Many devices have trouble transferring more than 32KB at a time, diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 238a8088e17f..5eb895b19c55 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -416,7 +416,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, /* don't submit s-g requests during abort processing */ if (test_bit(US_FLIDX_ABORTING, &us->dflags)) - return USB_STOR_XFER_ERROR; + goto usb_stor_xfer_error; /* initialize the scatter-gather request block */ usb_stor_dbg(us, "xfer %u bytes, %d entries\n", length, num_sg); @@ -424,7 +424,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, sg, num_sg, length, GFP_NOIO); if (result) { usb_stor_dbg(us, "usb_sg_init returned %d\n", result); - return USB_STOR_XFER_ERROR; + goto usb_stor_xfer_error; } /* @@ -452,6 +452,11 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, *act_len = us->current_sg.bytes; return interpret_urb_result(us, pipe, length, result, us->current_sg.bytes); + +usb_stor_xfer_error: + if (act_len) + *act_len = 0; + return USB_STOR_XFER_ERROR; } /* diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index c8a577309e8f..bef89c6bd1d7 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -690,6 +690,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, fallthrough; case DMA_TO_DEVICE: cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB; + break; case DMA_NONE: break; } @@ -837,24 +838,17 @@ static int uas_slave_alloc(struct scsi_device *sdev) */ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); + if (devinfo->flags & US_FL_MAX_SECTORS_64) + blk_queue_max_hw_sectors(sdev->request_queue, 64); + else if (devinfo->flags & US_FL_MAX_SECTORS_240) + blk_queue_max_hw_sectors(sdev->request_queue, 240); + return 0; } static int uas_slave_configure(struct scsi_device *sdev) { struct uas_dev_info *devinfo = sdev->hostdata; - struct device *dev = sdev->host->dma_dev; - - if (devinfo->flags & US_FL_MAX_SECTORS_64) - blk_queue_max_hw_sectors(sdev->request_queue, 64); - else if (devinfo->flags & US_FL_MAX_SECTORS_240) - blk_queue_max_hw_sectors(sdev->request_queue, 240); - else if (devinfo->udev->speed >= USB_SPEED_SUPER) - blk_queue_max_hw_sectors(sdev->request_queue, 2048); - - blk_queue_max_hw_sectors(sdev->request_queue, - min_t(size_t, queue_max_hw_sectors(sdev->request_queue), - dma_max_mapping_size(dev) >> SECTOR_SHIFT)); if (devinfo->flags & US_FL_NO_REPORT_OPCODES) sdev->no_report_opcodes = 1; @@ -874,6 +868,9 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_NO_READ_CAPACITY_16) sdev->no_read_capacity_16 = 1; + /* Some disks cannot handle WRITE_SAME */ + if (devinfo->flags & US_FL_NO_SAME) + sdev->no_write_same = 1; /* * Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. @@ -1040,7 +1037,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) shost->can_queue = devinfo->qdepth - 2; usb_set_intfdata(intf, shost); - result = scsi_add_host_with_dma(shost, &intf->dev, udev->bus->sysdev); + result = scsi_add_host(shost, &intf->dev); if (result) goto free_streams; diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 711ab240058c..870e9cf3d5dc 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -35,12 +35,15 @@ UNUSUAL_DEV(0x054c, 0x087d, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), -/* Reported-by: Julian Groß <julian.g@posteo.de> */ +/* + * Initially Reported-by: Julian Groß <julian.g@posteo.de> + * Further reports David C. Partridge <david.partridge@perdrix.co.uk> + */ UNUSUAL_DEV(0x059f, 0x105f, 0x0000, 0x9999, "LaCie", "2Big Quadra USB3", USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_REPORT_OPCODES), + US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME), /* * Apricorn USB3 dongle sometimes returns "USBSUSBSUSBS" in response to SCSI diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index c2ef367cf257..90aa9c12ffac 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -541,6 +541,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) case 'j': f |= US_FL_NO_REPORT_LUNS; break; + case 'k': + f |= US_FL_NO_SAME; + break; case 'l': f |= US_FL_NOT_LOCKABLE; break; @@ -1049,9 +1052,8 @@ int usb_stor_probe2(struct us_data *us) goto BadDevice; usb_autopm_get_interface_no_resume(us->pusb_intf); snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s", - dev_name(dev)); - result = scsi_add_host_with_dma(us_to_host(us), dev, - us->pusb_dev->bus->sysdev); + dev_name(&us->pusb_intf->dev)); + result = scsi_add_host(us_to_host(us), dev); if (result) { dev_warn(dev, "Unable to add the scsi host\n"); diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index e7f120874c48..270e81c087e9 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -64,8 +64,9 @@ config TYPEC_HD3SS3220 config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C - depends on REGMAP_I2C - depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH + select POWER_SUPPLY + select REGMAP_I2C + select USB_ROLE_SWITCH help Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power Delivery controller. diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 35eec707cb51..4f6e58dfb81d 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -11,6 +11,7 @@ #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> +#include <linux/usb/pd_vdo.h> #include "bus.h" @@ -18,6 +19,7 @@ struct typec_plug { struct device dev; enum typec_plug_index index; struct ida mode_ids; + int num_altmodes; }; struct typec_cable { @@ -33,6 +35,7 @@ struct typec_partner { struct usb_pd_identity *identity; enum typec_accessory accessory; struct ida mode_ids; + int num_altmodes; }; struct typec_port { @@ -81,6 +84,29 @@ static const char * const typec_accessory_modes[] = { [TYPEC_ACCESSORY_DEBUG] = "debug", }; +/* Product types defined in USB PD Specification R3.0 V2.0 */ +static const char * const product_type_ufp[8] = { + [IDH_PTYPE_UNDEF] = "undefined", + [IDH_PTYPE_HUB] = "hub", + [IDH_PTYPE_PERIPH] = "peripheral", + [IDH_PTYPE_PSD] = "psd", + [IDH_PTYPE_AMA] = "ama", +}; + +static const char * const product_type_dfp[8] = { + [IDH_PTYPE_DFP_UNDEF] = "undefined", + [IDH_PTYPE_DFP_HUB] = "hub", + [IDH_PTYPE_DFP_HOST] = "host", + [IDH_PTYPE_DFP_PB] = "power_brick", + [IDH_PTYPE_DFP_AMC] = "amc", +}; + +static const char * const product_type_cable[8] = { + [IDH_PTYPE_UNDEF] = "undefined", + [IDH_PTYPE_PCABLE] = "passive", + [IDH_PTYPE_ACABLE] = "active", +}; + static struct usb_pd_identity *get_pd_identity(struct device *dev) { if (is_typec_partner(dev)) { @@ -95,6 +121,32 @@ static struct usb_pd_identity *get_pd_identity(struct device *dev) return NULL; } +static const char *get_pd_product_type(struct device *dev) +{ + struct typec_port *port = to_typec_port(dev->parent); + struct usb_pd_identity *id = get_pd_identity(dev); + const char *ptype = NULL; + + if (is_typec_partner(dev)) { + if (!id) + return NULL; + + if (port->data_role == TYPEC_HOST) + ptype = product_type_ufp[PD_IDH_PTYPE(id->id_header)]; + else + ptype = product_type_dfp[PD_IDH_DFP_PTYPE(id->id_header)]; + } else if (is_typec_cable(dev)) { + if (id) + ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)]; + else + ptype = to_typec_cable(dev)->active ? + product_type_cable[IDH_PTYPE_ACABLE] : + product_type_cable[IDH_PTYPE_PCABLE]; + } + + return ptype; +} + static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -122,10 +174,40 @@ static ssize_t product_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(product); +static ssize_t product_type_vdo1_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_pd_identity *id = get_pd_identity(dev); + + return sysfs_emit(buf, "0x%08x\n", id->vdo[0]); +} +static DEVICE_ATTR_RO(product_type_vdo1); + +static ssize_t product_type_vdo2_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_pd_identity *id = get_pd_identity(dev); + + return sysfs_emit(buf, "0x%08x\n", id->vdo[1]); +} +static DEVICE_ATTR_RO(product_type_vdo2); + +static ssize_t product_type_vdo3_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_pd_identity *id = get_pd_identity(dev); + + return sysfs_emit(buf, "0x%08x\n", id->vdo[2]); +} +static DEVICE_ATTR_RO(product_type_vdo3); + static struct attribute *usb_pd_id_attrs[] = { &dev_attr_id_header.attr, &dev_attr_cert_stat.attr, &dev_attr_product.attr, + &dev_attr_product_type_vdo1.attr, + &dev_attr_product_type_vdo2.attr, + &dev_attr_product_type_vdo3.attr, NULL }; @@ -139,13 +221,49 @@ static const struct attribute_group *usb_pd_id_groups[] = { NULL, }; +static void typec_product_type_notify(struct device *dev) +{ + char *envp[2] = { }; + const char *ptype; + + ptype = get_pd_product_type(dev); + if (!ptype) + return; + + sysfs_notify(&dev->kobj, NULL, "type"); + + envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype); + if (!envp[0]) + return; + + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); + kfree(envp[0]); +} + static void typec_report_identity(struct device *dev) { sysfs_notify(&dev->kobj, "identity", "id_header"); sysfs_notify(&dev->kobj, "identity", "cert_stat"); sysfs_notify(&dev->kobj, "identity", "product"); + sysfs_notify(&dev->kobj, "identity", "product_type_vdo1"); + sysfs_notify(&dev->kobj, "identity", "product_type_vdo2"); + sysfs_notify(&dev->kobj, "identity", "product_type_vdo3"); + typec_product_type_notify(dev); } +static ssize_t +type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + const char *ptype; + + ptype = get_pd_product_type(dev); + if (!ptype) + return 0; + + return sysfs_emit(buf, "%s\n", ptype); +} +static DEVICE_ATTR_RO(type); + /* ------------------------------------------------------------------------- */ /* Alternate Modes */ @@ -382,7 +500,7 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group typec_altmode_group = { +static const struct attribute_group typec_altmode_group = { .is_visible = typec_altmode_attr_is_visible, .attrs = typec_altmode_attrs, }; @@ -532,12 +650,60 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev, } static DEVICE_ATTR_RO(supports_usb_power_delivery); +static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct typec_partner *partner; + struct typec_plug *plug; + int num_altmodes; + + if (is_typec_partner(dev)) { + partner = to_typec_partner(dev); + num_altmodes = partner->num_altmodes; + } else if (is_typec_plug(dev)) { + plug = to_typec_plug(dev); + num_altmodes = plug->num_altmodes; + } else { + return 0; + } + + return sysfs_emit(buf, "%d\n", num_altmodes); +} +static DEVICE_ATTR_RO(number_of_alternate_modes); + static struct attribute *typec_partner_attrs[] = { &dev_attr_accessory_mode.attr, &dev_attr_supports_usb_power_delivery.attr, + &dev_attr_number_of_alternate_modes.attr, + &dev_attr_type.attr, + NULL +}; + +static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); + + if (attr == &dev_attr_number_of_alternate_modes.attr) { + if (partner->num_altmodes < 0) + return 0; + } + + if (attr == &dev_attr_type.attr) + if (!get_pd_product_type(kobj_to_dev(kobj))) + return 0; + + return attr->mode; +} + +static const struct attribute_group typec_partner_group = { + .is_visible = typec_partner_attr_is_visible, + .attrs = typec_partner_attrs +}; + +static const struct attribute_group *typec_partner_groups[] = { + &typec_partner_group, NULL }; -ATTRIBUTE_GROUPS(typec_partner); static void typec_partner_release(struct device *dev) { @@ -571,6 +737,37 @@ int typec_partner_set_identity(struct typec_partner *partner) EXPORT_SYMBOL_GPL(typec_partner_set_identity); /** + * typec_partner_set_num_altmodes - Set the number of available partner altmodes + * @partner: The partner to be updated. + * @num_altmodes: The number of altmodes we want to specify as available. + * + * This routine is used to report the number of alternate modes supported by the + * partner. This value is *not* enforced in alternate mode registration routines. + * + * @partner.num_altmodes is set to -1 on partner registration, denoting that + * a valid value has not been set for it yet. + * + * Returns 0 on success or negative error number on failure. + */ +int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes) +{ + int ret; + + if (num_altmodes < 0) + return -EINVAL; + + partner->num_altmodes = num_altmodes; + ret = sysfs_update_group(&partner->dev.kobj, &typec_partner_group); + if (ret < 0) + return ret; + + sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes"); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_partner_set_num_altmodes); + +/** * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode * @partner: USB Type-C Partner that supports the alternate mode * @desc: Description of the alternate mode @@ -612,6 +809,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, ida_init(&partner->mode_ids); partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; + partner->num_altmodes = -1; if (desc->identity) { /* @@ -662,12 +860,71 @@ static void typec_plug_release(struct device *dev) kfree(plug); } +static struct attribute *typec_plug_attrs[] = { + &dev_attr_number_of_alternate_modes.attr, + NULL +}; + +static umode_t typec_plug_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_plug *plug = to_typec_plug(kobj_to_dev(kobj)); + + if (attr == &dev_attr_number_of_alternate_modes.attr) { + if (plug->num_altmodes < 0) + return 0; + } + + return attr->mode; +} + +static const struct attribute_group typec_plug_group = { + .is_visible = typec_plug_attr_is_visible, + .attrs = typec_plug_attrs +}; + +static const struct attribute_group *typec_plug_groups[] = { + &typec_plug_group, + NULL +}; + static const struct device_type typec_plug_dev_type = { .name = "typec_plug", + .groups = typec_plug_groups, .release = typec_plug_release, }; /** + * typec_plug_set_num_altmodes - Set the number of available plug altmodes + * @plug: The plug to be updated. + * @num_altmodes: The number of altmodes we want to specify as available. + * + * This routine is used to report the number of alternate modes supported by the + * plug. This value is *not* enforced in alternate mode registration routines. + * + * @plug.num_altmodes is set to -1 on plug registration, denoting that + * a valid value has not been set for it yet. + * + * Returns 0 on success or negative error number on failure. + */ +int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes) +{ + int ret; + + if (num_altmodes < 0) + return -EINVAL; + + plug->num_altmodes = num_altmodes; + ret = sysfs_update_group(&plug->dev.kobj, &typec_plug_group); + if (ret < 0) + return ret; + + sysfs_notify(&plug->dev.kobj, NULL, "number_of_alternate_modes"); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_plug_set_num_altmodes); + +/** * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode * @plug: USB Type-C Cable Plug that supports the alternate mode * @desc: Description of the alternate mode @@ -712,6 +969,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, sprintf(name, "plug%d", desc->index); ida_init(&plug->mode_ids); + plug->num_altmodes = -1; plug->index = desc->index; plug->dev.class = typec_class; plug->dev.parent = &cable->dev; @@ -744,15 +1002,6 @@ EXPORT_SYMBOL_GPL(typec_unregister_plug); /* Type-C Cables */ -static ssize_t -type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct typec_cable *cable = to_typec_cable(dev); - - return sprintf(buf, "%s\n", cable->active ? "active" : "passive"); -} -static DEVICE_ATTR_RO(type); - static const char * const typec_plug_types[] = { [USB_PLUG_NONE] = "unknown", [USB_PLUG_TYPE_A] = "type-a", @@ -1309,7 +1558,7 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group typec_group = { +static const struct attribute_group typec_group = { .is_visible = typec_attr_is_visible, .attrs = typec_attrs, }; @@ -1352,6 +1601,11 @@ const struct device_type typec_port_dev_type = { /* --------------------------------------- */ /* Driver callbacks to report role updates */ +static int partner_match(struct device *dev, void *data) +{ + return is_typec_partner(dev); +} + /** * typec_set_data_role - Report data role change * @port: The USB Type-C Port where the role was changed @@ -1361,12 +1615,23 @@ const struct device_type typec_port_dev_type = { */ void typec_set_data_role(struct typec_port *port, enum typec_data_role role) { + struct device *partner_dev; + if (port->data_role == role) return; port->data_role = role; sysfs_notify(&port->dev.kobj, NULL, "data_role"); kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); + + partner_dev = device_find_child(&port->dev, NULL, partner_match); + if (!partner_dev) + return; + + if (to_typec_partner(partner_dev)->identity) + typec_product_type_notify(partner_dev); + + put_device(partner_dev); } EXPORT_SYMBOL_GPL(typec_set_data_role); @@ -1407,11 +1672,6 @@ void typec_set_vconn_role(struct typec_port *port, enum typec_role role) } EXPORT_SYMBOL_GPL(typec_set_vconn_role); -static int partner_match(struct device *dev, void *data) -{ - return is_typec_partner(dev); -} - /** * typec_set_pwr_opmode - Report changed power operation mode * @port: The USB Type-C Port where the mode was changed diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index d7f63b74c6b1..e58ae8a7fefb 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -176,6 +176,7 @@ static int hsl_orientation(struct pmc_usb_port *port) static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) { u8 response[4]; + u8 status_res; int ret; /* @@ -189,9 +190,13 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) if (ret) return ret; - if (response[2] & PMC_USB_RESP_STATUS_FAILURE) { - if (response[2] & PMC_USB_RESP_STATUS_FATAL) + status_res = (msg[0] & 0xf) < PMC_USB_SAFE_MODE ? + response[2] : response[1]; + + if (status_res & PMC_USB_RESP_STATUS_FAILURE) { + if (status_res & PMC_USB_RESP_STATUS_FATAL) return -EIO; + return -EBUSY; } @@ -256,6 +261,7 @@ static int pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) { struct typec_thunderbolt_data *data = state->data; + u8 cable_rounded = TBT_CABLE_ROUNDED_SUPPORT(data->cable_mode); u8 cable_speed = TBT_CABLE_SPEED(data->cable_mode); struct altmode_req req = { }; @@ -284,6 +290,8 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); + req.mode_data |= PMC_USB_ALTMODE_TBT_GEN(cable_rounded); + return pmc_usb_command(port, (void *)&req, sizeof(req)); } diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 99562cc65ca6..ebc46b9f776c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -343,12 +343,11 @@ static int fusb302_sw_reset(struct fusb302_chip *chip) return ret; } -static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip) +static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip, u8 retry_count) { int ret = 0; - ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3, - FUSB_REG_CONTROL3_N_RETRIES_3 | + ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3, retry_count | FUSB_REG_CONTROL3_AUTO_RETRY); return ret; @@ -399,7 +398,7 @@ static int tcpm_init(struct tcpc_dev *dev) ret = fusb302_sw_reset(chip); if (ret < 0) return ret; - ret = fusb302_enable_tx_auto_retries(chip); + ret = fusb302_enable_tx_auto_retries(chip, FUSB_REG_CONTROL3_N_RETRIES_3); if (ret < 0) return ret; ret = fusb302_init_interrupt(chip); @@ -1017,7 +1016,7 @@ static const char * const transmit_type_name[] = { }; static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type, - const struct pd_message *msg) + const struct pd_message *msg, unsigned int negotiated_rev) { struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, tcpc_dev); @@ -1026,6 +1025,13 @@ static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type, mutex_lock(&chip->lock); switch (type) { case TCPC_TX_SOP: + /* nRetryCount 3 in P2.0 spec, whereas 2 in PD3.0 spec */ + ret = fusb302_enable_tx_auto_retries(chip, negotiated_rev > PD_REV20 ? + FUSB_REG_CONTROL3_N_RETRIES_2 : + FUSB_REG_CONTROL3_N_RETRIES_3); + if (ret < 0) + fusb302_log(chip, "Cannot update retry count ret=%d", ret); + ret = fusb302_pd_send_message(chip, msg); if (ret < 0) fusb302_log(chip, diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index f9f0af64da5f..af5524338a63 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -18,7 +18,11 @@ #include "tcpci.h" -#define PD_RETRY_COUNT 3 +#define PD_RETRY_COUNT_DEFAULT 3 +#define PD_RETRY_COUNT_3_0_OR_HIGHER 2 +#define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 +#define AUTO_DISCHARGE_PD_HEADROOM_MV 850 +#define AUTO_DISCHARGE_PPS_HEADROOM_MV 1250 struct tcpci { struct device *dev; @@ -268,6 +272,58 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); } +static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + int ret; + + ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE, + enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0); + return ret; +} + +static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode, + bool pps_active, u32 requested_vbus_voltage_mv) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + unsigned int pwr_ctrl, threshold = 0; + int ret; + + /* + * Indicates that vbus is going to go away due PR_SWAP, hard reset etc. + * Do not discharge vbus here. + */ + if (requested_vbus_voltage_mv == 0) + goto write_thresh; + + ret = regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &pwr_ctrl); + if (ret < 0) + return ret; + + if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) { + /* To prevent disconnect when the source is fast role swap is capable. */ + threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; + } else if (mode == TYPEC_PWR_MODE_PD) { + if (pps_active) + threshold = (95 * requested_vbus_voltage_mv / 100) - + AUTO_DISCHARGE_PD_HEADROOM_MV; + else + threshold = (95 * requested_vbus_voltage_mv / 100) - + AUTO_DISCHARGE_PPS_HEADROOM_MV; + } else { + /* 3.5V for non-pd sink */ + threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; + } + + threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV; + + if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX) + return -EINVAL; + +write_thresh: + return tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold); +} + static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) { struct tcpci *tcpci = tcpc_to_tcpci(dev); @@ -284,6 +340,14 @@ static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) return ret; } +static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + + if (tcpci->data->frs_sourcing_vbus) + tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); +} + static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -339,6 +403,19 @@ static int tcpci_get_vbus(struct tcpc_dev *tcpc) return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); } +static bool tcpci_is_vbus_vsafe0v(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, ®); + if (ret < 0) + return false; + + return !!(reg & TCPC_EXTENDED_STATUS_VSAFE0V); +} + static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -384,9 +461,8 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) return 0; } -static int tcpci_pd_transmit(struct tcpc_dev *tcpc, - enum tcpm_transmit_type type, - const struct pd_message *msg) +static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, + const struct pd_message *msg, unsigned int negotiated_rev) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); u16 header = msg ? le16_to_cpu(msg->header) : 0; @@ -434,7 +510,9 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, } } - reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); + /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ + reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) + << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); if (ret < 0) return ret; @@ -491,12 +569,22 @@ static int tcpci_init(struct tcpc_dev *tcpc) TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; if (tcpci->controls_vbus) reg |= TCPC_ALERT_POWER_STATUS; + /* Enable VSAFE0V status interrupt when detecting VSAFE0V is supported */ + if (tcpci->data->vbus_vsafe0v) { + reg |= TCPC_ALERT_EXTENDED_STATUS; + ret = regmap_write(tcpci->regmap, TCPC_EXTENDED_STATUS_MASK, + TCPC_EXTENDED_STATUS_VSAFE0V); + if (ret < 0) + return ret; + } return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); } irqreturn_t tcpci_irq(struct tcpci *tcpci) { u16 status; + int ret; + unsigned int raw; tcpci_read16(tcpci, TCPC_ALERT, &status); @@ -512,15 +600,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) tcpm_cc_change(tcpci->port); if (status & TCPC_ALERT_POWER_STATUS) { - unsigned int reg; - - regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, ®); - + regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw); /* * If power status mask has been reset, then the TCPC * has reset. */ - if (reg == 0xff) + if (raw == 0xff) tcpm_tcpc_reset(tcpci->port); else tcpm_vbus_change(tcpci->port); @@ -559,6 +644,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) tcpm_pd_receive(tcpci->port, &msg); } + if (status & TCPC_ALERT_EXTENDED_STATUS) { + ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, &raw); + if (!ret && (raw & TCPC_EXTENDED_STATUS_VSAFE0V)) + tcpm_vbus_change(tcpci->port); + } + if (status & TCPC_ALERT_RX_HARD_RST) tcpm_pd_hard_reset(tcpci->port); @@ -628,6 +719,16 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.pd_transmit = tcpci_pd_transmit; tcpci->tcpc.set_bist_data = tcpci_set_bist_data; tcpci->tcpc.enable_frs = tcpci_enable_frs; + tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; + + if (tcpci->data->auto_discharge_disconnect) { + tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; + tcpci->tcpc.set_auto_vbus_discharge_threshold = + tcpci_set_auto_vbus_discharge_threshold; + } + + if (tcpci->data->vbus_vsafe0v) + tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v; err = tcpci_parse_config(tcpci); if (err < 0) diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h index 5ef07a56d67a..116a69c85e38 100644 --- a/drivers/usb/typec/tcpm/tcpci.h +++ b/drivers/usb/typec/tcpm/tcpci.h @@ -8,6 +8,8 @@ #ifndef __LINUX_USB_TCPCI_H #define __LINUX_USB_TCPCI_H +#include <linux/usb/typec.h> + #define TCPC_VENDOR_ID 0x0 #define TCPC_PRODUCT_ID 0x2 #define TCPC_BCD_DEV 0x4 @@ -47,6 +49,9 @@ #define TCPC_TCPC_CTRL_ORIENTATION BIT(0) #define TCPC_TCPC_CTRL_BIST_TM BIT(1) +#define TCPC_EXTENDED_STATUS 0x20 +#define TCPC_EXTENDED_STATUS_VSAFE0V BIT(0) + #define TCPC_ROLE_CTRL 0x1a #define TCPC_ROLE_CTRL_DRP BIT(6) #define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 @@ -67,6 +72,7 @@ #define TCPC_POWER_CTRL 0x1c #define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) +#define TCPC_POWER_CTRL_AUTO_DISCHARGE BIT(4) #define TCPC_FAST_ROLE_SWAP_EN BIT(7) #define TCPC_CC_STATUS 0x1d @@ -133,6 +139,8 @@ #define TCPC_VBUS_VOLTAGE 0x70 #define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV 25 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX 0x3ff #define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 @@ -140,20 +148,32 @@ /* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */ #define TCPC_TRANSMIT_BUFFER_MAX_LEN 31 +struct tcpci; + /* - * @TX_BUF_BYTE_x_hidden + * @TX_BUF_BYTE_x_hidden: * optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT. + * @frs_sourcing_vbus: + * Optional; Callback to perform chip specific operations when FRS + * is sourcing vbus. + * @auto_discharge_disconnect: + * Optional; Enables TCPC to autonously discharge vbus on disconnect. + * @vbus_vsafe0v: + * optional; Set when TCPC can detect whether vbus is at VSAFE0V. */ -struct tcpci; struct tcpci_data { struct regmap *regmap; unsigned char TX_BUF_BYTE_x_hidden:1; + unsigned char auto_discharge_disconnect:1; + unsigned char vbus_vsafe0v:1; + int (*init)(struct tcpci *tcpci, struct tcpci_data *data); int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, bool enable); int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, enum typec_cc_status cc); int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink); + void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data); }; struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index 723d7dd38f75..319266329b42 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -112,11 +112,18 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip) return; } + /* Enable VSAFE0V detection */ + ret = max_tcpci_write8(chip, TCPC_EXTENDED_STATUS_MASK, TCPC_EXTENDED_STATUS_VSAFE0V); + if (ret < 0) { + dev_err(chip->dev, "Unable to unmask TCPC_EXTENDED_STATUS_VSAFE0V ret:%d\n", ret); + return; + } + alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS | TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS | /* Enable Extended alert for detecting Fast Role Swap Signal */ - TCPC_ALERT_EXTND; + TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS; ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask); if (ret < 0) { @@ -238,23 +245,22 @@ static void process_power_status(struct max_tcpci_chip *chip) if (ret < 0) return; - if (pwr_status == 0xff) { + if (pwr_status == 0xff) max_tcpci_init_regs(chip); - } else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) { + else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) tcpm_sourcing_vbus(chip->port); - /* - * Alawys re-enable boost here. - * In normal case, when say an headset is attached, TCPM would - * have instructed to TCPC to enable boost, so the call is a - * no-op. - * But for Fast Role Swap case, Boost turns on autonomously without - * AP intervention, but, needs AP to enable source mode explicitly - * for AP to regain control. - */ - max_tcpci_set_vbus(chip->tcpci, &chip->data, true, false); - } else { + else tcpm_vbus_change(chip->port); - } +} + +static void max_tcpci_frs_sourcing_vbus(struct tcpci *tcpci, struct tcpci_data *tdata) +{ + /* + * For Fast Role Swap case, Boost turns on autonomously without + * AP intervention, but, needs AP to enable source mode explicitly + * for AP to regain control. + */ + max_tcpci_set_vbus(tcpci, tdata, true, false); } static void process_tx(struct max_tcpci_chip *chip, u16 status) @@ -316,6 +322,12 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) } } + if (status & TCPC_ALERT_EXTENDED_STATUS) { + ret = max_tcpci_read8(chip, TCPC_EXTENDED_STATUS, (u8 *)®_status); + if (ret >= 0 && (reg_status & TCPC_EXTENDED_STATUS_VSAFE0V)) + tcpm_vbus_change(chip->port); + } + if (status & TCPC_ALERT_RX_STATUS) process_rx(chip, status); @@ -344,7 +356,7 @@ static irqreturn_t max_tcpci_irq(int irq, void *dev_id) { struct max_tcpci_chip *chip = dev_id; u16 status; - irqreturn_t irq_return; + irqreturn_t irq_return = IRQ_HANDLED; int ret; if (!chip->port) @@ -441,10 +453,13 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id chip->data.start_drp_toggling = max_tcpci_start_toggling; chip->data.TX_BUF_BYTE_x_hidden = true; chip->data.init = tcpci_init; + chip->data.frs_sourcing_vbus = max_tcpci_frs_sourcing_vbus; + chip->data.auto_discharge_disconnect = true; + chip->data.vbus_vsafe0v = true; max_tcpci_init_regs(chip); chip->tcpci = tcpci_register_port(chip->dev, &chip->data); - if (IS_ERR_OR_NULL(chip->tcpci)) { + if (IS_ERR(chip->tcpci)) { dev_err(&client->dev, "TCPCI port registration failed"); ret = PTR_ERR(chip->tcpci); return PTR_ERR(chip->tcpci); @@ -481,7 +496,7 @@ MODULE_DEVICE_TABLE(i2c, max_tcpci_id); #ifdef CONFIG_OF static const struct of_device_id max_tcpci_of_match[] = { - { .compatible = "maxim,tcpc", }, + { .compatible = "maxim,max33359", }, {}, }; MODULE_DEVICE_TABLE(of, max_tcpci_of_match); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a6fae1f86505..cedc6cf82d61 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -258,7 +258,19 @@ struct tcpm_port { bool attached; bool connected; enum typec_port_type port_type; + + /* + * Set to true when vbus is greater than VSAFE5V min. + * Set to false when vbus falls below vSinkDisconnect max threshold. + */ bool vbus_present; + + /* + * Set to true when vbus is less than VSAFE0V max. + * Set to false when vbus is greater than VSAFE0V max. + */ + bool vbus_vsafe0v; + bool vbus_never_low; bool vbus_source; bool vbus_charge; @@ -363,8 +375,8 @@ struct tcpm_port { /* port belongs to a self powered device */ bool self_powered; - /* FRS */ - enum frs_typec_current frs_current; + /* Sink FRS */ + enum frs_typec_current new_source_frs_current; /* Sink caps have been queried */ bool sink_cap_done; @@ -667,7 +679,7 @@ static int tcpm_pd_transmit(struct tcpm_port *port, tcpm_log(port, "PD TX, type: %#x", type); reinit_completion(&port->tx_complete); - ret = port->tcpc->pd_transmit(port->tcpc, type, msg); + ret = port->tcpc->pd_transmit(port->tcpc, type, msg, port->negotiated_rev); if (ret < 0) return ret; @@ -1706,6 +1718,24 @@ static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload, } } +static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port, + enum typec_pwr_opmode mode, bool pps_active, + u32 requested_vbus_voltage) +{ + int ret; + + if (!port->tcpc->set_auto_vbus_discharge_threshold) + return 0; + + ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc, mode, pps_active, + requested_vbus_voltage); + tcpm_log_force(port, + "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u ret:%d", + mode, pps_active ? 'y' : 'n', requested_vbus_voltage, ret); + + return ret; +} + static void tcpm_pd_data_request(struct tcpm_port *port, const struct pd_message *msg) { @@ -1713,7 +1743,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port, unsigned int cnt = pd_header_cnt_le(msg->header); unsigned int rev = pd_header_rev_le(msg->header); unsigned int i; - enum frs_typec_current frs_current; + enum frs_typec_current partner_frs_current; bool frs_enable; int ret; @@ -1786,12 +1816,13 @@ static void tcpm_pd_data_request(struct tcpm_port *port, for (i = 0; i < cnt; i++) port->sink_caps[i] = le32_to_cpu(msg->payload[i]); - frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >> + partner_frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >> PDO_FIXED_FRS_CURR_SHIFT; - frs_enable = frs_current && (frs_current <= port->frs_current); + frs_enable = partner_frs_current && (partner_frs_current <= + port->new_source_frs_current); tcpm_log(port, "Port partner FRS capable partner_frs_current:%u port_frs_current:%u enable:%c", - frs_current, port->frs_current, frs_enable ? 'y' : 'n'); + partner_frs_current, port->new_source_frs_current, frs_enable ? 'y' : 'n'); if (frs_enable) { ret = port->tcpc->enable_frs(port->tcpc, true); tcpm_log(port, "Enable FRS %s, ret:%d\n", ret ? "fail" : "success", ret); @@ -1875,6 +1906,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, port->current_limit, port->supply_voltage); port->explicit_contract = true; + tcpm_set_auto_vbus_discharge_threshold(port, + TYPEC_PWR_MODE_PD, + port->pps_data.active, + port->supply_voltage); tcpm_set_state(port, SNK_READY, 0); } else { /* @@ -2789,8 +2824,12 @@ static int tcpm_src_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, - tcpm_data_role_for_source(port)); + if (port->tcpc->enable_auto_vbus_discharge) { + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true); + tcpm_log_force(port, "enable vbus discharge ret:%d", ret); + } + + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port)); if (ret < 0) return ret; @@ -2857,6 +2896,12 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) static void tcpm_reset_port(struct tcpm_port *port) { + int ret; + + if (port->tcpc->enable_auto_vbus_discharge) { + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false); + tcpm_log_force(port, "Disable vbus discharge ret:%d", ret); + } tcpm_unregister_altmodes(port); tcpm_typec_disconnect(port); port->attached = false; @@ -2921,8 +2966,13 @@ static int tcpm_snk_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SINK, - tcpm_data_role_for_sink(port)); + if (port->tcpc->enable_auto_vbus_discharge) { + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true); + tcpm_log_force(port, "enable vbus discharge ret:%d", ret); + } + + ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port)); if (ret < 0) return ret; @@ -2997,10 +3047,9 @@ static inline enum tcpm_state unattached_state(struct tcpm_port *port) static void tcpm_check_send_discover(struct tcpm_port *port) { if (port->data_role == TYPEC_HOST && port->send_discover && - port->pd_capable) { + port->pd_capable) tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); - port->send_discover = false; - } + port->send_discover = false; } static void tcpm_swap_complete(struct tcpm_port *port, int result) @@ -3056,7 +3105,7 @@ static void run_state_machine(struct tcpm_port *port) else if (tcpm_port_is_audio(port)) tcpm_set_state(port, AUDIO_ACC_ATTACHED, PD_T_CC_DEBOUNCE); - else if (tcpm_port_is_source(port)) + else if (tcpm_port_is_source(port) && port->vbus_vsafe0v) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, @@ -3086,15 +3135,13 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRY_WAIT_DEBOUNCE: tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, - PD_T_PD_DEBOUNCE); + PD_T_TRY_CC_DEBOUNCE); break; case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: - if (port->vbus_present && tcpm_port_is_sink(port)) { + if (port->vbus_present && tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACHED, 0); - } else { - tcpm_set_state(port, SRC_TRYWAIT, 0); + else port->max_wait = 0; - } break; case SRC_TRYWAIT: tcpm_set_cc(port, tcpm_rp_cc(port)); @@ -3506,6 +3553,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: + /* Do not discharge/disconnect during hard reseet */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0); memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); if (port->pd_capable) @@ -3548,6 +3597,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_charge(port, true); } tcpm_set_attached_state(port, true); + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); tcpm_set_state(port, SNK_STARTUP, 0); break; @@ -3649,6 +3699,10 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0); break; case PR_SWAP_SRC_SNK_TRANSITION_OFF: + /* + * Prevent vbus discharge circuit from turning on during PR_SWAP + * as this is not a disconnect. + */ tcpm_set_vbus(port, false); port->explicit_contract = false; /* allow time for Vbus discharge, must be < tSrcSwapStdby */ @@ -3677,9 +3731,17 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON); break; case PR_SWAP_SRC_SNK_SINK_ON: + /* Set the vbus disconnect threshold for implicit contract */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); tcpm_set_state(port, SNK_STARTUP, 0); break; case PR_SWAP_SNK_SRC_SINK_OFF: + /* + * Prevent vbus discharge circuit from turning on during PR_SWAP + * as this is not a disconnect. + */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, + port->pps_data.active, 0); tcpm_set_charge(port, false); tcpm_set_state(port, hard_reset_state(port), PD_T_PS_SOURCE_OFF); @@ -4000,6 +4062,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, if (!tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (!tcpm_port_is_sink(port)) + tcpm_set_state(port, SRC_TRYWAIT, PD_T_TRY_CC_DEBOUNCE); + else + tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, 0); + break; case SNK_TRYWAIT: /* Do nothing, waiting for tCCDebounce */ break; @@ -4040,6 +4108,12 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) { tcpm_log_force(port, "VBUS on"); port->vbus_present = true; + /* + * When vbus_present is true i.e. Voltage at VBUS is greater than VSAFE5V implicitly + * states that vbus is not at VSAFE0V, hence clear the vbus_vsafe0v flag here. + */ + port->vbus_vsafe0v = false; + switch (port->state) { case SNK_TRANSITION_SINK_VBUS: port->explicit_contract = true; @@ -4086,11 +4160,24 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) case SNK_TRYWAIT_DEBOUNCE: /* Do nothing, waiting for Rp */ break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (port->vbus_present && tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_ATTACHED, 0); + break; case SRC_TRY_WAIT: case SRC_TRY_DEBOUNCE: /* Do nothing, waiting for sink detection */ break; + case FR_SWAP_SEND: + case FR_SWAP_SEND_TIMEOUT: + case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF: + case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED: + if (port->tcpc->frs_sourcing_vbus) + port->tcpc->frs_sourcing_vbus(port->tcpc); + break; case FR_SWAP_SNK_SRC_NEW_SINK_READY: + if (port->tcpc->frs_sourcing_vbus) + port->tcpc->frs_sourcing_vbus(port->tcpc); tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0); break; @@ -4116,16 +4203,8 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) case SNK_HARD_RESET_SINK_OFF: tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0); break; - case SRC_HARD_RESET_VBUS_OFF: - /* - * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait - * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V. - */ - tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); - break; case HARD_RESET_SEND: break; - case SNK_TRY: /* Do nothing, waiting for timeout */ break; @@ -4156,6 +4235,14 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) /* Do nothing, expected */ break; + case PR_SWAP_SNK_SRC_SOURCE_ON: + /* + * Do nothing when vbus off notification is received. + * TCPM can wait for PD_T_NEWSRC in PR_SWAP_SNK_SRC_SOURCE_ON + * for the vbus source to ramp up. + */ + break; + case PORT_RESET_WAIT_OFF: tcpm_set_state(port, tcpm_default_state(port), 0); break; @@ -4188,6 +4275,28 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) } } +static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) +{ + tcpm_log_force(port, "VBUS VSAFE0V"); + port->vbus_vsafe0v = true; + switch (port->state) { + case SRC_HARD_RESET_VBUS_OFF: + /* + * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait + * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V. + */ + tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); + break; + case SRC_ATTACH_WAIT: + if (tcpm_port_is_source(port)) + tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, + PD_T_CC_DEBOUNCE); + break; + default: + break; + } +} + static void _tcpm_pd_hard_reset(struct tcpm_port *port) { tcpm_log_force(port, "Received hard reset"); @@ -4223,10 +4332,19 @@ static void tcpm_pd_event_handler(struct kthread_work *work) bool vbus; vbus = port->tcpc->get_vbus(port->tcpc); - if (vbus) + if (vbus) { _tcpm_pd_vbus_on(port); - else + } else { _tcpm_pd_vbus_off(port); + /* + * When TCPC does not support detecting vsafe0v voltage level, + * treat vbus absent as vsafe0v. Else invoke is_vbus_vsafe0v + * to see if vbus has discharge to VSAFE0V. + */ + if (!port->tcpc->is_vbus_vsafe0v || + port->tcpc->is_vbus_vsafe0v(port->tcpc)) + _tcpm_pd_vbus_vsafe0v(port); + } } if (events & TCPM_CC_EVENT) { enum typec_cc_status cc1, cc2; @@ -4808,9 +4926,10 @@ sink: /* FRS can only be supported byb DRP ports */ if (port->port_type == TYPEC_PORT_DRP) { - ret = fwnode_property_read_u32(fwnode, "frs-typec-current", &frs_current); + ret = fwnode_property_read_u32(fwnode, "new-source-frs-typec-current", + &frs_current); if (ret >= 0 && frs_current <= FRS_5V_3A) - port->frs_current = frs_current; + port->new_source_frs_current = frs_current; } return 0; diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 9b745f432c91..79ae63950050 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -356,7 +356,8 @@ static int wcove_set_pd_rx(struct tcpc_dev *tcpc, bool on) static int wcove_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, - const struct pd_message *msg) + const struct pd_message *msg, + unsigned int negotiated_rev) { struct wcove_typec *wcove = tcpc_to_wcove(tcpc); unsigned int info = 0; diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index 3db33bb622c3..6e6ef6317523 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -9,6 +9,7 @@ #include <linux/i2c.h> #include <linux/acpi.h> #include <linux/module.h> +#include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/interrupt.h> #include <linux/usb/typec.h> @@ -55,6 +56,7 @@ enum { }; /* TPS_REG_POWER_STATUS bits */ +#define TPS_POWER_STATUS_CONNECTION BIT(0) #define TPS_POWER_STATUS_SOURCESINK BIT(1) #define TPS_POWER_STATUS_PWROPMODE(p) (((p) & GENMASK(3, 2)) >> 2) @@ -96,8 +98,25 @@ struct tps6598x { struct typec_partner *partner; struct usb_pd_identity partner_identity; struct usb_role_switch *role_sw; + struct typec_capability typec_cap; + + struct power_supply *psy; + struct power_supply_desc psy_desc; + enum power_supply_usb_type usb_type; +}; + +static enum power_supply_property tps6598x_psy_props[] = { + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_ONLINE, }; +static enum power_supply_usb_type tps6598x_psy_usb_types[] = { + POWER_SUPPLY_USB_TYPE_C, + POWER_SUPPLY_USB_TYPE_PD, +}; + +static const char *tps6598x_psy_name_prefix = "tps6598x-source-psy-"; + /* * Max data bytes for Data1, Data2, and other registers. See ch 1.3.2: * https://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf @@ -248,6 +267,8 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) if (desc.identity) typec_partner_set_identity(tps->partner); + power_supply_changed(tps->psy); + return 0; } @@ -260,6 +281,7 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status) typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false); + power_supply_changed(tps->psy); } static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, @@ -467,6 +489,83 @@ static const struct regmap_config tps6598x_regmap_config = { .max_register = 0x7F, }; +static int tps6598x_psy_get_online(struct tps6598x *tps, + union power_supply_propval *val) +{ + int ret; + u16 pwr_status; + + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); + if (ret < 0) + return ret; + + if ((pwr_status & TPS_POWER_STATUS_CONNECTION) && + (pwr_status & TPS_POWER_STATUS_SOURCESINK)) { + val->intval = 1; + } else { + val->intval = 0; + } + return 0; +} + +static int tps6598x_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct tps6598x *tps = power_supply_get_drvdata(psy); + u16 pwr_status; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_USB_TYPE: + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); + if (ret < 0) + return ret; + if (TPS_POWER_STATUS_PWROPMODE(pwr_status) == TYPEC_PWR_MODE_PD) + val->intval = POWER_SUPPLY_USB_TYPE_PD; + else + val->intval = POWER_SUPPLY_USB_TYPE_C; + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = tps6598x_psy_get_online(tps, val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int devm_tps6598_psy_register(struct tps6598x *tps) +{ + struct power_supply_config psy_cfg = {}; + const char *port_dev_name = dev_name(tps->dev); + char *psy_name; + + psy_cfg.drv_data = tps; + psy_cfg.fwnode = dev_fwnode(tps->dev); + + psy_name = devm_kasprintf(tps->dev, GFP_KERNEL, "%s%s", tps6598x_psy_name_prefix, + port_dev_name); + if (!psy_name) + return -ENOMEM; + + tps->psy_desc.name = psy_name; + tps->psy_desc.type = POWER_SUPPLY_TYPE_USB; + tps->psy_desc.usb_types = tps6598x_psy_usb_types; + tps->psy_desc.num_usb_types = ARRAY_SIZE(tps6598x_psy_usb_types); + tps->psy_desc.properties = tps6598x_psy_props; + tps->psy_desc.num_properties = ARRAY_SIZE(tps6598x_psy_props); + tps->psy_desc.get_property = tps6598x_psy_get_prop; + + tps->usb_type = POWER_SUPPLY_USB_TYPE_C; + + tps->psy = devm_power_supply_register(tps->dev, &tps->psy_desc, + &psy_cfg); + return PTR_ERR_OR_ZERO(tps->psy); +} + static int tps6598x_probe(struct i2c_client *client) { struct typec_capability typec_cap = { }; @@ -560,6 +659,10 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; } + ret = devm_tps6598_psy_register(tps); + if (ret) + return ret; + tps->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(tps->port)) { ret = PTR_ERR(tps->port); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 51a570d40a42..f02958927cbd 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -53,7 +53,7 @@ static int ucsi_acknowledge_connector_change(struct ucsi *ucsi) ctrl = UCSI_ACK_CC_CI; ctrl |= UCSI_ACK_CONNECTOR_CHANGE; - return ucsi->ops->async_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); + return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); } static int ucsi_exec_command(struct ucsi *ucsi, u64 command); @@ -625,21 +625,113 @@ static void ucsi_handle_connector_change(struct work_struct *work) struct ucsi_connector *con = container_of(work, struct ucsi_connector, work); struct ucsi *ucsi = con->ucsi; + struct ucsi_connector_status pre_ack_status; + struct ucsi_connector_status post_ack_status; enum typec_role role; + u16 inferred_changes; + u16 changed_flags; u64 command; int ret; mutex_lock(&con->lock); + /* + * Some/many PPMs have an issue where all fields in the change bitfield + * are cleared when an ACK is send. This will causes any change + * between GET_CONNECTOR_STATUS and ACK to be lost. + * + * We work around this by re-fetching the connector status afterwards. + * We then infer any changes that we see have happened but that may not + * be represented in the change bitfield. + * + * Also, even though we don't need to know the currently supported alt + * modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does + * not get stuck in case it assumes we do. + * Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be + * set in the change bitfield. + * + * We end up with the following actions: + * 1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes + * 2. UCSI_GET_CAM_SUPPORTED, discard result + * 3. ACK connector change + * 4. UCSI_GET_CONNECTOR_STATUS, store result + * 5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results + * 6. If PPM reported a new change, then restart in order to ACK + * 7. Process everything as usual. + * + * We may end up seeing a change twice, but we can only miss extremely + * short transitional changes. + */ + + /* 1. First UCSI_GET_CONNECTOR_STATUS */ + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_send_command(ucsi, command, &pre_ack_status, + sizeof(pre_ack_status)); + if (ret < 0) { + dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", + __func__, ret); + goto out_unlock; + } + con->unprocessed_changes |= pre_ack_status.change; + + /* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */ + command = UCSI_GET_CAM_SUPPORTED; + command |= UCSI_CONNECTOR_NUMBER(con->num); + ucsi_send_command(con->ucsi, command, NULL, 0); + + /* 3. ACK connector change */ + clear_bit(EVENT_PENDING, &ucsi->flags); + ret = ucsi_acknowledge_connector_change(ucsi); + if (ret) { + dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); + goto out_unlock; + } + + /* 4. Second UCSI_GET_CONNECTOR_STATUS */ command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(ucsi, command, &con->status, - sizeof(con->status)); + ret = ucsi_send_command(ucsi, command, &post_ack_status, + sizeof(post_ack_status)); if (ret < 0) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); goto out_unlock; } + /* 5. Inferre any missing changes */ + changed_flags = pre_ack_status.flags ^ post_ack_status.flags; + inferred_changes = 0; + if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0) + inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE; + + if (changed_flags & UCSI_CONSTAT_CONNECTED) + inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE; + + if (changed_flags & UCSI_CONSTAT_PWR_DIR) + inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE; + + if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0) + inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE; + + if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0) + inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE; + + /* Mask out anything that was correctly notified in the later call. */ + inferred_changes &= ~post_ack_status.change; + if (inferred_changes) + dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n", + __func__, inferred_changes); + + con->unprocessed_changes |= inferred_changes; + + /* 6. If PPM reported a new change, then restart in order to ACK */ + if (post_ack_status.change) + goto out_unlock; + + /* 7. Continue as if nothing happened */ + con->status = post_ack_status; + con->status.change = con->unprocessed_changes; + con->unprocessed_changes = 0; + role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || @@ -680,28 +772,19 @@ static void ucsi_handle_connector_change(struct work_struct *work) ucsi_port_psy_changed(con); } - if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) { - /* - * We don't need to know the currently supported alt modes here. - * Running GET_CAM_SUPPORTED command just to make sure the PPM - * does not get stuck in case it assumes we do so. - */ - command = UCSI_GET_CAM_SUPPORTED; - command |= UCSI_CONNECTOR_NUMBER(con->num); - ucsi_send_command(con->ucsi, command, NULL, 0); - } - if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) ucsi_partner_change(con); - ret = ucsi_acknowledge_connector_change(ucsi); - if (ret) - dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); - trace_ucsi_connector_change(con->num, &con->status); out_unlock: - clear_bit(EVENT_PENDING, &ucsi->flags); + if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) { + schedule_work(&con->work); + mutex_unlock(&con->lock); + return; + } + + clear_bit(EVENT_PROCESSING, &ucsi->flags); mutex_unlock(&con->lock); } @@ -719,7 +802,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) return; } - if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) + set_bit(EVENT_PENDING, &ucsi->flags); + + if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags)) schedule_work(&con->work); } EXPORT_SYMBOL_GPL(ucsi_connector_change); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index b7a92f246050..dd9ba60ab4a3 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -296,6 +296,7 @@ struct ucsi { #define EVENT_PENDING 0 #define COMMAND_PENDING 1 #define ACK_PENDING 2 +#define EVENT_PROCESSING 3 }; #define UCSI_MAX_SVID 5 @@ -322,6 +323,7 @@ struct ucsi_connector { struct typec_capability typec_cap; + u16 unprocessed_changes; struct ucsi_connector_status status; struct ucsi_connector_capability cap; struct power_supply *psy; diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index fbfe8f5933af..04976435ad73 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -103,11 +103,12 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) if (ret) return; + if (UCSI_CCI_CONNECTOR(cci)) + ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); + if (test_bit(COMMAND_PENDING, &ua->flags) && cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) complete(&ua->complete); - else if (UCSI_CCI_CONNECTOR(cci)) - ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); } static int ucsi_acpi_probe(struct platform_device *pdev) diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 4ce6c6a45eb1..2ab99244bc31 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -324,11 +324,6 @@ int usbip_recv(struct socket *sock, void *buf, int size) } while (msg_data_left(&msg)); if (usbip_dbg_flag_xmit) { - if (!in_interrupt()) - pr_debug("%-10s:", current->comm); - else - pr_debug("interrupt :"); - pr_debug("receiving....\n"); usbip_dump_buffer(buf, size); pr_debug("received, osize %d ret %d size %zd total %d\n", diff --git a/drivers/vdpa/Kconfig b/drivers/vdpa/Kconfig index 358f6048dd3c..6caf539091e5 100644 --- a/drivers/vdpa/Kconfig +++ b/drivers/vdpa/Kconfig @@ -32,6 +32,7 @@ config IFCVF config MLX5_VDPA bool + select VHOST_IOTLB help Support library for Mellanox VDPA drivers. Provides code that is common for all types of VDPA drivers. The following drivers are planned: diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index f22fce549862..6ff8a5096691 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -220,6 +220,7 @@ struct vhost_scsi_tmf { struct list_head queue_entry; struct se_cmd se_cmd; + u8 scsi_resp; struct vhost_scsi_inflight *inflight; struct iovec resp_iov; int in_iovs; @@ -426,6 +427,7 @@ static void vhost_scsi_queue_tm_rsp(struct se_cmd *se_cmd) struct vhost_scsi_tmf *tmf = container_of(se_cmd, struct vhost_scsi_tmf, se_cmd); + tmf->scsi_resp = se_cmd->se_tmr_req->response; transport_generic_free_cmd(&tmf->se_cmd, 0); } @@ -1183,7 +1185,7 @@ static void vhost_scsi_tmf_resp_work(struct vhost_work *work) vwork); int resp_code; - if (tmf->se_cmd.se_tmr_req->response == TMR_FUNCTION_COMPLETE) + if (tmf->scsi_resp == TMR_FUNCTION_COMPLETE) resp_code = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; else resp_code = VIRTIO_SCSI_S_FUNCTION_REJECTED; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 2754f3069738..29ed4173f04e 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -348,7 +348,9 @@ static long vhost_vdpa_get_iova_range(struct vhost_vdpa *v, u32 __user *argp) .last = v->range.last, }; - return copy_to_user(argp, &range, sizeof(range)); + if (copy_to_user(argp, &range, sizeof(range))) + return -EFAULT; + return 0; } static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, @@ -577,6 +579,8 @@ static int vhost_vdpa_map(struct vhost_vdpa *v, if (r) vhost_iotlb_del_range(dev->iotlb, iova, iova + size - 1); + else + atomic64_add(size >> PAGE_SHIFT, &dev->mm->pinned_vm); return r; } @@ -608,8 +612,9 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, unsigned long list_size = PAGE_SIZE / sizeof(struct page *); unsigned int gup_flags = FOLL_LONGTERM; unsigned long npages, cur_base, map_pfn, last_pfn = 0; - unsigned long locked, lock_limit, pinned, i; + unsigned long lock_limit, sz2pin, nchunks, i; u64 iova = msg->iova; + long pinned; int ret = 0; if (msg->iova < v->range.first || @@ -620,6 +625,7 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, msg->iova + msg->size - 1)) return -EEXIST; + /* Limit the use of memory for bookkeeping */ page_list = (struct page **) __get_free_page(GFP_KERNEL); if (!page_list) return -ENOMEM; @@ -628,52 +634,75 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, gup_flags |= FOLL_WRITE; npages = PAGE_ALIGN(msg->size + (iova & ~PAGE_MASK)) >> PAGE_SHIFT; - if (!npages) - return -EINVAL; + if (!npages) { + ret = -EINVAL; + goto free; + } mmap_read_lock(dev->mm); - locked = atomic64_add_return(npages, &dev->mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - - if (locked > lock_limit) { + if (npages + atomic64_read(&dev->mm->pinned_vm) > lock_limit) { ret = -ENOMEM; - goto out; + goto unlock; } cur_base = msg->uaddr & PAGE_MASK; iova &= PAGE_MASK; + nchunks = 0; while (npages) { - pinned = min_t(unsigned long, npages, list_size); - ret = pin_user_pages(cur_base, pinned, - gup_flags, page_list, NULL); - if (ret != pinned) + sz2pin = min_t(unsigned long, npages, list_size); + pinned = pin_user_pages(cur_base, sz2pin, + gup_flags, page_list, NULL); + if (sz2pin != pinned) { + if (pinned < 0) { + ret = pinned; + } else { + unpin_user_pages(page_list, pinned); + ret = -ENOMEM; + } goto out; + } + nchunks++; if (!last_pfn) map_pfn = page_to_pfn(page_list[0]); - for (i = 0; i < ret; i++) { + for (i = 0; i < pinned; i++) { unsigned long this_pfn = page_to_pfn(page_list[i]); u64 csize; if (last_pfn && (this_pfn != last_pfn + 1)) { /* Pin a contiguous chunk of memory */ csize = (last_pfn - map_pfn + 1) << PAGE_SHIFT; - if (vhost_vdpa_map(v, iova, csize, - map_pfn << PAGE_SHIFT, - msg->perm)) + ret = vhost_vdpa_map(v, iova, csize, + map_pfn << PAGE_SHIFT, + msg->perm); + if (ret) { + /* + * Unpin the pages that are left unmapped + * from this point on in the current + * page_list. The remaining outstanding + * ones which may stride across several + * chunks will be covered in the common + * error path subsequently. + */ + unpin_user_pages(&page_list[i], + pinned - i); goto out; + } + map_pfn = this_pfn; iova += csize; + nchunks = 0; } last_pfn = this_pfn; } - cur_base += ret << PAGE_SHIFT; - npages -= ret; + cur_base += pinned << PAGE_SHIFT; + npages -= pinned; } /* Pin the rest chunk */ @@ -681,10 +710,27 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, map_pfn << PAGE_SHIFT, msg->perm); out: if (ret) { + if (nchunks) { + unsigned long pfn; + + /* + * Unpin the outstanding pages which are yet to be + * mapped but haven't due to vdpa_map() or + * pin_user_pages() failure. + * + * Mapped pages are accounted in vdpa_map(), hence + * the corresponding unpinning will be handled by + * vdpa_unmap(). + */ + WARN_ON(!last_pfn); + for (pfn = map_pfn; pfn <= last_pfn; pfn++) + unpin_user_page(pfn_to_page(pfn)); + } vhost_vdpa_unmap(v, msg->iova, msg->size); - atomic64_sub(npages, &dev->mm->pinned_vm); } +unlock: mmap_read_unlock(dev->mm); +free: free_page((unsigned long)page_list); return ret; } diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index 8bd8b403f087..b7403ba8e7f7 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -730,7 +730,7 @@ EXPORT_SYMBOL(vringh_iov_pull_user); /** * vringh_iov_push_user - copy bytes into vring_iov. * @wiov: the wiov as passed to vringh_getdesc_user() (updated as we consume) - * @dst: the place to copy. + * @src: the place to copy from. * @len: the maximum length to copy. * * Returns the bytes copied <= len or a negative errno. @@ -976,7 +976,7 @@ EXPORT_SYMBOL(vringh_iov_pull_kern); /** * vringh_iov_push_kern - copy bytes into vring_iov. * @wiov: the wiov as passed to vringh_getdesc_kern() (updated as we consume) - * @dst: the place to copy. + * @src: the place to copy from. * @len: the maximum length to copy. * * Returns the bytes copied <= len or a negative errno. @@ -1333,7 +1333,7 @@ EXPORT_SYMBOL(vringh_iov_pull_iotlb); * vringh_iov_push_iotlb - copy bytes into vring_iov. * @vrh: the vring. * @wiov: the wiov as passed to vringh_getdesc_iotlb() (updated as we consume) - * @dst: the place to copy. + * @src: the place to copy from. * @len: the maximum length to copy. * * Returns the bytes copied <= len or a negative errno. |