mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
usb: fixes for v4.18-rc5
With a total of 20 non-merge commits, we have accumulated quite a few fixes. These include lot's of fixes the our audio gadget interface, a build error fix for PPC64 builds for the frescale PHY driver, sleep-while-atomic fixes on the r8a66597 UDC driver, 3-stage SETUP fix for the aspeed-vhub UDC and some other misc fixes. -----BEGIN PGP SIGNATURE----- iQJRBAABCgA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAltNmBgdHGZlbGlwZS5i YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQbp6hAAzShWRGLQowpnIau+ CwrI4cm5Gknz0BRCdgoXy+sofxof7WRJYnr+N/N7GlWHq5uZPpQv+br7aiYVxbQd 7YJfGUgfTS2FGGzcaoUP9S0VC9+U8UKfCKtCkuNhGABG9BGFKuS6R/QdavukxXQU uBiAUWonxVJlSJ5xnQZQ0pt2ZeUir+qEP0NgpmFQe2tjL9bQNSVu3dCL9/5QHDuk sGV4UnFSXyy86R7su0XViqpaFozYmhbQTqh5fy7V/Qmf7oaqRRs2hnTuNFt3I1a8 QHOsKkRQS7+4nc5CGWuWmR+6yzNZBjyHagC35KGOKgMvSPfo5Bn8e+eQmBOnfb98 nU8+Bh6FMg/U3JGmb4R0/ksIFzxfxipVODTvH6dPJzYmtvZsCuzItwX3TRnhpnQ9 B9Rg3tkL2BBr2fnCR5IlLlf6i1iSq798BSAokO6vlYa7LdSDRx16HM0oRUZE6d5X Mg2Zx7SLWV94FYR7RTN05/d/NofRUXycxEgG9toWIGvlu2fTl/Bh3weRenOfAxIr 6N03aXsguexFqP/IMrsfT5Hucjl3UtpQBQxnQV5aJ7QT/np00aoUgaXTkMztNpUs jceiTNOJawIc93w/1IWHwqcql2igD+R4FCKTpa+9GQ+4Ph5gJFDnXqo1lVmGqti9 kvLP5+uiqrXVu9dpOSFHFpucexM= =w2qX -----END PGP SIGNATURE----- Merge tag 'fixes-for-v4.18-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus Felipe writes: usb: fixes for v4.18-rc5 With a total of 20 non-merge commits, we have accumulated quite a few fixes. These include lot's of fixes the our audio gadget interface, a build error fix for PPC64 builds for the frescale PHY driver, sleep-while-atomic fixes on the r8a66597 UDC driver, 3-stage SETUP fix for the aspeed-vhub UDC and some other misc fixes.
This commit is contained in:
commit
2c3806c482
@ -16,7 +16,8 @@ A child node must exist to represent the core DWC3 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc3.txt.
|
||||
|
||||
Phy documentation is provided in the following places:
|
||||
Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt
|
||||
Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt - USB2.0 PHY
|
||||
Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt - Type-C PHY
|
||||
|
||||
Example device nodes:
|
||||
|
||||
|
@ -3430,7 +3430,7 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_in[idx];
|
||||
/* Proceed only unmasked ISOC EPs */
|
||||
if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
|
||||
if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
|
||||
continue;
|
||||
|
||||
epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx));
|
||||
@ -3476,7 +3476,7 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_out[idx];
|
||||
/* Proceed only unmasked ISOC EPs */
|
||||
if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
|
||||
if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
|
||||
continue;
|
||||
|
||||
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
|
||||
@ -3650,7 +3650,7 @@ irq_retry:
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_out[idx];
|
||||
/* Proceed only unmasked ISOC EPs */
|
||||
if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
|
||||
if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
|
||||
continue;
|
||||
|
||||
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
|
||||
|
@ -2665,34 +2665,35 @@ static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
|
||||
|
||||
#define DWC2_USB_DMA_ALIGN 4
|
||||
|
||||
struct dma_aligned_buffer {
|
||||
void *kmalloc_ptr;
|
||||
void *old_xfer_buffer;
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
static void dwc2_free_dma_aligned_buffer(struct urb *urb)
|
||||
{
|
||||
struct dma_aligned_buffer *temp;
|
||||
void *stored_xfer_buffer;
|
||||
size_t length;
|
||||
|
||||
if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
|
||||
return;
|
||||
|
||||
temp = container_of(urb->transfer_buffer,
|
||||
struct dma_aligned_buffer, data);
|
||||
/* Restore urb->transfer_buffer from the end of the allocated area */
|
||||
memcpy(&stored_xfer_buffer, urb->transfer_buffer +
|
||||
urb->transfer_buffer_length, sizeof(urb->transfer_buffer));
|
||||
|
||||
if (usb_urb_dir_in(urb))
|
||||
memcpy(temp->old_xfer_buffer, temp->data,
|
||||
urb->transfer_buffer_length);
|
||||
urb->transfer_buffer = temp->old_xfer_buffer;
|
||||
kfree(temp->kmalloc_ptr);
|
||||
if (usb_urb_dir_in(urb)) {
|
||||
if (usb_pipeisoc(urb->pipe))
|
||||
length = urb->transfer_buffer_length;
|
||||
else
|
||||
length = urb->actual_length;
|
||||
|
||||
memcpy(stored_xfer_buffer, urb->transfer_buffer, length);
|
||||
}
|
||||
kfree(urb->transfer_buffer);
|
||||
urb->transfer_buffer = stored_xfer_buffer;
|
||||
|
||||
urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
|
||||
}
|
||||
|
||||
static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
|
||||
{
|
||||
struct dma_aligned_buffer *temp, *kmalloc_ptr;
|
||||
void *kmalloc_ptr;
|
||||
size_t kmalloc_size;
|
||||
|
||||
if (urb->num_sgs || urb->sg ||
|
||||
@ -2700,22 +2701,29 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
|
||||
!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
|
||||
return 0;
|
||||
|
||||
/* Allocate a buffer with enough padding for alignment */
|
||||
/*
|
||||
* Allocate a buffer with enough padding for original transfer_buffer
|
||||
* pointer. This allocation is guaranteed to be aligned properly for
|
||||
* DMA
|
||||
*/
|
||||
kmalloc_size = urb->transfer_buffer_length +
|
||||
sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
|
||||
sizeof(urb->transfer_buffer);
|
||||
|
||||
kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
|
||||
if (!kmalloc_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Position our struct dma_aligned_buffer such that data is aligned */
|
||||
temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
|
||||
temp->kmalloc_ptr = kmalloc_ptr;
|
||||
temp->old_xfer_buffer = urb->transfer_buffer;
|
||||
/*
|
||||
* Position value of original urb->transfer_buffer pointer to the end
|
||||
* of allocation for later referencing
|
||||
*/
|
||||
memcpy(kmalloc_ptr + urb->transfer_buffer_length,
|
||||
&urb->transfer_buffer, sizeof(urb->transfer_buffer));
|
||||
|
||||
if (usb_urb_dir_out(urb))
|
||||
memcpy(temp->data, urb->transfer_buffer,
|
||||
memcpy(kmalloc_ptr, urb->transfer_buffer,
|
||||
urb->transfer_buffer_length);
|
||||
urb->transfer_buffer = temp->data;
|
||||
urb->transfer_buffer = kmalloc_ptr;
|
||||
|
||||
urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
|
||||
|
||||
|
@ -1231,7 +1231,10 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
|
||||
* 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.
|
||||
* interrupt (another NAK), which we'd retry. Note that we do not
|
||||
* delay retries for IN parts of control requests, as those are expected
|
||||
* to complete fairly quickly, and if we delay them we risk confusing
|
||||
* the device and cause it issue STALL.
|
||||
*
|
||||
* Note that in DMA mode software only gets involved to re-send NAKed
|
||||
* transfers for split transactions, so we only need to apply this
|
||||
@ -1244,7 +1247,9 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
|
||||
qtd->error_count = 0;
|
||||
qtd->complete_split = 0;
|
||||
qtd->num_naks++;
|
||||
qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY;
|
||||
qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY &&
|
||||
!(chan->ep_type == USB_ENDPOINT_XFER_CONTROL &&
|
||||
chan->ep_is_in);
|
||||
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
|
||||
goto handle_nak_done;
|
||||
}
|
||||
|
@ -973,15 +973,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
|
||||
req->request.length && req->request.zero) {
|
||||
u32 maxpacket;
|
||||
|
||||
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
|
||||
&req->request, dep->number);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
maxpacket = dep->endpoint.maxpacket;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
|
||||
req->request.length,
|
||||
|
@ -1819,7 +1819,6 @@ unknown:
|
||||
if (cdev->use_os_string && cdev->os_desc_config &&
|
||||
(ctrl->bRequestType & USB_TYPE_VENDOR) &&
|
||||
ctrl->bRequest == cdev->b_vendor_code) {
|
||||
struct usb_request *req;
|
||||
struct usb_configuration *os_desc_cfg;
|
||||
u8 *buf;
|
||||
int interface;
|
||||
|
@ -438,14 +438,14 @@ static struct usb_descriptor_header *hs_audio_desc[] = {
|
||||
};
|
||||
|
||||
struct cntrl_cur_lay3 {
|
||||
__u32 dCUR;
|
||||
__le32 dCUR;
|
||||
};
|
||||
|
||||
struct cntrl_range_lay3 {
|
||||
__u16 wNumSubRanges;
|
||||
__u32 dMIN;
|
||||
__u32 dMAX;
|
||||
__u32 dRES;
|
||||
__le16 wNumSubRanges;
|
||||
__le32 dMIN;
|
||||
__le32 dMAX;
|
||||
__le32 dRES;
|
||||
} __packed;
|
||||
|
||||
static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
|
||||
@ -559,13 +559,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
|
||||
if (!agdev->out_ep) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
|
||||
if (!agdev->in_ep) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
agdev->in_ep_maxpsize = max_t(u16,
|
||||
@ -703,9 +703,9 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
memset(&c, 0, sizeof(struct cntrl_cur_lay3));
|
||||
|
||||
if (entity_id == USB_IN_CLK_ID)
|
||||
c.dCUR = p_srate;
|
||||
c.dCUR = cpu_to_le32(p_srate);
|
||||
else if (entity_id == USB_OUT_CLK_ID)
|
||||
c.dCUR = c_srate;
|
||||
c.dCUR = cpu_to_le32(c_srate);
|
||||
|
||||
value = min_t(unsigned, w_length, sizeof c);
|
||||
memcpy(req->buf, &c, value);
|
||||
@ -742,15 +742,15 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
|
||||
if (entity_id == USB_IN_CLK_ID)
|
||||
r.dMIN = p_srate;
|
||||
r.dMIN = cpu_to_le32(p_srate);
|
||||
else if (entity_id == USB_OUT_CLK_ID)
|
||||
r.dMIN = c_srate;
|
||||
r.dMIN = cpu_to_le32(c_srate);
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r.dMAX = r.dMIN;
|
||||
r.dRES = 0;
|
||||
r.wNumSubRanges = 1;
|
||||
r.wNumSubRanges = cpu_to_le16(1);
|
||||
|
||||
value = min_t(unsigned, w_length, sizeof r);
|
||||
memcpy(req->buf, &r, value);
|
||||
|
@ -32,9 +32,6 @@ struct uac_req {
|
||||
struct uac_rtd_params {
|
||||
struct snd_uac_chip *uac; /* parent chip */
|
||||
bool ep_enabled; /* if the ep is enabled */
|
||||
/* Size of the ring buffer */
|
||||
size_t dma_bytes;
|
||||
unsigned char *dma_area;
|
||||
|
||||
struct snd_pcm_substream *ss;
|
||||
|
||||
@ -43,8 +40,6 @@ struct uac_rtd_params {
|
||||
|
||||
void *rbuf;
|
||||
|
||||
size_t period_size;
|
||||
|
||||
unsigned max_psize; /* MaxPacketSize of endpoint */
|
||||
struct uac_req *ureq;
|
||||
|
||||
@ -84,12 +79,12 @@ static const struct snd_pcm_hardware uac_pcm_hardware = {
|
||||
static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
unsigned pending;
|
||||
unsigned long flags;
|
||||
unsigned long flags, flags2;
|
||||
unsigned int hw_ptr;
|
||||
bool update_alsa = false;
|
||||
int status = req->status;
|
||||
struct uac_req *ur = req->context;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct uac_rtd_params *prm = ur->pp;
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
|
||||
@ -111,6 +106,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
if (!substream)
|
||||
goto exit;
|
||||
|
||||
snd_pcm_stream_lock_irqsave(substream, flags2);
|
||||
|
||||
runtime = substream->runtime;
|
||||
if (!runtime || !snd_pcm_running(substream)) {
|
||||
snd_pcm_stream_unlock_irqrestore(substream, flags2);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
@ -137,43 +140,46 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
req->actual = req->length;
|
||||
}
|
||||
|
||||
pending = prm->hw_ptr % prm->period_size;
|
||||
pending += req->actual;
|
||||
if (pending >= prm->period_size)
|
||||
update_alsa = true;
|
||||
|
||||
hw_ptr = prm->hw_ptr;
|
||||
prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
|
||||
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
/* Pack USB load in ALSA ring buffer */
|
||||
pending = prm->dma_bytes - hw_ptr;
|
||||
pending = runtime->dma_bytes - hw_ptr;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (unlikely(pending < req->actual)) {
|
||||
memcpy(req->buf, prm->dma_area + hw_ptr, pending);
|
||||
memcpy(req->buf + pending, prm->dma_area,
|
||||
memcpy(req->buf, runtime->dma_area + hw_ptr, pending);
|
||||
memcpy(req->buf + pending, runtime->dma_area,
|
||||
req->actual - pending);
|
||||
} else {
|
||||
memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
|
||||
memcpy(req->buf, runtime->dma_area + hw_ptr,
|
||||
req->actual);
|
||||
}
|
||||
} else {
|
||||
if (unlikely(pending < req->actual)) {
|
||||
memcpy(prm->dma_area + hw_ptr, req->buf, pending);
|
||||
memcpy(prm->dma_area, req->buf + pending,
|
||||
memcpy(runtime->dma_area + hw_ptr, req->buf, pending);
|
||||
memcpy(runtime->dma_area, req->buf + pending,
|
||||
req->actual - pending);
|
||||
} else {
|
||||
memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
|
||||
memcpy(runtime->dma_area + hw_ptr, req->buf,
|
||||
req->actual);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
/* update hw_ptr after data is copied to memory */
|
||||
prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;
|
||||
hw_ptr = prm->hw_ptr;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
snd_pcm_stream_unlock_irqrestore(substream, flags2);
|
||||
|
||||
if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
|
||||
exit:
|
||||
if (usb_ep_queue(ep, req, GFP_ATOMIC))
|
||||
dev_err(uac->card->dev, "%d Error!\n", __LINE__);
|
||||
|
||||
if (update_alsa)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
@ -236,40 +242,12 @@ static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static int uac_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct uac_rtd_params *prm;
|
||||
int err;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
return snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err >= 0) {
|
||||
prm->dma_bytes = substream->runtime->dma_bytes;
|
||||
prm->dma_area = substream->runtime->dma_area;
|
||||
prm->period_size = params_period_bytes(hw_params);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uac_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct uac_rtd_params *prm;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
prm->dma_area = NULL;
|
||||
prm->dma_bytes = 0;
|
||||
prm->period_size = 0;
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
@ -595,15 +573,15 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
|
||||
strcpy(pcm->name, pcm_name);
|
||||
strlcpy(pcm->name, pcm_name, sizeof(pcm->name));
|
||||
pcm->private_data = uac;
|
||||
uac->pcm = pcm;
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
|
||||
|
||||
strcpy(card->driver, card_name);
|
||||
strcpy(card->shortname, card_name);
|
||||
strlcpy(card->driver, card_name, sizeof(card->driver));
|
||||
strlcpy(card->shortname, card_name, sizeof(card->shortname));
|
||||
sprintf(card->longname, "%s %i", card_name, card->dev->id);
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
|
@ -108,6 +108,13 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
|
||||
/* Check our state, cancel pending requests if needed */
|
||||
if (ep->ep0.state != ep0_state_token) {
|
||||
EPDBG(ep, "wrong state\n");
|
||||
ast_vhub_nuke(ep, -EIO);
|
||||
|
||||
/*
|
||||
* Accept the packet regardless, this seems to happen
|
||||
* when stalling a SETUP packet that has an OUT data
|
||||
* phase.
|
||||
*/
|
||||
ast_vhub_nuke(ep, 0);
|
||||
goto stall;
|
||||
}
|
||||
@ -212,6 +219,8 @@ static void ast_vhub_ep0_do_send(struct ast_vhub_ep *ep,
|
||||
if (chunk && req->req.buf)
|
||||
memcpy(ep->buf, req->req.buf + req->req.actual, chunk);
|
||||
|
||||
vhub_dma_workaround(ep->buf);
|
||||
|
||||
/* Remember chunk size and trigger send */
|
||||
reg = VHUB_EP0_SET_TX_LEN(chunk);
|
||||
writel(reg, ep->ep0.ctlstat);
|
||||
@ -224,7 +233,7 @@ static void ast_vhub_ep0_rx_prime(struct ast_vhub_ep *ep)
|
||||
EPVDBG(ep, "rx prime\n");
|
||||
|
||||
/* Prime endpoint for receiving data */
|
||||
writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat + AST_VHUB_EP0_CTRL);
|
||||
writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
|
||||
}
|
||||
|
||||
static void ast_vhub_ep0_do_receive(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
|
||||
|
@ -66,11 +66,16 @@ static void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req)
|
||||
if (!req->req.dma) {
|
||||
|
||||
/* For IN transfers, copy data over first */
|
||||
if (ep->epn.is_in)
|
||||
if (ep->epn.is_in) {
|
||||
memcpy(ep->buf, req->req.buf + act, chunk);
|
||||
vhub_dma_workaround(ep->buf);
|
||||
}
|
||||
writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
|
||||
} else
|
||||
} else {
|
||||
if (ep->epn.is_in)
|
||||
vhub_dma_workaround(req->req.buf);
|
||||
writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
|
||||
}
|
||||
|
||||
/* Start DMA */
|
||||
req->active = true;
|
||||
@ -161,6 +166,7 @@ static inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep)
|
||||
static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
|
||||
struct ast_vhub_req *req)
|
||||
{
|
||||
struct ast_vhub_desc *desc = NULL;
|
||||
unsigned int act = req->act_count;
|
||||
unsigned int len = req->req.length;
|
||||
unsigned int chunk;
|
||||
@ -177,7 +183,6 @@ static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
|
||||
|
||||
/* While we can create descriptors */
|
||||
while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) {
|
||||
struct ast_vhub_desc *desc;
|
||||
unsigned int d_num;
|
||||
|
||||
/* Grab next free descriptor */
|
||||
@ -227,6 +232,9 @@ static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
|
||||
req->act_count = act = act + chunk;
|
||||
}
|
||||
|
||||
if (likely(desc))
|
||||
vhub_dma_workaround(desc);
|
||||
|
||||
/* Tell HW about new descriptors */
|
||||
writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next),
|
||||
ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
|
||||
|
@ -462,6 +462,39 @@ enum std_req_rc {
|
||||
#define DDBG(d, fmt, ...) do { } while(0)
|
||||
#endif
|
||||
|
||||
static inline void vhub_dma_workaround(void *addr)
|
||||
{
|
||||
/*
|
||||
* This works around a confirmed HW issue with the Aspeed chip.
|
||||
*
|
||||
* The core uses a different bus to memory than the AHB going to
|
||||
* the USB device controller. Due to the latter having a higher
|
||||
* priority than the core for arbitration on that bus, it's
|
||||
* possible for an MMIO to the device, followed by a DMA by the
|
||||
* device from memory to all be performed and services before
|
||||
* a previous store to memory gets completed.
|
||||
*
|
||||
* This the following scenario can happen:
|
||||
*
|
||||
* - Driver writes to a DMA descriptor (Mbus)
|
||||
* - Driver writes to the MMIO register to start the DMA (AHB)
|
||||
* - The gadget sees the second write and sends a read of the
|
||||
* descriptor to the memory controller (Mbus)
|
||||
* - The gadget hits memory before the descriptor write
|
||||
* causing it to read an obsolete value.
|
||||
*
|
||||
* Thankfully the problem is limited to the USB gadget device, other
|
||||
* masters in the SoC all have a lower priority than the core, thus
|
||||
* ensuring that the store by the core arrives first.
|
||||
*
|
||||
* The workaround consists of using a dummy read of the memory before
|
||||
* doing the MMIO writes. This will ensure that the previous writes
|
||||
* have been "pushed out".
|
||||
*/
|
||||
mb();
|
||||
(void)__raw_readl((void __iomem *)addr);
|
||||
}
|
||||
|
||||
/* core.c */
|
||||
void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
|
||||
int status);
|
||||
|
@ -832,11 +832,11 @@ static void init_controller(struct r8a66597 *r8a66597)
|
||||
|
||||
r8a66597_bset(r8a66597, XCKE, SYSCFG0);
|
||||
|
||||
msleep(3);
|
||||
mdelay(3);
|
||||
|
||||
r8a66597_bset(r8a66597, PLLC, SYSCFG0);
|
||||
|
||||
msleep(1);
|
||||
mdelay(1);
|
||||
|
||||
r8a66597_bset(r8a66597, SCKE, SYSCFG0);
|
||||
|
||||
@ -1190,7 +1190,7 @@ __acquires(r8a66597->lock)
|
||||
r8a66597->ep0_req->length = 2;
|
||||
/* AV: what happens if we get called again before that gets through? */
|
||||
spin_unlock(&r8a66597->lock);
|
||||
r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_KERNEL);
|
||||
r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_ATOMIC);
|
||||
spin_lock(&r8a66597->lock);
|
||||
}
|
||||
|
||||
|
@ -861,6 +861,7 @@ int usb_otg_start(struct platform_device *pdev)
|
||||
if (pdata->init && pdata->init(pdev) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
if (pdata->big_endian_mmio) {
|
||||
_fsl_readl = _fsl_readl_be;
|
||||
_fsl_writel = _fsl_writel_be;
|
||||
@ -868,6 +869,7 @@ int usb_otg_start(struct platform_device *pdev)
|
||||
_fsl_readl = _fsl_readl_le;
|
||||
_fsl_writel = _fsl_writel_le;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* request irq */
|
||||
p_otg->irq = platform_get_irq(pdev, 0);
|
||||
@ -958,7 +960,7 @@ int usb_otg_start(struct platform_device *pdev)
|
||||
/*
|
||||
* state file in sysfs
|
||||
*/
|
||||
static int show_fsl_usb2_otg_state(struct device *dev,
|
||||
static ssize_t show_fsl_usb2_otg_state(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct otg_fsm *fsm = &fsl_otg_dev->fsm;
|
||||
|
@ -44,12 +44,25 @@
|
||||
|
||||
/******************** Little Endian Handling ********************************/
|
||||
|
||||
#define cpu_to_le16(x) htole16(x)
|
||||
#define cpu_to_le32(x) htole32(x)
|
||||
/*
|
||||
* cpu_to_le16/32 are used when initializing structures, a context where a
|
||||
* function call is not allowed. To solve this, we code cpu_to_le16/32 in a way
|
||||
* that allows them to be used when initializing structures.
|
||||
*/
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define cpu_to_le16(x) (x)
|
||||
#define cpu_to_le32(x) (x)
|
||||
#else
|
||||
#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
|
||||
#define cpu_to_le32(x) \
|
||||
((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
|
||||
(((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
|
||||
#endif
|
||||
|
||||
#define le32_to_cpu(x) le32toh(x)
|
||||
#define le16_to_cpu(x) le16toh(x)
|
||||
|
||||
|
||||
/******************** Messages and Errors ***********************************/
|
||||
|
||||
static const char argv0[] = "ffs-test";
|
||||
|
Loading…
Reference in New Issue
Block a user