USB changes for v4.21
So it looks like folks are interested in dwc3 again. Almost 64% of the changes are in dwc3 this time around with some other bits in gadget functions and dwc2. There are two important parts here: a. removal of the waitqueue from dwc3's dequeue implementation, which will guarantee that gadget functions can dequeue from any context and; b. better method for starting isochronous transfers to avoid, as much as possible, missed isoc frames. Apart from these, we have the usual set of non-critical fixes and new features all over the place. -----BEGIN PGP SIGNATURE----- iQJRBAABCAA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAlwQ7sQdHGZlbGlwZS5i YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQbioA//fSpx5SV7undE1skG lgjx7Uv2lqnU1kJxhTtyH54tZVhGMmaz4mDVJ3bRk4xotdh3BGQ7nRuui9PDGZmC 81bSjRBVvHXG0xtQQ1AjNc6vTf5h2MVRAZ4U2dp7pkNOgkoWQSakyH3Tz4Brhpfh UWHLaVYrCb2R68b1pmQbJ6ckSkOu9Wt1yj3SJxvSOkRdbSgb9+khTNZJyyWuGort zw6pcK33AuLwfOuz7qk8Wihqwi6BOgSrCGU8UfspBRfGhZrw3DSTnS3wYZjdR0QF 2/jwoBwt59i2hpFsMHmMYGU+307oVQ6Y9b/PXpnj4KqIG2CROB8eirDFkToxe4hC Udy06IA70HUTBKzFmK1gCKZCZW3t2sB+zHtoVHJ8EARlFHWdLxLp83hA9p2Llfon 41iKnkf9CTiO2koEEzuOUOQW+KctIawryiUi4m+C5ZhJBKNk4GvIpXAg1JC9QX9E GyqEICkg1dvc3GG5HYjPU7xq8mEdcHVOrHoiUci4zo9hMh6nHt7dboNeXQ13LNU9 joEqLvKEhUsm0J7cKWYOpxlHmNWGWNgqRas4Mot9zLUP7yYrxqLT5b2hD3PcjPl4 Jl+rbm4/2YMztWAnCU85EEPwVgJx9k7uyAEtXtQaWUValsVsWUWI1/F//IjYeLZI AUx1euoarbCp6mXIoHLsOHV02uc= =n8VS -----END PGP SIGNATURE----- Merge tag 'usb-for-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: USB changes for v4.21 So it looks like folks are interested in dwc3 again. Almost 64% of the changes are in dwc3 this time around with some other bits in gadget functions and dwc2. There are two important parts here: a. removal of the waitqueue from dwc3's dequeue implementation, which will guarantee that gadget functions can dequeue from any context and; b. better method for starting isochronous transfers to avoid, as much as possible, missed isoc frames. Apart from these, we have the usual set of non-critical fixes and new features all over the place. * tag 'usb-for-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (56 commits) usb: dwc2: Fix disable all EP's on disconnect usb: dwc3: gadget: Disable CSP for stream OUT ep usb: dwc2: disable power_down on Amlogic devices Revert "usb: dwc3: pci: Use devm functions to get the phy GPIOs" USB: gadget: udc: s3c2410_udc: convert to DEFINE_SHOW_ATTRIBUTE usb: mtu3: fix dbginfo in qmu_tx_zlp_error_handler usb: dwc3: trace: add missing break statement to make compiler happy usb: dwc3: gadget: Report isoc transfer frame number usb: gadget: Introduce frame_number to usb_request usb: renesas_usbhs: Use SIMPLE_DEV_PM_OPS macro usb: renesas_usbhs: Remove dummy runtime PM callbacks usb: dwc2: host: use hrtimer for NAK retries usb: mtu3: clear SOFTCONN when clear USB3_EN if work as HS mode usb: mtu3: enable SETUPENDISR interrupt usb: mtu3: fix the issue about SetFeature(U1/U2_Enable) usb: mtu3: enable hardware remote wakeup from L1 automatically usb: mtu3: remove QMU checksum usb/mtu3: power down device ip at setup usb: dwc2: Disable power down feature on Samsung SoCs usb: dwc3: Correct the logic for checking TRB full in __dwc3_prepare_one_trb() ...
This commit is contained in:
commit
5ac93d0c5d
|
@ -37,7 +37,11 @@ Optional properties:
|
|||
- phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
|
||||
or "usb3-phy".
|
||||
- resets: a single pair of phandle and reset specifier
|
||||
- snps,usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
|
||||
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
|
||||
- snps,dis-start-transfer-quirk: when set, disable isoc START TRANSFER command
|
||||
failure SW work-around for DWC_usb31 version 1.70a-ea06
|
||||
and prior.
|
||||
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
|
||||
Only really useful for FPGA builds.
|
||||
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
|
||||
|
|
|
@ -262,7 +262,7 @@ static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg)
|
|||
if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
|
||||
dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
|
||||
dwc2_clear_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT);
|
||||
dwc2_set_bit(hsotg, DCFG, DCTL_RMTWKUPSIG);
|
||||
dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3165,8 +3165,6 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
|
|||
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
|
||||
}
|
||||
|
||||
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_disconnect - disconnect service
|
||||
* @hsotg: The device state.
|
||||
|
@ -3188,9 +3186,11 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
|
|||
/* all endpoints should be shutdown */
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
kill_all_requests(hsotg, hsotg->eps_in[ep],
|
||||
-ESHUTDOWN);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
kill_all_requests(hsotg, hsotg->eps_out[ep],
|
||||
-ESHUTDOWN);
|
||||
}
|
||||
|
||||
call_gadget(hsotg, disconnect);
|
||||
|
@ -3234,6 +3234,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
|
|||
GINTSTS_PTXFEMP | \
|
||||
GINTSTS_RXFLVL)
|
||||
|
||||
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
|
||||
/**
|
||||
* dwc2_hsotg_core_init - issue softreset to the core
|
||||
* @hsotg: The device state
|
||||
|
@ -4069,10 +4070,8 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
|||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
int index = hs_ep->index;
|
||||
unsigned long flags;
|
||||
u32 epctrl_reg;
|
||||
u32 ctrl;
|
||||
int locked;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
|
||||
|
||||
|
@ -4088,10 +4087,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
|||
|
||||
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
|
||||
|
||||
locked = spin_is_locked(&hsotg->lock);
|
||||
if (!locked)
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
ctrl = dwc2_readl(hsotg, epctrl_reg);
|
||||
|
||||
if (ctrl & DXEPCTL_EPENA)
|
||||
|
@ -4114,12 +4109,22 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
|||
hs_ep->fifo_index = 0;
|
||||
hs_ep->fifo_size = 0;
|
||||
|
||||
if (!locked)
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep)
|
||||
{
|
||||
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
ret = dwc2_hsotg_ep_disable(ep);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_list - check request is on the given endpoint
|
||||
* @ep: The endpoint to check.
|
||||
|
@ -4267,7 +4272,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
|
|||
|
||||
static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
|
||||
.enable = dwc2_hsotg_ep_enable,
|
||||
.disable = dwc2_hsotg_ep_disable,
|
||||
.disable = dwc2_hsotg_ep_disable_lock,
|
||||
.alloc_request = dwc2_hsotg_ep_alloc_request,
|
||||
.free_request = dwc2_hsotg_ep_free_request,
|
||||
.queue = dwc2_hsotg_ep_queue_lock,
|
||||
|
@ -4407,9 +4412,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
|
|||
/* all endpoints should be shutdown */
|
||||
for (ep = 1; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
@ -4857,9 +4862,9 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)
|
|||
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5026,6 +5031,7 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
|
|||
val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
|
||||
val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
|
||||
val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
|
||||
val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC;
|
||||
dwc2_writel(hsotg, val, GLPMCFG);
|
||||
dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
|
||||
|
||||
|
|
|
@ -366,7 +366,7 @@ struct dwc2_qh {
|
|||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
struct timer_list unreserve_timer;
|
||||
struct timer_list wait_timer;
|
||||
struct hrtimer wait_timer;
|
||||
struct dwc2_tt *dwc_tt;
|
||||
int ttport;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
#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))
|
||||
#define DWC2_RETRY_WAIT_DELAY 1*1E6L
|
||||
|
||||
/**
|
||||
* dwc2_periodic_channel_available() - Checks that a channel is available for a
|
||||
|
@ -1464,10 +1464,12 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
|
|||
* qh back to the "inactive" list, then queues transactions.
|
||||
*
|
||||
* @t: Pointer to wait_timer in a qh.
|
||||
*
|
||||
* Return: HRTIMER_NORESTART to not automatically restart this timer.
|
||||
*/
|
||||
static void dwc2_wait_timer_fn(struct timer_list *t)
|
||||
static enum hrtimer_restart dwc2_wait_timer_fn(struct hrtimer *t)
|
||||
{
|
||||
struct dwc2_qh *qh = from_timer(qh, t, wait_timer);
|
||||
struct dwc2_qh *qh = container_of(t, struct dwc2_qh, wait_timer);
|
||||
struct dwc2_hsotg *hsotg = qh->hsotg;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -1491,6 +1493,7 @@ static void dwc2_wait_timer_fn(struct timer_list *t)
|
|||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1521,7 +1524,8 @@ 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);
|
||||
hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
qh->wait_timer.function = &dwc2_wait_timer_fn;
|
||||
qh->ep_type = ep_type;
|
||||
qh->ep_is_in = ep_is_in;
|
||||
|
||||
|
@ -1690,7 +1694,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
* won't do anything anyway, but we want it to finish before we free
|
||||
* memory.
|
||||
*/
|
||||
del_timer_sync(&qh->wait_timer);
|
||||
hrtimer_cancel(&qh->wait_timer);
|
||||
|
||||
dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
|
||||
|
||||
|
@ -1716,6 +1720,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
{
|
||||
int status;
|
||||
u32 intr_mask;
|
||||
ktime_t delay;
|
||||
|
||||
if (dbg_qh(qh))
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
@ -1734,8 +1739,8 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
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);
|
||||
delay = ktime_set(0, DWC2_RETRY_WAIT_DELAY);
|
||||
hrtimer_start(&qh->wait_timer, delay, HRTIMER_MODE_REL);
|
||||
} else {
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
|
|
|
@ -333,6 +333,8 @@
|
|||
#define GLPMCFG_SNDLPM BIT(24)
|
||||
#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21)
|
||||
#define GLPMCFG_RETRY_CNT_SHIFT 21
|
||||
#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21)
|
||||
#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22)
|
||||
#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17)
|
||||
#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17
|
||||
#define GLPMCFG_L1RESUMEOK BIT(16)
|
||||
|
|
|
@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
|
|||
p->power_down = false;
|
||||
}
|
||||
|
||||
static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->power_down = 0;
|
||||
}
|
||||
|
||||
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
@ -111,6 +118,7 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
|
|||
p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
|
||||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT;
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
}
|
||||
|
||||
static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
|
||||
|
@ -151,7 +159,8 @@ const struct of_device_id dwc2_of_match_table[] = {
|
|||
{ .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
|
||||
{ .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
|
||||
{ .compatible = "snps,dwc2" },
|
||||
{ .compatible = "samsung,s3c6400-hsotg" },
|
||||
{ .compatible = "samsung,s3c6400-hsotg",
|
||||
.data = dwc2_set_s3c6400_params },
|
||||
{ .compatible = "amlogic,meson8-usb",
|
||||
.data = dwc2_set_amlogic_params },
|
||||
{ .compatible = "amlogic,meson8b-usb",
|
||||
|
|
|
@ -80,11 +80,12 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
|
|||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
/*
|
||||
* dwc_usb31 does not support OTG mode. If the controller
|
||||
* supports DRD but the dr_mode is not specified or set to OTG,
|
||||
* then set the mode to peripheral.
|
||||
* DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG
|
||||
* mode. If the controller supports DRD but the dr_mode is not
|
||||
* specified or set to OTG, then set the mode to peripheral.
|
||||
*/
|
||||
if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc))
|
||||
if (mode == USB_DR_MODE_OTG &&
|
||||
dwc->revision >= DWC3_REVISION_330A)
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
|
@ -661,6 +662,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
|||
|
||||
if (dwc->dis_enblslpm_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
else
|
||||
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
|
||||
if (dwc->dis_u2_freeclk_exists_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
|
||||
|
@ -702,6 +705,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
|
|||
/* Detected DWC_usb31 IP */
|
||||
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
|
||||
dwc->revision |= DWC3_REVISION_IS_DWC31;
|
||||
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -1244,8 +1248,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
"snps,is-utmi-l1-suspend");
|
||||
device_property_read_u8(dev, "snps,hird-threshold",
|
||||
&hird_threshold);
|
||||
dwc->dis_start_transfer_quirk = device_property_read_bool(dev,
|
||||
"snps,dis-start-transfer-quirk");
|
||||
dwc->usb3_lpm_capable = device_property_read_bool(dev,
|
||||
"snps,usb3_lpm_capable");
|
||||
dwc->usb2_lpm_disable = device_property_read_bool(dev,
|
||||
"snps,usb2-lpm-disable");
|
||||
device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
|
||||
&rx_thr_num_pkt_prd);
|
||||
device_property_read_u8(dev, "snps,rx-max-burst-prd",
|
||||
|
@ -1482,7 +1490,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize core: %d\n", ret);
|
||||
goto err4;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define DWC3_EP0_SETUP_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
#define DWC3_ISOC_MAX_RETRIES 5
|
||||
|
||||
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
|
||||
#define DWC3_EVENT_BUFFERS_SIZE 4096
|
||||
|
@ -174,13 +175,19 @@
|
|||
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
|
||||
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
|
||||
|
||||
/* Global Debug LSP MUX Select */
|
||||
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
|
||||
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
|
||||
#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4)
|
||||
#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf)
|
||||
|
||||
/* Global Debug Queue/FIFO Space Available Register */
|
||||
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
|
||||
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
|
||||
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
|
||||
|
||||
#define DWC3_TXFIFOQ 0
|
||||
#define DWC3_RXFIFOQ 1
|
||||
#define DWC3_TXFIFO 0
|
||||
#define DWC3_RXFIFO 1
|
||||
#define DWC3_TXREQQ 2
|
||||
#define DWC3_RXREQQ 3
|
||||
#define DWC3_RXINFOQ 4
|
||||
|
@ -253,6 +260,9 @@
|
|||
#define DWC3_GSTS_DEVICE_IP BIT(6)
|
||||
#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
|
||||
#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
|
||||
#define DWC3_GSTS_CURMOD(n) ((n) & 0x3)
|
||||
#define DWC3_GSTS_CURMOD_DEVICE 0
|
||||
#define DWC3_GSTS_CURMOD_HOST 1
|
||||
|
||||
/* Global USB2 PHY Configuration Register */
|
||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
|
||||
|
@ -321,6 +331,7 @@
|
|||
#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
|
||||
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
|
||||
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
|
||||
#define DWC3_GHWPARAMS1_ENDBC BIT(31)
|
||||
|
||||
/* Global HWPARAMS3 Register */
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
|
||||
|
@ -636,9 +647,9 @@ struct dwc3_event_buffer {
|
|||
/**
|
||||
* struct dwc3_ep - device side endpoint representation
|
||||
* @endpoint: usb endpoint
|
||||
* @cancelled_list: list of cancelled requests for this endpoint
|
||||
* @pending_list: list of pending requests for this endpoint
|
||||
* @started_list: list of started requests on this endpoint
|
||||
* @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
|
||||
* @lock: spinlock for endpoint request queue traversal
|
||||
* @regs: pointer to first endpoint register
|
||||
* @trb_pool: array of transaction buffers
|
||||
|
@ -656,14 +667,17 @@ struct dwc3_event_buffer {
|
|||
* @name: a human readable name e.g. ep1out-bulk
|
||||
* @direction: true for TX, false for RX
|
||||
* @stream_capable: true when streams are enabled
|
||||
* @combo_num: the test combination BIT[15:14] of the frame number to test
|
||||
* isochronous START TRANSFER command failure workaround
|
||||
* @start_cmd_status: the status of testing START TRANSFER command with
|
||||
* combo_num = 'b00
|
||||
*/
|
||||
struct dwc3_ep {
|
||||
struct usb_ep endpoint;
|
||||
struct list_head cancelled_list;
|
||||
struct list_head pending_list;
|
||||
struct list_head started_list;
|
||||
|
||||
wait_queue_head_t wait_end_transfer;
|
||||
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
|
||||
|
@ -705,6 +719,10 @@ struct dwc3_ep {
|
|||
|
||||
unsigned direction:1;
|
||||
unsigned stream_capable:1;
|
||||
|
||||
/* For isochronous START TRANSFER workaround only */
|
||||
u8 combo_num;
|
||||
int start_cmd_status;
|
||||
};
|
||||
|
||||
enum dwc3_phy {
|
||||
|
@ -766,6 +784,7 @@ enum dwc3_link_state {
|
|||
#define DWC3_TRB_CTRL_ISP_IMI BIT(10)
|
||||
#define DWC3_TRB_CTRL_IOC BIT(11)
|
||||
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
|
||||
#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14)
|
||||
|
||||
#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
|
||||
#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
|
||||
|
@ -847,11 +866,12 @@ struct dwc3_hwparams {
|
|||
* @epnum: endpoint number to which this request refers
|
||||
* @trb: pointer to struct dwc3_trb
|
||||
* @trb_dma: DMA address of @trb
|
||||
* @unaligned: true for OUT endpoints with length not divisible by maxp
|
||||
* @num_trbs: number of TRBs used by this request
|
||||
* @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
|
||||
* or unaligned OUT)
|
||||
* @direction: IN or OUT direction flag
|
||||
* @mapped: true when request has been dma-mapped
|
||||
* @started: request is started
|
||||
* @zero: wants a ZLP
|
||||
*/
|
||||
struct dwc3_request {
|
||||
struct usb_request request;
|
||||
|
@ -867,11 +887,12 @@ struct dwc3_request {
|
|||
struct dwc3_trb *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned unaligned:1;
|
||||
unsigned num_trbs;
|
||||
|
||||
unsigned needs_extra_trb:1;
|
||||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned started:1;
|
||||
unsigned zero:1;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -918,6 +939,7 @@ struct dwc3_scratchpad_array {
|
|||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
* @version_type: VERSIONTYPE register contents, a sub release of a revision
|
||||
* @dr_mode: requested mode of operation
|
||||
* @current_dr_role: current role of operation when in dual-role mode
|
||||
* @desired_dr_role: desired role of operation when in dual-role mode
|
||||
|
@ -945,6 +967,7 @@ struct dwc3_scratchpad_array {
|
|||
* @hwparams: copy of hwparams registers
|
||||
* @root: debugfs root folder pointer
|
||||
* @regset: debugfs pointer to regdump file
|
||||
* @dbg_lsp_select: current debug lsp mux register selection
|
||||
* @test_mode: true when we're entering a USB test mode
|
||||
* @test_mode_nr: test feature selector
|
||||
* @lpm_nyet_threshold: LPM NYET response threshold
|
||||
|
@ -970,7 +993,10 @@ struct dwc3_scratchpad_array {
|
|||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
|
||||
* not needed for DWC_usb31 version 1.70a-ea06 and below
|
||||
* @usb3_lpm_capable: set if hadrware supports Link Power Management
|
||||
* @usb2_lpm_disable: set to disable usb2 lpm
|
||||
* @disable_scramble_quirk: set if we enable the disable scramble quirk
|
||||
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
|
||||
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
|
||||
|
@ -1095,6 +1121,7 @@ struct dwc3 {
|
|||
#define DWC3_REVISION_290A 0x5533290a
|
||||
#define DWC3_REVISION_300A 0x5533300a
|
||||
#define DWC3_REVISION_310A 0x5533310a
|
||||
#define DWC3_REVISION_330A 0x5533330a
|
||||
|
||||
/*
|
||||
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
|
||||
|
@ -1103,6 +1130,17 @@ struct dwc3 {
|
|||
#define DWC3_REVISION_IS_DWC31 0x80000000
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
|
||||
|
||||
u32 version_type;
|
||||
|
||||
#define DWC31_VERSIONTYPE_EA01 0x65613031
|
||||
#define DWC31_VERSIONTYPE_EA02 0x65613032
|
||||
#define DWC31_VERSIONTYPE_EA03 0x65613033
|
||||
#define DWC31_VERSIONTYPE_EA04 0x65613034
|
||||
#define DWC31_VERSIONTYPE_EA05 0x65613035
|
||||
#define DWC31_VERSIONTYPE_EA06 0x65613036
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
|
@ -1121,6 +1159,8 @@ struct dwc3 {
|
|||
struct dentry *root;
|
||||
struct debugfs_regset32 *regset;
|
||||
|
||||
u32 dbg_lsp_select;
|
||||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
u8 lpm_nyet_threshold;
|
||||
|
@ -1145,7 +1185,9 @@ struct dwc3 {
|
|||
unsigned pullups_connected:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned dis_start_transfer_quirk:1;
|
||||
unsigned usb3_lpm_capable:1;
|
||||
unsigned usb2_lpm_disable:1;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
|
|
|
@ -116,6 +116,35 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_hs_link_string - returns highspeed and below link name
|
||||
* @link_state: link state code
|
||||
*/
|
||||
static inline const char *
|
||||
dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
|
||||
{
|
||||
switch (link_state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
return "On";
|
||||
case DWC3_LINK_STATE_U2:
|
||||
return "Sleep";
|
||||
case DWC3_LINK_STATE_U3:
|
||||
return "Suspend";
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
return "Disconnected";
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
return "Early Suspend";
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
return "Recovery";
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
return "Reset";
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
return "Resume";
|
||||
default:
|
||||
return "UNKNOWN link state\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_trb_type_string - returns TRB type as a string
|
||||
* @type: the type of the TRB
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "io.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define DWC3_LSP_MUX_UNSELECTED 0xfffff
|
||||
|
||||
#define dump_register(nm) \
|
||||
{ \
|
||||
.name = __stringify(nm), \
|
||||
|
@ -82,10 +84,6 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
|||
dump_register(GDBGFIFOSPACE),
|
||||
dump_register(GDBGLTSSM),
|
||||
dump_register(GDBGBMU),
|
||||
dump_register(GDBGLSPMUX),
|
||||
dump_register(GDBGLSP),
|
||||
dump_register(GDBGEPINFO0),
|
||||
dump_register(GDBGEPINFO1),
|
||||
dump_register(GPRTBIMAP_HS0),
|
||||
dump_register(GPRTBIMAP_HS1),
|
||||
dump_register(GPRTBIMAP_FS0),
|
||||
|
@ -279,6 +277,114 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
|||
dump_register(OSTS),
|
||||
};
|
||||
|
||||
static void dwc3_host_lsp(struct seq_file *s)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
bool dbc_enabled;
|
||||
u32 sel;
|
||||
u32 reg;
|
||||
u32 val;
|
||||
|
||||
dbc_enabled = !!(dwc->hwparams.hwparams1 & DWC3_GHWPARAMS1_ENDBC);
|
||||
|
||||
sel = dwc->dbg_lsp_select;
|
||||
if (sel == DWC3_LSP_MUX_UNSELECTED) {
|
||||
seq_puts(s, "Write LSP selection to print for host\n");
|
||||
return;
|
||||
}
|
||||
|
||||
reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val);
|
||||
|
||||
if (dbc_enabled && sel < 256) {
|
||||
reg |= DWC3_GDBGLSPMUX_ENDBC;
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_lsp(struct seq_file *s)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
reg = DWC3_GDBGLSPMUX_DEVSELECT(i);
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_lsp_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned int current_mode;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
current_mode = DWC3_GSTS_CURMOD(reg);
|
||||
|
||||
switch (current_mode) {
|
||||
case DWC3_GSTS_CURMOD_HOST:
|
||||
dwc3_host_lsp(s);
|
||||
break;
|
||||
case DWC3_GSTS_CURMOD_DEVICE:
|
||||
dwc3_gadget_lsp(s);
|
||||
break;
|
||||
default:
|
||||
seq_puts(s, "Mode is unknown, no LSP register printed\n");
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_lsp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dwc3_lsp_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t dwc3_lsp_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
char buf[32] = { 0 };
|
||||
u32 sel;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = kstrtouint(buf, 0, &sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->dbg_lsp_select = sel;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_lsp_fops = {
|
||||
.open = dwc3_lsp_open,
|
||||
.write = dwc3_lsp_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dwc3_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
|
@ -433,13 +539,24 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
enum dwc3_link_state state;
|
||||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
seq_puts(s, "Not available\n");
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
state = DWC3_DSTS_USBLNKST(reg);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
|
||||
seq_printf(s, "%s\n", dwc3_gadget_link_string(state));
|
||||
seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ?
|
||||
dwc3_gadget_link_string(state) :
|
||||
dwc3_gadget_hs_link_string(state));
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -457,6 +574,8 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
|||
unsigned long flags;
|
||||
enum dwc3_link_state state = 0;
|
||||
char buf[32];
|
||||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
@ -477,6 +596,21 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
|||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
|
||||
if (speed < DWC3_DSTS_SUPERSPEED &&
|
||||
state != DWC3_LINK_STATE_RECOV) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dwc3_gadget_set_link_state(dwc, state);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
@ -496,7 +630,7 @@ struct dwc3_ep_file_map {
|
|||
const struct file_operations *const fops;
|
||||
};
|
||||
|
||||
static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
@ -504,14 +638,18 @@ static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
|
|||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
|
||||
|
||||
/* Convert to bytes */
|
||||
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
val >>= 3;
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
@ -519,7 +657,11 @@ static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused)
|
|||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
|
||||
|
||||
/* Convert to bytes */
|
||||
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
val >>= 3;
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
@ -675,8 +817,32 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue);
|
||||
static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u64 ep_info;
|
||||
u32 lower_32_bits;
|
||||
u32 upper_32_bits;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
|
||||
lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0);
|
||||
upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1);
|
||||
|
||||
ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
|
||||
seq_printf(s, "0x%016llx\n", ep_info);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_size);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue);
|
||||
|
@ -684,10 +850,11 @@ DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue);
|
|||
DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_ep_info_register);
|
||||
|
||||
static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
|
||||
{ "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, },
|
||||
{ "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, },
|
||||
{ "tx_fifo_size", &dwc3_tx_fifo_size_fops, },
|
||||
{ "rx_fifo_size", &dwc3_rx_fifo_size_fops, },
|
||||
{ "tx_request_queue", &dwc3_tx_request_queue_fops, },
|
||||
{ "rx_request_queue", &dwc3_rx_request_queue_fops, },
|
||||
{ "rx_info_queue", &dwc3_rx_info_queue_fops, },
|
||||
|
@ -695,6 +862,7 @@ static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
|
|||
{ "event_queue", &dwc3_event_queue_fops, },
|
||||
{ "transfer_type", &dwc3_transfer_type_fops, },
|
||||
{ "trb_ring", &dwc3_trb_ring_fops, },
|
||||
{ "GDBGEPINFO", &dwc3_ep_info_register_fops, },
|
||||
};
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
|
||||
|
@ -742,6 +910,8 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
if (!dwc->regset)
|
||||
return;
|
||||
|
||||
dwc->dbg_lsp_select = DWC3_LSP_MUX_UNSELECTED;
|
||||
|
||||
dwc->regset->regs = dwc3_regs;
|
||||
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
|
||||
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
|
||||
|
@ -751,6 +921,9 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
|
||||
debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
|
||||
debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_lsp_fops);
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
|
||||
debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_mode_fops);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/extcon.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "core.h"
|
||||
|
@ -445,9 +446,19 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
|
|||
struct device *dev = dwc->dev;
|
||||
struct device_node *np_phy, *np_conn;
|
||||
struct extcon_dev *edev;
|
||||
const char *name;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "extcon"))
|
||||
return extcon_get_edev_by_phandle(dwc->dev, 0);
|
||||
if (device_property_read_bool(dev, "extcon"))
|
||||
return extcon_get_edev_by_phandle(dev, 0);
|
||||
|
||||
/*
|
||||
* Device tree platforms should get extcon via phandle.
|
||||
* On ACPI platforms, we get the name from a device property.
|
||||
* This device property is for kernel internal use only and
|
||||
* is expected to be set by the glue code.
|
||||
*/
|
||||
if (device_property_read_string(dev, "linux,extcon-name", &name) == 0)
|
||||
return extcon_get_extcon_dev(name);
|
||||
|
||||
np_phy = of_parse_phandle(dev->of_node, "phys", 0);
|
||||
np_conn = of_graph_get_remote_node(np_phy, -1, -1);
|
||||
|
|
|
@ -170,20 +170,20 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
|
|||
* put the gpio descriptors again here because the phy driver
|
||||
* might want to grab them, too.
|
||||
*/
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "cs",
|
||||
GPIOD_OUT_LOW);
|
||||
gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
||||
#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
|
||||
#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
|
||||
& ~((d)->interval - 1))
|
||||
|
||||
/**
|
||||
|
@ -647,8 +647,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
reg |= DWC3_DALEPENA_EP(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
|
||||
init_waitqueue_head(&dep->wait_end_transfer);
|
||||
|
||||
if (usb_endpoint_xfer_control(desc))
|
||||
goto out;
|
||||
|
||||
|
@ -672,7 +670,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
* Issue StartTransfer here with no-op TRB so we can always rely on No
|
||||
* Response Update Transfer command.
|
||||
*/
|
||||
if (usb_endpoint_xfer_bulk(desc) ||
|
||||
if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
|
||||
usb_endpoint_xfer_int(desc)) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_trb *trb;
|
||||
|
@ -919,8 +917,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
|||
struct usb_gadget *gadget = &dwc->gadget;
|
||||
enum usb_device_speed speed = gadget->speed;
|
||||
|
||||
dwc3_ep_inc_enq(dep);
|
||||
|
||||
trb->size = DWC3_TRB_SIZE_LENGTH(length);
|
||||
trb->bpl = lower_32_bits(dma);
|
||||
trb->bph = upper_32_bits(dma);
|
||||
|
@ -990,16 +986,20 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
|||
usb_endpoint_type(dep->endpoint.desc));
|
||||
}
|
||||
|
||||
/* always enable Continue on Short Packet */
|
||||
/*
|
||||
* Enable Continue on Short Packet
|
||||
* when endpoint is not a stream capable
|
||||
*/
|
||||
if (usb_endpoint_dir_out(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
if (!dep->stream_capable)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
|
||||
if (short_not_ok)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
}
|
||||
|
||||
if ((!no_interrupt && !chain) ||
|
||||
(dwc3_calc_trbs_left(dep) == 0))
|
||||
(dwc3_calc_trbs_left(dep) == 1))
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
|
||||
if (chain)
|
||||
|
@ -1010,6 +1010,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
|||
|
||||
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||
|
||||
dwc3_ep_inc_enq(dep);
|
||||
|
||||
trace_dwc3_prepare_trb(dep, trb);
|
||||
}
|
||||
|
||||
|
@ -1046,6 +1048,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
||||
}
|
||||
|
||||
req->num_trbs++;
|
||||
|
||||
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
|
||||
stream_id, short_not_ok, no_interrupt);
|
||||
}
|
||||
|
@ -1073,13 +1077,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->unaligned = true;
|
||||
req->needs_extra_trb = true;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_prepare_one_trb(dep, req, true, i);
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
req->num_trbs++;
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
|
||||
maxp - rem, false, 1,
|
||||
req->request.stream_id,
|
||||
|
@ -1117,13 +1122,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->unaligned = true;
|
||||
req->needs_extra_trb = true;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_prepare_one_trb(dep, req, true, 0);
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
req->num_trbs++;
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
|
||||
false, 1, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
|
@ -1133,13 +1139,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->zero = true;
|
||||
req->needs_extra_trb = true;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_prepare_one_trb(dep, req, true, 0);
|
||||
|
||||
/* Now prepare one extra TRB to handle ZLP */
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
req->num_trbs++;
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
|
||||
false, 1, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
|
@ -1232,6 +1239,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
|
|||
params.param1 = lower_32_bits(req->trb_dma);
|
||||
cmd = DWC3_DEPCMD_STARTTRANSFER;
|
||||
|
||||
if (dep->stream_capable)
|
||||
cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
|
||||
} else {
|
||||
|
@ -1263,17 +1273,151 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
|||
return DWC3_DSTS_SOFFN(reg);
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
||||
/**
|
||||
* dwc3_gadget_start_isoc_quirk - workaround invalid frame number
|
||||
* @dep: isoc endpoint
|
||||
*
|
||||
* This function tests for the correct combination of BIT[15:14] from the 16-bit
|
||||
* microframe number reported by the XferNotReady event for the future frame
|
||||
* number to start the isoc transfer.
|
||||
*
|
||||
* In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed
|
||||
* isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the
|
||||
* XferNotReady event are invalid. The driver uses this number to schedule the
|
||||
* isochronous transfer and passes it to the START TRANSFER command. Because
|
||||
* this number is invalid, the command may fail. If BIT[15:14] matches the
|
||||
* internal 16-bit microframe, the START TRANSFER command will pass and the
|
||||
* transfer will start at the scheduled time, if it is off by 1, the command
|
||||
* will still pass, but the transfer will start 2 seconds in the future. For all
|
||||
* other conditions, the START TRANSFER command will fail with bus-expiry.
|
||||
*
|
||||
* In order to workaround this issue, we can test for the correct combination of
|
||||
* BIT[15:14] by sending START TRANSFER commands with different values of
|
||||
* BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart
|
||||
* (or 2 seconds). 4 seconds into the future will result in a bus-expiry status.
|
||||
* As the result, within the 4 possible combinations for BIT[15:14], there will
|
||||
* be 2 successful and 2 failure START COMMAND status. One of the 2 successful
|
||||
* command status will result in a 2-second delay start. The smaller BIT[15:14]
|
||||
* value is the correct combination.
|
||||
*
|
||||
* Since there are only 4 outcomes and the results are ordered, we can simply
|
||||
* test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to
|
||||
* deduce the smaller successful combination.
|
||||
*
|
||||
* Let test0 = test status for combination 'b00 and test1 = test status for 'b01
|
||||
* of BIT[15:14]. The correct combination is as follow:
|
||||
*
|
||||
* if test0 fails and test1 passes, BIT[15:14] is 'b01
|
||||
* if test0 fails and test1 fails, BIT[15:14] is 'b10
|
||||
* if test0 passes and test1 fails, BIT[15:14] is 'b11
|
||||
* if test0 passes and test1 passes, BIT[15:14] is 'b00
|
||||
*
|
||||
* Synopsys STAR 9001202023: Wrong microframe number for isochronous IN
|
||||
* endpoints.
|
||||
*/
|
||||
static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
|
||||
{
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
dev_info(dep->dwc->dev, "%s: ran out of requests\n",
|
||||
dep->name);
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
return;
|
||||
int cmd_status = 0;
|
||||
bool test0;
|
||||
bool test1;
|
||||
|
||||
while (dep->combo_num < 2) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 test_frame_number;
|
||||
u32 cmd;
|
||||
|
||||
/*
|
||||
* Check if we can start isoc transfer on the next interval or
|
||||
* 4 uframes in the future with BIT[15:14] as dep->combo_num
|
||||
*/
|
||||
test_frame_number = dep->frame_number & 0x3fff;
|
||||
test_frame_number |= dep->combo_num << 14;
|
||||
test_frame_number += max_t(u32, 4, dep->interval);
|
||||
|
||||
params.param0 = upper_32_bits(dep->dwc->bounce_addr);
|
||||
params.param1 = lower_32_bits(dep->dwc->bounce_addr);
|
||||
|
||||
cmd = DWC3_DEPCMD_STARTTRANSFER;
|
||||
cmd |= DWC3_DEPCMD_PARAM(test_frame_number);
|
||||
cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
|
||||
/* Redo if some other failure beside bus-expiry is received */
|
||||
if (cmd_status && cmd_status != -EAGAIN) {
|
||||
dep->start_cmd_status = 0;
|
||||
dep->combo_num = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store the first test status */
|
||||
if (dep->combo_num == 0)
|
||||
dep->start_cmd_status = cmd_status;
|
||||
|
||||
dep->combo_num++;
|
||||
|
||||
/*
|
||||
* End the transfer if the START_TRANSFER command is successful
|
||||
* to wait for the next XferNotReady to test the command again
|
||||
*/
|
||||
if (cmd_status == 0) {
|
||||
dwc3_stop_active_transfer(dep, true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dep->frame_number = DWC3_ALIGN_FRAME(dep);
|
||||
__dwc3_gadget_kick_transfer(dep);
|
||||
/* test0 and test1 are both completed at this point */
|
||||
test0 = (dep->start_cmd_status == 0);
|
||||
test1 = (cmd_status == 0);
|
||||
|
||||
if (!test0 && test1)
|
||||
dep->combo_num = 1;
|
||||
else if (!test0 && !test1)
|
||||
dep->combo_num = 2;
|
||||
else if (test0 && !test1)
|
||||
dep->combo_num = 3;
|
||||
else if (test0 && test1)
|
||||
dep->combo_num = 0;
|
||||
|
||||
dep->frame_number &= 0x3fff;
|
||||
dep->frame_number |= dep->combo_num << 14;
|
||||
dep->frame_number += max_t(u32, 4, dep->interval);
|
||||
|
||||
/* Reinitialize test variables */
|
||||
dep->start_cmd_status = 0;
|
||||
dep->combo_num = 0;
|
||||
|
||||
return __dwc3_gadget_kick_transfer(dep);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
|
||||
(dwc->revision <= DWC3_USB31_REVISION_160A ||
|
||||
(dwc->revision == DWC3_USB31_REVISION_170A &&
|
||||
dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
|
||||
dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
|
||||
|
||||
if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
|
||||
return dwc3_gadget_start_isoc_quirk(dep);
|
||||
}
|
||||
|
||||
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
|
||||
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
|
@ -1314,8 +1458,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
|
||||
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
|
||||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
|
||||
__dwc3_gadget_start_isoc(dep);
|
||||
return 0;
|
||||
return __dwc3_gadget_start_isoc(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1341,6 +1484,40 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If request was already started, this means we had to
|
||||
* stop the transfer. With that we also need to ignore
|
||||
* all TRBs used by the request, however TRBs can only
|
||||
* be modified after completion of END_TRANSFER
|
||||
* command. So what we do here is that we wait for
|
||||
* END_TRANSFER completion and only after that, we jump
|
||||
* over TRBs by clearing HWO and incrementing dequeue
|
||||
* pointer.
|
||||
*/
|
||||
for (i = 0; i < req->num_trbs; i++) {
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
trb = req->trb + i;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_request *tmp;
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
|
||||
dwc3_gadget_ep_skip_trbs(dep, req);
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
||||
struct usb_request *request)
|
||||
{
|
||||
|
@ -1371,68 +1548,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
/* wait until it is processed */
|
||||
dwc3_stop_active_transfer(dep, true);
|
||||
|
||||
/*
|
||||
* If request was already started, this means we had to
|
||||
* stop the transfer. With that we also need to ignore
|
||||
* all TRBs used by the request, however TRBs can only
|
||||
* be modified after completion of END_TRANSFER
|
||||
* command. So what we do here is that we wait for
|
||||
* END_TRANSFER completion and only after that, we jump
|
||||
* over TRBs by clearing HWO and incrementing dequeue
|
||||
* pointer.
|
||||
*
|
||||
* Note that we have 2 possible types of transfers here:
|
||||
*
|
||||
* i) Linear buffer request
|
||||
* ii) SG-list based request
|
||||
*
|
||||
* SG-list based requests will have r->num_pending_sgs
|
||||
* set to a valid number (> 0). Linear requests,
|
||||
* normally use a single TRB.
|
||||
*
|
||||
* For each of these two cases, if r->unaligned flag is
|
||||
* set, one extra TRB has been used to align transfer
|
||||
* size to wMaxPacketSize.
|
||||
*
|
||||
* All of these cases need to be taken into
|
||||
* consideration so we don't mess up our TRB ring
|
||||
* pointers.
|
||||
*/
|
||||
wait_event_lock_irq(dep->wait_end_transfer,
|
||||
!(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
|
||||
dwc->lock);
|
||||
|
||||
if (!r->trb)
|
||||
goto out0;
|
||||
|
||||
if (r->num_pending_sgs) {
|
||||
struct dwc3_trb *trb;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < r->num_pending_sgs; i++) {
|
||||
trb = r->trb + i;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
|
||||
if (r->unaligned || r->zero) {
|
||||
trb = r->trb + r->num_pending_sgs + 1;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
} else {
|
||||
struct dwc3_trb *trb = r->trb;
|
||||
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
|
||||
if (r->unaligned || r->zero) {
|
||||
trb = r->trb + 1;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
}
|
||||
goto out1;
|
||||
dwc3_gadget_move_cancelled_request(req);
|
||||
goto out0;
|
||||
}
|
||||
dev_err(dwc->dev, "request %pK was not queued to %s\n",
|
||||
request, ep->name);
|
||||
|
@ -1440,9 +1560,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
goto out0;
|
||||
}
|
||||
|
||||
out1:
|
||||
/* giveback the request */
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
|
||||
out0:
|
||||
|
@ -1934,8 +2051,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
|||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
int epnum;
|
||||
u32 tmo_eps = 0;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
|
@ -1944,36 +2059,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
|||
|
||||
__dwc3_gadget_stop(dwc);
|
||||
|
||||
for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
int ret;
|
||||
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
|
||||
continue;
|
||||
|
||||
ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer,
|
||||
!(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
|
||||
dwc->lock, msecs_to_jiffies(5));
|
||||
|
||||
if (ret <= 0) {
|
||||
/* Timed out or interrupted! There's nothing much
|
||||
* we can do so we just log here and print which
|
||||
* endpoints timed out at the end.
|
||||
*/
|
||||
tmo_eps |= 1 << epnum;
|
||||
dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmo_eps) {
|
||||
dev_err(dwc->dev,
|
||||
"end transfer timed out on endpoints 0x%x [bitmap]\n",
|
||||
tmo_eps);
|
||||
}
|
||||
|
||||
out:
|
||||
dwc->gadget_driver = NULL;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
@ -2148,6 +2233,8 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
|||
dep->direction = direction;
|
||||
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
||||
dwc->eps[epnum] = dep;
|
||||
dep->combo_num = 0;
|
||||
dep->start_cmd_status = 0;
|
||||
|
||||
snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
|
||||
direction ? "in" : "out");
|
||||
|
@ -2176,6 +2263,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
|||
|
||||
INIT_LIST_HEAD(&dep->pending_list);
|
||||
INIT_LIST_HEAD(&dep->started_list);
|
||||
INIT_LIST_HEAD(&dep->cancelled_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2235,6 +2323,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
|
|||
dwc3_ep_inc_deq(dep);
|
||||
|
||||
trace_dwc3_complete_trb(dep, trb);
|
||||
req->num_trbs--;
|
||||
|
||||
/*
|
||||
* If we're in the middle of series of chained TRBs and we
|
||||
|
@ -2249,12 +2338,26 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
|
|||
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
|
||||
/*
|
||||
* For isochronous transfers, the first TRB in a service interval must
|
||||
* have the Isoc-First type. Track and report its interval frame number.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
(trb->ctrl & DWC3_TRBCTL_ISOCHRONOUS_FIRST)) {
|
||||
unsigned int frame_number;
|
||||
|
||||
frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl);
|
||||
frame_number &= ~(dep->interval - 1);
|
||||
req->request.frame_number = frame_number;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're dealing with unaligned size OUT transfer, we will be left
|
||||
* with one TRB pending in the ring. We need to manually clear HWO bit
|
||||
* from that TRB.
|
||||
*/
|
||||
if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
|
||||
|
||||
if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
return 1;
|
||||
}
|
||||
|
@ -2331,11 +2434,10 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
|
|||
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
|
||||
status);
|
||||
|
||||
if (req->unaligned || req->zero) {
|
||||
if (req->needs_extra_trb) {
|
||||
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
|
||||
status);
|
||||
req->unaligned = false;
|
||||
req->zero = false;
|
||||
req->needs_extra_trb = false;
|
||||
}
|
||||
|
||||
req->request.actual = req->request.length - req->remaining;
|
||||
|
@ -2430,7 +2532,7 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
|
|||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc3_gadget_endpoint_frame_from_event(dep, event);
|
||||
__dwc3_gadget_start_isoc(dep);
|
||||
(void) __dwc3_gadget_start_isoc(dep);
|
||||
}
|
||||
|
||||
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
|
@ -2468,7 +2570,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
|
||||
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
|
||||
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
|
||||
wake_up(&dep->wait_end_transfer);
|
||||
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
|
||||
}
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
|
|
|
@ -79,6 +79,21 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
|
|||
list_move_tail(&req->list, &dep->started_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
|
||||
* @req: the request to be moved
|
||||
*
|
||||
* Caller should take care of locking. This function will move @req from its
|
||||
* current list to the endpoint's cancelled_list.
|
||||
*/
|
||||
static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3_ep *dep = req->dep;
|
||||
|
||||
req->started = false;
|
||||
list_move_tail(&req->list, &dep->cancelled_list);
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ out:
|
|||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct property_entry props[3];
|
||||
struct property_entry props[4];
|
||||
struct platform_device *xhci;
|
||||
int ret, irq;
|
||||
struct resource *res;
|
||||
|
@ -93,6 +93,9 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
if (dwc->usb3_lpm_capable)
|
||||
props[prop_idx++].name = "usb3-lpm-capable";
|
||||
|
||||
if (dwc->usb2_lpm_disable)
|
||||
props[prop_idx++].name = "usb2-lpm-disable";
|
||||
|
||||
/**
|
||||
* WORKAROUND: dwc3 revisions <=3.00a have a limitation
|
||||
* where Port Disable command doesn't work.
|
||||
|
|
|
@ -199,7 +199,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
|||
__entry->param2 = params->param2;
|
||||
__entry->cmd_status = cmd_status;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x --> status: %s",
|
||||
TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
|
||||
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param0,
|
||||
__entry->param1, __entry->param2,
|
||||
|
@ -251,9 +251,11 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
s = "2x ";
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
s = "3x ";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s = "";
|
||||
} s; }),
|
||||
|
|
|
@ -18,11 +18,15 @@
|
|||
#include <linux/pagemap.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/usb/ccid.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
|
||||
|
@ -218,6 +222,8 @@ struct ffs_io_data {
|
|||
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
struct sg_table sgt;
|
||||
bool use_sg;
|
||||
|
||||
struct ffs_data *ffs;
|
||||
};
|
||||
|
@ -749,6 +755,65 @@ static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a virtually contiguous buffer and create a scatterlist describing it
|
||||
* @sg_table - pointer to a place to be filled with sg_table contents
|
||||
* @size - required buffer size
|
||||
*/
|
||||
static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
|
||||
{
|
||||
struct page **pages;
|
||||
void *vaddr, *ptr;
|
||||
unsigned int n_pages;
|
||||
int i;
|
||||
|
||||
vaddr = vmalloc(sz);
|
||||
if (!vaddr)
|
||||
return NULL;
|
||||
|
||||
n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
|
||||
pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pages) {
|
||||
vfree(vaddr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0, ptr = vaddr; i < n_pages; ++i, ptr += PAGE_SIZE)
|
||||
pages[i] = vmalloc_to_page(ptr);
|
||||
|
||||
if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) {
|
||||
kvfree(pages);
|
||||
vfree(vaddr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
kvfree(pages);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
|
||||
size_t data_len)
|
||||
{
|
||||
if (io_data->use_sg)
|
||||
return ffs_build_sg_list(&io_data->sgt, data_len);
|
||||
|
||||
return kmalloc(data_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void ffs_free_buffer(struct ffs_io_data *io_data)
|
||||
{
|
||||
if (!io_data->buf)
|
||||
return;
|
||||
|
||||
if (io_data->use_sg) {
|
||||
sg_free_table(&io_data->sgt);
|
||||
vfree(io_data->buf);
|
||||
} else {
|
||||
kfree(io_data->buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void ffs_user_copy_worker(struct work_struct *work)
|
||||
{
|
||||
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
|
||||
|
@ -776,7 +841,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
|
|||
|
||||
if (io_data->read)
|
||||
kfree(io_data->to_free);
|
||||
kfree(io_data->buf);
|
||||
ffs_free_buffer(io_data);
|
||||
kfree(io_data);
|
||||
}
|
||||
|
||||
|
@ -932,6 +997,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
* earlier
|
||||
*/
|
||||
gadget = epfile->ffs->gadget;
|
||||
io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE;
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
/* In the meantime, endpoint got disabled or changed. */
|
||||
|
@ -948,7 +1014,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
data = kmalloc(data_len, GFP_KERNEL);
|
||||
data = ffs_alloc_buffer(io_data, data_len);
|
||||
if (unlikely(!data)) {
|
||||
ret = -ENOMEM;
|
||||
goto error_mutex;
|
||||
|
@ -988,8 +1054,16 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
bool interrupted = false;
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
if (io_data->use_sg) {
|
||||
req->buf = NULL;
|
||||
req->sg = io_data->sgt.sgl;
|
||||
req->num_sgs = io_data->sgt.nents;
|
||||
} else {
|
||||
req->buf = data;
|
||||
}
|
||||
req->length = data_len;
|
||||
|
||||
io_data->buf = data;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
@ -1022,8 +1096,14 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
if (io_data->use_sg) {
|
||||
req->buf = NULL;
|
||||
req->sg = io_data->sgt.sgl;
|
||||
req->num_sgs = io_data->sgt.nents;
|
||||
} else {
|
||||
req->buf = data;
|
||||
}
|
||||
req->length = data_len;
|
||||
|
||||
io_data->buf = data;
|
||||
io_data->ep = ep->ep;
|
||||
|
@ -1052,7 +1132,7 @@ error_lock:
|
|||
error_mutex:
|
||||
mutex_unlock(&epfile->mutex);
|
||||
error:
|
||||
kfree(data);
|
||||
ffs_free_buffer(io_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1926,7 +2006,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
|
|||
|
||||
static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
ffs_entity_callback entity,
|
||||
void *priv)
|
||||
void *priv, int *current_class)
|
||||
{
|
||||
struct usb_descriptor_header *_ds = (void *)data;
|
||||
u8 length;
|
||||
|
@ -1984,6 +2064,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
|||
__entity(INTERFACE, ds->bInterfaceNumber);
|
||||
if (ds->iInterface)
|
||||
__entity(STRING, ds->iInterface);
|
||||
*current_class = ds->bInterfaceClass;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1997,11 +2078,22 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
|||
}
|
||||
break;
|
||||
|
||||
case HID_DT_HID:
|
||||
pr_vdebug("hid descriptor\n");
|
||||
if (length != sizeof(struct hid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
case USB_TYPE_CLASS | 0x01:
|
||||
if (*current_class == USB_INTERFACE_CLASS_HID) {
|
||||
pr_vdebug("hid descriptor\n");
|
||||
if (length != sizeof(struct hid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
} else if (*current_class == USB_INTERFACE_CLASS_CCID) {
|
||||
pr_vdebug("ccid descriptor\n");
|
||||
if (length != sizeof(struct ccid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
} else {
|
||||
pr_vdebug("unknown descriptor: %d for class %d\n",
|
||||
_ds->bDescriptorType, *current_class);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case USB_DT_OTG:
|
||||
if (length != sizeof(struct usb_otg_descriptor))
|
||||
|
@ -2058,6 +2150,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
|||
{
|
||||
const unsigned _len = len;
|
||||
unsigned long num = 0;
|
||||
int current_class = -1;
|
||||
|
||||
ENTER();
|
||||
|
||||
|
@ -2078,7 +2171,8 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
|||
if (!data)
|
||||
return _len - len;
|
||||
|
||||
ret = ffs_do_single_desc(data, len, entity, priv);
|
||||
ret = ffs_do_single_desc(data, len, entity, priv,
|
||||
¤t_class);
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_debug("%s returns %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
|
|
@ -102,7 +102,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
|
|||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
}
|
||||
|
||||
static struct vb2_ops uvc_queue_qops = {
|
||||
static const struct vb2_ops uvc_queue_qops = {
|
||||
.queue_setup = uvc_queue_setup,
|
||||
.buf_prepare = uvc_buffer_prepare,
|
||||
.buf_queue = uvc_buffer_queue,
|
||||
|
|
|
@ -438,7 +438,7 @@ static int ast_vhub_udc_stop(struct usb_gadget *gadget)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops ast_vhub_udc_ops = {
|
||||
static const struct usb_gadget_ops ast_vhub_udc_ops = {
|
||||
.get_frame = ast_vhub_udc_get_frame,
|
||||
.wakeup = ast_vhub_udc_wakeup,
|
||||
.pullup = ast_vhub_udc_pullup,
|
||||
|
|
|
@ -358,6 +358,7 @@ struct renesas_usb3 {
|
|||
bool extcon_host; /* check id and set EXTCON_USB_HOST */
|
||||
bool extcon_usb; /* check vbus and set EXTCON_USB */
|
||||
bool forced_b_device;
|
||||
bool start_to_connect;
|
||||
};
|
||||
|
||||
#define gadget_to_renesas_usb3(_gadget) \
|
||||
|
@ -476,7 +477,8 @@ static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
|
|||
static void usb3_init_epc_registers(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3_write(usb3, ~0, USB3_USB_INT_STA_1);
|
||||
usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
|
||||
if (!usb3->workaround_for_vbus)
|
||||
usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
|
||||
}
|
||||
|
||||
static bool usb3_wakeup_usb2_phy(struct renesas_usb3 *usb3)
|
||||
|
@ -700,8 +702,7 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
|
|||
usb3_set_mode_by_role_sw(usb3, host);
|
||||
usb3_vbus_out(usb3, a_dev);
|
||||
/* for A-Peripheral or forced B-device mode */
|
||||
if ((!host && a_dev) ||
|
||||
(usb3->workaround_for_vbus && usb3->forced_b_device))
|
||||
if ((!host && a_dev) || usb3->start_to_connect)
|
||||
usb3_connect(usb3);
|
||||
spin_unlock_irqrestore(&usb3->lock, flags);
|
||||
}
|
||||
|
@ -2432,7 +2433,11 @@ static ssize_t renesas_usb3_b_device_write(struct file *file,
|
|||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "1", 1))
|
||||
usb3->start_to_connect = false;
|
||||
if (usb3->workaround_for_vbus && usb3->forced_b_device &&
|
||||
!strncmp(buf, "2", 1))
|
||||
usb3->start_to_connect = true;
|
||||
else if (!strncmp(buf, "1", 1))
|
||||
usb3->forced_b_device = true;
|
||||
else
|
||||
usb3->forced_b_device = false;
|
||||
|
@ -2440,7 +2445,7 @@ static ssize_t renesas_usb3_b_device_write(struct file *file,
|
|||
if (usb3->workaround_for_vbus)
|
||||
usb3_disconnect(usb3);
|
||||
|
||||
/* Let this driver call usb3_connect() anyway */
|
||||
/* Let this driver call usb3_connect() if needed */
|
||||
usb3_check_id(usb3);
|
||||
|
||||
return count;
|
||||
|
|
|
@ -119,7 +119,7 @@ static void dprintk(int level, const char *fmt, ...)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
|
||||
static int s3c2410_udc_debugfs_show(struct seq_file *m, void *p)
|
||||
{
|
||||
u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;
|
||||
u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
|
||||
|
@ -168,20 +168,7 @@ static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations s3c2410_udc_debugfs_fops = {
|
||||
.open = s3c2410_udc_debugfs_fops_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(s3c2410_udc_debugfs);
|
||||
|
||||
/* io macros */
|
||||
|
||||
|
|
|
@ -402,7 +402,7 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
|
|||
return;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
|
||||
dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, req);
|
||||
|
||||
mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
|
||||
|
||||
|
|
|
@ -874,24 +874,7 @@ static int usbhsc_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsc_runtime_nop(struct device *dev)
|
||||
{
|
||||
/* Runtime PM callback shared between ->runtime_suspend()
|
||||
* and ->runtime_resume(). Simply returns success.
|
||||
*
|
||||
* This driver re-initializes all registers after
|
||||
* pm_runtime_get_sync() anyway so there is no need
|
||||
* to save and restore registers here.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops usbhsc_pm_ops = {
|
||||
.suspend = usbhsc_suspend,
|
||||
.resume = usbhsc_resume,
|
||||
.runtime_suspend = usbhsc_runtime_nop,
|
||||
.runtime_resume = usbhsc_runtime_nop,
|
||||
};
|
||||
static SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume);
|
||||
|
||||
static struct platform_driver renesas_usbhs_driver = {
|
||||
.driver = {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Vincent Pelletier
|
||||
*/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __CCID_H
|
||||
#define __CCID_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define USB_INTERFACE_CLASS_CCID 0x0b
|
||||
|
||||
struct ccid_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__le16 bcdCCID;
|
||||
__u8 bMaxSlotIndex;
|
||||
__u8 bVoltageSupport;
|
||||
__le32 dwProtocols;
|
||||
__le32 dwDefaultClock;
|
||||
__le32 dwMaximumClock;
|
||||
__u8 bNumClockSupported;
|
||||
__le32 dwDataRate;
|
||||
__le32 dwMaxDataRate;
|
||||
__u8 bNumDataRatesSupported;
|
||||
__le32 dwMaxIFSD;
|
||||
__le32 dwSynchProtocols;
|
||||
__le32 dwMechanical;
|
||||
__le32 dwFeatures;
|
||||
__le32 dwMaxCCIDMessageLength;
|
||||
__u8 bClassGetResponse;
|
||||
__u8 bClassEnvelope;
|
||||
__le16 wLcdLayout;
|
||||
__u8 bPINSupport;
|
||||
__u8 bMaxCCIDBusySlots;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif /* __CCID_H */
|
|
@ -61,6 +61,8 @@ struct usb_ep;
|
|||
* invalidated by the error may first be dequeued.
|
||||
* @context: For use by the completion callback
|
||||
* @list: For use by the gadget driver.
|
||||
* @frame_number: Reports the interval number in (micro)frame in which the
|
||||
* isochronous transfer was transmitted or received.
|
||||
* @status: Reports completion code, zero or a negative errno.
|
||||
* Normally, faults block the transfer queue from advancing until
|
||||
* the completion callback returns.
|
||||
|
@ -112,6 +114,8 @@ struct usb_request {
|
|||
void *context;
|
||||
struct list_head list;
|
||||
|
||||
unsigned frame_number; /* ISO ONLY */
|
||||
|
||||
int status;
|
||||
unsigned actual;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue