mirror of https://gitee.com/openkylin/linux.git
usb: changes for v4.16 merge window
Not many changes here, the most important being an improvement for TI's AM57xx and DRA7xx devices which allows them to disable a metastability workaround in situations where we know what's going on. Other than that, we have a set of changes on Renesas UDC to make the code a little easier to read and maintain while also better supporting extcon framework. The u_serial adaptation layer learned to use kfifo instead of cooking its own FIFO implementation. DWC3 learned to decode a few more USB requests on the trace output. -----BEGIN PGP SIGNATURE----- iQJQBAABCgA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAlpTWGYdHGZlbGlwZS5i YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQYDsw/4jOBzuVM2A28oCDzd H9RCagGp4dXpdvLRpueE+L16AA/5MrAI1H1Tdp063Xr+CmlPeMf98JRI5tfBpfn5 8bE6yTojMfvb7vL82kjrnKwKh59IvKVLlFftPBIGJp1uKl5KwWUWMWRcvnpVJHdY SlZ0U79Pje7RBuq0BkBQ9Lm47pPm5sMqzCyyte4wZbR+6GkAA5W0PBuRes0JLBHo gUb9h5ns5afrxEdvzUXKpVpd++oP/ZLoZ++jYJ10Z5qSC8+y0DiwRxq7OdOSil+c BEMKe7I46tGzDFZ2wuMQByHWqCXp7o+9VSf5BYxyP1L58qH1N7M7GyH0Oe+r5xAi tjFrnL0C8Ax+ouaXLBfmFzv/52BjEqGoqtscWZ4XyVqq98NC1bP5b+qVmIkzXkdv P7T2lOFZX0ILDX7QXIQbXv71UA/m3bwi3IG0eNX5Rik5xQjDVHnLDpuj87wW1wSH aUg7sHpggn6GdOICHw9Zry3Z07u3Rab8ThuH0Z4oBI2+3EGhbXTHDjpKYwFYCWEw nI+9XloOdEkOE6P3S2VO1cQ7YkJ+r4/YRDRvPq3XfLETfxPHwIjqiVeSQWaBA2U8 Om4f/1Oe83jtpfl1T7zL2z08TVPDNMOpE2j4FThA682Pa15emqymRXGDgDmFVizO boMiMHcP8Wp0MMegK/3RL4syrg== =uwh/ -----END PGP SIGNATURE----- Merge tag 'usb-for-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: usb: changes for v4.16 merge window Not many changes here, the most important being an improvement for TI's AM57xx and DRA7xx devices which allows them to disable a metastability workaround in situations where we know what's going on. Other than that, we have a set of changes on Renesas UDC to make the code a little easier to read and maintain while also better supporting extcon framework. The u_serial adaptation layer learned to use kfifo instead of cooking its own FIFO implementation. DWC3 learned to decode a few more USB requests on the trace output.
This commit is contained in:
commit
958e052c54
|
@ -47,6 +47,8 @@ Optional properties:
|
|||
from P0 to P1/P2/P3 without delay.
|
||||
- snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check
|
||||
during HS transmit.
|
||||
- snps,dis_metastability_quirk: when set, disable metastability workaround.
|
||||
CAUTION: use only if you are absolutely sure of it.
|
||||
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
|
||||
utmi_l1_suspend_n, false when asserts utmi_sleep_n
|
||||
- snps,hird-threshold: HIRD threshold
|
||||
|
|
|
@ -929,6 +929,7 @@ struct dwc2_hsotg {
|
|||
int irq;
|
||||
struct clk *clk;
|
||||
struct reset_control *reset;
|
||||
struct reset_control *reset_ecc;
|
||||
|
||||
unsigned int queuing_high_bandwidth:1;
|
||||
unsigned int srp_success:1;
|
||||
|
@ -971,6 +972,7 @@ struct dwc2_hsotg {
|
|||
} flags;
|
||||
|
||||
struct list_head non_periodic_sched_inactive;
|
||||
struct list_head non_periodic_sched_waiting;
|
||||
struct list_head non_periodic_sched_active;
|
||||
struct list_head *non_periodic_qh_ptr;
|
||||
struct list_head periodic_sched_inactive;
|
||||
|
|
|
@ -659,6 +659,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
|
|||
list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive,
|
||||
qh_list_entry)
|
||||
dev_dbg(hsotg->dev, " %p\n", qh);
|
||||
dev_dbg(hsotg->dev, " NP waiting sched:\n");
|
||||
list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting,
|
||||
qh_list_entry)
|
||||
dev_dbg(hsotg->dev, " %p\n", qh);
|
||||
dev_dbg(hsotg->dev, " NP active sched:\n");
|
||||
list_for_each_entry(qh, &hsotg->non_periodic_sched_active,
|
||||
qh_list_entry)
|
||||
|
@ -1818,6 +1822,7 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
|
|||
static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready);
|
||||
|
@ -4998,6 +5003,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
|
|||
|
||||
/* Free memory for QH/QTD lists */
|
||||
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready);
|
||||
|
@ -5159,6 +5165,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
|
|||
|
||||
/* Initialize the non-periodic schedule */
|
||||
INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive);
|
||||
INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting);
|
||||
INIT_LIST_HEAD(&hsotg->non_periodic_sched_active);
|
||||
|
||||
/* Initialize the periodic schedule */
|
||||
|
|
|
@ -314,12 +314,16 @@ struct dwc2_hs_transfer_time {
|
|||
* descriptor and indicates original XferSize value for the
|
||||
* descriptor
|
||||
* @unreserve_timer: Timer for releasing periodic reservation.
|
||||
* @wait_timer: Timer used to wait before re-queuing.
|
||||
* @dwc2_tt: Pointer to our tt info (or NULL if no tt).
|
||||
* @ttport: Port number within our tt.
|
||||
* @tt_buffer_dirty True if clear_tt_buffer_complete is pending
|
||||
* @unreserve_pending: True if we planned to unreserve but haven't yet.
|
||||
* @schedule_low_speed: True if we have a low/full speed component (either the
|
||||
* host is in low/full speed mode or do_split).
|
||||
* @want_wait: We should wait before re-queuing; only matters for non-
|
||||
* periodic transfers and is ignored for periodic ones.
|
||||
* @wait_timer_cancel: Set to true to cancel the wait_timer.
|
||||
*
|
||||
* A Queue Head (QH) holds the static characteristics of an endpoint and
|
||||
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
|
||||
|
@ -354,11 +358,14 @@ struct dwc2_qh {
|
|||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
struct timer_list unreserve_timer;
|
||||
struct timer_list wait_timer;
|
||||
struct dwc2_tt *dwc_tt;
|
||||
int ttport;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
unsigned unreserve_pending:1;
|
||||
unsigned schedule_low_speed:1;
|
||||
unsigned want_wait:1;
|
||||
unsigned wait_timer_cancel:1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -389,6 +396,7 @@ struct dwc2_qh {
|
|||
* @n_desc: Number of DMA descriptors for this QTD
|
||||
* @isoc_frame_index_last: Last activated frame (packet) index, used in
|
||||
* descriptor DMA mode only
|
||||
* @num_naks: Number of NAKs received on this QTD.
|
||||
* @urb: URB for this transfer
|
||||
* @qh: Queue head for this QTD
|
||||
* @qtd_list_entry: For linking to the QH's list of QTDs
|
||||
|
@ -419,6 +427,7 @@ struct dwc2_qtd {
|
|||
u8 error_count;
|
||||
u8 n_desc;
|
||||
u16 isoc_frame_index_last;
|
||||
u16 num_naks;
|
||||
struct dwc2_hcd_urb *urb;
|
||||
struct dwc2_qh *qh;
|
||||
struct list_head qtd_list_entry;
|
||||
|
|
|
@ -53,6 +53,12 @@
|
|||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
|
||||
/*
|
||||
* If we get this many NAKs on a split transaction we'll slow down
|
||||
* retransmission. A 1 here means delay after the first NAK.
|
||||
*/
|
||||
#define DWC2_NAKS_BEFORE_DELAY 3
|
||||
|
||||
/* This function is for debug only */
|
||||
static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
|
@ -1201,11 +1207,25 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
|
|||
/*
|
||||
* Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
|
||||
* interrupt. Re-start the SSPLIT transfer.
|
||||
*
|
||||
* Normally for non-periodic transfers we'll retry right away, but to
|
||||
* avoid interrupt storms we'll wait before retrying if we've got
|
||||
* several NAKs. If we didn't do this we'd retry directly from the
|
||||
* interrupt handler and could end up quickly getting another
|
||||
* interrupt (another NAK), which we'd retry.
|
||||
*
|
||||
* Note that in DMA mode software only gets involved to re-send NAKed
|
||||
* transfers for split transactions, so we only need to apply this
|
||||
* delaying logic when handling splits. In non-DMA mode presumably we
|
||||
* might want a similar delay if someone can demonstrate this problem
|
||||
* affects that code path too.
|
||||
*/
|
||||
if (chan->do_split) {
|
||||
if (chan->complete_split)
|
||||
qtd->error_count = 0;
|
||||
qtd->complete_split = 0;
|
||||
qtd->num_naks++;
|
||||
qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY;
|
||||
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
|
||||
goto handle_nak_done;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,9 @@
|
|||
/* Wait this long before releasing periodic reservation */
|
||||
#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
|
||||
|
||||
/* If we get a NAK, wait this long before retrying */
|
||||
#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1))
|
||||
|
||||
/**
|
||||
* dwc2_periodic_channel_available() - Checks that a channel is available for a
|
||||
* periodic transfer
|
||||
|
@ -1440,6 +1443,55 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
|
|||
list_del_init(&qh->qh_list_entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_wait_timer_fn() - Timer function to re-queue after waiting
|
||||
*
|
||||
* As per the spec, a NAK indicates that "a function is temporarily unable to
|
||||
* transmit or receive data, but will eventually be able to do so without need
|
||||
* of host intervention".
|
||||
*
|
||||
* That means that when we encounter a NAK we're supposed to retry.
|
||||
*
|
||||
* ...but if we retry right away (from the interrupt handler that saw the NAK)
|
||||
* then we can end up with an interrupt storm (if the other side keeps NAKing
|
||||
* us) because on slow enough CPUs it could take us longer to get out of the
|
||||
* interrupt routine than it takes for the device to send another NAK. That
|
||||
* leads to a constant stream of NAK interrupts and the CPU locks.
|
||||
*
|
||||
* ...so instead of retrying right away in the case of a NAK we'll set a timer
|
||||
* to retry some time later. This function handles that timer and moves the
|
||||
* qh back to the "inactive" list, then queues transactions.
|
||||
*
|
||||
* @t: Pointer to wait_timer in a qh.
|
||||
*/
|
||||
static void dwc2_wait_timer_fn(struct timer_list *t)
|
||||
{
|
||||
struct dwc2_qh *qh = from_timer(qh, t, wait_timer);
|
||||
struct dwc2_hsotg *hsotg = qh->hsotg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
/*
|
||||
* We'll set wait_timer_cancel to true if we want to cancel this
|
||||
* operation in dwc2_hcd_qh_unlink().
|
||||
*/
|
||||
if (!qh->wait_timer_cancel) {
|
||||
enum dwc2_transaction_type tr_type;
|
||||
|
||||
qh->want_wait = false;
|
||||
|
||||
list_move(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
|
||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_qh_init() - Initializes a QH structure
|
||||
*
|
||||
|
@ -1468,6 +1520,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
/* Initialize QH */
|
||||
qh->hsotg = hsotg;
|
||||
timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0);
|
||||
timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0);
|
||||
qh->ep_type = ep_type;
|
||||
qh->ep_is_in = ep_is_in;
|
||||
|
||||
|
@ -1628,6 +1681,16 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
dwc2_do_unreserve(hsotg, qh);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't have the lock so we can safely wait until the wait timer
|
||||
* finishes. Of course, at this point in time we'd better have set
|
||||
* wait_timer_active to false so if this timer was still pending it
|
||||
* won't do anything anyway, but we want it to finish before we free
|
||||
* memory.
|
||||
*/
|
||||
del_timer_sync(&qh->wait_timer);
|
||||
|
||||
dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
|
||||
|
||||
if (qh->desc_list)
|
||||
|
@ -1663,9 +1726,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
qh->start_active_frame = hsotg->frame_number;
|
||||
qh->next_active_frame = qh->start_active_frame;
|
||||
|
||||
/* Always start in inactive schedule */
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
if (qh->want_wait) {
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_waiting);
|
||||
qh->wait_timer_cancel = false;
|
||||
mod_timer(&qh->wait_timer,
|
||||
jiffies + DWC2_RETRY_WAIT_DELAY + 1);
|
||||
} else {
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1695,6 +1765,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
||||
/* If the wait_timer is pending, this will stop it from acting */
|
||||
qh->wait_timer_cancel = true;
|
||||
|
||||
if (list_empty(&qh->qh_list_entry))
|
||||
/* QH is not in a schedule */
|
||||
return;
|
||||
|
@ -1903,7 +1976,7 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
if (dwc2_qh_is_non_per(qh)) {
|
||||
dwc2_hcd_qh_unlink(hsotg, qh);
|
||||
if (!list_empty(&qh->qtd_list))
|
||||
/* Add back to inactive non-periodic schedule */
|
||||
/* Add back to inactive/waiting non-periodic schedule */
|
||||
dwc2_hcd_qh_add(hsotg, qh);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -221,6 +221,15 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
|
|||
|
||||
reset_control_deassert(hsotg->reset);
|
||||
|
||||
hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc");
|
||||
if (IS_ERR(hsotg->reset_ecc)) {
|
||||
ret = PTR_ERR(hsotg->reset_ecc);
|
||||
dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reset_control_deassert(hsotg->reset_ecc);
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
|
@ -319,6 +328,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
|||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
|
||||
reset_control_assert(hsotg->reset);
|
||||
reset_control_assert(hsotg->reset_ecc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1062,6 +1062,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
|
||||
&dwc->fladj);
|
||||
|
||||
dwc->dis_metastability_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_metastability_quirk");
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
|
|
|
@ -796,7 +796,6 @@ struct dwc3_scratchpad_array {
|
|||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
* @usb3_generic_phy: pointer to USB3 PHY
|
||||
* @ulpi: pointer to ulpi interface
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
* @u2sel: parameter from Set SEL request.
|
||||
* @u2pel: parameter from Set SEL request.
|
||||
* @u1sel: parameter from Set SEL request.
|
||||
|
@ -857,6 +856,7 @@ struct dwc3_scratchpad_array {
|
|||
* 1 - -3.5dB de-emphasis
|
||||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* @dis_metastability_quirk: set to disable metastability quirk.
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
* increments or 0 to disable.
|
||||
*/
|
||||
|
@ -955,7 +955,6 @@ struct dwc3 {
|
|||
enum dwc3_ep0_state ep0state;
|
||||
enum dwc3_link_state link_state;
|
||||
|
||||
u16 isoch_delay;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
u8 u1sel;
|
||||
|
@ -1010,6 +1009,8 @@ struct dwc3 {
|
|||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
unsigned dis_metastability_quirk:1;
|
||||
|
||||
u16 imod_interval;
|
||||
};
|
||||
|
||||
|
|
|
@ -247,6 +247,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
|
|||
case USB_DEVICE_TEST_MODE:
|
||||
s = "Test Mode";
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
s = "U1 Enable";
|
||||
break;
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
s = "U2 Enable";
|
||||
break;
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
s = "LTM Enable";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
} s; }),
|
||||
|
|
|
@ -736,11 +736,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct
|
|||
if (wIndex || wLength)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* REVISIT It's unclear from Databook what to do with this
|
||||
* value. For now, just cache it.
|
||||
*/
|
||||
dwc->isoch_delay = wValue;
|
||||
dwc->gadget.isoch_delay = wValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2005,7 +2005,8 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
|
|||
* STAR#9000525659: Clock Domain Crossing on DCTL in
|
||||
* USB 2.0 Mode
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A) {
|
||||
if (dwc->revision < DWC3_REVISION_220A &&
|
||||
!dwc->dis_metastability_quirk) {
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
} else {
|
||||
switch (speed) {
|
||||
|
@ -3224,7 +3225,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
* is less than super speed because we don't have means, yet, to tell
|
||||
* composite.c that we are USB 2.0 + LPM ECN.
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A)
|
||||
if (dwc->revision < DWC3_REVISION_220A &&
|
||||
!dwc->dis_metastability_quirk)
|
||||
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
|
||||
dwc->revision);
|
||||
|
||||
|
|
|
@ -37,12 +37,12 @@ DECLARE_EVENT_CLASS(dwc3_log_io,
|
|||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_io, dwc3_readl,
|
||||
TP_PROTO(void *base, u32 offset, u32 value),
|
||||
TP_PROTO(void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_io, dwc3_writel,
|
||||
TP_PROTO(void *base, u32 offset, u32 value),
|
||||
TP_PROTO(void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value)
|
||||
);
|
||||
|
||||
|
|
|
@ -266,6 +266,7 @@ static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
}
|
||||
|
||||
static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
|
||||
__releases(&ffs->ev.waitq.lock)
|
||||
{
|
||||
struct usb_request *req = ffs->ep0req;
|
||||
int ret;
|
||||
|
@ -458,6 +459,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
|||
/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
|
||||
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
|
||||
size_t n)
|
||||
__releases(&ffs->ev.waitq.lock)
|
||||
{
|
||||
/*
|
||||
* n cannot be bigger than ffs->ev.count, which cannot be bigger than
|
||||
|
@ -543,6 +545,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
|||
break;
|
||||
}
|
||||
|
||||
/* unlocks spinlock */
|
||||
return __ffs_ep0_read_events(ffs, buf,
|
||||
min(n, (size_t)ffs->ev.count));
|
||||
|
||||
|
@ -1246,7 +1249,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
desc = epfile->ep->descs[desc_idx];
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = copy_to_user((void *)value, desc, desc->bLength);
|
||||
ret = copy_to_user((void __user *)value, desc, desc->bLength);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
|
@ -2324,7 +2327,7 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
|
|||
length, pnl, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
|
||||
pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl));
|
||||
if (length != 14 + pnl + pdl) {
|
||||
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
|
||||
length, pnl, pdl, type);
|
||||
|
@ -2878,7 +2881,7 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
|
|||
|
||||
ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
|
||||
ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
|
||||
ext_prop->data_len = le32_to_cpu(*(u32 *)
|
||||
ext_prop->data_len = le32_to_cpu(*(__le32 *)
|
||||
usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
|
||||
length = ext_prop->name_len + ext_prop->data_len + 14;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
|
@ -80,19 +81,11 @@
|
|||
#define WRITE_BUF_SIZE 8192 /* TX only */
|
||||
#define GS_CONSOLE_BUF_SIZE 8192
|
||||
|
||||
/* circular buffer */
|
||||
struct gs_buf {
|
||||
unsigned buf_size;
|
||||
char *buf_buf;
|
||||
char *buf_get;
|
||||
char *buf_put;
|
||||
};
|
||||
|
||||
/* console info */
|
||||
struct gscons_info {
|
||||
struct gs_port *port;
|
||||
struct task_struct *console_thread;
|
||||
struct gs_buf con_buf;
|
||||
struct kfifo con_buf;
|
||||
/* protect the buf and busy flag */
|
||||
spinlock_t con_lock;
|
||||
int req_busy;
|
||||
|
@ -122,7 +115,7 @@ struct gs_port {
|
|||
struct list_head write_pool;
|
||||
int write_started;
|
||||
int write_allocated;
|
||||
struct gs_buf port_write_buf;
|
||||
struct kfifo port_write_buf;
|
||||
wait_queue_head_t drain_wait; /* wait while writes drain */
|
||||
bool write_busy;
|
||||
wait_queue_head_t close_wait;
|
||||
|
@ -154,144 +147,6 @@ static struct portmaster {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Circular Buffer */
|
||||
|
||||
/*
|
||||
* gs_buf_alloc
|
||||
*
|
||||
* Allocate a circular buffer and all associated memory.
|
||||
*/
|
||||
static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
|
||||
{
|
||||
gb->buf_buf = kmalloc(size, GFP_KERNEL);
|
||||
if (gb->buf_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
gb->buf_size = size;
|
||||
gb->buf_put = gb->buf_buf;
|
||||
gb->buf_get = gb->buf_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_free
|
||||
*
|
||||
* Free the buffer and all associated memory.
|
||||
*/
|
||||
static void gs_buf_free(struct gs_buf *gb)
|
||||
{
|
||||
kfree(gb->buf_buf);
|
||||
gb->buf_buf = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_clear
|
||||
*
|
||||
* Clear out all data in the circular buffer.
|
||||
*/
|
||||
static void gs_buf_clear(struct gs_buf *gb)
|
||||
{
|
||||
gb->buf_get = gb->buf_put;
|
||||
/* equivalent to a get of all data available */
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_data_avail
|
||||
*
|
||||
* Return the number of bytes of data written into the circular
|
||||
* buffer.
|
||||
*/
|
||||
static unsigned gs_buf_data_avail(struct gs_buf *gb)
|
||||
{
|
||||
return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_space_avail
|
||||
*
|
||||
* Return the number of bytes of space available in the circular
|
||||
* buffer.
|
||||
*/
|
||||
static unsigned gs_buf_space_avail(struct gs_buf *gb)
|
||||
{
|
||||
return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_put
|
||||
*
|
||||
* Copy data data from a user buffer and put it into the circular buffer.
|
||||
* Restrict to the amount of space available.
|
||||
*
|
||||
* Return the number of bytes copied.
|
||||
*/
|
||||
static unsigned
|
||||
gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
len = gs_buf_space_avail(gb);
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
len = gb->buf_buf + gb->buf_size - gb->buf_put;
|
||||
if (count > len) {
|
||||
memcpy(gb->buf_put, buf, len);
|
||||
memcpy(gb->buf_buf, buf+len, count - len);
|
||||
gb->buf_put = gb->buf_buf + count - len;
|
||||
} else {
|
||||
memcpy(gb->buf_put, buf, count);
|
||||
if (count < len)
|
||||
gb->buf_put += count;
|
||||
else /* count == len */
|
||||
gb->buf_put = gb->buf_buf;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_get
|
||||
*
|
||||
* Get data from the circular buffer and copy to the given buffer.
|
||||
* Restrict to the amount of data available.
|
||||
*
|
||||
* Return the number of bytes copied.
|
||||
*/
|
||||
static unsigned
|
||||
gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
len = gs_buf_data_avail(gb);
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
len = gb->buf_buf + gb->buf_size - gb->buf_get;
|
||||
if (count > len) {
|
||||
memcpy(buf, gb->buf_get, len);
|
||||
memcpy(buf+len, gb->buf_buf, count - len);
|
||||
gb->buf_get = gb->buf_buf + count - len;
|
||||
} else {
|
||||
memcpy(buf, gb->buf_get, count);
|
||||
if (count < len)
|
||||
gb->buf_get += count;
|
||||
else /* count == len */
|
||||
gb->buf_get = gb->buf_buf;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* I/O glue between TTY (upper) and USB function (lower) driver layers */
|
||||
|
||||
/*
|
||||
|
@ -346,11 +201,11 @@ gs_send_packet(struct gs_port *port, char *packet, unsigned size)
|
|||
{
|
||||
unsigned len;
|
||||
|
||||
len = gs_buf_data_avail(&port->port_write_buf);
|
||||
len = kfifo_len(&port->port_write_buf);
|
||||
if (len < size)
|
||||
size = len;
|
||||
if (size != 0)
|
||||
size = gs_buf_get(&port->port_write_buf, packet, size);
|
||||
size = kfifo_out(&port->port_write_buf, packet, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -398,7 +253,7 @@ __acquires(&port->port_lock)
|
|||
|
||||
req->length = len;
|
||||
list_del(&req->list);
|
||||
req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
|
||||
req->zero = kfifo_is_empty(&port->port_write_buf);
|
||||
|
||||
pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
port->port_num, len, *((u8 *)req->buf),
|
||||
|
@ -787,10 +642,11 @@ static int gs_open(struct tty_struct *tty, struct file *file)
|
|||
spin_lock_irq(&port->port_lock);
|
||||
|
||||
/* allocate circular buffer on first open */
|
||||
if (port->port_write_buf.buf_buf == NULL) {
|
||||
if (!kfifo_initialized(&port->port_write_buf)) {
|
||||
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
|
||||
status = kfifo_alloc(&port->port_write_buf,
|
||||
WRITE_BUF_SIZE, GFP_KERNEL);
|
||||
spin_lock_irq(&port->port_lock);
|
||||
|
||||
if (status) {
|
||||
|
@ -839,7 +695,7 @@ static int gs_writes_finished(struct gs_port *p)
|
|||
|
||||
/* return true on disconnect or empty buffer */
|
||||
spin_lock_irq(&p->port_lock);
|
||||
cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
|
||||
cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf);
|
||||
spin_unlock_irq(&p->port_lock);
|
||||
|
||||
return cond;
|
||||
|
@ -875,7 +731,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
/* wait for circular write buffer to drain, disconnect, or at
|
||||
* most GS_CLOSE_TIMEOUT seconds; then discard the rest
|
||||
*/
|
||||
if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
|
||||
if (kfifo_len(&port->port_write_buf) > 0 && gser) {
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
wait_event_interruptible_timeout(port->drain_wait,
|
||||
gs_writes_finished(port),
|
||||
|
@ -889,9 +745,9 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
* let the push tasklet fire again until we're re-opened.
|
||||
*/
|
||||
if (gser == NULL)
|
||||
gs_buf_free(&port->port_write_buf);
|
||||
kfifo_free(&port->port_write_buf);
|
||||
else
|
||||
gs_buf_clear(&port->port_write_buf);
|
||||
kfifo_reset(&port->port_write_buf);
|
||||
|
||||
port->port.tty = NULL;
|
||||
|
||||
|
@ -915,7 +771,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
|||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (count)
|
||||
count = gs_buf_put(&port->port_write_buf, buf, count);
|
||||
count = kfifo_in(&port->port_write_buf, buf, count);
|
||||
/* treat count == 0 as flush_chars() */
|
||||
if (port->port_usb)
|
||||
gs_start_tx(port);
|
||||
|
@ -934,7 +790,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
|
|||
port->port_num, tty, ch, __builtin_return_address(0));
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
status = gs_buf_put(&port->port_write_buf, &ch, 1);
|
||||
status = kfifo_put(&port->port_write_buf, ch);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return status;
|
||||
|
@ -961,7 +817,7 @@ static int gs_write_room(struct tty_struct *tty)
|
|||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (port->port_usb)
|
||||
room = gs_buf_space_avail(&port->port_write_buf);
|
||||
room = kfifo_avail(&port->port_write_buf);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
|
||||
|
@ -977,7 +833,7 @@ static int gs_chars_in_buffer(struct tty_struct *tty)
|
|||
int chars = 0;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
chars = gs_buf_data_avail(&port->port_write_buf);
|
||||
chars = kfifo_len(&port->port_write_buf);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
|
||||
|
@ -1148,7 +1004,7 @@ static int gs_console_thread(void *data)
|
|||
ep = port->port_usb->in;
|
||||
|
||||
spin_lock_irq(&info->con_lock);
|
||||
count = gs_buf_data_avail(&info->con_buf);
|
||||
count = kfifo_len(&info->con_buf);
|
||||
size = ep->maxpacket;
|
||||
|
||||
if (count > 0 && !info->req_busy) {
|
||||
|
@ -1156,7 +1012,7 @@ static int gs_console_thread(void *data)
|
|||
if (count < size)
|
||||
size = count;
|
||||
|
||||
xfer = gs_buf_get(&info->con_buf, req->buf, size);
|
||||
xfer = kfifo_out(&info->con_buf, req->buf, size);
|
||||
req->length = xfer;
|
||||
|
||||
spin_unlock(&info->con_lock);
|
||||
|
@ -1192,7 +1048,7 @@ static int gs_console_setup(struct console *co, char *options)
|
|||
info->req_busy = 0;
|
||||
spin_lock_init(&info->con_lock);
|
||||
|
||||
status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE);
|
||||
status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL);
|
||||
if (status) {
|
||||
pr_err("%s: allocate console buffer failed\n", __func__);
|
||||
return status;
|
||||
|
@ -1202,7 +1058,7 @@ static int gs_console_setup(struct console *co, char *options)
|
|||
co, "gs_console");
|
||||
if (IS_ERR(info->console_thread)) {
|
||||
pr_err("%s: cannot create console thread\n", __func__);
|
||||
gs_buf_free(&info->con_buf);
|
||||
kfifo_free(&info->con_buf);
|
||||
return PTR_ERR(info->console_thread);
|
||||
}
|
||||
wake_up_process(info->console_thread);
|
||||
|
@ -1217,7 +1073,7 @@ static void gs_console_write(struct console *co,
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->con_lock, flags);
|
||||
gs_buf_put(&info->con_buf, buf, count);
|
||||
kfifo_in(&info->con_buf, buf, count);
|
||||
spin_unlock_irqrestore(&info->con_lock, flags);
|
||||
|
||||
wake_up_process(info->console_thread);
|
||||
|
@ -1256,7 +1112,7 @@ static void gserial_console_exit(void)
|
|||
unregister_console(&gserial_cons);
|
||||
if (!IS_ERR_OR_NULL(info->console_thread))
|
||||
kthread_stop(info->console_thread);
|
||||
gs_buf_free(&info->con_buf);
|
||||
kfifo_free(&info->con_buf);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -1529,7 +1385,7 @@ void gserial_disconnect(struct gserial *gser)
|
|||
/* finally, free any unused/unusable I/O buffers */
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (port->port.count == 0 && !port->openclose)
|
||||
gs_buf_free(&port->port_write_buf);
|
||||
kfifo_free(&port->port_write_buf);
|
||||
gs_free_requests(gser->out, &port->read_pool, NULL);
|
||||
gs_free_requests(gser->out, &port->read_queue, NULL);
|
||||
gs_free_requests(gser->in, &port->write_pool, NULL);
|
||||
|
|
|
@ -1470,7 +1470,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
dev->setup_wLength = w_length;
|
||||
dev->setup_out_ready = 0;
|
||||
dev->setup_out_error = 0;
|
||||
value = 0;
|
||||
|
||||
/* read DATA stage for OUT right away */
|
||||
if (unlikely (!dev->setup_in && w_length)) {
|
||||
|
|
|
@ -2385,10 +2385,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
goto out_uninit;
|
||||
}
|
||||
if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
|
||||
dev_name(dev), udc) < 0) {
|
||||
dev_err(dev, "error requesting IRQ #%d\n", irq);
|
||||
goto out_uninit;
|
||||
}
|
||||
dev_name(dev), udc) < 0)
|
||||
goto report_request_failure;
|
||||
|
||||
/* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
|
||||
for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
|
||||
|
@ -2398,10 +2396,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
goto out_uninit;
|
||||
}
|
||||
if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
|
||||
dev_name(dev), &udc->iudma[i]) < 0) {
|
||||
dev_err(dev, "error requesting IRQ #%d\n", irq);
|
||||
goto out_uninit;
|
||||
}
|
||||
dev_name(dev), &udc->iudma[i]) < 0)
|
||||
goto report_request_failure;
|
||||
}
|
||||
|
||||
bcm63xx_udc_init_debugfs(udc);
|
||||
|
@ -2413,6 +2409,10 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
out_uninit:
|
||||
bcm63xx_uninit_udc_hw(udc);
|
||||
return rc;
|
||||
|
||||
report_request_failure:
|
||||
dev_err(dev, "error requesting IRQ #%d\n", irq);
|
||||
goto out_uninit;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -925,20 +925,8 @@ static void dummy_udc_set_speed(struct usb_gadget *_gadget,
|
|||
struct dummy *dum;
|
||||
|
||||
dum = gadget_dev_to_dummy(&_gadget->dev);
|
||||
|
||||
if (mod_data.is_super_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_SUPER, speed);
|
||||
else if (mod_data.is_high_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed);
|
||||
else
|
||||
dum->gadget.speed = USB_SPEED_FULL;
|
||||
|
||||
dum->gadget.speed = speed;
|
||||
dummy_udc_update_ep0(dum);
|
||||
|
||||
if (dum->gadget.speed < speed)
|
||||
dev_dbg(udc_dev(dum), "This device can perform faster"
|
||||
" if you connect it to a %s port...\n",
|
||||
usb_speed_string(speed));
|
||||
}
|
||||
|
||||
static int dummy_udc_start(struct usb_gadget *g,
|
||||
|
@ -2193,8 +2181,6 @@ static int dummy_hub_control(
|
|||
USB_PORT_STAT_LOW_SPEED;
|
||||
break;
|
||||
default:
|
||||
dum_hcd->dum->gadget.speed =
|
||||
USB_SPEED_FULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1543,7 +1543,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
|
|||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
break;
|
||||
case WAIT_FOR_SETUP:
|
||||
ERR("Unexpect ep0 packets\n");
|
||||
ERR("Unexpected ep0 packets\n");
|
||||
break;
|
||||
default:
|
||||
ep0stall(udc);
|
||||
|
|
|
@ -979,8 +979,6 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
|
|||
|
||||
max = ep->fifo_size;
|
||||
do {
|
||||
is_short = 0;
|
||||
|
||||
udccsr = udc_ep_readl(ep, UDCCSR);
|
||||
if (udccsr & UDCCSR_PC) {
|
||||
ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
|
||||
|
@ -1134,7 +1132,6 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
if (unlikely(!_ep))
|
||||
return -EINVAL;
|
||||
|
||||
dev = udc_usb_ep->dev;
|
||||
ep = udc_usb_ep->pxa_ep;
|
||||
if (unlikely(!ep))
|
||||
return -EINVAL;
|
||||
|
|
|
@ -225,6 +225,7 @@ DECLARE_EVENT_CLASS(udc_log_req,
|
|||
__field(unsigned, short_not_ok)
|
||||
__field(int, status)
|
||||
__field(int, ret)
|
||||
__field(struct usb_request *, req)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name);
|
||||
|
@ -238,9 +239,10 @@ DECLARE_EVENT_CLASS(udc_log_req,
|
|||
__entry->short_not_ok = req->short_not_ok;
|
||||
__entry->status = req->status;
|
||||
__entry->ret = ret;
|
||||
__entry->req = req;
|
||||
),
|
||||
TP_printk("%s: length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
|
||||
__get_str(name), __entry->actual, __entry->length,
|
||||
TP_printk("%s: req %p length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
|
||||
__get_str(name),__entry->req, __entry->actual, __entry->length,
|
||||
__entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
|
|
|
@ -963,10 +963,8 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
|
|||
gfp_t gfp_flags)
|
||||
{
|
||||
struct xusb_ep *ep = to_xusb_ep(_ep);
|
||||
struct xusb_udc *udc;
|
||||
struct xusb_req *req;
|
||||
|
||||
udc = ep->udc;
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
|
|
@ -323,6 +323,14 @@ static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
|
|||
return *phy == match_data;
|
||||
}
|
||||
|
||||
static void usb_charger_init(struct usb_phy *usb_phy)
|
||||
{
|
||||
usb_phy->chg_type = UNKNOWN_TYPE;
|
||||
usb_phy->chg_state = USB_CHARGER_DEFAULT;
|
||||
usb_phy_set_default_current(usb_phy);
|
||||
INIT_WORK(&usb_phy->chg_work, usb_phy_notify_charger_work);
|
||||
}
|
||||
|
||||
static int usb_add_extcon(struct usb_phy *x)
|
||||
{
|
||||
int ret;
|
||||
|
@ -406,10 +414,6 @@ static int usb_add_extcon(struct usb_phy *x)
|
|||
}
|
||||
}
|
||||
|
||||
usb_phy_set_default_current(x);
|
||||
INIT_WORK(&x->chg_work, usb_phy_notify_charger_work);
|
||||
x->chg_type = UNKNOWN_TYPE;
|
||||
x->chg_state = USB_CHARGER_DEFAULT;
|
||||
if (x->type_nb.notifier_call)
|
||||
__usb_phy_get_charger_type(x);
|
||||
|
||||
|
@ -704,6 +708,7 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
usb_charger_init(x);
|
||||
ret = usb_add_extcon(x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -749,6 +754,7 @@ int usb_add_phy_dev(struct usb_phy *x)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
usb_charger_init(x);
|
||||
ret = usb_add_extcon(x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -581,6 +581,15 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
break;
|
||||
case USBHS_TYPE_RCAR_GEN3_WITH_PLL:
|
||||
priv->pfunc = usbhs_rcar3_with_pll_ops;
|
||||
if (!IS_ERR_OR_NULL(priv->edev)) {
|
||||
priv->nb.notifier_call = priv->pfunc.notifier;
|
||||
ret = devm_extcon_register_notifier(&pdev->dev,
|
||||
priv->edev,
|
||||
EXTCON_USB_HOST,
|
||||
&priv->nb);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "no notifier registered\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!info->platform_callback.get_id) {
|
||||
|
|
|
@ -249,6 +249,7 @@ struct usbhs_priv {
|
|||
struct platform_device *pdev;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct notifier_block nb;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
|
|
|
@ -94,8 +94,6 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
|
|||
return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node);
|
||||
}
|
||||
|
||||
static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
|
||||
struct usbhs_fifo *fifo);
|
||||
static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
|
||||
struct usbhs_fifo *fifo);
|
||||
static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
|
||||
|
@ -124,10 +122,11 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
|
|||
chan = usbhsf_dma_chan_get(fifo, pkt);
|
||||
if (chan) {
|
||||
dmaengine_terminate_all(chan);
|
||||
usbhsf_fifo_clear(pipe, fifo);
|
||||
usbhsf_dma_unmap(pkt);
|
||||
}
|
||||
|
||||
usbhs_pipe_clear_without_sequence(pipe, 0, 0);
|
||||
|
||||
__usbhsf_pkt_del(pkt);
|
||||
}
|
||||
|
||||
|
@ -256,15 +255,9 @@ static void usbhsf_send_terminator(struct usbhs_pipe *pipe,
|
|||
static int usbhsf_fifo_barrier(struct usbhs_priv *priv,
|
||||
struct usbhs_fifo *fifo)
|
||||
{
|
||||
int timeout = 1024;
|
||||
|
||||
do {
|
||||
/* The FIFO port is accessible */
|
||||
if (usbhs_read(priv, fifo->ctr) & FRDY)
|
||||
return 0;
|
||||
|
||||
udelay(10);
|
||||
} while (timeout--);
|
||||
/* The FIFO port is accessible */
|
||||
if (usbhs_read(priv, fifo->ctr) & FRDY)
|
||||
return 0;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
@ -278,8 +271,8 @@ static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
|
|||
if (!usbhs_pipe_is_dcp(pipe)) {
|
||||
/*
|
||||
* This driver checks the pipe condition first to avoid -EBUSY
|
||||
* from usbhsf_fifo_barrier() with about 10 msec delay in
|
||||
* the interrupt handler if the pipe is RX direction and empty.
|
||||
* from usbhsf_fifo_barrier() if the pipe is RX direction and
|
||||
* empty.
|
||||
*/
|
||||
if (usbhs_pipe_is_dir_in(pipe))
|
||||
ret = usbhs_pipe_is_accessible(pipe);
|
||||
|
|
|
@ -590,10 +590,22 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe)
|
|||
}
|
||||
}
|
||||
|
||||
void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
|
||||
/* Should call usbhsp_pipe_select() before */
|
||||
void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe,
|
||||
int needs_bfre, int bfre_enable)
|
||||
{
|
||||
int sequence;
|
||||
|
||||
usbhsp_pipe_select(pipe);
|
||||
sequence = usbhs_pipe_get_data_sequence(pipe);
|
||||
if (needs_bfre)
|
||||
usbhsp_pipe_cfg_set(pipe, BFRE, bfre_enable ? BFRE : 0);
|
||||
usbhs_pipe_clear(pipe);
|
||||
usbhs_pipe_data_sequence(pipe, sequence);
|
||||
}
|
||||
|
||||
void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
|
||||
{
|
||||
if (usbhs_pipe_is_dcp(pipe))
|
||||
return;
|
||||
|
||||
|
@ -602,10 +614,7 @@ void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
|
|||
if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE)))
|
||||
return;
|
||||
|
||||
sequence = usbhs_pipe_get_data_sequence(pipe);
|
||||
usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0);
|
||||
usbhs_pipe_clear(pipe);
|
||||
usbhs_pipe_data_sequence(pipe, sequence);
|
||||
usbhs_pipe_clear_without_sequence(pipe, 1, enable);
|
||||
}
|
||||
|
||||
static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
|
||||
|
|
|
@ -80,6 +80,8 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
|
|||
struct usbhs_pkt *pkt, int map));
|
||||
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_clear(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe,
|
||||
int needs_bfre, int bfre_enable);
|
||||
int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_enable(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_disable(struct usbhs_pipe *pipe);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
* Remarks: bit[31:11] and bit[9:6] should be 0
|
||||
*/
|
||||
#define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */
|
||||
#define UGCTRL2_USB0SEL_EHCI 0x00000010
|
||||
#define UGCTRL2_USB0SEL_HSUSB 0x00000020
|
||||
#define UGCTRL2_USB0SEL_OTG 0x00000030
|
||||
#define UGCTRL2_VBUSSEL 0x00000400
|
||||
|
@ -44,13 +45,25 @@ static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg)
|
|||
return ioread32(priv->base + reg);
|
||||
}
|
||||
|
||||
static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val)
|
||||
{
|
||||
usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3);
|
||||
}
|
||||
|
||||
static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci)
|
||||
{
|
||||
if (ehci)
|
||||
usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI);
|
||||
else
|
||||
usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB);
|
||||
}
|
||||
|
||||
static int usbhs_rcar3_power_ctrl(struct platform_device *pdev,
|
||||
void __iomem *base, int enable)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
|
||||
|
||||
usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG |
|
||||
UGCTRL2_VBUSSEL);
|
||||
usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL);
|
||||
|
||||
if (enable) {
|
||||
usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
|
||||
|
@ -70,11 +83,14 @@ static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev,
|
|||
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
|
||||
u32 val;
|
||||
int timeout = 1000;
|
||||
bool is_host = false;
|
||||
|
||||
if (enable) {
|
||||
usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */
|
||||
usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 |
|
||||
UGCTRL2_USB0SEL_HSUSB);
|
||||
if (priv->edev)
|
||||
is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST);
|
||||
|
||||
usbhs_rcar3_set_usbsel(priv, is_host);
|
||||
|
||||
usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
|
||||
do {
|
||||
|
@ -96,6 +112,16 @@ static int usbhs_rcar3_get_id(struct platform_device *pdev)
|
|||
return USBHS_GADGET;
|
||||
}
|
||||
|
||||
static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event,
|
||||
void *data)
|
||||
{
|
||||
struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb);
|
||||
|
||||
usbhs_rcar3_set_usbsel(priv, !!event);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
|
||||
.power_ctrl = usbhs_rcar3_power_ctrl,
|
||||
.get_id = usbhs_rcar3_get_id,
|
||||
|
@ -104,4 +130,5 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
|
|||
const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = {
|
||||
.power_ctrl = usbhs_rcar3_power_and_pll_ctrl,
|
||||
.get_id = usbhs_rcar3_get_id,
|
||||
.notifier = usbhs_rcar3_notifier,
|
||||
};
|
||||
|
|
|
@ -330,6 +330,7 @@ struct usb_gadget_ops {
|
|||
* @name: Identifies the controller hardware type. Used in diagnostics
|
||||
* and sometimes configuration.
|
||||
* @dev: Driver model state for this abstract device.
|
||||
* @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP
|
||||
* @out_epnum: last used out ep number
|
||||
* @in_epnum: last used in ep number
|
||||
* @mA: last set mA value
|
||||
|
@ -394,6 +395,7 @@ struct usb_gadget {
|
|||
enum usb_device_state state;
|
||||
const char *name;
|
||||
struct device dev;
|
||||
unsigned isoch_delay;
|
||||
unsigned out_epnum;
|
||||
unsigned in_epnum;
|
||||
unsigned mA;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
#ifndef RENESAS_USB_H
|
||||
#define RENESAS_USB_H
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
|
@ -98,6 +99,13 @@ struct renesas_usbhs_platform_callback {
|
|||
* VBUS control is needed for Host
|
||||
*/
|
||||
int (*set_vbus)(struct platform_device *pdev, int enable);
|
||||
|
||||
/*
|
||||
* option:
|
||||
* extcon notifier to set host/peripheral mode.
|
||||
*/
|
||||
int (*notifier)(struct notifier_block *nb, unsigned long event,
|
||||
void *data);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue