usb: changes for v4.12
With 51 non-merge commits, this is one of the smallest USB Gadget pull requests. Apart from your expected set of non-critical fixes, and other miscellaneous items, we have most of the changes in dwc3 (52.5%) with all other UDCs following with 34.8%. As for the actual changes, the most important of them are all the recent changes to reduce memory footprint of dwc3, bare minimum dual-role support on dwc3 and reworked endpoint count and initialization routines. -----BEGIN PGP SIGNATURE----- iQJRBAABCAA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAljsjRwdHGZlbGlwZS5i YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQaPZA/9H6GgNlvdGWPweJ0r g86iBmio/Qn334SOCAJDrdw4ULSTSQR8nQHGWuETE2pQ2uYA/dbBWvjkx8Pujs+u Ye2ig/iYchvHhY7cIWBG6A/iwvBClZF+yTv4ruh3i99BOGeAtGSyFS3HftQZLkGs 360CritNs+fSJsU6jnCE2+808evbkRiMaEfiBpiqXF2wvjMonWUMUlHaTBTHzDnk k2/NJSzDINr4HaALalJxVpEetC500LDkQ531O5s7V6NIareCIBOh2li9xZJur38h 728BbCIiwPrdfLI2gGJLUOJq5vf5fJPT6DMJDN4hsu7gXcl2GiIc3nqlcxBB8P9R kurWgjVd8aIGS7qqZzfDBH7I5UgFbJZbtVXy6UhSRILF+hzAb/l95pZUOyTwUnbj ew3ALD2H7XolJT9jLG/q1CaQQMjudVnhLF2s1Ota8dsBeOShH6yfS6hGGQ8L1j8x kXe3dl4HEj9s7gT0S+tVN+kt6bLGZ013CjCbuZgG9JLRkswyn780KTgL4pbjHiAE KNGlCGUPRh3AiDLSbQ4IRU/J/0MVFs51k8aPn6PWpUqU8nc1QNEfczqrlAGPvkqU nE77OktC1UCTrId7F8kllTEglOrR4CVXuT2N7rYzwgM2jmfHLzb0DaJ+a/ZdsUfI DjuY8jNbuGnvZRRiVQU5ynva5BU= =wYOI -----END PGP SIGNATURE----- Merge tag 'usb-for-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: usb: changes for v4.12 With 51 non-merge commits, this is one of the smallest USB Gadget pull requests. Apart from your expected set of non-critical fixes, and other miscellaneous items, we have most of the changes in dwc3 (52.5%) with all other UDCs following with 34.8%. As for the actual changes, the most important of them are all the recent changes to reduce memory footprint of dwc3, bare minimum dual-role support on dwc3 and reworked endpoint count and initialization routines.
This commit is contained in:
commit
ba7756d082
|
@ -0,0 +1,15 @@
|
|||
What: /sys/devices/platform/<renesas_usb3's name>/role
|
||||
Date: March 2017
|
||||
KernelVersion: 4.13
|
||||
Contact: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
Description:
|
||||
This file can be read and write.
|
||||
The file can show/change the drd mode of usb.
|
||||
|
||||
Write the following string to change the mode:
|
||||
"host" - switching mode from peripheral to host.
|
||||
"peripheral" - switching mode from host to peripheral.
|
||||
|
||||
Read the file, then it shows the following strings:
|
||||
"host" - The mode is host now.
|
||||
"peripheral" - The mode is peripheral now.
|
|
@ -31,6 +31,13 @@
|
|||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/otg-fsm.h>
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
|
||||
__func__, ## args)
|
||||
#else
|
||||
#define VDBG(stuff...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* Change USB protocol when there is a protocol change */
|
||||
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
|
||||
{
|
||||
|
|
|
@ -423,6 +423,10 @@ enum dwc2_ep0_state {
|
|||
* needed.
|
||||
* 0 - No (default)
|
||||
* 1 - Yes
|
||||
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
|
||||
* register.
|
||||
* 0 - Deactivate the transceiver (default)
|
||||
* 1 - Activate the transceiver
|
||||
* @g_dma: Enables gadget dma usage (default: autodetect).
|
||||
* @g_dma_desc: Enables gadget descriptor DMA (default: autodetect).
|
||||
* @g_rx_fifo_size: The periodic rx fifo size for the device, in
|
||||
|
@ -477,6 +481,7 @@ struct dwc2_core_params {
|
|||
bool uframe_sched;
|
||||
bool external_id_pin_ctl;
|
||||
bool hibernation;
|
||||
bool activate_stm_fs_transceiver;
|
||||
u16 max_packet_count;
|
||||
u32 max_transfer_size;
|
||||
u32 ahbcfg;
|
||||
|
|
|
@ -121,7 +121,7 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
|
|||
|
||||
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg, i2cctl;
|
||||
u32 usbcfg, ggpio, i2cctl;
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
|
@ -145,6 +145,19 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
|||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (hsotg->params.activate_stm_fs_transceiver) {
|
||||
ggpio = dwc2_readl(hsotg->regs + GGPIO);
|
||||
if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
|
||||
dev_dbg(hsotg->dev, "Activating transceiver\n");
|
||||
/*
|
||||
* STM32F4x9 uses the GGPIO register as general
|
||||
* core configuration register.
|
||||
*/
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
|
||||
dwc2_writel(ggpio, hsotg->regs + GGPIO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3264,6 +3277,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
|
|||
dwc2_core_init(hsotg, false);
|
||||
dwc2_enable_global_interrupts(hsotg);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dwc2_hsotg_disconnect(hsotg);
|
||||
dwc2_hsotg_core_init_disconnected(hsotg, false);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
dwc2_hsotg_core_connect(hsotg);
|
||||
|
|
|
@ -225,6 +225,8 @@
|
|||
|
||||
#define GPVNDCTL HSOTG_REG(0x0034)
|
||||
#define GGPIO HSOTG_REG(0x0038)
|
||||
#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16)
|
||||
|
||||
#define GUID HSOTG_REG(0x003c)
|
||||
#define GSNPSID HSOTG_REG(0x0040)
|
||||
#define GHWCFG1 HSOTG_REG(0x0044)
|
||||
|
|
|
@ -120,6 +120,22 @@ static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
|
|||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
|
||||
}
|
||||
|
||||
static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
|
||||
p->speed = DWC2_SPEED_PARAM_FULL;
|
||||
p->host_rx_fifo_size = 128;
|
||||
p->host_nperio_tx_fifo_size = 96;
|
||||
p->host_perio_tx_fifo_size = 96;
|
||||
p->max_packet_count = 256;
|
||||
p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
|
||||
p->i2c_enable = false;
|
||||
p->uframe_sched = false;
|
||||
p->activate_stm_fs_transceiver = true;
|
||||
}
|
||||
|
||||
const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
|
||||
{ .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
|
||||
|
@ -133,6 +149,9 @@ const struct of_device_id dwc2_of_match_table[] = {
|
|||
{ .compatible = "amlogic,meson-gxbb-usb",
|
||||
.data = dwc2_set_amlogic_params },
|
||||
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
|
||||
{ .compatible = "st,stm32f4x9-fsotg",
|
||||
.data = dwc2_set_stm32f4x9_fsotg_params },
|
||||
{ .compatible = "st,stm32f4x9-hsotg" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
|
||||
|
|
|
@ -214,20 +214,11 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
|
|||
hsotg->reset = devm_reset_control_get_optional(hsotg->dev, "dwc2");
|
||||
if (IS_ERR(hsotg->reset)) {
|
||||
ret = PTR_ERR(hsotg->reset);
|
||||
switch (ret) {
|
||||
case -ENOENT:
|
||||
case -ENOTSUPP:
|
||||
hsotg->reset = NULL;
|
||||
break;
|
||||
default:
|
||||
dev_err(hsotg->dev, "error getting reset control %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
dev_err(hsotg->dev, "error getting reset control %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hsotg->reset)
|
||||
reset_control_deassert(hsotg->reset);
|
||||
reset_control_deassert(hsotg->reset);
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
@ -326,8 +317,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
|||
if (hsotg->ll_hw_enabled)
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
|
||||
if (hsotg->reset)
|
||||
reset_control_assert(hsotg->reset);
|
||||
reset_control_assert(hsotg->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ config USB_DWC3_GADGET
|
|||
config USB_DWC3_DUAL_ROLE
|
||||
bool "Dual Role mode"
|
||||
depends on ((USB=y || USB=USB_DWC3) && (USB_GADGET=y || USB_GADGET=USB_DWC3))
|
||||
depends on (EXTCON=y || EXTCON=USB_DWC3)
|
||||
help
|
||||
This is the default mode of working of DWC3 controller where
|
||||
both host and gadget features are enabled.
|
||||
|
|
|
@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
|
|||
dwc3-y += gadget.o ep0.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
|
||||
dwc3-y += drd.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC3_ULPI),)
|
||||
dwc3-y += ulpi.o
|
||||
endif
|
||||
|
|
|
@ -100,7 +100,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
|
||||
static int dwc3_event_buffers_setup(struct dwc3 *dwc);
|
||||
|
||||
static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
|
@ -110,6 +113,69 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
|||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
}
|
||||
|
||||
static void __dwc3_set_mode(struct work_struct *work)
|
||||
{
|
||||
struct dwc3 *dwc = work_to_dwc(work);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!dwc->desired_dr_role)
|
||||
return;
|
||||
|
||||
if (dwc->desired_dr_role == dwc->current_dr_role)
|
||||
return;
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return;
|
||||
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
dwc3_gadget_exit(dwc);
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_set_prtcap(dwc, dwc->desired_dr_role);
|
||||
|
||||
dwc->current_dr_role = dwc->desired_dr_role;
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
switch (dwc->desired_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to initialize host\n");
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to initialize peripheral\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->desired_dr_role = mode;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
queue_work(system_power_efficient_wq, &dwc->drd_work);
|
||||
}
|
||||
|
||||
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
@ -397,8 +463,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
|
|||
{
|
||||
struct dwc3_hwparams *parms = &dwc->hwparams;
|
||||
|
||||
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
|
||||
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
|
||||
dwc->num_eps = DWC3_NUM_EPS(parms);
|
||||
}
|
||||
|
||||
static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
||||
|
@ -431,6 +496,12 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
|||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
|
||||
/*
|
||||
* Make sure UX_EXIT_PX is cleared as that causes issues with some
|
||||
* PHYs. Also, this bit is not supposed to be used in normal operation.
|
||||
*/
|
||||
reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
|
||||
|
||||
/*
|
||||
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
|
||||
* to '0' during coreConsultant configuration. So default value
|
||||
|
@ -714,21 +785,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
goto err4;
|
||||
}
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
break;
|
||||
default:
|
||||
dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* ENDXFER polling is available on version 3.10a and later of
|
||||
* the DWC_usb3 controller. It is NOT available in the
|
||||
|
@ -846,6 +902,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
|||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
|
@ -854,6 +911,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
|||
}
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
|
@ -862,17 +920,11 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
|||
}
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
ret = dwc3_host_init(dwc);
|
||||
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
|
||||
ret = dwc3_drd_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
dev_err(dev, "failed to initialize dual-role\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
@ -894,8 +946,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
|||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_host_exit(dwc);
|
||||
dwc3_gadget_exit(dwc);
|
||||
dwc3_drd_exit(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
|
|
|
@ -23,10 +23,12 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
@ -39,9 +41,8 @@
|
|||
|
||||
/* Global constants */
|
||||
#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
|
||||
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
|
||||
#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_EP0_SETUP_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
|
@ -66,7 +67,7 @@
|
|||
#define DWC3_DEVICE_EVENT_OVERFLOW 11
|
||||
|
||||
#define DWC3_GEVNTCOUNT_MASK 0xfffc
|
||||
#define DWC3_GEVNTCOUNT_EHB (1 << 31)
|
||||
#define DWC3_GEVNTCOUNT_EHB BIT(31)
|
||||
#define DWC3_GSNPSID_MASK 0xffff0000
|
||||
#define DWC3_GSNPSREV_MASK 0xffff
|
||||
|
||||
|
@ -116,20 +117,20 @@
|
|||
#define DWC3_VER_NUMBER 0xc1a0
|
||||
#define DWC3_VER_TYPE 0xc1a4
|
||||
|
||||
#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
|
||||
#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04))
|
||||
#define DWC3_GUSB2PHYCFG(n) (0xc200 + ((n) * 0x04))
|
||||
#define DWC3_GUSB2I2CCTL(n) (0xc240 + ((n) * 0x04))
|
||||
|
||||
#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04))
|
||||
#define DWC3_GUSB2PHYACC(n) (0xc280 + ((n) * 0x04))
|
||||
|
||||
#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04))
|
||||
#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + ((n) * 0x04))
|
||||
|
||||
#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04))
|
||||
#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04))
|
||||
#define DWC3_GTXFIFOSIZ(n) (0xc300 + ((n) * 0x04))
|
||||
#define DWC3_GRXFIFOSIZ(n) (0xc380 + ((n) * 0x04))
|
||||
|
||||
#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10))
|
||||
#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10))
|
||||
#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10))
|
||||
#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
|
||||
#define DWC3_GEVNTADRLO(n) (0xc400 + ((n) * 0x10))
|
||||
#define DWC3_GEVNTADRHI(n) (0xc404 + ((n) * 0x10))
|
||||
#define DWC3_GEVNTSIZ(n) (0xc408 + ((n) * 0x10))
|
||||
#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
|
||||
|
||||
#define DWC3_GHWPARAMS8 0xc600
|
||||
#define DWC3_GFLADJ 0xc630
|
||||
|
@ -143,13 +144,13 @@
|
|||
#define DWC3_DGCMD 0xc714
|
||||
#define DWC3_DALEPENA 0xc720
|
||||
|
||||
#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10))
|
||||
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
|
||||
#define DWC3_DEPCMDPAR2 0x00
|
||||
#define DWC3_DEPCMDPAR1 0x04
|
||||
#define DWC3_DEPCMDPAR0 0x08
|
||||
#define DWC3_DEPCMD 0x0c
|
||||
|
||||
#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
|
||||
#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
|
||||
|
||||
/* OTG Registers */
|
||||
#define DWC3_OCFG 0xcc00
|
||||
|
@ -176,11 +177,11 @@
|
|||
/* Global RX Threshold Configuration Register */
|
||||
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
|
||||
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
|
||||
#define DWC3_GRXTHRCFG_PKTCNTSEL (1 << 29)
|
||||
#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
|
||||
|
||||
/* Global Configuration Register */
|
||||
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
|
||||
#define DWC3_GCTL_U2RSTECN (1 << 16)
|
||||
#define DWC3_GCTL_U2RSTECN BIT(16)
|
||||
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
|
||||
#define DWC3_GCTL_CLK_BUS (0)
|
||||
#define DWC3_GCTL_CLK_PIPE (1)
|
||||
|
@ -193,24 +194,24 @@
|
|||
#define DWC3_GCTL_PRTCAP_DEVICE 2
|
||||
#define DWC3_GCTL_PRTCAP_OTG 3
|
||||
|
||||
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
|
||||
#define DWC3_GCTL_SOFITPSYNC (1 << 10)
|
||||
#define DWC3_GCTL_CORESOFTRESET BIT(11)
|
||||
#define DWC3_GCTL_SOFITPSYNC BIT(10)
|
||||
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
|
||||
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
|
||||
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
|
||||
#define DWC3_GCTL_U2EXIT_LFPS (1 << 2)
|
||||
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
|
||||
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
|
||||
#define DWC3_GCTL_DISSCRAMBLE BIT(3)
|
||||
#define DWC3_GCTL_U2EXIT_LFPS BIT(2)
|
||||
#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
|
||||
#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
|
||||
|
||||
/* Global User Control 1 Register */
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
|
||||
/* Global USB2 PHY Configuration Register */
|
||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
|
||||
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
|
||||
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
|
||||
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
|
||||
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
|
||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
|
||||
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
|
||||
#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
|
||||
#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
|
||||
#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
|
||||
#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
|
||||
#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
|
||||
#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
|
||||
|
@ -221,25 +222,26 @@
|
|||
#define UTMI_PHYIF_8_BIT 0
|
||||
|
||||
/* Global USB2 PHY Vendor Control Register */
|
||||
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
|
||||
#define DWC3_GUSB2PHYACC_BUSY (1 << 23)
|
||||
#define DWC3_GUSB2PHYACC_WRITE (1 << 22)
|
||||
#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25)
|
||||
#define DWC3_GUSB2PHYACC_BUSY BIT(23)
|
||||
#define DWC3_GUSB2PHYACC_WRITE BIT(22)
|
||||
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
|
||||
#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
|
||||
#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
|
||||
|
||||
/* Global USB3 PIPE Control Register */
|
||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
||||
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
|
||||
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
|
||||
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
|
||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST BIT(31)
|
||||
#define DWC3_GUSB3PIPECTL_U2SSINP3OK BIT(29)
|
||||
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28)
|
||||
#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27)
|
||||
#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(24)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1)
|
||||
#define DWC3_GUSB3PIPECTL_DEPOCHANGE (1 << 18)
|
||||
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
|
||||
#define DWC3_GUSB3PIPECTL_LFPSFILT (1 << 9)
|
||||
#define DWC3_GUSB3PIPECTL_RX_DETOPOLL (1 << 8)
|
||||
#define DWC3_GUSB3PIPECTL_DEPOCHANGE BIT(18)
|
||||
#define DWC3_GUSB3PIPECTL_SUSPHY BIT(17)
|
||||
#define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9)
|
||||
#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8)
|
||||
#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
|
||||
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
|
||||
|
||||
|
@ -248,7 +250,7 @@
|
|||
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
|
||||
|
||||
/* Global Event Size Registers */
|
||||
#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
|
||||
#define DWC3_GEVNTSIZ_INTMASK BIT(31)
|
||||
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
|
||||
|
||||
/* Global HWPARAMS0 Register */
|
||||
|
@ -289,18 +291,18 @@
|
|||
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
|
||||
|
||||
/* Global HWPARAMS6 Register */
|
||||
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
|
||||
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
|
||||
|
||||
/* Global HWPARAMS7 Register */
|
||||
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
|
||||
|
||||
/* Global Frame Length Adjustment Register */
|
||||
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
|
||||
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
|
||||
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
|
||||
|
||||
/* Global User Control Register 2 */
|
||||
#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
|
||||
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
|
||||
|
||||
/* Device Configuration Register */
|
||||
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
|
||||
|
@ -310,23 +312,23 @@
|
|||
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
|
||||
#define DWC3_DCFG_SUPERSPEED (4 << 0)
|
||||
#define DWC3_DCFG_HIGHSPEED (0 << 0)
|
||||
#define DWC3_DCFG_FULLSPEED (1 << 0)
|
||||
#define DWC3_DCFG_FULLSPEED BIT(0)
|
||||
#define DWC3_DCFG_LOWSPEED (2 << 0)
|
||||
|
||||
#define DWC3_DCFG_NUMP_SHIFT 17
|
||||
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
|
||||
#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
|
||||
#define DWC3_DCFG_LPM_CAP (1 << 22)
|
||||
#define DWC3_DCFG_LPM_CAP BIT(22)
|
||||
|
||||
/* Device Control Register */
|
||||
#define DWC3_DCTL_RUN_STOP (1 << 31)
|
||||
#define DWC3_DCTL_CSFTRST (1 << 30)
|
||||
#define DWC3_DCTL_LSFTRST (1 << 29)
|
||||
#define DWC3_DCTL_RUN_STOP BIT(31)
|
||||
#define DWC3_DCTL_CSFTRST BIT(30)
|
||||
#define DWC3_DCTL_LSFTRST BIT(29)
|
||||
|
||||
#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
|
||||
#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
|
||||
|
||||
#define DWC3_DCTL_APPL1RES (1 << 23)
|
||||
#define DWC3_DCTL_APPL1RES BIT(23)
|
||||
|
||||
/* These apply for core versions 1.87a and earlier */
|
||||
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
|
||||
|
@ -341,15 +343,15 @@
|
|||
#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
|
||||
#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
|
||||
|
||||
#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
|
||||
#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
|
||||
#define DWC3_DCTL_CRS (1 << 17)
|
||||
#define DWC3_DCTL_CSS (1 << 16)
|
||||
#define DWC3_DCTL_KEEP_CONNECT BIT(19)
|
||||
#define DWC3_DCTL_L1_HIBER_EN BIT(18)
|
||||
#define DWC3_DCTL_CRS BIT(17)
|
||||
#define DWC3_DCTL_CSS BIT(16)
|
||||
|
||||
#define DWC3_DCTL_INITU2ENA (1 << 12)
|
||||
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
|
||||
#define DWC3_DCTL_INITU1ENA (1 << 10)
|
||||
#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
|
||||
#define DWC3_DCTL_INITU2ENA BIT(12)
|
||||
#define DWC3_DCTL_ACCEPTU2ENA BIT(11)
|
||||
#define DWC3_DCTL_INITU1ENA BIT(10)
|
||||
#define DWC3_DCTL_ACCEPTU1ENA BIT(9)
|
||||
#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
|
||||
|
||||
#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
|
||||
|
@ -364,36 +366,36 @@
|
|||
#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11))
|
||||
|
||||
/* Device Event Enable Register */
|
||||
#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12)
|
||||
#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11)
|
||||
#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10)
|
||||
#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
|
||||
#define DWC3_DEVTEN_SOFEN (1 << 7)
|
||||
#define DWC3_DEVTEN_EOPFEN (1 << 6)
|
||||
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
|
||||
#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
|
||||
#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
|
||||
#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
|
||||
#define DWC3_DEVTEN_USBRSTEN (1 << 1)
|
||||
#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
|
||||
#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN BIT(12)
|
||||
#define DWC3_DEVTEN_EVNTOVERFLOWEN BIT(11)
|
||||
#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
|
||||
#define DWC3_DEVTEN_ERRTICERREN BIT(9)
|
||||
#define DWC3_DEVTEN_SOFEN BIT(7)
|
||||
#define DWC3_DEVTEN_EOPFEN BIT(6)
|
||||
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
|
||||
#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
|
||||
#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
|
||||
#define DWC3_DEVTEN_CONNECTDONEEN BIT(2)
|
||||
#define DWC3_DEVTEN_USBRSTEN BIT(1)
|
||||
#define DWC3_DEVTEN_DISCONNEVTEN BIT(0)
|
||||
|
||||
/* Device Status Register */
|
||||
#define DWC3_DSTS_DCNRD (1 << 29)
|
||||
#define DWC3_DSTS_DCNRD BIT(29)
|
||||
|
||||
/* This applies for core versions 1.87a and earlier */
|
||||
#define DWC3_DSTS_PWRUPREQ (1 << 24)
|
||||
#define DWC3_DSTS_PWRUPREQ BIT(24)
|
||||
|
||||
/* These apply for core versions 1.94a and later */
|
||||
#define DWC3_DSTS_RSS (1 << 25)
|
||||
#define DWC3_DSTS_SSS (1 << 24)
|
||||
#define DWC3_DSTS_RSS BIT(25)
|
||||
#define DWC3_DSTS_SSS BIT(24)
|
||||
|
||||
#define DWC3_DSTS_COREIDLE (1 << 23)
|
||||
#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
|
||||
#define DWC3_DSTS_COREIDLE BIT(23)
|
||||
#define DWC3_DSTS_DEVCTRLHLT BIT(22)
|
||||
|
||||
#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18)
|
||||
#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18)
|
||||
|
||||
#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
|
||||
#define DWC3_DSTS_RXFIFOEMPTY BIT(17)
|
||||
|
||||
#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
|
||||
#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
|
||||
|
@ -403,7 +405,7 @@
|
|||
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
|
||||
#define DWC3_DSTS_SUPERSPEED (4 << 0)
|
||||
#define DWC3_DSTS_HIGHSPEED (0 << 0)
|
||||
#define DWC3_DSTS_FULLSPEED (1 << 0)
|
||||
#define DWC3_DSTS_FULLSPEED BIT(0)
|
||||
#define DWC3_DSTS_LOWSPEED (2 << 0)
|
||||
|
||||
/* Device Generic Command Register */
|
||||
|
@ -421,26 +423,26 @@
|
|||
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
|
||||
|
||||
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
|
||||
#define DWC3_DGCMD_CMDACT (1 << 10)
|
||||
#define DWC3_DGCMD_CMDIOC (1 << 8)
|
||||
#define DWC3_DGCMD_CMDACT BIT(10)
|
||||
#define DWC3_DGCMD_CMDIOC BIT(8)
|
||||
|
||||
/* Device Generic Command Parameter Register */
|
||||
#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
|
||||
#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT BIT(0)
|
||||
#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
|
||||
#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
|
||||
#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
|
||||
#define DWC3_DGCMDPAR_TX_FIFO BIT(5)
|
||||
#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
|
||||
#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
|
||||
#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0)
|
||||
|
||||
/* Device Endpoint Command Register */
|
||||
#define DWC3_DEPCMD_PARAM_SHIFT 16
|
||||
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
|
||||
#define DWC3_DEPCMD_CLEARPENDIN (1 << 11)
|
||||
#define DWC3_DEPCMD_CMDACT (1 << 10)
|
||||
#define DWC3_DEPCMD_CMDIOC (1 << 8)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM BIT(11)
|
||||
#define DWC3_DEPCMD_CLEARPENDIN BIT(11)
|
||||
#define DWC3_DEPCMD_CMDACT BIT(10)
|
||||
#define DWC3_DEPCMD_CMDIOC BIT(8)
|
||||
|
||||
#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)
|
||||
#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)
|
||||
|
@ -458,7 +460,7 @@
|
|||
#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
|
||||
|
||||
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
|
||||
#define DWC3_DALEPENA_EP(n) (1 << n)
|
||||
#define DWC3_DALEPENA_EP(n) BIT(n)
|
||||
|
||||
#define DWC3_DEPCMD_TYPE_CONTROL 0
|
||||
#define DWC3_DEPCMD_TYPE_ISOC 1
|
||||
|
@ -500,8 +502,8 @@ struct dwc3_event_buffer {
|
|||
struct dwc3 *dwc;
|
||||
};
|
||||
|
||||
#define DWC3_EP_FLAG_STALLED (1 << 0)
|
||||
#define DWC3_EP_FLAG_WEDGED (1 << 1)
|
||||
#define DWC3_EP_FLAG_STALLED BIT(0)
|
||||
#define DWC3_EP_FLAG_WEDGED BIT(1)
|
||||
|
||||
#define DWC3_EP_DIRECTION_TX true
|
||||
#define DWC3_EP_DIRECTION_RX false
|
||||
|
@ -550,17 +552,17 @@ struct dwc3_ep {
|
|||
|
||||
u32 saved_state;
|
||||
unsigned flags;
|
||||
#define DWC3_EP_ENABLED (1 << 0)
|
||||
#define DWC3_EP_STALL (1 << 1)
|
||||
#define DWC3_EP_WEDGE (1 << 2)
|
||||
#define DWC3_EP_BUSY (1 << 4)
|
||||
#define DWC3_EP_PENDING_REQUEST (1 << 5)
|
||||
#define DWC3_EP_MISSED_ISOC (1 << 6)
|
||||
#define DWC3_EP_END_TRANSFER_PENDING (1 << 7)
|
||||
#define DWC3_EP_TRANSFER_STARTED (1 << 8)
|
||||
#define DWC3_EP_ENABLED BIT(0)
|
||||
#define DWC3_EP_STALL BIT(1)
|
||||
#define DWC3_EP_WEDGE BIT(2)
|
||||
#define DWC3_EP_BUSY BIT(4)
|
||||
#define DWC3_EP_PENDING_REQUEST BIT(5)
|
||||
#define DWC3_EP_MISSED_ISOC BIT(6)
|
||||
#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
|
||||
#define DWC3_EP_TRANSFER_STARTED BIT(8)
|
||||
|
||||
/* This last one is specific to EP0 */
|
||||
#define DWC3_EP0_DIR_IN (1 << 31)
|
||||
#define DWC3_EP0_DIR_IN BIT(31)
|
||||
|
||||
/*
|
||||
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
|
||||
|
@ -638,13 +640,13 @@ enum dwc3_link_state {
|
|||
#define DWC3_TRB_STS_XFER_IN_PROG 4
|
||||
|
||||
/* TRB Control */
|
||||
#define DWC3_TRB_CTRL_HWO (1 << 0)
|
||||
#define DWC3_TRB_CTRL_LST (1 << 1)
|
||||
#define DWC3_TRB_CTRL_CHN (1 << 2)
|
||||
#define DWC3_TRB_CTRL_CSP (1 << 3)
|
||||
#define DWC3_TRB_CTRL_HWO BIT(0)
|
||||
#define DWC3_TRB_CTRL_LST BIT(1)
|
||||
#define DWC3_TRB_CTRL_CHN BIT(2)
|
||||
#define DWC3_TRB_CTRL_CSP BIT(3)
|
||||
#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4)
|
||||
#define DWC3_TRB_CTRL_ISP_IMI (1 << 10)
|
||||
#define DWC3_TRB_CTRL_IOC (1 << 11)
|
||||
#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_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
|
||||
|
@ -746,6 +748,7 @@ struct dwc3_request {
|
|||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned started:1;
|
||||
unsigned zero:1;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -758,15 +761,11 @@ struct dwc3_scratchpad_array {
|
|||
|
||||
/**
|
||||
* struct dwc3 - representation of our controller
|
||||
* @ctrl_req: usb control request which is used for ep0
|
||||
* @drd_work - workqueue used for role swapping
|
||||
* @ep0_trb: trb which is used for the ctrl_req
|
||||
* @ep0_bounce: bounce buffer for ep0
|
||||
* @zlp_buf: used when request->zero is set
|
||||
* @setup_buf: used while precessing STD USB requests
|
||||
* @ctrl_req_addr: dma address of ctrl_req
|
||||
* @ep0_trb: dma address of ep0_trb
|
||||
* @ep0_usb_req: dummy req used while handling STD USB requests
|
||||
* @ep0_bounce_addr: dma address of ep0_bounce
|
||||
* @scratch_addr: dma address of scratchbuf
|
||||
* @ep0_in_setup: one control transfer is completed and enter setup phase
|
||||
* @lock: for synchronizing
|
||||
|
@ -784,6 +783,10 @@ struct dwc3_scratchpad_array {
|
|||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
* @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
|
||||
* @edev: extcon handle
|
||||
* @edev_nb: extcon notifier
|
||||
* @hsphy_mode: UTMI phy mode, one of following:
|
||||
* - USBPHY_INTERFACE_MODE_UTMI
|
||||
* - USBPHY_INTERFACE_MODE_UTMIW
|
||||
|
@ -799,8 +802,7 @@ struct dwc3_scratchpad_array {
|
|||
* @u2pel: parameter from Set SEL request.
|
||||
* @u1sel: parameter from Set SEL request.
|
||||
* @u1pel: parameter from Set SEL request.
|
||||
* @num_out_eps: number of out endpoints
|
||||
* @num_in_eps: number of in endpoints
|
||||
* @num_eps: number of endpoints
|
||||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
|
@ -858,17 +860,13 @@ struct dwc3_scratchpad_array {
|
|||
* increments or 0 to disable.
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
struct work_struct drd_work;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *bounce;
|
||||
void *ep0_bounce;
|
||||
void *zlp_buf;
|
||||
void *scratchbuf;
|
||||
u8 *setup_buf;
|
||||
dma_addr_t ctrl_req_addr;
|
||||
dma_addr_t ep0_trb_addr;
|
||||
dma_addr_t bounce_addr;
|
||||
dma_addr_t ep0_bounce_addr;
|
||||
dma_addr_t scratch_addr;
|
||||
struct dwc3_request ep0_usb_req;
|
||||
struct completion ep0_in_setup;
|
||||
|
@ -900,6 +898,10 @@ struct dwc3 {
|
|||
size_t regs_size;
|
||||
|
||||
enum usb_dr_mode dr_mode;
|
||||
u32 current_dr_role;
|
||||
u32 desired_dr_role;
|
||||
struct extcon_dev *edev;
|
||||
struct notifier_block edev_nb;
|
||||
enum usb_phy_interface hsphy_mode;
|
||||
|
||||
u32 fladj;
|
||||
|
@ -960,8 +962,7 @@ struct dwc3 {
|
|||
|
||||
u8 speed;
|
||||
|
||||
u8 num_out_eps;
|
||||
u8 num_in_eps;
|
||||
u8 num_eps;
|
||||
|
||||
struct dwc3_hwparams hwparams;
|
||||
struct dentry *root;
|
||||
|
@ -1010,7 +1011,7 @@ struct dwc3 {
|
|||
u16 imod_interval;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
@ -1054,13 +1055,13 @@ struct dwc3_event_depevt {
|
|||
u32 status:4;
|
||||
|
||||
/* Within XferNotReady */
|
||||
#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3)
|
||||
#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3)
|
||||
|
||||
/* Within XferComplete */
|
||||
#define DEPEVT_STATUS_BUSERR (1 << 0)
|
||||
#define DEPEVT_STATUS_SHORT (1 << 1)
|
||||
#define DEPEVT_STATUS_IOC (1 << 2)
|
||||
#define DEPEVT_STATUS_LST (1 << 3)
|
||||
#define DEPEVT_STATUS_BUSERR BIT(0)
|
||||
#define DEPEVT_STATUS_SHORT BIT(1)
|
||||
#define DEPEVT_STATUS_IOC BIT(2)
|
||||
#define DEPEVT_STATUS_LST BIT(3)
|
||||
|
||||
/* Stream event only */
|
||||
#define DEPEVT_STREAMEVT_FOUND 1
|
||||
|
@ -1221,6 +1222,16 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
|||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_drd_init(struct dwc3 *dwc);
|
||||
void dwc3_drd_exit(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_drd_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_drd_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
/* power management interface */
|
||||
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc);
|
||||
|
|
|
@ -124,6 +124,34 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_trb_type_string - returns TRB type as a string
|
||||
* @type: the type of the TRB
|
||||
*/
|
||||
static inline const char *dwc3_trb_type_string(unsigned int type)
|
||||
{
|
||||
switch (type) {
|
||||
case DWC3_TRBCTL_NORMAL:
|
||||
return "normal";
|
||||
case DWC3_TRBCTL_CONTROL_SETUP:
|
||||
return "setup";
|
||||
case DWC3_TRBCTL_CONTROL_STATUS2:
|
||||
return "status2";
|
||||
case DWC3_TRBCTL_CONTROL_STATUS3:
|
||||
return "status3";
|
||||
case DWC3_TRBCTL_CONTROL_DATA:
|
||||
return "data";
|
||||
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
|
||||
return "isoc-first";
|
||||
case DWC3_TRBCTL_ISOCHRONOUS:
|
||||
return "isoc";
|
||||
case DWC3_TRBCTL_LINK_TRB:
|
||||
return "link";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
|
||||
{
|
||||
switch (state) {
|
||||
|
|
|
@ -300,7 +300,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
|
|||
seq_printf(s, "device\n");
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_OTG:
|
||||
seq_printf(s, "OTG\n");
|
||||
seq_printf(s, "otg\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
|
||||
|
@ -319,7 +319,6 @@ static ssize_t dwc3_mode_write(struct file *file,
|
|||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
u32 mode = 0;
|
||||
char buf[32];
|
||||
|
||||
|
@ -327,19 +326,16 @@ static ssize_t dwc3_mode_write(struct file *file,
|
|||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "host", 4))
|
||||
mode |= DWC3_GCTL_PRTCAP_HOST;
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
|
||||
if (!strncmp(buf, "device", 6))
|
||||
mode |= DWC3_GCTL_PRTCAP_DEVICE;
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
|
||||
if (!strncmp(buf, "otg", 3))
|
||||
mode |= DWC3_GCTL_PRTCAP_OTG;
|
||||
mode = DWC3_GCTL_PRTCAP_OTG;
|
||||
|
||||
dwc3_set_mode(dwc, mode);
|
||||
|
||||
if (mode) {
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_set_mode(dwc, mode);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -446,52 +442,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
state = DWC3_DSTS_USBLNKST(reg);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
switch (state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
seq_printf(s, "U0\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_U1:
|
||||
seq_printf(s, "U1\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_U2:
|
||||
seq_printf(s, "U2\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_U3:
|
||||
seq_printf(s, "U3\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
seq_printf(s, "SS.Disabled\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
seq_printf(s, "Rx.Detect\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_SS_INACT:
|
||||
seq_printf(s, "SS.Inactive\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_POLL:
|
||||
seq_printf(s, "Poll\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
seq_printf(s, "Recovery\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_HRESET:
|
||||
seq_printf(s, "HRESET\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_CMPLY:
|
||||
seq_printf(s, "Compliance\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_LPBK:
|
||||
seq_printf(s, "Loopback\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
seq_printf(s, "Reset\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
seq_printf(s, "Resume\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", state);
|
||||
}
|
||||
seq_printf(s, "%s\n", dwc3_gadget_link_string(state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -689,30 +640,6 @@ static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline const char *dwc3_trb_type_string(struct dwc3_trb *trb)
|
||||
{
|
||||
switch (DWC3_TRBCTL_TYPE(trb->ctrl)) {
|
||||
case DWC3_TRBCTL_NORMAL:
|
||||
return "normal";
|
||||
case DWC3_TRBCTL_CONTROL_SETUP:
|
||||
return "control-setup";
|
||||
case DWC3_TRBCTL_CONTROL_STATUS2:
|
||||
return "control-status2";
|
||||
case DWC3_TRBCTL_CONTROL_STATUS3:
|
||||
return "control-status3";
|
||||
case DWC3_TRBCTL_CONTROL_DATA:
|
||||
return "control-data";
|
||||
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
|
||||
return "isoc-first";
|
||||
case DWC3_TRBCTL_ISOCHRONOUS:
|
||||
return "isoc";
|
||||
case DWC3_TRBCTL_LINK_TRB:
|
||||
return "link";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
|
@ -733,10 +660,11 @@ static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
|
|||
|
||||
for (i = 0; i < DWC3_TRB_NUM; i++) {
|
||||
struct dwc3_trb *trb = &dep->trb_pool[i];
|
||||
unsigned int type = DWC3_TRBCTL_TYPE(trb->ctrl);
|
||||
|
||||
seq_printf(s, "%08x%08x,%d,%s,%d,%d,%d,%d,%d,%d\n",
|
||||
trb->bph, trb->bpl, trb->size,
|
||||
dwc3_trb_type_string(trb),
|
||||
dwc3_trb_type_string(type),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_IOC),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_ISP_IMI),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_CSP),
|
||||
|
@ -822,19 +750,8 @@ static void dwc3_debugfs_create_endpoint_dirs(struct dwc3 *dwc,
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_in_eps; i++) {
|
||||
u8 epnum = (i << 1) | 1;
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
dwc3_debugfs_create_endpoint_dir(dep, parent);
|
||||
}
|
||||
|
||||
for (i = 0; i < dwc->num_out_eps; i++) {
|
||||
u8 epnum = (i << 1);
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
for (i = 0; i < dwc->num_eps; i++) {
|
||||
struct dwc3_ep *dep = dwc->eps[i];
|
||||
|
||||
if (!dep)
|
||||
continue;
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* drd.c - DesignWare USB3 DRD Controller Dual-role support
|
||||
*
|
||||
* Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Roger Quadros <rogerq@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/extcon.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
|
||||
static void dwc3_drd_update(struct dwc3 *dwc)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
|
||||
if (id < 0)
|
||||
id = 0;
|
||||
|
||||
dwc3_set_mode(dwc, id ?
|
||||
DWC3_GCTL_PRTCAP_HOST :
|
||||
DWC3_GCTL_PRTCAP_DEVICE);
|
||||
}
|
||||
|
||||
static int dwc3_drd_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
|
||||
|
||||
dwc3_set_mode(dwc, event ?
|
||||
DWC3_GCTL_PRTCAP_HOST :
|
||||
DWC3_GCTL_PRTCAP_DEVICE);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
int dwc3_drd_init(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dwc->dev->of_node) {
|
||||
if (of_property_read_bool(dwc->dev->of_node, "extcon"))
|
||||
dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
|
||||
|
||||
if (IS_ERR(dwc->edev))
|
||||
return PTR_ERR(dwc->edev);
|
||||
|
||||
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
|
||||
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
&dwc->edev_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "couldn't register cable notifier\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dwc3_drd_update(dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_drd_exit(struct dwc3 *dwc)
|
||||
{
|
||||
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
&dwc->edev_nb);
|
||||
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
flush_work(&dwc->drd_work);
|
||||
dwc3_gadget_exit(dwc);
|
||||
}
|
|
@ -147,53 +147,53 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
|
||||
if (IS_ERR(exynos->vdd33)) {
|
||||
ret = PTR_ERR(exynos->vdd33);
|
||||
goto err2;
|
||||
goto vdd33_err;
|
||||
}
|
||||
ret = regulator_enable(exynos->vdd33);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable VDD33 supply\n");
|
||||
goto err2;
|
||||
goto vdd33_err;
|
||||
}
|
||||
|
||||
exynos->vdd10 = devm_regulator_get(dev, "vdd10");
|
||||
if (IS_ERR(exynos->vdd10)) {
|
||||
ret = PTR_ERR(exynos->vdd10);
|
||||
goto err3;
|
||||
goto vdd10_err;
|
||||
}
|
||||
ret = regulator_enable(exynos->vdd10);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable VDD10 supply\n");
|
||||
goto err3;
|
||||
goto vdd10_err;
|
||||
}
|
||||
|
||||
ret = dwc3_exynos_register_phys(exynos);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
goto err4;
|
||||
goto phys_err;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add dwc3 core\n");
|
||||
goto err5;
|
||||
goto populate_err;
|
||||
}
|
||||
} else {
|
||||
dev_err(dev, "no device node, failed to add dwc3 core\n");
|
||||
ret = -ENODEV;
|
||||
goto err5;
|
||||
goto populate_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
populate_err:
|
||||
platform_device_unregister(exynos->usb2_phy);
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
err4:
|
||||
phys_err:
|
||||
regulator_disable(exynos->vdd10);
|
||||
err3:
|
||||
vdd10_err:
|
||||
regulator_disable(exynos->vdd33);
|
||||
err2:
|
||||
vdd33_err:
|
||||
clk_disable_unprepare(exynos->axius_clk);
|
||||
axius_clk_err:
|
||||
clk_disable_unprepare(exynos->susp_clk);
|
||||
|
|
|
@ -79,40 +79,40 @@
|
|||
#define USBOTGSS_DEBUG_OFFSET 0x0600
|
||||
|
||||
/* SYSCONFIG REGISTER */
|
||||
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
|
||||
#define USBOTGSS_SYSCONFIG_DMADISABLE BIT(16)
|
||||
|
||||
/* IRQ_EOI REGISTER */
|
||||
#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
|
||||
#define USBOTGSS_IRQ_EOI_LINE_NUMBER BIT(0)
|
||||
|
||||
/* IRQS0 BITS */
|
||||
#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0)
|
||||
#define USBOTGSS_IRQO_COREIRQ_ST BIT(0)
|
||||
|
||||
/* IRQMISC BITS */
|
||||
#define USBOTGSS_IRQMISC_DMADISABLECLR (1 << 17)
|
||||
#define USBOTGSS_IRQMISC_OEVT (1 << 16)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_RISE (1 << 13)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_RISE (1 << 12)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE (1 << 11)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_RISE (1 << 8)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_FALL (1 << 5)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_FALL (1 << 4)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL (1 << 3)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_FALL (1 << 0)
|
||||
#define USBOTGSS_IRQMISC_DMADISABLECLR BIT(17)
|
||||
#define USBOTGSS_IRQMISC_OEVT BIT(16)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_RISE BIT(13)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_RISE BIT(12)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE BIT(11)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_RISE BIT(8)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_FALL BIT(5)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_FALL BIT(4)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL BIT(3)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_FALL BIT(0)
|
||||
|
||||
/* UTMI_OTG_STATUS REGISTER */
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_DRVVBUS (1 << 5)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_CHRGVBUS (1 << 4)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_DISCHRGVBUS (1 << 3)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_IDPULLUP (1 << 0)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_DRVVBUS BIT(5)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_CHRGVBUS BIT(4)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_DISCHRGVBUS BIT(3)
|
||||
#define USBOTGSS_UTMI_OTG_STATUS_IDPULLUP BIT(0)
|
||||
|
||||
/* UTMI_OTG_CTRL REGISTER */
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SW_MODE (1 << 31)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT (1 << 9)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_TXBITSTUFFENABLE (1 << 8)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_IDDIG (1 << 4)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SESSEND (1 << 3)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID (1 << 2)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID (1 << 1)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SW_MODE BIT(31)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT BIT(9)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_TXBITSTUFFENABLE BIT(8)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_IDDIG BIT(4)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SESSEND BIT(3)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID BIT(2)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID BIT(1)
|
||||
|
||||
struct dwc3_omap {
|
||||
struct device *dev;
|
||||
|
|
|
@ -39,14 +39,13 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
|
|||
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
struct dwc3_ep *dep, struct dwc3_request *req);
|
||||
|
||||
static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
|
||||
static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
|
||||
dma_addr_t buf_dma, u32 len, u32 type, bool chain)
|
||||
{
|
||||
struct dwc3_trb *trb;
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
struct dwc3 *dwc;
|
||||
|
||||
dwc = dep->dwc;
|
||||
trb = &dwc->ep0_trb[dep->trb_enqueue];
|
||||
|
||||
if (chain)
|
||||
|
@ -69,16 +68,17 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
|
|||
trace_dwc3_prepare_trb(dep, trb);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
|
||||
static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3 *dwc;
|
||||
int ret;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
if (dep->flags & DWC3_EP_BUSY)
|
||||
return 0;
|
||||
|
||||
dwc = dep->dwc;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
|
||||
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
|
||||
|
@ -279,13 +279,15 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
|
|||
|
||||
void dwc3_ep0_out_start(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
int ret;
|
||||
|
||||
complete(&dwc->ep0_in_setup);
|
||||
|
||||
dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
|
||||
dep = dwc->eps[0];
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
|
||||
DWC3_TRBCTL_CONTROL_SETUP, false);
|
||||
ret = dwc3_ep0_start_trans(dwc, 0);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
WARN_ON(ret < 0);
|
||||
}
|
||||
|
||||
|
@ -794,7 +796,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
|
||||
struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb;
|
||||
int ret = -EINVAL;
|
||||
u32 len;
|
||||
|
||||
|
@ -834,7 +836,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
struct usb_request *ur;
|
||||
struct dwc3_trb *trb;
|
||||
struct dwc3_ep *ep0;
|
||||
unsigned transfer_size = 0;
|
||||
unsigned maxp;
|
||||
unsigned remaining_ur_length;
|
||||
void *buf;
|
||||
|
@ -847,9 +848,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
ep0 = dwc->eps[0];
|
||||
|
||||
dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
|
||||
|
||||
trb = dwc->ep0_trb;
|
||||
|
||||
trace_dwc3_complete_trb(ep0, trb);
|
||||
|
||||
r = next_request(&ep0->pending_list);
|
||||
|
@ -870,58 +869,23 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
remaining_ur_length = ur->length;
|
||||
|
||||
length = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
maxp = ep0->endpoint.maxpacket;
|
||||
|
||||
if (dwc->ep0_bounced) {
|
||||
/*
|
||||
* Handle the first TRB before handling the bounce buffer if
|
||||
* the request length is greater than the bounce buffer size
|
||||
*/
|
||||
if (ur->length > DWC3_EP0_BOUNCE_SIZE) {
|
||||
transfer_size = ALIGN(ur->length - maxp, maxp);
|
||||
transferred = transfer_size - length;
|
||||
buf = (u8 *)buf + transferred;
|
||||
ur->actual += transferred;
|
||||
remaining_ur_length -= transferred;
|
||||
|
||||
trb++;
|
||||
length = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
ep0->trb_enqueue = 0;
|
||||
}
|
||||
|
||||
transfer_size = roundup((ur->length - transfer_size),
|
||||
maxp);
|
||||
|
||||
transferred = min_t(u32, remaining_ur_length,
|
||||
transfer_size - length);
|
||||
memcpy(buf, dwc->ep0_bounce, transferred);
|
||||
} else {
|
||||
transferred = ur->length - length;
|
||||
}
|
||||
|
||||
transferred = ur->length - length;
|
||||
ur->actual += transferred;
|
||||
|
||||
if ((epnum & 1) && ur->actual < ur->length) {
|
||||
/* for some reason we did not get everything out */
|
||||
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
} else {
|
||||
dwc3_gadget_giveback(ep0, r, 0);
|
||||
|
||||
if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
|
||||
ur->length && ur->zero) {
|
||||
int ret;
|
||||
|
||||
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
|
||||
|
||||
dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
|
||||
0, DWC3_TRBCTL_CONTROL_DATA, false);
|
||||
ret = dwc3_ep0_start_trans(dwc, epnum);
|
||||
WARN_ON(ret < 0);
|
||||
}
|
||||
if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
|
||||
ur->length && ur->zero) || dwc->ep0_bounced) {
|
||||
trb++;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
trace_dwc3_complete_trb(ep0, trb);
|
||||
ep0->trb_enqueue = 0;
|
||||
dwc->ep0_bounced = false;
|
||||
}
|
||||
|
||||
if ((epnum & 1) && ur->actual < ur->length)
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
else
|
||||
dwc3_gadget_giveback(ep0, r, 0);
|
||||
}
|
||||
|
||||
static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
|
@ -997,14 +961,13 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
req->direction = !!dep->number;
|
||||
|
||||
if (req->request.length == 0) {
|
||||
dwc3_ep0_prepare_one_trb(dwc, dep->number,
|
||||
dwc->ctrl_req_addr, 0,
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
|
||||
DWC3_TRBCTL_CONTROL_DATA, false);
|
||||
ret = dwc3_ep0_start_trans(dwc, dep->number);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
|
||||
&& (dep->number == 0)) {
|
||||
u32 transfer_size = 0;
|
||||
u32 maxpacket;
|
||||
u32 rem;
|
||||
|
||||
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
|
||||
&req->request, dep->number);
|
||||
|
@ -1012,36 +975,55 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
return;
|
||||
|
||||
maxpacket = dep->endpoint.maxpacket;
|
||||
|
||||
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
|
||||
transfer_size = ALIGN(req->request.length - maxpacket,
|
||||
maxpacket);
|
||||
dwc3_ep0_prepare_one_trb(dwc, dep->number,
|
||||
req->request.dma,
|
||||
transfer_size,
|
||||
DWC3_TRBCTL_CONTROL_DATA,
|
||||
true);
|
||||
}
|
||||
|
||||
transfer_size = roundup((req->request.length - transfer_size),
|
||||
maxpacket);
|
||||
|
||||
rem = req->request.length % maxpacket;
|
||||
dwc->ep0_bounced = true;
|
||||
|
||||
dwc3_ep0_prepare_one_trb(dwc, dep->number,
|
||||
dwc->ep0_bounce_addr, transfer_size,
|
||||
DWC3_TRBCTL_CONTROL_DATA, false);
|
||||
ret = dwc3_ep0_start_trans(dwc, dep->number);
|
||||
/* prepare normal TRB */
|
||||
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
|
||||
req->request.length,
|
||||
DWC3_TRBCTL_CONTROL_DATA,
|
||||
true);
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
|
||||
maxpacket - rem,
|
||||
DWC3_TRBCTL_CONTROL_DATA,
|
||||
false);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
|
||||
req->request.length && req->request.zero) {
|
||||
u32 maxpacket;
|
||||
u32 rem;
|
||||
|
||||
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
|
||||
&req->request, dep->number);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
maxpacket = dep->endpoint.maxpacket;
|
||||
rem = req->request.length % maxpacket;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
|
||||
req->request.length,
|
||||
DWC3_TRBCTL_CONTROL_DATA,
|
||||
true);
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
|
||||
0, DWC3_TRBCTL_CONTROL_DATA,
|
||||
false);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
} else {
|
||||
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
|
||||
&req->request, dep->number);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
|
||||
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
|
||||
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
|
||||
false);
|
||||
ret = dwc3_ep0_start_trans(dwc, dep->number);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
}
|
||||
|
||||
WARN_ON(ret < 0);
|
||||
|
@ -1055,9 +1037,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
|||
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
|
||||
: DWC3_TRBCTL_CONTROL_STATUS2;
|
||||
|
||||
dwc3_ep0_prepare_one_trb(dwc, dep->number,
|
||||
dwc->ctrl_req_addr, 0, type, false);
|
||||
return dwc3_ep0_start_trans(dwc, dep->number);
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, type, false);
|
||||
return dwc3_ep0_start_trans(dep);
|
||||
}
|
||||
|
||||
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
|
|
|
@ -171,7 +171,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
int status)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned int unmap_after_complete = false;
|
||||
|
||||
req->started = false;
|
||||
list_del(&req->list);
|
||||
|
@ -181,19 +180,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
if (req->request.status == -EINPROGRESS)
|
||||
req->request.status = status;
|
||||
|
||||
/*
|
||||
* NOTICE we don't want to unmap before calling ->complete() if we're
|
||||
* dealing with a bounced ep0 request. If we unmap it here, we would end
|
||||
* up overwritting the contents of req->buf and this could confuse the
|
||||
* gadget driver.
|
||||
*/
|
||||
if (dwc->ep0_bounced && dep->number <= 1) {
|
||||
dwc->ep0_bounced = false;
|
||||
unmap_after_complete = true;
|
||||
} else {
|
||||
usb_gadget_unmap_request_by_dev(dwc->sysdev,
|
||||
&req->request, req->direction);
|
||||
}
|
||||
usb_gadget_unmap_request_by_dev(dwc->sysdev,
|
||||
&req->request, req->direction);
|
||||
|
||||
trace_dwc3_gadget_giveback(req);
|
||||
|
||||
|
@ -201,10 +189,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
usb_gadget_giveback_request(&dep->endpoint, &req->request);
|
||||
spin_lock(&dwc->lock);
|
||||
|
||||
if (unmap_after_complete)
|
||||
usb_gadget_unmap_request_by_dev(dwc->sysdev,
|
||||
&req->request, req->direction);
|
||||
|
||||
if (dep->number > 1)
|
||||
pm_runtime_put(dwc->dev);
|
||||
}
|
||||
|
@ -1060,6 +1044,22 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
|||
false, 0, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
req->request.no_interrupt);
|
||||
} else if (req->request.zero && req->request.length &&
|
||||
(IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->zero = 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];
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
|
||||
false, 0, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
req->request.no_interrupt);
|
||||
} else {
|
||||
dwc3_prepare_one_trb(dep, req, false, 0);
|
||||
}
|
||||
|
@ -1184,8 +1184,11 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|||
return;
|
||||
}
|
||||
|
||||
/* 4 micro frames in the future */
|
||||
uf = cur_uf + dep->interval * 4;
|
||||
/*
|
||||
* Schedule the first trb for one interval in the future or at
|
||||
* least 4 microframes.
|
||||
*/
|
||||
uf = cur_uf + max_t(u32, 4, dep->interval);
|
||||
|
||||
__dwc3_gadget_kick_transfer(dep, uf);
|
||||
}
|
||||
|
@ -1272,31 +1275,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
|
||||
struct usb_request *request)
|
||||
{
|
||||
dwc3_gadget_ep_free_request(ep, request);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct usb_request *request;
|
||||
struct usb_ep *ep = &dep->endpoint;
|
||||
|
||||
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
|
||||
request->length = 0;
|
||||
request->buf = dwc->zlp_buf;
|
||||
request->complete = __dwc3_gadget_ep_zlp_complete;
|
||||
|
||||
req = to_dwc3_request(request);
|
||||
|
||||
return __dwc3_gadget_ep_queue(dep, req);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
|
@ -1310,17 +1288,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
||||
|
||||
/*
|
||||
* Okay, here's the thing, if gadget driver has requested for a ZLP by
|
||||
* setting request->zero, instead of doing magic, we will just queue an
|
||||
* extra usb_request ourselves so that it gets handled the same way as
|
||||
* any other request.
|
||||
*/
|
||||
if (ret == 0 && request->zero && request->length &&
|
||||
(request->length % ep->maxpacket == 0))
|
||||
ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -1400,7 +1367,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
|
||||
if (r->unaligned) {
|
||||
if (r->unaligned || r->zero) {
|
||||
trb = r->trb + r->num_pending_sgs + 1;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
|
@ -1411,7 +1378,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
|
||||
if (r->unaligned) {
|
||||
if (r->unaligned || r->zero) {
|
||||
trb = r->trb + 1;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
|
@ -2006,14 +1973,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
||||
u8 num, u32 direction)
|
||||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u8 i;
|
||||
u8 epnum;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
u8 epnum = (i << 1) | (direction ? 1 : 0);
|
||||
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
||||
|
||||
for (epnum = 0; epnum < num; epnum++) {
|
||||
bool direction = epnum & 1;
|
||||
|
||||
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
||||
if (!dep)
|
||||
|
@ -2021,12 +1989,12 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|||
|
||||
dep->dwc = dwc;
|
||||
dep->number = epnum;
|
||||
dep->direction = !!direction;
|
||||
dep->direction = direction;
|
||||
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
||||
dwc->eps[epnum] = dep;
|
||||
|
||||
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
|
||||
(epnum & 1) ? "in" : "out");
|
||||
direction ? "in" : "out");
|
||||
|
||||
dep->endpoint.name = dep->name;
|
||||
|
||||
|
@ -2053,7 +2021,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|||
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||
mdwidth /= 8;
|
||||
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(i));
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(epnum >> 1));
|
||||
size = DWC3_GTXFIFOSIZ_TXFDEF(size);
|
||||
|
||||
/* FIFO Depth is in MDWDITH bytes. Multiply */
|
||||
|
@ -2103,7 +2071,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|||
dep->endpoint.caps.type_int = true;
|
||||
}
|
||||
|
||||
dep->endpoint.caps.dir_in = !!direction;
|
||||
dep->endpoint.caps.dir_in = direction;
|
||||
dep->endpoint.caps.dir_out = !direction;
|
||||
|
||||
INIT_LIST_HEAD(&dep->pending_list);
|
||||
|
@ -2113,27 +2081,6 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
||||
|
||||
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "failed to initialize IN endpoints\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
|
@ -2197,7 +2144,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
* with one TRB pending in the ring. We need to manually clear HWO bit
|
||||
* from that TRB.
|
||||
*/
|
||||
if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
|
||||
if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
return 1;
|
||||
}
|
||||
|
@ -2291,11 +2238,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
event, status, chain);
|
||||
}
|
||||
|
||||
if (req->unaligned) {
|
||||
if (req->unaligned || req->zero) {
|
||||
trb = &dep->trb_pool[dep->trb_dequeue];
|
||||
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
||||
event, status, false);
|
||||
req->unaligned = false;
|
||||
req->zero = false;
|
||||
}
|
||||
|
||||
req->request.actual = length - req->remaining;
|
||||
|
@ -3161,49 +3109,26 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
|
||||
dwc->irq_gadget = irq;
|
||||
|
||||
dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
|
||||
&dwc->ctrl_req_addr, GFP_KERNEL);
|
||||
if (!dwc->ctrl_req) {
|
||||
dev_err(dwc->dev, "failed to allocate ctrl request\n");
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
|
||||
sizeof(*dwc->ep0_trb) * 2,
|
||||
&dwc->ep0_trb_addr, GFP_KERNEL);
|
||||
if (!dwc->ep0_trb) {
|
||||
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
|
||||
dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL);
|
||||
if (!dwc->setup_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
dwc->ep0_bounce = dma_alloc_coherent(dwc->sysdev,
|
||||
DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
|
||||
GFP_KERNEL);
|
||||
if (!dwc->ep0_bounce) {
|
||||
dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
|
||||
if (!dwc->zlp_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err4;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
|
||||
&dwc->bounce_addr, GFP_KERNEL);
|
||||
if (!dwc->bounce) {
|
||||
ret = -ENOMEM;
|
||||
goto err5;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
init_completion(&dwc->ep0_in_setup);
|
||||
|
@ -3241,39 +3166,31 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
* sure we're starting from a well known location.
|
||||
*/
|
||||
|
||||
ret = dwc3_gadget_init_endpoints(dwc);
|
||||
ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
|
||||
if (ret)
|
||||
goto err6;
|
||||
goto err3;
|
||||
|
||||
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register udc\n");
|
||||
goto err6;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err6:
|
||||
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
|
||||
dwc->bounce_addr);
|
||||
|
||||
err5:
|
||||
kfree(dwc->zlp_buf);
|
||||
|
||||
err4:
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
err3:
|
||||
kfree(dwc->setup_buf);
|
||||
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
|
||||
dwc->bounce_addr);
|
||||
|
||||
err2:
|
||||
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
kfree(dwc->setup_buf);
|
||||
|
||||
err1:
|
||||
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
|
||||
dwc->ctrl_req, dwc->ctrl_req_addr);
|
||||
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
|
@ -3284,22 +3201,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{
|
||||
usb_del_gadget_udc(&dwc->gadget);
|
||||
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
|
||||
dwc->bounce_addr);
|
||||
dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
dwc->bounce_addr);
|
||||
kfree(dwc->setup_buf);
|
||||
kfree(dwc->zlp_buf);
|
||||
|
||||
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
|
||||
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
|
||||
dwc->ctrl_req, dwc->ctrl_req_addr);
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
}
|
||||
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
|
|
|
@ -29,16 +29,16 @@ struct dwc3;
|
|||
|
||||
/* DEPCFG parameter 1 */
|
||||
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
|
||||
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
|
||||
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
|
||||
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
|
||||
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
|
||||
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
|
||||
#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8)
|
||||
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
|
||||
#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
|
||||
#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
|
||||
#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(12)
|
||||
#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
|
||||
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
|
||||
#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
|
||||
#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
|
||||
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
|
||||
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
|
||||
#define DWC3_DEPCFG_BULK_BASED BIT(30)
|
||||
#define DWC3_DEPCFG_FIFO_BASED BIT(31)
|
||||
|
||||
/* DEPCFG parameter 0 */
|
||||
#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
|
||||
|
@ -47,10 +47,10 @@ struct dwc3;
|
|||
#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
|
||||
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
|
||||
/* This applies for core versions earlier than 1.94a */
|
||||
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
|
||||
#define DWC3_DEPCFG_IGN_SEQ_NUM BIT(31)
|
||||
/* These apply for core versions 1.94a and later */
|
||||
#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
|
||||
#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
|
||||
#define DWC3_DEPCFG_ACTION_RESTORE BIT(30)
|
||||
#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
|
||||
|
||||
/* DEPXFERCFG parameter 0 */
|
||||
|
|
|
@ -27,31 +27,6 @@
|
|||
#include "core.h"
|
||||
#include "debug.h"
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_msg,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf),
|
||||
TP_STRUCT__entry(__dynamic_array(char, msg, DWC3_MSG_MAX)),
|
||||
TP_fast_assign(
|
||||
vsnprintf(__get_str(msg), DWC3_MSG_MAX, vaf->fmt, *vaf->va);
|
||||
),
|
||||
TP_printk("%s", __get_str(msg))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_msg, dwc3_core,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_io,
|
||||
TP_PROTO(void *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value),
|
||||
|
@ -198,7 +173,7 @@ DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
|
|||
__entry->param = param;
|
||||
__entry->status = status;
|
||||
),
|
||||
TP_printk("cmd '%s' [%d] param %08x --> status: %s",
|
||||
TP_printk("cmd '%s' [%x] param %08x --> status: %s",
|
||||
dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param,
|
||||
dwc3_gadget_generic_cmd_status_string(__entry->status)
|
||||
|
@ -298,36 +273,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
__entry->ctrl & DWC3_TRB_CTRL_CSP ? 'S' : 's',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_ISP_IMI ? 'S' : 's',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_IOC ? 'C' : 'c',
|
||||
({char *s;
|
||||
switch (__entry->ctrl & 0x3f0) {
|
||||
case DWC3_TRBCTL_NORMAL:
|
||||
s = "normal";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_SETUP:
|
||||
s = "setup";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_STATUS2:
|
||||
s = "status2";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_STATUS3:
|
||||
s = "status3";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_DATA:
|
||||
s = "data";
|
||||
break;
|
||||
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
|
||||
s = "isoc-first";
|
||||
break;
|
||||
case DWC3_TRBCTL_ISOCHRONOUS:
|
||||
s = "isoc";
|
||||
break;
|
||||
case DWC3_TRBCTL_LINK_TRB:
|
||||
s = "link";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
break;
|
||||
} s; })
|
||||
dwc3_trb_type_string(DWC3_TRBCTL_TYPE(__entry->ctrl))
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ config USB_F_TCM
|
|||
# this first set of drivers all depend on bulk-capable hardware.
|
||||
|
||||
config USB_CONFIGFS
|
||||
tristate "USB functions configurable through configfs"
|
||||
tristate "USB Gadget functions configurable through configfs"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
A Linux USB "gadget" can be set up through configfs.
|
||||
|
@ -458,8 +458,9 @@ config USB_CONFIGFS_F_TCM
|
|||
UAS utilizes the USB 3.0 feature called streams support.
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
tristate "USB Gadget precomposed configurations"
|
||||
default USB_ETH
|
||||
optional
|
||||
help
|
||||
A Linux "Gadget Driver" talks to the USB Peripheral Controller
|
||||
driver through the abstract "gadget" API. Some other operating
|
||||
|
@ -476,6 +477,12 @@ choice
|
|||
not be able work with that controller, or might need to implement
|
||||
a less common variant of a device class protocol.
|
||||
|
||||
The available choices each represent a single precomposed USB
|
||||
gadget configuration. In the device model, each option contains
|
||||
both the device instantiation as a child for a USB gadget
|
||||
controller, and the relevant drivers for each function declared
|
||||
by the device.
|
||||
|
||||
source "drivers/usb/gadget/legacy/Kconfig"
|
||||
|
||||
endchoice
|
||||
|
|
|
@ -246,7 +246,6 @@ EXPORT_SYMBOL_GPL(ffs_lock);
|
|||
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name);
|
||||
static struct ffs_dev *_ffs_alloc_dev(void);
|
||||
static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
|
||||
static void _ffs_free_dev(struct ffs_dev *dev);
|
||||
static void *ffs_acquire_dev(const char *dev_name);
|
||||
static void ffs_release_dev(struct ffs_data *ffs_data);
|
||||
|
@ -3302,9 +3301,10 @@ static struct ffs_dev *_ffs_do_find_dev(const char *name)
|
|||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(dev, &ffs_devices, entry) {
|
||||
if (!dev->name || !name)
|
||||
continue;
|
||||
if (strcmp(dev->name, name) == 0)
|
||||
return dev;
|
||||
}
|
||||
|
@ -3380,42 +3380,11 @@ static void ffs_free_inst(struct usb_function_instance *f)
|
|||
kfree(opts);
|
||||
}
|
||||
|
||||
#define MAX_INST_NAME_LEN 40
|
||||
|
||||
static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
|
||||
{
|
||||
struct f_fs_opts *opts;
|
||||
char *ptr;
|
||||
const char *tmp;
|
||||
int name_len, ret;
|
||||
|
||||
name_len = strlen(name) + 1;
|
||||
if (name_len > MAX_INST_NAME_LEN)
|
||||
if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name))
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
ptr = kstrndup(name, name_len, GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
opts = to_f_fs_opts(fi);
|
||||
tmp = NULL;
|
||||
|
||||
ffs_dev_lock();
|
||||
|
||||
tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
|
||||
ret = _ffs_name_dev(opts->dev, ptr);
|
||||
if (ret) {
|
||||
kfree(ptr);
|
||||
ffs_dev_unlock();
|
||||
return ret;
|
||||
}
|
||||
opts->dev->name_allocated = true;
|
||||
|
||||
ffs_dev_unlock();
|
||||
|
||||
kfree(tmp);
|
||||
|
||||
return 0;
|
||||
return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *ffs_alloc_inst(void)
|
||||
|
@ -3545,32 +3514,19 @@ static struct ffs_dev *_ffs_alloc_dev(void)
|
|||
return dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
* The caller is responsible for "name" being available whenever f_fs needs it
|
||||
*/
|
||||
static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
|
||||
{
|
||||
struct ffs_dev *existing;
|
||||
|
||||
existing = _ffs_do_find_dev(name);
|
||||
if (existing)
|
||||
return -EBUSY;
|
||||
|
||||
dev->name = name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller is responsible for "name" being available whenever f_fs needs it
|
||||
*/
|
||||
int ffs_name_dev(struct ffs_dev *dev, const char *name)
|
||||
{
|
||||
int ret;
|
||||
struct ffs_dev *existing;
|
||||
int ret = 0;
|
||||
|
||||
ffs_dev_lock();
|
||||
ret = _ffs_name_dev(dev, name);
|
||||
|
||||
existing = _ffs_do_find_dev(name);
|
||||
if (!existing)
|
||||
strlcpy(dev->name, name, ARRAY_SIZE(dev->name));
|
||||
else if (existing != dev)
|
||||
ret = -EBUSY;
|
||||
|
||||
ffs_dev_unlock();
|
||||
|
||||
return ret;
|
||||
|
@ -3600,8 +3556,6 @@ EXPORT_SYMBOL_GPL(ffs_single_dev);
|
|||
static void _ffs_free_dev(struct ffs_dev *dev)
|
||||
{
|
||||
list_del(&dev->entry);
|
||||
if (dev->name_allocated)
|
||||
kfree(dev->name);
|
||||
|
||||
/* Clear the private_data pointer to stop incorrect dev access */
|
||||
if (dev->ffs_data)
|
||||
|
|
|
@ -178,6 +178,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req);
|
|||
static int
|
||||
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
|
||||
{
|
||||
struct usb_gadget *g = dev->gadget;
|
||||
struct sk_buff *skb;
|
||||
int retval = -ENOMEM;
|
||||
size_t size = 0;
|
||||
|
@ -209,8 +210,11 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
|
|||
*/
|
||||
size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
|
||||
size += dev->port_usb->header_len;
|
||||
size += out->maxpacket - 1;
|
||||
size -= size % out->maxpacket;
|
||||
|
||||
if (g->quirk_ep_out_aligned_size) {
|
||||
size += out->maxpacket - 1;
|
||||
size -= size % out->maxpacket;
|
||||
}
|
||||
|
||||
if (dev->port_usb->is_fixed)
|
||||
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
|
||||
|
@ -401,13 +405,12 @@ static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
|
|||
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
|
||||
{
|
||||
struct usb_request *req;
|
||||
struct usb_request *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
/* fill unused rxq slots with some skb */
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
while (!list_empty(&dev->rx_reqs)) {
|
||||
req = container_of(dev->rx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
|
||||
list_del_init(&req->list);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
|
||||
|
@ -527,7 +530,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
|||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
req = container_of(dev->tx_reqs.next, struct usb_request, list);
|
||||
req = list_first_entry(&dev->tx_reqs, struct usb_request, list);
|
||||
list_del(&req->list);
|
||||
|
||||
/* temporarily stop TX queue when the freelist empties */
|
||||
|
@ -1122,6 +1125,7 @@ void gether_disconnect(struct gether *link)
|
|||
{
|
||||
struct eth_dev *dev = link->ioport;
|
||||
struct usb_request *req;
|
||||
struct usb_request *tmp;
|
||||
|
||||
WARN_ON(!dev);
|
||||
if (!dev)
|
||||
|
@ -1138,9 +1142,7 @@ void gether_disconnect(struct gether *link)
|
|||
*/
|
||||
usb_ep_disable(link->in_ep);
|
||||
spin_lock(&dev->req_lock);
|
||||
while (!list_empty(&dev->tx_reqs)) {
|
||||
req = container_of(dev->tx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_for_each_entry_safe(req, tmp, &dev->tx_reqs, list) {
|
||||
list_del(&req->list);
|
||||
|
||||
spin_unlock(&dev->req_lock);
|
||||
|
@ -1152,9 +1154,7 @@ void gether_disconnect(struct gether *link)
|
|||
|
||||
usb_ep_disable(link->out_ep);
|
||||
spin_lock(&dev->req_lock);
|
||||
while (!list_empty(&dev->rx_reqs)) {
|
||||
req = container_of(dev->rx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
|
||||
list_del(&req->list);
|
||||
|
||||
spin_unlock(&dev->req_lock);
|
||||
|
|
|
@ -40,15 +40,16 @@
|
|||
struct f_fs_opts;
|
||||
|
||||
struct ffs_dev {
|
||||
const char *name;
|
||||
bool name_allocated;
|
||||
bool mounted;
|
||||
bool desc_ready;
|
||||
bool single;
|
||||
struct ffs_data *ffs_data;
|
||||
struct f_fs_opts *opts;
|
||||
struct list_head entry;
|
||||
|
||||
char name[41];
|
||||
|
||||
bool mounted;
|
||||
bool desc_ready;
|
||||
bool single;
|
||||
|
||||
int (*ffs_ready_callback)(struct ffs_data *ffs);
|
||||
void (*ffs_closed_callback)(struct ffs_data *ffs);
|
||||
void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
|
||||
|
|
|
@ -2125,7 +2125,7 @@ static struct configfs_item_operations uvc_item_ops = {
|
|||
.release = uvc_attr_release,
|
||||
};
|
||||
|
||||
#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \
|
||||
#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
|
||||
static ssize_t f_uvc_opts_##cname##_show( \
|
||||
struct config_item *item, char *page) \
|
||||
{ \
|
||||
|
@ -2168,16 +2168,16 @@ end: \
|
|||
return ret; \
|
||||
} \
|
||||
\
|
||||
UVC_ATTR(f_uvc_opts_, cname, aname)
|
||||
UVC_ATTR(f_uvc_opts_, cname, cname)
|
||||
|
||||
#define identity_conv(x) (x)
|
||||
|
||||
UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv,
|
||||
16);
|
||||
UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu,
|
||||
3072);
|
||||
UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv,
|
||||
15);
|
||||
UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv,
|
||||
kstrtou8, u8, identity_conv, 16);
|
||||
UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu,
|
||||
kstrtou16, u16, le16_to_cpu, 3072);
|
||||
UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv,
|
||||
kstrtou8, u8, identity_conv, 15);
|
||||
|
||||
#undef identity_conv
|
||||
|
||||
|
|
|
@ -62,8 +62,9 @@ config USB_ATMEL_USBA
|
|||
|
||||
The fifo_mode parameter is used to select endpoint allocation mode.
|
||||
fifo_mode = 0 is used to let the driver autoconfigure the endpoints.
|
||||
In this case 2 banks are allocated for isochronous endpoints and
|
||||
only one bank is allocated for the rest of the endpoints.
|
||||
In this case, for ep1 2 banks are allocated if it works in isochronous
|
||||
mode and only 1 bank otherwise. For the rest of the endpoints
|
||||
only 1 bank is allocated.
|
||||
|
||||
fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration
|
||||
allowing the usage of ep1 - ep6
|
||||
|
@ -191,6 +192,7 @@ config USB_RENESAS_USBHS_UDC
|
|||
config USB_RENESAS_USB3
|
||||
tristate 'Renesas USB3.0 Peripheral controller'
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
depends on EXTCON
|
||||
help
|
||||
Renesas USB3.0 Peripheral controller is a USB peripheral controller
|
||||
that supports super, high, and full speed USB 3.0 data transfers.
|
||||
|
@ -253,6 +255,20 @@ config USB_MV_U3D
|
|||
MARVELL PXA2128 Processor series include a super speed USB3.0 device
|
||||
controller, which support super speed USB peripheral.
|
||||
|
||||
config USB_SNP_CORE
|
||||
depends on USB_AMD5536UDC
|
||||
tristate
|
||||
help
|
||||
This enables core driver support for Synopsys USB 2.0 Device
|
||||
controller.
|
||||
|
||||
This will be enabled when PCI or Platform driver for this UDC is
|
||||
selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
|
||||
USB_AMD5536UDC options.
|
||||
|
||||
This IP is different to the High Speed OTG IP that can be enabled
|
||||
by selecting USB_DWC2 or USB_DWC3 options.
|
||||
|
||||
#
|
||||
# Controllers available in both integrated and discrete versions
|
||||
#
|
||||
|
@ -278,6 +294,7 @@ source "drivers/usb/gadget/udc/bdc/Kconfig"
|
|||
config USB_AMD5536UDC
|
||||
tristate "AMD5536 UDC"
|
||||
depends on USB_PCI
|
||||
select USB_SNP_CORE
|
||||
help
|
||||
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
|
||||
It is a USB Highspeed DMA capable USB device controller. Beside ep0
|
||||
|
@ -285,6 +302,9 @@ config USB_AMD5536UDC
|
|||
The UDC port supports OTG operation, and may be used as a host port
|
||||
if it's not being used to implement peripheral or OTG roles.
|
||||
|
||||
This UDC is based on Synopsys USB device controller IP and selects
|
||||
CONFIG_USB_SNP_CORE option to build the core driver.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "amd5536udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
|
|
@ -10,7 +10,8 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o
|
|||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
|
||||
obj-$(CONFIG_USB_SNP_CORE) += amd5536udc.o
|
||||
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
|
||||
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
|
||||
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
|
||||
obj-$(CONFIG_USB_GOKU) += goku_udc.o
|
||||
|
|
|
@ -11,27 +11,15 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536.
|
||||
* It is a USB Highspeed DMA capable USB device controller. Beside ep0 it
|
||||
* provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
|
||||
*
|
||||
* Make sure that UDC is assigned to port 4 by BIOS settings (port can also
|
||||
* be used as host port) and UOC bits PAD_EN and APU are set (should be done
|
||||
* by BIOS init).
|
||||
*
|
||||
* UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
|
||||
* work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
|
||||
* can be used with gadget ether.
|
||||
* This file does the core driver implementation for the UDC that is based
|
||||
* on Synopsys device controller IP (different than HS OTG IP) that is either
|
||||
* connected through PCI bus or integrated to SoC platforms.
|
||||
*/
|
||||
|
||||
/* debug control */
|
||||
/* #define UDC_VERBOSE */
|
||||
|
||||
/* Driver strings */
|
||||
#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
|
||||
#define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller"
|
||||
#define UDC_DRIVER_VERSION_STRING "01.00.0206"
|
||||
|
||||
/* system */
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -46,23 +34,12 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* gadget stack */
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* udc specific */
|
||||
#include "amd5536udc.h"
|
||||
|
||||
|
||||
static void udc_tasklet_disconnect(unsigned long);
|
||||
static void empty_req_queue(struct udc_ep *);
|
||||
static void udc_setup_endpoints(struct udc *dev);
|
||||
|
@ -72,7 +49,7 @@ static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
|
|||
|
||||
/* description */
|
||||
static const char mod_desc[] = UDC_MOD_DESCRIPTION;
|
||||
static const char name[] = "amd5536udc";
|
||||
static const char name[] = "udc";
|
||||
|
||||
/* structure to hold endpoint function pointers */
|
||||
static const struct usb_ep_ops udc_ep_ops;
|
||||
|
@ -208,30 +185,11 @@ static const struct {
|
|||
#undef EP_INFO
|
||||
};
|
||||
|
||||
/* DMA usage flag */
|
||||
static bool use_dma = 1;
|
||||
/* packet per buffer dma */
|
||||
static bool use_dma_ppb = 1;
|
||||
/* with per descr. update */
|
||||
static bool use_dma_ppb_du;
|
||||
/* buffer fill mode */
|
||||
static int use_dma_bufferfill_mode;
|
||||
/* full speed only mode */
|
||||
static bool use_fullspeed;
|
||||
/* tx buffer size for high speed */
|
||||
static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
|
||||
|
||||
/* module parameters */
|
||||
module_param(use_dma, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_dma, "true for DMA");
|
||||
module_param(use_dma_ppb, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
|
||||
module_param(use_dma_ppb_du, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_dma_ppb_du,
|
||||
"true for DMA in packet per buffer mode with descriptor update");
|
||||
module_param(use_fullspeed, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Prints UDC device registers and endpoint irq registers */
|
||||
static void print_regs(struct udc *dev)
|
||||
|
@ -267,7 +225,7 @@ static void print_regs(struct udc *dev)
|
|||
}
|
||||
|
||||
/* Masks unused interrupts */
|
||||
static int udc_mask_unused_interrupts(struct udc *dev)
|
||||
int udc_mask_unused_interrupts(struct udc *dev)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
|
@ -287,6 +245,7 @@ static int udc_mask_unused_interrupts(struct udc *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
|
||||
|
||||
/* Enables endpoint 0 interrupts */
|
||||
static int udc_enable_ep0_interrupts(struct udc *dev)
|
||||
|
@ -306,7 +265,7 @@ static int udc_enable_ep0_interrupts(struct udc *dev)
|
|||
}
|
||||
|
||||
/* Enables device interrupts for SET_INTF and SET_CONFIG */
|
||||
static int udc_enable_dev_setup_interrupts(struct udc *dev)
|
||||
int udc_enable_dev_setup_interrupts(struct udc *dev)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
|
@ -325,6 +284,7 @@ static int udc_enable_dev_setup_interrupts(struct udc *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
|
||||
|
||||
/* Calculates fifo start of endpoint based on preceding endpoints */
|
||||
static int udc_set_txfifo_addr(struct udc_ep *ep)
|
||||
|
@ -608,27 +568,23 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
|
|||
}
|
||||
|
||||
/* frees pci pool descriptors of a DMA chain */
|
||||
static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
|
||||
static void udc_free_dma_chain(struct udc *dev, struct udc_request *req)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct udc_data_dma *td;
|
||||
struct udc_data_dma *td_last = NULL;
|
||||
struct udc_data_dma *td = req->td_data;
|
||||
unsigned int i;
|
||||
|
||||
dma_addr_t addr_next = 0x00;
|
||||
dma_addr_t addr = (dma_addr_t)td->next;
|
||||
|
||||
DBG(dev, "free chain req = %p\n", req);
|
||||
|
||||
/* do not free first desc., will be done by free for request */
|
||||
td_last = req->td_data;
|
||||
td = phys_to_virt(td_last->next);
|
||||
|
||||
for (i = 1; i < req->chain_len; i++) {
|
||||
dma_pool_free(dev->data_requests, td,
|
||||
(dma_addr_t)td_last->next);
|
||||
td_last = td;
|
||||
td = phys_to_virt(td_last->next);
|
||||
td = phys_to_virt(addr);
|
||||
addr_next = (dma_addr_t)td->next;
|
||||
dma_pool_free(dev->data_requests, td, addr);
|
||||
addr = addr_next;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* Frees request packet, called by gadget driver */
|
||||
|
@ -1507,7 +1463,7 @@ static void make_ep_lists(struct udc *dev)
|
|||
}
|
||||
|
||||
/* Inits UDC context */
|
||||
static void udc_basic_init(struct udc *dev)
|
||||
void udc_basic_init(struct udc *dev)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
|
@ -1543,6 +1499,7 @@ static void udc_basic_init(struct udc *dev)
|
|||
dev->data_ep_enabled = 0;
|
||||
dev->data_ep_queued = 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udc_basic_init);
|
||||
|
||||
/* init registers at driver load time */
|
||||
static int startup_registers(struct udc *dev)
|
||||
|
@ -3031,7 +2988,7 @@ __acquires(dev->lock)
|
|||
}
|
||||
|
||||
/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
|
||||
static irqreturn_t udc_irq(int irq, void *pdev)
|
||||
irqreturn_t udc_irq(int irq, void *pdev)
|
||||
{
|
||||
struct udc *dev = pdev;
|
||||
u32 reg;
|
||||
|
@ -3083,16 +3040,18 @@ static irqreturn_t udc_irq(int irq, void *pdev)
|
|||
spin_unlock(&dev->lock);
|
||||
return ret_val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udc_irq);
|
||||
|
||||
/* Tears down device */
|
||||
static void gadget_release(struct device *pdev)
|
||||
void gadget_release(struct device *pdev)
|
||||
{
|
||||
struct amd5536udc *dev = dev_get_drvdata(pdev);
|
||||
kfree(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gadget_release);
|
||||
|
||||
/* Cleanup on device remove */
|
||||
static void udc_remove(struct udc *dev)
|
||||
void udc_remove(struct udc *dev)
|
||||
{
|
||||
/* remove timer */
|
||||
stop_timer++;
|
||||
|
@ -3108,9 +3067,10 @@ static void udc_remove(struct udc *dev)
|
|||
del_timer_sync(&udc_pollstall_timer);
|
||||
udc = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udc_remove);
|
||||
|
||||
/* free all the dma pools */
|
||||
static void free_dma_pools(struct udc *dev)
|
||||
void free_dma_pools(struct udc *dev)
|
||||
{
|
||||
dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
|
||||
dev->ep[UDC_EP0OUT_IX].td_phys);
|
||||
|
@ -3119,35 +3079,10 @@ static void free_dma_pools(struct udc *dev)
|
|||
dma_pool_destroy(dev->stp_requests);
|
||||
dma_pool_destroy(dev->data_requests);
|
||||
}
|
||||
|
||||
/* Reset all pci context */
|
||||
static void udc_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct udc *dev;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
/* gadget driver must not be registered */
|
||||
if (WARN_ON(dev->driver))
|
||||
return;
|
||||
|
||||
/* dma pool cleanup */
|
||||
free_dma_pools(dev);
|
||||
|
||||
/* reset controller */
|
||||
writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
|
||||
free_irq(pdev->irq, dev);
|
||||
iounmap(dev->virt_addr);
|
||||
release_mem_region(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
pci_disable_device(pdev);
|
||||
|
||||
udc_remove(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(free_dma_pools);
|
||||
|
||||
/* create dma pools on init */
|
||||
static int init_dma_pools(struct udc *dev)
|
||||
int init_dma_pools(struct udc *dev)
|
||||
{
|
||||
struct udc_stp_dma *td_stp;
|
||||
struct udc_data_dma *td_data;
|
||||
|
@ -3210,9 +3145,10 @@ static int init_dma_pools(struct udc *dev)
|
|||
dev->data_requests = NULL;
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(init_dma_pools);
|
||||
|
||||
/* general probe */
|
||||
static int udc_probe(struct udc *dev)
|
||||
int udc_probe(struct udc *dev)
|
||||
{
|
||||
char tmp[128];
|
||||
u32 reg;
|
||||
|
@ -3276,137 +3212,7 @@ static int udc_probe(struct udc *dev)
|
|||
finished:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Called by pci bus driver to init pci context */
|
||||
static int udc_pci_probe(
|
||||
struct pci_dev *pdev,
|
||||
const struct pci_device_id *id
|
||||
)
|
||||
{
|
||||
struct udc *dev;
|
||||
unsigned long resource;
|
||||
unsigned long len;
|
||||
int retval = 0;
|
||||
|
||||
/* one udc only */
|
||||
if (udc) {
|
||||
dev_dbg(&pdev->dev, "already probed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* init */
|
||||
dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* pci setup */
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
retval = -ENODEV;
|
||||
goto err_pcidev;
|
||||
}
|
||||
|
||||
/* PCI resource allocation */
|
||||
resource = pci_resource_start(pdev, 0);
|
||||
len = pci_resource_len(pdev, 0);
|
||||
|
||||
if (!request_mem_region(resource, len, name)) {
|
||||
dev_dbg(&pdev->dev, "pci device used already\n");
|
||||
retval = -EBUSY;
|
||||
goto err_memreg;
|
||||
}
|
||||
|
||||
dev->virt_addr = ioremap_nocache(resource, len);
|
||||
if (!dev->virt_addr) {
|
||||
dev_dbg(&pdev->dev, "start address cannot be mapped\n");
|
||||
retval = -EFAULT;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "irq not set\n");
|
||||
retval = -ENODEV;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
/* udc csr registers base */
|
||||
dev->csr = dev->virt_addr + UDC_CSR_ADDR;
|
||||
/* dev registers base */
|
||||
dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
|
||||
/* ep registers base */
|
||||
dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
|
||||
/* fifo's base */
|
||||
dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR);
|
||||
dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
|
||||
|
||||
if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
|
||||
dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
/* chip revision for Hs AMD5536 */
|
||||
dev->chiprev = pdev->revision;
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
/* init dma pools */
|
||||
if (use_dma) {
|
||||
retval = init_dma_pools(dev);
|
||||
if (retval != 0)
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
dev->phys_addr = resource;
|
||||
dev->irq = pdev->irq;
|
||||
dev->pdev = pdev;
|
||||
|
||||
/* general probing */
|
||||
if (udc_probe(dev)) {
|
||||
retval = -ENODEV;
|
||||
goto err_probe;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_probe:
|
||||
if (use_dma)
|
||||
free_dma_pools(dev);
|
||||
err_dma:
|
||||
free_irq(pdev->irq, dev);
|
||||
err_irq:
|
||||
iounmap(dev->virt_addr);
|
||||
err_ioremap:
|
||||
release_mem_region(resource, len);
|
||||
err_memreg:
|
||||
pci_disable_device(pdev);
|
||||
err_pcidev:
|
||||
kfree(dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* PCI device parameters */
|
||||
static const struct pci_device_id pci_id[] = {
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
|
||||
.class = PCI_CLASS_SERIAL_USB_DEVICE,
|
||||
.class_mask = 0xffffffff,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_id);
|
||||
|
||||
/* PCI functions */
|
||||
static struct pci_driver udc_pci_driver = {
|
||||
.name = (char *) name,
|
||||
.id_table = pci_id,
|
||||
.probe = udc_pci_probe,
|
||||
.remove = udc_pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(udc_pci_driver);
|
||||
EXPORT_SYMBOL_GPL(udc_probe);
|
||||
|
||||
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
||||
MODULE_AUTHOR("Thomas Dahlmann");
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
#ifndef AMD5536UDC_H
|
||||
#define AMD5536UDC_H
|
||||
|
||||
/* debug control */
|
||||
/* #define UDC_VERBOSE */
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* various constants */
|
||||
#define UDC_RDE_TIMER_SECONDS 1
|
||||
#define UDC_RDE_TIMER_DIV 10
|
||||
|
@ -567,6 +573,36 @@ union udc_setup_data {
|
|||
struct usb_ctrlrequest request;
|
||||
};
|
||||
|
||||
/* Function declarations */
|
||||
int udc_enable_dev_setup_interrupts(struct udc *dev);
|
||||
int udc_mask_unused_interrupts(struct udc *dev);
|
||||
irqreturn_t udc_irq(int irq, void *pdev);
|
||||
void gadget_release(struct device *pdev);
|
||||
void udc_basic_init(struct udc *dev);
|
||||
void free_dma_pools(struct udc *dev);
|
||||
int init_dma_pools(struct udc *dev);
|
||||
void udc_remove(struct udc *dev);
|
||||
int udc_probe(struct udc *dev);
|
||||
|
||||
/* DMA usage flag */
|
||||
static bool use_dma = 1;
|
||||
/* packet per buffer dma */
|
||||
static bool use_dma_ppb = 1;
|
||||
/* with per descr. update */
|
||||
static bool use_dma_ppb_du;
|
||||
/* full speed only mode */
|
||||
static bool use_fullspeed;
|
||||
|
||||
/* module parameters */
|
||||
module_param(use_dma, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_dma, "true for DMA");
|
||||
module_param(use_dma_ppb, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
|
||||
module_param(use_dma_ppb_du, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_dma_ppb_du,
|
||||
"true for DMA in packet per buffer mode with descriptor update");
|
||||
module_param(use_fullspeed, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
* SET and GET bitfields in u32 values
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller
|
||||
*
|
||||
* Copyright (C) 2005-2007 AMD (http://www.amd.com)
|
||||
* Author: Thomas Dahlmann
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536.
|
||||
* It is a USB Highspeed DMA capable USB device controller. Beside ep0 it
|
||||
* provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
|
||||
*
|
||||
* Make sure that UDC is assigned to port 4 by BIOS settings (port can also
|
||||
* be used as host port) and UOC bits PAD_EN and APU are set (should be done
|
||||
* by BIOS init).
|
||||
*
|
||||
* UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
|
||||
* work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
|
||||
* can be used with gadget ether.
|
||||
*
|
||||
* This file does pci device registration, and the core driver implementation
|
||||
* is done in amd5536udc.c
|
||||
*
|
||||
* The driver is split so as to use the core UDC driver which is based on
|
||||
* Synopsys device controller IP (different than HS OTG IP) in UDCs
|
||||
* integrated to SoC platforms.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Driver strings */
|
||||
#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
|
||||
|
||||
/* system */
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
/* udc specific */
|
||||
#include "amd5536udc.h"
|
||||
|
||||
/* pointer to device object */
|
||||
static struct udc *udc;
|
||||
|
||||
/* description */
|
||||
static const char mod_desc[] = UDC_MOD_DESCRIPTION;
|
||||
static const char name[] = "amd5536udc-pci";
|
||||
|
||||
/* Reset all pci context */
|
||||
static void udc_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct udc *dev;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
/* gadget driver must not be registered */
|
||||
if (WARN_ON(dev->driver))
|
||||
return;
|
||||
|
||||
/* dma pool cleanup */
|
||||
free_dma_pools(dev);
|
||||
|
||||
/* reset controller */
|
||||
writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
|
||||
free_irq(pdev->irq, dev);
|
||||
iounmap(dev->virt_addr);
|
||||
release_mem_region(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
pci_disable_device(pdev);
|
||||
|
||||
udc_remove(dev);
|
||||
}
|
||||
|
||||
/* Called by pci bus driver to init pci context */
|
||||
static int udc_pci_probe(
|
||||
struct pci_dev *pdev,
|
||||
const struct pci_device_id *id
|
||||
)
|
||||
{
|
||||
struct udc *dev;
|
||||
unsigned long resource;
|
||||
unsigned long len;
|
||||
int retval = 0;
|
||||
|
||||
/* one udc only */
|
||||
if (udc) {
|
||||
dev_dbg(&pdev->dev, "already probed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* init */
|
||||
dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* pci setup */
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
retval = -ENODEV;
|
||||
goto err_pcidev;
|
||||
}
|
||||
|
||||
/* PCI resource allocation */
|
||||
resource = pci_resource_start(pdev, 0);
|
||||
len = pci_resource_len(pdev, 0);
|
||||
|
||||
if (!request_mem_region(resource, len, name)) {
|
||||
dev_dbg(&pdev->dev, "pci device used already\n");
|
||||
retval = -EBUSY;
|
||||
goto err_memreg;
|
||||
}
|
||||
|
||||
dev->virt_addr = ioremap_nocache(resource, len);
|
||||
if (!dev->virt_addr) {
|
||||
dev_dbg(&pdev->dev, "start address cannot be mapped\n");
|
||||
retval = -EFAULT;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "irq not set\n");
|
||||
retval = -ENODEV;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
/* udc csr registers base */
|
||||
dev->csr = dev->virt_addr + UDC_CSR_ADDR;
|
||||
/* dev registers base */
|
||||
dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
|
||||
/* ep registers base */
|
||||
dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
|
||||
/* fifo's base */
|
||||
dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR);
|
||||
dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
|
||||
|
||||
if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
|
||||
dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
/* chip revision for Hs AMD5536 */
|
||||
dev->chiprev = pdev->revision;
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
/* init dma pools */
|
||||
if (use_dma) {
|
||||
retval = init_dma_pools(dev);
|
||||
if (retval != 0)
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
dev->phys_addr = resource;
|
||||
dev->irq = pdev->irq;
|
||||
dev->pdev = pdev;
|
||||
|
||||
/* general probing */
|
||||
if (udc_probe(dev)) {
|
||||
retval = -ENODEV;
|
||||
goto err_probe;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_probe:
|
||||
if (use_dma)
|
||||
free_dma_pools(dev);
|
||||
err_dma:
|
||||
free_irq(pdev->irq, dev);
|
||||
err_irq:
|
||||
iounmap(dev->virt_addr);
|
||||
err_ioremap:
|
||||
release_mem_region(resource, len);
|
||||
err_memreg:
|
||||
pci_disable_device(pdev);
|
||||
err_pcidev:
|
||||
kfree(dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* PCI device parameters */
|
||||
static const struct pci_device_id pci_id[] = {
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
|
||||
.class = PCI_CLASS_SERIAL_USB_DEVICE,
|
||||
.class_mask = 0xffffffff,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_id);
|
||||
|
||||
/* PCI functions */
|
||||
static struct pci_driver udc_pci_driver = {
|
||||
.name = (char *) name,
|
||||
.id_table = pci_id,
|
||||
.probe = udc_pci_probe,
|
||||
.remove = udc_pci_remove,
|
||||
};
|
||||
module_pci_driver(udc_pci_driver);
|
||||
|
||||
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
||||
MODULE_AUTHOR("Thomas Dahlmann");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -321,7 +321,6 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
|
|||
|
||||
static ushort fifo_mode;
|
||||
|
||||
/* "modprobe ... fifo_mode=1" etc */
|
||||
module_param(fifo_mode, ushort, 0x0);
|
||||
MODULE_PARM_DESC(fifo_mode, "Endpoint configuration mode");
|
||||
|
||||
|
@ -371,7 +370,7 @@ static struct usba_fifo_cfg mode_4_cfg[] = {
|
|||
};
|
||||
/* Add additional configurations here */
|
||||
|
||||
int usba_config_fifo_table(struct usba_udc *udc)
|
||||
static int usba_config_fifo_table(struct usba_udc *udc)
|
||||
{
|
||||
int n;
|
||||
|
||||
|
@ -1076,11 +1075,9 @@ static int atmel_usba_start(struct usb_gadget *gadget,
|
|||
struct usb_gadget_driver *driver);
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget);
|
||||
|
||||
static struct usb_ep *atmel_usba_match_ep(
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp
|
||||
)
|
||||
static struct usb_ep *atmel_usba_match_ep(struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp)
|
||||
{
|
||||
struct usb_ep *_ep;
|
||||
struct usba_ep *ep;
|
||||
|
@ -1100,7 +1097,6 @@ static struct usb_ep *atmel_usba_match_ep(
|
|||
ep = to_usba_ep(_ep);
|
||||
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
break;
|
||||
|
||||
|
@ -1141,7 +1137,7 @@ static struct usb_ep *atmel_usba_match_ep(
|
|||
ep->udc->configured_ep++;
|
||||
}
|
||||
|
||||
return _ep;
|
||||
return _ep;
|
||||
}
|
||||
|
||||
static const struct usb_gadget_ops usba_udc_ops = {
|
||||
|
@ -1855,8 +1851,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
* but it's clearly harmless...
|
||||
*/
|
||||
if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED))
|
||||
dev_dbg(&udc->pdev->dev,
|
||||
"ODD: EP0 configuration is invalid!\n");
|
||||
dev_err(&udc->pdev->dev,
|
||||
"ODD: EP0 configuration is invalid!\n");
|
||||
|
||||
/* Preallocate other endpoints */
|
||||
n = fifo_mode ? udc->num_ep : udc->configured_ep;
|
||||
|
@ -1864,8 +1860,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
ep = &udc->usba_ep[i];
|
||||
usba_ep_writel(ep, CFG, ep->ept_cfg);
|
||||
if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
|
||||
dev_dbg(&udc->pdev->dev,
|
||||
"ODD: EP%d configuration is invalid!\n", i);
|
||||
dev_err(&udc->pdev->dev,
|
||||
"ODD: EP%d configuration is invalid!\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2089,8 +2085,9 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
|||
while ((pp = of_get_next_child(np, pp)))
|
||||
udc->num_ep++;
|
||||
udc->configured_ep = 1;
|
||||
} else
|
||||
} else {
|
||||
udc->num_ep = usba_config_fifo_table(udc);
|
||||
}
|
||||
|
||||
eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep,
|
||||
GFP_KERNEL);
|
||||
|
@ -2118,14 +2115,34 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
|||
dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
ep->fifo_size = fifo_mode ? udc->fifo_cfg[i].fifo_size : val;
|
||||
if (fifo_mode) {
|
||||
if (val < udc->fifo_cfg[i].fifo_size) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Using max fifo-size value from DT\n");
|
||||
ep->fifo_size = val;
|
||||
} else {
|
||||
ep->fifo_size = udc->fifo_cfg[i].fifo_size;
|
||||
}
|
||||
} else {
|
||||
ep->fifo_size = val;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
ep->nr_banks = fifo_mode ? udc->fifo_cfg[i].nr_banks : val;
|
||||
if (fifo_mode) {
|
||||
if (val < udc->fifo_cfg[i].nr_banks) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Using max nb-banks value from DT\n");
|
||||
ep->nr_banks = val;
|
||||
} else {
|
||||
ep->nr_banks = udc->fifo_cfg[i].nr_banks;
|
||||
}
|
||||
} else {
|
||||
ep->nr_banks = val;
|
||||
}
|
||||
|
||||
ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
|
||||
ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
|
||||
|
|
|
@ -1273,6 +1273,7 @@ void usb_del_gadget_udc(struct usb_gadget *gadget)
|
|||
flush_work(&gadget->work);
|
||||
device_unregister(&udc->dev);
|
||||
device_unregister(&gadget->dev);
|
||||
memset(&gadget->dev, 0x00, sizeof(gadget->dev));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
|
||||
|
||||
|
|
|
@ -2062,16 +2062,13 @@ static int dummy_hub_control(
|
|||
}
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
if (hcd->speed == HCD_USB3) {
|
||||
if (dum_hcd->port_status & USB_PORT_STAT_POWER)
|
||||
dev_dbg(dummy_dev(dum_hcd),
|
||||
"power-off\n");
|
||||
} else
|
||||
if (dum_hcd->port_status &
|
||||
USB_SS_PORT_STAT_POWER)
|
||||
dev_dbg(dummy_dev(dum_hcd),
|
||||
"power-off\n");
|
||||
/* FALLS THROUGH */
|
||||
dev_dbg(dummy_dev(dum_hcd), "power-off\n");
|
||||
if (hcd->speed == HCD_USB3)
|
||||
dum_hcd->port_status &= ~USB_SS_PORT_STAT_POWER;
|
||||
else
|
||||
dum_hcd->port_status &= ~USB_PORT_STAT_POWER;
|
||||
set_link_state(dum_hcd);
|
||||
break;
|
||||
default:
|
||||
dum_hcd->port_status &= ~(1 << wValue);
|
||||
set_link_state(dum_hcd);
|
||||
|
@ -2242,14 +2239,13 @@ static int dummy_hub_control(
|
|||
if ((dum_hcd->port_status &
|
||||
USB_SS_PORT_STAT_POWER) != 0) {
|
||||
dum_hcd->port_status |= (1 << wValue);
|
||||
set_link_state(dum_hcd);
|
||||
}
|
||||
} else
|
||||
if ((dum_hcd->port_status &
|
||||
USB_PORT_STAT_POWER) != 0) {
|
||||
dum_hcd->port_status |= (1 << wValue);
|
||||
set_link_state(dum_hcd);
|
||||
}
|
||||
set_link_state(dum_hcd);
|
||||
}
|
||||
break;
|
||||
case GetPortErrorCount:
|
||||
|
|
|
@ -2675,6 +2675,8 @@ static const struct platform_device_id fsl_udc_devtype[] = {
|
|||
.name = "imx-udc-mx27",
|
||||
}, {
|
||||
.name = "imx-udc-mx51",
|
||||
}, {
|
||||
.name = "fsl-usb2-udc",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
|
|
|
@ -1835,13 +1835,18 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
}
|
||||
|
||||
/* we will access controller register, so enable the u3d controller */
|
||||
clk_enable(u3d->clk);
|
||||
retval = clk_enable(u3d->clk);
|
||||
if (retval) {
|
||||
dev_err(&dev->dev, "clk_enable error %d\n", retval);
|
||||
goto err_u3d_enable;
|
||||
}
|
||||
|
||||
if (pdata->phy_init) {
|
||||
retval = pdata->phy_init(u3d->phy_regs);
|
||||
if (retval) {
|
||||
dev_err(&dev->dev, "init phy error %d\n", retval);
|
||||
goto err_u3d_enable;
|
||||
clk_disable(u3d->clk);
|
||||
goto err_phy_init;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1974,15 +1979,13 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
dma_free_coherent(&dev->dev, u3d->ep_context_size,
|
||||
u3d->ep_context, u3d->ep_context_dma);
|
||||
err_alloc_ep_context:
|
||||
if (pdata->phy_deinit)
|
||||
pdata->phy_deinit(u3d->phy_regs);
|
||||
clk_disable(u3d->clk);
|
||||
err_phy_init:
|
||||
err_u3d_enable:
|
||||
iounmap(u3d->cap_regs);
|
||||
err_map_cap_regs:
|
||||
err_get_cap_regs:
|
||||
err_get_clk:
|
||||
clk_put(u3d->clk);
|
||||
err_get_clk:
|
||||
kfree(u3d);
|
||||
err_alloc_private:
|
||||
err_pdata:
|
||||
|
|
|
@ -445,7 +445,8 @@ static int mv_ep_enable(struct usb_ep *_ep,
|
|||
struct mv_dqh *dqh;
|
||||
u16 max = 0;
|
||||
u32 bit_pos, epctrlx, direction;
|
||||
unsigned char zlt = 0, ios = 0, mult = 0;
|
||||
const unsigned char zlt = 1;
|
||||
unsigned char ios, mult;
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct mv_ep, ep);
|
||||
|
@ -465,8 +466,6 @@ static int mv_ep_enable(struct usb_ep *_ep,
|
|||
* disable HW zero length termination select
|
||||
* driver handles zero length packet through req->req.zero
|
||||
*/
|
||||
zlt = 1;
|
||||
|
||||
bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
|
||||
|
||||
/* Check if the Endpoint is Primed */
|
||||
|
@ -481,16 +480,16 @@ static int mv_ep_enable(struct usb_ep *_ep,
|
|||
(unsigned)bit_pos);
|
||||
goto en_done;
|
||||
}
|
||||
|
||||
/* Set the max packet length, interrupt on Setup and Mult fields */
|
||||
ios = 0;
|
||||
mult = 0;
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
zlt = 1;
|
||||
mult = 0;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
ios = 1;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
mult = 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
/* Calculate transactions needed for high bandwidth iso */
|
||||
|
|
|
@ -1608,9 +1608,6 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void udc_enable(struct pxa_udc *udc);
|
||||
static void udc_disable(struct pxa_udc *udc);
|
||||
|
||||
/**
|
||||
* pxa_udc_vbus_session - Called by external transceiver to enable/disable udc
|
||||
* @_gadget: usb gadget
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -37,6 +38,9 @@
|
|||
#define USB3_USB_INT_ENA_2 0x22c
|
||||
#define USB3_STUP_DAT_0 0x230
|
||||
#define USB3_STUP_DAT_1 0x234
|
||||
#define USB3_USB_OTG_STA 0x268
|
||||
#define USB3_USB_OTG_INT_STA 0x26c
|
||||
#define USB3_USB_OTG_INT_ENA 0x270
|
||||
#define USB3_P0_MOD 0x280
|
||||
#define USB3_P0_CON 0x288
|
||||
#define USB3_P0_STA 0x28c
|
||||
|
@ -124,6 +128,9 @@
|
|||
/* USB_INT_ENA_2 and USB_INT_STA_2 */
|
||||
#define USB_INT_2_PIPE(n) BIT(n)
|
||||
|
||||
/* USB_OTG_STA, USB_OTG_INT_STA and USB_OTG_INT_ENA */
|
||||
#define USB_OTG_IDMON BIT(4)
|
||||
|
||||
/* P0_MOD */
|
||||
#define P0_MOD_DIR BIT(6)
|
||||
|
||||
|
@ -257,6 +264,8 @@ struct renesas_usb3 {
|
|||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct extcon_dev *extcon;
|
||||
struct work_struct extcon_work;
|
||||
|
||||
struct renesas_usb3_ep *usb3_ep;
|
||||
int num_usb3_eps;
|
||||
|
@ -269,6 +278,8 @@ struct renesas_usb3 {
|
|||
u8 ep0_buf[USB3_EP0_BUF_SIZE];
|
||||
bool softconnect;
|
||||
bool workaround_for_vbus;
|
||||
bool extcon_host; /* check id and set EXTCON_USB_HOST */
|
||||
bool extcon_usb; /* check vbus and set EXTCON_USB */
|
||||
};
|
||||
|
||||
#define gadget_to_renesas_usb3(_gadget) \
|
||||
|
@ -332,6 +343,15 @@ static int usb3_wait(struct renesas_usb3 *usb3, u32 reg, u32 mask,
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void renesas_usb3_extcon_work(struct work_struct *work)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = container_of(work, struct renesas_usb3,
|
||||
extcon_work);
|
||||
|
||||
extcon_set_state_sync(usb3->extcon, EXTCON_USB_HOST, usb3->extcon_host);
|
||||
extcon_set_state_sync(usb3->extcon, EXTCON_USB, usb3->extcon_usb);
|
||||
}
|
||||
|
||||
static void usb3_enable_irq_1(struct renesas_usb3 *usb3, u32 bits)
|
||||
{
|
||||
usb3_set_bit(usb3, bits, USB3_USB_INT_ENA_1);
|
||||
|
@ -352,6 +372,11 @@ static void usb3_disable_pipe_irq(struct renesas_usb3 *usb3, int num)
|
|||
usb3_clear_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2);
|
||||
}
|
||||
|
||||
static bool usb3_is_host(struct renesas_usb3 *usb3)
|
||||
{
|
||||
return !(usb3_read(usb3, USB3_DRD_CON) & DRD_CON_PERI_CON);
|
||||
}
|
||||
|
||||
static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
|
||||
{
|
||||
/* Set AXI_INT */
|
||||
|
@ -362,10 +387,6 @@ static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
|
|||
|
||||
static void usb3_init_epc_registers(struct renesas_usb3 *usb3)
|
||||
{
|
||||
/* FIXME: How to change host / peripheral mode as well? */
|
||||
usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
|
||||
usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
|
||||
|
||||
usb3_write(usb3, ~0, USB3_USB_INT_STA_1);
|
||||
usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
|
||||
}
|
||||
|
@ -531,18 +552,70 @@ static void usb3_check_vbus(struct renesas_usb3 *usb3)
|
|||
if (usb3->workaround_for_vbus) {
|
||||
usb3_connect(usb3);
|
||||
} else {
|
||||
if (usb3_read(usb3, USB3_USB_STA) & USB_STA_VBUS_STA)
|
||||
usb3->extcon_usb = !!(usb3_read(usb3, USB3_USB_STA) &
|
||||
USB_STA_VBUS_STA);
|
||||
if (usb3->extcon_usb)
|
||||
usb3_connect(usb3);
|
||||
else
|
||||
usb3_disconnect(usb3);
|
||||
|
||||
schedule_work(&usb3->extcon_work);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
|
||||
{
|
||||
if (host)
|
||||
usb3_clear_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
|
||||
else
|
||||
usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
|
||||
}
|
||||
|
||||
static void usb3_vbus_out(struct renesas_usb3 *usb3, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
usb3_set_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
|
||||
else
|
||||
usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
|
||||
}
|
||||
|
||||
static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&usb3->lock, flags);
|
||||
usb3_set_mode(usb3, host);
|
||||
usb3_vbus_out(usb3, a_dev);
|
||||
if (!host && a_dev) /* for A-Peripheral */
|
||||
usb3_connect(usb3);
|
||||
spin_unlock_irqrestore(&usb3->lock, flags);
|
||||
}
|
||||
|
||||
static bool usb3_is_a_device(struct renesas_usb3 *usb3)
|
||||
{
|
||||
return !(usb3_read(usb3, USB3_USB_OTG_STA) & USB_OTG_IDMON);
|
||||
}
|
||||
|
||||
static void usb3_check_id(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3->extcon_host = usb3_is_a_device(usb3);
|
||||
|
||||
if (usb3->extcon_host)
|
||||
usb3_mode_config(usb3, true, true);
|
||||
else
|
||||
usb3_mode_config(usb3, false, false);
|
||||
|
||||
schedule_work(&usb3->extcon_work);
|
||||
}
|
||||
|
||||
static void renesas_usb3_init_controller(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3_init_axi_bridge(usb3);
|
||||
usb3_init_epc_registers(usb3);
|
||||
usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_STA);
|
||||
usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_ENA);
|
||||
|
||||
usb3_check_id(usb3);
|
||||
usb3_check_vbus(usb3);
|
||||
}
|
||||
|
||||
|
@ -551,6 +624,7 @@ static void renesas_usb3_stop_controller(struct renesas_usb3 *usb3)
|
|||
usb3_disconnect(usb3);
|
||||
usb3_write(usb3, 0, USB3_P0_INT_ENA);
|
||||
usb3_write(usb3, 0, USB3_PN_INT_ENA);
|
||||
usb3_write(usb3, 0, USB3_USB_OTG_INT_ENA);
|
||||
usb3_write(usb3, 0, USB3_USB_INT_ENA_1);
|
||||
usb3_write(usb3, 0, USB3_USB_INT_ENA_2);
|
||||
usb3_write(usb3, 0, USB3_AXI_INT_ENA);
|
||||
|
@ -1474,10 +1548,22 @@ static void usb3_irq_epc_int_2(struct renesas_usb3 *usb3, u32 int_sta_2)
|
|||
}
|
||||
}
|
||||
|
||||
static void usb3_irq_idmon_change(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3_check_id(usb3);
|
||||
}
|
||||
|
||||
static void usb3_irq_otg_int(struct renesas_usb3 *usb3, u32 otg_int_sta)
|
||||
{
|
||||
if (otg_int_sta & USB_OTG_IDMON)
|
||||
usb3_irq_idmon_change(usb3);
|
||||
}
|
||||
|
||||
static void usb3_irq_epc(struct renesas_usb3 *usb3)
|
||||
{
|
||||
u32 int_sta_1 = usb3_read(usb3, USB3_USB_INT_STA_1);
|
||||
u32 int_sta_2 = usb3_read(usb3, USB3_USB_INT_STA_2);
|
||||
u32 otg_int_sta = usb3_read(usb3, USB3_USB_OTG_INT_STA);
|
||||
|
||||
int_sta_1 &= usb3_read(usb3, USB3_USB_INT_ENA_1);
|
||||
if (int_sta_1) {
|
||||
|
@ -1488,6 +1574,12 @@ static void usb3_irq_epc(struct renesas_usb3 *usb3)
|
|||
int_sta_2 &= usb3_read(usb3, USB3_USB_INT_ENA_2);
|
||||
if (int_sta_2)
|
||||
usb3_irq_epc_int_2(usb3, int_sta_2);
|
||||
|
||||
otg_int_sta &= usb3_read(usb3, USB3_USB_OTG_INT_ENA);
|
||||
if (otg_int_sta) {
|
||||
usb3_write(usb3, otg_int_sta, USB3_USB_OTG_INT_STA);
|
||||
usb3_irq_otg_int(usb3, otg_int_sta);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t renesas_usb3_irq(int irq, void *_usb3)
|
||||
|
@ -1756,11 +1848,49 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = {
|
|||
.set_selfpowered = renesas_usb3_set_selfpowered,
|
||||
};
|
||||
|
||||
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
|
||||
bool new_mode_is_host;
|
||||
|
||||
if (!usb3->driver)
|
||||
return -ENODEV;
|
||||
|
||||
if (!strncmp(buf, "host", strlen("host")))
|
||||
new_mode_is_host = true;
|
||||
else if (!strncmp(buf, "peripheral", strlen("peripheral")))
|
||||
new_mode_is_host = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (new_mode_is_host == usb3_is_host(usb3))
|
||||
return -EINVAL;
|
||||
|
||||
usb3_mode_config(usb3, new_mode_is_host, usb3_is_a_device(usb3));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t role_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
|
||||
|
||||
if (!usb3->driver)
|
||||
return -ENODEV;
|
||||
|
||||
return sprintf(buf, "%s\n", usb3_is_host(usb3) ? "host" : "peripheral");
|
||||
}
|
||||
static DEVICE_ATTR_RW(role);
|
||||
|
||||
/*------- platform_driver ------------------------------------------------*/
|
||||
static int renesas_usb3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_role);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
|
@ -1894,6 +2024,12 @@ static const struct of_device_id usb3_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, usb3_of_match);
|
||||
|
||||
static const unsigned int renesas_usb3_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static int renesas_usb3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct renesas_usb3 *usb3;
|
||||
|
@ -1937,6 +2073,17 @@ static int renesas_usb3_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
INIT_WORK(&usb3->extcon_work, renesas_usb3_extcon_work);
|
||||
usb3->extcon = devm_extcon_dev_allocate(&pdev->dev, renesas_usb3_cable);
|
||||
if (IS_ERR(usb3->extcon))
|
||||
return PTR_ERR(usb3->extcon);
|
||||
|
||||
ret = devm_extcon_dev_register(&pdev->dev, usb3->extcon);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register extcon\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* for ep0 handling */
|
||||
usb3->ep0_req = __renesas_usb3_ep_alloc_request(GFP_KERNEL);
|
||||
if (!usb3->ep0_req)
|
||||
|
@ -1946,6 +2093,10 @@ static int renesas_usb3_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto err_add_udc;
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_role);
|
||||
if (ret < 0)
|
||||
goto err_dev_create;
|
||||
|
||||
usb3->workaround_for_vbus = priv->workaround_for_vbus;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
@ -1955,6 +2106,9 @@ static int renesas_usb3_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
err_dev_create:
|
||||
usb_del_gadget_udc(&usb3->gadget);
|
||||
|
||||
err_add_udc:
|
||||
__renesas_usb3_ep_free_request(usb3->ep0_req);
|
||||
|
||||
|
|
|
@ -223,25 +223,25 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
|
|||
return 0;
|
||||
|
||||
otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
|
||||
ret = extcon_register_notifier(edev, EXTCON_USB,
|
||||
ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB,
|
||||
&otg_sx->vbus_nb);
|
||||
if (ret < 0)
|
||||
dev_err(ssusb->dev, "failed to register notifier for USB\n");
|
||||
|
||||
otg_sx->id_nb.notifier_call = ssusb_id_notifier;
|
||||
ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
|
||||
ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST,
|
||||
&otg_sx->id_nb);
|
||||
if (ret < 0)
|
||||
dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
|
||||
|
||||
dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
|
||||
extcon_get_cable_state_(edev, EXTCON_USB),
|
||||
extcon_get_cable_state_(edev, EXTCON_USB_HOST));
|
||||
extcon_get_state(edev, EXTCON_USB),
|
||||
extcon_get_state(edev, EXTCON_USB_HOST));
|
||||
|
||||
/* default as host, switch to device mode if needed */
|
||||
if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == false)
|
||||
if (extcon_get_state(edev, EXTCON_USB_HOST) == false)
|
||||
ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
|
||||
if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
|
||||
if (extcon_get_state(edev, EXTCON_USB) == true)
|
||||
ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
|
||||
|
||||
return 0;
|
||||
|
@ -367,13 +367,6 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
|
|||
|
||||
cancel_delayed_work(&otg_sx->extcon_reg_dwork);
|
||||
|
||||
if (otg_sx->edev) {
|
||||
extcon_unregister_notifier(otg_sx->edev,
|
||||
EXTCON_USB, &otg_sx->vbus_nb);
|
||||
extcon_unregister_notifier(otg_sx->edev,
|
||||
EXTCON_USB_HOST, &otg_sx->id_nb);
|
||||
}
|
||||
|
||||
if (otg_sx->manual_drd_enabled)
|
||||
ssusb_debugfs_exit(ssusb);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,13 @@
|
|||
|
||||
#include "phy-fsl-usb.h"
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
|
||||
__func__, ## args)
|
||||
#else
|
||||
#define VDBG(stuff...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define DRIVER_VERSION "Rev. 1.55"
|
||||
#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
|
||||
#define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
|
||||
|
|
|
@ -21,21 +21,6 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#undef VERBOSE
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
|
||||
__func__, ## args)
|
||||
#else
|
||||
#define VDBG(stuff...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
|
||||
#else
|
||||
#define MPC_LOC do {} while (0)
|
||||
#endif
|
||||
|
||||
#define PROTO_UNDEF (0)
|
||||
#define PROTO_HOST (1)
|
||||
#define PROTO_GADGET (2)
|
||||
|
|
|
@ -158,7 +158,7 @@ struct usb_ext_prop_desc {
|
|||
* |-----+-----------------------+------+-------------------------------------|
|
||||
* | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st|
|
||||
* | | | | interface in an IAD group |
|
||||
* | 1 | Reserved | U8 | 0 |
|
||||
* | 1 | Reserved | U8 | 1 |
|
||||
* | 2 | CompatibleID | U8[8]| compatible ID string |
|
||||
* | 10 | SubCompatibleID | U8[8]| subcompatible ID string |
|
||||
* | 18 | Reserved | U8[6]| 0 |
|
||||
|
|
Loading…
Reference in New Issue