mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-18 10:13:57 +08:00
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: Felipe Balbi <balbi@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUdJoNAAoJEIaOsuA1yqREqDoP/iwVojhk1qb2PQAJvLkl8To4 R34RM8zt8IRhE+yWIW8hm8xvsLiwvujBMgsphUmyu2k0X/NtPl1ElrDvObWwO0N8 ouyLISRw/wLUs8hJwsNVhIX0VjPxDQ6648C3OZYjySTtNtsh0rBw84ALX+IWkDye Thb823xEunGYVwjhq8WrfT94q6LjyPp00XHlA4mf3XgSpXC2tKBFGmIcfmwObzYI LkON7kJTp14XEeIYGYJO+7MBpo8s7YFmnpsDOV2QYlz/i0pRdjLwaKMN5T7D7DO5 YdvcQFQUM53dvq+tiw2k2iW4gmn9ktHHlckn8W5hhQZCs4ujSzYy/vP4GcbNEaTf CuDocJGTr7ieU2IqYiTM+wRYavBoHLq9V5AHZmgsZ/pVF8lvG0hcX0fmd7XXScjv GaKJqyQMaKZ9tRF9RZdMZeOrICp1X1X+KcqdG7yXoOtsVdn0O3zHdxYcZcVa29X/ cXuttoTywQgfrlI/uWWwpkYlqiHeawK1EooZ/6fYQDviLm4lrLPp94vF/5MqGM1j f2tNpG63iOzLUUYJa+YgPqs6RrZLM3soZcf3bFAEt9sA99gc3ExwCYXurBEnTONr XUowTfNKxPaVjNPZ1evGwF5F+WLEK4s88weB9lArd2S3NFP3DQV1cRpWj8rLYE72 lX6aWEWUDqnUHmwJUgIb =WYyZ -----END PGP SIGNATURE----- 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: Felipe Balbi <balbi@ti.com>
This commit is contained in:
commit
2d4d9f35ba
93
Documentation/ABI/stable/sysfs-class-udc
Normal file
93
Documentation/ABI/stable/sysfs-class-udc
Normal file
@ -0,0 +1,93 @@
|
||||
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:
|
11
Documentation/ABI/testing/configfs-usb-gadget-hid
Normal file
11
Documentation/ABI/testing/configfs-usb-gadget-hid
Normal file
@ -0,0 +1,11 @@
|
||||
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
|
12
Documentation/ABI/testing/configfs-usb-gadget-midi
Normal file
12
Documentation/ABI/testing/configfs-usb-gadget-midi
Normal file
@ -0,0 +1,12 @@
|
||||
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 (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 (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 both generic PHY and USB PHY layers aren't enabled */
|
||||
if (PTR_ERR(ci->phy) == -ENOSYS &&
|
||||
PTR_ERR(ci->usb_phy) == -ENXIO)
|
||||
return -ENXIO;
|
||||
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
|
||||
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 @@ err_allocate_root_hub:
|
||||
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);
|
||||
|
@ -1,6 +1,6 @@
|
||||
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.
|
||||
|
||||
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 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
|
||||
bool "DWC2 Platform"
|
||||
depends on USB_DWC2_HOST
|
||||
default USB_DWC2_HOST
|
||||
help
|
||||
The Designware USB2.0 platform interface module for
|
||||
controllers directly connected to the CPU. This is only
|
||||
used for host mode.
|
||||
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
|
||||
|
@ -1,28 +1,28 @@
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
@ -36,6 +37,7 @@
|
||||
#include <linux/platform_data/s3c-hsotg.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "hw.h"
|
||||
|
||||
/* conversion functions */
|
||||
static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
|
||||
@ -48,9 +50,9 @@ static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
|
||||
return container_of(ep, struct s3c_hsotg_ep, ep);
|
||||
}
|
||||
|
||||
static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
|
||||
static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
|
||||
{
|
||||
return container_of(gadget, struct s3c_hsotg, gadget);
|
||||
return container_of(gadget, struct dwc2_hsotg, gadget);
|
||||
}
|
||||
|
||||
static inline void __orr32(void __iomem *ptr, u32 val)
|
||||
@ -64,7 +66,7 @@ static inline void __bic32(void __iomem *ptr, u32 val)
|
||||
}
|
||||
|
||||
/* forward decleration of functions */
|
||||
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* using_dma - return the DMA status of the driver.
|
||||
@ -85,7 +87,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
|
||||
*
|
||||
* Until this issue is sorted out, we always return 'false'.
|
||||
*/
|
||||
static inline bool using_dma(struct s3c_hsotg *hsotg)
|
||||
static inline bool using_dma(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return false; /* support is not complete */
|
||||
}
|
||||
@ -95,7 +97,7 @@ static inline bool using_dma(struct s3c_hsotg *hsotg)
|
||||
* @hsotg: The device state
|
||||
* @ints: A bitmask of the interrupts to enable
|
||||
*/
|
||||
static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
static void s3c_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
|
||||
{
|
||||
u32 gsintmsk = readl(hsotg->regs + GINTMSK);
|
||||
u32 new_gsintmsk;
|
||||
@ -113,7 +115,7 @@ static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
* @hsotg: The device state
|
||||
* @ints: A bitmask of the interrupts to enable
|
||||
*/
|
||||
static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
|
||||
{
|
||||
u32 gsintmsk = readl(hsotg->regs + GINTMSK);
|
||||
u32 new_gsintmsk;
|
||||
@ -134,7 +136,7 @@ static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
* Set or clear the mask for an individual endpoint's interrupt
|
||||
* request.
|
||||
*/
|
||||
static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
|
||||
unsigned int ep, unsigned int dir_in,
|
||||
unsigned int en)
|
||||
{
|
||||
@ -159,7 +161,7 @@ static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
|
||||
* s3c_hsotg_init_fifo - initialise non-periodic FIFOs
|
||||
* @hsotg: The device instance.
|
||||
*/
|
||||
static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned int ep;
|
||||
unsigned int addr;
|
||||
@ -283,7 +285,7 @@ static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
|
||||
* This is the reverse of s3c_hsotg_map_dma(), called for the completion
|
||||
* of a request to ensure the buffer is ready for access by the caller.
|
||||
*/
|
||||
static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req)
|
||||
{
|
||||
@ -312,7 +314,7 @@ static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* This routine is only needed for PIO
|
||||
*/
|
||||
static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req)
|
||||
{
|
||||
@ -517,7 +519,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
|
||||
* Start the given request running by setting the endpoint registers
|
||||
* appropriately, and writing any data to the FIFOs.
|
||||
*/
|
||||
static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req,
|
||||
bool continuing)
|
||||
@ -707,7 +709,7 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
||||
* DMA memory, then we map the memory and mark our request to allow us to
|
||||
* cleanup on completion.
|
||||
*/
|
||||
static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
@ -736,7 +738,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
bool first;
|
||||
|
||||
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
|
||||
@ -768,7 +770,7 @@ static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
@ -799,7 +801,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
|
||||
|
||||
@ -814,7 +816,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
|
||||
* Convert the given wIndex into a pointer to an driver endpoint
|
||||
* structure, or return NULL if it is not a valid endpoint.
|
||||
*/
|
||||
static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
|
||||
static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
|
||||
u32 windex)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
|
||||
@ -843,7 +845,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
|
||||
* Create a request and queue it on the given endpoint. This is useful as
|
||||
* an internal method of sending replies to certain control requests, etc.
|
||||
*/
|
||||
static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *ep,
|
||||
void *buff,
|
||||
int length)
|
||||
@ -884,7 +886,7 @@ static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
|
||||
* @hsotg: The device state
|
||||
* @ctrl: USB control request
|
||||
*/
|
||||
static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
@ -955,7 +957,7 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
|
||||
* @hsotg: The device state
|
||||
* @ctrl: USB control request
|
||||
*/
|
||||
static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
@ -1028,8 +1030,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_stall_ep0 - stall ep0
|
||||
@ -1037,7 +1038,7 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
*
|
||||
* Set stall for ep0 as response for setup request.
|
||||
*/
|
||||
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
u32 reg;
|
||||
@ -1076,7 +1077,7 @@ static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
|
||||
* needs to work out what to do next (and whether to pass it on to the
|
||||
* gadget driver).
|
||||
*/
|
||||
static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
@ -1107,7 +1108,6 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
|
||||
switch (ctrl->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
dcfg = readl(hsotg->regs + DCFG);
|
||||
dcfg &= ~DCFG_DEVADDR_MASK;
|
||||
dcfg |= (le16_to_cpu(ctrl->wValue) <<
|
||||
@ -1161,7 +1161,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
|
||||
if (req->status < 0) {
|
||||
dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
|
||||
@ -1183,7 +1183,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
|
||||
* Enqueue a request on EP0 if necessary to received any SETUP packets
|
||||
* received from the host.
|
||||
*/
|
||||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct usb_request *req = hsotg->ctrl_req;
|
||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||
@ -1226,7 +1226,7 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
|
||||
*
|
||||
* Note, expects the ep to already be locked as appropriate.
|
||||
*/
|
||||
static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req,
|
||||
int result)
|
||||
@ -1291,7 +1291,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
||||
* endpoint, so sort out whether we need to read the data into a request
|
||||
* that has been made for that endpoint.
|
||||
*/
|
||||
static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
@ -1356,7 +1356,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
* currently believed that we do not need to wait for any space in
|
||||
* the TxFIFO.
|
||||
*/
|
||||
static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_req *req)
|
||||
{
|
||||
u32 ctrl;
|
||||
@ -1398,7 +1398,7 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
|
||||
* transfer for an OUT endpoint has been completed, either by a short
|
||||
* packet or by the finish of a transfer.
|
||||
*/
|
||||
static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
|
||||
int epnum, bool was_setup)
|
||||
{
|
||||
u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
|
||||
@ -1471,7 +1471,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* Return the current frame number
|
||||
*/
|
||||
static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
|
||||
static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts;
|
||||
|
||||
@ -1498,7 +1498,7 @@ static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
|
||||
* as the actual data should be sent to the memory directly and we turn
|
||||
* on the completion interrupts to get notifications of transfer completion.
|
||||
*/
|
||||
static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 grxstsr = readl(hsotg->regs + GRXSTSP);
|
||||
u32 epnum, status, size;
|
||||
@ -1590,7 +1590,7 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
|
||||
* Configure the maximum packet size for the given endpoint, updating
|
||||
* the hardware control registers to reflect this.
|
||||
*/
|
||||
static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
|
||||
unsigned int ep, unsigned int mps)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
|
||||
@ -1645,7 +1645,7 @@ bad_mps:
|
||||
* @hsotg: The driver state
|
||||
* @idx: The index for the endpoint (0..15)
|
||||
*/
|
||||
static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
|
||||
static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
|
||||
{
|
||||
int timeout;
|
||||
int val;
|
||||
@ -1681,7 +1681,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
|
||||
* Check to see if there is a request that has data to send, and if so
|
||||
* make an attempt to write data into the FIFO.
|
||||
*/
|
||||
static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
@ -1714,7 +1714,7 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
|
||||
* An IN transfer has been completed, update the transfer's state and then
|
||||
* call the relevant completion routines.
|
||||
*/
|
||||
static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
@ -1791,7 +1791,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* Process and clear any interrupt pending for an individual endpoint
|
||||
*/
|
||||
static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
||||
static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
int dir_in)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
|
||||
@ -1916,7 +1916,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
||||
* Handle updating the device settings after the enumeration phase has
|
||||
* been completed.
|
||||
*/
|
||||
static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts = readl(hsotg->regs + DSTS);
|
||||
int ep0_mps = 0, ep_mps = 8;
|
||||
@ -1993,7 +1993,7 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
|
||||
* Go through the requests on the given endpoint and mark them
|
||||
* completed with the given result code.
|
||||
*/
|
||||
static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||
static void kill_all_requests(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *ep,
|
||||
int result, bool force)
|
||||
{
|
||||
@ -2027,22 +2027,27 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||
* transactions and signal the gadget driver that this
|
||||
* has happened.
|
||||
*/
|
||||
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg)
|
||||
void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned ep;
|
||||
|
||||
if (!hsotg->connected)
|
||||
return;
|
||||
|
||||
hsotg->connected = 0;
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||
kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
|
||||
|
||||
call_gadget(hsotg, disconnect);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
|
||||
* @hsotg: The device state:
|
||||
* @periodic: True if this is a periodic FIFO interrupt
|
||||
*/
|
||||
static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
|
||||
static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep;
|
||||
int epno, ret;
|
||||
@ -2076,7 +2081,7 @@ static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
|
||||
*
|
||||
* Issue a soft reset to the core, and await the core finishing it.
|
||||
*/
|
||||
static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
|
||||
static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int timeout;
|
||||
u32 grstctl;
|
||||
@ -2124,7 +2129,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
|
||||
*
|
||||
* Issue a soft reset to the core, and await the core finishing it.
|
||||
*/
|
||||
static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
||||
void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
s3c_hsotg_corereset(hsotg);
|
||||
|
||||
@ -2241,12 +2246,23 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
||||
readl(hsotg->regs + DOEPCTL0));
|
||||
|
||||
/* clear global NAKs */
|
||||
writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK,
|
||||
writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
|
||||
hsotg->regs + DCTL);
|
||||
|
||||
/* must be at-least 3ms to allow bus to see disconnect */
|
||||
mdelay(3);
|
||||
|
||||
hsotg->last_rst = jiffies;
|
||||
}
|
||||
|
||||
static void s3c_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* set the soft-disconnect bit */
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
|
||||
}
|
||||
|
||||
void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* remove the soft-disconnect and let's go */
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
|
||||
}
|
||||
@ -2258,7 +2274,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
||||
*/
|
||||
static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = pw;
|
||||
struct dwc2_hsotg *hsotg = pw;
|
||||
int retry_count = 8;
|
||||
u32 gintsts;
|
||||
u32 gintmsk;
|
||||
@ -2273,31 +2289,11 @@ irq_retry:
|
||||
|
||||
gintsts &= gintmsk;
|
||||
|
||||
if (gintsts & GINTSTS_OTGINT) {
|
||||
u32 otgint = readl(hsotg->regs + GOTGINT);
|
||||
|
||||
dev_info(hsotg->dev, "OTGInt: %08x\n", otgint);
|
||||
|
||||
writel(otgint, hsotg->regs + GOTGINT);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_SESSREQINT) {
|
||||
dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__);
|
||||
writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_ENUMDONE) {
|
||||
writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
|
||||
|
||||
s3c_hsotg_irq_enumdone(hsotg);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_CONIDSTSCHNG) {
|
||||
dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n",
|
||||
readl(hsotg->regs + DSTS),
|
||||
readl(hsotg->regs + GOTGCTL));
|
||||
|
||||
writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
hsotg->connected = 1;
|
||||
}
|
||||
|
||||
if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
|
||||
@ -2340,8 +2336,8 @@ irq_retry:
|
||||
kill_all_requests(hsotg, &hsotg->eps[0],
|
||||
-ECONNRESET, true);
|
||||
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2380,25 +2376,6 @@ irq_retry:
|
||||
s3c_hsotg_handle_rx(hsotg);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_MODEMIS) {
|
||||
dev_warn(hsotg->dev, "warning, mode mismatch triggered\n");
|
||||
writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_USBSUSP) {
|
||||
dev_info(hsotg->dev, "GINTSTS_USBSusp\n");
|
||||
writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
|
||||
call_gadget(hsotg, suspend);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_WKUPINT) {
|
||||
dev_info(hsotg->dev, "GINTSTS_WkUpIn\n");
|
||||
writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
|
||||
call_gadget(hsotg, resume);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_ERLYSUSP) {
|
||||
dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
|
||||
writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS);
|
||||
@ -2450,7 +2427,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
int index = hs_ep->index;
|
||||
u32 epctrl_reg;
|
||||
@ -2593,7 +2570,7 @@ error:
|
||||
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
int index = hs_ep->index;
|
||||
unsigned long flags;
|
||||
@ -2658,7 +2635,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
|
||||
@ -2684,7 +2661,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
int index = hs_ep->index;
|
||||
u32 epreg;
|
||||
u32 epctl;
|
||||
@ -2748,7 +2725,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||
static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
@ -2777,7 +2754,7 @@ static struct usb_ep_ops s3c_hsotg_ep_ops = {
|
||||
* A wrapper for platform code responsible for controlling
|
||||
* low-level USB code
|
||||
*/
|
||||
static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_phy_enable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
||||
@ -2800,7 +2777,7 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
|
||||
* A wrapper for platform code responsible for controlling
|
||||
* low-level USB code
|
||||
*/
|
||||
static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
||||
@ -2818,7 +2795,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
|
||||
* s3c_hsotg_init - initalize the usb core
|
||||
* @hsotg: The driver state
|
||||
*/
|
||||
static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* unmask subset of endpoint interrupts */
|
||||
|
||||
@ -2868,7 +2845,8 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
|
||||
static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!hsotg) {
|
||||
@ -2889,6 +2867,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
WARN_ON(hsotg->driver);
|
||||
|
||||
driver->driver.bus = NULL;
|
||||
@ -2905,11 +2884,22 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
goto err;
|
||||
}
|
||||
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_init(hsotg);
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
hsotg->enabled = 0;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
|
||||
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
hsotg->driver = NULL;
|
||||
return ret;
|
||||
}
|
||||
@ -2921,16 +2911,17 @@ err:
|
||||
*
|
||||
* Stop udc hw block and stay tunned for future transmissions
|
||||
*/
|
||||
static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags = 0;
|
||||
int ep;
|
||||
|
||||
if (!hsotg)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 1; ep < hsotg->num_of_eps; ep++)
|
||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||
@ -2939,13 +2930,18 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||
|
||||
hsotg->driver = NULL;
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
hsotg->enabled = 0;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
|
||||
clk_disable(hsotg->clk);
|
||||
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2969,23 +2965,26 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
|
||||
*/
|
||||
static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags = 0;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on);
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (is_on) {
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
clk_enable(hsotg->clk);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
hsotg->enabled = 1;
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
} else {
|
||||
s3c_hsotg_core_disconnect(hsotg);
|
||||
hsotg->enabled = 0;
|
||||
clk_disable(hsotg->clk);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
}
|
||||
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3007,7 +3006,7 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
|
||||
* creation) to give to the gadget driver. Setup the endpoint name, any
|
||||
* direction information and other state that may be required.
|
||||
*/
|
||||
static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
int epnum)
|
||||
{
|
||||
@ -3056,7 +3055,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* Read the USB core HW configuration registers
|
||||
*/
|
||||
static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 cfg2, cfg3, cfg4;
|
||||
/* check hardware configuration */
|
||||
@ -3080,7 +3079,7 @@ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
|
||||
* s3c_hsotg_dump - dump state of the udc
|
||||
* @param: The device state
|
||||
*/
|
||||
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
struct device *dev = hsotg->dev;
|
||||
@ -3139,7 +3138,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
|
||||
*/
|
||||
static int state_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = seq->private;
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int idx;
|
||||
|
||||
@ -3209,7 +3208,7 @@ static const struct file_operations state_fops = {
|
||||
*/
|
||||
static int fifo_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = seq->private;
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
u32 val;
|
||||
int idx;
|
||||
@ -3265,7 +3264,7 @@ static const char *decode_direction(int is_in)
|
||||
static int ep_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep = seq->private;
|
||||
struct s3c_hsotg *hsotg = ep->parent;
|
||||
struct dwc2_hsotg *hsotg = ep->parent;
|
||||
struct s3c_hsotg_req *req;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int index = ep->index;
|
||||
@ -3342,7 +3341,7 @@ static const struct file_operations ep_fops = {
|
||||
* with the same name as the device itself, in case we end up
|
||||
* with multiple blocks in future systems.
|
||||
*/
|
||||
static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dentry *root;
|
||||
unsigned epidx;
|
||||
@ -3388,7 +3387,7 @@ static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
|
||||
*
|
||||
* Cleanup (remove) the debugfs files for use on module exit.
|
||||
*/
|
||||
static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned epidx;
|
||||
|
||||
@ -3403,27 +3402,21 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c_hsotg_probe - probe function for hsotg driver
|
||||
* @pdev: The platform information for the driver
|
||||
* dwc2_gadget_init - init function for gadget
|
||||
* @dwc2: The data structure for the DWC2 driver.
|
||||
* @irq: The IRQ number for the controller.
|
||||
*/
|
||||
|
||||
static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{
|
||||
struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = hsotg->dev;
|
||||
struct s3c_hsotg_plat *plat = dev->platform_data;
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct s3c_hsotg_ep *eps;
|
||||
struct s3c_hsotg *hsotg;
|
||||
struct resource *res;
|
||||
int epnum;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
|
||||
if (!hsotg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
@ -3431,14 +3424,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
* Attempt to find a generic PHY, then look for an old style
|
||||
* USB PHY, finally fall back to pdata
|
||||
*/
|
||||
phy = devm_phy_get(&pdev->dev, "usb2-phy");
|
||||
phy = devm_phy_get(dev, "usb2-phy");
|
||||
if (IS_ERR(phy)) {
|
||||
uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(uphy)) {
|
||||
/* Fallback for pdata */
|
||||
plat = dev_get_platdata(&pdev->dev);
|
||||
plat = dev_get_platdata(dev);
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev,
|
||||
dev_err(dev,
|
||||
"no platform data or transceiver defined\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
@ -3455,43 +3448,24 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
hsotg->phyif = GUSBCFG_PHYIF8;
|
||||
}
|
||||
|
||||
hsotg->dev = dev;
|
||||
|
||||
hsotg->clk = devm_clk_get(&pdev->dev, "otg");
|
||||
hsotg->clk = devm_clk_get(dev, "otg");
|
||||
if (IS_ERR(hsotg->clk)) {
|
||||
dev_err(dev, "cannot get otg clock\n");
|
||||
return PTR_ERR(hsotg->clk);
|
||||
hsotg->clk = NULL;
|
||||
dev_dbg(dev, "cannot get otg clock\n");
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hsotg);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
hsotg->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hsotg->regs)) {
|
||||
ret = PTR_ERR(hsotg->regs);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "cannot find IRQ\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
|
||||
hsotg->irq = ret;
|
||||
|
||||
dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
|
||||
|
||||
hsotg->gadget.max_speed = USB_SPEED_HIGH;
|
||||
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
|
||||
hsotg->gadget.name = dev_name(dev);
|
||||
|
||||
/* reset the system */
|
||||
|
||||
clk_prepare_enable(hsotg->clk);
|
||||
ret = clk_prepare_enable(hsotg->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable otg clk\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
|
||||
/* regulators */
|
||||
|
||||
@ -3509,7 +3483,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
hsotg->supplies);
|
||||
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret);
|
||||
dev_err(dev, "failed to enable supplies: %d\n", ret);
|
||||
goto err_supplies;
|
||||
}
|
||||
|
||||
@ -3520,14 +3494,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
s3c_hsotg_hw_cfg(hsotg);
|
||||
s3c_hsotg_init(hsotg);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
|
||||
dev_name(dev), hsotg);
|
||||
ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (ret < 0) {
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
dev_err(dev, "cannot claim IRQ\n");
|
||||
dev_err(dev, "cannot claim IRQ for gadget\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
@ -3573,11 +3547,11 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "failed to disable supplies: %d\n", ret);
|
||||
dev_err(dev, "failed to disable supplies: %d\n", ret);
|
||||
goto err_ep_mem;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
|
||||
ret = usb_add_gadget_udc(dev, &hsotg->gadget);
|
||||
if (ret)
|
||||
goto err_ep_mem;
|
||||
|
||||
@ -3596,47 +3570,44 @@ err_clk:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_gadget_init);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_remove - remove function for hsotg driver
|
||||
* @pdev: The platform information for the driver
|
||||
*/
|
||||
static int s3c_hsotg_remove(struct platform_device *pdev)
|
||||
int s3c_hsotg_remove(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&hsotg->gadget);
|
||||
|
||||
s3c_hsotg_delete_debug(hsotg);
|
||||
|
||||
if (hsotg->driver) {
|
||||
/* should have been done already by driver model core */
|
||||
usb_gadget_unregister_driver(hsotg->driver);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_remove);
|
||||
|
||||
static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->driver)
|
||||
dev_info(hsotg->dev, "suspending usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
if (hsotg->driver) {
|
||||
int ep;
|
||||
|
||||
dev_info(hsotg->dev, "suspending usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (hsotg->enabled)
|
||||
s3c_hsotg_core_disconnect(hsotg);
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||
|
||||
@ -3645,57 +3616,37 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
clk_disable(hsotg->clk);
|
||||
}
|
||||
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_suspend);
|
||||
|
||||
static int s3c_hsotg_resume(struct platform_device *pdev)
|
||||
int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
if (hsotg->driver) {
|
||||
dev_info(hsotg->dev, "resuming usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
clk_enable(hsotg->clk);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
hsotg->supplies);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
if (hsotg->enabled)
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id s3c_hsotg_of_ids[] = {
|
||||
{ .compatible = "samsung,s3c6400-hsotg", },
|
||||
{ .compatible = "snps,dwc2", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver s3c_hsotg_driver = {
|
||||
.driver = {
|
||||
.name = "s3c-hsotg",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(s3c_hsotg_of_ids),
|
||||
},
|
||||
.probe = s3c_hsotg_probe,
|
||||
.remove = s3c_hsotg_remove,
|
||||
.suspend = s3c_hsotg_suspend,
|
||||
.resume = s3c_hsotg_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(s3c_hsotg_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:s3c-hsotg");
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_resume);
|
||||
|
@ -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 = ¶ms_bcm2835 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_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->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->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 @@ err_usb2phy_init:
|
||||
}
|
||||
|
||||
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_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_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_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,17 +134,33 @@ 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;
|
||||
}
|
||||
|
||||
exynos->dev = dev;
|
||||
exynos->clk = clk;
|
||||
|
||||
clk_prepare_enable(exynos->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);
|
||||
|
||||
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)) {
|
||||
ret = PTR_ERR(exynos->vdd33);
|
||||
@ -185,7 +202,9 @@ err4:
|
||||
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, ¶ms);
|
||||
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 @@ err0:
|
||||
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 @@ unknown:
|
||||
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 @@ unknown:
|
||||
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 @@ unknown:
|
||||
/* 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 @@ fail:
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
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,6 +681,223 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int hidg_get_minor(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(f_hid_opts);
|
||||
CONFIGFS_ATTR_OPS(f_hid_opts);
|
||||
|
||||
static void hid_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_hid_opts *opts = to_f_hid_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
static struct config_item_type hid_func_type = {
|
||||
.ct_item_ops = &hidg_item_ops,
|
||||
.ct_attrs = hid_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static inline void hidg_put_minor(int minor)
|
||||
{
|
||||
ida_simple_remove(&hidg_ida, minor);
|
||||
}
|
||||
|
||||
static void hidg_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_hid_opts *opts;
|
||||
|
||||
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);
|
||||
@ -654,102 +912,82 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
usb_ep_free_request(hidg->in_ep, hidg->req);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(hidg->report_desc);
|
||||
kfree(hidg);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* 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,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* usb_configuration */
|
||||
|
||||
int __init hidg_bind_config(struct usb_configuration *c,
|
||||
struct hidg_func_descriptor *fdesc, int index)
|
||||
static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_hidg *hidg;
|
||||
int status;
|
||||
|
||||
if (index >= minors)
|
||||
return -ENOENT;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
return ERR_PTR(-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;
|
||||
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");
|
||||
|
||||
status = alloc_chrdev_region(&dev, 0, count, "hidg");
|
||||
if (!status) {
|
||||
major = MAJOR(dev);
|
||||
minors = count;
|
||||
if (IS_ERR(hidg_class)) {
|
||||
status = PTR_ERR(hidg_class);
|
||||
hidg_class = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
status = alloc_chrdev_region(&dev, 0, count, "hidg");
|
||||
if (status) {
|
||||
class_destroy(hidg_class);
|
||||
hidg_class = NULL;
|
||||
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 @@ fail_f_midi:
|
||||
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 @@ fail:
|
||||
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)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
int status, i;
|
||||
return container_of(to_config_group(item), struct f_midi_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
|
||||
return -EINVAL;
|
||||
CONFIGFS_ATTR_STRUCT(f_midi_opts);
|
||||
CONFIGFS_ATTR_OPS(f_midi_opts);
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
midi = kzalloc(sizeof *midi, GFP_KERNEL);
|
||||
if (!midi) {
|
||||
status = -ENOMEM;
|
||||
goto fail;
|
||||
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;
|
||||
}
|
||||
|
||||
for (i = 0; i < in_ports; i++) {
|
||||
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 (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);
|
||||
if (!midi) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
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->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.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->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;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
42
drivers/usb/gadget/function/u_hid.h
Normal file
42
drivers/usb/gadget/function/u_hid.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 */
|
40
drivers/usb/gadget/function/u_midi.h
Normal file
40
drivers/usb/gadget/function/u_midi.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_3:
|
||||
dbgp.o_ep->driver_data = NULL;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
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 = {
|
||||
|
@ -17,11 +17,14 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/g_hid.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
#define DRIVER_DESC "HID Gadget"
|
||||
#define DRIVER_VERSION "2010/03/16"
|
||||
|
||||
#include "u_hid.h"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */
|
||||
@ -29,17 +32,9 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "f_hid.c"
|
||||
|
||||
|
||||
struct hidg_func_node {
|
||||
struct usb_function_instance *fi;
|
||||
struct usb_function *f;
|
||||
struct list_head node;
|
||||
struct hidg_func_descriptor *func;
|
||||
};
|
||||
@ -113,8 +108,8 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
|
||||
static int __init do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct hidg_func_node *e;
|
||||
int func = 0, status = 0;
|
||||
struct hidg_func_node *e, *n;
|
||||
int status = 0;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
c->descriptors = otg_desc;
|
||||
@ -122,11 +117,24 @@ static int __init do_config(struct usb_configuration *c)
|
||||
}
|
||||
|
||||
list_for_each_entry(e, &hidg_func_list, node) {
|
||||
status = hidg_bind_config(c, e->func, func++);
|
||||
if (status)
|
||||
break;
|
||||
e->f = usb_get_function(e->fi);
|
||||
if (IS_ERR(e->f))
|
||||
goto put;
|
||||
status = usb_add_function(c, e->f);
|
||||
if (status < 0) {
|
||||
usb_put_function(e->f);
|
||||
goto put;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
put:
|
||||
list_for_each_entry(n, &hidg_func_list, node) {
|
||||
if (n == e)
|
||||
break;
|
||||
usb_remove_function(c, n->f);
|
||||
usb_put_function(n->f);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -143,6 +151,8 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct list_head *tmp;
|
||||
struct hidg_func_node *n, *m;
|
||||
struct f_hid_opts *hid_opts;
|
||||
int status, funcs = 0;
|
||||
|
||||
list_for_each(tmp, &hidg_func_list)
|
||||
@ -151,10 +161,20 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
|
||||
if (!funcs)
|
||||
return -ENODEV;
|
||||
|
||||
/* set up HID */
|
||||
status = ghid_setup(cdev->gadget, funcs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
list_for_each_entry(n, &hidg_func_list, node) {
|
||||
n->fi = usb_get_function_instance("hid");
|
||||
if (IS_ERR(n->fi)) {
|
||||
status = PTR_ERR(n->fi);
|
||||
goto put;
|
||||
}
|
||||
hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
|
||||
hid_opts->subclass = n->func->subclass;
|
||||
hid_opts->protocol = n->func->protocol;
|
||||
hid_opts->report_length = n->func->report_length;
|
||||
hid_opts->report_desc_length = n->func->report_desc_length;
|
||||
hid_opts->report_desc = n->func->report_desc;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
@ -162,24 +182,37 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
|
||||
|
||||
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;
|
||||
|
||||
/* register our configuration */
|
||||
status = usb_add_config(cdev, &config_driver, do_config);
|
||||
if (status < 0)
|
||||
return status;
|
||||
goto put;
|
||||
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
||||
|
||||
return 0;
|
||||
|
||||
put:
|
||||
list_for_each_entry(m, &hidg_func_list, node) {
|
||||
if (m == n)
|
||||
break;
|
||||
usb_put_function_instance(m->fi);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit hid_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
ghid_cleanup();
|
||||
struct hidg_func_node *n;
|
||||
|
||||
list_for_each_entry(n, &hidg_func_list, node) {
|
||||
usb_put_function(n->f);
|
||||
usb_put_function_instance(n->fi);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -260,7 +293,7 @@ module_init(hidg_init);
|
||||
|
||||
static void __exit hidg_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&hidg_plat_driver);
|
||||
usb_composite_unregister(&hidg_driver);
|
||||
platform_driver_unregister(&hidg_plat_driver);
|
||||
}
|
||||
module_exit(hidg_cleanup);
|
||||
|
@ -208,6 +208,43 @@ static struct usb_descriptor_header *hs_printer_function[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* Added endpoint descriptors for 3.0 devices
|
||||
*/
|
||||
|
||||
static struct usb_endpoint_descriptor ss_ep_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
|
||||
.bLength = sizeof(ss_ep_in_comp_desc),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_ep_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
|
||||
.bLength = sizeof(ss_ep_out_comp_desc),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ss_printer_function[] = {
|
||||
(struct usb_descriptor_header *) &intf_desc,
|
||||
(struct usb_descriptor_header *) &ss_ep_in_desc,
|
||||
(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_ep_out_desc,
|
||||
(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
@ -220,7 +257,20 @@ static const struct usb_descriptor_header *otg_desc[] = {
|
||||
};
|
||||
|
||||
/* maxpacket and other transfer characteristics vary by speed. */
|
||||
#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs))
|
||||
static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *fs,
|
||||
struct usb_endpoint_descriptor *hs,
|
||||
struct usb_endpoint_descriptor *ss)
|
||||
{
|
||||
switch (gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
return ss;
|
||||
case USB_SPEED_HIGH:
|
||||
return hs;
|
||||
default:
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -793,11 +843,12 @@ set_printer_interface(struct printer_dev *dev)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc);
|
||||
dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
|
||||
&ss_ep_in_desc);
|
||||
dev->in_ep->driver_data = dev;
|
||||
|
||||
dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc,
|
||||
&fs_ep_out_desc);
|
||||
dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
|
||||
&hs_ep_out_desc, &ss_ep_out_desc);
|
||||
dev->out_ep->driver_data = dev;
|
||||
|
||||
result = usb_ep_enable(dev->in_ep);
|
||||
@ -1016,9 +1067,11 @@ autoconf_fail:
|
||||
/* assumes that all endpoints are dual-speed */
|
||||
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
|
||||
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
|
||||
ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
|
||||
ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_printer_function,
|
||||
hs_printer_function, NULL);
|
||||
hs_printer_function, ss_printer_function);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1253,7 +1306,7 @@ static __refdata struct usb_composite_driver printer_driver = {
|
||||
.name = shortname,
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
.bind = printer_bind,
|
||||
.unbind = printer_unbind,
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -840,6 +840,31 @@ static void udc_reinit(struct at91_udc *udc)
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_gadget(struct at91_udc *udc)
|
||||
{
|
||||
struct usb_gadget_driver *driver = udc->driver;
|
||||
int i;
|
||||
|
||||
if (udc->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
driver = NULL;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->suspended = 0;
|
||||
|
||||
for (i = 0; i < NUM_ENDPOINTS; i++) {
|
||||
struct at91_ep *ep = &udc->ep[i];
|
||||
|
||||
ep->stopped = 1;
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
}
|
||||
if (driver) {
|
||||
spin_unlock(&udc->lock);
|
||||
usb_gadget_udc_reset(&udc->gadget, driver);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
|
||||
udc_reinit(udc);
|
||||
}
|
||||
|
||||
static void stop_activity(struct at91_udc *udc)
|
||||
{
|
||||
struct usb_gadget_driver *driver = udc->driver;
|
||||
@ -870,12 +895,10 @@ static void clk_on(struct at91_udc *udc)
|
||||
return;
|
||||
udc->clocked = 1;
|
||||
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
|
||||
clk_set_rate(udc->uclk, 48000000);
|
||||
clk_prepare_enable(udc->uclk);
|
||||
}
|
||||
clk_prepare_enable(udc->iclk);
|
||||
clk_prepare_enable(udc->fclk);
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
clk_enable(udc->uclk);
|
||||
clk_enable(udc->iclk);
|
||||
clk_enable(udc->fclk);
|
||||
}
|
||||
|
||||
static void clk_off(struct at91_udc *udc)
|
||||
@ -884,10 +907,10 @@ static void clk_off(struct at91_udc *udc)
|
||||
return;
|
||||
udc->clocked = 0;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
clk_disable_unprepare(udc->fclk);
|
||||
clk_disable_unprepare(udc->iclk);
|
||||
clk_disable(udc->fclk);
|
||||
clk_disable(udc->iclk);
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
clk_disable_unprepare(udc->uclk);
|
||||
clk_disable(udc->uclk);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -984,8 +1007,8 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
|
||||
|
||||
static int at91_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int at91_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int at91_stop(struct usb_gadget *gadget);
|
||||
|
||||
static const struct usb_gadget_ops at91_udc_ops = {
|
||||
.get_frame = at91_get_frame,
|
||||
.wakeup = at91_wakeup,
|
||||
@ -1426,7 +1449,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
|
||||
at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
|
||||
VDBG("end bus reset\n");
|
||||
udc->addr = 0;
|
||||
stop_activity(udc);
|
||||
reset_gadget(udc);
|
||||
|
||||
/* enable ep0 */
|
||||
at91_udp_write(udc, AT91_UDP_CSR(0),
|
||||
@ -1512,20 +1535,11 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void nop_release(struct device *dev)
|
||||
{
|
||||
/* nothing to free */
|
||||
}
|
||||
|
||||
static struct at91_udc controller = {
|
||||
.gadget = {
|
||||
.ops = &at91_udc_ops,
|
||||
.ep0 = &controller.ep[0].ep,
|
||||
.name = driver_name,
|
||||
.dev = {
|
||||
.init_name = "gadget",
|
||||
.release = nop_release,
|
||||
}
|
||||
},
|
||||
.ep[0] = {
|
||||
.ep = {
|
||||
@ -1641,12 +1655,10 @@ static int at91_start(struct usb_gadget *gadget,
|
||||
udc->enabled = 1;
|
||||
udc->selfpowered = 1;
|
||||
|
||||
DBG("bound to %s\n", driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int at91_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct at91_udc *udc;
|
||||
unsigned long flags;
|
||||
@ -1659,7 +1671,6 @@ static int at91_stop(struct usb_gadget *gadget,
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
DBG("unbound from %s\n", driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1780,14 +1791,24 @@ static int at91udc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* don't do anything until we have both gadget driver and VBUS */
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
|
||||
clk_set_rate(udc->uclk, 48000000);
|
||||
retval = clk_prepare(udc->uclk);
|
||||
if (retval)
|
||||
goto fail1;
|
||||
}
|
||||
retval = clk_prepare(udc->fclk);
|
||||
if (retval)
|
||||
goto fail1a;
|
||||
|
||||
retval = clk_prepare_enable(udc->iclk);
|
||||
if (retval)
|
||||
goto fail1;
|
||||
goto fail1b;
|
||||
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
|
||||
at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff);
|
||||
/* Clear all pending interrupts - UDP may be used by bootloader. */
|
||||
at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff);
|
||||
clk_disable_unprepare(udc->iclk);
|
||||
clk_disable(udc->iclk);
|
||||
|
||||
/* request UDC and maybe VBUS irqs */
|
||||
udc->udp_irq = platform_get_irq(pdev, 0);
|
||||
@ -1795,7 +1816,7 @@ static int at91udc_probe(struct platform_device *pdev)
|
||||
0, driver_name, udc);
|
||||
if (retval < 0) {
|
||||
DBG("request irq %d failed\n", udc->udp_irq);
|
||||
goto fail1;
|
||||
goto fail1c;
|
||||
}
|
||||
if (gpio_is_valid(udc->board.vbus_pin)) {
|
||||
retval = gpio_request(udc->board.vbus_pin, "udc_vbus");
|
||||
@ -1848,6 +1869,13 @@ fail3:
|
||||
gpio_free(udc->board.vbus_pin);
|
||||
fail2:
|
||||
free_irq(udc->udp_irq, udc);
|
||||
fail1c:
|
||||
clk_unprepare(udc->iclk);
|
||||
fail1b:
|
||||
clk_unprepare(udc->fclk);
|
||||
fail1a:
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
clk_unprepare(udc->uclk);
|
||||
fail1:
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk))
|
||||
clk_put(udc->uclk);
|
||||
@ -1896,6 +1924,11 @@ static int __exit at91udc_remove(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
clk_unprepare(udc->uclk);
|
||||
clk_unprepare(udc->fclk);
|
||||
clk_unprepare(udc->iclk);
|
||||
|
||||
clk_put(udc->iclk);
|
||||
clk_put(udc->fclk);
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
|
@ -987,8 +987,8 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
|
||||
|
||||
static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget);
|
||||
|
||||
static const struct usb_gadget_ops usba_udc_ops = {
|
||||
.get_frame = usba_udc_get_frame,
|
||||
.wakeup = usba_udc_wakeup,
|
||||
@ -1007,19 +1007,10 @@ static struct usb_endpoint_descriptor usba_ep0_desc = {
|
||||
.bInterval = 1,
|
||||
};
|
||||
|
||||
static void nop_release(struct device *dev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static struct usb_gadget usba_gadget_template = {
|
||||
.ops = &usba_udc_ops,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.name = "atmel_usba_udc",
|
||||
.dev = {
|
||||
.init_name = "gadget",
|
||||
.release = nop_release,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1685,11 +1676,10 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
|
||||
reset_all_endpoints(udc);
|
||||
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN
|
||||
&& udc->driver && udc->driver->disconnect) {
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock(&udc->lock);
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
usb_gadget_udc_reset(&udc->gadget, udc->driver);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
|
||||
@ -1791,8 +1781,6 @@ static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
return ret;
|
||||
}
|
||||
|
||||
DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name);
|
||||
|
||||
udc->vbus_prev = 0;
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
enable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
@ -1809,8 +1797,7 @@ static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
|
||||
unsigned long flags;
|
||||
@ -1830,8 +1817,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
|
||||
DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
return 0;
|
||||
|
@ -1836,8 +1836,7 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget,
|
||||
* @gadget: USB slave device.
|
||||
* @driver: Driver for USB slave devices.
|
||||
*/
|
||||
static int bcm63xx_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int bcm63xx_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct bcm63xx_udc *udc = gadget_to_udc(gadget);
|
||||
unsigned long flags;
|
||||
@ -1963,7 +1962,7 @@ static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct bcm63xx_udc *udc = dev_id;
|
||||
u32 stat;
|
||||
bool disconnected = false;
|
||||
bool disconnected = false, bus_reset = false;
|
||||
|
||||
stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) &
|
||||
usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG);
|
||||
@ -1991,7 +1990,7 @@ static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
|
||||
|
||||
udc->ep0_req_reset = 1;
|
||||
schedule_work(&udc->ep0_wq);
|
||||
disconnected = true;
|
||||
bus_reset = true;
|
||||
}
|
||||
if (stat & BIT(USBD_EVENT_IRQ_SETUP)) {
|
||||
if (bcm63xx_update_link_speed(udc)) {
|
||||
@ -2014,6 +2013,8 @@ static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
|
||||
|
||||
if (disconnected && udc->driver)
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
else if (bus_reset && udc->driver)
|
||||
usb_gadget_udc_reset(&udc->gadget, udc->driver);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -2324,10 +2325,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
||||
int rc = -ENOMEM, i, irq;
|
||||
|
||||
udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
|
||||
if (!udc) {
|
||||
dev_err(dev, "cannot allocate memory\n");
|
||||
if (!udc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, udc);
|
||||
udc->dev = dev;
|
||||
|
21
drivers/usb/gadget/udc/bdc/Kconfig
Normal file
21
drivers/usb/gadget/udc/bdc/Kconfig
Normal file
@ -0,0 +1,21 @@
|
||||
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
|
8
drivers/usb/gadget/udc/bdc/Makefile
Normal file
8
drivers/usb/gadget/udc/bdc/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
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
|
490
drivers/usb/gadget/udc/bdc/bdc.h
Normal file
490
drivers/usb/gadget/udc/bdc/bdc.h
Normal file
@ -0,0 +1,490 @@
|
||||
/*
|
||||
* bdc.h - header for the BRCM BDC USB3.0 device controller
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BDC_H__
|
||||
#define __LINUX_BDC_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define BRCM_BDC_NAME "bdc_usb3"
|
||||
#define BRCM_BDC_DESC "BDC device controller driver"
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
/* BDC command operation timeout in usec*/
|
||||
#define BDC_CMD_TIMEOUT 1000
|
||||
/* BDC controller operation timeout in usec*/
|
||||
#define BDC_COP_TIMEOUT 500
|
||||
|
||||
/*
|
||||
* Maximum size of ep0 response buffer for ch9 requests,
|
||||
* the set_sel request uses 6 so far, the max.
|
||||
*/
|
||||
#define EP0_RESPONSE_BUFF 6
|
||||
/* Start with SS as default */
|
||||
#define EP0_MAX_PKT_SIZE 512
|
||||
|
||||
/* 64 entries in a SRR */
|
||||
#define NUM_SR_ENTRIES 64
|
||||
|
||||
/* Num of bds per table */
|
||||
#define NUM_BDS_PER_TABLE 32
|
||||
|
||||
/* Num of tables in bd list for control,bulk and Int ep */
|
||||
#define NUM_TABLES 2
|
||||
|
||||
/* Num of tables in bd list for Isoch ep */
|
||||
#define NUM_TABLES_ISOCH 6
|
||||
|
||||
/* U1 Timeout default: 248usec */
|
||||
#define U1_TIMEOUT 0xf8
|
||||
|
||||
/* Interrupt coalescence in usec */
|
||||
#define INT_CLS 500
|
||||
|
||||
/* Register offsets */
|
||||
/* Configuration and Capability registers */
|
||||
#define BDC_BDCCFG0 0x00
|
||||
#define BDC_BDCCFG1 0x04
|
||||
#define BDC_BDCCAP0 0x08
|
||||
#define BDC_BDCCAP1 0x0c
|
||||
#define BDC_CMDPAR0 0x10
|
||||
#define BDC_CMDPAR1 0x14
|
||||
#define BDC_CMDPAR2 0x18
|
||||
#define BDC_CMDSC 0x1c
|
||||
#define BDC_USPC 0x20
|
||||
#define BDC_USPPMS 0x28
|
||||
#define BDC_USPPM2 0x2c
|
||||
#define BDC_SPBBAL 0x38
|
||||
#define BDC_SPBBAH 0x3c
|
||||
#define BDC_BDCSC 0x40
|
||||
#define BDC_XSFNTF 0x4c
|
||||
|
||||
#define BDC_DVCSA 0x50
|
||||
#define BDC_DVCSB 0x54
|
||||
#define BDC_EPSTS0(n) (0x60 + (n * 0x10))
|
||||
#define BDC_EPSTS1(n) (0x64 + (n * 0x10))
|
||||
#define BDC_EPSTS2(n) (0x68 + (n * 0x10))
|
||||
#define BDC_EPSTS3(n) (0x6c + (n * 0x10))
|
||||
#define BDC_EPSTS4(n) (0x70 + (n * 0x10))
|
||||
#define BDC_EPSTS5(n) (0x74 + (n * 0x10))
|
||||
#define BDC_EPSTS6(n) (0x78 + (n * 0x10))
|
||||
#define BDC_EPSTS7(n) (0x7c + (n * 0x10))
|
||||
#define BDC_SRRBAL(n) (0x200 + (n * 0x10))
|
||||
#define BDC_SRRBAH(n) (0x204 + (n * 0x10))
|
||||
#define BDC_SRRINT(n) (0x208 + (n * 0x10))
|
||||
#define BDC_INTCTLS(n) (0x20c + (n * 0x10))
|
||||
|
||||
/* Extended capability regs */
|
||||
#define BDC_FSCNOC 0xcd4
|
||||
#define BDC_FSCNIC 0xce4
|
||||
#define NUM_NCS(p) (p >> 28)
|
||||
|
||||
/* Register bit fields and Masks */
|
||||
/* BDC Configuration 0 */
|
||||
#define BDC_PGS(p) (((p) & (0x7 << 8)) >> 8)
|
||||
#define BDC_SPB(p) (p & 0x7)
|
||||
|
||||
/* BDC Capability1 */
|
||||
#define BDC_P64 (1 << 0)
|
||||
|
||||
/* BDC Command register */
|
||||
#define BDC_CMD_FH 0xe
|
||||
#define BDC_CMD_DNC 0x6
|
||||
#define BDC_CMD_EPO 0x4
|
||||
#define BDC_CMD_BLA 0x3
|
||||
#define BDC_CMD_EPC 0x2
|
||||
#define BDC_CMD_DVC 0x1
|
||||
#define BDC_CMD_CWS (0x1 << 5)
|
||||
#define BDC_CMD_CST(p) (((p) & (0xf << 6))>>6)
|
||||
#define BDC_CMD_EPN(p) ((p & 0x1f) << 10)
|
||||
#define BDC_SUB_CMD_ADD (0x1 << 17)
|
||||
#define BDC_SUB_CMD_FWK (0x4 << 17)
|
||||
/* Reset sequence number */
|
||||
#define BDC_CMD_EPO_RST_SN (0x1 << 16)
|
||||
#define BDC_CMD_EP0_XSD (0x1 << 16)
|
||||
#define BDC_SUB_CMD_ADD_EP (0x1 << 17)
|
||||
#define BDC_SUB_CMD_DRP_EP (0x2 << 17)
|
||||
#define BDC_SUB_CMD_EP_STP (0x2 << 17)
|
||||
#define BDC_SUB_CMD_EP_STL (0x4 << 17)
|
||||
#define BDC_SUB_CMD_EP_RST (0x1 << 17)
|
||||
#define BDC_CMD_SRD (1 << 27)
|
||||
|
||||
/* CMD completion status */
|
||||
#define BDC_CMDS_SUCC 0x1
|
||||
#define BDC_CMDS_PARA 0x3
|
||||
#define BDC_CMDS_STAT 0x4
|
||||
#define BDC_CMDS_FAIL 0x5
|
||||
#define BDC_CMDS_INTL 0x6
|
||||
#define BDC_CMDS_BUSY 0xf
|
||||
|
||||
/* CMDSC Param 2 shifts */
|
||||
#define EPT_SHIFT 22
|
||||
#define MP_SHIFT 10
|
||||
#define MB_SHIFT 6
|
||||
#define EPM_SHIFT 4
|
||||
|
||||
/* BDC USPSC */
|
||||
#define BDC_VBC (1 << 31)
|
||||
#define BDC_PRC (1 << 30)
|
||||
#define BDC_PCE (1 << 29)
|
||||
#define BDC_CFC (1 << 28)
|
||||
#define BDC_PCC (1 << 27)
|
||||
#define BDC_PSC (1 << 26)
|
||||
#define BDC_VBS (1 << 25)
|
||||
#define BDC_PRS (1 << 24)
|
||||
#define BDC_PCS (1 << 23)
|
||||
#define BDC_PSP(p) (((p) & (0x7 << 20))>>20)
|
||||
#define BDC_SCN (1 << 8)
|
||||
#define BDC_SDC (1 << 7)
|
||||
#define BDC_SWS (1 << 4)
|
||||
|
||||
#define BDC_USPSC_RW (BDC_SCN|BDC_SDC|BDC_SWS|0xf)
|
||||
#define BDC_PSP(p) (((p) & (0x7 << 20))>>20)
|
||||
|
||||
#define BDC_SPEED_FS 0x1
|
||||
#define BDC_SPEED_LS 0x2
|
||||
#define BDC_SPEED_HS 0x3
|
||||
#define BDC_SPEED_SS 0x4
|
||||
|
||||
#define BDC_PST(p) (p & 0xf)
|
||||
#define BDC_PST_MASK 0xf
|
||||
|
||||
/* USPPMS */
|
||||
#define BDC_U2E (0x1 << 31)
|
||||
#define BDC_U1E (0x1 << 30)
|
||||
#define BDC_U2A (0x1 << 29)
|
||||
#define BDC_PORT_W1S (0x1 << 17)
|
||||
#define BDC_U1T(p) ((p) & 0xff)
|
||||
#define BDC_U2T(p) (((p) & 0xff) << 8)
|
||||
#define BDC_U1T_MASK 0xff
|
||||
|
||||
/* USBPM2 */
|
||||
/* Hardware LPM Enable */
|
||||
#define BDC_HLE (1 << 16)
|
||||
|
||||
/* BDC Status and Control */
|
||||
#define BDC_COP_RST (1 << 29)
|
||||
#define BDC_COP_RUN (2 << 29)
|
||||
#define BDC_COP_STP (4 << 29)
|
||||
|
||||
#define BDC_COP_MASK (BDC_COP_RST|BDC_COP_RUN|BDC_COP_STP)
|
||||
|
||||
#define BDC_COS (1 << 28)
|
||||
#define BDC_CSTS(p) (((p) & (0x7 << 20)) >> 20)
|
||||
#define BDC_MASK_MCW (1 << 7)
|
||||
#define BDC_GIE (1 << 1)
|
||||
#define BDC_GIP (1 << 0)
|
||||
|
||||
#define BDC_HLT 1
|
||||
#define BDC_NOR 2
|
||||
#define BDC_OIP 7
|
||||
|
||||
/* Buffer descriptor and Status report bit fields and masks */
|
||||
#define BD_TYPE_BITMASK (0xf)
|
||||
#define BD_CHAIN 0xf
|
||||
|
||||
#define BD_TFS_SHIFT 4
|
||||
#define BD_SOT (1 << 26)
|
||||
#define BD_EOT (1 << 27)
|
||||
#define BD_ISP (1 << 29)
|
||||
#define BD_IOC (1 << 30)
|
||||
#define BD_SBF (1 << 31)
|
||||
|
||||
#define BD_INTR_TARGET(p) (((p) & 0x1f) << 27)
|
||||
|
||||
#define BDC_SRR_RWS (1 << 4)
|
||||
#define BDC_SRR_RST (1 << 3)
|
||||
#define BDC_SRR_ISR (1 << 2)
|
||||
#define BDC_SRR_IE (1 << 1)
|
||||
#define BDC_SRR_IP (1 << 0)
|
||||
#define BDC_SRR_EPI(p) (((p) & (0xff << 24)) >> 24)
|
||||
#define BDC_SRR_DPI(p) (((p) & (0xff << 16)) >> 16)
|
||||
#define BDC_SRR_DPI_MASK 0x00ff0000
|
||||
|
||||
#define MARK_CHAIN_BD (BD_CHAIN|BD_EOT|BD_SOT)
|
||||
|
||||
/* Control transfer BD specific fields */
|
||||
#define BD_DIR_IN (1 << 25)
|
||||
|
||||
#define BDC_PTC_MASK 0xf0000000
|
||||
|
||||
/* status report defines */
|
||||
#define SR_XSF 0
|
||||
#define SR_USPC 4
|
||||
#define SR_BD_LEN(p) (p & 0xffffff)
|
||||
|
||||
#define XSF_SUCC 0x1
|
||||
#define XSF_SHORT 0x3
|
||||
#define XSF_BABB 0x4
|
||||
#define XSF_SETUP_RECV 0x6
|
||||
#define XSF_DATA_START 0x7
|
||||
#define XSF_STATUS_START 0x8
|
||||
|
||||
#define XSF_STS(p) (((p) >> 28) & 0xf)
|
||||
|
||||
/* Transfer BD fields */
|
||||
#define BD_LEN(p) ((p) & 0x1ffff)
|
||||
#define BD_LTF (1 << 25)
|
||||
#define BD_TYPE_DS 0x1
|
||||
#define BD_TYPE_SS 0x2
|
||||
|
||||
#define BDC_EP_ENABLED (1 << 0)
|
||||
#define BDC_EP_STALL (1 << 1)
|
||||
#define BDC_EP_STOP (1 << 2)
|
||||
|
||||
/* One BD can transfer max 65536 bytes */
|
||||
#define BD_MAX_BUFF_SIZE (1 << 16)
|
||||
/* Maximum bytes in one XFR, Refer to BDC spec */
|
||||
#define MAX_XFR_LEN 16777215
|
||||
|
||||
/* defines for Force Header command */
|
||||
#define DEV_NOTF_TYPE 6
|
||||
#define FWK_SUBTYPE 1
|
||||
#define TRA_PACKET 4
|
||||
|
||||
#define to_bdc_ep(e) container_of(e, struct bdc_ep, usb_ep)
|
||||
#define to_bdc_req(r) container_of(r, struct bdc_req, usb_req)
|
||||
#define gadget_to_bdc(g) container_of(g, struct bdc, gadget)
|
||||
|
||||
/* FUNCTION WAKE DEV NOTIFICATION interval, USB3 spec table 8.13 */
|
||||
#define BDC_TNOTIFY 2500 /*in ms*/
|
||||
/* Devstatus bitfields */
|
||||
#define REMOTE_WAKEUP_ISSUED (1 << 16)
|
||||
#define DEVICE_SUSPENDED (1 << 17)
|
||||
#define FUNC_WAKE_ISSUED (1 << 18)
|
||||
#define REMOTE_WAKE_ENABLE (1 << USB_DEVICE_REMOTE_WAKEUP)
|
||||
|
||||
/* On disconnect, preserve these bits and clear rest */
|
||||
#define DEVSTATUS_CLEAR (1 << USB_DEVICE_SELF_POWERED)
|
||||
/* Hardware and software Data structures */
|
||||
|
||||
/* Endpoint bd: buffer descriptor */
|
||||
struct bdc_bd {
|
||||
__le32 offset[4];
|
||||
};
|
||||
|
||||
/* Status report in Status report ring(srr) */
|
||||
struct bdc_sr {
|
||||
__le32 offset[4];
|
||||
};
|
||||
|
||||
/* bd_table: contigous bd's in a table */
|
||||
struct bd_table {
|
||||
struct bdc_bd *start_bd;
|
||||
/* dma address of start bd of table*/
|
||||
dma_addr_t dma;
|
||||
};
|
||||
|
||||
/*
|
||||
* Each endpoint has a bdl(buffer descriptor list), bdl consists of 1 or more bd
|
||||
* table's chained to each other through a chain bd, every table has equal
|
||||
* number of bds. the software uses bdi(bd index) to refer to particular bd in
|
||||
* the list.
|
||||
*/
|
||||
struct bd_list {
|
||||
/* Array of bd table pointers*/
|
||||
struct bd_table **bd_table_array;
|
||||
/* How many tables chained to each other */
|
||||
int num_tabs;
|
||||
/* Max_bdi = num_tabs * num_bds_table - 1 */
|
||||
int max_bdi;
|
||||
/* current enq bdi from sw point of view */
|
||||
int eqp_bdi;
|
||||
/* current deq bdi from sw point of view */
|
||||
int hwd_bdi;
|
||||
/* numbers of bds per table */
|
||||
int num_bds_table;
|
||||
};
|
||||
|
||||
struct bdc_req;
|
||||
|
||||
/* Representation of a transfer, one transfer can have multiple bd's */
|
||||
struct bd_transfer {
|
||||
struct bdc_req *req;
|
||||
/* start bd index */
|
||||
int start_bdi;
|
||||
/* this will be the next hw dqp when this transfer completes */
|
||||
int next_hwd_bdi;
|
||||
/* number of bds in this transfer */
|
||||
int num_bds;
|
||||
};
|
||||
|
||||
/*
|
||||
* Representation of a gadget request, every gadget request is contained
|
||||
* by 1 bd_transfer.
|
||||
*/
|
||||
struct bdc_req {
|
||||
struct usb_request usb_req;
|
||||
struct list_head queue;
|
||||
struct bdc_ep *ep;
|
||||
/* only one Transfer per request */
|
||||
struct bd_transfer bd_xfr;
|
||||
int epnum;
|
||||
};
|
||||
|
||||
/* scratchpad buffer needed by bdc hardware */
|
||||
struct bdc_scratchpad {
|
||||
dma_addr_t sp_dma;
|
||||
void *buff;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
/* endpoint representation */
|
||||
struct bdc_ep {
|
||||
struct usb_ep usb_ep;
|
||||
struct list_head queue;
|
||||
struct bdc *bdc;
|
||||
u8 ep_type;
|
||||
u8 dir;
|
||||
u8 ep_num;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
unsigned int flags;
|
||||
char name[20];
|
||||
/* endpoint bd list*/
|
||||
struct bd_list bd_list;
|
||||
/*
|
||||
* HW generates extra event for multi bd tranfers, this flag helps in
|
||||
* ignoring the extra event
|
||||
*/
|
||||
bool ignore_next_sr;
|
||||
};
|
||||
|
||||
/* bdc cmmand parameter structure */
|
||||
struct bdc_cmd_params {
|
||||
u32 param2;
|
||||
u32 param1;
|
||||
u32 param0;
|
||||
};
|
||||
|
||||
/* status report ring(srr), currently one srr is supported for entire system */
|
||||
struct srr {
|
||||
struct bdc_sr *sr_bds;
|
||||
u16 eqp_index;
|
||||
u16 dqp_index;
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
||||
/* EP0 states */
|
||||
enum bdc_ep0_state {
|
||||
WAIT_FOR_SETUP = 0,
|
||||
WAIT_FOR_DATA_START,
|
||||
WAIT_FOR_DATA_XMIT,
|
||||
WAIT_FOR_STATUS_START,
|
||||
WAIT_FOR_STATUS_XMIT,
|
||||
STATUS_PENDING
|
||||
};
|
||||
|
||||
/* Link states */
|
||||
enum bdc_link_state {
|
||||
BDC_LINK_STATE_U0 = 0x00,
|
||||
BDC_LINK_STATE_U3 = 0x03,
|
||||
BDC_LINK_STATE_RX_DET = 0x05,
|
||||
BDC_LINK_STATE_RESUME = 0x0f
|
||||
};
|
||||
|
||||
/* representation of bdc */
|
||||
struct bdc {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *gadget_driver;
|
||||
struct device *dev;
|
||||
/* device lock */
|
||||
spinlock_t lock;
|
||||
|
||||
/* num of endpoints for a particular instantiation of IP */
|
||||
unsigned int num_eps;
|
||||
/*
|
||||
* Array of ep's, it uses the same index covention as bdc hw i.e.
|
||||
* 1 for ep0, 2 for 1out,3 for 1in ....
|
||||
*/
|
||||
struct bdc_ep **bdc_ep_array;
|
||||
void __iomem *regs;
|
||||
struct bdc_scratchpad scratchpad;
|
||||
u32 sp_buff_size;
|
||||
/* current driver supports 1 status ring */
|
||||
struct srr srr;
|
||||
/* Last received setup packet */
|
||||
struct usb_ctrlrequest setup_pkt;
|
||||
struct bdc_req ep0_req;
|
||||
struct bdc_req status_req;
|
||||
enum bdc_ep0_state ep0_state;
|
||||
bool delayed_status;
|
||||
bool zlp_needed;
|
||||
bool reinit;
|
||||
bool pullup;
|
||||
/* Bits 0-15 are standard and 16-31 for proprietary information */
|
||||
u32 devstatus;
|
||||
int irq;
|
||||
void *mem;
|
||||
u32 dev_addr;
|
||||
/* DMA pools */
|
||||
struct dma_pool *bd_table_pool;
|
||||
u8 test_mode;
|
||||
/* array of callbacks for various status report handlers */
|
||||
void (*sr_handler[2])(struct bdc *, struct bdc_sr *);
|
||||
/* ep0 callback handlers */
|
||||
void (*sr_xsf_ep0[3])(struct bdc *, struct bdc_sr *);
|
||||
/* ep0 response buffer for ch9 requests like GET_STATUS and SET_SEL */
|
||||
unsigned char ep0_response_buff[EP0_RESPONSE_BUFF];
|
||||
/*
|
||||
* Timer to check if host resumed transfer after bdc sent Func wake
|
||||
* notification packet after a remote wakeup. if not, then resend the
|
||||
* Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4
|
||||
*/
|
||||
struct delayed_work func_wake_notify;
|
||||
};
|
||||
|
||||
static inline u32 bdc_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void bdc_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
/* Buffer descriptor list operations */
|
||||
void bdc_notify_xfr(struct bdc *, u32);
|
||||
void bdc_softconn(struct bdc *);
|
||||
void bdc_softdisconn(struct bdc *);
|
||||
int bdc_run(struct bdc *);
|
||||
int bdc_stop(struct bdc *);
|
||||
int bdc_reset(struct bdc *);
|
||||
int bdc_udc_init(struct bdc *);
|
||||
void bdc_udc_exit(struct bdc *);
|
||||
int bdc_reinit(struct bdc *);
|
||||
|
||||
/* Status report handlers */
|
||||
/* Upstream port status change sr */
|
||||
void bdc_sr_uspc(struct bdc *, struct bdc_sr *);
|
||||
/* transfer sr */
|
||||
void bdc_sr_xsf(struct bdc *, struct bdc_sr *);
|
||||
/* EP0 XSF handlers */
|
||||
void bdc_xsf_ep0_setup_recv(struct bdc *, struct bdc_sr *);
|
||||
void bdc_xsf_ep0_data_start(struct bdc *, struct bdc_sr *);
|
||||
void bdc_xsf_ep0_status_start(struct bdc *, struct bdc_sr *);
|
||||
|
||||
#endif /* __LINUX_BDC_H__ */
|
376
drivers/usb/gadget/udc/bdc/bdc_cmd.c
Normal file
376
drivers/usb/gadget/udc/bdc/bdc_cmd.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
* bdc_cmd.c - BRCM BDC USB3.0 device controller
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "bdc.h"
|
||||
#include "bdc_cmd.h"
|
||||
#include "bdc_dbg.h"
|
||||
|
||||
/* Issues a cmd to cmd processor and waits for cmd completion */
|
||||
static int bdc_issue_cmd(struct bdc *bdc, u32 cmd_sc, u32 param0,
|
||||
u32 param1, u32 param2)
|
||||
{
|
||||
u32 timeout = BDC_CMD_TIMEOUT;
|
||||
u32 cmd_status;
|
||||
u32 temp;
|
||||
|
||||
bdc_writel(bdc->regs, BDC_CMDPAR0, param0);
|
||||
bdc_writel(bdc->regs, BDC_CMDPAR1, param1);
|
||||
bdc_writel(bdc->regs, BDC_CMDPAR2, param2);
|
||||
|
||||
/* Issue the cmd */
|
||||
/* Make sure the cmd params are written before asking HW to exec cmd */
|
||||
wmb();
|
||||
bdc_writel(bdc->regs, BDC_CMDSC, cmd_sc | BDC_CMD_CWS | BDC_CMD_SRD);
|
||||
do {
|
||||
temp = bdc_readl(bdc->regs, BDC_CMDSC);
|
||||
dev_dbg_ratelimited(bdc->dev, "cmdsc=%x", temp);
|
||||
cmd_status = BDC_CMD_CST(temp);
|
||||
if (cmd_status != BDC_CMDS_BUSY) {
|
||||
dev_dbg(bdc->dev,
|
||||
"command completed cmd_sts:%x\n", cmd_status);
|
||||
return cmd_status;
|
||||
}
|
||||
udelay(1);
|
||||
} while (timeout--);
|
||||
|
||||
dev_err(bdc->dev,
|
||||
"command operation timedout cmd_status=%d\n", cmd_status);
|
||||
|
||||
return cmd_status;
|
||||
}
|
||||
|
||||
/* Submits cmd and analyze the return value of bdc_issue_cmd */
|
||||
static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
|
||||
u32 param0, u32 param1, u32 param2)
|
||||
{
|
||||
u32 temp, cmd_status;
|
||||
int reset_bdc = 0;
|
||||
int ret;
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_CMDSC);
|
||||
dev_dbg(bdc->dev,
|
||||
"%s:CMDSC:%08x cmdsc:%08x param0=%08x param1=%08x param2=%08x\n",
|
||||
__func__, temp, cmd_sc, param0, param1, param2);
|
||||
|
||||
cmd_status = BDC_CMD_CST(temp);
|
||||
if (cmd_status == BDC_CMDS_BUSY) {
|
||||
dev_err(bdc->dev, "command processor busy: %x\n", cmd_status);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = bdc_issue_cmd(bdc, cmd_sc, param0, param1, param2);
|
||||
switch (ret) {
|
||||
case BDC_CMDS_SUCC:
|
||||
dev_dbg(bdc->dev, "command completed successfully\n");
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case BDC_CMDS_PARA:
|
||||
dev_err(bdc->dev, "command parameter error\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
||||
case BDC_CMDS_STAT:
|
||||
dev_err(bdc->dev, "Invalid device/ep state\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
||||
case BDC_CMDS_FAIL:
|
||||
dev_err(bdc->dev, "Command failed?\n");
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
|
||||
case BDC_CMDS_INTL:
|
||||
dev_err(bdc->dev, "BDC Internal error\n");
|
||||
reset_bdc = 1;
|
||||
ret = -ECONNRESET;
|
||||
break;
|
||||
|
||||
case BDC_CMDS_BUSY:
|
||||
dev_err(bdc->dev,
|
||||
"command timedout waited for %dusec\n",
|
||||
BDC_CMD_TIMEOUT);
|
||||
reset_bdc = 1;
|
||||
ret = -ECONNRESET;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(bdc->dev, "Unknown command completion code:%x\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Deconfigure the endpoint from HW */
|
||||
int bdc_dconfig_ep(struct bdc *bdc, struct bdc_ep *ep)
|
||||
{
|
||||
u32 cmd_sc;
|
||||
|
||||
cmd_sc = BDC_SUB_CMD_DRP_EP|BDC_CMD_EPN(ep->ep_num)|BDC_CMD_EPC;
|
||||
dev_dbg(bdc->dev, "%s ep->ep_num =%d cmd_sc=%x\n", __func__,
|
||||
ep->ep_num, cmd_sc);
|
||||
|
||||
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Reinitalize the bdlist after config ep command */
|
||||
static void ep_bd_list_reinit(struct bdc_ep *ep)
|
||||
{
|
||||
struct bdc *bdc = ep->bdc;
|
||||
struct bdc_bd *bd;
|
||||
|
||||
ep->bd_list.eqp_bdi = 0;
|
||||
ep->bd_list.hwd_bdi = 0;
|
||||
bd = ep->bd_list.bd_table_array[0]->start_bd;
|
||||
dev_dbg(bdc->dev, "%s ep:%p bd:%p\n", __func__, ep, bd);
|
||||
memset(bd, 0, sizeof(struct bdc_bd));
|
||||
bd->offset[3] |= cpu_to_le32(BD_SBF);
|
||||
}
|
||||
|
||||
/* Configure an endpoint */
|
||||
int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep)
|
||||
{
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
u32 param0, param1, param2, cmd_sc;
|
||||
u32 mps, mbs, mul, si;
|
||||
int ret;
|
||||
|
||||
desc = ep->desc;
|
||||
comp_desc = ep->comp_desc;
|
||||
cmd_sc = mul = mbs = param2 = 0;
|
||||
param0 = lower_32_bits(ep->bd_list.bd_table_array[0]->dma);
|
||||
param1 = upper_32_bits(ep->bd_list.bd_table_array[0]->dma);
|
||||
cpu_to_le32s(¶m0);
|
||||
cpu_to_le32s(¶m1);
|
||||
|
||||
dev_dbg(bdc->dev, "%s: param0=%08x param1=%08x",
|
||||
__func__, param0, param1);
|
||||
si = desc->bInterval;
|
||||
si = clamp_val(si, 1, 16) - 1;
|
||||
|
||||
mps = usb_endpoint_maxp(desc);
|
||||
mps &= 0x7ff;
|
||||
param2 |= mps << MP_SHIFT;
|
||||
param2 |= usb_endpoint_type(desc) << EPT_SHIFT;
|
||||
|
||||
switch (bdc->gadget.speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
if (usb_endpoint_xfer_int(desc) ||
|
||||
usb_endpoint_xfer_isoc(desc)) {
|
||||
param2 |= si;
|
||||
if (usb_endpoint_xfer_isoc(desc) && comp_desc)
|
||||
mul = comp_desc->bmAttributes;
|
||||
|
||||
}
|
||||
param2 |= mul << EPM_SHIFT;
|
||||
if (comp_desc)
|
||||
mbs = comp_desc->bMaxBurst;
|
||||
param2 |= mbs << MB_SHIFT;
|
||||
break;
|
||||
|
||||
case USB_SPEED_HIGH:
|
||||
if (usb_endpoint_xfer_isoc(desc) ||
|
||||
usb_endpoint_xfer_int(desc)) {
|
||||
param2 |= si;
|
||||
|
||||
mbs = (usb_endpoint_maxp(desc) & 0x1800) >> 11;
|
||||
param2 |= mbs << MB_SHIFT;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_LOW:
|
||||
/* the hardware accepts SI in 125usec range */
|
||||
if (usb_endpoint_xfer_isoc(desc))
|
||||
si += 3;
|
||||
|
||||
/*
|
||||
* FS Int endpoints can have si of 1-255ms but the controller
|
||||
* accepts 2^bInterval*125usec, so convert ms to nearest power
|
||||
* of 2
|
||||
*/
|
||||
if (usb_endpoint_xfer_int(desc))
|
||||
si = fls(desc->bInterval * 8) - 1;
|
||||
|
||||
param2 |= si;
|
||||
break;
|
||||
default:
|
||||
dev_err(bdc->dev, "UNKNOWN speed ERR\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd_sc |= BDC_CMD_EPC|BDC_CMD_EPN(ep->ep_num)|BDC_SUB_CMD_ADD_EP;
|
||||
|
||||
dev_dbg(bdc->dev, "cmd_sc=%x param2=%08x\n", cmd_sc, param2);
|
||||
ret = bdc_submit_cmd(bdc, cmd_sc, param0, param1, param2);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "command failed :%x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ep_bd_list_reinit(ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the HW deq pointer, if this command is successful, HW will start
|
||||
* fetching the next bd from address dma_addr.
|
||||
*/
|
||||
int bdc_ep_bla(struct bdc *bdc, struct bdc_ep *ep, dma_addr_t dma_addr)
|
||||
{
|
||||
u32 param0, param1;
|
||||
u32 cmd_sc = 0;
|
||||
|
||||
dev_dbg(bdc->dev, "%s: add=%08llx\n", __func__,
|
||||
(unsigned long long)(dma_addr));
|
||||
param0 = lower_32_bits(dma_addr);
|
||||
param1 = upper_32_bits(dma_addr);
|
||||
cpu_to_le32s(¶m0);
|
||||
cpu_to_le32s(¶m1);
|
||||
|
||||
cmd_sc |= BDC_CMD_EPN(ep->ep_num)|BDC_CMD_BLA;
|
||||
dev_dbg(bdc->dev, "cmd_sc=%x\n", cmd_sc);
|
||||
|
||||
return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
|
||||
}
|
||||
|
||||
/* Set the address sent bu Host in SET_ADD request */
|
||||
int bdc_address_device(struct bdc *bdc, u32 add)
|
||||
{
|
||||
u32 cmd_sc = 0;
|
||||
u32 param2;
|
||||
|
||||
dev_dbg(bdc->dev, "%s: add=%d\n", __func__, add);
|
||||
cmd_sc |= BDC_SUB_CMD_ADD|BDC_CMD_DVC;
|
||||
param2 = add & 0x7f;
|
||||
|
||||
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
|
||||
}
|
||||
|
||||
/* Send a Function Wake notification packet using FH command */
|
||||
int bdc_function_wake_fh(struct bdc *bdc, u8 intf)
|
||||
{
|
||||
u32 param0, param1;
|
||||
u32 cmd_sc = 0;
|
||||
|
||||
param0 = param1 = 0;
|
||||
dev_dbg(bdc->dev, "%s intf=%d\n", __func__, intf);
|
||||
cmd_sc |= BDC_CMD_FH;
|
||||
param0 |= TRA_PACKET;
|
||||
param0 |= (bdc->dev_addr << 25);
|
||||
param1 |= DEV_NOTF_TYPE;
|
||||
param1 |= (FWK_SUBTYPE<<4);
|
||||
dev_dbg(bdc->dev, "param0=%08x param1=%08x\n", param0, param1);
|
||||
|
||||
return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
|
||||
}
|
||||
|
||||
/* Send a Function Wake notification packet using DNC command */
|
||||
int bdc_function_wake(struct bdc *bdc, u8 intf)
|
||||
{
|
||||
u32 cmd_sc = 0;
|
||||
u32 param2 = 0;
|
||||
|
||||
dev_dbg(bdc->dev, "%s intf=%d", __func__, intf);
|
||||
param2 |= intf;
|
||||
cmd_sc |= BDC_SUB_CMD_FWK|BDC_CMD_DNC;
|
||||
|
||||
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
|
||||
}
|
||||
|
||||
/* Stall the endpoint */
|
||||
int bdc_ep_set_stall(struct bdc *bdc, int epnum)
|
||||
{
|
||||
u32 cmd_sc = 0;
|
||||
|
||||
dev_dbg(bdc->dev, "%s epnum=%d\n", __func__, epnum);
|
||||
/* issue a stall endpoint command */
|
||||
cmd_sc |= BDC_SUB_CMD_EP_STL | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
|
||||
|
||||
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* resets the endpoint, called when host sends CLEAR_FEATURE(HALT) */
|
||||
int bdc_ep_clear_stall(struct bdc *bdc, int epnum)
|
||||
{
|
||||
struct bdc_ep *ep;
|
||||
u32 cmd_sc = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(bdc->dev, "%s: epnum=%d\n", __func__, epnum);
|
||||
ep = bdc->bdc_ep_array[epnum];
|
||||
/*
|
||||
* If we are not in stalled then stall Endpoint and issue clear stall,
|
||||
* his will reset the seq number for non EP0.
|
||||
*/
|
||||
if (epnum != 1) {
|
||||
/* if the endpoint it not stallled */
|
||||
if (!(ep->flags & BDC_EP_STALL)) {
|
||||
ret = bdc_ep_set_stall(bdc, epnum);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Preserve the seq number for ep0 only */
|
||||
if (epnum != 1)
|
||||
cmd_sc |= BDC_CMD_EPO_RST_SN;
|
||||
|
||||
/* issue a reset endpoint command */
|
||||
cmd_sc |= BDC_SUB_CMD_EP_RST | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
|
||||
|
||||
ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "command failed:%x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
bdc_notify_xfr(bdc, epnum);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Stop the endpoint, called when software wants to dequeue some request */
|
||||
int bdc_stop_ep(struct bdc *bdc, int epnum)
|
||||
{
|
||||
struct bdc_ep *ep;
|
||||
u32 cmd_sc = 0;
|
||||
int ret;
|
||||
|
||||
ep = bdc->bdc_ep_array[epnum];
|
||||
dev_dbg(bdc->dev, "%s: ep:%s ep->flags:%08x\n", __func__,
|
||||
ep->name, ep->flags);
|
||||
/* Endpoint has to be in running state to execute stop ep command */
|
||||
if (!(ep->flags & BDC_EP_ENABLED)) {
|
||||
dev_err(bdc->dev, "stop endpoint called for disabled ep\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ep->flags & BDC_EP_STALL) || (ep->flags & BDC_EP_STOP))
|
||||
return 0;
|
||||
|
||||
/* issue a stop endpoint command */
|
||||
cmd_sc |= BDC_CMD_EP0_XSD | BDC_SUB_CMD_EP_STP
|
||||
| BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
|
||||
|
||||
ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev,
|
||||
"stop endpoint command didn't complete:%d ep:%s\n",
|
||||
ret, ep->name);
|
||||
return ret;
|
||||
}
|
||||
ep->flags |= BDC_EP_STOP;
|
||||
bdc_dump_epsts(bdc);
|
||||
|
||||
return ret;
|
||||
}
|
29
drivers/usb/gadget/udc/bdc/bdc_cmd.h
Normal file
29
drivers/usb/gadget/udc/bdc/bdc_cmd.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* bdc_cmd.h - header for the BDC debug functions
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#ifndef __LINUX_BDC_CMD_H__
|
||||
#define __LINUX_BDC_CMD_H__
|
||||
|
||||
/* Command operations */
|
||||
int bdc_address_device(struct bdc *, u32);
|
||||
int bdc_config_ep(struct bdc *, struct bdc_ep *);
|
||||
int bdc_dconfig_ep(struct bdc *, struct bdc_ep *);
|
||||
int bdc_stop_ep(struct bdc *, int);
|
||||
int bdc_ep_set_stall(struct bdc *, int);
|
||||
int bdc_ep_clear_stall(struct bdc *, int);
|
||||
int bdc_ep_set_halt(struct bdc_ep *, u32 , int);
|
||||
int bdc_ep_bla(struct bdc *, struct bdc_ep *, dma_addr_t);
|
||||
int bdc_function_wake(struct bdc*, u8);
|
||||
int bdc_function_wake_fh(struct bdc*, u8);
|
||||
|
||||
#endif /* __LINUX_BDC_CMD_H__ */
|
533
drivers/usb/gadget/udc/bdc/bdc_core.c
Normal file
533
drivers/usb/gadget/udc/bdc/bdc_core.c
Normal file
@ -0,0 +1,533 @@
|
||||
/*
|
||||
* bdc_core.c - BRCM BDC USB3.0 device controller core operations
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "bdc.h"
|
||||
#include "bdc_dbg.h"
|
||||
|
||||
/* Poll till controller status is not OIP */
|
||||
static int poll_oip(struct bdc *bdc, int usec)
|
||||
{
|
||||
u32 status;
|
||||
/* Poll till STS!= OIP */
|
||||
while (usec) {
|
||||
status = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
if (BDC_CSTS(status) != BDC_OIP) {
|
||||
dev_dbg(bdc->dev,
|
||||
"poll_oip complete status=%d",
|
||||
BDC_CSTS(status));
|
||||
return 0;
|
||||
}
|
||||
udelay(10);
|
||||
usec -= 10;
|
||||
}
|
||||
dev_err(bdc->dev, "Err: operation timedout BDCSC: 0x%08x\n", status);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Stop the BDC controller */
|
||||
int bdc_stop(struct bdc *bdc)
|
||||
{
|
||||
int ret;
|
||||
u32 temp;
|
||||
|
||||
dev_dbg(bdc->dev, "%s ()\n\n", __func__);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
/* Check if BDC is already halted */
|
||||
if (BDC_CSTS(temp) == BDC_HLT) {
|
||||
dev_vdbg(bdc->dev, "BDC already halted\n");
|
||||
return 0;
|
||||
}
|
||||
temp &= ~BDC_COP_MASK;
|
||||
temp |= BDC_COS|BDC_COP_STP;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
|
||||
ret = poll_oip(bdc, BDC_COP_TIMEOUT);
|
||||
if (ret)
|
||||
dev_err(bdc->dev, "bdc stop operation failed");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Issue a reset to BDC controller */
|
||||
int bdc_reset(struct bdc *bdc)
|
||||
{
|
||||
u32 temp;
|
||||
int ret;
|
||||
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
/* First halt the controller */
|
||||
ret = bdc_stop(bdc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
temp &= ~BDC_COP_MASK;
|
||||
temp |= BDC_COS|BDC_COP_RST;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
ret = poll_oip(bdc, BDC_COP_TIMEOUT);
|
||||
if (ret)
|
||||
dev_err(bdc->dev, "bdc reset operation failed");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Run the BDC controller */
|
||||
int bdc_run(struct bdc *bdc)
|
||||
{
|
||||
u32 temp;
|
||||
int ret;
|
||||
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
/* if BDC is already in running state then do not do anything */
|
||||
if (BDC_CSTS(temp) == BDC_NOR) {
|
||||
dev_warn(bdc->dev, "bdc is already in running state\n");
|
||||
return 0;
|
||||
}
|
||||
temp &= ~BDC_COP_MASK;
|
||||
temp |= BDC_COP_RUN;
|
||||
temp |= BDC_COS;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
ret = poll_oip(bdc, BDC_COP_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "bdc run operation failed:%d", ret);
|
||||
return ret;
|
||||
}
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
if (BDC_CSTS(temp) != BDC_NOR) {
|
||||
dev_err(bdc->dev, "bdc not in normal mode after RUN op :%d\n",
|
||||
BDC_CSTS(temp));
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Present the termination to the host, typically called from upstream port
|
||||
* event with Vbus present =1
|
||||
*/
|
||||
void bdc_softconn(struct bdc *bdc)
|
||||
{
|
||||
u32 uspc;
|
||||
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
uspc &= ~BDC_PST_MASK;
|
||||
uspc |= BDC_LINK_STATE_RX_DET;
|
||||
uspc |= BDC_SWS;
|
||||
dev_dbg(bdc->dev, "%s () uspc=%08x\n", __func__, uspc);
|
||||
bdc_writel(bdc->regs, BDC_USPC, uspc);
|
||||
}
|
||||
|
||||
/* Remove the termination */
|
||||
void bdc_softdisconn(struct bdc *bdc)
|
||||
{
|
||||
u32 uspc;
|
||||
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
uspc |= BDC_SDC;
|
||||
uspc &= ~BDC_SCN;
|
||||
dev_dbg(bdc->dev, "%s () uspc=%x\n", __func__, uspc);
|
||||
bdc_writel(bdc->regs, BDC_USPC, uspc);
|
||||
}
|
||||
|
||||
/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
|
||||
static int scratchpad_setup(struct bdc *bdc)
|
||||
{
|
||||
int sp_buff_size;
|
||||
u32 low32;
|
||||
u32 upp32;
|
||||
|
||||
sp_buff_size = BDC_SPB(bdc_readl(bdc->regs, BDC_BDCCFG0));
|
||||
dev_dbg(bdc->dev, "%s() sp_buff_size=%d\n", __func__, sp_buff_size);
|
||||
if (!sp_buff_size) {
|
||||
dev_dbg(bdc->dev, "Scratchpad buffer not needed\n");
|
||||
return 0;
|
||||
}
|
||||
/* Refer to BDC spec, Table 4 for description of SPB */
|
||||
sp_buff_size = 1 << (sp_buff_size + 5);
|
||||
dev_dbg(bdc->dev, "Allocating %d bytes for scratchpad\n", sp_buff_size);
|
||||
bdc->scratchpad.buff = dma_zalloc_coherent(bdc->dev, sp_buff_size,
|
||||
&bdc->scratchpad.sp_dma, GFP_KERNEL);
|
||||
|
||||
if (!bdc->scratchpad.buff)
|
||||
goto fail;
|
||||
|
||||
bdc->sp_buff_size = sp_buff_size;
|
||||
bdc->scratchpad.size = sp_buff_size;
|
||||
low32 = lower_32_bits(bdc->scratchpad.sp_dma);
|
||||
upp32 = upper_32_bits(bdc->scratchpad.sp_dma);
|
||||
cpu_to_le32s(&low32);
|
||||
cpu_to_le32s(&upp32);
|
||||
bdc_writel(bdc->regs, BDC_SPBBAL, low32);
|
||||
bdc_writel(bdc->regs, BDC_SPBBAH, upp32);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
bdc->scratchpad.buff = NULL;
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Allocate the status report ring */
|
||||
static int setup_srr(struct bdc *bdc, int interrupter)
|
||||
{
|
||||
dev_dbg(bdc->dev, "%s() NUM_SR_ENTRIES:%d\n", __func__, NUM_SR_ENTRIES);
|
||||
/* Reset the SRR */
|
||||
bdc_writel(bdc->regs, BDC_SRRINT(0), BDC_SRR_RWS | BDC_SRR_RST);
|
||||
bdc->srr.dqp_index = 0;
|
||||
/* allocate the status report descriptors */
|
||||
bdc->srr.sr_bds = dma_zalloc_coherent(
|
||||
bdc->dev,
|
||||
NUM_SR_ENTRIES * sizeof(struct bdc_bd),
|
||||
&bdc->srr.dma_addr,
|
||||
GFP_KERNEL);
|
||||
if (!bdc->srr.sr_bds)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize the HW regs and internal data structures */
|
||||
static void bdc_mem_init(struct bdc *bdc, bool reinit)
|
||||
{
|
||||
u8 size = 0;
|
||||
u32 usb2_pm;
|
||||
u32 low32;
|
||||
u32 upp32;
|
||||
u32 temp;
|
||||
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
bdc->ep0_state = WAIT_FOR_SETUP;
|
||||
bdc->dev_addr = 0;
|
||||
bdc->srr.eqp_index = 0;
|
||||
bdc->srr.dqp_index = 0;
|
||||
bdc->zlp_needed = false;
|
||||
bdc->delayed_status = false;
|
||||
|
||||
bdc_writel(bdc->regs, BDC_SPBBAL, bdc->scratchpad.sp_dma);
|
||||
/* Init the SRR */
|
||||
temp = BDC_SRR_RWS | BDC_SRR_RST;
|
||||
/* Reset the SRR */
|
||||
bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
|
||||
dev_dbg(bdc->dev, "bdc->srr.sr_bds =%p\n", bdc->srr.sr_bds);
|
||||
temp = lower_32_bits(bdc->srr.dma_addr);
|
||||
size = fls(NUM_SR_ENTRIES) - 2;
|
||||
temp |= size;
|
||||
dev_dbg(bdc->dev, "SRRBAL[0]=%08x NUM_SR_ENTRIES:%d size:%d\n",
|
||||
temp, NUM_SR_ENTRIES, size);
|
||||
|
||||
low32 = lower_32_bits(temp);
|
||||
upp32 = upper_32_bits(bdc->srr.dma_addr);
|
||||
cpu_to_le32s(&low32);
|
||||
cpu_to_le32s(&upp32);
|
||||
|
||||
/* Write the dma addresses into regs*/
|
||||
bdc_writel(bdc->regs, BDC_SRRBAL(0), low32);
|
||||
bdc_writel(bdc->regs, BDC_SRRBAH(0), upp32);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_SRRINT(0));
|
||||
temp |= BDC_SRR_IE;
|
||||
temp &= ~(BDC_SRR_RST | BDC_SRR_RWS);
|
||||
bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
|
||||
|
||||
/* Set the Interrupt Coalescence ~500 usec */
|
||||
temp = bdc_readl(bdc->regs, BDC_INTCTLS(0));
|
||||
temp &= ~0xffff;
|
||||
temp |= INT_CLS;
|
||||
bdc_writel(bdc->regs, BDC_INTCTLS(0), temp);
|
||||
|
||||
usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
|
||||
dev_dbg(bdc->dev, "usb2_pm=%08x", usb2_pm);
|
||||
/* Enable hardware LPM Enable */
|
||||
usb2_pm |= BDC_HLE;
|
||||
bdc_writel(bdc->regs, BDC_USPPM2, usb2_pm);
|
||||
|
||||
/* readback for debug */
|
||||
usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
|
||||
dev_dbg(bdc->dev, "usb2_pm=%08x\n", usb2_pm);
|
||||
|
||||
/* Disable any unwanted SR's on SRR */
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
/* We don't want Microframe counter wrap SR */
|
||||
temp |= BDC_MASK_MCW;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
|
||||
/*
|
||||
* In some error cases, driver has to reset the entire BDC controller
|
||||
* in that case reinit is passed as 1
|
||||
*/
|
||||
if (reinit) {
|
||||
/* Enable interrupts */
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
temp |= BDC_GIE;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
/* Init scratchpad to 0 */
|
||||
memset(bdc->scratchpad.buff, 0, bdc->sp_buff_size);
|
||||
/* Initialize SRR to 0 */
|
||||
memset(bdc->srr.sr_bds, 0,
|
||||
NUM_SR_ENTRIES * sizeof(struct bdc_bd));
|
||||
} else {
|
||||
/* One time initiaization only */
|
||||
/* Enable status report function pointers */
|
||||
bdc->sr_handler[0] = bdc_sr_xsf;
|
||||
bdc->sr_handler[1] = bdc_sr_uspc;
|
||||
|
||||
/* EP0 status report function pointers */
|
||||
bdc->sr_xsf_ep0[0] = bdc_xsf_ep0_setup_recv;
|
||||
bdc->sr_xsf_ep0[1] = bdc_xsf_ep0_data_start;
|
||||
bdc->sr_xsf_ep0[2] = bdc_xsf_ep0_status_start;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the dynamic memory */
|
||||
static void bdc_mem_free(struct bdc *bdc)
|
||||
{
|
||||
dev_dbg(bdc->dev, "%s\n", __func__);
|
||||
/* Free SRR */
|
||||
if (bdc->srr.sr_bds)
|
||||
dma_free_coherent(bdc->dev,
|
||||
NUM_SR_ENTRIES * sizeof(struct bdc_bd),
|
||||
bdc->srr.sr_bds, bdc->srr.dma_addr);
|
||||
|
||||
/* Free scratchpad */
|
||||
if (bdc->scratchpad.buff)
|
||||
dma_free_coherent(bdc->dev, bdc->sp_buff_size,
|
||||
bdc->scratchpad.buff, bdc->scratchpad.sp_dma);
|
||||
|
||||
/* Destroy the dma pools */
|
||||
if (bdc->bd_table_pool)
|
||||
dma_pool_destroy(bdc->bd_table_pool);
|
||||
|
||||
/* Free the bdc_ep array */
|
||||
kfree(bdc->bdc_ep_array);
|
||||
|
||||
bdc->srr.sr_bds = NULL;
|
||||
bdc->scratchpad.buff = NULL;
|
||||
bdc->bd_table_pool = NULL;
|
||||
bdc->bdc_ep_array = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* bdc reinit gives a controller reset and reinitialize the registers,
|
||||
* called from disconnect/bus reset scenario's, to ensure proper HW cleanup
|
||||
*/
|
||||
int bdc_reinit(struct bdc *bdc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(bdc->dev, "%s\n", __func__);
|
||||
ret = bdc_stop(bdc);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = bdc_reset(bdc);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* the reinit flag is 1 */
|
||||
bdc_mem_init(bdc, true);
|
||||
ret = bdc_run(bdc);
|
||||
out:
|
||||
bdc->reinit = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allocate all the dyanmic memory */
|
||||
static int bdc_mem_alloc(struct bdc *bdc)
|
||||
{
|
||||
u32 page_size;
|
||||
unsigned int num_ieps, num_oeps;
|
||||
|
||||
dev_dbg(bdc->dev,
|
||||
"%s() NUM_BDS_PER_TABLE:%d\n", __func__,
|
||||
NUM_BDS_PER_TABLE);
|
||||
page_size = BDC_PGS(bdc_readl(bdc->regs, BDC_BDCCFG0));
|
||||
/* page size is 2^pgs KB */
|
||||
page_size = 1 << page_size;
|
||||
/* KB */
|
||||
page_size <<= 10;
|
||||
dev_dbg(bdc->dev, "page_size=%d\n", page_size);
|
||||
|
||||
/* Create a pool of bd tables */
|
||||
bdc->bd_table_pool =
|
||||
dma_pool_create("BDC BD tables", bdc->dev, NUM_BDS_PER_TABLE * 16,
|
||||
16, page_size);
|
||||
|
||||
if (!bdc->bd_table_pool)
|
||||
goto fail;
|
||||
|
||||
if (scratchpad_setup(bdc))
|
||||
goto fail;
|
||||
|
||||
/* read from regs */
|
||||
num_ieps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNIC));
|
||||
num_oeps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNOC));
|
||||
/* +2: 1 for ep0 and the other is rsvd i.e. bdc_ep[0] is rsvd */
|
||||
bdc->num_eps = num_ieps + num_oeps + 2;
|
||||
dev_dbg(bdc->dev,
|
||||
"ieps:%d eops:%d num_eps:%d\n",
|
||||
num_ieps, num_oeps, bdc->num_eps);
|
||||
/* allocate array of ep pointers */
|
||||
bdc->bdc_ep_array = kcalloc(bdc->num_eps, sizeof(struct bdc_ep *),
|
||||
GFP_KERNEL);
|
||||
if (!bdc->bdc_ep_array)
|
||||
goto fail;
|
||||
|
||||
dev_dbg(bdc->dev, "Allocating sr report0\n");
|
||||
if (setup_srr(bdc, 0))
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
dev_warn(bdc->dev, "Couldn't initialize memory\n");
|
||||
bdc_mem_free(bdc);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* opposite to bdc_hw_init */
|
||||
static void bdc_hw_exit(struct bdc *bdc)
|
||||
{
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
bdc_mem_free(bdc);
|
||||
}
|
||||
|
||||
/* Initialize the bdc HW and memory */
|
||||
static int bdc_hw_init(struct bdc *bdc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
ret = bdc_reset(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "err resetting bdc abort bdc init%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = bdc_mem_alloc(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "Mem alloc failed, aborting\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
bdc_mem_init(bdc, 0);
|
||||
bdc_dbg_regs(bdc);
|
||||
dev_dbg(bdc->dev, "HW Init done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bdc *bdc;
|
||||
struct resource *res;
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
u32 temp;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
|
||||
if (!bdc)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
bdc->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(bdc->regs)) {
|
||||
dev_err(dev, "ioremap error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "platform_get_irq failed:%d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
spin_lock_init(&bdc->lock);
|
||||
platform_set_drvdata(pdev, bdc);
|
||||
bdc->irq = irq;
|
||||
bdc->dev = dev;
|
||||
dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
if ((temp & BDC_P64) &&
|
||||
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
|
||||
dev_dbg(bdc->dev, "Using 64-bit address\n");
|
||||
} else {
|
||||
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "No suitable DMA config available, abort\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
dev_dbg(bdc->dev, "Using 32-bit address\n");
|
||||
}
|
||||
ret = bdc_hw_init(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "BDC init failure:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = bdc_udc_init(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "BDC Gadget init failure:%d\n", ret);
|
||||
goto cleanup;
|
||||
}
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
bdc_hw_exit(bdc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bdc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bdc *bdc;
|
||||
|
||||
bdc = platform_get_drvdata(pdev);
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
bdc_udc_exit(bdc);
|
||||
bdc_hw_exit(bdc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bdc_driver = {
|
||||
.driver = {
|
||||
.name = BRCM_BDC_NAME,
|
||||
.owner = THIS_MODULE
|
||||
},
|
||||
.probe = bdc_probe,
|
||||
.remove = bdc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bdc_driver);
|
||||
MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION(BRCM_BDC_DESC);
|
123
drivers/usb/gadget/udc/bdc/bdc_dbg.c
Normal file
123
drivers/usb/gadget/udc/bdc/bdc_dbg.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* bdc_dbg.c - BRCM BDC USB3.0 device controller debug functions
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bdc.h"
|
||||
#include "bdc_dbg.h"
|
||||
|
||||
void bdc_dbg_regs(struct bdc *bdc)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
dev_vdbg(bdc->dev, "bdc->regs:%p\n", bdc->regs);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCCFG0);
|
||||
dev_vdbg(bdc->dev, "bdccfg0:0x%08x\n", temp);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCCFG1);
|
||||
dev_vdbg(bdc->dev, "bdccfg1:0x%08x\n", temp);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCCAP0);
|
||||
dev_vdbg(bdc->dev, "bdccap0:0x%08x\n", temp);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
|
||||
dev_vdbg(bdc->dev, "bdccap1:0x%08x\n", temp);
|
||||
temp = bdc_readl(bdc->regs, BDC_USPC);
|
||||
dev_vdbg(bdc->dev, "uspc:0x%08x\n", temp);
|
||||
temp = bdc_readl(bdc->regs, BDC_DVCSA);
|
||||
dev_vdbg(bdc->dev, "dvcsa:0x%08x\n", temp);
|
||||
temp = bdc_readl(bdc->regs, BDC_DVCSB);
|
||||
dev_vdbg(bdc->dev, "dvcsb:0x%x08\n", temp);
|
||||
}
|
||||
|
||||
void bdc_dump_epsts(struct bdc *bdc)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS0(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS0:0x%08x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS1(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS1:0x%x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS2(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS2:0x%08x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS3(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS3:0x%08x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS4(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS4:0x%08x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS5(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS5:0x%08x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS6(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS6:0x%08x\n", temp);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_EPSTS7(0));
|
||||
dev_vdbg(bdc->dev, "BDC_EPSTS7:0x%08x\n", temp);
|
||||
}
|
||||
|
||||
void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
|
||||
{
|
||||
struct bdc_sr *sr;
|
||||
dma_addr_t addr;
|
||||
int i;
|
||||
|
||||
sr = bdc->srr.sr_bds;
|
||||
addr = bdc->srr.dma_addr;
|
||||
dev_vdbg(bdc->dev, "bdc_dbg_srr sr:%p dqp_index:%d\n",
|
||||
sr, bdc->srr.dqp_index);
|
||||
for (i = 0; i < NUM_SR_ENTRIES; i++) {
|
||||
sr = &bdc->srr.sr_bds[i];
|
||||
dev_vdbg(bdc->dev, "%llx %08x %08x %08x %08x\n",
|
||||
(unsigned long long)addr,
|
||||
le32_to_cpu(sr->offset[0]),
|
||||
le32_to_cpu(sr->offset[1]),
|
||||
le32_to_cpu(sr->offset[2]),
|
||||
le32_to_cpu(sr->offset[3]));
|
||||
addr += sizeof(*sr);
|
||||
}
|
||||
}
|
||||
|
||||
void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
|
||||
{
|
||||
struct bd_list *bd_list = &ep->bd_list;
|
||||
struct bd_table *bd_table;
|
||||
struct bdc_bd *bd;
|
||||
int tbi, bdi, gbdi;
|
||||
dma_addr_t dma;
|
||||
|
||||
gbdi = 0;
|
||||
dev_vdbg(bdc->dev,
|
||||
"Dump bd list for %s epnum:%d\n",
|
||||
ep->name, ep->ep_num);
|
||||
|
||||
dev_vdbg(bdc->dev,
|
||||
"tabs:%d max_bdi:%d eqp_bdi:%d hwd_bdi:%d num_bds_table:%d\n",
|
||||
bd_list->num_tabs, bd_list->max_bdi, bd_list->eqp_bdi,
|
||||
bd_list->hwd_bdi, bd_list->num_bds_table);
|
||||
|
||||
for (tbi = 0; tbi < bd_list->num_tabs; tbi++) {
|
||||
bd_table = bd_list->bd_table_array[tbi];
|
||||
for (bdi = 0; bdi < bd_list->num_bds_table; bdi++) {
|
||||
bd = bd_table->start_bd + bdi;
|
||||
dma = bd_table->dma + (sizeof(struct bdc_bd) * bdi);
|
||||
dev_vdbg(bdc->dev,
|
||||
"tbi:%2d bdi:%2d gbdi:%2d virt:%p phys:%llx %08x %08x %08x %08x\n",
|
||||
tbi, bdi, gbdi++, bd, (unsigned long long)dma,
|
||||
le32_to_cpu(bd->offset[0]),
|
||||
le32_to_cpu(bd->offset[1]),
|
||||
le32_to_cpu(bd->offset[2]),
|
||||
le32_to_cpu(bd->offset[3]));
|
||||
}
|
||||
dev_vdbg(bdc->dev, "\n\n");
|
||||
}
|
||||
}
|
37
drivers/usb/gadget/udc/bdc/bdc_dbg.h
Normal file
37
drivers/usb/gadget/udc/bdc/bdc_dbg.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* bdc_dbg.h - header for the BDC debug functions
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#ifndef __LINUX_BDC_DBG_H__
|
||||
#define __LINUX_BDC_DBG_H__
|
||||
|
||||
#include "bdc.h"
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_VERBOSE
|
||||
void bdc_dbg_bd_list(struct bdc *, struct bdc_ep*);
|
||||
void bdc_dbg_srr(struct bdc *, u32);
|
||||
void bdc_dbg_regs(struct bdc *);
|
||||
void bdc_dump_epsts(struct bdc *);
|
||||
#else
|
||||
static inline void bdc_dbg_regs(struct bdc *bdc)
|
||||
{ }
|
||||
|
||||
static inline void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
|
||||
{ }
|
||||
|
||||
static inline void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
|
||||
{ }
|
||||
|
||||
static inline void bdc_dump_epsts(struct bdc *bdc)
|
||||
{ }
|
||||
#endif /* CONFIG_USB_GADGET_VERBOSE */
|
||||
#endif /* __LINUX_BDC_DBG_H__ */
|
2023
drivers/usb/gadget/udc/bdc/bdc_ep.c
Normal file
2023
drivers/usb/gadget/udc/bdc/bdc_ep.c
Normal file
File diff suppressed because it is too large
Load Diff
22
drivers/usb/gadget/udc/bdc/bdc_ep.h
Normal file
22
drivers/usb/gadget/udc/bdc/bdc_ep.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* bdc_ep.h - header for the BDC debug functions
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#ifndef __LINUX_BDC_EP_H__
|
||||
#define __LINUX_BDC_EP_H__
|
||||
|
||||
int bdc_init_ep(struct bdc *);
|
||||
int bdc_ep_disable(struct bdc_ep *);
|
||||
int bdc_ep_enable(struct bdc_ep *);
|
||||
void bdc_free_ep(struct bdc *);
|
||||
|
||||
#endif /* __LINUX_BDC_EP_H__ */
|
132
drivers/usb/gadget/udc/bdc/bdc_pci.c
Normal file
132
drivers/usb/gadget/udc/bdc/bdc_pci.c
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* bdc_pci.c - BRCM BDC USB3.0 device controller PCI interface file.
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* Based on drivers under drivers/usb/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "bdc.h"
|
||||
|
||||
#define BDC_PCI_PID 0x1570
|
||||
|
||||
struct bdc_pci {
|
||||
struct device *dev;
|
||||
struct platform_device *bdc;
|
||||
};
|
||||
|
||||
static int bdc_setup_msi(struct pci_dev *pci)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_enable_msi(pci);
|
||||
if (ret) {
|
||||
pr_err("failed to allocate MSI entry\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bdc_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
|
||||
{
|
||||
struct resource res[2];
|
||||
struct platform_device *bdc;
|
||||
struct bdc_pci *glue;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = devm_kzalloc(&pci->dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue)
|
||||
return -ENOMEM;
|
||||
|
||||
glue->dev = &pci->dev;
|
||||
ret = pci_enable_device(pci);
|
||||
if (ret) {
|
||||
dev_err(&pci->dev, "failed to enable pci device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pci_set_master(pci);
|
||||
|
||||
bdc = platform_device_alloc(BRCM_BDC_NAME, PLATFORM_DEVID_AUTO);
|
||||
if (!bdc)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
bdc_setup_msi(pci);
|
||||
|
||||
res[0].start = pci_resource_start(pci, 0);
|
||||
res[0].end = pci_resource_end(pci, 0);
|
||||
res[0].name = BRCM_BDC_NAME;
|
||||
res[0].flags = IORESOURCE_MEM;
|
||||
|
||||
res[1].start = pci->irq;
|
||||
res[1].name = BRCM_BDC_NAME;
|
||||
res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
ret = platform_device_add_resources(bdc, res, ARRAY_SIZE(res));
|
||||
if (ret) {
|
||||
dev_err(&pci->dev,
|
||||
"couldn't add resources to bdc device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, glue);
|
||||
|
||||
dma_set_coherent_mask(&bdc->dev, pci->dev.coherent_dma_mask);
|
||||
|
||||
bdc->dev.dma_mask = pci->dev.dma_mask;
|
||||
bdc->dev.dma_parms = pci->dev.dma_parms;
|
||||
bdc->dev.parent = &pci->dev;
|
||||
glue->bdc = bdc;
|
||||
|
||||
ret = platform_device_add(bdc);
|
||||
if (ret) {
|
||||
dev_err(&pci->dev, "failed to register bdc device\n");
|
||||
platform_device_put(bdc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bdc_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct bdc_pci *glue = pci_get_drvdata(pci);
|
||||
|
||||
platform_device_unregister(glue->bdc);
|
||||
pci_disable_msi(pci);
|
||||
}
|
||||
|
||||
static struct pci_device_id bdc_pci_id_table[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, BDC_PCI_PID), },
|
||||
{} /* Terminating Entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, bdc_pci_id_table);
|
||||
|
||||
static struct pci_driver bdc_pci_driver = {
|
||||
.name = "bdc-pci",
|
||||
.id_table = bdc_pci_id_table,
|
||||
.probe = bdc_pci_probe,
|
||||
.remove = bdc_pci_remove,
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("BRCM BDC USB3 PCI Glue layer");
|
||||
module_pci_driver(bdc_pci_driver);
|
587
drivers/usb/gadget/udc/bdc/bdc_udc.c
Normal file
587
drivers/usb/gadget/udc/bdc/bdc_udc.c
Normal file
@ -0,0 +1,587 @@
|
||||
/*
|
||||
* bdc_udc.c - BRCM BDC USB3.0 device controller gagdet ops
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* Author: Ashwini Pahuja
|
||||
*
|
||||
* Based on drivers under drivers/usb/gadget/udc/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "bdc.h"
|
||||
#include "bdc_ep.h"
|
||||
#include "bdc_cmd.h"
|
||||
#include "bdc_dbg.h"
|
||||
|
||||
static const struct usb_gadget_ops bdc_gadget_ops;
|
||||
|
||||
static const char * const conn_speed_str[] = {
|
||||
"Not connected",
|
||||
"Full Speed",
|
||||
"Low Speed",
|
||||
"High Speed",
|
||||
"Super Speed",
|
||||
};
|
||||
|
||||
/* EP0 initial descripror */
|
||||
static struct usb_endpoint_descriptor bdc_gadget_ep0_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
|
||||
.bEndpointAddress = 0,
|
||||
.wMaxPacketSize = cpu_to_le16(EP0_MAX_PKT_SIZE),
|
||||
};
|
||||
|
||||
/* Advance the srr dqp maintained by SW */
|
||||
static void srr_dqp_index_advc(struct bdc *bdc, u32 srr_num)
|
||||
{
|
||||
struct srr *srr;
|
||||
|
||||
srr = &bdc->srr;
|
||||
dev_dbg_ratelimited(bdc->dev, "srr->dqp_index:%d\n", srr->dqp_index);
|
||||
srr->dqp_index++;
|
||||
/* rollback to 0 if we are past the last */
|
||||
if (srr->dqp_index == NUM_SR_ENTRIES)
|
||||
srr->dqp_index = 0;
|
||||
}
|
||||
|
||||
/* connect sr */
|
||||
static void bdc_uspc_connected(struct bdc *bdc)
|
||||
{
|
||||
u32 speed, temp;
|
||||
u32 usppms;
|
||||
int ret;
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_USPC);
|
||||
speed = BDC_PSP(temp);
|
||||
dev_dbg(bdc->dev, "%s speed=%x\n", __func__, speed);
|
||||
switch (speed) {
|
||||
case BDC_SPEED_SS:
|
||||
bdc_gadget_ep0_desc.wMaxPacketSize =
|
||||
cpu_to_le16(EP0_MAX_PKT_SIZE);
|
||||
bdc->gadget.ep0->maxpacket = EP0_MAX_PKT_SIZE;
|
||||
bdc->gadget.speed = USB_SPEED_SUPER;
|
||||
/* Enable U1T in SS mode */
|
||||
usppms = bdc_readl(bdc->regs, BDC_USPPMS);
|
||||
usppms &= ~BDC_U1T(0xff);
|
||||
usppms |= BDC_U1T(U1_TIMEOUT);
|
||||
usppms |= BDC_PORT_W1S;
|
||||
bdc_writel(bdc->regs, BDC_USPPMS, usppms);
|
||||
break;
|
||||
|
||||
case BDC_SPEED_HS:
|
||||
bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
bdc->gadget.ep0->maxpacket = 64;
|
||||
bdc->gadget.speed = USB_SPEED_HIGH;
|
||||
break;
|
||||
|
||||
case BDC_SPEED_FS:
|
||||
bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
bdc->gadget.ep0->maxpacket = 64;
|
||||
bdc->gadget.speed = USB_SPEED_FULL;
|
||||
break;
|
||||
|
||||
case BDC_SPEED_LS:
|
||||
bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
bdc->gadget.ep0->maxpacket = 8;
|
||||
bdc->gadget.speed = USB_SPEED_LOW;
|
||||
break;
|
||||
default:
|
||||
dev_err(bdc->dev, "UNDEFINED SPEED\n");
|
||||
return;
|
||||
}
|
||||
dev_dbg(bdc->dev, "connected at %s\n", conn_speed_str[speed]);
|
||||
/* Now we know the speed, configure ep0 */
|
||||
bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
|
||||
ret = bdc_config_ep(bdc, bdc->bdc_ep_array[1]);
|
||||
if (ret)
|
||||
dev_err(bdc->dev, "EP0 config failed\n");
|
||||
bdc->bdc_ep_array[1]->usb_ep.desc = &bdc_gadget_ep0_desc;
|
||||
bdc->bdc_ep_array[1]->flags |= BDC_EP_ENABLED;
|
||||
usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
/* device got disconnected */
|
||||
static void bdc_uspc_disconnected(struct bdc *bdc, bool reinit)
|
||||
{
|
||||
struct bdc_ep *ep;
|
||||
|
||||
dev_dbg(bdc->dev, "%s\n", __func__);
|
||||
/*
|
||||
* Only stop ep0 from here, rest of the endpoints will be disabled
|
||||
* from gadget_disconnect
|
||||
*/
|
||||
ep = bdc->bdc_ep_array[1];
|
||||
if (ep && (ep->flags & BDC_EP_ENABLED))
|
||||
/* if enabled then stop and remove requests */
|
||||
bdc_ep_disable(ep);
|
||||
|
||||
if (bdc->gadget_driver && bdc->gadget_driver->disconnect) {
|
||||
spin_unlock(&bdc->lock);
|
||||
bdc->gadget_driver->disconnect(&bdc->gadget);
|
||||
spin_lock(&bdc->lock);
|
||||
}
|
||||
/* Set Unknown speed */
|
||||
bdc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
bdc->devstatus &= DEVSTATUS_CLEAR;
|
||||
bdc->delayed_status = false;
|
||||
bdc->reinit = reinit;
|
||||
bdc->test_mode = false;
|
||||
}
|
||||
|
||||
/* TNotify wkaeup timer */
|
||||
static void bdc_func_wake_timer(struct work_struct *work)
|
||||
{
|
||||
struct bdc *bdc = container_of(work, struct bdc, func_wake_notify.work);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(bdc->dev, "%s\n", __func__);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
/*
|
||||
* Check if host has started transferring on endpoints
|
||||
* FUNC_WAKE_ISSUED is cleared when transfer has started after resume
|
||||
*/
|
||||
if (bdc->devstatus & FUNC_WAKE_ISSUED) {
|
||||
dev_dbg(bdc->dev, "FUNC_WAKE_ISSUED FLAG IS STILL SET\n");
|
||||
/* flag is still set, so again send func wake */
|
||||
bdc_function_wake_fh(bdc, 0);
|
||||
schedule_delayed_work(&bdc->func_wake_notify,
|
||||
msecs_to_jiffies(BDC_TNOTIFY));
|
||||
}
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
}
|
||||
|
||||
/* handler for Link state change condition */
|
||||
static void handle_link_state_change(struct bdc *bdc, u32 uspc)
|
||||
{
|
||||
u32 link_state;
|
||||
|
||||
dev_dbg(bdc->dev, "Link state change");
|
||||
link_state = BDC_PST(uspc);
|
||||
switch (link_state) {
|
||||
case BDC_LINK_STATE_U3:
|
||||
if ((bdc->gadget.speed != USB_SPEED_UNKNOWN) &&
|
||||
bdc->gadget_driver->suspend) {
|
||||
dev_dbg(bdc->dev, "Entered Suspend mode\n");
|
||||
spin_unlock(&bdc->lock);
|
||||
bdc->devstatus |= DEVICE_SUSPENDED;
|
||||
bdc->gadget_driver->suspend(&bdc->gadget);
|
||||
spin_lock(&bdc->lock);
|
||||
}
|
||||
break;
|
||||
case BDC_LINK_STATE_U0:
|
||||
if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) {
|
||||
bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
|
||||
if (bdc->gadget.speed == USB_SPEED_SUPER) {
|
||||
bdc_function_wake_fh(bdc, 0);
|
||||
bdc->devstatus |= FUNC_WAKE_ISSUED;
|
||||
/*
|
||||
* Start a Notification timer and check if the
|
||||
* Host transferred anything on any of the EPs,
|
||||
* if not then send function wake again every
|
||||
* TNotification secs until host initiates
|
||||
* transfer to BDC, USB3 spec Table 8.13
|
||||
*/
|
||||
schedule_delayed_work(
|
||||
&bdc->func_wake_notify,
|
||||
msecs_to_jiffies(BDC_TNOTIFY));
|
||||
dev_dbg(bdc->dev, "sched func_wake_notify\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BDC_LINK_STATE_RESUME:
|
||||
dev_dbg(bdc->dev, "Resumed from Suspend\n");
|
||||
if (bdc->devstatus & DEVICE_SUSPENDED) {
|
||||
bdc->gadget_driver->resume(&bdc->gadget);
|
||||
bdc->devstatus &= ~DEVICE_SUSPENDED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_dbg(bdc->dev, "link state:%d\n", link_state);
|
||||
}
|
||||
}
|
||||
|
||||
/* something changes on upstream port, handle it here */
|
||||
void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
|
||||
{
|
||||
u32 clear_flags = 0;
|
||||
u32 uspc;
|
||||
bool connected = false;
|
||||
bool disconn = false;
|
||||
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
dev_dbg(bdc->dev, "%s uspc=0x%08x\n", __func__, uspc);
|
||||
|
||||
/* Port connect changed */
|
||||
if (uspc & BDC_PCC) {
|
||||
/* Vbus not present, and not connected to Downstream port */
|
||||
if ((uspc & BDC_VBC) && !(uspc & BDC_VBS) && !(uspc & BDC_PCS))
|
||||
disconn = true;
|
||||
else if ((uspc & BDC_PCS) && !BDC_PST(uspc))
|
||||
connected = true;
|
||||
}
|
||||
|
||||
/* Change in VBus and VBus is present */
|
||||
if ((uspc & BDC_VBC) && (uspc & BDC_VBS)) {
|
||||
if (bdc->pullup) {
|
||||
dev_dbg(bdc->dev, "Do a softconnect\n");
|
||||
/* Attached state, do a softconnect */
|
||||
bdc_softconn(bdc);
|
||||
usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED);
|
||||
}
|
||||
clear_flags = BDC_VBC;
|
||||
} else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) {
|
||||
/* Hot reset, warm reset, 2.0 bus reset or disconn */
|
||||
dev_dbg(bdc->dev, "Port reset or disconn\n");
|
||||
bdc_uspc_disconnected(bdc, disconn);
|
||||
clear_flags = BDC_PCC|BDC_PCS|BDC_PRS|BDC_PRC;
|
||||
} else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) {
|
||||
/* Change in Link state */
|
||||
handle_link_state_change(bdc, uspc);
|
||||
clear_flags = BDC_PSC|BDC_PCS;
|
||||
}
|
||||
|
||||
/*
|
||||
* In SS we might not have PRC bit set before connection, but in 2.0
|
||||
* the PRC bit is set before connection, so moving this condition out
|
||||
* of bus reset to handle both SS/2.0 speeds.
|
||||
*/
|
||||
if (connected) {
|
||||
/* This is the connect event for U0/L0 */
|
||||
dev_dbg(bdc->dev, "Connected\n");
|
||||
bdc_uspc_connected(bdc);
|
||||
bdc->devstatus &= ~(DEVICE_SUSPENDED);
|
||||
}
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
uspc &= (~BDC_USPSC_RW);
|
||||
dev_dbg(bdc->dev, "uspc=%x\n", uspc);
|
||||
bdc_writel(bdc->regs, BDC_USPC, clear_flags);
|
||||
}
|
||||
|
||||
/* Main interrupt handler for bdc */
|
||||
static irqreturn_t bdc_udc_interrupt(int irq, void *_bdc)
|
||||
{
|
||||
u32 eqp_index, dqp_index, sr_type, srr_int;
|
||||
struct bdc_sr *sreport;
|
||||
struct bdc *bdc = _bdc;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
spin_lock(&bdc->lock);
|
||||
status = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
if (!(status & BDC_GIP)) {
|
||||
spin_unlock(&bdc->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
|
||||
/* Check if the SRR IP bit it set? */
|
||||
if (!(srr_int & BDC_SRR_IP)) {
|
||||
dev_warn(bdc->dev, "Global irq pending but SRR IP is 0\n");
|
||||
spin_unlock(&bdc->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
eqp_index = BDC_SRR_EPI(srr_int);
|
||||
dqp_index = BDC_SRR_DPI(srr_int);
|
||||
dev_dbg(bdc->dev,
|
||||
"%s eqp_index=%d dqp_index=%d srr.dqp_index=%d\n\n",
|
||||
__func__, eqp_index, dqp_index, bdc->srr.dqp_index);
|
||||
|
||||
/* check for ring empty condition */
|
||||
if (eqp_index == dqp_index) {
|
||||
dev_dbg(bdc->dev, "SRR empty?\n");
|
||||
spin_unlock(&bdc->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
while (bdc->srr.dqp_index != eqp_index) {
|
||||
sreport = &bdc->srr.sr_bds[bdc->srr.dqp_index];
|
||||
/* sreport is read before using it */
|
||||
rmb();
|
||||
sr_type = le32_to_cpu(sreport->offset[3]) & BD_TYPE_BITMASK;
|
||||
dev_dbg_ratelimited(bdc->dev, "sr_type=%d\n", sr_type);
|
||||
switch (sr_type) {
|
||||
case SR_XSF:
|
||||
bdc->sr_handler[0](bdc, sreport);
|
||||
break;
|
||||
|
||||
case SR_USPC:
|
||||
bdc->sr_handler[1](bdc, sreport);
|
||||
break;
|
||||
default:
|
||||
dev_warn(bdc->dev, "SR:%d not handled\n", sr_type);
|
||||
}
|
||||
/* Advance the srr dqp index */
|
||||
srr_dqp_index_advc(bdc, 0);
|
||||
}
|
||||
/* update the hw dequeue pointer */
|
||||
srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
|
||||
srr_int &= ~BDC_SRR_DPI_MASK;
|
||||
srr_int &= ~(BDC_SRR_RWS|BDC_SRR_RST|BDC_SRR_ISR);
|
||||
srr_int |= ((bdc->srr.dqp_index) << 16);
|
||||
srr_int |= BDC_SRR_IP;
|
||||
bdc_writel(bdc->regs, BDC_SRRINT(0), srr_int);
|
||||
srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
|
||||
if (bdc->reinit) {
|
||||
ret = bdc_reinit(bdc);
|
||||
if (ret)
|
||||
dev_err(bdc->dev, "err in bdc reinit\n");
|
||||
}
|
||||
|
||||
spin_unlock(&bdc->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Gadget ops */
|
||||
static int bdc_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct bdc *bdc = gadget_to_bdc(gadget);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
if (bdc->gadget_driver) {
|
||||
dev_err(bdc->dev, "%s is already bound to %s\n",
|
||||
bdc->gadget.name,
|
||||
bdc->gadget_driver->driver.name);
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
* Run the controller from here and when BDC is connected to
|
||||
* Host then driver will receive a USPC SR with VBUS present
|
||||
* and then driver will do a softconnect.
|
||||
*/
|
||||
ret = bdc_run(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "%s bdc run fail\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
bdc->gadget_driver = driver;
|
||||
bdc->gadget.dev.driver = &driver->driver;
|
||||
err:
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bdc_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct bdc *bdc = gadget_to_bdc(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
bdc_stop(bdc);
|
||||
bdc->gadget_driver = NULL;
|
||||
bdc->gadget.dev.driver = NULL;
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdc_udc_pullup(struct usb_gadget *gadget, int is_on)
|
||||
{
|
||||
struct bdc *bdc = gadget_to_bdc(gadget);
|
||||
unsigned long flags;
|
||||
u32 uspc;
|
||||
|
||||
dev_dbg(bdc->dev, "%s() is_on:%d\n", __func__, is_on);
|
||||
if (!gadget)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
if (!is_on) {
|
||||
bdc_softdisconn(bdc);
|
||||
bdc->pullup = false;
|
||||
} else {
|
||||
/*
|
||||
* For a self powered device, we need to wait till we receive
|
||||
* a VBUS change and Vbus present event, then if pullup flag
|
||||
* is set, then only we present the Termintation.
|
||||
*/
|
||||
bdc->pullup = true;
|
||||
/*
|
||||
* Check if BDC is already connected to Host i.e Vbus=1,
|
||||
* if yes, then present TERM now, this is typical for bus
|
||||
* powered devices.
|
||||
*/
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
if (uspc & BDC_VBS)
|
||||
bdc_softconn(bdc);
|
||||
}
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
|
||||
int is_self)
|
||||
{
|
||||
struct bdc *bdc = gadget_to_bdc(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
if (!is_self)
|
||||
bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
|
||||
else
|
||||
bdc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
|
||||
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdc_udc_wakeup(struct usb_gadget *gadget)
|
||||
{
|
||||
struct bdc *bdc = gadget_to_bdc(gadget);
|
||||
unsigned long flags;
|
||||
u8 link_state;
|
||||
u32 uspc;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(bdc->dev,
|
||||
"%s() bdc->devstatus=%08x\n",
|
||||
__func__, bdc->devstatus);
|
||||
|
||||
if (!(bdc->devstatus & REMOTE_WAKE_ENABLE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
link_state = BDC_PST(uspc);
|
||||
dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
|
||||
if (link_state != BDC_LINK_STATE_U3) {
|
||||
dev_warn(bdc->dev,
|
||||
"can't wakeup from link state %d\n",
|
||||
link_state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (bdc->gadget.speed == USB_SPEED_SUPER)
|
||||
bdc->devstatus |= REMOTE_WAKEUP_ISSUED;
|
||||
|
||||
uspc &= ~BDC_PST_MASK;
|
||||
uspc &= (~BDC_USPSC_RW);
|
||||
uspc |= BDC_PST(BDC_LINK_STATE_U0);
|
||||
uspc |= BDC_SWS;
|
||||
bdc_writel(bdc->regs, BDC_USPC, uspc);
|
||||
uspc = bdc_readl(bdc->regs, BDC_USPC);
|
||||
link_state = BDC_PST(uspc);
|
||||
dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
|
||||
out:
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct usb_gadget_ops bdc_gadget_ops = {
|
||||
.wakeup = bdc_udc_wakeup,
|
||||
.set_selfpowered = bdc_udc_set_selfpowered,
|
||||
.pullup = bdc_udc_pullup,
|
||||
.udc_start = bdc_udc_start,
|
||||
.udc_stop = bdc_udc_stop,
|
||||
};
|
||||
|
||||
/* Init the gadget interface and register the udc */
|
||||
int bdc_udc_init(struct bdc *bdc)
|
||||
{
|
||||
u32 temp;
|
||||
int ret;
|
||||
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
bdc->gadget.ops = &bdc_gadget_ops;
|
||||
bdc->gadget.max_speed = USB_SPEED_SUPER;
|
||||
bdc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
bdc->gadget.dev.parent = bdc->dev;
|
||||
|
||||
bdc->gadget.sg_supported = false;
|
||||
|
||||
|
||||
bdc->gadget.name = BRCM_BDC_NAME;
|
||||
ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
|
||||
IRQF_SHARED , BRCM_BDC_NAME, bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev,
|
||||
"failed to request irq #%d %d\n",
|
||||
bdc->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdc_init_ep(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "failed to register udc\n");
|
||||
goto err0;
|
||||
}
|
||||
usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED);
|
||||
bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
|
||||
/*
|
||||
* Allocate bd list for ep0 only, ep0 will be enabled on connect
|
||||
* status report when the speed is known
|
||||
*/
|
||||
ret = bdc_ep_enable(bdc->bdc_ep_array[1]);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "fail to enable %s\n",
|
||||
bdc->bdc_ep_array[1]->name);
|
||||
goto err1;
|
||||
}
|
||||
INIT_DELAYED_WORK(&bdc->func_wake_notify, bdc_func_wake_timer);
|
||||
/* Enable Interrupts */
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
temp |= BDC_GIE;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
return 0;
|
||||
err1:
|
||||
usb_del_gadget_udc(&bdc->gadget);
|
||||
err0:
|
||||
bdc_free_ep(bdc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bdc_udc_exit(struct bdc *bdc)
|
||||
{
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
bdc_ep_disable(bdc->bdc_ep_array[1]);
|
||||
usb_del_gadget_udc(&bdc->gadget);
|
||||
bdc_free_ep(bdc);
|
||||
}
|
@ -367,19 +367,22 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
|
||||
dum_hcd->active)
|
||||
dum_hcd->resuming = 0;
|
||||
|
||||
/* if !connected or reset */
|
||||
/* Currently !connected or in reset */
|
||||
if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
|
||||
(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
|
||||
/*
|
||||
* We're connected and not reset (reset occurred now),
|
||||
* and driver attached - disconnect!
|
||||
*/
|
||||
if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
|
||||
(dum_hcd->old_status & USB_PORT_STAT_RESET) == 0 &&
|
||||
dum->driver) {
|
||||
unsigned disconnect = USB_PORT_STAT_CONNECTION &
|
||||
dum_hcd->old_status & (~dum_hcd->port_status);
|
||||
unsigned reset = USB_PORT_STAT_RESET &
|
||||
(~dum_hcd->old_status) & dum_hcd->port_status;
|
||||
|
||||
/* Report reset and disconnect events to the driver */
|
||||
if (dum->driver && (disconnect || reset)) {
|
||||
stop_activity(dum);
|
||||
spin_unlock(&dum->lock);
|
||||
dum->driver->disconnect(&dum->gadget);
|
||||
if (reset)
|
||||
usb_gadget_udc_reset(&dum->gadget, dum->driver);
|
||||
else
|
||||
dum->driver->disconnect(&dum->gadget);
|
||||
spin_lock(&dum->lock);
|
||||
}
|
||||
} else if (dum_hcd->active != dum_hcd->old_active) {
|
||||
@ -851,8 +854,7 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value)
|
||||
|
||||
static int dummy_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int dummy_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int dummy_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops dummy_ops = {
|
||||
.get_frame = dummy_g_get_frame,
|
||||
@ -908,23 +910,16 @@ static int dummy_udc_start(struct usb_gadget *g,
|
||||
*/
|
||||
|
||||
dum->devstatus = 0;
|
||||
|
||||
dum->driver = driver;
|
||||
dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int dummy_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
|
||||
struct dummy *dum = dum_hcd->dum;
|
||||
|
||||
if (driver)
|
||||
dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
dum->driver = NULL;
|
||||
|
||||
return 0;
|
||||
@ -2370,7 +2365,6 @@ static void dummy_stop(struct usb_hcd *hcd)
|
||||
|
||||
dum = hcd_to_dummy_hcd(hcd)->dum;
|
||||
device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
|
||||
usb_gadget_unregister_driver(dum->driver);
|
||||
dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -1887,8 +1887,7 @@ static int qe_get_frame(struct usb_gadget *gadget)
|
||||
|
||||
static int fsl_qe_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int fsl_qe_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int fsl_qe_stop(struct usb_gadget *gadget);
|
||||
|
||||
/* defined in usb_gadget.h */
|
||||
static const struct usb_gadget_ops qe_gadget_ops = {
|
||||
@ -1918,7 +1917,7 @@ static int reset_queues(struct qe_udc *udc)
|
||||
|
||||
/* report disconnect; the driver is already quiesced */
|
||||
spin_unlock(&udc->lock);
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
usb_gadget_udc_reset(&udc->gadget, udc->driver);
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
return 0;
|
||||
@ -2305,13 +2304,10 @@ static int fsl_qe_start(struct usb_gadget *gadget,
|
||||
udc->ep0_dir = USB_DIR_OUT;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
dev_info(udc->dev, "%s bind to driver %s\n", udc->gadget.name,
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_qe_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int fsl_qe_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct qe_udc *udc;
|
||||
struct qe_ep *loop_ep;
|
||||
@ -2336,8 +2332,6 @@ static int fsl_qe_stop(struct usb_gadget *gadget,
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
dev_info(udc->dev, "unregistered gadget driver '%s'\r\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2538,7 +2532,6 @@ static int qe_udc_probe(struct platform_device *ofdev)
|
||||
/* create a buf for ZLP send, need to remain zeroed */
|
||||
udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL);
|
||||
if (udc->nullbuf == NULL) {
|
||||
dev_err(udc->dev, "cannot alloc nullbuf\n");
|
||||
ret = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
@ -1236,9 +1236,8 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
|
||||
|
||||
static int fsl_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int fsl_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
/* defined in gadget.h */
|
||||
static int fsl_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops fsl_gadget_ops = {
|
||||
.get_frame = fsl_get_frame,
|
||||
.wakeup = fsl_wakeup,
|
||||
@ -1772,7 +1771,7 @@ static void bus_resume(struct fsl_udc *udc)
|
||||
}
|
||||
|
||||
/* Clear up all ep queues */
|
||||
static int reset_queues(struct fsl_udc *udc)
|
||||
static int reset_queues(struct fsl_udc *udc, bool bus_reset)
|
||||
{
|
||||
u8 pipe;
|
||||
|
||||
@ -1781,7 +1780,10 @@ static int reset_queues(struct fsl_udc *udc)
|
||||
|
||||
/* report disconnect; the driver is already quiesced */
|
||||
spin_unlock(&udc->lock);
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
if (bus_reset)
|
||||
usb_gadget_udc_reset(&udc->gadget, udc->driver);
|
||||
else
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
return 0;
|
||||
@ -1835,7 +1837,7 @@ static void reset_irq(struct fsl_udc *udc)
|
||||
udc->bus_reset = 1;
|
||||
/* Reset all the queues, include XD, dTD, EP queue
|
||||
* head and TR Queue */
|
||||
reset_queues(udc);
|
||||
reset_queues(udc, true);
|
||||
udc->usb_state = USB_STATE_DEFAULT;
|
||||
} else {
|
||||
VDBG("Controller reset");
|
||||
@ -1844,7 +1846,7 @@ static void reset_irq(struct fsl_udc *udc)
|
||||
dr_controller_setup(udc);
|
||||
|
||||
/* Reset all internal used Queues */
|
||||
reset_queues(udc);
|
||||
reset_queues(udc, false);
|
||||
|
||||
ep0_setup(udc);
|
||||
|
||||
@ -1975,8 +1977,7 @@ static int fsl_udc_start(struct usb_gadget *g,
|
||||
}
|
||||
|
||||
/* Disconnect from gadget driver */
|
||||
static int fsl_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int fsl_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct fsl_ep *loop_ep;
|
||||
unsigned long flags;
|
||||
|
@ -1320,8 +1320,7 @@ static int fusb300_udc_start(struct usb_gadget *g,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fusb300_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int fusb300_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct fusb300 *fusb300 = to_fusb300(g);
|
||||
|
||||
|
@ -992,8 +992,7 @@ static int goku_get_frame(struct usb_gadget *_gadget)
|
||||
|
||||
static int goku_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int goku_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int goku_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops goku_ops = {
|
||||
.get_frame = goku_get_frame,
|
||||
@ -1364,8 +1363,7 @@ static void stop_activity(struct goku_udc *dev)
|
||||
udc_enable(dev);
|
||||
}
|
||||
|
||||
static int goku_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int goku_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct goku_udc *dev = to_goku_udc(g);
|
||||
unsigned long flags;
|
||||
|
@ -1932,14 +1932,10 @@ static int gr_udc_start(struct usb_gadget *gadget,
|
||||
|
||||
spin_unlock(&dev->lock);
|
||||
|
||||
dev_info(dev->dev, "Started with gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gr_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int gr_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct gr_udc *dev = to_gr_udc(gadget);
|
||||
unsigned long flags;
|
||||
@ -1951,8 +1947,6 @@ static int gr_udc_stop(struct usb_gadget *gadget,
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
dev_info(dev->dev, "Stopped\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -582,8 +582,7 @@ static void create_debug_file(struct lpc32xx_udc *udc)
|
||||
|
||||
static void remove_debug_file(struct lpc32xx_udc *udc)
|
||||
{
|
||||
if (udc->pde)
|
||||
debugfs_remove(udc->pde);
|
||||
debugfs_remove(udc->pde);
|
||||
}
|
||||
|
||||
#else
|
||||
@ -2559,7 +2558,7 @@ static int lpc32xx_pullup(struct usb_gadget *gadget, int is_on)
|
||||
}
|
||||
|
||||
static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *);
|
||||
static int lpc32xx_stop(struct usb_gadget *, struct usb_gadget_driver *);
|
||||
static int lpc32xx_stop(struct usb_gadget *);
|
||||
|
||||
static const struct usb_gadget_ops lpc32xx_udc_ops = {
|
||||
.get_frame = lpc32xx_get_frame,
|
||||
@ -2961,15 +2960,11 @@ static int lpc32xx_start(struct usb_gadget *gadget,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc32xx_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int lpc32xx_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
int i;
|
||||
struct lpc32xx_udc *udc = to_udc(gadget);
|
||||
|
||||
if (!driver || driver != udc->driver)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
|
||||
disable_irq(udc->udp_irq[i]);
|
||||
|
||||
|
@ -1142,7 +1142,7 @@ static void irq_device_state(struct m66592 *m66592)
|
||||
m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
|
||||
|
||||
if (dvsq == M66592_DS_DFLT) { /* bus reset */
|
||||
m66592->driver->disconnect(&m66592->gadget);
|
||||
usb_gadget_udc_reset(&m66592->gadget, m66592->driver);
|
||||
m66592_update_usb_speed(m66592);
|
||||
}
|
||||
if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
|
||||
@ -1485,8 +1485,7 @@ static int m66592_udc_start(struct usb_gadget *g,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int m66592_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int m66592_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct m66592 *m66592 = to_m66592(g);
|
||||
|
||||
|
@ -1266,8 +1266,7 @@ static int mv_u3d_start(struct usb_gadget *g,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_u3d_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int mv_u3d_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
|
||||
struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
|
||||
@ -1284,7 +1283,7 @@ static int mv_u3d_stop(struct usb_gadget *g,
|
||||
mv_u3d_controller_stop(u3d);
|
||||
/* stop all usb activities */
|
||||
u3d->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
mv_u3d_stop_activity(u3d, driver);
|
||||
mv_u3d_stop_activity(u3d, NULL);
|
||||
mv_u3d_disable(u3d);
|
||||
|
||||
if (pdata->phy_deinit)
|
||||
|
@ -1223,7 +1223,7 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
|
||||
}
|
||||
|
||||
static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *);
|
||||
static int mv_udc_stop(struct usb_gadget *, struct usb_gadget_driver *);
|
||||
static int mv_udc_stop(struct usb_gadget *);
|
||||
/* device controller usb_gadget_ops structure */
|
||||
static const struct usb_gadget_ops mv_ops = {
|
||||
|
||||
@ -1307,6 +1307,23 @@ static void nuke(struct mv_ep *ep, int status)
|
||||
}
|
||||
}
|
||||
|
||||
static void gadget_reset(struct mv_udc *udc, struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct mv_ep *ep;
|
||||
|
||||
nuke(&udc->eps[0], -ESHUTDOWN);
|
||||
|
||||
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
}
|
||||
|
||||
/* report reset; the driver is already quiesced */
|
||||
if (driver) {
|
||||
spin_unlock(&udc->lock);
|
||||
usb_gadget_udc_reset(&udc->gadget, driver);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
}
|
||||
/* stop all USB activities */
|
||||
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
|
||||
{
|
||||
@ -1371,8 +1388,7 @@ static int mv_udc_start(struct usb_gadget *gadget,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int mv_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct mv_udc *udc;
|
||||
unsigned long flags;
|
||||
@ -1386,7 +1402,7 @@ static int mv_udc_stop(struct usb_gadget *gadget,
|
||||
|
||||
/* stop all usb activities */
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
stop_activity(udc, driver);
|
||||
stop_activity(udc, NULL);
|
||||
mv_udc_disable(udc);
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
@ -1882,7 +1898,7 @@ static void irq_process_reset(struct mv_udc *udc)
|
||||
dev_info(&udc->dev->dev, "usb bus reset\n");
|
||||
udc->usb_state = USB_STATE_DEFAULT;
|
||||
/* reset all the queues, stop all USB activities */
|
||||
stop_activity(udc, udc->driver);
|
||||
gadget_reset(udc, udc->driver);
|
||||
} else {
|
||||
dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n",
|
||||
readl(&udc->op_regs->portsc));
|
||||
@ -2107,10 +2123,8 @@ static int mv_udc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
|
||||
if (udc == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for udc\n");
|
||||
if (udc == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
udc->done = &release_done;
|
||||
udc->pdata = dev_get_platdata(&pdev->dev);
|
||||
@ -2207,7 +2221,6 @@ static int mv_udc_probe(struct platform_device *pdev)
|
||||
size = udc->max_eps * sizeof(struct mv_ep) *2;
|
||||
udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
|
||||
if (udc->eps == NULL) {
|
||||
dev_err(&pdev->dev, "allocate ep memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_destroy_dma;
|
||||
}
|
||||
@ -2216,7 +2229,6 @@ static int mv_udc_probe(struct platform_device *pdev)
|
||||
udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req),
|
||||
GFP_KERNEL);
|
||||
if (!udc->status_req) {
|
||||
dev_err(&pdev->dev, "allocate status_req memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_destroy_dma;
|
||||
}
|
||||
|
@ -1169,8 +1169,7 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
|
||||
static int net2272_start(struct usb_gadget *_gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int net2272_stop(struct usb_gadget *_gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int net2272_stop(struct usb_gadget *_gadget);
|
||||
|
||||
static const struct usb_gadget_ops net2272_ops = {
|
||||
.get_frame = net2272_get_frame,
|
||||
@ -1471,8 +1470,6 @@ static int net2272_start(struct usb_gadget *_gadget,
|
||||
*/
|
||||
net2272_ep0_start(dev);
|
||||
|
||||
dev_dbg(dev->dev, "%s ready\n", driver->driver.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1502,8 +1499,7 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
|
||||
net2272_usb_reinit(dev);
|
||||
}
|
||||
|
||||
static int net2272_stop(struct usb_gadget *_gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int net2272_stop(struct usb_gadget *_gadget)
|
||||
{
|
||||
struct net2272 *dev;
|
||||
unsigned long flags;
|
||||
@ -1511,12 +1507,11 @@ static int net2272_stop(struct usb_gadget *_gadget,
|
||||
dev = container_of(_gadget, struct net2272, gadget);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
stop_activity(dev, driver);
|
||||
stop_activity(dev, NULL);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
dev->driver = NULL;
|
||||
|
||||
dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1987,17 +1982,42 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
|
||||
mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED);
|
||||
|
||||
if (stat & tmp) {
|
||||
bool reset = false;
|
||||
bool disconnect = false;
|
||||
|
||||
/*
|
||||
* Ignore disconnects and resets if the speed hasn't been set.
|
||||
* VBUS can bounce and there's always an initial reset.
|
||||
*/
|
||||
net2272_write(dev, IRQSTAT1, tmp);
|
||||
if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
|
||||
((net2272_read(dev, USBCTL1) & mask) == 0))
|
||||
|| ((net2272_read(dev, USBCTL1) & (1 << VBUS_PIN))
|
||||
== 0))
|
||||
&& (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
|
||||
dev_dbg(dev->dev, "disconnect %s\n",
|
||||
dev->driver->driver.name);
|
||||
stop_activity(dev, dev->driver);
|
||||
net2272_ep0_start(dev);
|
||||
return;
|
||||
if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
if ((stat & (1 << VBUS_INTERRUPT)) &&
|
||||
(net2272_read(dev, USBCTL1) &
|
||||
(1 << VBUS_PIN)) == 0) {
|
||||
disconnect = true;
|
||||
dev_dbg(dev->dev, "disconnect %s\n",
|
||||
dev->driver->driver.name);
|
||||
} else if ((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
|
||||
(net2272_read(dev, USBCTL1) & mask)
|
||||
== 0) {
|
||||
reset = true;
|
||||
dev_dbg(dev->dev, "reset %s\n",
|
||||
dev->driver->driver.name);
|
||||
}
|
||||
|
||||
if (disconnect || reset) {
|
||||
stop_activity(dev, dev->driver);
|
||||
net2272_ep0_start(dev);
|
||||
spin_unlock(&dev->lock);
|
||||
if (reset)
|
||||
usb_gadget_udc_reset
|
||||
(&dev->gadget, dev->driver);
|
||||
else
|
||||
(dev->driver->disconnect)
|
||||
(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
stat &= ~tmp;
|
||||
|
||||
@ -2200,18 +2220,8 @@ static void
|
||||
net2272_remove(struct net2272 *dev)
|
||||
{
|
||||
usb_del_gadget_udc(&dev->gadget);
|
||||
|
||||
/* start with the driver above us */
|
||||
if (dev->driver) {
|
||||
/* should have been done already by driver model core */
|
||||
dev_warn(dev->dev, "pci remove, driver '%s' is still registered\n",
|
||||
dev->driver->driver.name);
|
||||
usb_gadget_unregister_driver(dev->driver);
|
||||
}
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
iounmap(dev->base_addr);
|
||||
|
||||
device_remove_file(dev->dev, &dev_attr_registers);
|
||||
|
||||
dev_info(dev->dev, "unbind\n");
|
||||
|
@ -1118,10 +1118,10 @@ static void scan_dma_completions(struct net2280_ep *ep)
|
||||
break;
|
||||
} else if (!ep->is_in &&
|
||||
(req->req.length % ep->ep.maxpacket) != 0) {
|
||||
tmp = readl(&ep->regs->ep_stat);
|
||||
if (ep->dev->quirks & PLX_SUPERSPEED)
|
||||
return dma_done(ep, req, tmp, 0);
|
||||
|
||||
tmp = readl(&ep->regs->ep_stat);
|
||||
/* AVOID TROUBLE HERE by not issuing short reads from
|
||||
* your gadget driver. That helps avoids errata 0121,
|
||||
* 0122, and 0124; not all cases trigger the warning.
|
||||
@ -1548,8 +1548,7 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
|
||||
static int net2280_start(struct usb_gadget *_gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int net2280_stop(struct usb_gadget *_gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int net2280_stop(struct usb_gadget *_gadget);
|
||||
|
||||
static const struct usb_gadget_ops net2280_ops = {
|
||||
.get_frame = net2280_get_frame,
|
||||
@ -2397,11 +2396,6 @@ static int net2280_start(struct usb_gadget *_gadget,
|
||||
|
||||
ep0_start(dev);
|
||||
|
||||
ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n",
|
||||
driver->driver.name,
|
||||
readl(&dev->usb->usbctl),
|
||||
readl(&dev->usb->stdrsp));
|
||||
|
||||
/* pci writes may still be posted */
|
||||
return 0;
|
||||
|
||||
@ -2437,8 +2431,7 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
|
||||
usb_reinit(dev);
|
||||
}
|
||||
|
||||
static int net2280_stop(struct usb_gadget *_gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int net2280_stop(struct usb_gadget *_gadget)
|
||||
{
|
||||
struct net2280 *dev;
|
||||
unsigned long flags;
|
||||
@ -2446,11 +2439,9 @@ static int net2280_stop(struct usb_gadget *_gadget,
|
||||
dev = container_of(_gadget, struct net2280, gadget);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
stop_activity(dev, driver);
|
||||
stop_activity(dev, NULL);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
dev->driver = NULL;
|
||||
|
||||
net2280_led_active(dev, 0);
|
||||
|
||||
/* Disable full-speed test mode */
|
||||
@ -2460,8 +2451,7 @@ static int net2280_stop(struct usb_gadget *_gadget,
|
||||
device_remove_file(&dev->pdev->dev, &dev_attr_function);
|
||||
device_remove_file(&dev->pdev->dev, &dev_attr_queues);
|
||||
|
||||
ep_dbg(dev, "unregistered driver '%s'\n",
|
||||
driver ? driver->driver.name : "");
|
||||
dev->driver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3318,17 +3308,42 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
|
||||
* only indicates a change in the reset state).
|
||||
*/
|
||||
if (stat & tmp) {
|
||||
bool reset = false;
|
||||
bool disconnect = false;
|
||||
|
||||
/*
|
||||
* Ignore disconnects and resets if the speed hasn't been set.
|
||||
* VBUS can bounce and there's always an initial reset.
|
||||
*/
|
||||
writel(tmp, &dev->regs->irqstat1);
|
||||
if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
|
||||
((readl(&dev->usb->usbstat) & mask) == 0)) ||
|
||||
((readl(&dev->usb->usbctl) &
|
||||
BIT(VBUS_PIN)) == 0)) &&
|
||||
(dev->gadget.speed != USB_SPEED_UNKNOWN)) {
|
||||
ep_dbg(dev, "disconnect %s\n",
|
||||
dev->driver->driver.name);
|
||||
stop_activity(dev, dev->driver);
|
||||
ep0_start(dev);
|
||||
return;
|
||||
if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
if ((stat & BIT(VBUS_INTERRUPT)) &&
|
||||
(readl(&dev->usb->usbctl) &
|
||||
BIT(VBUS_PIN)) == 0) {
|
||||
disconnect = true;
|
||||
ep_dbg(dev, "disconnect %s\n",
|
||||
dev->driver->driver.name);
|
||||
} else if ((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
|
||||
(readl(&dev->usb->usbstat) & mask)
|
||||
== 0) {
|
||||
reset = true;
|
||||
ep_dbg(dev, "reset %s\n",
|
||||
dev->driver->driver.name);
|
||||
}
|
||||
|
||||
if (disconnect || reset) {
|
||||
stop_activity(dev, dev->driver);
|
||||
ep0_start(dev);
|
||||
spin_unlock(&dev->lock);
|
||||
if (reset)
|
||||
usb_gadget_udc_reset
|
||||
(&dev->gadget, dev->driver);
|
||||
else
|
||||
(dev->driver->disconnect)
|
||||
(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
stat &= ~tmp;
|
||||
|
||||
|
@ -1311,8 +1311,7 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)
|
||||
|
||||
static int omap_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int omap_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int omap_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops omap_gadget_ops = {
|
||||
.get_frame = omap_get_frame,
|
||||
@ -2102,8 +2101,7 @@ done:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int omap_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int omap_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
unsigned long flags;
|
||||
int status = -ENODEV;
|
||||
|
@ -1240,8 +1240,8 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
|
||||
|
||||
static int pch_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pch_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pch_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops pch_udc_ops = {
|
||||
.get_frame = pch_udc_pcd_get_frame,
|
||||
.wakeup = pch_udc_pcd_wakeup,
|
||||
@ -2592,9 +2592,9 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
|
||||
/* Complete request queue */
|
||||
empty_req_queue(ep);
|
||||
}
|
||||
if (dev->driver && dev->driver->disconnect) {
|
||||
if (dev->driver) {
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->disconnect(&dev->gadget);
|
||||
usb_gadget_udc_reset(&dev->gadget, dev->driver);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
}
|
||||
@ -3008,8 +3008,7 @@ static int pch_udc_start(struct usb_gadget *g,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int pch_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct pch_udc_dev *dev = to_pch_udc(g);
|
||||
|
||||
|
@ -998,8 +998,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
|
||||
static int pxa25x_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pxa25x_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pxa25x_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops pxa25x_udc_ops = {
|
||||
.get_frame = pxa25x_udc_get_frame,
|
||||
@ -1135,11 +1134,7 @@ static const struct file_operations debug_fops = {
|
||||
dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
|
||||
S_IRUGO, NULL, dev, &debug_fops); \
|
||||
} while (0)
|
||||
#define remove_debug_files(dev) \
|
||||
do { \
|
||||
if (dev->debugfs_udc) \
|
||||
debugfs_remove(dev->debugfs_udc); \
|
||||
} while (0)
|
||||
#define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc)
|
||||
|
||||
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
|
||||
|
||||
@ -1284,6 +1279,33 @@ bind_fail:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* don't disconnect drivers more than once */
|
||||
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
driver = NULL;
|
||||
dev->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
/* prevent new request submissions, kill any outstanding requests */
|
||||
for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
|
||||
struct pxa25x_ep *ep = &dev->ep[i];
|
||||
|
||||
ep->stopped = 1;
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
}
|
||||
del_timer_sync(&dev->timer);
|
||||
|
||||
/* report reset; the driver is already quiesced */
|
||||
if (driver)
|
||||
usb_gadget_udc_reset(&dev->gadget, driver);
|
||||
|
||||
/* re-init driver-visible data structures */
|
||||
udc_reinit(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
|
||||
{
|
||||
@ -1311,15 +1333,14 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
|
||||
udc_reinit(dev);
|
||||
}
|
||||
|
||||
static int pxa25x_udc_stop(struct usb_gadget*g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int pxa25x_udc_stop(struct usb_gadget*g)
|
||||
{
|
||||
struct pxa25x_udc *dev = to_pxa25x(g);
|
||||
|
||||
local_irq_disable();
|
||||
dev->pullup = 0;
|
||||
pullup(dev);
|
||||
stop_activity(dev, driver);
|
||||
stop_activity(dev, NULL);
|
||||
local_irq_enable();
|
||||
|
||||
if (!IS_ERR_OR_NULL(dev->transceiver))
|
||||
@ -1723,7 +1744,7 @@ pxa25x_udc_irq(int irq, void *_dev)
|
||||
/* reset driver and endpoints,
|
||||
* in case that's not yet done
|
||||
*/
|
||||
stop_activity (dev, dev->driver);
|
||||
reset_gadget(dev, dev->driver);
|
||||
|
||||
} else {
|
||||
DBG(DBG_VERBOSE, "USB reset end\n");
|
||||
|
@ -22,10 +22,13 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/platform_data/pxa2xx_udc.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
@ -1507,18 +1510,13 @@ static struct usb_ep_ops pxa_ep_ops = {
|
||||
*/
|
||||
static void dplus_pullup(struct pxa_udc *udc, int on)
|
||||
{
|
||||
if (on) {
|
||||
if (gpio_is_valid(udc->mach->gpio_pullup))
|
||||
gpio_set_value(udc->mach->gpio_pullup,
|
||||
!udc->mach->gpio_pullup_inverted);
|
||||
if (udc->mach->udc_command)
|
||||
udc->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
|
||||
} else {
|
||||
if (gpio_is_valid(udc->mach->gpio_pullup))
|
||||
gpio_set_value(udc->mach->gpio_pullup,
|
||||
udc->mach->gpio_pullup_inverted);
|
||||
if (udc->mach->udc_command)
|
||||
udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
|
||||
if (udc->gpiod) {
|
||||
gpiod_set_value(udc->gpiod, on);
|
||||
} else if (udc->udc_command) {
|
||||
if (on)
|
||||
udc->udc_command(PXA2XX_UDC_CMD_CONNECT);
|
||||
else
|
||||
udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
|
||||
}
|
||||
udc->pullup_on = on;
|
||||
}
|
||||
@ -1609,7 +1607,7 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
|
||||
{
|
||||
struct pxa_udc *udc = to_gadget_udc(_gadget);
|
||||
|
||||
if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
|
||||
if (!udc->gpiod && !udc->udc_command)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
dplus_pullup(udc, is_active);
|
||||
@ -1671,8 +1669,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
|
||||
static int pxa27x_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pxa27x_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pxa27x_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops pxa_udc_ops = {
|
||||
.get_frame = pxa_udc_get_frame,
|
||||
@ -1701,10 +1698,10 @@ static void udc_disable(struct pxa_udc *udc)
|
||||
udc_writel(udc, UDCICR1, 0);
|
||||
|
||||
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
|
||||
clk_disable(udc->clk);
|
||||
|
||||
ep0_idle(udc);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
clk_disable(udc->clk);
|
||||
|
||||
udc->enabled = 0;
|
||||
}
|
||||
@ -1757,16 +1754,16 @@ static void udc_enable(struct pxa_udc *udc)
|
||||
if (udc->enabled)
|
||||
return;
|
||||
|
||||
clk_enable(udc->clk);
|
||||
udc_writel(udc, UDCICR0, 0);
|
||||
udc_writel(udc, UDCICR1, 0);
|
||||
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
|
||||
|
||||
clk_enable(udc->clk);
|
||||
|
||||
ep0_idle(udc);
|
||||
udc->gadget.speed = USB_SPEED_FULL;
|
||||
memset(&udc->stats, 0, sizeof(udc->stats));
|
||||
|
||||
pxa_eps_setup(udc);
|
||||
udc_set_mask_UDCCR(udc, UDCCR_UDE);
|
||||
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
|
||||
udelay(2);
|
||||
@ -1859,12 +1856,11 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
|
||||
*
|
||||
* Returns 0 if no error, -ENODEV, -EINVAL otherwise
|
||||
*/
|
||||
static int pxa27x_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int pxa27x_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct pxa_udc *udc = to_pxa(g);
|
||||
|
||||
stop_activity(udc, driver);
|
||||
stop_activity(udc, NULL);
|
||||
udc_disable(udc);
|
||||
dplus_pullup(udc, 0);
|
||||
|
||||
@ -2404,6 +2400,14 @@ static struct pxa_udc memory = {
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static struct of_device_id udc_pxa_dt_ids[] = {
|
||||
{ .compatible = "marvell,pxa270-udc" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* pxa_udc_probe - probes the udc device
|
||||
* @_dev: platform device
|
||||
@ -2416,81 +2420,77 @@ static int pxa_udc_probe(struct platform_device *pdev)
|
||||
struct resource *regs;
|
||||
struct pxa_udc *udc = &memory;
|
||||
int retval = 0, gpio;
|
||||
struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev);
|
||||
unsigned long gpio_flags;
|
||||
|
||||
if (mach) {
|
||||
gpio_flags = mach->gpio_pullup_inverted ? GPIOF_ACTIVE_LOW : 0;
|
||||
gpio = mach->gpio_pullup;
|
||||
if (gpio_is_valid(gpio)) {
|
||||
retval = devm_gpio_request_one(&pdev->dev, gpio,
|
||||
gpio_flags,
|
||||
"USB D+ pullup");
|
||||
if (retval)
|
||||
return retval;
|
||||
udc->gpiod = gpio_to_desc(mach->gpio_pullup);
|
||||
}
|
||||
udc->udc_command = mach->udc_command;
|
||||
} else {
|
||||
udc->gpiod = devm_gpiod_get(&pdev->dev, NULL);
|
||||
}
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
return -ENXIO;
|
||||
udc->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
if (IS_ERR(udc->regs))
|
||||
return PTR_ERR(udc->regs);
|
||||
udc->irq = platform_get_irq(pdev, 0);
|
||||
if (udc->irq < 0)
|
||||
return udc->irq;
|
||||
|
||||
udc->dev = &pdev->dev;
|
||||
udc->mach = dev_get_platdata(&pdev->dev);
|
||||
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
|
||||
gpio = udc->mach->gpio_pullup;
|
||||
if (gpio_is_valid(gpio)) {
|
||||
retval = gpio_request(gpio, "USB D+ pullup");
|
||||
if (retval == 0)
|
||||
gpio_direction_output(gpio,
|
||||
udc->mach->gpio_pullup_inverted);
|
||||
}
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "Couldn't request gpio %d : %d\n",
|
||||
gpio, retval);
|
||||
return retval;
|
||||
if (IS_ERR(udc->gpiod)) {
|
||||
dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
|
||||
PTR_ERR(udc->gpiod));
|
||||
return PTR_ERR(udc->gpiod);
|
||||
}
|
||||
if (udc->gpiod)
|
||||
gpiod_direction_output(udc->gpiod, 0);
|
||||
|
||||
udc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(udc->clk))
|
||||
return PTR_ERR(udc->clk);
|
||||
|
||||
udc->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(udc->clk)) {
|
||||
retval = PTR_ERR(udc->clk);
|
||||
goto err_clk;
|
||||
}
|
||||
retval = clk_prepare(udc->clk);
|
||||
if (retval)
|
||||
goto err_clk_prepare;
|
||||
|
||||
retval = -ENOMEM;
|
||||
udc->regs = ioremap(regs->start, resource_size(regs));
|
||||
if (!udc->regs) {
|
||||
dev_err(&pdev->dev, "Unable to map UDC I/O memory\n");
|
||||
goto err_map;
|
||||
}
|
||||
return retval;
|
||||
|
||||
udc->vbus_sensed = 0;
|
||||
|
||||
the_controller = udc;
|
||||
platform_set_drvdata(pdev, udc);
|
||||
udc_init_data(udc);
|
||||
pxa_eps_setup(udc);
|
||||
|
||||
/* irq setup after old hardware state is cleaned up */
|
||||
retval = request_irq(udc->irq, pxa_udc_irq,
|
||||
IRQF_SHARED, driver_name, udc);
|
||||
retval = devm_request_irq(&pdev->dev, udc->irq, pxa_udc_irq,
|
||||
IRQF_SHARED, driver_name, udc);
|
||||
if (retval != 0) {
|
||||
dev_err(udc->dev, "%s: can't get irq %i, err %d\n",
|
||||
driver_name, udc->irq, retval);
|
||||
goto err_irq;
|
||||
goto err;
|
||||
}
|
||||
|
||||
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
|
||||
if (retval)
|
||||
goto err_add_udc;
|
||||
goto err;
|
||||
|
||||
pxa_init_debugfs(udc);
|
||||
|
||||
if (should_enable_udc(udc))
|
||||
udc_enable(udc);
|
||||
return 0;
|
||||
|
||||
err_add_udc:
|
||||
free_irq(udc->irq, udc);
|
||||
err_irq:
|
||||
iounmap(udc->regs);
|
||||
err_map:
|
||||
err:
|
||||
clk_unprepare(udc->clk);
|
||||
err_clk_prepare:
|
||||
clk_put(udc->clk);
|
||||
udc->clk = NULL;
|
||||
err_clk:
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -2501,22 +2501,15 @@ err_clk:
|
||||
static int pxa_udc_remove(struct platform_device *_dev)
|
||||
{
|
||||
struct pxa_udc *udc = platform_get_drvdata(_dev);
|
||||
int gpio = udc->mach->gpio_pullup;
|
||||
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
usb_gadget_unregister_driver(udc->driver);
|
||||
free_irq(udc->irq, udc);
|
||||
pxa_cleanup_debugfs(udc);
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_free(gpio);
|
||||
|
||||
usb_put_phy(udc->transceiver);
|
||||
|
||||
udc->transceiver = NULL;
|
||||
the_controller = NULL;
|
||||
clk_unprepare(udc->clk);
|
||||
clk_put(udc->clk);
|
||||
iounmap(udc->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2546,19 +2539,11 @@ extern void pxa27x_clear_otgph(void);
|
||||
*/
|
||||
static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state)
|
||||
{
|
||||
int i;
|
||||
struct pxa_udc *udc = platform_get_drvdata(_dev);
|
||||
struct pxa_ep *ep;
|
||||
|
||||
ep = &udc->pxa_ep[0];
|
||||
udc->udccsr0 = udc_ep_readl(ep, UDCCSR);
|
||||
for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
|
||||
ep = &udc->pxa_ep[i];
|
||||
ep->udccsr_value = udc_ep_readl(ep, UDCCSR);
|
||||
ep->udccr_value = udc_ep_readl(ep, UDCCR);
|
||||
ep_dbg(ep, "udccsr:0x%03x, udccr:0x%x\n",
|
||||
ep->udccsr_value, ep->udccr_value);
|
||||
}
|
||||
|
||||
udc_disable(udc);
|
||||
udc->pullup_resume = udc->pullup_on;
|
||||
@ -2576,19 +2561,11 @@ static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state)
|
||||
*/
|
||||
static int pxa_udc_resume(struct platform_device *_dev)
|
||||
{
|
||||
int i;
|
||||
struct pxa_udc *udc = platform_get_drvdata(_dev);
|
||||
struct pxa_ep *ep;
|
||||
|
||||
ep = &udc->pxa_ep[0];
|
||||
udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME));
|
||||
for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
|
||||
ep = &udc->pxa_ep[i];
|
||||
udc_ep_writel(ep, UDCCSR, ep->udccsr_value);
|
||||
udc_ep_writel(ep, UDCCR, ep->udccr_value);
|
||||
ep_dbg(ep, "udccsr:0x%03x, udccr:0x%x\n",
|
||||
ep->udccsr_value, ep->udccr_value);
|
||||
}
|
||||
|
||||
dplus_pullup(udc, udc->pullup_resume);
|
||||
if (should_enable_udc(udc))
|
||||
@ -2615,6 +2592,7 @@ static struct platform_driver udc_driver = {
|
||||
.driver = {
|
||||
.name = "pxa27x-udc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(udc_pxa_dt_ids),
|
||||
},
|
||||
.probe = pxa_udc_probe,
|
||||
.remove = pxa_udc_remove,
|
||||
|
@ -420,7 +420,8 @@ struct udc_stats {
|
||||
* @usb_gadget: udc gadget structure
|
||||
* @driver: bound gadget (zero, g_ether, g_mass_storage, ...)
|
||||
* @dev: device
|
||||
* @mach: machine info, used to activate specific GPIO
|
||||
* @udc_command: machine specific function to activate D+ pullup
|
||||
* @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none)
|
||||
* @transceiver: external transceiver to handle vbus sense and D+ pullup
|
||||
* @ep0state: control endpoint state machine state
|
||||
* @stats: statistics on udc usage
|
||||
@ -446,7 +447,8 @@ struct pxa_udc {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct device *dev;
|
||||
struct pxa2xx_udc_mach_info *mach;
|
||||
void (*udc_command)(int);
|
||||
struct gpio_desc *gpiod;
|
||||
struct usb_phy *transceiver;
|
||||
|
||||
enum ep0_state ep0state;
|
||||
|
@ -1345,7 +1345,7 @@ static void irq_device_state(struct r8a66597 *r8a66597)
|
||||
if (dvsq == DS_DFLT) {
|
||||
/* bus reset */
|
||||
spin_unlock(&r8a66597->lock);
|
||||
r8a66597->driver->disconnect(&r8a66597->gadget);
|
||||
usb_gadget_udc_reset(&r8a66597->gadget, r8a66597->driver);
|
||||
spin_lock(&r8a66597->lock);
|
||||
r8a66597_update_usb_speed(r8a66597);
|
||||
}
|
||||
@ -1763,8 +1763,7 @@ static int r8a66597_start(struct usb_gadget *gadget,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r8a66597_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int r8a66597_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
|
||||
unsigned long flags;
|
||||
@ -1846,10 +1845,7 @@ static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
|
||||
r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(r8a66597->sudmac_reg))
|
||||
return PTR_ERR(r8a66597->sudmac_reg);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(r8a66597->sudmac_reg);
|
||||
}
|
||||
|
||||
static int r8a66597_probe(struct platform_device *pdev)
|
||||
|
@ -1172,8 +1172,6 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
|
||||
}
|
||||
|
||||
enable_irq(hsudc->irq);
|
||||
dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
|
||||
|
||||
s3c_hsudc_reconfig(hsudc);
|
||||
|
||||
pm_runtime_get_sync(hsudc->dev);
|
||||
@ -1190,8 +1188,7 @@ err_supplies:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c_hsudc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int s3c_hsudc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct s3c_hsudc *hsudc = to_hsudc(gadget);
|
||||
unsigned long flags;
|
||||
@ -1199,11 +1196,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
|
||||
if (!hsudc)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver != hsudc->driver)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&hsudc->lock, flags);
|
||||
hsudc->driver = NULL;
|
||||
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
s3c_hsudc_uninit_phy();
|
||||
|
||||
@ -1220,9 +1213,8 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
|
||||
disable_irq(hsudc->irq);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||
hsudc->driver = NULL;
|
||||
|
||||
dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1267,10 +1259,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
||||
hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
|
||||
sizeof(struct s3c_hsudc_ep) * pd->epnum,
|
||||
GFP_KERNEL);
|
||||
if (!hsudc) {
|
||||
dev_err(dev, "cannot allocate memory\n");
|
||||
if (!hsudc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
hsudc->dev = dev;
|
||||
|
@ -1541,8 +1541,7 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
|
||||
|
||||
static int s3c2410_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int s3c2410_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int s3c2410_udc_stop(struct usb_gadget *g);
|
||||
|
||||
static const struct usb_gadget_ops s3c2410_ops = {
|
||||
.get_frame = s3c2410_udc_get_frame,
|
||||
@ -1683,8 +1682,7 @@ static int s3c2410_udc_start(struct usb_gadget *g,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2410_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int s3c2410_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct s3c2410_udc *udc = to_s3c2410(g);
|
||||
|
||||
|
@ -174,8 +174,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_start - tells usb device controller to start up
|
||||
* @gadget: The gadget we want to get started
|
||||
* @driver: The driver we want to bind to @gadget
|
||||
* @udc: The UDC to be started
|
||||
*
|
||||
* This call is issued by the UDC Class driver when it's about
|
||||
* to register a gadget driver to the device controller, before
|
||||
@ -186,10 +185,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static inline int usb_gadget_udc_start(struct usb_udc *udc)
|
||||
{
|
||||
return gadget->ops->udc_start(gadget, driver);
|
||||
return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,10 +202,9 @@ static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
|
||||
* far as powering off UDC completely and disable its data
|
||||
* line pullups.
|
||||
*/
|
||||
static inline void usb_gadget_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
|
||||
{
|
||||
gadget->ops->udc_stop(gadget, driver);
|
||||
udc->gadget->ops->udc_stop(udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -328,14 +325,14 @@ EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
|
||||
static void usb_gadget_remove_driver(struct usb_udc *udc)
|
||||
{
|
||||
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
|
||||
udc->gadget->name);
|
||||
udc->driver->function);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
udc->driver->unbind(udc->gadget);
|
||||
usb_gadget_udc_stop(udc->gadget, NULL);
|
||||
usb_gadget_udc_stop(udc);
|
||||
|
||||
udc->driver = NULL;
|
||||
udc->dev.driver = NULL;
|
||||
@ -395,7 +392,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
|
||||
ret = driver->bind(udc->gadget, driver);
|
||||
if (ret)
|
||||
goto err1;
|
||||
ret = usb_gadget_udc_start(udc->gadget, driver);
|
||||
ret = usb_gadget_udc_start(udc);
|
||||
if (ret) {
|
||||
driver->unbind(udc->gadget);
|
||||
goto err1;
|
||||
@ -414,7 +411,7 @@ err1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
|
||||
int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
int ret = -ENODEV;
|
||||
@ -438,7 +435,7 @@ out:
|
||||
mutex_unlock(&udc_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udc_attach_driver);
|
||||
EXPORT_SYMBOL_GPL(usb_udc_attach_driver);
|
||||
|
||||
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
||||
{
|
||||
@ -513,11 +510,12 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
|
||||
}
|
||||
|
||||
if (sysfs_streq(buf, "connect")) {
|
||||
usb_gadget_udc_start(udc->gadget, udc->driver);
|
||||
usb_gadget_udc_start(udc);
|
||||
usb_gadget_connect(udc->gadget);
|
||||
} else if (sysfs_streq(buf, "disconnect")) {
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
usb_gadget_udc_stop(udc->gadget, udc->driver);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
usb_gadget_udc_stop(udc);
|
||||
} else {
|
||||
dev_err(dev, "unsupported command '%s'\n", buf);
|
||||
return -EINVAL;
|
||||
|
@ -1403,8 +1403,7 @@ err:
|
||||
*
|
||||
* Return: zero always
|
||||
*/
|
||||
static int xudc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int xudc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct xusb_udc *udc = to_udc(gadget);
|
||||
unsigned long flags;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user