提交 2d4d9f35 编写于 作者: G Greg Kroah-Hartman

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

Felipe writes:

usb: patches for v3.19 merge window

This time, a very pull request with 216 non-merge
commits. Most of the commits contained here are
sparse or coccinelle fixes ranging from missing
'static' to returning 0 in case of errors.

More importantly, we have the removal the now
unnecessary 'driver' argument to ->udc_stop().

DWC2 learned about Dual-Role builds. Users of
this IP can now have a single driver built for
host and device roles.

DWC3 got support for two new HW platforms: Exynos7
and AMD.

The Broadcom USB 3.0 Device Controller IP is now
supported and so is PLX USB338x, which means DWC3
has lost is badge as the only USB 3.0 peripheral
IP supported on Linux.

Thanks for Tony Lindgren's work, we can now have
a distro-like kernel where all MUSB glue layers
can be built into the same kernel (statically
or dynamically linked) and it'll work in PIO (DMA
will come probably on v3.20).

Other than these, the usual set of cleanups and
non-critical fixes.
Signed-off-by: NFelipe Balbi <balbi@ti.com>
What: /sys/class/udc/<udc>/a_alt_hnp_support
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates if an OTG A-Host supports HNP at an alternate port.
Users:
What: /sys/class/udc/<udc>/a_hnp_support
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates if an OTG A-Host supports HNP at this port.
Users:
What: /sys/class/udc/<udc>/b_hnp_enable
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates if an OTG A-Host enabled HNP support.
Users:
What: /sys/class/udc/<udc>/current_speed
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates the current negotiated speed at this port.
Users:
What: /sys/class/udc/<udc>/is_a_peripheral
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates that this port is the default Host on an OTG session
but HNP was used to switch roles.
Users:
What: /sys/class/udc/<udc>/is_otg
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates that this port support OTG.
Users:
What: /sys/class/udc/<udc>/maximum_speed
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates the maximum USB speed supported by this port.
Users:
What: /sys/class/udc/<udc>/maximum_speed
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates the maximum USB speed supported by this port.
Users:
What: /sys/class/udc/<udc>/soft_connect
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Allows users to disconnect data pullup resistors thus causing a
logical disconnection from the USB Host.
Users:
What: /sys/class/udc/<udc>/srp
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Allows users to manually start Session Request Protocol.
Users:
What: /sys/class/udc/<udc>/state
Date: June 2011
KernelVersion: 3.1
Contact: Felipe Balbi <balbi@kernel.org>
Description:
Indicates current state of the USB Device Controller. Valid
states are: 'not-attached', 'attached', 'powered',
'reconnecting', 'unauthenticated', 'default', 'addressed',
'configured', and 'suspended'; however not all USB Device
Controllers support reporting all states.
Users:
What: /config/usb-gadget/gadget/functions/hid.name
Date: Nov 2014
KernelVersion: 3.19
Description:
The attributes:
protocol - HID protocol to use
report_desc - blob corresponding to HID report descriptors
except the data passed through /dev/hidg<N>
report_length - HID report length
subclass - HID device subclass to use
What: /config/usb-gadget/gadget/functions/midi.name
Date: Nov 2014
KernelVersion: 3.19
Description:
The attributes:
index - index value for the USB MIDI adapter
id - ID string for the USB MIDI adapter
buflen - MIDI buffer length
qlen - USB read request queue length
in_ports - number of MIDI input ports
out_ports - number of MIDI output ports
......@@ -14,6 +14,29 @@ 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.
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
Only really useful for FPGA builds.
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
- snps,lpm-nyet-threshold: LPM NYET threshold
- snps,u2exit_lfps_quirk: set if we want to enable u2exit lfps quirk
- snps,u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
- snps,req_p1p2p3_quirk: when set, the core will always request for
P1/P2/P3 transition sequence.
- snps,del_p1p2p3_quirk: when set core will delay P1/P2/P3 until a certain
amount of 8B10B errors occur.
- snps,del_phy_power_chg_quirk: when set core will delay PHY power change
from P0 to P1/P2/P3.
- snps,lfps_filter_quirk: when set core will filter LFPS reception.
- snps,rx_detect_poll_quirk: when set core will disable a 400us delay to start
Polling LFPS after RX.Detect.
- snps,tx_de_emphasis_quirk: when set core will set Tx de-emphasis value.
- snps,tx_de_emphasis: the value driven to the PHY is controlled by the
LTSSM during USB3 Compliance mode.
- snps,dis_u3_susphy_quirk: when set core will disable USB3 suspend phy.
- snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy.
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
utmi_l1_suspend_n, false when asserts utmi_sleep_n
- snps,hird-threshold: HIRD threshold
This is usually a subnode to DWC3 glue to which it is connected.
......
......@@ -82,8 +82,10 @@ Example:
DWC3
Required properties:
- compatible: should be "samsung,exynos5250-dwusb3" for USB 3.0 DWC3
controller.
- compatible: should be one of the following -
"samsung,exynos5250-dwusb3": for USB 3.0 DWC3 controller on
Exynos5250/5420.
"samsung,exynos7-dwusb3": for USB 3.0 DWC3 controller on Exynos7.
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
with 'reg' property.
- ranges: allows valid 1:1 translation between child's address space and
......
......@@ -29,3 +29,25 @@ Example:
marvell,port-mode = <2>; /* PMM_GLOBAL_MODE */
};
UDC
Required properties:
- compatible: Should be "marvell,pxa270-udc" for USB controllers
used in device mode.
- reg: usb device MMIO address space
- interrupts: single interrupt generated by the UDC IP
- clocks: input clock of the UDC IP (see clock-bindings.txt)
Optional properties:
- gpios:
- gpio activated to control the USB D+ pullup (see gpio.txt)
Example:
pxa27x_udc: udc@40600000 {
compatible = "marvell,pxa270-udc";
reg = <0x40600000 0x10000>;
interrupts = <11>;
clocks = <&pxa2xx_clks 11>;
gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
};
......@@ -74,6 +74,13 @@ static struct platform_device my_hid = {
You can add as many HID functions as you want, only limited by
the amount of interrupt endpoints your gadget driver supports.
Configuration with configfs
Instead of adding fake platform devices and drivers in order to pass
some data to the kernel, if HID is a part of a gadget composed with
configfs the hidg_func_descriptor.report_desc is passed to the kernel
by writing the appropriate stream of bytes to a configfs attribute.
Send and receive HID reports
HID reports can be sent/received using read/write on the
......
......@@ -817,6 +817,8 @@
maximum-speed = "high-speed";
dr_mode = "otg";
status = "disabled";
snps,dis_u3_susphy_quirk;
snps,dis_u2_susphy_quirk;
};
};
......@@ -839,6 +841,8 @@
maximum-speed = "high-speed";
dr_mode = "otg";
status = "disabled";
snps,dis_u3_susphy_quirk;
snps,dis_u2_susphy_quirk;
};
};
......
......@@ -378,6 +378,26 @@ static void quirk_ati_exploding_mce(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_exploding_mce);
/*
* In the AMD NL platform, this device ([1022:7912]) has a class code of
* PCI_CLASS_SERIAL_USB_XHCI (0x0c0330), which means the xhci driver will
* claim it.
* But the dwc3 driver is a more specific driver for this device, and we'd
* prefer to use it instead of xhci. To prevent xhci from claiming the
* device, change the class code to 0x0c03fe, which the PCI r3.0 spec
* defines as "USB device (not host controller)". The dwc3 driver can then
* claim it based on its Vendor and Device ID.
*/
static void quirk_amd_nl_class(struct pci_dev *pdev)
{
/*
* Use 'USB Device' (0x0c03fe) instead of PCI header provided
*/
pdev->class = 0x0c03fe;
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB,
quirk_amd_nl_class);
/*
* Let's make the southbridge information explicit instead
* of having to worry about people probing the ACPI areas,
......
......@@ -60,7 +60,7 @@ EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
{
struct omap_usb *phy = phy_to_omapusb(otg->phy);
struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
if (!phy->comparator)
return -ENODEV;
......@@ -70,7 +70,7 @@ static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
static int omap_usb_start_srp(struct usb_otg *otg)
{
struct omap_usb *phy = phy_to_omapusb(otg->phy);
struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
if (!phy->comparator)
return -ENODEV;
......@@ -80,11 +80,9 @@ static int omap_usb_start_srp(struct usb_otg *otg)
static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
{
struct usb_phy *phy = otg->phy;
otg->host = host;
if (!host)
phy->state = OTG_STATE_UNDEFINED;
otg->state = OTG_STATE_UNDEFINED;
return 0;
}
......@@ -92,11 +90,9 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
static int omap_usb_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
struct usb_phy *phy = otg->phy;
otg->gadget = gadget;
if (!gadget)
phy->state = OTG_STATE_UNDEFINED;
otg->state = OTG_STATE_UNDEFINED;
return 0;
}
......@@ -255,7 +251,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
otg->set_vbus = omap_usb_set_vbus;
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
otg->start_srp = omap_usb_start_srp;
otg->phy = &phy->phy;
otg->usb_phy = &phy->phy;
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
......
......@@ -606,7 +606,7 @@ static int twl4030_set_peripheral(struct usb_otg *otg,
otg->gadget = gadget;
if (!gadget)
otg->phy->state = OTG_STATE_UNDEFINED;
otg->state = OTG_STATE_UNDEFINED;
return 0;
}
......@@ -618,7 +618,7 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
otg->host = host;
if (!host)
otg->phy->state = OTG_STATE_UNDEFINED;
otg->state = OTG_STATE_UNDEFINED;
return 0;
}
......@@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->phy.otg = otg;
twl->phy.type = USB_PHY_TYPE_USB2;
otg->phy = &twl->phy;
otg->usb_phy = &twl->phy;
otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral;
......
......@@ -161,7 +161,8 @@ struct hw_bank {
* @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active
* @transceiver: pointer to USB PHY, if any
* @phy: pointer to PHY, if any
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver
* @debugfs: root dentry for this controller in debugfs
* @id_event: indicates there is an id event, and handled at ci_otg_work
......@@ -177,6 +178,7 @@ struct ci_hdrc {
struct ci_role_driver *roles[CI_ROLE_END];
enum ci_role role;
bool is_otg;
struct usb_otg otg;
struct otg_fsm fsm;
struct ci_otg_fsm_timer_list *fsm_timer;
struct work_struct work;
......@@ -201,7 +203,9 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata;
int vbus_active;
struct usb_phy *transceiver;
struct phy *phy;
/* old usb_phy interface */
struct usb_phy *usb_phy;
struct usb_hcd *hcd;
struct dentry *debugfs;
bool id_event;
......
......@@ -147,7 +147,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto err_clk;
}
pdata.phy = data->phy;
pdata.usb_phy = data->phy;
if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX)
pdata.flags |= CI_HDRC_IMX28_WRITE_FIX;
......
......@@ -26,15 +26,15 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
writel(0, USB_AHBMODE);
usb_phy_init(ci->transceiver);
usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
/*
* Put the transceiver in non-driving mode. Otherwise host
* Put the phy in non-driving mode. Otherwise host
* may not detect soft-disconnection.
*/
usb_phy_notify_disconnect(ci->transceiver, USB_SPEED_UNKNOWN);
usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
......@@ -68,7 +68,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (IS_ERR(phy))
return PTR_ERR(phy);
ci_hdrc_msm_platdata.phy = phy;
ci_hdrc_msm_platdata.usb_phy = phy;
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
......
......@@ -47,6 +47,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/idr.h>
......@@ -298,6 +299,49 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
}
}
/**
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
* interfaces
* @ci: the controller
*
* This function returns an error code if the phy failed to init
*/
static int _ci_usb_phy_init(struct ci_hdrc *ci)
{
int ret;
if (ci->phy) {
ret = phy_init(ci->phy);
if (ret)
return ret;
ret = phy_power_on(ci->phy);
if (ret) {
phy_exit(ci->phy);
return ret;
}
} else {
ret = usb_phy_init(ci->usb_phy);
}
return ret;
}
/**
* _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
* interfaces
* @ci: the controller
*/
static void ci_usb_phy_exit(struct ci_hdrc *ci)
{
if (ci->phy) {
phy_power_off(ci->phy);
phy_exit(ci->phy);
} else {
usb_phy_shutdown(ci->usb_phy);
}
}
/**
* ci_usb_phy_init: initialize phy according to different phy type
* @ci: the controller
......@@ -312,7 +356,7 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW:
case USBPHY_INTERFACE_MODE_HSIC:
ret = usb_phy_init(ci->transceiver);
ret = _ci_usb_phy_init(ci);
if (ret)
return ret;
hw_phymode_configure(ci);
......@@ -320,12 +364,12 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
case USBPHY_INTERFACE_MODE_ULPI:
case USBPHY_INTERFACE_MODE_SERIAL:
hw_phymode_configure(ci);
ret = usb_phy_init(ci->transceiver);
ret = _ci_usb_phy_init(ci);
if (ret)
return ret;
break;
default:
ret = usb_phy_init(ci->transceiver);
ret = _ci_usb_phy_init(ci);
}
return ret;
......@@ -605,23 +649,26 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
if (ci->platdata->phy)
ci->transceiver = ci->platdata->phy;
else
ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR(ci->transceiver)) {
ret = PTR_ERR(ci->transceiver);
/*
* 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)
return ret;
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
ci->usb_phy = ci->platdata->usb_phy;
} else {
ci->phy = devm_phy_get(dev, "usb-phy");
ci->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
/* if both generic PHY and USB PHY layers aren't enabled */
if (PTR_ERR(ci->phy) == -ENOSYS &&
PTR_ERR(ci->usb_phy) == -ENXIO)
return -ENXIO;
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
return -EPROBE_DEFER;
dev_err(dev, "no usb2 phy configured\n");
return -EPROBE_DEFER;
if (IS_ERR(ci->phy))
ci->phy = NULL;
else if (IS_ERR(ci->usb_phy))
ci->usb_phy = NULL;
}
ret = ci_usb_phy_init(ci);
......@@ -728,7 +775,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
stop:
ci_role_destroy(ci);
deinit_phy:
usb_phy_shutdown(ci->transceiver);
ci_usb_phy_exit(ci);
return ret;
}
......@@ -741,7 +788,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
free_irq(ci->irq, ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
usb_phy_shutdown(ci->transceiver);
ci_usb_phy_exit(ci);
return 0;
}
......
......@@ -220,7 +220,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
/* ------ State ----- */
seq_printf(s, "OTG state: %s\n\n",
usb_otg_state_string(ci->transceiver->state));
usb_otg_state_string(ci->otg.state));
/* ------ State Machine Variables ----- */
seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
......
......@@ -98,8 +98,11 @@ static int host_start(struct ci_hdrc *ci)
hcd->has_tt = 1;
hcd->power_budget = ci->platdata->power_budget;
hcd->usb_phy = ci->transceiver;
hcd->tpl_support = ci->platdata->tpl_support;
if (ci->phy)
hcd->phy = ci->phy;
else
hcd->usb_phy = ci->usb_phy;
ehci = hcd_to_ehci(hcd);
ehci->caps = ci->hw_bank.cap;
......@@ -117,10 +120,11 @@ static int host_start(struct ci_hdrc *ci)
if (ret) {
goto put_hcd;
} else {
struct usb_otg *otg = ci->transceiver->otg;
struct usb_otg *otg = &ci->otg;
ci->hcd = hcd;
if (otg) {
if (ci_otg_is_fsm_mode(ci)) {
otg->host = &hcd->self;
hcd->self.otg_port = 1;
}
......
......@@ -328,7 +328,7 @@ static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator)
set_tmout(ci, indicator);
/* only vbus fall below B_sess_vld in b_idle state */
if (ci->transceiver->state == OTG_STATE_B_IDLE)
if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
ci_otg_queue_work(ci);
}
......@@ -582,11 +582,11 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
* when there is no gadget class driver
*/
if (ci->fsm.id && !(ci->driver) &&
ci->transceiver->state < OTG_STATE_A_IDLE)
ci->fsm.otg->state < OTG_STATE_A_IDLE)
return 0;
if (otg_statemachine(&ci->fsm)) {
if (ci->transceiver->state == OTG_STATE_A_IDLE) {
if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
/*
* Further state change for cases:
* a_idle to b_idle; or
......@@ -600,7 +600,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci_otg_queue_work(ci);
if (ci->id_event)
ci->id_event = false;
} else if (ci->transceiver->state == OTG_STATE_B_IDLE) {
} else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
if (ci->fsm.b_sess_vld) {
ci->fsm.power_up = 0;
/*
......@@ -627,7 +627,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
switch (ci->transceiver->state) {
switch (ci->fsm.otg->state) {
case OTG_STATE_A_WAIT_BCON:
if (port_conn) {
fsm->b_conn = 1;
......@@ -778,20 +778,17 @@ void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
{
int retval = 0;
struct usb_otg *otg;
otg = devm_kzalloc(ci->dev,
sizeof(struct usb_otg), GFP_KERNEL);
if (!otg)
return -ENOMEM;
if (ci->phy)
ci->otg.phy = ci->phy;
else
ci->otg.usb_phy = ci->usb_phy;
otg->phy = ci->transceiver;
otg->gadget = &ci->gadget;
ci->fsm.otg = otg;
ci->transceiver->otg = ci->fsm.otg;
ci->otg.gadget = &ci->gadget;
ci->fsm.otg = &ci->otg;
ci->fsm.power_up = 1;
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
ci->transceiver->state = OTG_STATE_UNDEFINED;
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
ci->fsm.ops = &ci_otg_ops;
mutex_init(&ci->fsm.lock);
......
......@@ -692,10 +692,8 @@ __acquires(ci->lock)
int retval;
spin_unlock(&ci->lock);
if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
if (ci->driver)
ci->driver->disconnect(&ci->gadget);
}
if (ci->gadget.speed != USB_SPEED_UNKNOWN)
usb_gadget_udc_reset(&ci->gadget, ci->driver);
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
......@@ -709,8 +707,6 @@ __acquires(ci->lock)
if (ci->status == NULL)
retval = -ENOMEM;
usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT);
done:
spin_lock(&ci->lock);
......@@ -1519,8 +1515,8 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
if (ci->transceiver)
return usb_phy_set_power(ci->transceiver, ma);
if (ci->usb_phy)
return usb_phy_set_power(ci->usb_phy, ma);
return -ENOTSUPP;
}
......@@ -1544,8 +1540,7 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
static int ci_udc_stop(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
static int ci_udc_stop(struct usb_gadget *gadget);
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
......@@ -1682,8 +1677,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
/**
* ci_udc_stop: unregister a gadget driver
*/
static int ci_udc_stop(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
static int ci_udc_stop(struct usb_gadget *gadget)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
unsigned long flags;
......
......@@ -124,10 +124,10 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{
state_changed = 1;
if (fsm->otg->phy->state == new_state)
if (fsm->otg->state == new_state)
return 0;
VDBG("Set state: %s\n", usb_otg_state_string(new_state));
otg_leave_state(fsm, fsm->otg->phy->state);
otg_leave_state(fsm, fsm->otg->state);
switch (new_state) {
case OTG_STATE_B_IDLE:
otg_drv_vbus(fsm, 0);
......@@ -236,7 +236,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
break;
}
fsm->otg->phy->state = new_state;
fsm->otg->state = new_state;
return 0;
}
......@@ -247,7 +247,7 @@ int otg_statemachine(struct otg_fsm *fsm)
mutex_lock(&fsm->lock);
state = fsm->otg->phy->state;
state = fsm->otg->state;
state_changed = 0;
/* State machine state change judgement */
......
......@@ -2650,7 +2650,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
}
if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
struct phy *phy = phy_get(hcd->self.controller, "usb");
if (IS_ERR(phy)) {
......@@ -2670,6 +2670,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
goto err_phy;
}
hcd->phy = phy;
hcd->remove_phy = 1;
}
}
......@@ -2816,7 +2817,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
err_register_bus:
hcd_buffer_destroy(hcd);
err_create_buf:
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
phy_power_off(hcd->phy);
phy_exit(hcd->phy);
phy_put(hcd->phy);
......@@ -2900,7 +2901,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_deregister_bus(&hcd->self);
hcd_buffer_destroy(hcd);
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
phy_power_off(hcd->phy);
phy_exit(hcd->phy);
phy_put(hcd->phy);
......
config USB_DWC2
bool "DesignWare USB2 DRD Core Support"
depends on USB
tristate "DesignWare USB2 DRD Core Support"
depends on USB || USB_GADGET
help
Say Y here if your system has a Dual Role Hi-Speed USB
controller based on the DesignWare HSOTG IP Core.
......@@ -10,49 +10,61 @@ config USB_DWC2
bus interface module (if you have a PCI bus system) will be
called dwc2_pci.ko, and the platform interface module (for
controllers directly connected to the CPU) will be called
dwc2_platform.ko. For gadget mode, there will be a single
module called dwc2_gadget.ko.
NOTE: The s3c-hsotg driver is now renamed to dwc2_gadget. The
host and gadget drivers are still currently separate drivers.
There are plans to merge the dwc2_gadget driver with the dwc2
host driver in the near future to create a dual-role driver.
dwc2_platform.ko. For all modes(host, gadget and dual-role), there
will be an additional module named dwc2.ko.
if USB_DWC2
choice
bool "DWC2 Mode Selection"
default USB_DWC2_DUAL_ROLE if (USB && USB_GADGET)
default USB_DWC2_HOST if (USB && !USB_GADGET)
default USB_DWC2_PERIPHERAL if (!USB && USB_GADGET)
config USB_DWC2_HOST
tristate "Host only mode"
bool "Host only mode"
depends on USB
help
The Designware USB2.0 high-speed host controller
integrated into many SoCs.
integrated into many SoCs. Select this option if you want the
driver to operate in Host-only mode.
config USB_DWC2_PLATFORM
bool "DWC2 Platform"
depends on USB_DWC2_HOST
default USB_DWC2_HOST
comment "Gadget/Dual-role mode requires USB Gadget support to be enabled"
config USB_DWC2_PERIPHERAL
bool "Gadget only mode"
depends on USB_GADGET=y || USB_GADGET=USB_DWC2
help
The Designware USB2.0 platform interface module for
controllers directly connected to the CPU. This is only
used for host mode.
The Designware USB2.0 high-speed gadget controller
integrated into many SoCs. Select this option if you want the
driver to operate in Peripheral-only mode. This option requires
USB_GADGET to be enabled.
config USB_DWC2_DUAL_ROLE
bool "Dual Role mode"
depends on (USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2)
help
Select this option if you want the driver to work in a dual-role
mode. In this mode both host and gadget features are enabled, and
the role will be determined by the cable that gets plugged-in. This
option requires USB_GADGET to be enabled.
endchoice
config USB_DWC2_PLATFORM
tristate "DWC2 Platform"
default USB_DWC2_HOST || USB_DWC2_PERIPHERAL
help
The Designware USB2.0 platform interface module for
controllers directly connected to the CPU.
config USB_DWC2_PCI
bool "DWC2 PCI"
tristate "DWC2 PCI"
depends on USB_DWC2_HOST && PCI
default USB_DWC2_HOST
help
The Designware USB2.0 PCI interface module for controllers
connected to a PCI bus. This is only used for host mode.
comment "Gadget mode requires USB Gadget support to be enabled"
config USB_DWC2_PERIPHERAL
tristate "Gadget only mode"
depends on USB_GADGET
help
The Designware USB2.0 high-speed gadget controller
integrated into many SoCs.
config USB_DWC2_DEBUG
bool "Enable Debugging Messages"
help
......
ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o
obj-$(CONFIG_USB_DWC2) += dwc2.o
dwc2-y := core.o core_intr.o
dwc2-y += hcd.o hcd_intr.o
dwc2-y += hcd_queue.o hcd_ddma.o
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
dwc2-y += hcd.o hcd_intr.o
dwc2-y += hcd_queue.o hcd_ddma.o
endif
ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
dwc2-y += gadget.o
endif
# NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to
# this location and renamed gadget.c. When building for dynamically linked
# modules, dwc2_gadget.ko will get built for peripheral mode. For host mode,
# the core module will be dwc2.ko, the PCI bus interface module will called
# dwc2_pci.ko and the platform interface module will be called dwc2_platform.ko.
# At present the host and gadget driver will be separate drivers, but there
# are plans in the near future to create a dual-role driver.
# modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform
# interface module will be called dwc2_platform.ko.
ifneq ($(CONFIG_USB_DWC2_PCI),)
obj-$(CONFIG_USB_DWC2_HOST) += dwc2_pci.o
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
dwc2_pci-y := pci.o
endif
ifneq ($(CONFIG_USB_DWC2_PLATFORM),)
obj-$(CONFIG_USB_DWC2_HOST) += dwc2_platform.o
dwc2_platform-y := platform.o
endif
obj-$(CONFIG_USB_DWC2_PERIPHERAL) += dwc2_gadget.o
dwc2_gadget-y := gadget.o
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
dwc2_platform-y := platform.o
......@@ -458,16 +458,6 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
/* Clear the SRP success bit for FS-I2c */
hsotg->srp_success = 0;
if (irq >= 0) {
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
irq);
retval = devm_request_irq(hsotg->dev, irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
}
/* Enable common interrupts */
dwc2_enable_common_interrupts(hsotg);
......
......@@ -84,7 +84,7 @@ static const char * const s3c_hsotg_supply_names[] = {
*/
#define EP0_MPS_LIMIT 64
struct s3c_hsotg;
struct dwc2_hsotg;
struct s3c_hsotg_req;
/**
......@@ -130,7 +130,7 @@ struct s3c_hsotg_req;
struct s3c_hsotg_ep {
struct usb_ep ep;
struct list_head queue;
struct s3c_hsotg *parent;
struct dwc2_hsotg *parent;
struct s3c_hsotg_req *req;
struct dentry *debugfs;
......@@ -154,67 +154,6 @@ struct s3c_hsotg_ep {
char name[10];
};
/**
* struct s3c_hsotg - driver state.
* @dev: The parent device supplied to the probe function
* @driver: USB gadget driver
* @phy: The otg phy transceiver structure for phy control.
* @uphy: The otg phy transceiver structure for old USB phy control.
* @plat: The platform specific configuration data. This can be removed once
* all SoCs support usb transceiver.
* @regs: The memory area mapped for accessing registers.
* @irq: The IRQ number we are using
* @supplies: Definition of USB power supplies
* @phyif: PHY interface width
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
* @num_of_eps: Number of available EPs (excluding EP0)
* @debug_root: root directrory for debugfs.
* @debug_file: main status file for debugfs.
* @debug_fifo: FIFO status file for debugfs.
* @ep0_reply: Request used for ep0 reply.
* @ep0_buff: Buffer for EP0 reply data, if needed.
* @ctrl_buff: Buffer for EP0 control requests.
* @ctrl_req: Request for EP0 control packets.
* @setup: NAK management for EP0 SETUP
* @last_rst: Time of last reset
* @eps: The endpoints being supplied to the gadget framework
*/
struct s3c_hsotg {
struct device *dev;
struct usb_gadget_driver *driver;
struct phy *phy;
struct usb_phy *uphy;
struct s3c_hsotg_plat *plat;
spinlock_t lock;
void __iomem *regs;
int irq;
struct clk *clk;
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
u32 phyif;
int fifo_mem;
unsigned int dedicated_fifos:1;
unsigned char num_of_eps;
u32 fifo_map;
struct dentry *debug_root;
struct dentry *debug_file;
struct dentry *debug_fifo;
struct usb_request *ep0_reply;
struct usb_request *ctrl_req;
u8 ep0_buff[8];
u8 ctrl_buff[8];
struct usb_gadget gadget;
unsigned int setup;
unsigned long last_rst;
struct s3c_hsotg_ep *eps;
};
/**
* struct s3c_hsotg_req - data transfer request
* @req: The USB gadget request
......@@ -229,6 +168,7 @@ struct s3c_hsotg_req {
unsigned char mapped;
};
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
#define call_gadget(_hs, _entry) \
do { \
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
......@@ -238,6 +178,9 @@ do { \
spin_lock(&_hs->lock); \
} \
} while (0)
#else
#define call_gadget(_hs, _entry) do {} while (0)
#endif
struct dwc2_hsotg;
struct dwc2_host_chan;
......@@ -495,11 +438,13 @@ struct dwc2_hw_params {
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
* and periodic schedules
*
* These are common for both host and peripheral modes:
*
* @dev: The struct device pointer
* @regs: Pointer to controller regs
* @core_params: Parameters that define how the core should be configured
* @hw_params: Parameters that were autodetected from the
* hardware registers
* @core_params: Parameters that define how the core should be configured
* @op_state: The operational State, during transitions (a_host=>
* a_peripheral and b_device=>b_host) this may not match
* the core, but allows the software to determine
......@@ -508,6 +453,8 @@ struct dwc2_hw_params {
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
* transfer are in process of being queued
* @srp_success: Stores status of SRP request in the case of a FS PHY
......@@ -517,6 +464,9 @@ struct dwc2_hw_params {
* interrupt
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
* @lx_state: Lx state of connected device
*
* These are for host mode:
*
* @flags: Flags for handling root port state changes
* @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
* Transfers associated with these QHs are not currently
......@@ -585,11 +535,31 @@ struct dwc2_hw_params {
* @status_buf_dma: DMA address for status_buf
* @start_work: Delayed work for handling host A-cable connection
* @reset_work: Delayed work for handling a port reset
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
* @otg_port: OTG port number
* @frame_list: Frame list
* @frame_list_dma: Frame list DMA address
*
* These are for peripheral mode:
*
* @driver: USB gadget driver
* @phy: The otg phy transceiver structure for phy control.
* @uphy: The otg phy transceiver structure for old USB phy control.
* @plat: The platform specific configuration data. This can be removed once
* all SoCs support usb transceiver.
* @supplies: Definition of USB power supplies
* @phyif: PHY interface width
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
* @num_of_eps: Number of available EPs (excluding EP0)
* @debug_root: Root directrory for debugfs.
* @debug_file: Main status file for debugfs.
* @debug_fifo: FIFO status file for debugfs.
* @ep0_reply: Request used for ep0 reply.
* @ep0_buff: Buffer for EP0 reply data, if needed.
* @ctrl_buff: Buffer for EP0 control requests.
* @ctrl_req: Request for EP0 control packets.
* @setup: NAK management for EP0 SETUP
* @last_rst: Time of last reset
* @eps: The endpoints being supplied to the gadget framework
*/
struct dwc2_hsotg {
struct device *dev;
......@@ -601,6 +571,16 @@ struct dwc2_hsotg {
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
struct phy *phy;
struct usb_phy *uphy;
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
spinlock_t lock;
struct mutex init_mutex;
void *priv;
int irq;
struct clk *clk;
unsigned int queuing_high_bandwidth:1;
unsigned int srp_success:1;
......@@ -609,6 +589,18 @@ struct dwc2_hsotg {
struct timer_list wkp_timer;
enum dwc2_lx_state lx_state;
struct dentry *debug_root;
struct dentry *debug_file;
struct dentry *debug_fifo;
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_90a 0x4f54290a
#define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
struct {
......@@ -655,19 +647,10 @@ struct dwc2_hsotg {
struct delayed_work start_work;
struct delayed_work reset_work;
spinlock_t lock;
void *priv;
u8 otg_port;
u32 *frame_list;
dma_addr_t frame_list_dma;
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_90a 0x4f54290a
#define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a
#ifdef DEBUG
u32 frrem_samples;
u64 frrem_accum;
......@@ -686,6 +669,31 @@ struct dwc2_hsotg {
u32 hfnum_other_samples_b;
u64 hfnum_other_frrem_accum_b;
#endif
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/* Gadget structures */
struct usb_gadget_driver *driver;
struct s3c_hsotg_plat *plat;
u32 phyif;
int fifo_mem;
unsigned int dedicated_fifos:1;
unsigned char num_of_eps;
u32 fifo_map;
struct usb_request *ep0_reply;
struct usb_request *ctrl_req;
u8 ep0_buff[8];
u8 ctrl_buff[8];
struct usb_gadget gadget;
unsigned int enabled:1;
unsigned int connected:1;
unsigned int setup:1;
unsigned long last_rst;
struct s3c_hsotg_ep *eps;
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
};
/* Reasons for halting a host channel */
......@@ -955,4 +963,43 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
*/
extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg);
/* Gadget defines */
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg);
extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2);
extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
#else
static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
{ return 0; }
static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2) {}
static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
#else
static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {}
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
const struct dwc2_core_params *params)
{ return 0; }
#endif
#endif /* __DWC2_CORE_H__ */
......@@ -128,6 +128,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dwc2_op_state_str(hsotg));
gotgctl = readl(hsotg->regs + GOTGCTL);
if (dwc2_is_device_mode(hsotg))
s3c_hsotg_disconnect(hsotg);
if (hsotg->op_state == OTG_STATE_B_HOST) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
} else {
......@@ -287,9 +290,11 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
* Release lock before scheduling workq as it holds spinlock during
* scheduling.
*/
spin_unlock(&hsotg->lock);
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
spin_lock(&hsotg->lock);
if (hsotg->wq_otg) {
spin_unlock(&hsotg->lock);
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
spin_lock(&hsotg->lock);
}
/* Clear interrupt */
writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
......@@ -312,6 +317,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
/* Clear interrupt */
writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
/*
* Report disconnect if there is any previous session established
*/
if (dwc2_is_device_mode(hsotg))
s3c_hsotg_disconnect(hsotg);
}
/*
......
此差异已折叠。
......@@ -1371,6 +1371,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_core_init(hsotg, false, -1);
dwc2_enable_global_interrupts(hsotg);
s3c_hsotg_core_init_disconnected(hsotg);
s3c_hsotg_core_connect(hsotg);
} else {
/* A-Device connector (Host Mode) */
dev_dbg(hsotg->dev, "connId A\n");
......@@ -1471,6 +1473,30 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
}
}
static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
{
u32 hprt0;
/* After clear the Stop PHY clock bit, we should wait for a moment
* for PLL work stable with clock output.
*/
writel(0, hsotg->regs + PCGCTL);
usleep_range(2000, 4000);
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_RES;
writel(hprt0, hsotg->regs + HPRT0);
hprt0 &= ~HPRT0_SUSP;
/* according to USB2.0 Spec 7.1.7.7, the host must send the resume
* signal for at least 20ms
*/
usleep_range(20000, 25000);
hprt0 &= ~HPRT0_RES;
writel(hprt0, hsotg->regs + HPRT0);
hsotg->lx_state = DWC2_L0;
}
/* Handles hub class-specific requests */
static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u16 wvalue, u16 windex, char *buf, u16 wlength)
......@@ -1516,17 +1542,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
case USB_PORT_FEAT_SUSPEND:
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
writel(0, hsotg->regs + PCGCTL);
usleep_range(20000, 40000);
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_RES;
writel(hprt0, hsotg->regs + HPRT0);
hprt0 &= ~HPRT0_SUSP;
usleep_range(100000, 150000);
hprt0 &= ~HPRT0_RES;
writel(hprt0, hsotg->regs + HPRT0);
dwc2_port_resume(hsotg);
break;
case USB_PORT_FEAT_POWER:
......@@ -2299,6 +2315,55 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
usleep_range(1000, 3000);
}
static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
u32 hprt0;
if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
(hsotg->op_state == OTG_STATE_A_HOST)))
return 0;
/* TODO: We get into suspend from 'on' state, maybe we need to do
* something if we get here from DWC2_L1(LPM sleep) state one day.
*/
if (hsotg->lx_state != DWC2_L0)
return 0;
hprt0 = dwc2_read_hprt0(hsotg);
if (hprt0 & HPRT0_CONNSTS) {
dwc2_port_suspend(hsotg, 1);
} else {
u32 pcgctl = readl(hsotg->regs + PCGCTL);
pcgctl |= PCGCTL_STOPPCLK;
writel(pcgctl, hsotg->regs + PCGCTL);
}
return 0;
}
static int _dwc2_hcd_resume(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
u32 hprt0;
if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
(hsotg->op_state == OTG_STATE_A_HOST)))
return 0;
if (hsotg->lx_state != DWC2_L2)
return 0;
hprt0 = dwc2_read_hprt0(hsotg);
if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP))
dwc2_port_resume(hsotg);
else
writel(0, hsotg->regs + PCGCTL);
return 0;
}
/* Returns the current frame number */
static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
{
......@@ -2669,6 +2734,9 @@ static struct hc_driver dwc2_hc_driver = {
.hub_status_data = _dwc2_hcd_hub_status_data,
.hub_control = _dwc2_hcd_hub_control,
.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
.bus_suspend = _dwc2_hcd_suspend,
.bus_resume = _dwc2_hcd_resume,
};
/*
......@@ -2778,6 +2846,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
int i, num_channels;
int retval;
if (usb_disabled())
return -ENODEV;
dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
/* Detect config values from hardware */
......@@ -2839,7 +2910,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
hcd->has_tt = 1;
spin_lock_init(&hsotg->lock);
((struct wrapper_priv_data *) &hcd->hcd_priv)->hsotg = hsotg;
hsotg->priv = hcd;
......
......@@ -668,9 +668,6 @@ extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
*/
extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
* and 0 otherwise
......@@ -679,13 +676,6 @@ extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
*/
extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_get_frame_number() - Returns current frame number
*
* @hsotg: The DWC2 HCD
*/
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_dump_state() - Dumps hsotg state
*
......
......@@ -141,6 +141,13 @@ static int dwc2_driver_probe(struct pci_dev *dev,
pci_set_master(dev);
retval = devm_request_irq(hsotg->dev, dev->irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
spin_lock_init(&hsotg->lock);
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
if (retval) {
pci_disable_device(dev);
......
......@@ -40,6 +40,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/usb/of.h>
......@@ -121,6 +122,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
dwc2_hcd_remove(hsotg);
s3c_hsotg_remove(hsotg);
return 0;
}
......@@ -129,6 +131,7 @@ static const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
{ .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
{ .compatible = "snps,dwc2", .data = NULL },
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
......@@ -155,9 +158,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
int retval;
int irq;
if (usb_disabled())
return -ENODEV;
match = of_match_device(dwc2_of_match_table, &dev->dev);
if (match && match->data) {
params = match->data;
......@@ -194,6 +194,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
return irq;
}
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
irq);
retval = devm_request_irq(hsotg->dev, irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
if (IS_ERR(hsotg->regs))
......@@ -204,6 +212,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
spin_lock_init(&hsotg->lock);
mutex_init(&hsotg->init_mutex);
retval = dwc2_gadget_init(hsotg, irq);
if (retval)
return retval;
retval = dwc2_hcd_init(hsotg, irq, params);
if (retval)
return retval;
......@@ -213,10 +226,35 @@ static int dwc2_driver_probe(struct platform_device *dev)
return retval;
}
static int __maybe_unused dwc2_suspend(struct device *dev)
{
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
int ret = 0;
if (dwc2_is_device_mode(dwc2))
ret = s3c_hsotg_suspend(dwc2);
return ret;
}
static int __maybe_unused dwc2_resume(struct device *dev)
{
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
int ret = 0;
if (dwc2_is_device_mode(dwc2))
ret = s3c_hsotg_resume(dwc2);
return ret;
}
static const struct dev_pm_ops dwc2_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
};
static struct platform_driver dwc2_platform_driver = {
.driver = {
.name = dwc2_driver_name,
.of_match_table = dwc2_of_match_table,
.pm = &dwc2_dev_pm_ops,
},
.probe = dwc2_driver_probe,
.remove = dwc2_driver_remove,
......
......@@ -55,7 +55,7 @@ config USB_DWC3_OMAP
config USB_DWC3_EXYNOS
tristate "Samsung Exynos Platform"
depends on ARCH_EXYNOS || COMPILE_TEST
depends on ARCH_EXYNOS && OF || COMPILE_TEST
default USB_DWC3
help
Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
......
......@@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
......@@ -32,6 +33,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
......@@ -362,6 +364,72 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
}
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
*/
static void dwc3_phy_setup(struct dwc3 *dwc)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
* to '0' during coreConsultant configuration. So default value
* will be '0' when the core is reset. Application needs to set it
* to '1' after the core initialization is completed.
*/
if (dwc->revision > DWC3_REVISION_194A)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
if (dwc->req_p1p2p3_quirk)
reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
if (dwc->del_p1p2p3_quirk)
reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN;
if (dwc->del_phy_power_chg_quirk)
reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE;
if (dwc->lfps_filter_quirk)
reg |= DWC3_GUSB3PIPECTL_LFPSFILT;
if (dwc->rx_detect_poll_quirk)
reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL;
if (dwc->tx_de_emphasis_quirk)
reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis);
if (dwc->dis_u3_susphy_quirk)
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
mdelay(100);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
* be '0' when the core is reset. Application needs to set it to
* '1' after the core initialization is completed.
*/
if (dwc->revision > DWC3_REVISION_194A)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
mdelay(100);
}
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
......@@ -384,6 +452,12 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
dwc->revision = reg;
/*
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
*/
dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
/* Handle USB2.0-only core configuration */
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
......@@ -414,7 +488,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
reg &= ~DWC3_GCTL_DISSCRAMBLE;
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
......@@ -441,11 +514,34 @@ static int dwc3_core_init(struct dwc3 *dwc)
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
/* enable hibernation here */
dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
/*
* REVISIT Enabling this bit so that host-mode hibernation
* will work. Device-mode hibernation is not yet implemented.
*/
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
dev_dbg(dwc->dev, "No power optimization available\n");
}
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
dev_dbg(dwc->dev, "it is on FPGA board\n");
dwc->is_fpga = true;
}
WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga,
"disable_scramble cannot be used on non-FPGA builds\n");
if (dwc->disable_scramble_quirk && dwc->is_fpga)
reg |= DWC3_GCTL_DISSCRAMBLE;
else
reg &= ~DWC3_GCTL_DISSCRAMBLE;
if (dwc->u2exit_lfps_quirk)
reg |= DWC3_GCTL_U2EXIT_LFPS;
/*
* WORKAROUND: DWC3 revisions <1.90a have a bug
* where the device can fail to connect at SuperSpeed
......@@ -459,6 +555,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
dwc3_phy_setup(dwc);
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
goto err1;
......@@ -630,6 +728,9 @@ static int dwc3_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct resource *res;
struct dwc3 *dwc;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
int ret;
......@@ -685,22 +786,96 @@ static int dwc3_probe(struct platform_device *pdev)
*/
res->start -= DWC3_GLOBALS_REGS_START;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
/* default to -3.5dB de-emphasis */
tx_de_emphasis = 1;
/*
* default to assert utmi_sleep_n and use maximum allowed HIRD
* threshold value of 0b1100
*/
hird_threshold = 12;
if (node) {
dwc->maximum_speed = of_usb_get_maximum_speed(node);
dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
dwc->has_lpm_erratum = of_property_read_bool(node,
"snps,has-lpm-erratum");
of_property_read_u8(node, "snps,lpm-nyet-threshold",
&lpm_nyet_threshold);
dwc->is_utmi_l1_suspend = of_property_read_bool(node,
"snps,is-utmi-l1-suspend");
of_property_read_u8(node, "snps,hird-threshold",
&hird_threshold);
dwc->needs_fifo_resize = of_property_read_bool(node,
"tx-fifo-resize");
dwc->dr_mode = of_usb_get_dr_mode(node);
dwc->disable_scramble_quirk = of_property_read_bool(node,
"snps,disable_scramble_quirk");
dwc->u2exit_lfps_quirk = of_property_read_bool(node,
"snps,u2exit_lfps_quirk");
dwc->u2ss_inp3_quirk = of_property_read_bool(node,
"snps,u2ss_inp3_quirk");
dwc->req_p1p2p3_quirk = of_property_read_bool(node,
"snps,req_p1p2p3_quirk");
dwc->del_p1p2p3_quirk = of_property_read_bool(node,
"snps,del_p1p2p3_quirk");
dwc->del_phy_power_chg_quirk = of_property_read_bool(node,
"snps,del_phy_power_chg_quirk");
dwc->lfps_filter_quirk = of_property_read_bool(node,
"snps,lfps_filter_quirk");
dwc->rx_detect_poll_quirk = of_property_read_bool(node,
"snps,rx_detect_poll_quirk");
dwc->dis_u3_susphy_quirk = of_property_read_bool(node,
"snps,dis_u3_susphy_quirk");
dwc->dis_u2_susphy_quirk = of_property_read_bool(node,
"snps,dis_u2_susphy_quirk");
dwc->tx_de_emphasis_quirk = of_property_read_bool(node,
"snps,tx_de_emphasis_quirk");
of_property_read_u8(node, "snps,tx_de_emphasis",
&tx_de_emphasis);
} else if (pdata) {
dwc->maximum_speed = pdata->maximum_speed;
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
if (pdata->lpm_nyet_threshold)
lpm_nyet_threshold = pdata->lpm_nyet_threshold;
dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
if (pdata->hird_threshold)
hird_threshold = pdata->hird_threshold;
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
dwc->dr_mode = pdata->dr_mode;
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
if (pdata->tx_de_emphasis)
tx_de_emphasis = pdata->tx_de_emphasis;
}
/* default to superspeed if no maximum_speed passed */
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
dwc->maximum_speed = USB_SPEED_SUPER;
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
ret = dwc3_core_get_phy(dwc);
if (ret)
return ret;
......@@ -708,9 +883,11 @@ static int dwc3_probe(struct platform_device *pdev)
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
if (!dev->dma_mask) {
dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
}
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
......@@ -815,50 +992,6 @@ static int dwc3_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
static int dwc3_prepare(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
dwc3_gadget_prepare(dwc);
/* FALLTHROUGH */
case USB_DR_MODE_HOST:
default:
dwc3_event_buffers_cleanup(dwc);
break;
}
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
static void dwc3_complete(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_event_buffers_setup(dwc);
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
dwc3_gadget_complete(dwc);
/* FALLTHROUGH */
case USB_DR_MODE_HOST:
default:
break;
}
spin_unlock_irqrestore(&dwc->lock, flags);
}
static int dwc3_suspend(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
......@@ -873,7 +1006,7 @@ static int dwc3_suspend(struct device *dev)
/* FALLTHROUGH */
case USB_DR_MODE_HOST:
default:
/* do nothing */
dwc3_event_buffers_cleanup(dwc);
break;
}
......@@ -906,6 +1039,7 @@ static int dwc3_resume(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
dwc3_event_buffers_setup(dwc);
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
switch (dwc->dr_mode) {
......@@ -934,9 +1068,6 @@ static int dwc3_resume(struct device *dev)
}
static const struct dev_pm_ops dwc3_dev_pm_ops = {
.prepare = dwc3_prepare,
.complete = dwc3_complete,
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
};
......@@ -958,12 +1089,24 @@ static const struct of_device_id of_dwc3_match[] = {
MODULE_DEVICE_TABLE(of, of_dwc3_match);
#endif
#ifdef CONFIG_ACPI
#define ACPI_ID_INTEL_BSW "808622B7"
static const struct acpi_device_id dwc3_acpi_match[] = {
{ ACPI_ID_INTEL_BSW, 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
#endif
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
.remove = dwc3_remove,
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
.acpi_match_table = ACPI_PTR(dwc3_acpi_match),
.pm = DWC3_PM_OPS,
},
};
......
......@@ -166,6 +166,7 @@
#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)
......@@ -175,7 +176,17 @@
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 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_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
/* Global TX Fifo Size Register */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
......@@ -210,6 +221,9 @@
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
......@@ -243,16 +257,19 @@
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
/* These apply for core versions 1.94a and later */
#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_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_INITU2ENA (1 << 12)
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
#define DWC3_DCTL_INITU1ENA (1 << 10)
#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
#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_TSTCTRL_MASK (0xf << 1)
#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK)
......@@ -657,17 +674,41 @@ struct dwc3_scratchpad_array {
* @regset: debugfs pointer to regdump file
* @test_mode: true when we're entering a USB test mode
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
* @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
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
* 0 - utmi_sleep_n
* 1 - utmi_l1_suspend_n
* @is_selfpowered: true when we are selfpowered
* @is_fpga: true when we are using the FPGA board
* @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
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
* @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk
* @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk
* @del_phy_power_chg_quirk: set if we enable delay phy power change quirk
* @lfps_filter_quirk: set if we enable LFPS filter quirk
* @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk
* @dis_u3_susphy_quirk: set if we disable usb3 suspend phy
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
* 1 - -3.5dB de-emphasis
* 2 - No de-emphasis
* 3 - Reserved
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
......@@ -759,18 +800,37 @@ struct dwc3 {
u8 test_mode;
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
unsigned has_hibernation:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
unsigned is_selfpowered:1;
unsigned is_fpga: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;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
unsigned u2ss_inp3_quirk:1;
unsigned req_p1p2p3_quirk:1;
unsigned del_p1p2p3_quirk:1;
unsigned del_phy_power_chg_quirk:1;
unsigned lfps_filter_quirk:1;
unsigned rx_detect_poll_quirk:1;
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
};
/* -------------------------------------------------------------------------- */
......@@ -964,20 +1024,9 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
/* power management interface */
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_prepare(struct dwc3 *dwc);
void dwc3_gadget_complete(struct dwc3 *dwc);
int dwc3_gadget_suspend(struct dwc3 *dwc);
int dwc3_gadget_resume(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_prepare(struct dwc3 *dwc)
{
return 0;
}
static inline void dwc3_gadget_complete(struct dwc3 *dwc)
{
}
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
{
return 0;
......
......@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/usb/otg.h>
......@@ -35,6 +34,9 @@ struct dwc3_exynos {
struct device *dev;
struct clk *clk;
struct clk *susp_clk;
struct clk *axius_clk;
struct regulator *vdd33;
struct regulator *vdd10;
};
......@@ -106,7 +108,6 @@ static int dwc3_exynos_remove_child(struct device *dev, void *unused)
static int dwc3_exynos_probe(struct platform_device *pdev)
{
struct dwc3_exynos *exynos;
struct clk *clk;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
......@@ -133,16 +134,32 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
return ret;
}
clk = devm_clk_get(dev, "usbdrd30");
if (IS_ERR(clk)) {
exynos->dev = dev;
exynos->clk = devm_clk_get(dev, "usbdrd30");
if (IS_ERR(exynos->clk)) {
dev_err(dev, "couldn't get clock\n");
return -EINVAL;
}
clk_prepare_enable(exynos->clk);
exynos->dev = dev;
exynos->clk = clk;
exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
if (IS_ERR(exynos->susp_clk)) {
dev_dbg(dev, "no suspend clk specified\n");
exynos->susp_clk = NULL;
}
clk_prepare_enable(exynos->susp_clk);
clk_prepare_enable(exynos->clk);
if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
if (IS_ERR(exynos->axius_clk)) {
dev_err(dev, "no AXI UpScaler clk specified\n");
return -ENODEV;
}
clk_prepare_enable(exynos->axius_clk);
} else {
exynos->axius_clk = NULL;
}
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
if (IS_ERR(exynos->vdd33)) {
......@@ -185,7 +202,9 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
err3:
regulator_disable(exynos->vdd33);
err2:
clk_disable_unprepare(clk);
clk_disable_unprepare(exynos->axius_clk);
clk_disable_unprepare(exynos->susp_clk);
clk_disable_unprepare(exynos->clk);
return ret;
}
......@@ -197,6 +216,8 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
platform_device_unregister(exynos->usb2_phy);
platform_device_unregister(exynos->usb3_phy);
clk_disable_unprepare(exynos->axius_clk);
clk_disable_unprepare(exynos->susp_clk);
clk_disable_unprepare(exynos->clk);
regulator_disable(exynos->vdd33);
......@@ -205,19 +226,19 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id exynos_dwc3_match[] = {
{ .compatible = "samsung,exynos5250-dwusb3" },
{ .compatible = "samsung,exynos7-dwusb3" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
#endif
#ifdef CONFIG_PM_SLEEP
static int dwc3_exynos_suspend(struct device *dev)
{
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
clk_disable(exynos->axius_clk);
clk_disable(exynos->clk);
regulator_disable(exynos->vdd33);
......@@ -243,6 +264,7 @@ static int dwc3_exynos_resume(struct device *dev)
}
clk_enable(exynos->clk);
clk_enable(exynos->axius_clk);
/* runtime set active to reflect active state. */
pm_runtime_disable(dev);
......@@ -266,7 +288,7 @@ static struct platform_driver dwc3_exynos_driver = {
.remove = dwc3_exynos_remove,
.driver = {
.name = "exynos-dwc3",
.of_match_table = of_match_ptr(exynos_dwc3_match),
.of_match_table = exynos_dwc3_match,
.pm = DEV_PM_OPS,
},
};
......
......@@ -123,6 +123,7 @@ static int kdwc3_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "missing irq\n");
error = irq;
goto err_irq;
}
......
......@@ -593,27 +593,12 @@ static const struct of_device_id of_dwc3_match[] = {
MODULE_DEVICE_TABLE(of, of_dwc3_match);
#ifdef CONFIG_PM_SLEEP
static int dwc3_omap_prepare(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
dwc3_omap_disable_irqs(omap);
return 0;
}
static void dwc3_omap_complete(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
dwc3_omap_enable_irqs(omap);
}
static int dwc3_omap_suspend(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
omap->utmi_otg_status = dwc3_omap_read_utmi_status(omap);
dwc3_omap_disable_irqs(omap);
return 0;
}
......@@ -623,6 +608,7 @@ static int dwc3_omap_resume(struct device *dev)
struct dwc3_omap *omap = dev_get_drvdata(dev);
dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
dwc3_omap_enable_irqs(omap);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
......@@ -632,8 +618,6 @@ static int dwc3_omap_resume(struct device *dev)
}
static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
.prepare = dwc3_omap_prepare,
.complete = dwc3_omap_complete,
SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume)
};
......
......@@ -25,6 +25,8 @@
#include <linux/usb/otg.h>
#include <linux/usb/usb_phy_generic.h>
#include "platform_data.h"
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
......@@ -102,6 +104,9 @@ static int dwc3_pci_probe(struct pci_dev *pci,
struct dwc3_pci *glue;
int ret;
struct device *dev = &pci->dev;
struct dwc3_platform_data dwc3_pdata;
memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata));
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
......@@ -140,6 +145,31 @@ static int dwc3_pci_probe(struct pci_dev *pci,
res[1].name = "dwc_usb3";
res[1].flags = IORESOURCE_IRQ;
if (pci->vendor == PCI_VENDOR_ID_AMD &&
pci->device == PCI_DEVICE_ID_AMD_NL_USB) {
dwc3_pdata.has_lpm_erratum = true;
dwc3_pdata.lpm_nyet_threshold = 0xf;
dwc3_pdata.u2exit_lfps_quirk = true;
dwc3_pdata.u2ss_inp3_quirk = true;
dwc3_pdata.req_p1p2p3_quirk = true;
dwc3_pdata.del_p1p2p3_quirk = true;
dwc3_pdata.del_phy_power_chg_quirk = true;
dwc3_pdata.lfps_filter_quirk = true;
dwc3_pdata.rx_detect_poll_quirk = true;
dwc3_pdata.tx_de_emphasis_quirk = true;
dwc3_pdata.tx_de_emphasis = 1;
/*
* FIXME these quirks should be removed when AMD NL
* taps out
*/
dwc3_pdata.disable_scramble_quirk = true;
dwc3_pdata.dis_u3_susphy_quirk = true;
dwc3_pdata.dis_u2_susphy_quirk = true;
}
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
......@@ -148,6 +178,10 @@ static int dwc3_pci_probe(struct pci_dev *pci,
pci_set_drvdata(pci, glue);
ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata));
if (ret)
goto err3;
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
dwc3->dev.dma_mask = dev->dma_mask;
......@@ -185,6 +219,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
......
......@@ -243,7 +243,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset");
if (IS_ERR(dwc3_data->rstc_rst)) {
dev_err(&pdev->dev, "could not get reset controller\n");
ret = PTR_ERR(dwc3_data->rstc_pwrdn);
ret = PTR_ERR(dwc3_data->rstc_rst);
goto undo_powerdown;
}
......
......@@ -86,6 +86,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
trace_dwc3_prepare_trb(dep, trb);
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_STARTTRANSFER, &params);
if (ret < 0) {
......@@ -441,7 +443,6 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
case USB_DEVICE_LTM_ENABLE:
return -EINVAL;
break;
case USB_DEVICE_TEST_MODE:
if ((wIndex & 0xff) != 0)
......@@ -550,7 +551,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (state) {
case USB_STATE_DEFAULT:
return -EINVAL;
break;
case USB_STATE_ADDRESS:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
......@@ -700,35 +700,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
case USB_REQ_SET_SEL:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL");
ret = dwc3_ep0_set_sel(dwc, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n");
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
default:
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n");
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
}
......@@ -791,6 +791,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
trb = dwc->ep0_trb;
trace_dwc3_complete_trb(ep0, trb);
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
......@@ -855,6 +857,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
dep = dwc->eps[0];
trb = dwc->ep0_trb;
trace_dwc3_complete_trb(dep, trb);
if (!list_empty(&dep->request_list)) {
r = next_request(&dep->request_list);
......@@ -875,7 +879,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING)
dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n");
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
......
......@@ -1140,8 +1140,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
if (!dep->endpoint.desc) {
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
request, ep->name);
spin_unlock_irqrestore(&dwc->lock, flags);
return -ESHUTDOWN;
ret = -ESHUTDOWN;
goto out;
}
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
request, req->dep->name)) {
ret = -EINVAL;
goto out;
}
dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
......@@ -1149,6 +1155,8 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
trace_dwc3_ep_queue(req);
ret = __dwc3_gadget_ep_queue(dep, req);
out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
......@@ -1622,8 +1630,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
return ret;
}
static int dwc3_gadget_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver)
static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
......@@ -2034,6 +2041,17 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->resume(&dwc->gadget);
}
}
static void dwc3_reset_gadget(struct dwc3 *dwc)
{
if (!dwc->gadget_driver)
return;
if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
......@@ -2140,6 +2158,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
......@@ -2177,11 +2196,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_gadget_disconnect_interrupt(dwc);
}
/* after reset -> Default State */
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
dwc3_disconnect_gadget(dwc);
dwc3_reset_gadget(dwc);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
......@@ -2287,11 +2302,20 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
/*
* TODO: This should be configurable. For now using
* maximum allowed HIRD threshold value of 0b1100
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
* DCFG.LPMCap is set, core responses with an ACK and the
* BESL value in the LPM token is less than or equal to LPM
* NYET threshold.
*/
reg |= DWC3_DCTL_HIRD_THRES(12);
WARN_ONCE(dwc->revision < DWC3_REVISION_240A
&& dwc->has_lpm_erratum,
"LPM Erratum not available on dwc3 revisisions < 2.40a\n");
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
} else {
......@@ -2744,26 +2768,13 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc->ctrl_req, dwc->ctrl_req_addr);
}
int dwc3_gadget_prepare(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
if (dwc->pullups_connected) {
dwc3_gadget_disable_irq(dwc);
dwc3_gadget_run_stop(dwc, true, true);
}
return 0;
}
void dwc3_gadget_complete(struct dwc3 *dwc)
{
if (dwc->pullups_connected) {
dwc3_gadget_enable_irq(dwc);
dwc3_gadget_run_stop(dwc, true, false);
}
}
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
......@@ -2798,6 +2809,11 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
if (dwc->pullups_connected) {
dwc3_gadget_enable_irq(dwc);
dwc3_gadget_run_stop(dwc, true, false);
}
return 0;
err1:
......
......@@ -24,4 +24,24 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode;
bool tx_fifo_resize;
unsigned is_utmi_l1_suspend:1;
u8 hird_threshold;
u8 lpm_nyet_threshold;
unsigned disable_scramble_quirk:1;
unsigned has_lpm_erratum:1;
unsigned u2exit_lfps_quirk:1;
unsigned u2ss_inp3_quirk:1;
unsigned req_p1p2p3_quirk:1;
unsigned del_p1p2p3_quirk:1;
unsigned del_phy_power_chg_quirk:1;
unsigned lfps_filter_quirk:1;
unsigned rx_detect_poll_quirk:1;
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
};
......@@ -61,7 +61,7 @@ DECLARE_EVENT_CLASS(dwc3_log_event,
TP_fast_assign(
__entry->event = event;
),
TP_printk("event %08x\n", __entry->event)
TP_printk("event %08x", __entry->event)
);
DEFINE_EVENT(dwc3_log_event, dwc3_event,
......@@ -157,7 +157,7 @@ DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
__entry->cmd = cmd;
__entry->param = param;
),
TP_printk("cmd '%s' [%d] param %08x\n",
TP_printk("cmd '%s' [%d] param %08x",
dwc3_gadget_generic_cmd_string(__entry->cmd),
__entry->cmd, __entry->param
)
......@@ -175,17 +175,21 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
TP_STRUCT__entry(
__dynamic_array(char, name, DWC3_MSG_MAX)
__field(unsigned int, cmd)
__field(struct dwc3_gadget_ep_cmd_params *, params)
__field(u32, param0)
__field(u32, param1)
__field(u32, param2)
),
TP_fast_assign(
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
__entry->cmd = cmd;
__entry->params = params;
__entry->param0 = params->param0;
__entry->param1 = params->param1;
__entry->param2 = params->param2;
),
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n",
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x",
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
__entry->cmd, __entry->params->param0,
__entry->params->param1, __entry->params->param2
__entry->cmd, __entry->param0,
__entry->param1, __entry->param2
)
);
......@@ -214,7 +218,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->size = trb->size;
__entry->ctrl = trb->ctrl;
),
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n",
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x",
__get_str(name), __entry->trb, __entry->bph, __entry->bpl,
__entry->size, __entry->ctrl
)
......
......@@ -190,6 +190,12 @@ config USB_F_UAC2
config USB_F_UVC
tristate
config USB_F_MIDI
tristate
config USB_F_HID
tristate
choice
tristate "USB Gadget Drivers"
default USB_ETH
......@@ -362,6 +368,61 @@ config USB_CONFIGFS_F_FS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
config USB_CONFIGFS_F_UAC1
boolean "Audio Class 1.0"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
select SND_PCM
select USB_F_UAC1
help
This Audio function implements 1 AudioControl interface,
1 AudioStreaming Interface each for USB-OUT and USB-IN.
This driver requires a real Audio codec to be present
on the device.
config USB_CONFIGFS_F_UAC2
boolean "Audio Class 2.0"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
select SND_PCM
select USB_F_UAC2
help
This Audio function is compatible with USB Audio Class
specification 2.0. It implements 1 AudioControl interface,
1 AudioStreaming Interface each for USB-OUT and USB-IN.
This driver doesn't expect any real Audio codec to be present
on the device - the audio streams are simply sinked to and
sourced from a virtual ALSA sound card created. The user-space
application may choose to do whatever it wants with the data
received from the USB Host and choose to provide whatever it
wants as audio data to the USB Host.
config USB_CONFIGFS_F_MIDI
boolean "MIDI function"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
select SND_RAWMIDI
select USB_F_MIDI
help
The MIDI Function acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
a sound "card" in the ALSA sound system. Other MIDI
connections can then be made on the gadget system, using
ALSA's aconnect utility etc.
config USB_CONFIGFS_F_HID
boolean "HID function"
depends on USB_CONFIGFS
select USB_F_HID
help
The HID function driver provides generic emulation of USB
Human Interface Devices (HID).
For more information, see Documentation/usb/gadget_hid.txt.
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
......
......@@ -1246,10 +1246,49 @@ EXPORT_SYMBOL_GPL(usb_string_ids_n);
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
{
struct usb_composite_dev *cdev;
if (req->status || req->actual != req->length)
DBG((struct usb_composite_dev *) ep->driver_data,
"setup complete --> %d, %d/%d\n",
req->status, req->actual, req->length);
/*
* REVIST The same ep0 requests are shared with function drivers
* so they don't have to maintain the same ->complete() stubs.
*
* Because of that, we need to check for the validity of ->context
* here, even though we know we've set it to something useful.
*/
if (!req->context)
return;
cdev = req->context;
if (cdev->req == req)
cdev->setup_pending = false;
else if (cdev->os_desc_req == req)
cdev->os_desc_pending = false;
else
WARN(1, "unknown request %p\n", req);
}
static int composite_ep0_queue(struct usb_composite_dev *cdev,
struct usb_request *req, gfp_t gfp_flags)
{
int ret;
ret = usb_ep_queue(cdev->gadget->ep0, req, gfp_flags);
if (ret == 0) {
if (cdev->req == req)
cdev->setup_pending = true;
else if (cdev->os_desc_req == req)
cdev->os_desc_pending = true;
else
WARN(1, "unknown request %p\n", req);
}
return ret;
}
static int count_ext_compat(struct usb_configuration *c)
......@@ -1428,6 +1467,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
* when we delegate to it.
*/
req->zero = 0;
req->context = cdev;
req->complete = composite_setup_complete;
req->length = 0;
gadget->ep0->driver_data = cdev;
......@@ -1624,6 +1664,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
int count = 0;
req = cdev->os_desc_req;
req->context = cdev;
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
......@@ -1686,8 +1727,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
break;
}
req->length = value;
req->context = cdev;
req->zero = value < w_length;
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
......@@ -1757,8 +1799,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
/* respond with data transfer before status phase? */
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
req->context = cdev;
req->zero = value < w_length;
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
......@@ -1893,6 +1936,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
goto fail_dev;
cdev->req->complete = composite_setup_complete;
cdev->req->context = cdev;
gadget->ep0->driver_data = cdev;
cdev->driver = composite;
......@@ -1937,6 +1981,7 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
kfree(cdev->os_desc_req);
goto end;
}
cdev->os_desc_req->context = cdev;
cdev->os_desc_req->complete = composite_setup_complete;
end:
return ret;
......@@ -1951,10 +1996,16 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
kfree(uc);
}
if (cdev->os_desc_req) {
if (cdev->os_desc_pending)
usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req);
kfree(cdev->os_desc_req->buf);
usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
}
if (cdev->req) {
if (cdev->setup_pending)
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
kfree(cdev->req->buf);
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
}
......@@ -2013,8 +2064,7 @@ static int composite_bind(struct usb_gadget *gadget,
/*-------------------------------------------------------------------------*/
static void
composite_suspend(struct usb_gadget *gadget)
void composite_suspend(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
......@@ -2037,8 +2087,7 @@ composite_suspend(struct usb_gadget *gadget)
usb_gadget_vbus_draw(gadget, 2);
}
static void
composite_resume(struct usb_gadget *gadget)
void composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
......@@ -2158,7 +2207,8 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);
req->length = 0;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
req->context = cdev;
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
......
......@@ -271,7 +271,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi,
ret = -EBUSY;
goto err;
}
ret = udc_attach_driver(name, &gi->composite.gadget_driver);
ret = usb_udc_attach_driver(name, &gi->composite.gadget_driver);
if (ret)
goto err;
gi->udc_name = name;
......@@ -1453,6 +1453,9 @@ static const struct usb_gadget_driver configfs_driver_template = {
.reset = composite_disconnect,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.max_speed = USB_SPEED_SUPER,
.driver = {
.owner = THIS_MODULE,
......
......@@ -38,3 +38,7 @@ usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
......@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
......@@ -21,9 +22,14 @@
#include <linux/usb/g_hid.h>
#include "u_f.h"
#include "u_hid.h"
#define HIDG_MINORS 4
static int major, minors;
static struct class *hidg_class;
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
......@@ -160,6 +166,26 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
NULL,
};
/*-------------------------------------------------------------------------*/
/* Strings */
#define CT_FUNC_HID_IDX 0
static struct usb_string ct_func_string_defs[] = {
[CT_FUNC_HID_IDX].s = "HID Interface",
{}, /* end of list */
};
static struct usb_gadget_strings ct_func_string_table = {
.language = 0x0409, /* en-US */
.strings = ct_func_string_defs,
};
static struct usb_gadget_strings *ct_func_strings[] = {
&ct_func_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
/* Char Device */
......@@ -552,13 +578,22 @@ const struct file_operations f_hidg_fops = {
.llseek = noop_llseek,
};
static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_ep *ep;
struct f_hidg *hidg = func_to_hidg(f);
struct usb_string *us;
struct device *device;
int status;
dev_t dev;
/* maybe allocate device-global string IDs, and patch descriptors */
us = usb_gstrings_attach(c->cdev, ct_func_strings,
ARRAY_SIZE(ct_func_string_defs));
if (IS_ERR(us))
return PTR_ERR(us);
hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
......@@ -623,10 +658,16 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
goto fail_free_descs;
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
device = device_create(hidg_class, NULL, dev, NULL,
"%s%d", "hidg", hidg->minor);
if (IS_ERR(device)) {
status = PTR_ERR(device);
goto del;
}
return 0;
del:
cdev_del(&hidg->cdev);
fail_free_descs:
usb_free_all_descriptors(f);
fail:
......@@ -640,116 +681,313 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
return status;
}
static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
static inline int hidg_get_minor(void)
{
struct f_hidg *hidg = func_to_hidg(f);
int ret;
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
usb_free_all_descriptors(f);
return ret;
}
kfree(hidg->report_desc);
kfree(hidg);
static inline struct f_hid_opts *to_f_hid_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_hid_opts,
func_inst.group);
}
/*-------------------------------------------------------------------------*/
/* Strings */
CONFIGFS_ATTR_STRUCT(f_hid_opts);
CONFIGFS_ATTR_OPS(f_hid_opts);
#define CT_FUNC_HID_IDX 0
static void hid_attr_release(struct config_item *item)
{
struct f_hid_opts *opts = to_f_hid_opts(item);
static struct usb_string ct_func_string_defs[] = {
[CT_FUNC_HID_IDX].s = "HID Interface",
{}, /* end of list */
};
usb_put_function_instance(&opts->func_inst);
}
static struct usb_gadget_strings ct_func_string_table = {
.language = 0x0409, /* en-US */
.strings = ct_func_string_defs,
static struct configfs_item_operations hidg_item_ops = {
.release = hid_attr_release,
.show_attribute = f_hid_opts_attr_show,
.store_attribute = f_hid_opts_attr_store,
};
static struct usb_gadget_strings *ct_func_strings[] = {
&ct_func_string_table,
#define F_HID_OPT(name, prec, limit) \
static ssize_t f_hid_opts_##name##_show(struct f_hid_opts *opts, char *page)\
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%d\n", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_hid_opts_##name##_store(struct f_hid_opts *opts, \
const char *page, size_t len) \
{ \
int ret; \
u##prec num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = kstrtou##prec(page, 0, &num); \
if (ret) \
goto end; \
\
if (num > limit) { \
ret = -EINVAL; \
goto end; \
} \
opts->name = num; \
ret = len; \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
static struct f_hid_opts_attribute f_hid_opts_##name = \
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_hid_opts_##name##_show,\
f_hid_opts_##name##_store)
F_HID_OPT(subclass, 8, 255);
F_HID_OPT(protocol, 8, 255);
F_HID_OPT(report_length, 16, 65536);
static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
{
int result;
mutex_lock(&opts->lock);
result = opts->report_desc_length;
memcpy(page, opts->report_desc, opts->report_desc_length);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_hid_opts_report_desc_store(struct f_hid_opts *opts,
const char *page, size_t len)
{
int ret = -EBUSY;
char *d;
mutex_lock(&opts->lock);
if (opts->refcnt)
goto end;
if (len > PAGE_SIZE) {
ret = -ENOSPC;
goto end;
}
d = kmemdup(page, len, GFP_KERNEL);
if (!d) {
ret = -ENOMEM;
goto end;
}
kfree(opts->report_desc);
opts->report_desc = d;
opts->report_desc_length = len;
opts->report_desc_alloc = true;
ret = len;
end:
mutex_unlock(&opts->lock);
return ret;
}
static struct f_hid_opts_attribute f_hid_opts_report_desc =
__CONFIGFS_ATTR(report_desc, S_IRUGO | S_IWUSR,
f_hid_opts_report_desc_show,
f_hid_opts_report_desc_store);
static struct configfs_attribute *hid_attrs[] = {
&f_hid_opts_subclass.attr,
&f_hid_opts_protocol.attr,
&f_hid_opts_report_length.attr,
&f_hid_opts_report_desc.attr,
NULL,
};
/*-------------------------------------------------------------------------*/
/* usb_configuration */
static struct config_item_type hid_func_type = {
.ct_item_ops = &hidg_item_ops,
.ct_attrs = hid_attrs,
.ct_owner = THIS_MODULE,
};
int __init hidg_bind_config(struct usb_configuration *c,
struct hidg_func_descriptor *fdesc, int index)
static inline void hidg_put_minor(int minor)
{
struct f_hidg *hidg;
int status;
ida_simple_remove(&hidg_ida, minor);
}
if (index >= minors)
return -ENOENT;
static void hidg_free_inst(struct usb_function_instance *f)
{
struct f_hid_opts *opts;
/* maybe allocate device-global string IDs, and patch descriptors */
if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
hidg_interface_desc.iInterface = status;
opts = container_of(f, struct f_hid_opts, func_inst);
mutex_lock(&hidg_ida_lock);
hidg_put_minor(opts->minor);
if (idr_is_empty(&hidg_ida.idr))
ghid_cleanup();
mutex_unlock(&hidg_ida_lock);
if (opts->report_desc_alloc)
kfree(opts->report_desc);
kfree(opts);
}
static struct usb_function_instance *hidg_alloc_inst(void)
{
struct f_hid_opts *opts;
struct usb_function_instance *ret;
int status = 0;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = hidg_free_inst;
ret = &opts->func_inst;
mutex_lock(&hidg_ida_lock);
if (idr_is_empty(&hidg_ida.idr)) {
status = ghid_setup(NULL, HIDG_MINORS);
if (status) {
ret = ERR_PTR(status);
kfree(opts);
goto unlock;
}
}
opts->minor = hidg_get_minor();
if (opts->minor < 0) {
ret = ERR_PTR(opts->minor);
kfree(opts);
if (idr_is_empty(&hidg_ida.idr))
ghid_cleanup();
goto unlock;
}
config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type);
unlock:
mutex_unlock(&hidg_ida_lock);
return ret;
}
static void hidg_free(struct usb_function *f)
{
struct f_hidg *hidg;
struct f_hid_opts *opts;
hidg = func_to_hidg(f);
opts = container_of(f->fi, struct f_hid_opts, func_inst);
kfree(hidg->report_desc);
kfree(hidg);
mutex_lock(&opts->lock);
--opts->refcnt;
mutex_unlock(&opts->lock);
}
static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
usb_free_all_descriptors(f);
}
static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
{
struct f_hidg *hidg;
struct f_hid_opts *opts;
/* allocate and initialize one new instance */
hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
if (!hidg)
return -ENOMEM;
hidg->minor = index;
hidg->bInterfaceSubClass = fdesc->subclass;
hidg->bInterfaceProtocol = fdesc->protocol;
hidg->report_length = fdesc->report_length;
hidg->report_desc_length = fdesc->report_desc_length;
hidg->report_desc = kmemdup(fdesc->report_desc,
fdesc->report_desc_length,
GFP_KERNEL);
if (!hidg->report_desc) {
kfree(hidg);
return -ENOMEM;
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_hid_opts, func_inst);
mutex_lock(&opts->lock);
++opts->refcnt;
hidg->minor = opts->minor;
hidg->bInterfaceSubClass = opts->subclass;
hidg->bInterfaceProtocol = opts->protocol;
hidg->report_length = opts->report_length;
hidg->report_desc_length = opts->report_desc_length;
if (opts->report_desc) {
hidg->report_desc = kmemdup(opts->report_desc,
opts->report_desc_length,
GFP_KERNEL);
if (!hidg->report_desc) {
kfree(hidg);
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOMEM);
}
}
mutex_unlock(&opts->lock);
hidg->func.name = "hid";
hidg->func.strings = ct_func_strings;
hidg->func.bind = hidg_bind;
hidg->func.unbind = hidg_unbind;
hidg->func.set_alt = hidg_set_alt;
hidg->func.disable = hidg_disable;
hidg->func.setup = hidg_setup;
hidg->func.free_func = hidg_free;
/* this could me made configurable at some point */
hidg->qlen = 4;
status = usb_add_function(c, &hidg->func);
if (status)
kfree(hidg);
return status;
return &hidg->func;
}
int __init ghid_setup(struct usb_gadget *g, int count)
DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fabien Chouteau");
int ghid_setup(struct usb_gadget *g, int count)
{
int status;
dev_t dev;
hidg_class = class_create(THIS_MODULE, "hidg");
if (IS_ERR(hidg_class)) {
status = PTR_ERR(hidg_class);
hidg_class = NULL;
return status;
}
status = alloc_chrdev_region(&dev, 0, count, "hidg");
if (!status) {
major = MAJOR(dev);
minors = count;
if (status) {
class_destroy(hidg_class);
hidg_class = NULL;
return status;
}
return status;
major = MAJOR(dev);
minors = count;
return 0;
}
void ghid_cleanup(void)
......
......@@ -20,6 +20,7 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
......@@ -33,6 +34,7 @@
#include <linux/usb/midi.h>
#include "u_f.h"
#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
......@@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
/* B.3.1 Standard AC Interface Descriptor */
static struct usb_interface_descriptor ac_interface_desc __initdata = {
static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
......@@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = {
};
/* B.3.2 Class-Specific AC Interface Descriptor */
static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
static struct uac1_ac_header_descriptor_1 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
......@@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
};
/* B.4.1 Standard MS Interface Descriptor */
static struct usb_interface_descriptor ms_interface_desc __initdata = {
static struct usb_interface_descriptor ms_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
......@@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = {
};
/* B.4.2 Class-Specific MS Interface Descriptor */
static struct usb_ms_header_descriptor ms_header_desc __initdata = {
static struct usb_ms_header_descriptor ms_header_desc = {
.bLength = USB_DT_MS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
......@@ -387,29 +389,6 @@ static void f_midi_disable(struct usb_function *f)
usb_ep_disable(midi->out_ep);
}
static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_midi *midi = func_to_midi(f);
struct snd_card *card;
DBG(cdev, "unbind\n");
/* just to be sure */
f_midi_disable(f);
card = midi->card;
midi->card = NULL;
if (card)
snd_card_free(card);
kfree(midi->id);
midi->id = NULL;
usb_free_all_descriptors(f);
kfree(midi);
}
static int f_midi_snd_free(struct snd_device *device)
{
return 0;
......@@ -654,6 +633,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = {
.trigger = f_midi_out_trigger
};
static inline void f_midi_unregister_card(struct f_midi *midi)
{
if (midi->card) {
snd_card_free(midi->card);
midi->card = NULL;
}
}
/* register as a sound "card" */
static int f_midi_register_card(struct f_midi *midi)
{
......@@ -715,17 +702,13 @@ static int f_midi_register_card(struct f_midi *midi)
return 0;
fail:
if (midi->card) {
snd_card_free(midi->card);
midi->card = NULL;
}
f_midi_unregister_card(midi);
return err;
}
/* MIDI function driver setup/binding */
static int __init
f_midi_bind(struct usb_configuration *c, struct usb_function *f)
static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_descriptor_header **midi_function;
struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
......@@ -734,15 +717,23 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
struct usb_string *us;
int status, n, jack = 1, i = 0;
midi->gadget = cdev->gadget;
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
status = f_midi_register_card(midi);
if (status < 0)
goto fail_register;
/* maybe allocate device-global string ID */
if (midi_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
goto fail;
midi_string_defs[0].id = status;
us = usb_gstrings_attach(c->cdev, midi_strings,
ARRAY_SIZE(midi_string_defs));
if (IS_ERR(us)) {
status = PTR_ERR(us);
goto fail;
}
ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id;
/* We have two interfaces, AudioControl and MIDIStreaming */
status = usb_interface_id(c, f);
......@@ -892,6 +883,8 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
kfree(midi_function);
usb_free_descriptors(f->hs_descriptors);
fail:
f_midi_unregister_card(midi);
fail_register:
/* we might as well release our claims on endpoints */
if (midi->out_ep)
midi->out_ep->driver_data = NULL;
......@@ -903,42 +896,235 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
return status;
}
/**
* f_midi_bind_config - add USB MIDI function to a configuration
* @c: the configuration to supcard the USB audio function
* @index: the soundcard index to use for the ALSA device creation
* @id: the soundcard id to use for the ALSA device creation
* @buflen: the buffer length to use
* @qlen the number of read requests to pre-allocate
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*/
int __init f_midi_bind_config(struct usb_configuration *c,
int index, char *id,
unsigned int in_ports,
unsigned int out_ports,
unsigned int buflen,
unsigned int qlen)
static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_midi_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_midi_opts);
CONFIGFS_ATTR_OPS(f_midi_opts);
static void midi_attr_release(struct config_item *item)
{
struct f_midi_opts *opts = to_f_midi_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations midi_item_ops = {
.release = midi_attr_release,
.show_attribute = f_midi_opts_attr_show,
.store_attribute = f_midi_opts_attr_store,
};
#define F_MIDI_OPT(name, test_limit, limit) \
static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%d\n", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \
const char *page, size_t len) \
{ \
int ret; \
u32 num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = kstrtou32(page, 0, &num); \
if (ret) \
goto end; \
\
if (test_limit && num > limit) { \
ret = -EINVAL; \
goto end; \
} \
opts->name = num; \
ret = len; \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
static struct f_midi_opts_attribute f_midi_opts_##name = \
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \
f_midi_opts_##name##_store)
F_MIDI_OPT(index, true, SNDRV_CARDS);
F_MIDI_OPT(buflen, false, 0);
F_MIDI_OPT(qlen, false, 0);
F_MIDI_OPT(in_ports, true, MAX_PORTS);
F_MIDI_OPT(out_ports, true, MAX_PORTS);
static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page)
{
int result;
mutex_lock(&opts->lock);
result = strlcpy(page, opts->id, PAGE_SIZE);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts,
const char *page, size_t len)
{
int ret;
char *c;
mutex_lock(&opts->lock);
if (opts->refcnt) {
ret = -EBUSY;
goto end;
}
c = kstrndup(page, len, GFP_KERNEL);
if (!c) {
ret = -ENOMEM;
goto end;
}
if (opts->id_allocated)
kfree(opts->id);
opts->id = c;
opts->id_allocated = true;
ret = len;
end:
mutex_unlock(&opts->lock);
return ret;
}
static struct f_midi_opts_attribute f_midi_opts_id =
__CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show,
f_midi_opts_id_store);
static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_index.attr,
&f_midi_opts_buflen.attr,
&f_midi_opts_qlen.attr,
&f_midi_opts_in_ports.attr,
&f_midi_opts_out_ports.attr,
&f_midi_opts_id.attr,
NULL,
};
static struct config_item_type midi_func_type = {
.ct_item_ops = &midi_item_ops,
.ct_attrs = midi_attrs,
.ct_owner = THIS_MODULE,
};
static void f_midi_free_inst(struct usb_function_instance *f)
{
struct f_midi_opts *opts;
opts = container_of(f, struct f_midi_opts, func_inst);
if (opts->id_allocated)
kfree(opts->id);
kfree(opts);
}
static struct usb_function_instance *f_midi_alloc_inst(void)
{
struct f_midi_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1;
opts->buflen = 256;
opts->qlen = 32;
opts->in_ports = 1;
opts->out_ports = 1;
config_group_init_type_name(&opts->func_inst.group, "",
&midi_func_type);
return &opts->func_inst;
}
static void f_midi_free(struct usb_function *f)
{
struct f_midi *midi;
struct f_midi_opts *opts;
int i;
midi = func_to_midi(f);
opts = container_of(f->fi, struct f_midi_opts, func_inst);
kfree(midi->id);
mutex_lock(&opts->lock);
for (i = opts->in_ports - 1; i >= 0; --i)
kfree(midi->in_port[i]);
kfree(midi);
--opts->refcnt;
mutex_unlock(&opts->lock);
}
static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_midi *midi = func_to_midi(f);
struct snd_card *card;
DBG(cdev, "unbind\n");
/* just to be sure */
f_midi_disable(f);
card = midi->card;
midi->card = NULL;
if (card)
snd_card_free(card);
usb_free_all_descriptors(f);
}
static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
{
struct f_midi *midi;
struct f_midi_opts *opts;
int status, i;
opts = container_of(fi, struct f_midi_opts, func_inst);
mutex_lock(&opts->lock);
/* sanity check */
if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
return -EINVAL;
if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
/* allocate and initialize one new instance */
midi = kzalloc(sizeof *midi, GFP_KERNEL);
midi = kzalloc(sizeof(*midi), GFP_KERNEL);
if (!midi) {
status = -ENOMEM;
goto fail;
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOMEM);
}
for (i = 0; i < in_ports; i++) {
for (i = 0; i < opts->in_ports; i++) {
struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port) {
status = -ENOMEM;
mutex_unlock(&opts->lock);
goto setup_fail;
}
......@@ -948,39 +1134,37 @@ int __init f_midi_bind_config(struct usb_configuration *c,
midi->in_port[i] = port;
}
midi->gadget = c->cdev->gadget;
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
/* set up ALSA midi devices */
midi->in_ports = in_ports;
midi->out_ports = out_ports;
status = f_midi_register_card(midi);
if (status < 0)
goto setup_fail;
midi->func.name = "gmidi function";
midi->func.strings = midi_strings;
midi->func.bind = f_midi_bind;
midi->func.unbind = f_midi_unbind;
midi->func.set_alt = f_midi_set_alt;
midi->func.disable = f_midi_disable;
midi->id = kstrdup(id, GFP_KERNEL);
midi->index = index;
midi->buflen = buflen;
midi->qlen = qlen;
status = usb_add_function(c, &midi->func);
if (status)
goto setup_fail;
return 0;
midi->id = kstrdup(opts->id, GFP_KERNEL);
if (opts->id && !midi->id) {
status = -ENOMEM;
mutex_unlock(&opts->lock);
goto kstrdup_fail;
}
midi->in_ports = opts->in_ports;
midi->out_ports = opts->out_ports;
midi->index = opts->index;
midi->buflen = opts->buflen;
midi->qlen = opts->qlen;
++opts->refcnt;
mutex_unlock(&opts->lock);
midi->func.name = "gmidi function";
midi->func.bind = f_midi_bind;
midi->func.unbind = f_midi_unbind;
midi->func.set_alt = f_midi_set_alt;
midi->func.disable = f_midi_disable;
midi->func.free_func = f_midi_free;
return &midi->func;
kstrdup_fail:
f_midi_unregister_card(midi);
setup_fail:
for (--i; i >= 0; i--)
kfree(midi->in_port[i]);
kfree(midi);
fail:
return status;
return ERR_PTR(status);
}
DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc);
......@@ -1441,6 +1441,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
NULL);
if (status)
goto fail;
/*
* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
......
......@@ -375,8 +375,7 @@ static struct sk_buff *rndis_add_header(struct gether *port,
struct sk_buff *skb2;
skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
if (skb2)
rndis_add_hdr(skb2);
rndis_add_hdr(skb2);
dev_kfree_skb(skb);
return skb2;
......
/*
* u_hid.h
*
* Utility definitions for the hid function
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.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 as
* published by the Free Software Foundation.
*/
#ifndef U_HID_H
#define U_HID_H
#include <linux/usb/composite.h>
struct f_hid_opts {
struct usb_function_instance func_inst;
int minor;
unsigned char subclass;
unsigned char protocol;
unsigned short report_length;
unsigned short report_desc_length;
unsigned char *report_desc;
bool report_desc_alloc;
/*
* Protect the data form concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
int ghid_setup(struct usb_gadget *g, int count);
void ghid_cleanup(void);
#endif /* U_HID_H */
/*
* u_midi.h
*
* Utility definitions for the midi function
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.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 as
* published by the Free Software Foundation.
*/
#ifndef U_MIDI_H
#define U_MIDI_H
#include <linux/usb/composite.h>
struct f_midi_opts {
struct usb_function_instance func_inst;
int index;
char *id;
bool id_allocated;
unsigned int in_ports;
unsigned int out_ports;
unsigned int buflen;
unsigned int qlen;
/*
* Protect the data form concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
#endif /* U_MIDI_H */
......@@ -213,9 +213,6 @@ static int gaudio_open_snd_dev(struct gaudio *card)
fn_cap = opts->fn_cap;
fn_cntl = opts->fn_cntl;
if (!card)
return -ENODEV;
/* Open control device */
snd = &card->control;
snd->filp = filp_open(fn_cntl, O_RDWR, 0);
......
......@@ -287,6 +287,7 @@ config USB_MIDI_GADGET
depends on SND
select USB_LIBCOMPOSITE
select SND_RAWMIDI
select USB_F_MIDI
help
The MIDI Gadget acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
......@@ -419,6 +420,7 @@ endif # TTY
config USB_G_HID
tristate "HID Gadget"
select USB_LIBCOMPOSITE
select USB_F_HID
help
The HID gadget driver provides generic emulation of USB
Human Interface Devices (HID).
......
......@@ -237,7 +237,7 @@ static void dbgp_unbind(struct usb_gadget *gadget)
static unsigned char tty_line;
#endif
static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
static int dbgp_configure_endpoints(struct usb_gadget *gadget)
{
int stp;
......@@ -273,19 +273,10 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
dbgp.serial->in->desc = &i_desc;
dbgp.serial->out->desc = &o_desc;
if (gserial_alloc_line(&tty_line)) {
stp = 3;
goto fail_3;
}
#endif
return 0;
fail_3:
dbgp.o_ep->driver_data = NULL;
#else
return 0;
#endif
fail_2:
dbgp.i_ep->driver_data = NULL;
fail_1:
......@@ -324,10 +315,17 @@ static int __init dbgp_bind(struct usb_gadget *gadget,
err = -ENOMEM;
goto fail;
}
if (gserial_alloc_line(&tty_line)) {
stp = 4;
err = -ENODEV;
goto fail;
}
#endif
err = dbgp_configure_endpoints(gadget);
if (err < 0) {
stp = 4;
stp = 5;
goto fail;
}
......@@ -383,6 +381,10 @@ static int dbgp_setup(struct usb_gadget *gadget,
#ifdef CONFIG_USB_G_DBGP_PRINTK
err = dbgp_enable_ep();
#else
err = dbgp_configure_endpoints(gadget);
if (err < 0) {
goto fail;
}
err = gserial_connect(dbgp.serial, tty_line);
#endif
if (err < 0)
......
......@@ -37,7 +37,7 @@
#include "gadget_chips.h"
#include "f_midi.c"
#include "u_midi.h"
/*-------------------------------------------------------------------------*/
......@@ -115,8 +115,13 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
static struct usb_function_instance *fi_midi;
static struct usb_function *f_midi;
static int __exit midi_unbind(struct usb_composite_dev *dev)
{
usb_put_function(f_midi);
usb_put_function_instance(fi_midi);
return 0;
}
......@@ -130,28 +135,54 @@ static struct usb_configuration midi_config = {
static int __init midi_bind_config(struct usb_configuration *c)
{
return f_midi_bind_config(c, index, id,
in_ports, out_ports,
buflen, qlen);
int status;
f_midi = usb_get_function(fi_midi);
if (IS_ERR(f_midi))
return PTR_ERR(f_midi);
status = usb_add_function(c, f_midi);
if (status < 0) {
usb_put_function(f_midi);
return status;
}
return 0;
}
static int __init midi_bind(struct usb_composite_dev *cdev)
{
struct f_midi_opts *midi_opts;
int status;
fi_midi = usb_get_function_instance("midi");
if (IS_ERR(fi_midi))
return PTR_ERR(fi_midi);
midi_opts = container_of(fi_midi, struct f_midi_opts, func_inst);
midi_opts->index = index;
midi_opts->id = id;
midi_opts->in_ports = in_ports;
midi_opts->out_ports = out_ports;
midi_opts->buflen = buflen;
midi_opts->qlen = qlen;
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
goto put;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
status = usb_add_config(cdev, &midi_config, midi_bind_config);
if (status < 0)
return status;
goto put;
usb_composite_overwrite_options(cdev, &coverwrite);
pr_info("%s\n", longname);
return 0;
put:
usb_put_function_instance(fi_midi);
return status;
}
static __refdata struct usb_composite_driver midi_driver = {
......
此差异已折叠。
此差异已折叠。
......@@ -241,6 +241,8 @@ config USB_M66592
dynamically linked module called "m66592_udc" and force all
gadget drivers to also be dynamically linked.
source "drivers/usb/gadget/udc/bdc/Kconfig"
#
# Controllers available only in discrete form (and all PCI controllers)
#
......
......@@ -30,3 +30,4 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
obj-$(CONFIG_USB_BDC_UDC) += bdc/
......@@ -1401,9 +1401,8 @@ static int udc_wakeup(struct usb_gadget *gadget)
static int amd5536_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
static int amd5536_udc_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver);
/* gadget operations */
static int amd5536_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops udc_ops = {
.wakeup = udc_wakeup,
.get_frame = udc_get_frame,
......@@ -1962,8 +1961,7 @@ __acquires(dev->lock)
}
/* Called by gadget driver to unregister itself */
static int amd5536_udc_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver)
static int amd5536_udc_stop(struct usb_gadget *g)
{
struct udc *dev = to_amd5536_udc(g);
unsigned long flags;
......@@ -1971,7 +1969,7 @@ static int amd5536_udc_stop(struct usb_gadget *g,
spin_lock_irqsave(&dev->lock, flags);
udc_mask_unused_interrupts(dev);
shutdown(dev, driver);
shutdown(dev, NULL);
spin_unlock_irqrestore(&dev->lock, flags);
dev->driver = NULL;
......@@ -2873,7 +2871,7 @@ __acquires(dev->lock)
dev->driver->resume(&dev->gadget);
dev->sys_suspended = 0;
}
dev->driver->disconnect(&dev->gadget);
usb_gadget_udc_reset(&dev->gadget, dev->driver);
spin_lock(&dev->lock);
/* disable ep0 to empty req queue */
......
此差异已折叠。
config USB_BDC_UDC
tristate "Broadcom USB3.0 device controller IP driver(BDC)"
depends on USB_GADGET && HAS_DMA
help
BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP
then select this driver.
Say "y" here to link the driver statically, or "m" to build a dynamically
linked module called "bdc".
if USB_BDC_UDC
comment "Platform Support"
config USB_BDC_PCI
tristate "BDC support for PCIe based platforms"
depends on PCI
default USB_BDC_UDC
help
Enable support for platforms which have BDC connected through PCIe, such as Lego3 FPGA platform.
endif
obj-$(CONFIG_USB_BDC_UDC) += bdc.o
bdc-y := bdc_core.o bdc_cmd.o bdc_ep.o bdc_udc.o
ifneq ($(CONFIG_USB_GADGET_VERBOSE),)
bdc-y += bdc_dbg.o
endif
obj-$(CONFIG_USB_BDC_PCI) += bdc_pci.o
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -1053,8 +1053,7 @@ static void fotg210_init(struct fotg210_udc *fotg210)
iowrite32(value, fotg210->reg + FOTG210_DMISGR0);
}
static int fotg210_udc_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver)
static int fotg210_udc_stop(struct usb_gadget *g)
{
struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
unsigned long flags;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册