diff options
31 files changed, 277 insertions, 245 deletions
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 52fb41046b34..44e8bab159ad 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -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 diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 31749c79045f..cd77af3b1565 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -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; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 7b6eb0ad513b..a5d72fcd1603 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -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 */ diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 78e9e01051b5..ad60e46e66e1 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -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; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 916d991b96b8..a5dfd9d8bd9a 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -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; } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index fcd1676c7f0b..e34ad5e65350 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -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 @@ -1441,6 +1444,55 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, } /** + * 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 * * @hsotg: The HCD state structure for the DWC OTG controller @@ -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; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 3e26550d13dd..4703478f702f 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -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; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 07832509584f..c32d2b965ffb 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -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; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4a4a4c98508c..03c7aaaac926 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -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; }; diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 368f8e59219a..bfb90c52d8fc 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -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; }), diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index fd3e7ad2eb0e..9c2e4a17918e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -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; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 639dd1b163a0..616ef49ccb49 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -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); diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index a9dd5c64e6c7..babaee981aa7 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -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) ); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index b6cf5ab5a0a1..038a27a13ebc 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -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 @@ done_spin: /* 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; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 4d653d2960d4..29436f75bbe0 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -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); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 9343ec436485..cb8e1761d405 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1470,7 +1470,6 @@ delegate: 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)) { diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 29f254793592..465ccd1104de 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -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; } /** diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index d0128f92ec5a..e744d4b7bfed 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -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; } } diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index d606d4f13098..e5b4ee96c4bf 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -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); diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index be2761f1b3f5..fadcf2653c3d 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -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; diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index f07ddb3f4bb9..2d1f68b5ea76 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -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", diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 7da2b9ce8cb3..6407e433bc78 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -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; diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index f97cb47577fc..bceb2c9988dd 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -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; diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 56079bb6759a..c5289b3ecf8d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -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) { diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 64797784a6df..c9747f064601 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -249,6 +249,7 @@ struct usbhs_priv { struct platform_device *pdev; struct extcon_dev *edev; + struct notifier_block nb; spinlock_t lock; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 2d24ef3076ef..5925d111bd47 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -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); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 093cd8e87335..9677e0e31475 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -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) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index d3d002244891..3080423e600c 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -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); diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index c929d296c77b..d0ea4ff89622 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -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, }; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0142f3af0da6..66a5cff7ee14 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -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; diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 67102f3d59d4..9482735d4ca5 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -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); }; /* |