Commit 7338a065 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'usb-for-v3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

usb: patches for v3.15

another substantial pull request with new features all over
the place.

dwc3 got a bit closer towards hibernation support with after
a few patches re-factoring code to be reused for hibernation.
Also in dwc3 two new workarounds for known silicon bugs have
been implemented, some randconfig build errors have been fixed,
and it was taught about the new generic phy layer.

MUSB on AM335x now supports isochronous transfers thanks to
George Cherian's work.

The atmel_usba driver got two crash fixes: one when no endpoint
was specified in DeviceTree data and another when stopping the UDC
in DEBUG builds.

Function FS got a much needed fix to ffs_epfile_io() which was
copying too much data to userspace in some cases.

The printer gadget got a fix for a possible deadlock and plugged
a memory leak.

Ethernet drivers now use NAPI for RX which gives improved throughput.

Other than that, the usual miscelaneous fixes, cleanups, and
the like.
Signed-of-by: default avatarFelipe Balbi <balbi@ti.com>
parents c9050b64 4b76e14d
USB PHY
TI PHY: DT DOCUMENTATION FOR PHYs in TI PLATFORMs
OMAP CONTROL PHY
Required properties:
- compatible: Should be one of
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
e.g. USB2_PHY on OMAP5.
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
e.g. USB3 PHY and SATA PHY on OMAP5.
"ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
DRA7 platform.
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
AM437 platform.
- reg : Address and length of the register set for the device. It contains
the address of "otghs_control" for control-phy-otghs or "power" register
for other types.
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
other types.
omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,control-phy-otghs";
reg = <0x4a00233c 0x4>;
reg-names = "otghs_control";
};
OMAP USB2 PHY
......@@ -21,15 +46,22 @@ usb2phy@4a0ad080 {
#phy-cells = <0>;
};
OMAP USB3 PHY
TI PIPE3 PHY
Required properties:
- compatible: Should be "ti,omap-usb3"
- compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
"ti,omap-usb3" is deprecated.
- reg : Address and length of the register set for the device.
- reg-names: The names of the register addresses corresponding to the registers
filled in "reg".
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
clock-names.
- clock-names: should include:
* "wkupclk" - wakeup clock.
* "sysclk" - system clock.
* "refclk" - reference clock.
Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
......@@ -38,11 +70,17 @@ Optional properties:
This is usually a subnode of ocp2scp to which it is connected.
usb3phy@4a084400 {
compatible = "ti,omap-usb3";
compatible = "ti,phy-usb3";
reg = <0x4a084400 0x80>,
<0x4a084800 0x64>,
<0x4a084c00 0x40>;
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_usb>;
#phy-cells = <0>;
clocks = <&usb_phy_cm_clk32k>,
<&sys_clkin>,
<&usb_otg_ss_refclk960m>;
clock-names = "wkupclk",
"sysclk",
"refclk";
};
......@@ -6,11 +6,13 @@ Required properties:
- compatible: must be "snps,dwc3"
- reg : Address and length of the register set for the device
- interrupts: Interrupts used by the dwc3 controller.
Optional properties:
- usb-phy : array of phandle for the PHY device. The first element
in the array is expected to be a handle to the USB2/HS PHY and
the second element is expected to be a handle to the USB3/SS PHY
Optional properties:
- phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
This is usually a subnode to DWC3 glue to which it is connected.
......
* Freescale MXS USB Phy Device
Required properties:
- compatible: Should be "fsl,imx23-usbphy"
- compatible: should contain:
* "fsl,imx23-usbphy" for imx23 and imx28
* "fsl,imx6q-usbphy" for imx6dq and imx6dl
* "fsl,imx6sl-usbphy" for imx6sl
"fsl,imx23-usbphy" is still a fallback for other strings
- reg: Should contain registers location and length
- interrupts: Should contain phy interrupt
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
Example:
usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
reg = <0x020c9000 0x1000>;
interrupts = <0 44 0x04>;
fsl,anatop = <&anatop>;
};
......@@ -76,27 +76,3 @@ omap_dwc3 {
ranges;
};
OMAP CONTROL USB
Required properties:
- compatible: Should be one of
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
e.g. USB2_PHY on OMAP5.
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
e.g. USB3 PHY and SATA PHY on OMAP5.
"ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on
DRA7 platform.
"ti,control-phy-am437usb2" - if it has power down register like USB2 PHY on
AM437 platform.
- reg : Address and length of the register set for the device. It contains
the address of "otghs_control" for control-phy-otghs or "power" register
for other types.
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
other types.
omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,control-phy-otghs";
reg = <0x4a00233c 0x4>;
reg-names = "otghs_control";
};
......@@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
static void dwc3_core_soft_reset(struct dwc3 *dwc)
static int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
int ret;
/* Before Resetting PHY, put Core in Reset */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
......@@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
usb_phy_init(dwc->usb2_phy);
usb_phy_init(dwc->usb3_phy);
ret = phy_init(dwc->usb2_generic_phy);
if (ret < 0)
return ret;
ret = phy_init(dwc->usb3_generic_phy);
if (ret < 0) {
phy_exit(dwc->usb2_generic_phy);
return ret;
}
mdelay(100);
/* Clear USB3 PHY reset */
......@@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
return 0;
}
/**
......@@ -242,6 +254,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
}
}
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
{
if (!dwc->has_hibernation)
return 0;
if (!dwc->nr_scratch)
return 0;
dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
if (!dwc->scratchbuf)
return -ENOMEM;
return 0;
}
static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
{
dma_addr_t scratch_addr;
u32 param;
int ret;
if (!dwc->has_hibernation)
return 0;
if (!dwc->nr_scratch)
return 0;
/* should never fall here */
if (!WARN_ON(dwc->scratchbuf))
return 0;
scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(dwc->dev, scratch_addr)) {
dev_err(dwc->dev, "failed to map scratch buffer\n");
ret = -EFAULT;
goto err0;
}
dwc->scratch_addr = scratch_addr;
param = lower_32_bits(scratch_addr);
ret = dwc3_send_gadget_generic_command(dwc,
DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
if (ret < 0)
goto err1;
param = upper_32_bits(scratch_addr);
ret = dwc3_send_gadget_generic_command(dwc,
DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
if (ret < 0)
goto err1;
return 0;
err1:
dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
err0:
return ret;
}
static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
{
if (!dwc->has_hibernation)
return;
if (!dwc->nr_scratch)
return;
/* should never fall here */
if (!WARN_ON(dwc->scratchbuf))
return;
dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
kfree(dwc->scratchbuf);
}
static void dwc3_core_num_eps(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
......@@ -277,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
static int dwc3_core_init(struct dwc3 *dwc)
{
unsigned long timeout;
u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
int ret;
......@@ -306,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
cpu_relax();
} while (true);
dwc3_core_soft_reset(dwc);
ret = dwc3_core_soft_reset(dwc);
if (ret)
goto err0;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
......@@ -314,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc)
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
/**
* WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
* issue which would cause xHCI compliance tests to fail.
*
* Because of that we cannot enable clock gating on such
* configurations.
*
* Refers to:
*
* STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
* SOF/ITP Mode Used
*/
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
dwc->dr_mode == USB_DR_MODE_OTG) &&
(dwc->revision >= DWC3_REVISION_210A &&
dwc->revision <= DWC3_REVISION_250A))
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
else
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
break;
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
/* enable hibernation here */
dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
break;
default:
dev_dbg(dwc->dev, "No power optimization available\n");
......@@ -333,16 +454,36 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
goto err1;
ret = dwc3_setup_scratch_buffers(dwc);
if (ret)
goto err2;
return 0;
err2:
dwc3_free_scratch_buffers(dwc);
err1:
usb_phy_shutdown(dwc->usb2_phy);
usb_phy_shutdown(dwc->usb3_phy);
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
err0:
return ret;
}
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_free_scratch_buffers(dwc);
usb_phy_shutdown(dwc->usb2_phy);
usb_phy_shutdown(dwc->usb3_phy);
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
}
#define DWC3_ALIGN_MASK (16 - 1)
......@@ -411,32 +552,52 @@ static int dwc3_probe(struct platform_device *pdev)
if (IS_ERR(dwc->usb2_phy)) {
ret = PTR_ERR(dwc->usb2_phy);
/*
* if -ENXIO is returned, it means PHY layer wasn't
* enabled, so it makes no sense to return -EPROBE_DEFER
* in that case, since no PHY driver will ever probe.
*/
if (ret == -ENXIO)
if (ret == -ENXIO || ret == -ENODEV) {
dwc->usb2_phy = NULL;
} else if (ret == -EPROBE_DEFER) {
return ret;
dev_err(dev, "no usb2 phy configured\n");
return -EPROBE_DEFER;
} else {
dev_err(dev, "no usb2 phy configured\n");
return ret;
}
}
if (IS_ERR(dwc->usb3_phy)) {
ret = PTR_ERR(dwc->usb3_phy);
if (ret == -ENXIO || ret == -ENODEV) {
dwc->usb3_phy = NULL;
} else if (ret == -EPROBE_DEFER) {
return ret;
} else {
dev_err(dev, "no usb3 phy configured\n");
return ret;
}
}
/*
* if -ENXIO is returned, it means PHY layer wasn't
* enabled, so it makes no sense to return -EPROBE_DEFER
* in that case, since no PHY driver will ever probe.
*/
if (ret == -ENXIO)
dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
if (IS_ERR(dwc->usb2_generic_phy)) {
ret = PTR_ERR(dwc->usb2_generic_phy);
if (ret == -ENOSYS || ret == -ENODEV) {
dwc->usb2_generic_phy = NULL;
} else if (ret == -EPROBE_DEFER) {
return ret;
} else {
dev_err(dev, "no usb2 phy configured\n");
return ret;
}
}
dev_err(dev, "no usb3 phy configured\n");
return -EPROBE_DEFER;
dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
if (IS_ERR(dwc->usb3_generic_phy)) {
ret = PTR_ERR(dwc->usb3_generic_phy);
if (ret == -ENOSYS || ret == -ENODEV) {
dwc->usb3_generic_phy = NULL;
} else if (ret == -EPROBE_DEFER) {
return ret;
} else {
dev_err(dev, "no usb3 phy configured\n");
return ret;
}
}
dwc->xhci_resources[0].start = res->start;
......@@ -479,6 +640,14 @@ static int dwc3_probe(struct platform_device *pdev)
goto err0;
}
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
dwc->dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
dwc->dr_mode = USB_DR_MODE_OTG;
ret = dwc3_core_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize core\n");
......@@ -487,21 +656,20 @@ static int dwc3_probe(struct platform_device *pdev)
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0)
goto err1;
ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0)
goto err_usb2phy_power;
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
goto err1;
goto err_usb3phy_power;
}
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
dwc->dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
dwc->dr_mode = USB_DR_MODE_OTG;
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
......@@ -568,6 +736,12 @@ static int dwc3_probe(struct platform_device *pdev)
err2:
dwc3_event_buffers_cleanup(dwc);
err_usb3phy_power:
phy_power_off(dwc->usb3_generic_phy);
err_usb2phy_power:
phy_power_off(dwc->usb2_generic_phy);
err1:
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
......@@ -585,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev)
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
......@@ -682,6 +858,8 @@ static int dwc3_suspend(struct device *dev)
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
return 0;
}
......@@ -690,9 +868,17 @@ static int dwc3_resume(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
int ret;
usb_phy_init(dwc->usb3_phy);
usb_phy_init(dwc->usb2_phy);
ret = phy_init(dwc->usb2_generic_phy);
if (ret < 0)
return ret;
ret = phy_init(dwc->usb3_generic_phy);
if (ret < 0)
goto err_usb2phy_init;
spin_lock_irqsave(&dwc->lock, flags);
......@@ -716,6 +902,11 @@ static int dwc3_resume(struct device *dev)
pm_runtime_enable(dev);
return 0;
err_usb2phy_init:
phy_exit(dwc->usb2_generic_phy);
return ret;
}
static const struct dev_pm_ops dwc3_dev_pm_ops = {
......
......@@ -31,11 +31,14 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
/* Global constants */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
#define DWC3_EVENT_SIZE 4 /* bytes */
#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
......@@ -157,6 +160,7 @@
#define DWC3_GCTL_PRTCAP_OTG 3
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
#define DWC3_GCTL_SOFITPSYNC (1 << 10)
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
......@@ -318,7 +322,7 @@
/* 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_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
......@@ -393,6 +397,7 @@ struct dwc3_event_buffer {
* @busy_slot: first slot which is owned by HW
* @desc: usb_endpoint_descriptor pointer
* @dwc: pointer to DWC controller
* @saved_state: ep state saved during hibernation
* @flags: endpoint flags (wedged, stalled, ...)
* @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
......@@ -415,6 +420,7 @@ struct dwc3_ep {
const struct usb_ss_ep_comp_descriptor *comp_desc;
struct dwc3 *dwc;
u32 saved_state;
unsigned flags;
#define DWC3_EP_ENABLED (1 << 0)
#define DWC3_EP_STALL (1 << 1)
......@@ -598,6 +604,7 @@ struct dwc3_scratchpad_array {
* @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
* @lock: for synchronizing
* @dev: pointer to our struct device
* @xhci: pointer to our xHCI child
......@@ -606,6 +613,7 @@ struct dwc3_scratchpad_array {
* @gadget_driver: pointer to the gadget driver
* @regs: base address for our registers
* @regs_size: address space size
* @nr_scratch: number of scratch buffers
* @num_event_buffers: calculated number of event buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
......@@ -613,16 +621,10 @@ struct dwc3_scratchpad_array {
* @dr_mode: requested mode of operation
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
* @usb3_generic_phy: pointer to USB3 PHY
* @dcfg: saved contents of DCFG register
* @gctl: saved contents of GCTL register
* @is_selfpowered: true when we are selfpowered
* @three_stage_setup: set if we perform a three phase setup
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
* @start_config_issued: true when StartConfig command has been issued
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @isoch_delay: wValue from Set Isochronous Delay request;
* @u2sel: parameter from Set SEL request.
* @u2pel: parameter from Set SEL request.
......@@ -637,15 +639,31 @@ struct dwc3_scratchpad_array {
* @mem: points to start of memory which is used for this struct.
* @hwparams: copy of hwparams registers
* @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
* @test_mode: true when we're entering a USB test mode
* @test_mode_nr: test feature selector
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
* @has_hibernation: true when dwc3 was configured with Hibernation
* @is_selfpowered: true when we are selfpowered
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @pullups_connected: true when Run/Stop bit is set
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
void *ep0_bounce;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
/* device lock */
......@@ -665,6 +683,9 @@ struct dwc3 {
struct usb_phy *usb2_phy;
struct usb_phy *usb3_phy;
struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy;
void __iomem *regs;
size_t regs_size;
......@@ -674,6 +695,7 @@ struct dwc3 {
u32 dcfg;
u32 gctl;
u32 nr_scratch;
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
......@@ -695,17 +717,9 @@ struct dwc3 {
#define DWC3_REVISION_230A 0x5533230a
#define DWC3_REVISION_240A 0x5533240a
#define DWC3_REVISION_250A 0x5533250a
unsigned is_selfpowered:1;
unsigned three_stage_setup:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
unsigned start_config_issued:1;
unsigned setup_packet_pending:1;
unsigned delayed_status:1;
unsigned needs_fifo_resize:1;
unsigned resize_fifos:1;
unsigned pullups_connected:1;
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
......@@ -730,6 +744,18 @@ struct dwc3 {
u8 test_mode;
u8 test_mode_nr;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
unsigned has_hibernation:1;
unsigned is_selfpowered:1;
unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
unsigned start_config_issued:1;
unsigned three_stage_setup:1;
};
/* -------------------------------------------------------------------------- */
......@@ -815,15 +841,15 @@ struct dwc3_event_depevt {
* 12 - VndrDevTstRcved
* @reserved15_12: Reserved, not used
* @event_info: Information about this event
* @reserved31_24: Reserved, not used
* @reserved31_25: Reserved, not used
*/
struct dwc3_event_devt {
u32 one_bit:1;
u32 device_event:7;
u32 type:4;
u32 reserved15_12:4;
u32 event_info:8;
u32 reserved31_24:8;
u32 event_info:9;
u32 reserved31_25:7;
} __packed;
/**
......@@ -856,6 +882,19 @@ union dwc3_event {
struct dwc3_event_gevt gevt;
};
/**
* struct dwc3_gadget_ep_cmd_params - representation of endpoint command
* parameters
* @param2: third parameter
* @param1: second parameter
* @param0: first parameter
*/
struct dwc3_gadget_ep_cmd_params {
u32 param2;
u32 param1;
u32 param0;
};
/*
* DWC3 Features to be used as Driver Data
*/
......@@ -881,11 +920,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc)
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_gadget_init(struct dwc3 *dwc);
void dwc3_gadget_exit(struct dwc3 *dwc);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
{ }
static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{ return 0; }
static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
{ return 0; }
static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
enum dwc3_link_state state)
{ return 0; }
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{ return 0; }
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
int cmd, u32 param)
{ return 0; }
#endif
/* power management interface */
......
......@@ -424,11 +424,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory base resource\n");
return -EINVAL;
}
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
......
This diff is collapsed.
......@@ -56,12 +56,6 @@ struct dwc3;
/* DEPXFERCFG parameter 0 */
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
struct dwc3_gadget_ep_cmd_params {
u32 param2;
u32 param1;
u32 param0;
};
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
......@@ -85,9 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
......@@ -95,9 +86,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
......
......@@ -301,7 +301,6 @@ config USB_PXA27X
gadget drivers to also be dynamically linked.
config USB_S3C_HSOTG
depends on ARM
tristate "Designware/S3C HS/OtG USB Device controller"
help
The Designware USB2.0 high-speed gadget controller
......
......@@ -1758,15 +1758,15 @@ static int at91udc_probe(struct platform_device *pdev)
/* newer chips have more FIFO memory than rm9200 */
if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) {
usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512);
usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512);
udc->ep[0].maxpacket = 64;
udc->ep[3].maxpacket = 64;
udc->ep[4].maxpacket = 512;
udc->ep[5].maxpacket = 512;
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
udc->ep[3].maxpacket = 64;
} else if (cpu_is_at91sam9263()) {
usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
udc->ep[0].maxpacket = 64;
udc->ep[3].maxpacket = 64;
}
udc->udp_baseaddr = ioremap(res->start, resource_size(res));
......
......@@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
if (dma_status) {
int i;
for (i = 1; i < USBA_NR_ENDPOINTS; i++)
for (i = 1; i < USBA_NR_DMAS; i++)
if (dma_status & (1 << i))
usba_dma_irq(udc, &udc->usba_ep[i]);
}
......@@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
if (ep_status) {
int i;
for (i = 0; i < USBA_NR_ENDPOINTS; i++)
for (i = 0; i < udc->num_ep; i++)
if (ep_status & (1 << i)) {
if (ep_is_control(&udc->usba_ep[i]))
usba_control_irq(udc, &udc->usba_ep[i]);
......@@ -1827,12 +1827,12 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
toggle_bias(0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
udc->driver = NULL;
clk_disable_unprepare(udc->hclk);
clk_disable_unprepare(udc->pclk);
DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name);
DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name);
udc->driver = NULL;
return 0;
}
......@@ -1914,6 +1914,12 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
i++;
}
if (i == 0) {
dev_err(&pdev->dev, "of_probe: no endpoint specified\n");
ret = -EINVAL;
goto err;
}
return eps;
err:
return ERR_PTR(ret);
......
......@@ -210,7 +210,7 @@
#define USBA_FIFO_BASE(x) ((x) << 16)
/* Synth parameters */
#define USBA_NR_ENDPOINTS 7
#define USBA_NR_DMAS 7
#define EP0_FIFO_SIZE 64
#define EP0_EPT_SIZE USBA_EPT_SIZE_64
......
This diff is collapsed.
......@@ -225,14 +225,8 @@ static void gr_dfs_create(struct gr_udc *dev)
const char *name = "gr_udc_state";
dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
if (IS_ERR(dev->dfs_root)) {
dev_err(dev->dev, "Failed to create debugfs directory\n");
return;
}
dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root,
dev, &gr_dfs_fops);
if (IS_ERR(dev->dfs_state))
dev_err(dev->dev, "Failed to create debugfs file %s\n", name);
dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev,
&gr_dfs_fops);
}
static void gr_dfs_delete(struct gr_udc *dev)
......
......@@ -427,12 +427,17 @@ setup_rx_reqs(struct printer_dev *dev)
req->length = USB_BUFSIZE;
req->complete = rx_complete;
/* here, we unlock, and only unlock, to avoid deadlock. */
spin_unlock(&dev->lock);
error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock);
if (error) {
DBG(dev, "rx submit --> %d\n", error);
list_add(&req->list, &dev->rx_reqs);
break;
} else {
}
/* if the req is empty, then add it into dev->rx_reqs_active. */
else if (list_empty(&req->list)) {
list_add(&req->list, &dev->rx_reqs_active);
}
}
......@@ -1133,6 +1138,7 @@ static int __init printer_bind_config(struct usb_configuration *c)
NULL, "g_printer");
if (IS_ERR(dev->pdev)) {
ERROR(dev, "Failed to create device: g_printer\n");
status = PTR_ERR(dev->pdev);
goto fail;
}
......
......@@ -617,7 +617,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
to_write = DIV_ROUND_UP(to_write, 4);
data = hs_req->req.buf + buf_pos;
writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
return (to_write >= can_write) ? -ENOSPC : 0;
}
......@@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
ureq->length, ureq->actual);
if (0)
dev_dbg(hsotg->dev,
"REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n",
ureq->buf, length, ureq->dma,
"REQ buf %p len %d dma 0x%pad noi=%d zp=%d snok=%d\n",
ureq->buf, length, &ureq->dma,
ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
maxreq = get_ep_limit(hs_ep);
......@@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
writel(ureq->dma, hsotg->regs + dma_reg);
dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n",
__func__, ureq->dma, dma_reg);
dev_dbg(hsotg->dev, "%s: 0x%pad => 0x%08x\n",
__func__, &ureq->dma, dma_reg);
}
ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */
......@@ -1185,6 +1185,41 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
/**
* s3c_hsotg_stall_ep0 - stall ep0
* @hsotg: The device state
*
* Set stall for ep0 as response for setup request.
*/
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) {
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
u32 reg;
u32 ctrl;
dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
/*
* DxEPCTL_Stall will be cleared by EP once it has
* taken effect, so no need to clear later.
*/
ctrl = readl(hsotg->regs + reg);
ctrl |= DxEPCTL_Stall;
ctrl |= DxEPCTL_CNAK;
writel(ctrl, hsotg->regs + reg);
dev_dbg(hsotg->dev,
"written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
ctrl, reg, readl(hsotg->regs + reg));
/*
* complete won't be called, so we enqueue
* setup request here
*/
s3c_hsotg_enqueue_setup(hsotg);
}
/**
* s3c_hsotg_process_control - process a control request
* @hsotg: The device state
......@@ -1262,38 +1297,8 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
* so respond with a STALL for the status stage to indicate failure.
*/
if (ret < 0) {
u32 reg;
u32 ctrl;
dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
/*
* DxEPCTL_Stall will be cleared by EP once it has
* taken effect, so no need to clear later.
*/
ctrl = readl(hsotg->regs + reg);
ctrl |= DxEPCTL_Stall;
ctrl |= DxEPCTL_CNAK;
writel(ctrl, hsotg->regs + reg);
dev_dbg(hsotg->dev,
"written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
ctrl, reg, readl(hsotg->regs + reg));
/*
* don't believe we need to anything more to get the EP
* to reply with a STALL packet
*/
/*
* complete won't be called, so we enqueue
* setup request here
*/
s3c_hsotg_enqueue_setup(hsotg);
}
if (ret < 0)
s3c_hsotg_stall_ep0(hsotg);
}
/**
......@@ -1488,7 +1493,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
* note, we might over-write the buffer end by 3 bytes depending on
* alignment of the data.
*/
readsl(fifo, hs_req->req.buf + read_ptr, to_read);
ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read);
}
/**
......@@ -2832,6 +2837,15 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
if (index == 0) {
if (value)
s3c_hsotg_stall_ep0(hs);
else
dev_warn(hs->dev,
"%s: can't clear halt on ep0\n", __func__);
return 0;
}
/* write both IN and OUT control registers */
epreg = DIEPCTL(index);
......@@ -3760,10 +3774,55 @@ static int s3c_hsotg_remove(struct platform_device *pdev)
return 0;
}
#if 1
#define s3c_hsotg_suspend NULL
#define s3c_hsotg_resume NULL
#endif
static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
{
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
unsigned long flags;
int ret = 0;
if (hsotg->driver)
dev_info(hsotg->dev, "suspending usb gadget %s\n",
hsotg->driver->driver.name);
spin_lock_irqsave(&hsotg->lock, flags);
s3c_hsotg_disconnect(hsotg);
s3c_hsotg_phy_disable(hsotg);
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock_irqrestore(&hsotg->lock, flags);
if (hsotg->driver) {
int ep;
for (ep = 0; ep < hsotg->num_of_eps; ep++)
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
}
return ret;
}
static int s3c_hsotg_resume(struct platform_device *pdev)
{
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
unsigned long flags;
int ret = 0;
if (hsotg->driver) {
dev_info(hsotg->dev, "resuming usb gadget %s\n",
hsotg->driver->driver.name);
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
}
spin_lock_irqsave(&hsotg->lock, flags);
hsotg->last_rst = jiffies;
s3c_hsotg_phy_enable(hsotg);
s3c_hsotg_core_init(hsotg);
spin_unlock_irqrestore(&hsotg->lock, flags);
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id s3c_hsotg_of_ids[] = {
......
......@@ -1344,7 +1344,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
return 0;
err_add_udc:
err_add_device:
clk_disable(hsudc->uclk);
err_res:
if (!IS_ERR_OR_NULL(hsudc->transceiver))
......
......@@ -48,6 +48,8 @@
#define UETH__VERSION "29-May-2008"
#define GETHER_NAPI_WEIGHT 32
struct eth_dev {
/* lock is held while accessing port_usb
*/
......@@ -72,6 +74,7 @@ struct eth_dev {
struct sk_buff_head *list);
struct work_struct work;
struct napi_struct rx_napi;
unsigned long todo;
#define WORK_RX_MEMORY 0
......@@ -253,18 +256,16 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
DBG(dev, "rx submit --> %d\n", retval);
if (skb)
dev_kfree_skb_any(skb);
spin_lock_irqsave(&dev->req_lock, flags);
list_add(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
}
return retval;
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct sk_buff *skb = req->context, *skb2;
struct sk_buff *skb = req->context;
struct eth_dev *dev = ep->driver_data;
int status = req->status;
bool rx_queue = 0;
switch (status) {
......@@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
} else {
skb_queue_tail(&dev->rx_frames, skb);
}
skb = NULL;
skb2 = skb_dequeue(&dev->rx_frames);
while (skb2) {
if (status < 0
|| ETH_HLEN > skb2->len
|| skb2->len > VLAN_ETH_FRAME_LEN) {
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
DBG(dev, "rx length %d\n", skb2->len);
dev_kfree_skb_any(skb2);
goto next_frame;
}
skb2->protocol = eth_type_trans(skb2, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb2->len;
/* no buffer copies needed, unless hardware can't
* use skb buffers.
*/
status = netif_rx(skb2);
next_frame:
skb2 = skb_dequeue(&dev->rx_frames);
}
if (!status)
rx_queue = 1;
break;
/* software-driven interface shutdown */
......@@ -334,22 +313,20 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
/* FALLTHROUGH */
default:
rx_queue = 1;
dev_kfree_skb_any(skb);
dev->net->stats.rx_errors++;
DBG(dev, "rx status %d\n", status);
break;
}
if (skb)
dev_kfree_skb_any(skb);
if (!netif_running(dev->net)) {
clean:
spin_lock(&dev->req_lock);
list_add(&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock);
req = NULL;
}
if (req)
rx_submit(dev, req, GFP_ATOMIC);
if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi)))
__napi_schedule(&dev->rx_napi);
}
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
......@@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
struct usb_request *req;
unsigned long flags;
int rx_counts = 0;
/* fill unused rxq slots with some skb */
spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) {
if (++rx_counts > qlen(dev->gadget, dev->qmult))
break;
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags);
if (rx_submit(dev, req, gfp_flags) < 0) {
spin_lock_irqsave(&dev->req_lock, flags);
list_add(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
defer_kevent(dev, WORK_RX_MEMORY);
return;
}
......@@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
spin_unlock_irqrestore(&dev->req_lock, flags);
}
static int gether_poll(struct napi_struct *napi, int budget)
{
struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi);
struct sk_buff *skb;
unsigned int work_done = 0;
int status = 0;
while ((skb = skb_dequeue(&dev->rx_frames))) {
if (status < 0
|| ETH_HLEN > skb->len
|| skb->len > VLAN_ETH_FRAME_LEN) {
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
DBG(dev, "rx length %d\n", skb->len);
dev_kfree_skb_any(skb);
continue;
}
skb->protocol = eth_type_trans(skb, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
status = netif_rx_ni(skb);
}
if (netif_running(dev->net)) {
rx_fill(dev, GFP_KERNEL);
work_done++;
}
if (work_done < budget)
napi_complete(&dev->rx_napi);
return work_done;
}
static void eth_work(struct work_struct *work)
{
struct eth_dev *dev = container_of(work, struct eth_dev, work);
......@@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
/* and open the tx floodgates */
atomic_set(&dev->tx_qlen, 0);
netif_wake_queue(dev->net);
napi_enable(&dev->rx_napi);
}
static int eth_open(struct net_device *net)
......@@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net)
unsigned long flags;
VDBG(dev, "%s\n", __func__);
napi_disable(&dev->rx_napi);
netif_stop_queue(net);
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
......@@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
return ERR_PTR(-ENOMEM);
dev = netdev_priv(net);
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work);
......@@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname)
return ERR_PTR(-ENOMEM);
dev = netdev_priv(net);
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work);
......@@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link)
{
struct eth_dev *dev = link->ioport;
struct usb_request *req;
struct sk_buff *skb;
WARN_ON(!dev);
if (!dev)
......@@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link)
spin_lock(&dev->req_lock);
}
spin_unlock(&dev->req_lock);
spin_lock(&dev->rx_frames.lock);
while ((skb = __skb_dequeue(&dev->rx_frames)))
dev_kfree_skb_any(skb);
spin_unlock(&dev->rx_frames.lock);
link->in_ep->driver_data = NULL;
link->in_ep->desc = NULL;
......
......@@ -65,10 +65,8 @@ static inline void ffs_dev_unlock(void)
mutex_unlock(&ffs_lock);
}
struct ffs_dev *ffs_alloc_dev(void);
int ffs_name_dev(struct ffs_dev *dev, const char *name);
int ffs_single_dev(struct ffs_dev *dev);
void ffs_free_dev(struct ffs_dev *dev);
struct ffs_epfile;
struct ffs_function;
......@@ -125,7 +123,7 @@ enum ffs_setup_state {
* setup. If this state is set read/write on ep0 return
* -EIDRM. This state is only set when adding event.
*/
FFS_SETUP_CANCELED
FFS_SETUP_CANCELLED
};
struct ffs_data {
......@@ -156,7 +154,6 @@ struct ffs_data {
*/
struct usb_request *ep0req; /* P: mutex */
struct completion ep0req_completion; /* P: mutex */
int ep0req_status; /* P: mutex */
/* reference counter */
atomic_t ref;
......@@ -168,19 +165,18 @@ struct ffs_data {
/*
* Possible transitions:
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
* happens only in ep0 read which is P: mutex
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
* happens only in ep0 i/o which is P: mutex
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
* + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock
* + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg
*
* This field should never be accessed directly and instead
* ffs_setup_state_clear_cancelled function should be used.
*/
enum ffs_setup_state setup_state;
#define FFS_SETUP_STATE(ffs) \
((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
FFS_SETUP_CANCELED, FFS_NO_SETUP))
/* Events & such. */
struct {
u8 types[4];
......@@ -210,16 +206,16 @@ struct ffs_data {
/* filled by __ffs_data_got_descs() */
/*
* Real descriptors are 16 bytes after raw_descs (so you need
* to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
* first full speed descriptor). raw_descs_length and
* raw_fs_descs_length do not have those 16 bytes added.
* raw_descs is what you kfree, real_descs points inside of raw_descs,
* where full speed, high speed and super speed descriptors start.
* real_descs_length is the length of all those descriptors.
*/
const void *raw_descs_data;
const void *raw_descs;
unsigned raw_descs_length;
unsigned raw_fs_descs_length;
unsigned fs_descs_count;
unsigned hs_descs_count;
unsigned ss_descs_count;
unsigned short strings_count;
unsigned short interfaces_count;
......
......@@ -43,6 +43,7 @@ config USB_MUSB_HOST
config USB_MUSB_GADGET
bool "Gadget only mode"
depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC
depends on HAS_DMA
help
Select this when you want to use MUSB in gadget mode only,
thereby the host feature will be regressed.
......@@ -50,6 +51,7 @@ config USB_MUSB_GADGET
config USB_MUSB_DUAL_ROLE
bool "Dual Role mode"
depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC))
depends on HAS_DMA
help
This is the default mode of working of MUSB controller where
both host and gadget features are enabled.
......
......@@ -438,7 +438,6 @@ void musb_hnp_stop(struct musb *musb)
static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
u8 devctl)
{
struct usb_otg *otg = musb->xceiv->otg;
irqreturn_t handled = IRQ_NONE;
dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl,
......@@ -656,7 +655,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
break;
case OTG_STATE_B_PERIPHERAL:
musb_g_suspend(musb);
musb->is_active = otg->gadget->b_hnp_enable;
musb->is_active = musb->g.b_hnp_enable;
if (musb->is_active) {
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
......@@ -672,7 +671,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
break;
case OTG_STATE_A_HOST:
musb->xceiv->state = OTG_STATE_A_SUSPEND;
musb->is_active = otg->host->b_hnp_enable;
musb->is_active = musb->hcd->self.b_hnp_enable;
break;
case OTG_STATE_B_HOST:
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
......
......@@ -39,6 +39,7 @@ struct cppi41_dma_channel {
u32 transferred;
u32 packet_sz;
struct list_head tx_check;
struct work_struct dma_completion;
};
#define MUSB_DMA_NUM_CHANNELS 15
......@@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
return true;
}
static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
{
if (in && hw_ep->in_qh) {
if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
return true;
} else if (hw_ep->out_qh) {
if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
return true;
}
return false;
}
static void cppi41_dma_callback(void *private_data);
static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
......@@ -119,7 +132,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
struct musb *musb = hw_ep->musb;
if (!cppi41_channel->prog_len) {
if (!cppi41_channel->prog_len ||
(cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) {
/* done, complete */
cppi41_channel->channel.actual_len =
......@@ -165,6 +179,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
}
}
static void cppi_trans_done_work(struct work_struct *work)
{
unsigned long flags;
struct cppi41_dma_channel *cppi41_channel =
container_of(work, struct cppi41_dma_channel, dma_completion);
struct cppi41_dma_controller *controller = cppi41_channel->controller;
struct musb *musb = controller->musb;
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
bool empty;
if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
spin_lock_irqsave(&musb->lock, flags);
cppi41_trans_done(cppi41_channel);
spin_unlock_irqrestore(&musb->lock, flags);
} else {
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
spin_lock_irqsave(&musb->lock, flags);
cppi41_trans_done(cppi41_channel);
spin_unlock_irqrestore(&musb->lock, flags);
} else {
schedule_work(&cppi41_channel->dma_completion);
}
}
}
static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
{
struct cppi41_dma_controller *controller;
......@@ -228,6 +268,14 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
if (!cppi41_channel->is_tx) {
if (is_isoc(hw_ep, 1))
schedule_work(&cppi41_channel->dma_completion);
else
cppi41_trans_done(cppi41_channel);
goto out;
}
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
cppi41_trans_done(cppi41_channel);
......@@ -264,6 +312,10 @@ static void cppi41_dma_callback(void *private_data)
goto out;
}
}
if (is_isoc(hw_ep, 0)) {
schedule_work(&cppi41_channel->dma_completion);
goto out;
}
list_add_tail(&cppi41_channel->tx_check,
&controller->early_tx_list);
if (!hrtimer_active(&controller->early_tx)) {
......@@ -448,12 +500,25 @@ static int cppi41_dma_channel_program(struct dma_channel *channel,
dma_addr_t dma_addr, u32 len)
{
int ret;
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
int hb_mult = 0;
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
channel->status == MUSB_DMA_STATUS_BUSY);
if (is_host_active(cppi41_channel->controller->musb)) {
if (cppi41_channel->is_tx)
hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult;
else
hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult;
}
channel->status = MUSB_DMA_STATUS_BUSY;
channel->actual_len = 0;
if (hb_mult)
packet_sz = hb_mult * (packet_sz & 0x7FF);
ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len);
if (!ret)
channel->status = MUSB_DMA_STATUS_FREE;
......@@ -607,6 +672,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
cppi41_channel->port_num = port;
cppi41_channel->is_tx = is_tx;
INIT_LIST_HEAD(&cppi41_channel->tx_check);
INIT_WORK(&cppi41_channel->dma_completion,
cppi_trans_done_work);
musb_dma = &cppi41_channel->channel;
musb_dma->private_data = cppi41_channel;
......
......@@ -45,6 +45,8 @@
#include <linux/of_irq.h>
#include <linux/usb/of.h>
#include <linux/debugfs.h>
#include "musb_core.h"
static const struct of_device_id musb_dsps_of_match[];
......@@ -136,6 +138,26 @@ struct dsps_glue {
unsigned long last_timer; /* last timer data for each instance */
struct dsps_context context;
struct debugfs_regset32 regset;
struct dentry *dbgfs_root;
};
static const struct debugfs_reg32 dsps_musb_regs[] = {
{ "revision", 0x00 },
{ "control", 0x14 },
{ "status", 0x18 },
{ "eoi", 0x24 },
{ "intr0_stat", 0x30 },
{ "intr1_stat", 0x34 },
{ "intr0_set", 0x38 },
{ "intr1_set", 0x3c },
{ "txmode", 0x70 },
{ "rxmode", 0x74 },
{ "autoreq", 0xd0 },
{ "srpfixtime", 0xd4 },
{ "tdown", 0xd8 },
{ "phy_utmi", 0xe0 },
{ "mode", 0xe8 },
};
static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
......@@ -368,6 +390,30 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
return ret;
}
static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue)
{
struct dentry *root;
struct dentry *file;
char buf[128];
sprintf(buf, "%s.dsps", dev_name(musb->controller));
root = debugfs_create_dir(buf, NULL);
if (!root)
return -ENOMEM;
glue->dbgfs_root = root;
glue->regset.regs = dsps_musb_regs;
glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs);
glue->regset.base = musb->ctrl_base;
file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset);
if (!file) {
debugfs_remove_recursive(root);
return -ENOMEM;
}
return 0;
}
static int dsps_musb_init(struct musb *musb)
{
struct device *dev = musb->controller;
......@@ -377,6 +423,7 @@ static int dsps_musb_init(struct musb *musb)
void __iomem *reg_base;
struct resource *r;
u32 rev, val;
int ret;
r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control");
if (!r)
......@@ -410,6 +457,10 @@ static int dsps_musb_init(struct musb *musb)
val &= ~(1 << wrp->otg_disable);
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
ret = dsps_musb_dbg_init(musb, glue);
if (ret)
return ret;
return 0;
}
......@@ -616,7 +667,7 @@ static int dsps_probe(struct platform_device *pdev)
wrp = match->data;
/* allocate glue */
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "unable to allocate glue memory\n");
return -ENOMEM;
......@@ -644,7 +695,6 @@ static int dsps_probe(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
err2:
pm_runtime_disable(&pdev->dev);
kfree(glue);
return ret;
}
......@@ -657,7 +707,9 @@ static int dsps_remove(struct platform_device *pdev)
/* disable usbss clocks */
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
kfree(glue);
debugfs_remove_recursive(glue->dbgfs_root);
return 0;
}
......
......@@ -1694,7 +1694,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
defined(CONFIG_USB_TI_CPPI41_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
......@@ -1707,10 +1708,30 @@ void musb_host_rx(struct musb *musb, u8 epnum)
if (d->status != -EILSEQ && d->status != -EOVERFLOW)
d->status = 0;
if (++qh->iso_idx >= urb->number_of_packets)
if (++qh->iso_idx >= urb->number_of_packets) {
done = true;
else
} else {
#if defined(CONFIG_USB_TI_CPPI41_DMA)
struct dma_controller *c;
dma_addr_t *buf;
u32 length, ret;
c = musb->dma_controller;
buf = (void *)
urb->iso_frame_desc[qh->iso_idx].offset
+ (u32)urb->transfer_dma;
length =
urb->iso_frame_desc[qh->iso_idx].length;
val |= MUSB_RXCSR_DMAENAB;
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
ret = c->channel_program(dma, qh->maxpacket,
0, (u32) buf, length);
#endif
done = false;
}
} else {
/* done if urb buffer is full or short packet is recd */
......@@ -1750,7 +1771,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
defined(CONFIG_USB_TI_CPPI41_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
......
......@@ -317,10 +317,12 @@ int otg_statemachine(struct otg_fsm *fsm)
otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
break;
case OTG_STATE_A_HOST:
if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
if (fsm->id || fsm->a_bus_drop)
otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
fsm->otg->host->b_hnp_enable)
otg_set_state(fsm, OTG_STATE_A_SUSPEND);
else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
else if (!fsm->b_conn)
otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
else if (!fsm->a_vbus_vld)
otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
......@@ -346,8 +348,7 @@ int otg_statemachine(struct otg_fsm *fsm)
otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
break;
case OTG_STATE_A_WAIT_VFALL:
if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req ||
(!fsm->a_sess_vld && !fsm->b_conn))
if (fsm->a_wait_vfall_tmout)
otg_set_state(fsm, OTG_STATE_A_IDLE);
break;
case OTG_STATE_A_VBUS_ERR:
......
This diff is collapsed.
......@@ -177,15 +177,15 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
struct clk *clk;
int retval;
pdata = dev_get_platdata(&pdev->dev);
pdata = dev_get_platdata(dev);
if (!pdata) {
dev_err(dev, "No platform data\n");
return -EINVAL;
}
clk = devm_clk_get(&pdev->dev, "usbhs");
clk = devm_clk_get(dev, "usbhs");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Can't get the clock\n");
dev_err(dev, "Can't get the clock\n");
return PTR_ERR(clk);
}
......
......@@ -111,6 +111,13 @@ struct usb_phy {
int (*set_suspend)(struct usb_phy *x,
int suspend);
/*
* Set wakeup enable for PHY, in that case, the PHY can be
* woken up from suspend status due to external events,
* like vbus change, dp/dm change and id.
*/
int (*set_wakeup)(struct usb_phy *x, bool enabled);
/* notify phy connect status change */
int (*notify_connect)(struct usb_phy *x,
enum usb_device_speed speed);
......@@ -264,6 +271,15 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend)
return 0;
}
static inline int
usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
{
if (x && x->set_wakeup)
return x->set_wakeup(x, enabled);
else
return 0;
}
static inline int
usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
{
......
......@@ -10,9 +10,15 @@
enum {
FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
FUNCTIONFS_STRINGS_MAGIC = 2
FUNCTIONFS_STRINGS_MAGIC = 2,
FUNCTIONFS_DESCRIPTORS_MAGIC_V2 = 3,
};
enum functionfs_flags {
FUNCTIONFS_HAS_FS_DESC = 1,
FUNCTIONFS_HAS_HS_DESC = 2,
FUNCTIONFS_HAS_SS_DESC = 4,
};
#ifndef __KERNEL__
......@@ -28,30 +34,40 @@ struct usb_endpoint_descriptor_no_audio {
} __attribute__((packed));
/*
* All numbers must be in little endian order.
*/
struct usb_functionfs_descs_head {
__le32 magic;
__le32 length;
__le32 fs_count;
__le32 hs_count;
} __attribute__((packed));
/*
* Descriptors format:
*
* | off | name | type | description |
* |-----+-----------+--------------+--------------------------------------|
* | 0 | magic | LE32 | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |
* | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2 |
* | 4 | length | LE32 | length of the whole data chunk |
* | 8 | flags | LE32 | combination of functionfs_flags |
* | | fs_count | LE32 | number of full-speed descriptors |
* | | hs_count | LE32 | number of high-speed descriptors |
* | | ss_count | LE32 | number of super-speed descriptors |
* | | fs_descrs | Descriptor[] | list of full-speed descriptors |
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
*
* Depending on which flags are set, various fields may be missing in the
* structure. Any flags that are not recognised cause the whole block to be
* rejected with -ENOSYS.
*
* Legacy descriptors format:
*
* | off | name | type | description |
* |-----+-----------+--------------+--------------------------------------|
* | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC |
* | 4 | length | LE32 | length of the whole data chunk |
* | 8 | fs_count | LE32 | number of full-speed descriptors |
* | 12 | hs_count | LE32 | number of high-speed descriptors |
* | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors |
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
*
* descs are just valid USB descriptors and have the following format:
* All numbers must be in little endian order.
*
* Descriptor[] is an array of valid USB descriptors which have the following
* format:
*
* | off | name | type | description |
* |-----+-----------------+------+--------------------------|
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment